From a7b83da149e0c0076d8ef9dbabc4219413553572 Mon Sep 17 00:00:00 2001 From: JulienAldon Date: Sun, 15 Feb 2026 01:09:36 +0100 Subject: [PATCH] add filters for all routes --- backend/src/productors/service.py | 6 +++- backend/src/products/products.py | 7 ++++- backend/src/products/service.py | 1 - backend/src/shipments/service.py | 13 +++++++- backend/src/shipments/shipments.py | 17 ++++++++-- backend/src/users/service.py | 10 +++++- backend/src/users/users.py | 14 +++++++-- frontend/index.html | 2 +- frontend/locales/en.json | 2 +- frontend/locales/fr.json | 2 +- frontend/src/components/Navbar/index.css | 2 +- frontend/src/components/Navbar/index.tsx | 31 +++++++++++++------ .../src/components/Productors/Row/index.tsx | 2 +- .../src/components/Shipments/Filter/index.tsx | 17 ++++++++++ .../src/components/Shipments/Modal/index.tsx | 1 + frontend/src/pages/Contract/index.tsx | 16 ++++++++-- frontend/src/pages/Forms/index.tsx | 27 ++++++++-------- frontend/src/pages/Productors/index.tsx | 24 +++++++------- frontend/src/pages/Products/index.tsx | 26 ++++++++-------- frontend/src/pages/Shipments/index.tsx | 30 +++++++++++------- frontend/src/pages/Users/index.tsx | 15 +++++++-- frontend/src/router.tsx | 1 - 22 files changed, 184 insertions(+), 82 deletions(-) diff --git a/backend/src/productors/service.py b/backend/src/productors/service.py index 68fc1ab..247b13e 100644 --- a/backend/src/productors/service.py +++ b/backend/src/productors/service.py @@ -1,7 +1,11 @@ from sqlmodel import Session, select import src.models as models -def get_all(session: Session, names: list[str], types: list[str]) -> list[models.ProductorPublic]: +def get_all( + session: Session, + names: list[str], + types: list[str] +) -> list[models.ProductorPublic]: statement = select(models.Productor) if len(names) > 0: statement = statement.where(models.Productor.name.in_(names)) diff --git a/backend/src/products/products.py b/backend/src/products/products.py index 0bdf100..b3da604 100644 --- a/backend/src/products/products.py +++ b/backend/src/products/products.py @@ -14,7 +14,12 @@ def get_products( types: list[str] = Query([]), productors: list[str] = Query([]), ): - return service.get_all(session, names, productors, types) + return service.get_all( + session, + names, + productors, + types, + ) @router.get('/{id}', response_model=models.ProductPublic) def get_product(id: int, session: Session = Depends(get_session)): diff --git a/backend/src/products/service.py b/backend/src/products/service.py index 951a992..ae91f28 100644 --- a/backend/src/products/service.py +++ b/backend/src/products/service.py @@ -6,7 +6,6 @@ def get_all( names: list[str], productors: list[str], types: list[str], - ) -> list[models.ProductPublic]: statement = select(models.Product) if len(names) > 0: diff --git a/backend/src/shipments/service.py b/backend/src/shipments/service.py index c2c0071..e5a8a1f 100644 --- a/backend/src/shipments/service.py +++ b/backend/src/shipments/service.py @@ -1,8 +1,19 @@ from sqlmodel import Session, select import src.models as models -def get_all(session: Session) -> list[models.ShipmentPublic]: +def get_all( + session: Session, + names: list[str], + dates: list[str], + forms: list[int] +) -> list[models.ShipmentPublic]: statement = select(models.Shipment) + if len(names) > 0: + statement = statement.where(models.Shipment.name.in_(names)) + if len(dates) > 0: + statement = statement.where(models.Shipment.date.in_(list(map(lambda x: datetime.strptime(x, '%Y-%m-%d'), dates)))) + if len(forms) > 0: + statement = statement.join(models.Form).where(models.Form.name.in_(forms)) return session.exec(statement.order_by(models.Shipment.name)).all() def get_one(session: Session, shipment_id: int) -> models.ShipmentPublic: diff --git a/backend/src/shipments/shipments.py b/backend/src/shipments/shipments.py index 7302e15..51ad853 100644 --- a/backend/src/shipments/shipments.py +++ b/backend/src/shipments/shipments.py @@ -1,15 +1,26 @@ -from fastapi import APIRouter, HTTPException, Depends +from fastapi import APIRouter, HTTPException, Depends, Query import src.messages as messages import src.models as models from src.database import get_session from sqlmodel import Session import src.shipments.service as service from src.auth.auth import get_current_user + router = APIRouter(prefix='/shipments') @router.get('/', response_model=list[models.ShipmentPublic], ) -def get_shipments(session: Session = Depends(get_session)): - return service.get_all(session) +def get_shipments( + session: Session = Depends(get_session), + names: list[str] = Query([]), + dates: list[str] = Query([]), + forms: list[str] = Query([]), +): + return service.get_all( + session, + names, + dates, + forms, + ) @router.get('/{id}', response_model=models.ShipmentPublic) def get_shipment(id: int, session: Session = Depends(get_session)): diff --git a/backend/src/users/service.py b/backend/src/users/service.py index 825f48a..febc227 100644 --- a/backend/src/users/service.py +++ b/backend/src/users/service.py @@ -1,8 +1,16 @@ from sqlmodel import Session, select import src.models as models -def get_all(session: Session) -> list[models.UserPublic]: +def get_all( + session: Session, + names: list[str], + emails: list[str], +) -> list[models.UserPublic]: statement = select(models.User) + if len(names) > 0: + statement = statement.where(models.User.name.in_(names)) + if len(emails) > 0: + statement = statement.where(models.User.email.in_(emails)) return session.exec(statement.order_by(models.User.name)).all() def get_one(session: Session, user_id: int) -> models.UserPublic: diff --git a/backend/src/users/users.py b/backend/src/users/users.py index 311e90f..a175ac2 100644 --- a/backend/src/users/users.py +++ b/backend/src/users/users.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, HTTPException, Depends +from fastapi import APIRouter, HTTPException, Depends, Query import src.messages as messages import src.models as models from src.database import get_session @@ -8,8 +8,16 @@ import src.users.service as service router = APIRouter(prefix='/users') @router.get('/', response_model=list[models.UserPublic]) -def get_users(session: Session = Depends(get_session)): - return service.get_all(session) +def get_users( + session: Session = Depends(get_session), + names: list[str] = Query([]), + emails: list[str] = Query([]), +): + return service.get_all( + session, + names, + emails, + ) @router.get('/{id}', response_model=models.UserPublic) def get_users(id: int, session: Session = Depends(get_session)): diff --git a/frontend/index.html b/frontend/index.html index 072a57e..fedb463 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - frontend + Amap Croix-luizet
diff --git a/frontend/locales/en.json b/frontend/locales/en.json index 2a05911..d0cf527 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -121,5 +121,5 @@ "error deleting shipment": "error deleting shipment", "there is no contract for now": "there is no contract for now.", "the product unit will be assigned to the quantity requested in the form": "the product unit will be assigned to the quantity requested in the form", - "all theses informations are for contract generation, no informations is stored outside of contracts": "all theses informations are for contract generation, no informations is stored outside of contracts." + "all theses informations are for contract generation": "all theses informations are for contract generation." } \ No newline at end of file diff --git a/frontend/locales/fr.json b/frontend/locales/fr.json index f105a60..635d965 100644 --- a/frontend/locales/fr.json +++ b/frontend/locales/fr.json @@ -134,5 +134,5 @@ "there is no contract for now": "Il n'y a pas de contrats pour le moment.", "the product unit will be assigned to the quantity requested in the form": "L'unité de vente du produit définit l'unité associée a la quantité demandée dans le formulaire des amapiens.", - "all theses informations are for contract generation, no informations is stored outside of contracts": "ces informations sont nécéssaires pour la génération de contrat, aucune information personnelle n'est gardée ailleurs que dans les contrats générés." + "all theses informations are for contract generation": "ces informations sont nécéssaires pour la génération de contrat." } \ No newline at end of file diff --git a/frontend/src/components/Navbar/index.css b/frontend/src/components/Navbar/index.css index b9fba6a..64c8c4a 100644 --- a/frontend/src/components/Navbar/index.css +++ b/frontend/src/components/Navbar/index.css @@ -3,11 +3,11 @@ nav { justify-self: left; padding: 1rem; background-color: var(--mantine-color-blue-4); + justify-content: space-between; } .navLink { color: #fff; font-weight: bold; - margin-right: 1rem; text-decoration: none; } \ No newline at end of file diff --git a/frontend/src/components/Navbar/index.tsx b/frontend/src/components/Navbar/index.tsx index caced57..b5ab26b 100644 --- a/frontend/src/components/Navbar/index.tsx +++ b/frontend/src/components/Navbar/index.tsx @@ -1,21 +1,34 @@ import { NavLink } from "react-router"; import { t } from "@/config/i18n"; import "./index.css"; +import { Group } from "@mantine/core"; +import { Config } from "@/config/config"; export function Navbar() { return ( ); diff --git a/frontend/src/components/Productors/Row/index.tsx b/frontend/src/components/Productors/Row/index.tsx index 6833b45..e8d7956 100644 --- a/frontend/src/components/Productors/Row/index.tsx +++ b/frontend/src/components/Productors/Row/index.tsx @@ -24,7 +24,7 @@ export default function ProductorRow({ { productor.payment_methods.map((value) =>( - + {t(value.name, {capfirst: true})} )) diff --git a/frontend/src/components/Shipments/Filter/index.tsx b/frontend/src/components/Shipments/Filter/index.tsx index c83d6b4..2497f56 100644 --- a/frontend/src/components/Shipments/Filter/index.tsx +++ b/frontend/src/components/Shipments/Filter/index.tsx @@ -4,18 +4,23 @@ import { t } from "@/config/i18n"; export type ShipmentFiltersProps = { names: string[]; + forms: string[]; filters: URLSearchParams; onFilterChange: (values: string[], filter: string) => void; } export default function ShipmentsFilters({ names, + forms, filters, onFilterChange }: ShipmentFiltersProps) { const defaultNames = useMemo(() => { return filters.getAll("names") }, [filters]); + const defaultForms = useMemo(() => { + return filters.getAll("forms") + }, [filters]); return ( @@ -28,6 +33,18 @@ export default function ShipmentsFilters({ onFilterChange(values, 'names') }} clearable + searchable + /> + { + onFilterChange(values, 'forms') + }} + clearable + searchable /> ); diff --git a/frontend/src/components/Shipments/Modal/index.tsx b/frontend/src/components/Shipments/Modal/index.tsx index f3a0942..2a5a0c7 100644 --- a/frontend/src/components/Shipments/Modal/index.tsx +++ b/frontend/src/components/Shipments/Modal/index.tsx @@ -44,6 +44,7 @@ export default function ShipmentModal({ const { data: allForms } = getForms(); const { data: allProducts } = getProducts(new URLSearchParams("types=1")); const { data: allProductors } = getProductors() + const formsSelect = useMemo(() => { return allForms?.map(form => ({value: String(form.id), label: `${form.name} ${form.season}`})) }, [allForms]); diff --git a/frontend/src/pages/Contract/index.tsx b/frontend/src/pages/Contract/index.tsx index fa81531..441e66e 100644 --- a/frontend/src/pages/Contract/index.tsx +++ b/frontend/src/pages/Contract/index.tsx @@ -80,7 +80,7 @@ export function Contract() { shipment.products ); if (total < (form?.minimum_shipment_value || 0)) { - return shipment.id; // mark shipment as invalid + return shipment.id; } return null; }) @@ -125,8 +125,18 @@ export function Contract() { } }, [inputForm, inputRefs, isShipmentsMinimumValue, form]); + if (!form) - return ; + return ( + + + + ); return ( @@ -134,7 +144,7 @@ export function Contract() { {form.name} {t("informations", {capfirst: true})} - {t("all theses informations are for contract generation, no informations is stored outside of contracts", {capfirst: true})} + {t("all theses informations are for contract generation", {capfirst: true})} { if (!id) @@ -80,11 +75,7 @@ export function Forms() { } }); closeModal(); - notifications.show({ - title: t("success", {capfirst: true}), - message: t("successfully edited form", {capfirst: true}), - }); - }, [editFormMutation]); + }, [editFormMutation, closeModal]); const onFilterChange = useCallback(( values: string[], @@ -102,9 +93,19 @@ export function Forms() { }); }, [searchParams, setSearchParams]) - if (!data || isPending) - return (); + if (!data || isPending) + return ( + + + + ); + return ( diff --git a/frontend/src/pages/Productors/index.tsx b/frontend/src/pages/Productors/index.tsx index 1d6a260..fd9521a 100644 --- a/frontend/src/pages/Productors/index.tsx +++ b/frontend/src/pages/Productors/index.tsx @@ -8,7 +8,6 @@ import { ProductorModal } from "@/components/Productors/Modal"; import { useCallback, useMemo } from "react"; import type { Productor, ProductorInputs } from "@/services/resources/productors"; import ProductorsFilters from "@/components/Productors/Filter"; -import { notifications } from "@mantine/notifications"; export default function Productors() { const [ searchParams, setSearchParams ] = useSearchParams(); @@ -51,11 +50,7 @@ export default function Productors() { ...productor }); closeModal(); - notifications.show({ - title: t("success", {capfirst: true}), - message: t("successfully created productor", {capfirst: true}), - }); - }, [createProductorMutation]); + }, [createProductorMutation, closeModal]); const handleEditProductor = useCallback(async (productor: ProductorInputs, id?: number) => { if (!id) @@ -65,11 +60,7 @@ export default function Productors() { productor: productor }); closeModal(); - notifications.show({ - title: t("success", {capfirst: true}), - message: t("successfully edited productor", {capfirst: true}), - }); - }, []); + }, [editProductorMutation, closeModal]); const onFilterChange = useCallback((values: string[], filter: string) => { setSearchParams(prev => { @@ -84,7 +75,16 @@ export default function Productors() { }, [searchParams, setSearchParams]) if (!productors || isPending) - return + return ( + + + + ); return ( diff --git a/frontend/src/pages/Products/index.tsx b/frontend/src/pages/Products/index.tsx index 1b2d28d..b0ece5f 100644 --- a/frontend/src/pages/Products/index.tsx +++ b/frontend/src/pages/Products/index.tsx @@ -8,7 +8,6 @@ import { ProductModal } from "@/components/Products/Modal"; import { useCallback, useMemo } from "react"; import { productCreateFromProductInputs, type Product, type ProductInputs } from "@/services/resources/products"; import ProductsFilters from "@/components/Products/Filter"; -import { notifications } from "@mantine/notifications"; export default function Products() { const [ searchParams, setSearchParams ] = useSearchParams(); @@ -49,11 +48,7 @@ export default function Products() { const handleCreateProduct = useCallback(async (product: ProductInputs) => { await createProductMutation.mutateAsync(productCreateFromProductInputs(product)); closeModal(); - notifications.show({ - title: t("success", {capfirst: true}), - message: t("successfully created product", {capfirst: true}), - }); - }, [createProductMutation]); + }, [createProductMutation, closeModal]); const handleEditProduct = useCallback(async (product: ProductInputs, id?: number) => { if (!id) @@ -63,11 +58,7 @@ export default function Products() { product: productCreateFromProductInputs(product) }); closeModal(); - notifications.show({ - title: t("success", {capfirst: true}), - message: t("successfully edited product", {capfirst: true}), - }); - }, []); + }, [editProductMutation, closeModal]); const onFilterChange = useCallback((values: string[], filter: string) => { setSearchParams(prev => { @@ -82,8 +73,17 @@ export default function Products() { }, [searchParams, setSearchParams]) if (!products || isPending) - return - + return ( + + + + ); + return ( diff --git a/frontend/src/pages/Shipments/index.tsx b/frontend/src/pages/Shipments/index.tsx index f366a9e..1f389e3 100644 --- a/frontend/src/pages/Shipments/index.tsx +++ b/frontend/src/pages/Shipments/index.tsx @@ -8,7 +8,6 @@ import { useCallback, useMemo } from "react"; import { shipmentCreateFromShipmentInputs, type Shipment, type ShipmentInputs } from "@/services/resources/shipments"; import ShipmentModal from "@/components/Shipments/Modal"; import ShipmentsFilters from "@/components/Shipments/Filter"; -import { notifications } from "@mantine/notifications"; export default function Shipments() { const [ searchParams, setSearchParams ] = useSearchParams(); @@ -38,17 +37,18 @@ export default function Shipments() { .filter((season, index, array) => array.indexOf(season) === index) }, [allShipments]) + const forms = useMemo(() => { + return allShipments?.map((shipment: Shipment) => (shipment.form.name)) + .filter((season, index, array) => array.indexOf(season) === index) + }, [allShipments]) + const createShipmentMutation = createShipment(); const editShipmentMutation = editShipment(); const handleCreateShipment = useCallback(async (shipment: ShipmentInputs) => { await createShipmentMutation.mutateAsync(shipmentCreateFromShipmentInputs(shipment)); closeModal(); - notifications.show({ - title: t("success", {capfirst: true}), - message: t("successfully created shipment", {capfirst: true}), - }); - }, [createShipmentMutation]); + }, [createShipmentMutation, closeModal]); const handleEditShipment = useCallback(async (shipment: ShipmentInputs, id?: number) => { if (!id) @@ -58,11 +58,7 @@ export default function Shipments() { shipment: shipmentCreateFromShipmentInputs(shipment) }); closeModal(); - notifications.show({ - title: t("success", {capfirst: true}), - message: t("successfully edited shipment", {capfirst: true}), - }); - }, []); + }, [editShipmentMutation, closeModal]); const onFilterChange = useCallback((values: string[], filter: string) => { setSearchParams(prev => { @@ -77,7 +73,16 @@ export default function Shipments() { }, [searchParams, setSearchParams]) if (!shipments || isPending) - return + return ( + + + + ); return ( @@ -106,6 +111,7 @@ export default function Shipments() { /> { await createUserMutation.mutateAsync(user); closeModal(); - }, [createUserMutation]); + }, [createUserMutation, closeModal]); const handleEditUser = useCallback(async (user: UserInputs, id?: number) => { if (!id) @@ -54,7 +54,7 @@ export default function Users() { user: user }); closeModal(); - }, []); + }, [editUserMutation, closeModal]); const onFilterChange = useCallback((values: string[], filter: string) => { setSearchParams(prev => { @@ -69,7 +69,16 @@ export default function Users() { }, [searchParams, setSearchParams]) if (!users || isPending) - return + return ( + + + + ); return ( diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index 08d075a..2751284 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -12,7 +12,6 @@ import Users from "@/pages/Users"; import Shipments from "./pages/Shipments"; import { Contract } from "./pages/Contract"; import { NotFound } from "./pages/NotFound"; -// import { CreateForms } from "@/pages/Forms/CreateForm"; export const router = createBrowserRouter([ {