@@ -99,6 +184,15 @@ function WaiterFloorShell() {
}
/>
{session.error &&
}
+ {workflowState.sessionExpired && (
+
+ )}
);
}
@@ -133,45 +227,85 @@ function WaiterFloorShell() {
Waiter Floor Operations
- Protected floor workflows for assignment visibility and order submission.
+ Protected floor workflows for assignment visibility, recent activity, and order submit or update actions.
{session.error && }
- {globalError && }
+ {workflowState.error && (
+
+ Reauthenticate
+
+ ) : undefined
+ }
+ />
+ )}
-
-
- setContextId(event.target.value)}
- placeholder="Context Id"
- style={{ width: 280 }}
- />
-
+
+
+
+
+ setContextId(event.target.value)}
+ placeholder="Context Id"
+ style={{ width: 280 }}
+ />
+
+ } onClick={() => void loadAssignments()} disabled={loadingAssignments}>
+ Retry
+
+
+ {assignments ? (
+ <>
+
+ {assignments.contextId}
+ {assignments.locationId}
+ {assignments.summary}
+
+ `${record.waiterId}-${record.tableId}`}
+ dataSource={assignments.assignments}
+ columns={assignmentColumns}
+ locale={{ emptyText: 'No active waiter assignments for this context.' }}
+ />
+ >
+ ) : (
+
+ )}
- {assignments ? (
-
- {assignments.contextId}
- {assignments.summary}
-
+
+ }>
+ {recentActivity && recentActivity.recentActivity.length > 0 ? (
+ {item}}
+ />
) : (
- No assignment snapshot loaded.
+
)}
-
-
+
+
}
/>
-
+
+
- {orderResponse && (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {lastOrderResponse ? (
- {orderResponse.orderId}
- {String(orderResponse.accepted)}
- {orderResponse.message}
+ {lastOrderResponse.contextId}
+ {lastOrderResponse.orderId}
+ {String(lastOrderResponse.accepted)}
+ {lastOrderResponse.status}
+ {lastOrderResponse.summary}
+ {lastOrderResponse.processedAtUtc}
+ ) : (
+
)}
-
+
+
+
pagination={false}
- rowKey={(record) => record.orderId}
+ rowKey={(record) => `${record.kind}-${record.response.orderId}-${record.response.processedAtUtc}`}
dataSource={orderHistory}
+ locale={{ emptyText: 'No recent floor order actions yet.' }}
columns={[
- { title: 'Order Id', dataIndex: 'orderId' },
{
- title: 'Accepted',
- render: (_, record) => {String(record.accepted)}
+ title: 'Action',
+ render: (_, record) => {record.kind}
},
- { title: 'Message', dataIndex: 'message' }
+ { title: 'Order Id', render: (_, record) => record.response.orderId },
+ { title: 'Status', render: (_, record) => record.response.status },
+ { title: 'Summary', render: (_, record) => record.response.summary },
+ { title: 'Processed At', render: (_, record) => record.response.processedAtUtc }
]}
/>
-
-
+
+
}
/>
(
});
}
+export async function putJson(
+ path: string,
+ body: unknown,
+ baseUrl = getApiBaseUrl()
+): Promise {
+ return requestJson(baseUrl, path, {
+ method: 'PUT',
+ body: JSON.stringify(body)
+ });
+}
+
export async function postNoContent(
path: string,
body: unknown,
diff --git a/src/api/dashboardApi.test.ts b/src/api/dashboardApi.test.ts
index 8c54030..4e106ff 100644
--- a/src/api/dashboardApi.test.ts
+++ b/src/api/dashboardApi.test.ts
@@ -2,11 +2,12 @@ import { describe, expect, it, vi } from 'vitest';
vi.mock('./client', () => ({
getJson: vi.fn(),
- postJson: vi.fn()
+ postJson: vi.fn(),
+ putJson: vi.fn()
}));
-import { getJson, postJson } from './client';
-import { loadDashboard, submitFloorOrder } from './dashboardApi';
+import { getJson, postJson, putJson } from './client';
+import { loadDashboard, loadRecentActivity, submitFloorOrder, updateFloorOrder } from './dashboardApi';
describe('waiter floor dashboard api', () => {
it('builds encoded assignments endpoint path', async () => {
@@ -17,6 +18,14 @@ describe('waiter floor dashboard api', () => {
expect(getJson).toHaveBeenCalledWith('/api/waiter/floor/assignments?contextId=ctx%20floor%2F1');
});
+ it('builds encoded activity endpoint path', async () => {
+ vi.mocked(getJson).mockResolvedValue({ ok: true });
+
+ await loadRecentActivity('ctx floor/1');
+
+ expect(getJson).toHaveBeenCalledWith('/api/waiter/floor/activity?contextId=ctx%20floor%2F1');
+ });
+
it('posts floor order payload', async () => {
vi.mocked(postJson).mockResolvedValue({ accepted: true });
@@ -34,4 +43,21 @@ describe('waiter floor dashboard api', () => {
itemCount: 2
});
});
+
+ it('puts floor order update payload without duplicating order id in the body', async () => {
+ vi.mocked(putJson).mockResolvedValue({ accepted: true });
+
+ await updateFloorOrder({
+ contextId: 'ctx',
+ tableId: 'T-1',
+ orderId: 'ORD/1',
+ itemCount: 4
+ });
+
+ expect(putJson).toHaveBeenCalledWith('/api/waiter/floor/orders/ORD%2F1', {
+ contextId: 'ctx',
+ tableId: 'T-1',
+ itemCount: 4
+ });
+ });
});
diff --git a/src/api/dashboardApi.ts b/src/api/dashboardApi.ts
index 51c9e97..35b91ed 100644
--- a/src/api/dashboardApi.ts
+++ b/src/api/dashboardApi.ts
@@ -1,8 +1,25 @@
-import { getJson, postJson } from './client';
+import { getJson, postJson, putJson } from './client';
+
+export type WaiterAssignment = {
+ waiterId: string;
+ tableId: string;
+ status: string;
+ activeOrders: number;
+};
export type WaiterAssignmentsResponse = {
contextId: string;
+ locationId: string;
summary: string;
+ assignments: WaiterAssignment[];
+ recentActivity: string[];
+};
+
+export type WaiterRecentActivityResponse = {
+ contextId: string;
+ locationId: string;
+ summary: string;
+ recentActivity: string[];
};
export type SubmitFloorOrderRequest = {
@@ -13,15 +30,46 @@ export type SubmitFloorOrderRequest = {
};
export type SubmitFloorOrderResponse = {
+ contextId: string;
orderId: string;
accepted: boolean;
- message: string;
+ summary: string;
+ status: string;
+ processedAtUtc: string;
+};
+
+export type UpdateFloorOrderRequest = {
+ contextId: string;
+ tableId: string;
+ orderId: string;
+ itemCount: number;
+};
+
+export type UpdateFloorOrderResponse = {
+ contextId: string;
+ orderId: string;
+ accepted: boolean;
+ summary: string;
+ status: string;
+ processedAtUtc: string;
};
export async function loadDashboard(contextId: string): Promise {
return getJson(`/api/waiter/floor/assignments?contextId=${encodeURIComponent(contextId)}`);
}
+export async function loadRecentActivity(contextId: string): Promise {
+ return getJson(`/api/waiter/floor/activity?contextId=${encodeURIComponent(contextId)}`);
+}
+
export async function submitFloorOrder(request: SubmitFloorOrderRequest): Promise {
return postJson('/api/waiter/floor/orders', request);
}
+
+export async function updateFloorOrder(request: UpdateFloorOrderRequest): Promise {
+ return putJson(`/api/waiter/floor/orders/${encodeURIComponent(request.orderId)}`, {
+ contextId: request.contextId,
+ tableId: request.tableId,
+ itemCount: request.itemCount
+ });
+}