From c3f306575fda9528e7dd7bf31282c7a1439f46e0 Mon Sep 17 00:00:00 2001 From: yaemiku Date: Sat, 4 Jan 2025 14:57:53 +0100 Subject: [PATCH] color changes, client, realization time --- ...al_options_alter_order_options_and_more.py | 36 ++++++++ ...der_realization_time_alter_order_status.py | 23 ++++++ backend/app/models.py | 33 +++++--- backend/backend/urls.py | 2 + frontend/Dockerfile.prod | 27 +++--- frontend/app/api/tools.ts | 3 +- frontend/app/components.tsx | 15 ++++ frontend/app/globals.css | 2 +- frontend/app/kitchen/page.tsx | 55 +++---------- frontend/app/layout.tsx | 11 +-- frontend/app/page.tsx | 1 - frontend/app/tools.ts | 35 ++++++++ frontend/app/waiter/[id]/new/page.tsx | 64 +++++++++++---- frontend/app/waiter/[id]/page.tsx | 82 +++++++------------ frontend/next.config.ts | 2 +- 15 files changed, 247 insertions(+), 144 deletions(-) create mode 100644 backend/app/migrations/0003_alter_meal_options_alter_order_options_and_more.py create mode 100644 backend/app/migrations/0004_alter_order_realization_time_alter_order_status.py create mode 100644 frontend/app/components.tsx create mode 100644 frontend/app/tools.ts diff --git a/backend/app/migrations/0003_alter_meal_options_alter_order_options_and_more.py b/backend/app/migrations/0003_alter_meal_options_alter_order_options_and_more.py new file mode 100644 index 0000000..61c2c2e --- /dev/null +++ b/backend/app/migrations/0003_alter_meal_options_alter_order_options_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 5.1.4 on 2024-12-21 13:34 + +import app.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0002_meal'), + ] + + operations = [ + migrations.AlterModelOptions( + name='meal', + options={'verbose_name': 'Danie', 'verbose_name_plural': 'Dania'}, + ), + migrations.AlterModelOptions( + name='order', + options={'verbose_name': 'Zamówienie', 'verbose_name_plural': 'Zamówienia'}, + ), + migrations.AlterModelOptions( + name='waiter', + options={'verbose_name': 'Kelner', 'verbose_name_plural': 'Kelnerzy'}, + ), + migrations.AddField( + model_name='order', + name='client', + field=models.CharField(blank=True, max_length=250, null=True, verbose_name='Klient'), + ), + migrations.AddField( + model_name='order', + name='realization_time', + field=models.DateTimeField(default=app.models.default_realization_time, verbose_name='Godzina realizacji'), + ), + ] diff --git a/backend/app/migrations/0004_alter_order_realization_time_alter_order_status.py b/backend/app/migrations/0004_alter_order_realization_time_alter_order_status.py new file mode 100644 index 0000000..bdd3189 --- /dev/null +++ b/backend/app/migrations/0004_alter_order_realization_time_alter_order_status.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.4 on 2024-12-21 14:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0003_alter_meal_options_alter_order_options_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='order', + name='realization_time', + field=models.CharField(blank=True, max_length=250, null=True, verbose_name='Godzina realizacji'), + ), + migrations.AlterField( + model_name='order', + name='status', + field=models.PositiveSmallIntegerField(choices=[(1, 'Zamówienie złożone'), (2, 'Zamówienie w trakcie przygotowywania'), (3, 'Zamówienie gotowe'), (4, 'Zamówienie zrealizowane')], default=1, verbose_name='Status zamówienia'), + ), + ] diff --git a/backend/app/models.py b/backend/app/models.py index fdea81f..ef468e3 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -1,9 +1,14 @@ from django.db import models from django.utils.translation import gettext_lazy as _ +from django.utils import timezone # Create your models here. +def default_realization_time(): + return timezone.now() # + datetime.timedelta(hours=1) + + class Waiter(models.Model): name = models.CharField(_('Imię i nazwisko'), max_length=120) @@ -11,8 +16,8 @@ class Waiter(models.Model): return self.name class Meta: - verbose_name = _("Kelner") - verbose_name_plural = _("Kelnerzy") + verbose_name = _('Kelner') + verbose_name_plural = _('Kelnerzy') class Meal(models.Model): @@ -22,23 +27,31 @@ class Meal(models.Model): return self.name class Meta: - verbose_name = _("Danie") - verbose_name_plural = _("Dania") + verbose_name = _('Danie') + verbose_name_plural = _('Dania') class Order(models.Model): created_on = models.DateTimeField(_('Utworzono'), auto_now_add=True) updated_on = models.DateTimeField(_('Zaktualizowano'), auto_now=True) - waiter = models.ForeignKey( - Waiter, models.SET_NULL, related_name='orders', verbose_name=_('Kelner'), null=True + realization_time = models.CharField( + _('Godzina realizacji'), max_length=250, null=True, blank=True ) + waiter = models.ForeignKey( + Waiter, + models.SET_NULL, + related_name='orders', + verbose_name=_('Kelner'), + null=True, + ) + client = models.CharField(_('Klient'), max_length=250, null=True, blank=True) data = models.JSONField(_('Dane zamówienia'), default=dict) class StatusChoices(models.IntegerChoices): ORDERED = (1, _('Zamówienie złożone')) IN_PROGRESS = (2, ('Zamówienie w trakcie przygotowywania')) READY = (3, _('Zamówienie gotowe')) - FINALIZED = (4, _('Zamówienie skończone')) + FINALIZED = (4, _('Zamówienie zrealizowane')) status = models.PositiveSmallIntegerField( _('Status zamówienia'), @@ -47,8 +60,8 @@ class Order(models.Model): ) def __str__(self): - return self.id + return str(self.id) class Meta: - verbose_name = _("Zamówienie") - verbose_name_plural = _("Zamówienia") + verbose_name = _('Zamówienie') + verbose_name_plural = _('Zamówienia') diff --git a/backend/backend/urls.py b/backend/backend/urls.py index e6b6839..12ff7c0 100644 --- a/backend/backend/urls.py +++ b/backend/backend/urls.py @@ -67,6 +67,8 @@ class OrderSerializer(serializers.ModelSerializer): 'id', 'created_on', 'updated_on', + 'realization_time', + 'client', 'waiter', 'waiter_name', 'data', diff --git a/frontend/Dockerfile.prod b/frontend/Dockerfile.prod index dac69be..12b697b 100644 --- a/frontend/Dockerfile.prod +++ b/frontend/Dockerfile.prod @@ -4,7 +4,7 @@ FROM node:alpine AS base FROM base AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. RUN apk add --no-cache libc6-compat -WORKDIR /app +WORKDIR /src # Install dependencies based on the preferred package manager COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ @@ -18,14 +18,15 @@ RUN \ # Rebuild the source code only when needed FROM base AS builder -WORKDIR /app -COPY --from=deps /app/node_modules ./node_modules +WORKDIR /src +COPY --from=deps /src/node_modules ./node_modules COPY . . # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the build. -# ENV NEXT_TELEMETRY_DISABLED 1 +ENV NEXT_TELEMETRY_DISABLED=1 +ENV NEXT_PRIVATE_STANDALONE=true RUN yarn build @@ -34,16 +35,16 @@ RUN yarn build # Production image, copy all the files and run next FROM base AS runner -WORKDIR /app +WORKDIR /src -ENV NODE_ENV production +ENV NODE_ENV=production # Uncomment the following line in case you want to disable telemetry during runtime. -ENV NEXT_TELEMETRY_DISABLED 1 +ENV NEXT_TELEMETRY_DISABLED=1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs -COPY --from=builder /app/public ./public +COPY --from=builder /src/public ./public # Set the correct permission for prerender cache RUN mkdir .next @@ -51,15 +52,13 @@ RUN chown nextjs:nodejs .next # Automatically leverage output traces to reduce image size # https://nextjs.org/docs/advanced-features/output-file-tracing -COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ -COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static +COPY --from=builder --chown=nextjs:nodejs /src/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /src/.next/static ./.next/static USER nextjs EXPOSE 3000 -ENV PORT 3000 +ENV PORT=3000 # set hostname to localhost -ENV HOSTNAME "0.0.0.0" - -CMD ["node", "server.js"] +CMD HOSTNAME="0.0.0.0" node server.js diff --git a/frontend/app/api/tools.ts b/frontend/app/api/tools.ts index 49e8754..6f91dd3 100644 --- a/frontend/app/api/tools.ts +++ b/frontend/app/api/tools.ts @@ -2,7 +2,8 @@ import useSWR from 'swr' -export const fetcher = (...args) => fetch(...args).then(res => res.json()) // @ts-ignore +// @ts-expect-error ignore ...args type +export const fetcher = (...args) => fetch(...args).then(res => res.json()) export function useUser (id: number) { diff --git a/frontend/app/components.tsx b/frontend/app/components.tsx new file mode 100644 index 0000000..6d1f4e4 --- /dev/null +++ b/frontend/app/components.tsx @@ -0,0 +1,15 @@ +import { Order } from './tools'; + +export function OrderID({ order }: { order: Order }) { + return ( +

+ + {'['} + {String(order.id % 1000).padStart(3, '0')} + {']'} + {' '} + - Godzina realizacji -{' '} + {order.realization_time} +

+ ); +} diff --git a/frontend/app/globals.css b/frontend/app/globals.css index 6a0d541..58bf8a3 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -3,7 +3,7 @@ @tailwind utilities; -form#form p { +form#form p, form#form div { @apply flex flex-col; } diff --git a/frontend/app/kitchen/page.tsx b/frontend/app/kitchen/page.tsx index 80cba19..6e3b10b 100644 --- a/frontend/app/kitchen/page.tsx +++ b/frontend/app/kitchen/page.tsx @@ -2,19 +2,9 @@ import { fetcher } from '@/app/api/tools'; import useSWR from 'swr'; -import Link from 'next/link'; import { useEffect, useRef, useState } from 'react'; - -type Order = { - id: number; - created_on: string; - updated_on: string; - waiter: number; - waiter_name: string; - data: [{ item: string; additional_info: string; finished: boolean }]; - status: number; - status_name: string; -}; +import { getDishBg, getOrderBg, Order } from '../tools'; +import { OrderID } from '../components'; export default function Home() { const audioRef = useRef({} as HTMLAudioElement); @@ -25,11 +15,7 @@ export default function Home() { }); const play = () => { - if (audioRef.current) { - audioRef.current.play(); - } else { - // Throw error - } + if (audioRef.current) audioRef.current.play(); }; useEffect(() => { @@ -48,12 +34,11 @@ export default function Home() { play(); setPlayedIDs(playedIDs.concat(justPlayed)); } - }, [data]); + }, [playedIDs, data]); const update_finished = (o: number, i: number, v: boolean) => { - let d = data; + const d = data; d[o].data[i].finished = v; - console.log(v); fetch(`/api/orders?id=${d[o].id}`, { method: 'PUT', body: JSON.stringify(d[o]), @@ -64,9 +49,8 @@ export default function Home() { }; const update_status = (o: number, v: number) => { - let d = data; + const d = data; d[o].status = v; - console.log(v); fetch(`/api/orders?id=${d[o].id}`, { method: 'PUT', body: JSON.stringify(d[o]), @@ -87,36 +71,23 @@ export default function Home() { {data?.map((order: Order, oi: number) => (
  • -

    - - {'['} - {String(order.id % 1000).padStart(3, '0')} - {']'} - {' '} - - {new Date(order.updated_on).toLocaleTimeString('pl-PL')} -

    +

    {order.waiter_name}

    + { (order.client) ?

    Klient: {order.client}

    : <>}

    @@ -124,7 +95,7 @@ export default function Home() { {order.data.map((dish, i) => (
  • update_finished(oi, i, e.target.checked)} />
    -

    {dish.item}

    +

    {dish.item} - {dish.takeout ? 'Na wynos' : 'Na miejscu'}

    {dish.additional_info}
  • diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index dae43f0..78e6bb6 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -1,9 +1,8 @@ -import type { Metadata } from "next"; -import "./globals.css"; +import type { Metadata } from 'next'; +import './globals.css'; export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: 'Bistro', }; export default function RootLayout({ @@ -13,9 +12,7 @@ export default function RootLayout({ }>) { return ( - + {children} diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 4e6078a..f6208de 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -1,4 +1,3 @@ -import Image from 'next/image'; import Link from 'next/link'; export default function Home() { diff --git a/frontend/app/tools.ts b/frontend/app/tools.ts new file mode 100644 index 0000000..1f0e2c4 --- /dev/null +++ b/frontend/app/tools.ts @@ -0,0 +1,35 @@ +export type Dish = { + item: string; + additional_info: string; + finished: boolean; + takeout: boolean; +}; + +export type Order = { + id: number; + created_on: string; + updated_on: string; + realization_time: string; + client: string; + waiter: number; + waiter_name: string; + data: Dish[]; + status: number; + status_name: string; +}; + +export function getOrderBg(order: Order): string { + const nonTakeouts = order.data.filter((dish) => !dish.takeout); + + if (order.status == 4) return 'bg-red-200 border-red-400'; + + return nonTakeouts.length > 0 + ? 'bg-green-200 border-green-400' + : 'bg-blue-200 border-blue-400'; +} + +export function getDishBg(dish: Dish): string { + 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 ff43f56..486464b 100644 --- a/frontend/app/waiter/[id]/new/page.tsx +++ b/frontend/app/waiter/[id]/new/page.tsx @@ -3,16 +3,15 @@ import Link from 'next/link'; import { useParams, useRouter } from 'next/navigation'; import * as React from 'react'; -import { useForm, useFieldArray, useWatch, Control } from 'react-hook-form'; +import { useForm, useFieldArray } from 'react-hook-form'; import { fetcher } from '@/app/api/tools'; import useSWR from 'swr'; +import { Dish } from '@/app/tools'; type FormValues = { - data: { - item: string; - additional_info: string; - finished: boolean; - }[]; + client: string; + realization_time: string; + data: Dish[]; }; export default function App() { @@ -27,9 +26,13 @@ export default function App() { formState: { errors }, } = useForm({ defaultValues: { - data: [{ item: '', additional_info: '', finished: false }], + client: '', + realization_time: new Date().toLocaleTimeString('pl-PL', { hour: '2-digit', minute: '2-digit' }), + data: [ + { item: 'Inne', additional_info: '', finished: false, takeout: false }, + ], }, - mode: 'onBlur', + mode: 'all', }); const { fields, append, remove } = useFieldArray({ name: 'data', @@ -45,7 +48,7 @@ export default function App() { }, }) .then(({ status }) => console.log(status)) - .then((_) => router.push(`/waiter/${id}`)); + .then(() => router.push(`/waiter/${id}`)); }; if (error) return
    Błąd przy ładowaniu danych
    ; @@ -59,6 +62,26 @@ export default function App() { className="flex flex-col max-w-screen-md w-full my-4" >

    Nowe zamówienie

    +

    + + +

    +

    + + +

    {fields.map((field, index) => { return (
    -

    +

    -

    -

    +

    + + +
    +
    +
    @@ -101,7 +134,7 @@ export default function App() { errors?.data?.[index]?.additional_info ? 'error' : '' } /> -

    +
    {fields.length > 1 ? (

    - + + + ); } diff --git a/frontend/app/waiter/[id]/page.tsx b/frontend/app/waiter/[id]/page.tsx index 7ffe5de..05f395b 100644 --- a/frontend/app/waiter/[id]/page.tsx +++ b/frontend/app/waiter/[id]/page.tsx @@ -4,6 +4,8 @@ import { fetcher } from '@/app/api/tools'; import useSWR from 'swr'; import { useParams } from 'next/navigation'; import Link from 'next/link'; +import { getDishBg, getOrderBg, Order } from '@/app/tools'; +import { OrderID } from '@/app/components'; export default function Home() { const { id } = useParams(); @@ -27,59 +29,33 @@ export default function Home() {

    Zamówienia:

      - {data?.map( - (order: { - id: number; - created_on: string; - updated_on: string; - waiter: number; - waiter_name: string; - data: [ - { item: string; additional_info: string; finished: boolean } - ]; - status: string; - status_name: string; - }) => ( -
    • -

      - - {'['} - {String(order.id % 1000).padStart(3, '0')} - {']'} - {' '} - - {new Date(order.updated_on).toLocaleTimeString('pl-PL')} -

      -

      {order.waiter_name}

      -

      {order.status_name}

      -
      -
        - {order.data.map((dish) => ( -
      • - -
        -

        {dish.item}

        -
        {dish.additional_info}
        -
        -
      • - ))} -
      -
      -
    • - ) - )} + {data?.map((order: Order) => ( +
    • + +

      Kelner: {order.waiter_name}

      + { (order.client) ?

      Klient: {order.client}

      : <>} +

      {order.status_name}

      +
      +
        + {order.data.map((dish) => ( +
      • + +
        +

        {dish.item} - {dish.takeout ? 'Na wynos' : 'Na miejscu'}

        +
        {dish.additional_info}
        +
        +
      • + ))} +
      +
      +
    • + ))}
    ); diff --git a/frontend/next.config.ts b/frontend/next.config.ts index e9ffa30..b9f4418 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -1,7 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + output: 'standalone' }; export default nextConfig;