Skip to content

Commit 52888a1

Browse files
committed
feat: update UI and add button to delete tickets
1 parent 8de6f09 commit 52888a1

4 files changed

Lines changed: 120 additions & 59 deletions

File tree

src/app/admin/prepare/order/page.tsx

Lines changed: 114 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ import { Heading } from "@/app/components/layout/Heading";
2222
import { Loading } from "@/app/components/Loading";
2323
import { useMutation, useQueryClient } from "@tanstack/react-query";
2424
import { useOrders } from "@/lib/fetch/order";
25-
import { useTickets } from "@/lib/fetch/ticket";
25+
import { deleteTicket, useTickets } from "@/lib/fetch/ticket";
2626
import { useTranslations } from "next-intl";
27+
import ErrorMessage from "@/app/components/ErrorMessage";
28+
import { assign } from "next/dist/shared/lib/router/utils/querystring";
2729

2830
const TicketItem = ({
2931
ticket,
@@ -40,7 +42,6 @@ const TicketItem = ({
4042
}) => {
4143
const isSelected = selectedTickets.has(ticket._id.toString());
4244

43-
// Dynamic styling based on selection state
4445
const getButtonStyles = () => {
4546
if (selectable && isSelected) {
4647
return "bg-primary-50 border-primary-300 text-primary-900";
@@ -51,9 +52,11 @@ const TicketItem = ({
5152
return (
5253
<div
5354
className={`w-full px-4 py-3 rounded-xl text-base border-2 transition-all duration-200 flex items-center justify-between ${getButtonStyles()}`}>
54-
<div className="flex items-center gap-3">
55+
56+
{/* Left side: Checkbox + Content */}
57+
<div className="flex items-center gap-3 flex-1">
5558
{selectable && (
56-
<div className="relative">
59+
<div className="flex-shrink-0">
5760
<input
5861
type="checkbox"
5962
checked={isSelected}
@@ -62,43 +65,56 @@ const TicketItem = ({
6265
/>
6366
</div>
6467
)}
65-
<div className="flex flex-col">
68+
69+
<div className="flex flex-col flex-1">
6670
<div className="flex flex-row items-center justify-between gap-2 mb-2">
6771
<span className="font-semibold">{ticket.itemTypeRef.name}</span>
6872
{ticket.timeslot && (
6973
<span className="text-sm bg-gray-100 text-gray-800 py-1 px-2 rounded-md">
70-
{timeslotToLocalTime(ticket.timeslot)}
71-
</span>
74+
{timeslotToLocalTime(ticket.timeslot)}
75+
</span>
7276
)}
7377
</div>
74-
<div className="flex gap-2 items-center">
75-
{ticket?.orderId && (
76-
<span
77-
className="text-sm bg-primary-100 text-primary-800 py-1 px-2 rounded-md">Order: {ticket.orderId.toString().slice(-6)}</span>
78-
)}
79-
{ticket.updatedAt && (
80-
<span
81-
className="text-sm bg-gray-100 text-gray-800 py-1 px-2 rounded-md">Updated: {formatDateTime(ticket.updatedAt, true)}</span>
78+
<div className="flex gap-2 items-center justify-between">
79+
<div className="flex gap-2 items-center flex-wrap">
80+
{ticket?.orderId && (
81+
<span className="text-sm bg-primary-100 text-primary-800 py-1 px-2 rounded-md">
82+
Order: {ticket.orderId.toString().slice(-6)}
83+
</span>
84+
)}
85+
{ticket.updatedAt && (
86+
<span className="text-sm bg-gray-100 text-gray-800 py-1 px-2 rounded-md">
87+
Updated: {formatDateTime(ticket.updatedAt, true)}
88+
</span>
89+
)}
90+
</div>
91+
92+
{/* Right side: Button (children) */}
93+
{children && (
94+
<div>
95+
{children}
96+
</div>
8297
)}
83-
{children}
8498
</div>
8599
</div>
86-
87100
</div>
88101
</div>
89102
);
90103
};
91104

105+
92106
const ItemTracker = ({
93107
tickets,
94108
orders,
95109
onMarkReady,
96-
onAssignToOrder
110+
onAssignToOrder,
111+
onDelete
97112
}: {
98113
tickets: ItemTicketDocumentWithItem[];
99114
orders: OrderDocument[];
100115
onMarkReady: (ticketId: string) => void;
101116
onAssignToOrder: (ticketIds: string[], orderId: string) => void;
117+
onDelete: (ticketIds: string[]) => void;
102118
}) => {
103119
const [selectedTickets, setSelectedTickets] = useState<Set<string>>(new Set());
104120
const [assignMode, setAssignMode] = useState(false);
@@ -152,31 +168,49 @@ const ItemTracker = ({
152168
setAssignMode(false);
153169
};
154170

171+
const handleDelete = () => {
172+
onDelete(Array.from(selectedTickets))
173+
setSelectedTickets(new Set());
174+
setAssignMode(false);
175+
}
176+
177+
const handleClear = () => {
178+
setSelectedTickets(new Set());
179+
setAssignMode(false);
180+
}
181+
155182
return (
156183
<div className="space-y-6">
157-
<div className="flex justify-between items-center">
158-
<h3 className="font-bold text-xl text-gray-900">{t('Admin.OrderManager.ItemTracker.title')}</h3>
159-
{selectedTickets.size > 0 && (
160-
<div className="flex gap-3">
161-
<Button
162-
onClick={() => setAssignMode(!assignMode)}
163-
color="bg-primary-500"
164-
textColor="text-white"
165-
className="font-medium"
166-
>
167-
{assignMode ? t('Admin.OrderManager.Actions.cancel') : t('Admin.OrderManager.Actions.assignToOrder', { count: selectedTickets.size })}
168-
</Button>
169-
<Button
170-
onClick={() => setSelectedTickets(new Set())}
171-
color="bg-gray-500"
172-
textColor="text-white"
173-
className="font-medium"
174-
>
175-
{t('Admin.OrderManager.Actions.clear')}
176-
</Button>
177-
</div>
178-
)}
179-
</div>
184+
{/* Action buttons */}
185+
{selectedTickets.size > 0 && (
186+
<div className="flex flex-col gap-3 mb-6">
187+
{!assignMode && <Button
188+
onClick={() => setAssignMode(!assignMode)}
189+
color="bg-primary-500"
190+
textColor="text-white"
191+
className="font-medium"
192+
>
193+
{t('Admin.OrderManager.Actions.assignToOrder', { count: selectedTickets.size })}
194+
</Button>
195+
}
196+
{!assignMode && <Button
197+
onClick={() => handleDelete()}
198+
color="bg-red-500"
199+
textColor="text-white"
200+
className="font-medium"
201+
>
202+
{t('Admin.OrderManager.Actions.delete', { count: selectedTickets.size })}
203+
</Button>}
204+
<Button
205+
onClick={() => handleClear()}
206+
color="bg-gray-500"
207+
textColor="text-white"
208+
className="font-medium"
209+
>
210+
{t('Admin.OrderManager.Actions.cancel')}
211+
</Button>
212+
</div>
213+
)}
180214

181215
{/* Active tickets */}
182216
<div className=" bg-orange-50 border-orange-200 rounded-2xl p-4 shadow-sm">
@@ -186,24 +220,22 @@ const ItemTracker = ({
186220
{ticketGroups.active.length}
187221
</span>
188222
</h4>
189-
<div className="space-y-3 max-h-64 overflow-y-auto custom-scrollbar">
223+
<div className="space-y-3 max-h-64 overflow-y-auto custom-scrollbar w-full">
190224
{ticketGroups.active.map(ticket => (
191-
<div key={ticket._id.toString()} className="flex items-center justify-between">
192-
<TicketItem ticket={ticket} selectedTickets={selectedTickets}
193-
handleToggleTicket={handleToggleTicket}>
194-
<Button
195-
onClick={() => onMarkReady(ticket._id.toString())}
196-
color="bg-green-500"
197-
textColor="text-white"
198-
className="font-medium"
199-
>
200-
<CheckIcon/>
201-
</Button>
202-
</TicketItem>
203-
</div>
225+
<TicketItem ticket={ticket} selectedTickets={selectedTickets}
226+
handleToggleTicket={handleToggleTicket} key={ticket._id.toString()}>
227+
<Button
228+
onClick={() => onMarkReady(ticket._id.toString())}
229+
color="bg-green-500"
230+
textColor="text-white"
231+
className="font-medium"
232+
>
233+
<CheckIcon/>
234+
</Button>
235+
</TicketItem>
204236
))}
205237
{ticketGroups.active.length === 0 && (
206-
<div className="text-center py-8">
238+
<div className="text-center py-2">
207239
<p className="text-gray-500 text-sm">No items being prepared</p>
208240
</div>
209241
)}
@@ -228,7 +260,7 @@ const ItemTracker = ({
228260
handleToggleTicket={handleToggleTicket}/>
229261
))}
230262
{ticketGroups.ready.length === 0 && (
231-
<div className="text-center py-8">
263+
<div className="text-center py-2">
232264
<p className="text-gray-500 text-sm">{t('Admin.OrderManager.ItemTracker.noReadyItemsAvailable')}</p>
233265
</div>
234266
)}
@@ -603,6 +635,23 @@ export default function OrderManagerDashboard() {
603635
},
604636
});
605637

638+
const deleteTicketMutation = useMutation({
639+
mutationFn: async (ticketId: string) => deleteTicket(ticketId),
640+
onSuccess: () => {
641+
queryClient.invalidateQueries({ queryKey: ['tickets'] });
642+
},
643+
onError: (error) => {
644+
setError(error.message);
645+
}
646+
});
647+
648+
const deleteSelectedTickets = (ticketIds: string[]) => {
649+
if (window.confirm(`Are you sure you want to delete ${ticketIds.length} ticket(s)?`)) {
650+
ticketIds.forEach(ticketId => {
651+
deleteTicketMutation.mutate(ticketId);
652+
});
653+
}
654+
};
606655
const filteredOrders = useMemo(() => {
607656
if (!orders) {
608657
return [];
@@ -673,6 +722,7 @@ export default function OrderManagerDashboard() {
673722
ticketIds,
674723
orderId
675724
})}
725+
onDelete={(ticketIds) => deleteSelectedTickets(ticketIds)}
676726
/>
677727
</div>
678728
</div>
@@ -696,7 +746,8 @@ export default function OrderManagerDashboard() {
696746
{t('Admin.OrderManager.Orders.completedOrders')}
697747
</Button>
698748
</div>
699-
<div className=" bg-primary-100 text-primary-800 text-sm px-3 py-1 rounded-full font-medium w-fit">
749+
<div
750+
className=" bg-primary-100 text-primary-800 text-sm px-3 py-1 rounded-full font-medium w-fit">
700751
{filteredOrders.length}
701752
</div>
702753
<div className="space-y-4">
@@ -732,6 +783,11 @@ export default function OrderManagerDashboard() {
732783
onClose={() => setShowScanner(false)}
733784
onScan={setSearchFilter}
734785
/>
786+
787+
{/* Error Toast */}
788+
{error && (
789+
<ErrorMessage error={error}/>
790+
)}
735791
</>
736792
);
737793
}

src/app/admin/prepare/pizza/page.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ const PizzaMakerStation = () => {
169169
}
170170
};
171171

172+
// @ts-ignore
172173
return (
173174
<>
174175
<Heading
@@ -218,13 +219,15 @@ const PizzaMakerStation = () => {
218219

219220
{/* Tickets List */}
220221
<div className="flex flex-col gap-3 max-h-[20rem] lg:max-h-[30rem] overflow-y-auto">
221-
{filteredTickets.toSorted((a, b) => {
222+
{filteredTickets.toSorted((a: ItemTicketDocumentWithItem, b: ItemTicketDocumentWithItem) => {
222223
if (a.status === TICKET_STATUS.DEMANDED && b.status === TICKET_STATUS.DEMANDED) {
223224
return (timeslotToDate(a.timeslot)?.getTime() || 0) - (timeslotToDate(b.timeslot)?.getTime() || 0)
224225
} else if (a.status === TICKET_STATUS.DEMANDED) {
225226
return -1
226227
} else if (b.status === TICKET_STATUS.DEMANDED) {
227228
return 1
229+
} else if (a.updatedAt && b.updatedAt) {
230+
return (new Date(b.updatedAt)?.getTime() ?? 0) - (new Date(a.updatedAt)?.getTime() ?? 0)
228231
}
229232
return (timeslotToDate(a.timeslot)?.getTime() || 0) - (timeslotToDate(b.timeslot)?.getTime() || 0)
230233
}).map(ticket => {

src/i18n/locales/de.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ translation:
204204
assignToOrder: "Bestellung zuweisen ({count})"
205205
cancel: "Abbrechen"
206206
clear: "Leeren"
207+
delete: "Löschen ({count})"
207208
markReady: "Als fertig markieren"
208209
deliverOrder: "Bestellung ausliefern"
209210
retrieveOrder: "Bestellung zurückziehen"

src/i18n/locales/en.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ translation:
206206
assignToOrder: "Assign to order"
207207
cancel: "Cancel"
208208
clear: "Clear"
209+
delete: "Delete ({count})"
209210
markReady: "Mark ready"
210211
deliverOrder: "Deliver order"
211212
force: "Force"

0 commit comments

Comments
 (0)