@@ -22,8 +22,10 @@ import { Heading } from "@/app/components/layout/Heading";
2222import { Loading } from "@/app/components/Loading" ;
2323import { useMutation , useQueryClient } from "@tanstack/react-query" ;
2424import { useOrders } from "@/lib/fetch/order" ;
25- import { useTickets } from "@/lib/fetch/ticket" ;
25+ import { deleteTicket , useTickets } from "@/lib/fetch/ticket" ;
2626import { useTranslations } from "next-intl" ;
27+ import ErrorMessage from "@/app/components/ErrorMessage" ;
28+ import { assign } from "next/dist/shared/lib/router/utils/querystring" ;
2729
2830const 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+
92106const 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}
0 commit comments