Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/[locale]/admin/sellers/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export default function CreateSellerPage() {

const updateField = (field: keyof FormState, value: string) => {
setForm((prev) => ({ ...prev, [field]: value }));
if (errors[field]) {
if (Object.hasOwn(errors, field)) {
setErrors((prev) => {
const next = { ...prev };
delete next[field];
Expand Down Expand Up @@ -158,9 +158,9 @@ export default function CreateSellerPage() {

router.push(`/${locale}/admin/sellers`);
router.refresh();
} catch (err: unknown) {
} catch (error: unknown) {
setServerError(
err instanceof Error ? err.message : dict.admin.createSellerError,
error instanceof Error ? error.message : dict.admin.createSellerError,
);
} finally {
setLoading(false);
Expand Down
10 changes: 6 additions & 4 deletions app/[locale]/admin/sellers/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@ export default async function AdminSellersPage({
defaultValue={filter.q ?? ''}
hiddenFields={{
pageSize: String(pageSize),
...(filter.sortBy ? { sortBy: filter.sortBy } : {}),
...(filter.sortDir ? { sortDir: filter.sortDir } : {}),
...(filter.sortBy && { sortBy: filter.sortBy }),
...(filter.sortDir && { sortDir: filter.sortDir }),
}}
/>
</div>
Expand Down Expand Up @@ -195,8 +195,10 @@ export default async function AdminSellersPage({
prevLabel={dict.admin.pagePrev}
nextLabel={dict.admin.pageNext}
pageInfo={dict.admin.pageXofY
.replace('{current}', String(currentPage))
.replace('{total}', String(totalPages))}
.split('{current}')
.join(currentPage.toString())
.split('{total}')
.join(totalPages.toString())}
/>
</>
)}
Expand Down
6 changes: 4 additions & 2 deletions app/[locale]/auth/change-password/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,11 @@ export default function ChangePasswordPage() {
setCurrentPassword('');
setNewPassword('');
setConfirmPassword('');
} catch (err: unknown) {
} catch (error_: unknown) {
setError(
err instanceof Error ? err.message : dict.auth.failedToChangePassword,
error_ instanceof Error
? error_.message
: dict.auth.failedToChangePassword,
);
} finally {
setLoading(false);
Expand Down
8 changes: 5 additions & 3 deletions app/[locale]/auth/reset-password/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function ResetPasswordPage() {
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [error, setError] = useState<string | null>(() =>
!token ? dict.auth.tokenExpired : null,
token ? null : dict.auth.tokenExpired,
);
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
Expand Down Expand Up @@ -64,9 +64,11 @@ export default function ResetPasswordPage() {
}
setSuccess(true);
router.push('/');
} catch (err: unknown) {
} catch (error_: unknown) {
setError(
err instanceof Error ? err.message : dict.auth.failedToResetPassword,
error_ instanceof Error
? error_.message
: dict.auth.failedToResetPassword,
);
} finally {
setLoading(false);
Expand Down
10 changes: 5 additions & 5 deletions app/[locale]/auth/signup/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ function applyIssue(errors: FormErrors, issue: ZodIssue) {
}
default: {
if (path.startsWith('address.')) {
const addrField = path.split('.')[1] as keyof AddressFields;
const addrField = path.split('.', 2)[1] as keyof AddressFields;
if (!errors.address) errors.address = {};
errors.address[addrField] = issue.message;
}
Expand Down Expand Up @@ -125,7 +125,7 @@ export default function SignUpPage() {

const updateField = (field: keyof FormState, value: string) => {
setForm((prev) => ({ ...prev, [field]: value }));
if (errors[field]) {
if (Object.hasOwn(errors, field)) {
setErrors((prev) => {
const next = { ...prev };
delete next[field];
Expand All @@ -139,7 +139,7 @@ export default function SignUpPage() {
...prev,
address: { ...prev.address, [field]: value },
}));
if (errors.address?.[field]) {
if (errors.address?.[field] !== undefined) {
setErrors((prev) => {
const next = {
...prev,
Expand Down Expand Up @@ -210,9 +210,9 @@ export default function SignUpPage() {
// Fallback: redirect to sign-in if auto-login fails
router.push(`/${locale}/auth/signin?registered=true`);
}
} catch (err: unknown) {
} catch (error: unknown) {
setServerError(
err instanceof Error ? err.message : 'An unexpected error occurred',
error instanceof Error ? error.message : 'An unexpected error occurred',
);
} finally {
setLoading(false);
Expand Down
22 changes: 13 additions & 9 deletions app/[locale]/auth/verify-email/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function VerifyEmailPage() {
const token = searchParams.get('token');
const [status, setStatus] = useState<
'loading' | 'success' | 'expired' | 'invalid'
>(() => (!token ? 'invalid' : 'loading'));
>(() => (token ? 'loading' : 'invalid'));
const dict = useDictionary();

useEffect(() => {
Expand All @@ -22,22 +22,26 @@ export default function VerifyEmailPage() {

const controller = new AbortController();

fetch(`/api/auth/verify-email?token=${encodeURIComponent(token)}`, {
signal: controller.signal,
})
.then((res) => res.json())
.then((data) => {
(async () => {
try {
const res = await fetch(
`/api/auth/verify-email?token=${encodeURIComponent(token)}`,
{
signal: controller.signal,
},
);
const data = await res.json();
if (data.success) {
setStatus('success');
} else if (data.error?.includes('expired')) {
setStatus('expired');
} else {
setStatus('invalid');
}
})
.catch(() => {
} catch {
setStatus('invalid');
});
}
})();

return () => controller.abort();
}, [token]);
Expand Down
6 changes: 3 additions & 3 deletions app/[locale]/cart/design-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ export function DesignPreview({
const canvasRef = useRef<HTMLCanvasElement | null>(null);

useEffect(() => {
let cancelled = false;
let isCancelled = false;

async function draw() {
const c = canvasRef.current?.getContext('2d');
if (!c) return;

const productImg = await loadImage(productImageUrl);
const designImg = await loadImage(designImageUrl);
if (cancelled) return;
if (isCancelled) return;

c.clearRect(0, 0, width, height);
c.fillStyle = '#f4f2e6';
Expand All @@ -63,7 +63,7 @@ export function DesignPreview({
draw();

return () => {
cancelled = true;
isCancelled = true;
};
}, [productImageUrl, designImageUrl, designPosition, width, height]);

Expand Down
9 changes: 4 additions & 5 deletions app/[locale]/checkout/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export default async function CheckoutPage({
<span className={styles.itemCustomization}>
{[
...item.customizations.flatMap((c) => [
c.size &&
c.size != null &&
`${dict.common.customizationSize}: ${c.size}`,
c.color &&
`${dict.common.customizationColor}: ${c.color}`,
Expand Down Expand Up @@ -260,10 +260,9 @@ export default async function CheckoutPage({
{isFirstPurchase && (
<div className={styles.totalRow}>
<span>
{dict.common.firstPurchaseDiscount.replace(
'{rate}',
String(FIRST_PURCHASE_DISCOUNT_RATE * 100),
)}
{dict.common.firstPurchaseDiscount
.split('{rate}')
.join((FIRST_PURCHASE_DISCOUNT_RATE * 100).toString())}
</span>
<span className={styles.discount}>
−{Money.format(discount, currency)}
Expand Down
8 changes: 4 additions & 4 deletions app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,12 @@ export default async function RootLayout({
// ADMIN/DESIGNER and have no orders
const role = (session?.user as { role?: string } | undefined)?.role;
const isInternal = role === ADMIN_ROLE || role === DESIGNER_ROLE;
let showBanner = !isInternal;
if (showBanner && session?.user?.id) {
let isShowBanner = !isInternal;
if (isShowBanner && session?.user?.id) {
const orderCount = await prisma.order.count({
where: { userId: session.user.id },
});
showBanner = orderCount === 0;
isShowBanner = orderCount === 0;
}

return (
Expand Down Expand Up @@ -142,7 +142,7 @@ export default async function RootLayout({
</div>
</header>

{showBanner && <HeaderBanner text={dict.common.promoBanner} />}
{isShowBanner && <HeaderBanner text={dict.common.promoBanner} />}

<main className={styles.main}>
<DictionaryProvider dict={dict}>
Expand Down
8 changes: 4 additions & 4 deletions app/[locale]/orders/[orderId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ export default async function OrderDetailPage({
)?.imageUrl;
const displayImage = customImage ?? item.productImageUrl;
const lineTotal =
item.unitPrice != null
? item.unitPrice * item.quantity
: undefined;
item.unitPrice == null
? undefined
: item.unitPrice * item.quantity;

return (
<div key={item.id} className={styles.itemRow}>
Expand All @@ -113,7 +113,7 @@ export default async function OrderDetailPage({
<span className={styles.itemCustomization}>
{item.customizationSnapshot
.flatMap((c) => [
c.size &&
c.size != null &&
`${dict.common.customizationSize}: ${c.size}`,
c.color &&
`${dict.common.customizationColor}: ${c.color}`,
Expand Down
4 changes: 2 additions & 2 deletions app/[locale]/orders/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ export default async function CustomerOrdersPage({
if (filter.pageSize !== 20) params.set('pageSize', String(filter.pageSize));
if (filter.q) params.set('q', filter.q);
const qs = params.toString();
return `/${locale}/orders${qs ? `?${qs}` : ''}`;
return qs ? `/${locale}/orders?${qs}` : `/${locale}/orders`;
};

return (
Expand All @@ -153,7 +153,7 @@ export default async function CustomerOrdersPage({
ariaLabel={dict.orders?.searchItems ?? 'Search by product'}
defaultValue={filter.q}
hiddenFields={
filter.status !== 'all' ? { status: filter.status } : undefined
filter.status === 'all' ? undefined : { status: filter.status }
}
/>
</div>
Expand Down
20 changes: 10 additions & 10 deletions app/[locale]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,16 @@ export default async function HomePage({
// Server-backed recent suggestions for authenticated users only.
// v1 spec: guests receive null; no localStorage / sessionStorage / cookies
// are ever written by the search feature.
const recent: RecentSearchSuggestion[] | null = session
? await new GetRecentSearchesUseCase(container.getSearchHistoryRepository())
.execute({ userId: session.id, locale })
.then((entries) =>
entries.map((e) => ({
term: e.term,
searchedAt: e.searchedAt.toISOString(),
})),
)
: null;
let recent: RecentSearchSuggestion[] | null = null;
if (session) {
const entries = await new GetRecentSearchesUseCase(
container.getSearchHistoryRepository(),
).execute({ userId: session.id, locale });
recent = entries.map((e) => ({
term: e.term,
searchedAt: e.searchedAt.toISOString(),
}));
}

// Client island receives a stable JSON shape. We pre-format
// the price string on the server so the client never receives
Expand Down
4 changes: 2 additions & 2 deletions app/[locale]/products/[id]/customization-experience.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ function CustomizationExperienceInner({
const { draft, setImage, setDesignPosition } = useCustomizationDraft();
const customizationModel =
ProductCustomizationConfig.fromJson(customizationConfig);
const allowsPhoto = customizationModel.allowPhotoDesign !== false;
const isAllowsPhoto = customizationModel.allowPhotoDesign !== false;
const activeProductImageUrl =
productImages.find((img) => img.alt === draft.color)?.url ??
previewBaseImageUrl;
Expand Down Expand Up @@ -146,7 +146,7 @@ function CustomizationExperienceInner({

<div className={styles.columns}>
<div className={styles.canvasCol}>
{allowsPhoto && previewBaseImageUrl && (
{isAllowsPhoto && previewBaseImageUrl && (
<MockupCanvasControl
productImageUrl={activeProductImageUrl}
initialDesignUrl={draft.imageUrl}
Expand Down
19 changes: 11 additions & 8 deletions app/[locale]/products/[id]/customization-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ export function CustomizationForm({
const config = ProductCustomizationConfig.fromJson(customizationConfig);

const textErrorId = errors.text ? 'customization-text-error' : undefined;
const sizeErrorId = errors.size ? 'customization-size-error' : undefined;
const sizeErrorId =
errors.size == null ? undefined : 'customization-size-error';

return (
<form
Expand Down Expand Up @@ -96,19 +97,21 @@ export function CustomizationForm({
</span>
<div className={formStyles.colorCarousel}>
{productImages.map((img) => {
const selected = draft.color === img.alt;
const isSelected = draft.color === img.alt;
return (
<div
key={img.url}
role="button"
tabIndex={0}
className={`${formStyles.colorItem} ${selected ? formStyles.colorItemSelected : ''}`}
className={`${formStyles.colorItem} ${isSelected ? formStyles.colorItemSelected : ''}`}
onClick={() => setColor(img.alt)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setColor(img.alt);
if (!(e.key === 'Enter' || e.key === ' ')) {
return;
}

e.preventDefault();
setColor(img.alt);
}}
title={img.alt}
>
Expand All @@ -131,7 +134,7 @@ export function CustomizationForm({
<select
value={draft.size ?? ''}
onChange={(event) => setSize(event.target.value || null)}
aria-invalid={errors.size ? 'true' : undefined}
aria-invalid={errors.size == null ? undefined : 'true'}
aria-describedby={sizeErrorId}
>
<option value="">{labels.customizationSizePlaceholder}</option>
Expand All @@ -141,7 +144,7 @@ export function CustomizationForm({
</option>
))}
</select>
{errors.size && (
{errors.size != null && (
<p id={sizeErrorId} role="alert">
{errors.size}
</p>
Expand Down
Loading
Loading