[WIP] front api exchanges

This commit is contained in:
2026-02-11 00:53:40 +01:00
parent feea610d09
commit 3b2b36839f
19 changed files with 694 additions and 32 deletions

View File

@@ -2,9 +2,9 @@ DB_USER=postgres
DB_PASS=postgres DB_PASS=postgres
DB_NAME=amap DB_NAME=amap
DB_HOST=localhost DB_HOST=localhost
ORIGINS=http://localhost:8000 ORIGINS=http://localhost:5173
SECRET_KEY= SECRET_KEY=
ROOT_FQDN=http://localhost VITE_API_URL=http://localhost:8000
KEYCLOAK_SERVER= KEYCLOAK_SERVER=
KEYCLOAK_REALM= KEYCLOAK_REALM=
KEYCLOAK_CLIENT_ID= KEYCLOAK_CLIENT_ID=

View File

@@ -10,12 +10,12 @@ vars:pre-request {
Route: forms Route: forms
ExamplePOSTBody: ''' ExamplePOSTBody: '''
{ {
"productor_id": 1, "productor_id": 2,
"referer_id": 1, "referer_id": 1,
"season": "Hiver-2026", "season": "Automne-2026",
"shipments": 5,
"start": "2026-01-10", "start": "2026-01-10",
"end": "2026-05-10" "end": "2026-05-10",
"name": "test very very very form long name"
} }
''' '''
ExamplePUTBody: ''' ExamplePUTBody: '''

View File

@@ -8,6 +8,6 @@ auth {
vars:pre-request { vars:pre-request {
Route: productors Route: productors
ExamplePOSTBody: {"name": "test", "address": "test", "payment": "test"} ExamplePOSTBody: {"name": "marie", "address": "test", "payment": "test"}
ExamplePUTBody: {"name": "updatetestt", "address": "updatetestt"} ExamplePUTBody: {"name": "updatetestt", "address": "updatetestt"}
} }

View File

@@ -13,7 +13,7 @@ vars:pre-request {
"name": "test", "name": "test",
"date": "2026-01-10", "date": "2026-01-10",
"product_ids": [1], "product_ids": [1],
"form_id": 3 "form_id": 1
} }
''' '''
ExamplePUTBody: ''' ExamplePUTBody: '''

View File

@@ -1,4 +1,4 @@
from fastapi import APIRouter, HTTPException, Depends from fastapi import APIRouter, HTTPException, Depends, Query
import src.messages as messages import src.messages as messages
import src.models as models import src.models as models
from src.database import get_session from src.database import get_session
@@ -8,29 +8,33 @@ import src.forms.service as service
router = APIRouter(prefix='/forms') router = APIRouter(prefix='/forms')
@router.get('/', response_model=list[models.FormPublic]) @router.get('/', response_model=list[models.FormPublic])
def get_forms(session: Session = Depends(get_session)): async def get_forms(
return service.get_all(session) seasons: list[str] = Query([]),
productors: list[str] = Query([]),
session: Session = Depends(get_session)
):
return service.get_all(session, seasons, productors)
@router.get('/{id}', response_model=models.FormPublic) @router.get('/{id}', response_model=models.FormPublic)
def get_forms(id: int, session: Session = Depends(get_session)): async def get_forms(id: int, session: Session = Depends(get_session)):
result = service.get_one(session, id) result = service.get_one(session, id)
if result is None: if result is None:
raise HTTPException(status_code=404, detail=messages.notfound) raise HTTPException(status_code=404, detail=messages.notfound)
return result return result
@router.post('/', response_model=models.FormPublic) @router.post('/', response_model=models.FormPublic)
def create_form(form: models.FormCreate, session: Session = Depends(get_session)): async def create_form(form: models.FormCreate, session: Session = Depends(get_session)):
return service.create_one(session, form) return service.create_one(session, form)
@router.put('/{id}', response_model=models.FormPublic) @router.put('/{id}', response_model=models.FormPublic)
def update_form(id: int, form: models.FormUpdate, session: Session = Depends(get_session)): async def update_form(id: int, form: models.FormUpdate, session: Session = Depends(get_session)):
result = service.update_one(session, id, form) result = service.update_one(session, id, form)
if result is None: if result is None:
raise HTTPException(status_code=404, detail=messages.notfound) raise HTTPException(status_code=404, detail=messages.notfound)
return result return result
@router.delete('/{id}', response_model=models.FormPublic) @router.delete('/{id}', response_model=models.FormPublic)
def delete_form(id: int, session: Session = Depends(get_session)): async def delete_form(id: int, session: Session = Depends(get_session)):
result = service.delete_one(session, id) result = service.delete_one(session, id)
if result is None: if result is None:
raise HTTPException(status_code=404, detail=messages.notfound) raise HTTPException(status_code=404, detail=messages.notfound)

View File

@@ -1,8 +1,16 @@
from sqlmodel import Session, select from sqlmodel import Session, select
import src.models as models import src.models as models
def get_all(session: Session) -> list[models.FormPublic]: def get_all(
session: Session,
seasons: list[str],
productors: list[str]
) -> list[models.FormPublic]:
statement = select(models.Form) statement = select(models.Form)
if len(seasons) > 0:
statement = statement.where(models.Form.season.in_(seasons))
if len(productors) > 0:
statement = statement.join(models.Productor).where(models.Productor.name.in_(productors))
return session.exec(statement).all() return session.exec(statement).all()
def get_one(session: Session, form_id: int) -> models.FormPublic: def get_one(session: Session, form_id: int) -> models.FormPublic:

View File

@@ -45,6 +45,7 @@ class ProductorCreate(ProductorBase):
class Unit(Enum): class Unit(Enum):
GRAMS = 1 GRAMS = 1
KILO = 2 KILO = 2
PIECE = 3
class ProductType(Enum): class ProductType(Enum):
PLANNED = 1 PLANNED = 1
@@ -96,7 +97,7 @@ class FormBase(SQLModel):
class FormPublic(FormBase): class FormPublic(FormBase):
id: int id: int
productor: ProductorPublic | None productor: ProductorPublic | None
referer: User referer: User | None
shipments: list["Shipment"] = [] shipments: list["Shipment"] = []
class Form(FormBase, table=True): class Form(FormBase, table=True):
@@ -164,4 +165,4 @@ class ShipmentUpdate(SQLModel):
product_ids: list[int] product_ids: list[int]
class ShipmentCreate(ShipmentBase): class ShipmentCreate(ShipmentBase):
product_ids: list[int] product_ids: list[int] | None

View File

@@ -12,7 +12,7 @@ class Settings(BaseSettings):
keycloak_client_id: str keycloak_client_id: str
keycloak_client_secret: str keycloak_client_secret: str
keycloak_redirect_uri: str keycloak_redirect_uri: str
root_fqdn: str vite_api_url: str
class Config: class Config:
env_file = "../.env" env_file = "../.env"

View File

@@ -0,0 +1,72 @@
import { Grid, NumberInput, Paper, Select, Stack, TextInput } from "@mantine/core";
import { t } from "../../config/i18n";
export type CreateProductProps = {
form: Record<string, any>;
}
export default function CreateProduct({form}: CreateProductProps) {
return (
<Stack>
<Grid>
<Grid.Col span={{ base: 12, md: 6, lg: 6 }}>
<TextInput
label={t("product name")}
placeholder={t("product name")}
radius="sm"
withAsterisk
{...form.getInputProps('name')}
/>
<NumberInput
label={t("product price")}
placeholder={t("product price")}
radius="sm"
withAsterisk
{...form.getInputProps('price')}
/>
<TextInput
label={t("product weight")}
placeholder={t("product weight")}
radius="sm"
withAsterisk
{...form.getInputProps('weight')}
/>
</Grid.Col>
<Grid.Col span={{ base: 12, md: 6, lg: 6 }}>
<Select
label={t("product type")}
placeholder={t("product type")}
radius="sm"
data={[
{value: "1", label: t("planned")},
{value: "2", label: t("reccurent")}
]}
defaultValue={"1"}
clearable
{...form.getInputProps('type')}
/>
<NumberInput
label={t("product price kg")}
placeholder={t("product price kg")}
radius="sm"
withAsterisk
{...form.getInputProps('pricekg')}
/>
<Select
label={t("product unit")}
placeholder={t("product unit")}
radius="sm"
data={[
{value: "1", label: t("grams")},
{value: "2", label: t("kilo")},
{value: "3", label: t("piece")}
]}
defaultValue={"2"}
clearable
{...form.getInputProps('unit')}
/>
</Grid.Col>
</Grid>
</Stack>
);
}

View File

@@ -0,0 +1,62 @@
import { Button, Group, Loader, Modal, Text, TextInput, Title, type ModalBaseProps } from "@mantine/core";
import { t } from "../../config/i18n";
import { useForm } from "@mantine/form";
import { IconCancel, IconPlus } from "@tabler/icons-react";
import { createProductor, type Productor } from "../../services/api";
import { useEffect } from "react";
export function CreateProductorModal({opened, onClose}: ModalBaseProps) {
const form = useForm<Productor>();
const mutation = createProductor();
return (
<Modal
size="50%"
opened={opened}
onClose={onClose}
title={t("create productor")}
>
<Title order={4}>{t("Informations")}</Title>
<TextInput
label={t("productor name")}
placeholder={t("productor name")}
radius="sm"
withAsterisk
{...form.getInputProps('name')}
/>
<TextInput
label={t("productor address")}
placeholder={t("productor address")}
radius="sm"
withAsterisk
{...form.getInputProps('address')}
/>
<TextInput
label={t("productor payment")}
placeholder={t("productor payment")}
radius="sm"
withAsterisk
{...form.getInputProps('payment')}
/>
{mutation.isError ? <Text>{t("an error occured")}:{mutation.error.message}</Text> : null}
{mutation.isSuccess ? <Text>{t("success")}</Text> : null}
<Group mt="sm" justify="space-between">
<Button
variant="filled"
color="red"
aria-label={t("cancel")}
leftSection={<IconCancel/>}
onClick={onClose}
>{t("cancel")}</Button>
<Button
variant="filled"
aria-label={t("create productor")}
leftSection={mutation.isPending ? <Loader/> : <IconPlus/>}
onClick={() => {
mutation.mutate(form.getValues());
}}
>{t("create productor")}</Button>
</Group>
</Modal>
);
}

View File

@@ -0,0 +1,56 @@
import { Button, Group, Loader, Modal, Text, TextInput, Title, type ModalBaseProps } from "@mantine/core";
import { t } from "../../config/i18n";
import { useForm } from "@mantine/form";
import { IconCancel, IconPlus } from "@tabler/icons-react";
import { DatePickerInput } from "@mantine/dates";
import { createShipment, type ShipmentCreate } from "../../services/api";
import { useEffect } from "react";
export function CreateShipmentModal({opened, onClose}: ModalBaseProps) {
const form = useForm<ShipmentCreate>();
const mutation = createShipment();
return (
<Modal
size="50%"
opened={opened}
onClose={onClose}
title={t("create shipment")}
>
<Title order={4}>{t("informations")}</Title>
<TextInput
label={t("shipment name")}
placeholder={t("shipment name")}
radius="sm"
withAsterisk
{...form.getInputProps('name')}
/>
<DatePickerInput
label={t("shipment date")}
placeholder={t("shipment date")}
radius="sm"
withAsterisk
{...form.getInputProps('date')}
/>
{mutation.isError ? <Text>{t("an error occured")}:{mutation.error.message}</Text> : null}
{mutation.isSuccess ? <Text>{t("success")}</Text> : null}
<Group mt="sm" justify="space-between">
<Button
variant="filled"
color="red"
aria-label={t("cancel")}
leftSection={<IconCancel/>}
onClick={onClose}
>{t("cancel")}</Button>
<Button
variant="filled"
aria-label={t("create shipment")}
leftSection={mutation.isPending ? <Loader/> : <IconPlus/>}
onClick={() => {
mutation.mutate({...form.getValues(), product_ids: []});
}}
>{t("create shipment")}</Button>
</Group>
</Modal>
);
}

View File

@@ -3,8 +3,9 @@ import { createRoot } from "react-dom/client";
import { RouterProvider } from "react-router"; import { RouterProvider } from "react-router";
import { router } from "./router.tsx"; import { router } from "./router.tsx";
import { MantineProvider } from "@mantine/core"; import { MantineProvider } from "@mantine/core";
import '@mantine/core/styles.css';
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import '@mantine/core/styles.css';
import '@mantine/dates/styles.css';
const queryClient = new QueryClient() const queryClient = new QueryClient()

View File

@@ -0,0 +1,154 @@
import { ActionIcon, Button, Group, Loader, Modal, MultiSelect, Select, Stack, Text, TextInput, Title, Tooltip } from "@mantine/core";
import { t } from "../../../config/i18n";
import { DatePickerInput } from "@mantine/dates";
import { useForm } from "@mantine/form";
import { IconCancel, IconPlus } from "@tabler/icons-react";
import { CreateProductorModal } from "../../../components/CreateProductorModal";
import { useDisclosure } from "@mantine/hooks";
import { CreateShipmentModal } from "../../../components/CreateShipmentModal";
import { createForm, getProductors, getShipments, getUsers, type FormCreate } from "../../../services/api";
import { useEffect, useMemo } from "react";
import { useNavigate } from "react-router";
export function CreateForms() {
const form = useForm<FormCreate>()
const navigate = useNavigate()
const [openedProductor, { open: openProductor, close: closeProductor }] = useDisclosure(false);
const [openedShipent, { open: openShipment, close: closeShipment }] = useDisclosure(false);
const {data: shipments} = getShipments();
const {data: productors} = getProductors();
const {data: users} = getUsers();
const mutation = createForm();
const usersSelect = useMemo(() => {
return users?.map(user => ({value: String(user.id), label: `${user.name}`}))
}, [users])
const productorsSelect = useMemo(() => {
return productors?.map(prod => ({value: String(prod.id), label: `${prod.name}`}))
}, [productors])
const shipmentsSelect = useMemo(() => {
return shipments?.map(ship => ({value: String(ship.id), label: `${ship.name} ${ship.date}`}))
}, [shipments])
if (mutation.isSuccess)
navigate('/forms')
return (
<Stack w={{base: "100%", sm: "50%", lg: "60%"}}>
<Title order={2}>{t("create form")}</Title>
<TextInput
label={t("form name")}
placeholder={t("form name")}
radius="sm"
withAsterisk
{...form.getInputProps('name')}
/>
<TextInput
label={t("contact season")}
placeholder={t("contact season")}
radius="sm"
withAsterisk
{...form.getInputProps('season')}
/>
<DatePickerInput
label={t("start date")}
placeholder={t("start date")}
withAsterisk
{...form.getInputProps('start')}
/>
<DatePickerInput
label={t("end date")}
placeholder={t("end date")}
withAsterisk
{...form.getInputProps('end')}
/>
<Group>
<Select
label={t("referer")}
placeholder={t("referer")}
nothingFoundMessage={t("nothing found")}
withAsterisk
clearable
allowDeselect
searchable
data={usersSelect || []}
{...form.getInputProps('referer_id')}
/>
</Group>
<Group>
<Select
label={t("productor")}
placeholder={t("productor")}
nothingFoundMessage={t("nothing found")}
withAsterisk
clearable
allowDeselect
searchable
data={productorsSelect || []}
{...form.getInputProps('productor_id')}
/>
<Tooltip label={t("create new productor")}>
<ActionIcon
onClick={openProductor}
aria-label={t("create new productor")}
>
<IconPlus/>
</ActionIcon>
</Tooltip>
<CreateProductorModal
opened={openedProductor}
onClose={closeProductor}
/>
</Group>
<Group>
<MultiSelect
label={t("shipment")}
placeholder={t("shipment")}
nothingFoundMessage={t("nothing found")}
withAsterisk
clearable
searchable
data={shipmentsSelect || []}
{...form.getInputProps('shipment')}
/>
<Tooltip label={t("create new shipment")}>
<ActionIcon
onClick={openShipment}
aria-label={t("create new shipment")}
>
<IconPlus/>
</ActionIcon>
</Tooltip>
<CreateShipmentModal
opened={openedShipent}
onClose={closeShipment}
/>
</Group>
{mutation.isError ? <Text>{t("an error occured")}:{mutation.error.message}</Text> : null}
{mutation.isSuccess ? <Text>{t("success")}</Text> : null}
<Group mt="sm" justify="space-between">
<Button
variant="filled"
color="red"
aria-label={t("cancel")}
leftSection={<IconCancel/>}
onClick={() => {
console.log(form.getValues())
}}
>{t("cancel")}</Button>
<Button
variant="filled"
aria-label={t("create form")}
leftSection={mutation.isPending ? <Loader/> : <IconPlus/>}
onClick={() => {
mutation.mutate(form.getValues());
}}
>{t("create form")}</Button>
</Group>
</Stack>
);
}

View File

@@ -0,0 +1,44 @@
import { Group, MultiSelect } from "@mantine/core";
import { t } from "../../../config/i18n";
import { useMemo } from "react";
export type FilterFormsProps = {
seasons: string[];
productors: string[];
filters: URLSearchParams;
onFilterChange: (values: string[], filter: string) => void;
}
export function FilterForms({seasons, productors, filters, onFilterChange}: FilterFormsProps) {
const defaultProductors = useMemo(() => {
return filters.getAll("productors")
}, [filters]);
const defaultSeasons = useMemo(() => {
return filters.getAll("seasons")
}, [filters]);
return (
<Group>
<MultiSelect
aria-label={t("Filter by season")}
placeholder={t("Filter by season")}
data={seasons}
defaultValue={defaultSeasons}
onChange={(values: string[]) => {
onFilterChange(values, 'seasons')
}}
clearable
/>
<MultiSelect
aria-label={t("Filter by productor")}
placeholder={t("Filter by productor")}
data={productors}
defaultValue={defaultProductors}
onChange={(values: string[]) => {
onFilterChange(values, 'productors')
}}
clearable
/>
</Group>
);
}

View File

@@ -1,10 +1,10 @@
import { Flex, Grid, Select, Stack, Text, TextInput, Title } from "@mantine/core"; import { Flex, Grid, Select, Stack, Text, TextInput, Title } from "@mantine/core";
import { t } from "../../config/i18n";
import { IconUser } from "@tabler/icons-react"; import { IconUser } from "@tabler/icons-react";
import ShipmentCard from "../../components/ShipmentCard"; import { getForm } from "../../../services/api";
import { getForm } from "../../services/api"; import { t } from "../../../config/i18n";
import ShipmentCard from "../../../components/ShipmentCard";
export function ContractForm() { export function ReadForm() {
const { isPending, error, data } = getForm(1); const { isPending, error, data } = getForm(1);
console.log(isPending, error, data); console.log(isPending, error, data);
return ( return (
@@ -14,7 +14,7 @@ export function ContractForm() {
align={"flex-start"} align={"flex-start"}
direction={"column"} direction={"column"}
> >
<Stack> {/* <Stack>
<Title>{t("form contract")}</Title> <Title>{t("form contract")}</Title>
<Text>{t("contract description that is rather long to show how text will be displayed even with unnecessary elements like this end of sentence")}</Text> <Text>{t("contract description that is rather long to show how text will be displayed even with unnecessary elements like this end of sentence")}</Text>
</Stack> </Stack>
@@ -73,7 +73,7 @@ export function ContractForm() {
unit: "piece" unit: "piece"
}} }}
/> />
</Stack> </Stack> */}
</Flex> </Flex>
); );
} }

View File

@@ -0,0 +1,92 @@
import { Stack, Loader, Text, Title, Paper, Group, Badge, ActionIcon, Grid, Flex, Select, MultiSelect, Tooltip } from "@mantine/core";
import { getForms, type Form } from "../../services/api";
import { t } from "../../config/i18n";
import { Link, useSearchParams } from "react-router";
import { IconPlus } from "@tabler/icons-react";
import { useCallback, useMemo } from "react";
import { FilterForms } from "./FilterForms";
export function Forms() {
const [ searchParams, setSearchParams ] = useSearchParams();
const { isPending, error, data } = getForms(searchParams);
const { data: allForms } = getForms();
const seasons = useMemo(() => {
return allForms?.map((form: Form) => (form.season))
.filter((season, index, array) => array.indexOf(season) === index)
}, [allForms])
const productors = useMemo(() => {
return allForms?.map((form: Form) => (form.productor.name))
.filter((productor, index, array) => array.indexOf(productor) === index)
}, [allForms])
const onFilterChange = useCallback((values: string[], filter: string) => {
setSearchParams(prev => {
const params = new URLSearchParams(prev);
params.delete(filter)
values.forEach(value => {
params.append(filter, value);
});
return params;
});
}, [searchParams, setSearchParams])
if (!data || isPending)
return (<Loader color="blue"/>);
return (
<Stack w={{base: "100%", sm: "50%", lg: "60%"}}>
<Group justify="space-between">
<Title order={1}>{t("All forms")}</Title>
<Tooltip label={t("create new form")}>
<ActionIcon
size="xl"
component={Link}
to="/forms/create"
>
<IconPlus/>
</ActionIcon>
</Tooltip>
</Group>
<FilterForms
productors={productors || []}
seasons={seasons || []}
filters={searchParams}
onFilterChange={onFilterChange}
/>
<Flex gap="md" wrap="wrap" justify="center">
{
data?.map((form: Form) => (
<Paper
key={form.id}
shadow="xl"
p="xl"
maw={{base: "100vw", md: "30vw", lg:"15vw"}}
component={Link}
to={`/form/${form.id}`}
>
<Group justify="space-between" wrap="nowrap">
<Title
order={3}
textWrap="wrap"
lineClamp={1}
>
{form.name}
</Title>
<Badge>{form.season}</Badge>
</Group>
<Group justify="space-between">
<Text>{form.productor.name}</Text>
<Text>{form.referer.name}</Text>
</Group>
</Paper>
))
}
</Flex>
</Stack>
);
}

View File

@@ -4,7 +4,10 @@ import {
import Root from "./root"; import Root from "./root";
import { Home } from "./pages/Home"; import { Home } from "./pages/Home";
import { ContractForm } from "./pages/ContractForm"; import { Forms } from "./pages/Forms";
import { ReadForm } from "./pages/Forms/ReadForm"
import { CreateForms } from "./pages/Forms/CreateForm";
// import { CreateForms } from "./pages/Forms/CreateForm";
export const router = createBrowserRouter([ export const router = createBrowserRouter([
{ {
@@ -13,7 +16,9 @@ export const router = createBrowserRouter([
// errorElement: <NotFound />, // errorElement: <NotFound />,
children: [ children: [
{ index: true, Component: Home }, { index: true, Component: Home },
{ path: "/forms", Component: ContractForm }, { path: "/forms", Component: Forms },
{ path: "/forms/create", Component: CreateForms },
{ path: "/form/:id", Component: ReadForm },
], ],
}, },
]); ]);

View File

@@ -1,10 +1,168 @@
import { useQuery } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient,type UseQueryResult } from "@tanstack/react-query";
import { Config } from "../config/config";
export function getForm(id: number) { export type Productor = {
return useQuery({ id: number;
name: string;
address: string;
payment: string;
}
export type Shipment = {
name: string;
date: string;
id: number;
}
export type ShipmentCreate = {
name: string;
date: string;
product_ids?: number[];
}
export type Form = {
id: number;
name: string;
season: string;
start: string;
end: string;
productor: Productor;
referer: User;
shipments: Shipment[];
}
export type FormCreate = {
name: string;
season: string;
start: string;
end: string;
productor_id: number;
referer_id: number;
shipments: Shipment[];
}
export type Product = {
id: number;
productor: Productor;
name: string;
unit: number;
price: number;
priceKg: number | null;
weight: number;
type: number;
}
export type User = {
id: number;
name: string;
email: string;
products: Product[];
}
export function getForm(id: number): UseQueryResult<Form, Error> {
return useQuery<Form>({
queryKey: ['form'], queryKey: ['form'],
queryFn: () => ( queryFn: () => (
fetch(`http://localhost:8000/forms/${id}`).then((res) => res.json()) fetch(`${Config.backend_uri}/forms/${id}`).then((res) => res.json())
), ),
}); });
} }
export function getForms(filters?: URLSearchParams): UseQueryResult<Form[], Error> {
const queryString = filters?.toString()
return useQuery<Form[]>({
queryKey: ['forms', queryString],
queryFn: () => (
fetch(`${Config.backend_uri}/forms${filters ? `?${queryString}` : ""}`)
.then((res) => res.json())
),
});
}
export function getShipments() {
return useQuery<Shipment[]>({
queryKey: ['shipments'],
queryFn: () => (
fetch(`${Config.backend_uri}/shipments`)
.then((res) => res.json())
),
});
}
export function getProductors() {
return useQuery<Productor[]>({
queryKey: ['productors'],
queryFn: () => (
fetch(`${Config.backend_uri}/productors`)
.then((res) => res.json())
),
});
}
export function getUsers() {
return useQuery<User[]>({
queryKey: ['users'],
queryFn: () => (
fetch(`${Config.backend_uri}/users`)
.then((res) => res.json())
),
});
}
export function createShipment() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (newShipment: ShipmentCreate) => {
return fetch(`${Config.backend_uri}/shipments`, {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(newShipment),
});
},
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ['shipments'] })
}
})
}
export function createProductor() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (newProductor: Productor) => {
return fetch(`${Config.backend_uri}/productors`, {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(newProductor),
});
},
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ['productors'] })
}
})
}
export function createForm() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (newForm: FormCreate) => {
return fetch(`${Config.backend_uri}/forms`, {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(newForm),
});
},
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ['forms'] })
}
})
}

View File

@@ -4,4 +4,9 @@ import react from '@vitejs/plugin-react'
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
server: {
watch: {
usePolling: true, // Enable polling for file changes
},
}
}) })