From b88bdc08963325fb350f669c9576ad86d14b1a13 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 15 Jan 2026 09:06:45 +0000 Subject: [PATCH] Tables --- frontend/src/menuAside.ts | 7 +- .../pages/reservations/reservations-new.tsx | 5 +- frontend/src/pages/tables/index.tsx | 122 ++++++++++++++++++ .../stores/reservations/reservationsSlice.ts | 8 +- frontend/src/stores/tables/tablesSlice.ts | 8 +- 5 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 frontend/src/pages/tables/index.tsx diff --git a/frontend/src/menuAside.ts b/frontend/src/menuAside.ts index 010d528..cd6344c 100644 --- a/frontend/src/menuAside.ts +++ b/frontend/src/menuAside.ts @@ -7,6 +7,11 @@ const menuAside: MenuAsideItem[] = [ icon: icon.mdiViewDashboardOutline, label: 'Dashboard', }, + { + href: '/tables', + label: 'Table Management', + icon: icon.mdiTableChair, + }, { href: '/users/users-list', @@ -50,7 +55,7 @@ const menuAside: MenuAsideItem[] = [ }, { href: '/tables/tables-list', - label: 'Tables', + label: 'Table List', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore icon: 'mdiSeat' in icon ? icon['mdiSeat' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, diff --git a/frontend/src/pages/reservations/reservations-new.tsx b/frontend/src/pages/reservations/reservations-new.tsx index 50671d0..9bc8ba8 100644 --- a/frontend/src/pages/reservations/reservations-new.tsx +++ b/frontend/src/pages/reservations/reservations-new.tsx @@ -184,7 +184,7 @@ const ReservationsNew = () => { // get from url params - const { dateRangeStart, dateRangeEnd } = router.query + const { dateRangeStart, dateRangeEnd, tableId } = router.query const handleSubmit = async (data) => { @@ -206,11 +206,12 @@ const ReservationsNew = () => { - dateRangeStart && dateRangeEnd ? + (dateRangeStart && dateRangeEnd) || tableId ? { ...initialValues, start_at: moment(dateRangeStart).format('YYYY-MM-DDTHH:mm'), end_at: moment(dateRangeEnd).format('YYYY-MM-DDTHH:mm'), + table: tableId, } : initialValues } diff --git a/frontend/src/pages/tables/index.tsx b/frontend/src/pages/tables/index.tsx new file mode 100644 index 0000000..9582984 --- /dev/null +++ b/frontend/src/pages/tables/index.tsx @@ -0,0 +1,122 @@ + +import { useState, useEffect } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import type { ReactElement } from 'react' +import { useRouter } from 'next/router' +import Link from 'next/link' +import Head from 'next/head' + +import { mdiTable } from '@mdi/js' +import CardBox from '../../components/CardBox' +import LayoutAuthenticated from '../../layouts/Authenticated' +import SectionMain from '../../components/SectionMain' +import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton' +import { getPageTitle } from '../../config' +import { fetchTables } from '../../stores/tables/tablesSlice' +import { fetchReservations } from '../../stores/reservations/reservationsSlice' + +const TableStatus = { + Available: 'Available', + Reserved: 'Reserved', + Occupied: 'Occupied', +} + +const TableCard = ({ table, status }) => { + const router = useRouter() + + const getStatusColor = () => { + switch (status) { + case TableStatus.Available: + return 'bg-green-500' + case TableStatus.Reserved: + return 'bg-orange-500' + case TableStatus.Occupied: + return 'bg-red-500' + default: + return 'bg-gray-400' + } + } + + const handleTableClick = () => { + if (status === TableStatus.Available) { + router.push(`/reservations/reservations-new?tableId=${table.id}`) + } else { + // Find reservation and show details, for now just log + console.log(`Table ${table.number} is ${status}`) + } + } + + return ( +
+
Table {table.number}
+
{table.capacity} Seats
+
{status}
+
+ ) +} + +const TablesPage = () => { + const dispatch = useDispatch() + const router = useRouter() + + const tables = useSelector((state) => state.tables.tables) + const reservations = useSelector((state) => state.reservations.reservations) + + const [tableStatuses, setTableStatuses] = useState({}) + + useEffect(() => { + dispatch(fetchTables()) + dispatch(fetchReservations()) + }, [dispatch]) + + useEffect(() => { + const statuses = {} + const now = new Date() + + tables.forEach((table) => { + const currentReservation = reservations.find( + (r) => + r.tableId === table.id && + new Date(r.startTime) <= now && + new Date(r.endTime) > now, + ) + + if (currentReservation) { + if (currentReservation.status === 'Confirmed') { + statuses[table.id] = TableStatus.Occupied + } else { + statuses[table.id] = TableStatus.Reserved + } + } else { + statuses[table.id] = TableStatus.Available + } + }) + setTableStatuses(statuses) + }, [tables, reservations]) + + return ( + <> + + {getPageTitle('Tables')} + + + + +
+ {tables.map((table) => ( + + ))} +
+
+ + ) +} + +TablesPage.getLayout = function getLayout(page: ReactElement) { + return {page} +} + +export default TablesPage diff --git a/frontend/src/stores/reservations/reservationsSlice.ts b/frontend/src/stores/reservations/reservationsSlice.ts index 36bf862..1f52b84 100644 --- a/frontend/src/stores/reservations/reservationsSlice.ts +++ b/frontend/src/stores/reservations/reservationsSlice.ts @@ -28,7 +28,7 @@ const initialState: MainState = { }, } -export const fetch = createAsyncThunk('reservations/fetch', async (data: any) => { +export const fetchReservations = createAsyncThunk('reservations/fetch', async (data: any = {}) => { const { id, query } = data const result = await axios.get( `reservations${ @@ -132,16 +132,16 @@ export const reservationsSlice = createSlice({ }, }, extraReducers: (builder) => { - builder.addCase(fetch.pending, (state) => { + builder.addCase(fetchReservations.pending, (state) => { state.loading = true resetNotify(state); }) - builder.addCase(fetch.rejected, (state, action) => { + builder.addCase(fetchReservations.rejected, (state, action) => { state.loading = false rejectNotify(state, action); }) - builder.addCase(fetch.fulfilled, (state, action) => { + builder.addCase(fetchReservations.fulfilled, (state, action) => { if (action.payload.rows && action.payload.count >= 0) { state.reservations = action.payload.rows; state.count = action.payload.count; diff --git a/frontend/src/stores/tables/tablesSlice.ts b/frontend/src/stores/tables/tablesSlice.ts index 4d6a929..8cc4f71 100644 --- a/frontend/src/stores/tables/tablesSlice.ts +++ b/frontend/src/stores/tables/tablesSlice.ts @@ -28,7 +28,7 @@ const initialState: MainState = { }, } -export const fetch = createAsyncThunk('tables/fetch', async (data: any) => { +export const fetchTables = createAsyncThunk('tables/fetch', async (data: any = {}) => { const { id, query } = data const result = await axios.get( `tables${ @@ -132,16 +132,16 @@ export const tablesSlice = createSlice({ }, }, extraReducers: (builder) => { - builder.addCase(fetch.pending, (state) => { + builder.addCase(fetchTables.pending, (state) => { state.loading = true resetNotify(state); }) - builder.addCase(fetch.rejected, (state, action) => { + builder.addCase(fetchTables.rejected, (state, action) => { state.loading = false rejectNotify(state, action); }) - builder.addCase(fetch.fulfilled, (state, action) => { + builder.addCase(fetchTables.fulfilled, (state, action) => { if (action.payload.rows && action.payload.count >= 0) { state.tables = action.payload.rows; state.count = action.payload.count;