diff --git a/README.md b/README.md index 771774c..9b2b40a 100644 --- a/README.md +++ b/README.md @@ -24,3 +24,6 @@ ### Show on cascade deletion ## Update contract after (without registration) + +## Preview form (if not visible can be accessed by referer nothing is stored) +## View and edit contract application (dashboard/contracts/id/edit/) diff --git a/backend/src/contracts/contracts.py b/backend/src/contracts/contracts.py index d30911d..a7cedad 100644 --- a/backend/src/contracts/contracts.py +++ b/backend/src/contracts/contracts.py @@ -196,6 +196,24 @@ def get_contract_file( ) +@router.get( + '/{_id}/preview-delete', + response_model=list[models.DeleteDependency] +) +async def preview_delete( + _id: int, + session: Session = Depends(get_session), + user: models.User = Depends(get_current_user), +): + if not service.is_allowed(session, user, _id=_id): + raise HTTPException( + status_code=403, + detail=messages.Messages.not_allowed('contract', 'delete') + ) + result = [] + return result + + @router.get('/{form_id}/files') def get_contract_files( form_id: int, diff --git a/backend/src/productors/productors.py b/backend/src/productors/productors.py index 50bca5d..a5a5c60 100644 --- a/backend/src/productors/productors.py +++ b/backend/src/productors/productors.py @@ -18,6 +18,30 @@ def get_productors( return service.get_all(session, user, names, types) +@router.get( + '/{_id}/preview-delete', + response_model=list[models.DeleteDependency] +) +async def preview_delete( + _id: int, + session: Session = Depends(get_session), + user: models.User = Depends(get_current_user), +): + if not service.is_allowed(session, user, _id=_id): + raise HTTPException( + status_code=403, + detail=messages.Messages.not_allowed('productors', 'delete') + ) + try: + result = service.get_delete_dependencies( + session, + _id + ) + except exceptions.ProductorNotFoundError as error: + raise HTTPException(status_code=404, detail=str(error)) from error + return result + + @router.get('/{_id}', response_model=models.ProductorPublic) def get_productor( _id: int, diff --git a/backend/src/productors/service.py b/backend/src/productors/service.py index 9df58b1..b4f2bc7 100644 --- a/backend/src/productors/service.py +++ b/backend/src/productors/service.py @@ -93,6 +93,33 @@ def delete_one(session: Session, _id: int) -> models.ProductorPublic: session.commit() return result + +def get_delete_dependencies( + session: Session, + _id: int +) -> list[models.DeleteDependency]: + statement = select(models.Productor).where(models.Productor.id == _id) + result = session.exec(statement) + productor = result.first() + if not productor: + raise exceptions.ProductorNotFoundError( + messages.Messages.not_found('productor')) + products_statement = ( + select(models.Product) + .where(models.Product.productor_id == _id) + .distinct() + ) + products = session.exec(products_statement).all() + result = [ + models.DeleteDependency( + name=pro.name, + id=pro.id, + type='product' + ) for pro in products + ] + return result + + def is_allowed( session: Session, user: models.User, diff --git a/backend/src/products/products.py b/backend/src/products/products.py index 9566896..26e77fa 100644 --- a/backend/src/products/products.py +++ b/backend/src/products/products.py @@ -26,6 +26,23 @@ def get_products( ) +@router.get( + '/{_id}/preview-delete', + response_model=list[models.DeleteDependency] +) +async def preview_delete( + _id: int, + session: Session = Depends(get_session), + user: models.User = Depends(get_current_user), +): + if not service.is_allowed(session, user, _id=_id): + raise HTTPException( + status_code=403, + detail=messages.Messages.not_allowed('product', 'delete') + ) + return [] + + @router.get('/{_id}', response_model=models.ProductPublic) def get_product( _id: int, diff --git a/backend/src/products/service.py b/backend/src/products/service.py index 3ce4ca4..c857f16 100644 --- a/backend/src/products/service.py +++ b/backend/src/products/service.py @@ -93,6 +93,7 @@ def delete_one( session.commit() return result + def is_allowed( session: Session, user: models.User, @@ -103,12 +104,8 @@ def is_allowed( return False if not _id: statement = ( - select(models.Product) - .join( - models.Productor, - models.Product.productor_id == models.Productor.id - ) - .where(models.Product.id == product.productor_id) + select(models.Productor) + .where(models.Productor.id == product.productor_id) ) productor = session.exec(statement).first() return productor.type in [r.name for r in user.roles] diff --git a/backend/src/shipments/shipments.py b/backend/src/shipments/shipments.py index 886dd51..50fdbdc 100644 --- a/backend/src/shipments/shipments.py +++ b/backend/src/shipments/shipments.py @@ -26,6 +26,23 @@ def get_shipments( ) +@router.get( + '/{_id}/preview-delete', + response_model=list[models.DeleteDependency] +) +async def preview_delete( + _id: int, + session: Session = Depends(get_session), + user: models.User = Depends(get_current_user), +): + if not service.is_allowed(session, user, _id=_id): + raise HTTPException( + status_code=403, + detail=messages.Messages.not_allowed('shipment', 'delete') + ) + return [] + + @router.get('/{_id}', response_model=models.ShipmentPublic) def get_shipment( _id: int, diff --git a/backend/src/users/users.py b/backend/src/users/users.py index 6904edb..c4b220b 100644 --- a/backend/src/users/users.py +++ b/backend/src/users/users.py @@ -36,6 +36,22 @@ def get_roles( return service.get_roles(session) +@router.get( + '/{_id}/preview-delete', + response_model=list[models.DeleteDependency] +) +async def preview_delete( + _id: int, + user: models.User = Depends(get_current_user), +): + if not service.is_allowed(user): + raise HTTPException( + status_code=403, + detail=messages.Messages.not_allowed('user', 'delete') + ) + return [] + + @router.get('/{_id}', response_model=models.UserPublic) def get_user( _id: int, diff --git a/frontend/locales/en.json b/frontend/locales/en.json index a9d74bf..20d5efc 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -39,6 +39,10 @@ "create productor": "create producer", "edit productor": "edit producer", "remove productor": "remove producer", + "remove form": "remove form", + "edit shipment": "edit shipment", + "create shipment": "create shipment", + "create user": "create user", "home": "home", "dashboard": "dashboard", "filter by name": "filter by name", diff --git a/frontend/locales/fr.json b/frontend/locales/fr.json index 1f8485a..2259ddf 100644 --- a/frontend/locales/fr.json +++ b/frontend/locales/fr.json @@ -39,6 +39,10 @@ "create productor": "créer le/la producteur·trice", "edit productor": "modifier le/la producteur·trice", "remove productor": "supprimer le/la producteur·trice", + "remove form": "supprimer un formulaire de contrat", + "edit shipment": "modifier la livraison", + "create shipment": "créer la livraison", + "create user": "créer l'utilisateur·trice", "home": "accueil", "dashboard": "tableau de bord", "filter by name": "filtrer par nom", @@ -183,7 +187,7 @@ "liter": "litres (L)", "are you sure you want to delete": "êtes vous sûr de vouloir supprimer", "this will also delete": "cette action supprimera aussi", - "delete entity": "supprimer un {{entity}}", + "delete entity": "supprimer le/la {{entity}}", "delete": "supprimer", "success": "succès", "success edit": "{{entity}} correctement édité", diff --git a/frontend/src/components/Contracts/Row/index.tsx b/frontend/src/components/Contracts/Row/index.tsx index aa47bfe..f1e8990 100644 --- a/frontend/src/components/Contracts/Row/index.tsx +++ b/frontend/src/components/Contracts/Row/index.tsx @@ -2,16 +2,17 @@ import { ActionIcon, Table, Tooltip } from "@mantine/core"; import { type Contract } from "@/services/resources/contracts"; import { IconDownload, IconX } from "@tabler/icons-react"; import { t } from "@/config/i18n"; -import { useDeleteContract, useGetContractFile } from "@/services/api"; +import { useGetContractFile } from "@/services/api"; import { useCallback } from "react"; +import { useNavigate } from "react-router"; export type ContractRowProps = { contract: Contract; }; export default function ContractRow({ contract }: ContractRowProps) { - const deleteMutation = useDeleteContract(); const getContractMutation = useGetContractFile(); + const navigate = useNavigate(); const handleDownload = useCallback(async () => { getContractMutation.mutateAsync(contract.id); @@ -29,12 +30,10 @@ export default function ContractRow({ contract }: ContractRowProps) { {contract.cheque_quantity > 0 && contract.cheque_quantity} {contract.payment_method} - { - `${Intl.NumberFormat("fr-FR", { - style: "currency", - currency: "EUR", - }).format(contract.total_price)}` - } + {`${Intl.NumberFormat("fr-FR", { + style: "currency", + currency: "EUR", + }).format(contract.total_price)}`} @@ -54,8 +53,9 @@ export default function ContractRow({ contract }: ContractRowProps) { color="red" size="sm" mr="5" - onClick={() => { - deleteMutation.mutate(contract.id); + onClick={(e) => { + e.stopPropagation(); + navigate(`/dashboard/contracts/${contract.id}/delete`); }} > diff --git a/frontend/src/components/DeleteModal/index.tsx b/frontend/src/components/DeleteModal/index.tsx index 3c7b5dc..72007ef 100644 --- a/frontend/src/components/DeleteModal/index.tsx +++ b/frontend/src/components/DeleteModal/index.tsx @@ -7,50 +7,48 @@ import { Link } from "react-router"; export type DeleteModalProps = ModalBaseProps & { handleSubmit: (id: number) => void; entityType: string; - entity?: {name: string, id: number} | undefined; -} + entity?: { name: string; id: number } | undefined; +}; export function DeleteModal({ - opened, - onClose, - handleSubmit, + opened, + onClose, + handleSubmit, entityType, entity, }: DeleteModalProps) { if (!entity) { - return null + return null; } - const {data: deleteDependencies} = useGetDeleteDependencies(entityType, entity.id); + const { data: deleteDependencies } = useGetDeleteDependencies(entityType, entity.id); return ( - {`${t("are you sure you want to delete", {capfirst: true})} : "${entity.name}"`} - {deleteDependencies && deleteDependencies.length > 0 ? {`${t("this will also delete", {capfirst: true})} :`} : null} + {`${t("are you sure you want to delete", { capfirst: true })} : "${entity.name}"`} + {deleteDependencies && deleteDependencies.length > 0 ? ( + {`${t("this will also delete", { capfirst: true })} :`} + ) : null} { - { - deleteDependencies?.map((dependency) => ( - - { - dependency.type === 'contract' ? `${t(dependency.type, {capfirst: true})} - ${dependency.name}` : - - {`${t(dependency.type, {capfirst: true})} - ${dependency.name}`} - - - } - - )) - } + {deleteDependencies?.map((dependency) => ( + + {dependency.type === "contract" ? ( + `${t(dependency.type, { capfirst: true })} - ${dependency.name}` + ) : ( + + {`${t(dependency.type, { capfirst: true })} - ${dependency.name}`} + + )} + + ))} } @@ -65,16 +63,16 @@ export function DeleteModal({ ); -} \ No newline at end of file +} diff --git a/frontend/src/components/Forms/Card/index.tsx b/frontend/src/components/Forms/Card/index.tsx index 9c2bc2f..ad8218f 100644 --- a/frontend/src/components/Forms/Card/index.tsx +++ b/frontend/src/components/Forms/Card/index.tsx @@ -10,37 +10,33 @@ export type FormCardProps = { }; export function FormCard({ form }: FormCardProps) { - const contractBaseTemplate = useGetContractFileTemplate() + const contractBaseTemplate = useGetContractFileTemplate(); return ( - - { - await contractBaseTemplate.mutateAsync(form.id) - }} - > - - - - - - - - + + { + await contractBaseTemplate.mutateAsync(form.id); + }} + > + + + + + + + + @@ -53,8 +53,7 @@ export default function FormModal({ opened, onClose, currentForm, handleSubmit } }); const usersSelect = useMemo(() => { - if (!users) - return []; + if (!users) return []; return users?.map((user) => ({ value: String(user.id), label: `${user.name}`, @@ -62,8 +61,7 @@ export default function FormModal({ opened, onClose, currentForm, handleSubmit } }, [users]); const productorsSelect = useMemo(() => { - if (!productors) - return []; + if (!productors) return []; return productors?.map((prod) => ({ value: String(prod.id), label: `${prod.name}`, @@ -142,10 +140,14 @@ export default function FormModal({ opened, onClose, currentForm, handleSubmit } radius="sm" {...form.getInputProps("minimum_shipment_value")} /> -