Forced merge: merge ai-dev into master
This commit is contained in:
commit
26a02431a3
@ -7,6 +7,11 @@ const menuAside: MenuAsideItem[] = [
|
|||||||
icon: icon.mdiViewDashboardOutline,
|
icon: icon.mdiViewDashboardOutline,
|
||||||
label: 'Dashboard',
|
label: 'Dashboard',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
href: '/tables',
|
||||||
|
label: 'Table Management',
|
||||||
|
icon: icon.mdiTableChair,
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
href: '/users/users-list',
|
href: '/users/users-list',
|
||||||
@ -50,7 +55,7 @@ const menuAside: MenuAsideItem[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/tables/tables-list',
|
href: '/tables/tables-list',
|
||||||
label: 'Tables',
|
label: 'Table List',
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
icon: 'mdiSeat' in icon ? icon['mdiSeat' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
icon: 'mdiSeat' in icon ? icon['mdiSeat' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||||
|
|||||||
@ -184,7 +184,7 @@ const ReservationsNew = () => {
|
|||||||
|
|
||||||
|
|
||||||
// get from url params
|
// get from url params
|
||||||
const { dateRangeStart, dateRangeEnd } = router.query
|
const { dateRangeStart, dateRangeEnd, tableId } = router.query
|
||||||
|
|
||||||
|
|
||||||
const handleSubmit = async (data) => {
|
const handleSubmit = async (data) => {
|
||||||
@ -206,11 +206,12 @@ const ReservationsNew = () => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
dateRangeStart && dateRangeEnd ?
|
(dateRangeStart && dateRangeEnd) || tableId ?
|
||||||
{
|
{
|
||||||
...initialValues,
|
...initialValues,
|
||||||
start_at: moment(dateRangeStart).format('YYYY-MM-DDTHH:mm'),
|
start_at: moment(dateRangeStart).format('YYYY-MM-DDTHH:mm'),
|
||||||
end_at: moment(dateRangeEnd).format('YYYY-MM-DDTHH:mm'),
|
end_at: moment(dateRangeEnd).format('YYYY-MM-DDTHH:mm'),
|
||||||
|
table: tableId,
|
||||||
} : initialValues
|
} : initialValues
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
122
frontend/src/pages/tables/index.tsx
Normal file
122
frontend/src/pages/tables/index.tsx
Normal file
@ -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 (
|
||||||
|
<div
|
||||||
|
className={`p-4 rounded-lg text-white cursor-pointer transform hover:scale-105 transition-transform ${getStatusColor()}`}
|
||||||
|
onClick={handleTableClick}
|
||||||
|
>
|
||||||
|
<div className="text-lg font-bold">Table {table.number}</div>
|
||||||
|
<div>{table.capacity} Seats</div>
|
||||||
|
<div className="mt-2">{status}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle('Tables')}</title>
|
||||||
|
</Head>
|
||||||
|
<SectionMain>
|
||||||
|
<SectionTitleLineWithButton icon={mdiTable} title="Tables" main />
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-6">
|
||||||
|
{tables.map((table) => (
|
||||||
|
<TableCard key={table.id} table={table} status={tableStatuses[table.id]} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</SectionMain>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TablesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutAuthenticated>{page}</LayoutAuthenticated>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TablesPage
|
||||||
@ -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 { id, query } = data
|
||||||
const result = await axios.get(
|
const result = await axios.get(
|
||||||
`reservations${
|
`reservations${
|
||||||
@ -132,16 +132,16 @@ export const reservationsSlice = createSlice({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
builder.addCase(fetch.pending, (state) => {
|
builder.addCase(fetchReservations.pending, (state) => {
|
||||||
state.loading = true
|
state.loading = true
|
||||||
resetNotify(state);
|
resetNotify(state);
|
||||||
})
|
})
|
||||||
builder.addCase(fetch.rejected, (state, action) => {
|
builder.addCase(fetchReservations.rejected, (state, action) => {
|
||||||
state.loading = false
|
state.loading = false
|
||||||
rejectNotify(state, action);
|
rejectNotify(state, action);
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.addCase(fetch.fulfilled, (state, action) => {
|
builder.addCase(fetchReservations.fulfilled, (state, action) => {
|
||||||
if (action.payload.rows && action.payload.count >= 0) {
|
if (action.payload.rows && action.payload.count >= 0) {
|
||||||
state.reservations = action.payload.rows;
|
state.reservations = action.payload.rows;
|
||||||
state.count = action.payload.count;
|
state.count = action.payload.count;
|
||||||
|
|||||||
@ -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 { id, query } = data
|
||||||
const result = await axios.get(
|
const result = await axios.get(
|
||||||
`tables${
|
`tables${
|
||||||
@ -132,16 +132,16 @@ export const tablesSlice = createSlice({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
builder.addCase(fetch.pending, (state) => {
|
builder.addCase(fetchTables.pending, (state) => {
|
||||||
state.loading = true
|
state.loading = true
|
||||||
resetNotify(state);
|
resetNotify(state);
|
||||||
})
|
})
|
||||||
builder.addCase(fetch.rejected, (state, action) => {
|
builder.addCase(fetchTables.rejected, (state, action) => {
|
||||||
state.loading = false
|
state.loading = false
|
||||||
rejectNotify(state, action);
|
rejectNotify(state, action);
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.addCase(fetch.fulfilled, (state, action) => {
|
builder.addCase(fetchTables.fulfilled, (state, action) => {
|
||||||
if (action.payload.rows && action.payload.count >= 0) {
|
if (action.payload.rows && action.payload.count >= 0) {
|
||||||
state.tables = action.payload.rows;
|
state.tables = action.payload.rows;
|
||||||
state.count = action.payload.count;
|
state.count = action.payload.count;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user