diff --git a/README.md b/README.md index 729ed5f..209176e 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,15 @@ ## Development ```bash -docker compose -f docker-compose.yaml.prod up -d --build +docker compose up -d --build docker compose exec -it backend python mange.py migrate docker compose exec -it backend python mange.py createsuperuser ``` Po czym -- backend jest dostępny na porcie ```8000``` -- frontend jest dostępny na porcie ```3000``` + +- backend jest dostępny na porcie ``8000`` +- frontend jest dostępny na porcie ``3000`` ## Produkcja @@ -21,4 +22,4 @@ docker compose -f docker-compose.yaml.prod exec -it backend python mange.py crea docker compose -f docker-compose.yaml.prod exec -it backend python manage.py collectstatic ``` -Po czym aplikacja jest dostępna na porcie ```8080``` \ No newline at end of file +Po czym aplikacja jest dostępna na porcie ``8080`` diff --git a/backend/app/models.py b/backend/app/models.py index ef468e3..08db56d 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -29,6 +29,7 @@ class Meal(models.Model): class Meta: verbose_name = _('Danie') verbose_name_plural = _('Dania') + ordering = ['name',] class Order(models.Model): diff --git a/backend/backend/urls.py b/backend/backend/urls.py index 12ff7c0..d51bbd7 100644 --- a/backend/backend/urls.py +++ b/backend/backend/urls.py @@ -55,6 +55,8 @@ class MealSerializer(serializers.ModelSerializer): class MealViewSet(viewsets.ModelViewSet): queryset = Meal.objects.all() serializer_class = MealSerializer + filter_backends = [filters.OrderingFilter] + ordering = ['name'] class OrderSerializer(serializers.ModelSerializer): @@ -88,9 +90,9 @@ class OrderViewSet(viewsets.ModelViewSet): ordering = ['-created_on'] def get_queryset(self): - five_minutes_ago = timezone.now() + datetime.timedelta(minutes=-5) + five_minutes_ago = timezone.now() + datetime.timedelta(minutes=-15) return Order.objects.filter( - ~Q(status=Order.StatusChoices.FINALIZED) + ~Q(status__gte=Order.StatusChoices.READY) | Q(updated_on__gte=five_minutes_ago) ) diff --git a/docker-compose.yaml.prod b/docker-compose.yaml.prod index 90b2c4f..af2466d 100644 --- a/docker-compose.yaml.prod +++ b/docker-compose.yaml.prod @@ -5,8 +5,6 @@ services: restart: unless-stopped volumes: - django_static_prod:/usr/src/app/static - ports: - - "8000:8000" environment: SECRET_KEY: ${SECRET_KEY} DEBUG: False @@ -24,8 +22,6 @@ services: restart: unless-stopped volumes: - ./frontend:/app - ports: - - "3000:3000" redis: restart: unless-stopped diff --git a/frontend/app/globals.css b/frontend/app/globals.css index 58bf8a3..a69bd9c 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -16,6 +16,10 @@ textarea { @apply p-1 ps-2 transition ease-in-out focus:outline-none border-b-2 border-b-gray-400 mb-2 focus:bg-gray-100 focus:border-gray-600 appearance-none; } +.error { + @apply !bg-red-200 ring-2 ring-red-400; +} + form#form select { @apply p-2 ps-2 rounded-md bg-gray-100; } diff --git a/frontend/app/kitchen/page.tsx b/frontend/app/kitchen/page.tsx index 6e3b10b..fb8b9e8 100644 --- a/frontend/app/kitchen/page.tsx +++ b/frontend/app/kitchen/page.tsx @@ -84,10 +84,10 @@ export default function Home() { } className="form border-2 my-2 border-gray-500" > - <option value="1">Zamówienie złożone</option> + {/* <option value="1">Zamówienie złożone</option> */} <option value="2">Zamówienie w trakcie przygotowywania</option> <option value="3">Zamówienie gotowe</option> - <option value="4">Zamówienie zrealizowane</option> + {/* <option value="4">Zamówienie zrealizowane</option> */} </select> </h3> <div className=""> @@ -103,7 +103,7 @@ export default function Home() { onChange={(e) => update_finished(oi, i, e.target.checked)} /> <div> - <p className="text-lg">{dish.item} - {dish.takeout ? 'Na wynos' : 'Na miejscu'}</p> + <p className="text-lg">{dish.item}{dish.times > 1 ? ` - x${dish.times}`: ''} - {dish.takeout ? 'Na wynos' : 'Na miejscu'}</p> <div>{dish.additional_info}</div> </div> </li> diff --git a/frontend/app/tools.ts b/frontend/app/tools.ts index 1f0e2c4..9763cdd 100644 --- a/frontend/app/tools.ts +++ b/frontend/app/tools.ts @@ -3,6 +3,7 @@ export type Dish = { additional_info: string; finished: boolean; takeout: boolean; + times: number; }; export type Order = { @@ -29,6 +30,8 @@ export function getOrderBg(order: Order): string { } export function getDishBg(dish: Dish): string { + if (dish.finished) return 'bg-red-200 border-red-400'; + return dish.takeout ? 'bg-blue-300 border-blue-500' : 'bg-green-300 border-green-500'; diff --git a/frontend/app/waiter/[id]/new/page.tsx b/frontend/app/waiter/[id]/new/page.tsx index 486464b..3cf77a6 100644 --- a/frontend/app/waiter/[id]/new/page.tsx +++ b/frontend/app/waiter/[id]/new/page.tsx @@ -8,10 +8,15 @@ import { fetcher } from '@/app/api/tools'; import useSWR from 'swr'; import { Dish } from '@/app/tools'; +type FormDish = Dish & { + filter: string; +}; + type FormValues = { + status: number; client: string; realization_time: string; - data: Dish[]; + data: FormDish[]; }; export default function App() { @@ -24,22 +29,37 @@ export default function App() { control, handleSubmit, formState: { errors }, + getValues, + setValue, + watch, } = useForm<FormValues>({ defaultValues: { + status: 2, client: '', - realization_time: new Date().toLocaleTimeString('pl-PL', { hour: '2-digit', minute: '2-digit' }), + realization_time: new Date().toLocaleTimeString('pl-PL', { + hour: '2-digit', + minute: '2-digit', + }), data: [ - { item: 'Inne', additional_info: '', finished: false, takeout: false }, + { + item: '', + additional_info: '', + finished: false, + takeout: false, + times: 1, + filter: '', + }, ], }, mode: 'all', + criteriaMode: 'all', + shouldFocusError: true }); const { fields, append, remove } = useFieldArray({ name: 'data', control, }); const onSubmit = (data: FormValues) => { - console.log(data); fetch('/api/orders', { method: 'POST', body: JSON.stringify({ waiter: id, ...data }), @@ -54,6 +74,8 @@ export default function App() { if (error) return <div>Błąd przy ładowaniu danych</div>; if (isLoading) return <div>Ładowanie</div>; + watch('data') + return ( <> <form @@ -95,21 +117,56 @@ export default function App() { required: false, })} /> - <div className="basis-1/3"> + <div className="basis-1/3 gap-2"> <label htmlFor={`data.${index}.item`}>Danie</label> + <input + {...register(`data.${index}.filter` as const, { + required: false, + })} + placeholder='Wyszukaj danie' + className={errors?.data?.[index]?.filter ? 'error' : ''} + onChange={(e) => setValue(`data.${index}.item`, data + ?.filter((data: { id: number; name: string }) => + data.name + .toLowerCase() + .includes( + e.target.value.toLowerCase() + ) + )[0].name, {shouldValidate: true})} + /> <select {...register(`data.${index}.item` as const, { required: true, })} className={errors?.data?.[index]?.item ? 'error' : ''} > - <option value="Inne">Inne</option> - {data?.map((data: { id: number; name: string }) => ( - <option key={data.name} value={data.name}> - {data.name} - </option> - ))} + {!!getValues(`data.${index}.filter`) ? ( + null + ) : ( + <option value="Inne">Inne</option> + )} + {data + ?.filter((data: { id: number; name: string }) => + data.name + .toLowerCase() + .includes( + getValues(`data.${index}.filter`).toLowerCase() + ) + ) + .map((data: { id: number; name: string }) => ( + <option key={data.name} value={data.name}> + {data.name} + </option> + ))} </select> + <label htmlFor={`data.${index}.times`}>Ilość</label> + <input + {...register(`data.${index}.times` as const, { + required: false, + })} + type="number" + className={errors?.data?.[index]?.times ? 'error' : ''} + /> <section className="flex flex-row my-2 gap-2"> <label htmlFor={`data.${index}.takeout`}>Na wynos</label> <input @@ -157,6 +214,8 @@ export default function App() { additional_info: '', finished: false, takeout: false, + times: 1, + filter: '', }) } > diff --git a/frontend/app/waiter/[id]/page.tsx b/frontend/app/waiter/[id]/page.tsx index 05f395b..9a9ef93 100644 --- a/frontend/app/waiter/[id]/page.tsx +++ b/frontend/app/waiter/[id]/page.tsx @@ -9,7 +9,7 @@ import { OrderID } from '@/app/components'; export default function Home() { const { id } = useParams(); - const { data, error, isLoading } = useSWR( + const { data, error, isLoading, mutate } = useSWR( `/api/orders?waiter=${id}`, fetcher, { @@ -17,6 +17,30 @@ export default function Home() { } ); + const update_finished = (o: number, i: number, v: boolean) => { + const d = data; + d[o].data[i].finished = v; + fetch(`/api/orders?id=${d[o].id}`, { + method: 'PUT', + body: JSON.stringify(d[o]), + headers: { + 'Content-Type': 'application/json', + }, + }).then(() => mutate(d)); + }; + + const update_status = (o: number, v: number) => { + const d = data; + d[o].status = v; + fetch(`/api/orders?id=${d[o].id}`, { + method: 'PUT', + body: JSON.stringify(d[o]), + headers: { + 'Content-Type': 'application/json', + }, + }).then(() => mutate(d)); + }; + if (error) return <div>Błąd przy ładowaniu danych</div>; if (isLoading) return <div>Ładowanie</div>; @@ -29,25 +53,42 @@ export default function Home() { </p> <p className="text-4xl">Zamówienia:</p> <ul className="max-w-screen-md w-full"> - {data?.map((order: Order) => ( + {data?.map((order: Order, oi: number) => ( <li key={order.id} className={`m-2 p-2 rounded-md border-2 ${getOrderBg(order)}`} > <OrderID order={order} /> - <h2 className="text-xl">Kelner: {order.waiter_name}</h2> + <h2 className="text-xl">{order.waiter_name}</h2> { (order.client) ? <h2 className="text-xl">Klient: {order.client}</h2> : <></>} - <h3>{order.status_name}</h3> + <h3> + <select + value={order.status} + onChange={(e) => + update_status(oi, e.target.value as unknown as number) + } + className="form border-2 my-2 border-gray-500" + > + {/* <option value="1">Zamówienie złożone</option> */} + <option value="2">Zamówienie w trakcie przygotowywania</option> + <option value="3">Zamówienie gotowe</option> + {/* <option value="4">Zamówienie zrealizowane</option> */} + </select> + </h3> <div className=""> <ul> - {order.data.map((dish) => ( + {order.data.map((dish, i) => ( <li key={dish.item} className={`p-2 m-2 flex gap-4 border-2 ${getDishBg(dish)}`} > - <input type="checkbox" checked={dish.finished} readOnly /> + <input + type="checkbox" + checked={dish.finished} + onChange={(e) => update_finished(oi, i, e.target.checked)} + /> <div> - <p className="text-lg">{dish.item} - {dish.takeout ? 'Na wynos' : 'Na miejscu'}</p> + <p className="text-lg">{dish.item}{dish.times > 1 ? ` - x${dish.times}`: ''} - {dish.takeout ? 'Na wynos' : 'Na miejscu'}</p> <div>{dish.additional_info}</div> </div> </li>