@@ -61,6 +61,23 @@ export default async function AdminOrdersPage({
6161 const hasNext = all . length > PAGE_SIZE ;
6262 const items = all . slice ( 0 , PAGE_SIZE ) ;
6363
64+ const viewModels = items . map ( order => {
65+ const currency = orderCurrency ( order , locale ) ;
66+ const totalMinor = pickMinor ( order ?. totalAmountMinor , order ?. totalAmount ) ;
67+
68+ return {
69+ id : order . id ,
70+ createdAt : formatDate ( order . createdAt , locale ) ,
71+ paymentStatus : order . paymentStatus ,
72+ totalFormatted :
73+ totalMinor === null ? '-' : formatMoney ( totalMinor , currency , locale ) ,
74+ itemCount : order . itemCount ,
75+ paymentProvider : order . paymentProvider ?? '-' ,
76+ viewHref : `/shop/admin/orders/${ order . id } ` ,
77+ viewAriaLabel : `View order ${ order . id } ` ,
78+ } ;
79+ } ) ;
80+
6481 return (
6582 < >
6683 < ShopAdminTopbar />
@@ -69,7 +86,7 @@ export default async function AdminOrdersPage({
6986 className = "mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8"
7087 aria-labelledby = "admin-orders-title"
7188 >
72- < header className = "flex items-start justify-between gap-4 " >
89+ < header className = "flex flex-col gap-4 sm:flex-row sm: items-start sm: justify-between" >
7390 < h1
7491 id = "admin-orders-title"
7592 className = "text-2xl font-bold text-foreground"
@@ -81,127 +98,195 @@ export default async function AdminOrdersPage({
8198 < input type = "hidden" name = { CSRF_FORM_FIELD } value = { csrfToken } />
8299 < button
83100 type = "submit"
84- className = "rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground transition-colors hover:bg-secondary"
101+ className = "inline-flex w-full items-center justify-center rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground transition-colors hover:bg-secondary sm:w-auto "
85102 >
86103 Reconcile stale
87104 </ button >
88105 </ form >
89106 </ header >
90107
91- < section className = "mt-6" aria-label = "Orders table" >
92- < div className = "overflow-x-auto" >
93- < table className = "min-w-full divide-y divide-border text-sm" >
94- < caption className = "sr-only" > Orders list</ caption >
95-
96- < thead className = "bg-muted/50" >
97- < tr >
98- < th
99- scope = "col"
100- className = "px-3 py-2 text-left font-semibold text-foreground"
101- >
102- Created
103- </ th >
104- < th
105- scope = "col"
106- className = "px-3 py-2 text-left font-semibold text-foreground"
107- >
108- Status
109- </ th >
110- < th
111- scope = "col"
112- className = "px-3 py-2 text-left font-semibold text-foreground"
108+ < section className = "mt-6" aria-label = "Orders list" >
109+ { /* Mobile cards */ }
110+ < div className = "md:hidden" >
111+ { viewModels . length === 0 ? (
112+ < div className = "rounded-md border border-border p-4 text-sm text-muted-foreground" >
113+ No orders yet.
114+ </ div >
115+ ) : (
116+ < ul className = "space-y-3" >
117+ { viewModels . map ( vm => (
118+ < li
119+ key = { vm . id }
120+ className = "rounded-lg border border-border bg-background p-4"
113121 >
114- Total
115- </ th >
116- < th
117- scope = "col"
118- className = "px-3 py-2 text-left font-semibold text-foreground"
119- >
120- Items
121- </ th >
122- < th
123- scope = "col"
124- className = "px-3 py-2 text-left font-semibold text-foreground"
125- >
126- Provider
127- </ th >
128- < th
129- scope = "col"
130- className = "px-3 py-2 text-left font-semibold text-foreground"
131- >
132- Order ID
133- </ th >
134- < th
135- scope = "col"
136- className = "px-3 py-2 text-left font-semibold text-foreground"
137- >
138- Actions
139- </ th >
140- </ tr >
141- </ thead >
122+ < div className = "flex items-start justify-between gap-3" >
123+ < div className = "min-w-0" >
124+ < div className = "text-xs text-muted-foreground" >
125+ { vm . createdAt }
126+ </ div >
127+ < div className = "mt-1" >
128+ < span className = "inline-flex rounded-full bg-muted px-2 py-1 text-xs font-medium text-foreground" >
129+ { vm . paymentStatus }
130+ </ span >
131+ </ div >
132+ </ div >
133+
134+ < div className = "shrink-0 whitespace-nowrap text-right text-sm font-medium text-foreground" >
135+ { vm . totalFormatted }
136+ </ div >
137+ </ div >
138+
139+ < dl className = "mt-3 grid grid-cols-2 gap-x-3 gap-y-2 text-xs" >
140+ < div >
141+ < dt className = "text-muted-foreground" > Items</ dt >
142+ < dd className = "text-foreground" > { vm . itemCount } </ dd >
143+ </ div >
142144
143- < tbody className = "divide-y divide-border" >
144- { items . length === 0 ? (
145+ < div className = "min-w-0" >
146+ < dt className = "text-muted-foreground" > Provider</ dt >
147+ < dd
148+ className = "truncate text-foreground"
149+ title = { vm . paymentProvider }
150+ >
151+ { vm . paymentProvider }
152+ </ dd >
153+ </ div >
154+
155+ < div className = "col-span-2" >
156+ < dt className = "text-muted-foreground" > Order ID</ dt >
157+ < dd
158+ className = "break-all font-mono text-[11px] text-muted-foreground"
159+ title = { vm . id }
160+ >
161+ { vm . id }
162+ </ dd >
163+ </ div >
164+ </ dl >
165+
166+ < div className = "mt-3" >
167+ < Link
168+ href = { vm . viewHref }
169+ className = "inline-flex items-center justify-center rounded-md border border-border px-2 py-1 text-xs font-medium text-foreground transition-colors hover:bg-secondary"
170+ aria-label = { vm . viewAriaLabel }
171+ >
172+ View
173+ </ Link >
174+ </ div >
175+ </ li >
176+ ) ) }
177+ </ ul >
178+ ) }
179+ </ div >
180+
181+ { /* Desktop table */ }
182+ < div className = "hidden md:block" >
183+ < div className = "overflow-x-auto" >
184+ < table className = "min-w-full divide-y divide-border text-sm" >
185+ < caption className = "sr-only" > Orders list</ caption >
186+
187+ < thead className = "bg-muted/50" >
145188 < tr >
146- < td className = "px-3 py-6 text-muted-foreground" colSpan = { 7 } >
147- No orders yet.
148- </ td >
189+ < th
190+ scope = "col"
191+ className = "px-3 py-2 text-left font-semibold text-foreground"
192+ >
193+ Created
194+ </ th >
195+ < th
196+ scope = "col"
197+ className = "px-3 py-2 text-left font-semibold text-foreground"
198+ >
199+ Status
200+ </ th >
201+ < th
202+ scope = "col"
203+ className = "px-3 py-2 text-left font-semibold text-foreground"
204+ >
205+ Total
206+ </ th >
207+ < th
208+ scope = "col"
209+ className = "px-3 py-2 text-left font-semibold text-foreground"
210+ >
211+ Items
212+ </ th >
213+ < th
214+ scope = "col"
215+ className = "px-3 py-2 text-left font-semibold text-foreground"
216+ >
217+ Provider
218+ </ th >
219+ < th
220+ scope = "col"
221+ className = "px-3 py-2 text-left font-semibold text-foreground"
222+ >
223+ Order ID
224+ </ th >
225+ < th
226+ scope = "col"
227+ className = "px-3 py-2 text-left font-semibold text-foreground"
228+ >
229+ Actions
230+ </ th >
149231 </ tr >
150- ) : (
151- items . map ( order => {
152- const currency = orderCurrency ( order , locale ) ;
153- const totalMinor = pickMinor (
154- order ?. totalAmountMinor ,
155- order ?. totalAmount
156- ) ;
157- const totalFormatted =
158- totalMinor === null
159- ? '-'
160- : formatMoney ( totalMinor , currency , locale ) ;
161-
162- return (
163- < tr key = { order . id } className = "hover:bg-muted/50" >
164- < td className = "px-3 py-2 text-muted-foreground" >
165- { formatDate ( order . createdAt , locale ) }
232+ </ thead >
233+
234+ < tbody className = "divide-y divide-border" >
235+ { viewModels . length === 0 ? (
236+ < tr >
237+ < td
238+ className = "px-3 py-6 text-muted-foreground"
239+ colSpan = { 7 }
240+ >
241+ No orders yet.
242+ </ td >
243+ </ tr >
244+ ) : (
245+ viewModels . map ( vm => (
246+ < tr key = { vm . id } className = "hover:bg-muted/50" >
247+ < td className = "px-3 py-2 text-muted-foreground whitespace-nowrap" >
248+ { vm . createdAt }
166249 </ td >
167250
168- < td className = "px-3 py-2" >
251+ < td className = "px-3 py-2 whitespace-nowrap " >
169252 < span className = "inline-flex rounded-full bg-muted px-2 py-1 text-xs font-medium text-foreground" >
170- { order . paymentStatus }
253+ { vm . paymentStatus }
171254 </ span >
172255 </ td >
173256
174- < td className = "px-3 py-2 text-foreground" >
175- { totalFormatted }
257+ < td className = "px-3 py-2 text-foreground whitespace-nowrap " >
258+ { vm . totalFormatted }
176259 </ td >
177260
178- < td className = "px-3 py-2 text-muted-foreground" >
179- { order . itemCount }
261+ < td className = "px-3 py-2 text-muted-foreground whitespace-nowrap " >
262+ { vm . itemCount }
180263 </ td >
181- < td className = "px-3 py-2 text-muted-foreground" >
182- { order . paymentProvider }
264+
265+ < td className = "px-3 py-2 text-muted-foreground whitespace-nowrap" >
266+ { vm . paymentProvider }
183267 </ td >
184268
185269 < td className = "px-3 py-2 font-mono text-xs text-muted-foreground break-all" >
186- { order . id }
270+ { vm . id }
187271 </ td >
188272
189- < td className = "px-3 py-2" >
273+ < td className = "px-3 py-2 whitespace-nowrap " >
190274 < Link
191- href = { `/shop/admin/orders/ ${ order . id } ` }
275+ href = { vm . viewHref }
192276 className = "rounded-md border border-border px-2 py-1 text-xs font-medium text-foreground transition-colors hover:bg-secondary"
193- aria-label = { `View order ${ order . id } ` }
277+ aria-label = { vm . viewAriaLabel }
194278 >
195279 View
196280 </ Link >
197281 </ td >
198282 </ tr >
199- ) ;
200- } )
201- ) }
202- </ tbody >
203- </ table >
283+ ) )
284+ ) }
285+ </ tbody >
286+ </ table >
287+ </ div >
204288 </ div >
289+
205290 < div className = "mt-4" >
206291 < AdminPagination
207292 basePath = "/shop/admin/orders"
0 commit comments