Skip to content

Commit b9df9fd

Browse files
(SP: 3)[Backend][UI] Harden Stripe refund handling & janitor auth; fix Admin Products currency + overflow (#120)
1 parent 0078242 commit b9df9fd

42 files changed

Lines changed: 969 additions & 394 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,11 @@ next-env.d.ts
6464
# Documentation (development only)
6565
.claude/
6666
CLAUDE.md
67-
frontend/docs/
67+
frontend/docs/
68+
frontend/.env.bak
69+
70+
71+
# local env backups
72+
frontend/.env*.bak
73+
frontend/.env.bak
74+

frontend/app/[locale]/shop/admin/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export default async function ShopAdminLayout({
2828
return (
2929
<>
3030
<div className="border-b border-border bg-background">
31-
<div className="mx-auto flex max-w-6xl items-center justify-between px-4 py-3">
31+
<div className="mx-auto flex max-w-7xl items-center justify-between px-4 py-3 sm:px-6 lg:px-8">
3232
<div className="flex items-center gap-3">
3333
<Link
3434
href="/shop/admin"

frontend/app/[locale]/shop/admin/orders/[id]/RefundButton.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,19 @@ export function RefundButton({ orderId, disabled }: Props) {
1616
async function onRefund() {
1717
setError(null);
1818

19-
const res = await fetch(`/api/shop/admin/orders/${orderId}/refund`, {
20-
method: 'POST',
21-
credentials: 'same-origin',
22-
headers: { 'Content-Type': 'application/json' },
23-
});
19+
let res: Response;
20+
try {
21+
res = await fetch(`/api/shop/admin/orders/${orderId}/refund`, {
22+
method: 'POST',
23+
credentials: 'same-origin',
24+
headers: { 'Content-Type': 'application/json' },
25+
});
26+
} catch (err) {
27+
const msg =
28+
err instanceof Error && err.message ? err.message : 'NETWORK_ERROR';
29+
setError(msg);
30+
return;
31+
}
2432

2533
let json: any = null;
2634
try {
@@ -46,14 +54,16 @@ export function RefundButton({ orderId, disabled }: Props) {
4654
onClick={onRefund}
4755
disabled={disabled || isPending}
4856
className="rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground transition-colors hover:bg-secondary disabled:cursor-not-allowed disabled:opacity-50"
49-
title={disabled ? 'Refund is only available for paid Stripe orders' : undefined}
57+
title={
58+
disabled
59+
? 'Refund is only available for paid Stripe orders'
60+
: undefined
61+
}
5062
>
5163
{isPending ? 'Refunding…' : 'Refund'}
5264
</button>
5365

54-
{error ? (
55-
<span className="text-xs text-destructive">{error}</span>
56-
) : null}
66+
{error ? <span className="text-xs text-destructive">{error}</span> : null}
5767
</div>
5868
);
5969
}

frontend/app/[locale]/shop/admin/orders/[id]/page.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function orderCurrency(
2323
locale: string
2424
): CurrencyCode {
2525
const c = order?.currency ?? resolveCurrencyFromLocale(locale);
26-
return (c === 'UAH' ? 'UAH' : 'USD') as CurrencyCode;
26+
return c === 'UAH' ? 'UAH' : 'USD';
2727
}
2828

2929
function formatDateTime(value: Date | null | undefined) {
@@ -41,9 +41,9 @@ export default async function AdminOrderDetailPage({
4141
if (!order) notFound();
4242

4343
const canRefund =
44-
order.paymentProvider === 'stripe' &&
45-
order.paymentStatus === 'paid' &&
46-
!!order.paymentIntentId;
44+
order.paymentProvider === 'stripe' &&
45+
order.paymentStatus === 'paid' &&
46+
!!order.paymentIntentId;
4747

4848
return (
4949
<div className="mx-auto max-w-6xl px-4 py-8">

frontend/app/[locale]/shop/admin/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { Link } from '@/i18n/routing';
33

44
export default function ShopAdminHomePage() {
55
return (
6-
<div className="mx-auto max-w-6xl px-4 py-8">
6+
<div className="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
7+
78
<h1 className="text-2xl font-bold text-foreground">Shop Admin</h1>
89
<p className="mt-2 text-sm text-muted-foreground">
910
Administrative tools for the merch shop.

frontend/app/[locale]/shop/admin/products/_components/product-form.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,15 +248,13 @@ export function ProductForm({
248248
setError(
249249
`${p.currency}: price is required when original price is set.`
250250
);
251-
setIsSubmitting(false);
252251
return;
253252
}
254253
}
255254

256255
const usd = effectivePrices.find(p => p.currency === 'USD');
257256
if (!usd || !usd.price.length) {
258257
setError('USD price is required.');
259-
setIsSubmitting(false);
260258
return;
261259
}
262260

@@ -275,7 +273,6 @@ export function ProductForm({
275273
}));
276274
} catch (e) {
277275
setError(e instanceof Error ? e.message : 'Invalid price value.');
278-
setIsSubmitting(false);
279276
return;
280277
}
281278

0 commit comments

Comments
 (0)