Forced merge: merge ai-dev into master
This commit is contained in:
commit
26a02431a3
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
}
|
||||
|
||||
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 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;
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user