Refactor code

This commit is contained in:
2026-01-31 16:04:29 +01:00
parent eb8dcd54a8
commit c712f31759
15 changed files with 14 additions and 67 deletions

View File

@ -2,15 +2,6 @@
import { z } from "zod"; import { z } from "zod";
/**
* Server action
* Forwards a multipart/form-data request (payload + files[])
* from the public app to the admin app's public commissions endpoint.
*
* Server-only env required:
* ADMIN_URL=https://admin.domain.com
*/
const submitPayloadSchema = z.object({ const submitPayloadSchema = z.object({
typeId: z.string().optional().nullable(), typeId: z.string().optional().nullable(),
optionId: z.string().optional().nullable(), optionId: z.string().optional().nullable(),
@ -36,7 +27,6 @@ export async function submitCommissionRequest(input: {
const payload = submitPayloadSchema.parse(input.payload); const payload = submitPayloadSchema.parse(input.payload);
const files = input.files ?? []; const files = input.files ?? [];
// Optional safety limits
const MAX_FILES = 10; const MAX_FILES = 10;
const MAX_BYTES_EACH = 10 * 1024 * 1024; // 10MB const MAX_BYTES_EACH = 10 * 1024 * 1024; // 10MB
@ -70,7 +60,6 @@ export async function submitCommissionRequest(input: {
const raw = await res.text().catch(() => ""); const raw = await res.text().catch(() => "");
const statusLine = `${res.status} ${res.statusText || ""}`.trim(); const statusLine = `${res.status} ${res.statusText || ""}`.trim();
// Show something useful even if raw is empty
let message = `Admin API error: ${statusLine}`; let message = `Admin API error: ${statusLine}`;
if (raw) { if (raw) {
@ -87,12 +76,9 @@ export async function submitCommissionRequest(input: {
} }
} }
// Log full body server-side for debugging (safe; this is server-only)
console.error("[submitCommissionRequest] upstream error", { statusLine, raw }); console.error("[submitCommissionRequest] upstream error", { statusLine, raw });
throw new Error(message); throw new Error(message);
} }
// Expected response: { id: string; createdAt: string }
return (await res.json()) as { id: string; createdAt: string }; return (await res.json()) as { id: string; createdAt: string };
} }

View File

@ -1,7 +1,7 @@
"use server"; "use server";
import type { PortfolioFilters } from "@/actions/portfolio/getPortfolioArtworksPage"; import type { PortfolioFilters } from "@/actions/portfolio/getPortfolioArtworksPage";
import { Prisma } from "@/generated/prisma/browser"; import type { Prisma } from "@/generated/prisma/browser";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
function coerceYear(y: PortfolioFilters["year"]) { function coerceYear(y: PortfolioFilters["year"]) {

View File

@ -107,7 +107,6 @@ export async function getPortfolioArtworksPage(args: {
.filter((y): y is number => typeof y === "number") .filter((y): y is number => typeof y === "number")
.sort((a, b) => b - a); .sort((a, b) => b - a);
// Segment logic (sortKey != null first, then null)
const inNullSegment = cursor?.afterSortKey === null; const inNullSegment = cursor?.afterSortKey === null;
const select = { const select = {
@ -180,7 +179,6 @@ export async function getPortfolioArtworksPage(args: {
if (!last) { if (!last) {
return { items, nextCursor: null, total, years, albums }; return { items, nextCursor: null, total, years, albums };
} }
// last.sortKey can be null only in null-segment, which we are not in here.
if (last.sortKey == null) { if (last.sortKey == null) {
return { items, nextCursor: null, total, years, albums }; return { items, nextCursor: null, total, years, albums };
} }

View File

@ -19,29 +19,6 @@ export default function Home() {
<SocialLinks /> <SocialLinks />
</div> </div>
</div> </div>
{/* Section Cards */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6">
{/* <p>
If you want to commission me you can find all the information you need under following link: <a href="https://linktr.ee/gaertan" target="_blank">Linktree</a>
</p> */}
{/* {sections.map((section) => (
<Link href={section.href} key={section.title}>
<Card className="hover:shadow-xl transition-shadow group">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<section.icon className="w-5 h-5 text-muted-foreground group-hover:text-primary" />
{section.title}
</CardTitle>
</CardHeader>
<CardContent className="text-sm text-muted-foreground">
{section.description}
</CardContent>
</Card>
</Link>
))} */}
</div>
</div > </div >
); );
} }

View File

@ -6,8 +6,6 @@ export default async function TosPage() {
orderBy: [{ version: "desc" }], orderBy: [{ version: "desc" }],
}) })
// console.log(tos?.markdown)
return ( return (
<div className="mx-auto w-full max-w-6xl px-4 py-8"> <div className="mx-auto w-full max-w-6xl px-4 py-8">
<div className="markdown"> <div className="markdown">

View File

@ -24,7 +24,7 @@ export async function GET(_req: NextRequest, context: { params: Promise<{ key: s
headers: { headers: {
"Content-Type": contentType, "Content-Type": contentType,
"Cache-Control": "public, max-age=3600", "Cache-Control": "public, max-age=3600",
"Content-Disposition": "inline", // use 'attachment' to force download "Content-Disposition": "inline",
}, },
}); });
} catch (err) { } catch (err) {

View File

@ -67,6 +67,7 @@ function BrandSvg({ icon }: { icon: SimpleIcon }) {
className="h-5 w-5 fill-current" className="h-5 w-5 fill-current"
aria-hidden="true" aria-hidden="true"
focusable="false" focusable="false"
// biome-ignore lint: lint/security/noDangerouslySetInnerHtml
dangerouslySetInnerHTML={{ __html: icon.svg }} dangerouslySetInnerHTML={{ __html: icon.svg }}
/> />
); );

View File

@ -26,6 +26,7 @@ export default function RawCloseButton({ targetHref }: RawCloseButtonProps) {
onClick={() => router.push(targetHref)} onClick={() => router.push(targetHref)}
className="absolute top-4 right-4 z-50 rounded-md bg-background/80 p-2 hover:bg-background/60 transition" className="absolute top-4 right-4 z-50 rounded-md bg-background/80 p-2 hover:bg-background/60 transition"
title="Close full view (ESC)" title="Close full view (ESC)"
type="button"
> >
<X className="w-6 h-6" /> <X className="w-6 h-6" />
</button> </button>

View File

@ -25,7 +25,6 @@ type Tag = {
slug: string; slug: string;
sortIndex: number; sortIndex: number;
parentId: string | null; parentId: string | null;
// these may exist, but we do NOT rely on them:
parent?: { id: string; name: string; slug: string; sortIndex: number } | null; parent?: { id: string; name: string; slug: string; sortIndex: number } | null;
children?: { id: string; name: string; slug: string; sortIndex: number; parentId: string | null }[]; children?: { id: string; name: string; slug: string; sortIndex: number; parentId: string | null }[];
}; };
@ -65,7 +64,6 @@ export default function TagFilterDialog({
const byId = useMemo(() => new Map(tags.map((t) => [t.id, t])), [tags]); const byId = useMemo(() => new Map(tags.map((t) => [t.id, t])), [tags]);
// Build children mapping from the flat list: parentId -> Tag[]
const childrenByParentId = useMemo(() => { const childrenByParentId = useMemo(() => {
const map = new Map<string, Tag[]>(); const map = new Map<string, Tag[]>();
for (const t of tags) { for (const t of tags) {
@ -74,7 +72,6 @@ export default function TagFilterDialog({
arr.push(t); arr.push(t);
map.set(t.parentId, arr); map.set(t.parentId, arr);
} }
// sort each child list
for (const [k, arr] of map) { for (const [k, arr] of map) {
map.set(k, arr.slice().sort(sortTags)); map.set(k, arr.slice().sort(sortTags));
} }
@ -101,7 +98,6 @@ export default function TagFilterDialog({
const s = new Set(prev); const s = new Set(prev);
if (next) { if (next) {
s.add(parent.slug); s.add(parent.slug);
// when selecting parent, remove child selections (redundant)
for (const c of children) s.delete(c.slug); for (const c of children) s.delete(c.slug);
} else { } else {
s.delete(parent.slug); s.delete(parent.slug);
@ -188,9 +184,6 @@ export default function TagFilterDialog({
/> />
<div className="min-w-0"> <div className="min-w-0">
<div className="truncate font-medium">{p.name}</div> <div className="truncate font-medium">{p.name}</div>
{/* <div className="text-xs text-muted-foreground">
{children.length ? "Parent tag" : "Tag"}
</div> */}
</div> </div>
</Label> </Label>
@ -231,11 +224,6 @@ export default function TagFilterDialog({
{orphanChildren.length ? ( {orphanChildren.length ? (
<div className="rounded-lg border p-4"> <div className="rounded-lg border p-4">
{/* <div className="mb-2 font-medium">Other tags</div> */}
{/* <div className="mb-3 text-xs text-muted-foreground">
These tags are not currently assigned to a visible parent.
</div> */}
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2"> <div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
{orphanChildren.map((t) => { {orphanChildren.map((t) => {
const checked = selectedSet.has(t.slug); const checked = selectedSet.has(t.slug);

View File

@ -1,7 +1,7 @@
"use client" "use client"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { CommissionExtra, CommissionOption, CommissionType, CommissionTypeExtra, CommissionTypeOption } from "@/generated/prisma/client" import type { CommissionExtra, CommissionOption, CommissionType, CommissionTypeExtra, CommissionTypeOption } from "@/generated/prisma/client"
type CommissionTypeWithItems = CommissionType & { type CommissionTypeWithItems = CommissionType & {
options: (CommissionTypeOption & { options: (CommissionTypeOption & {

View File

@ -12,7 +12,7 @@ import {
} from "@/components/ui/form"; } from "@/components/ui/form";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { import type {
CommissionCustomInput, CommissionCustomInput,
CommissionExtra, CommissionExtra,
CommissionOption, CommissionOption,
@ -28,7 +28,7 @@ import Link from "next/link";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import { useForm, useWatch } from "react-hook-form"; import { useForm, useWatch } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import * as z from "zod/v4"; import type * as z from "zod/v4";
import { FileDropzone } from "./FileDropzone"; import { FileDropzone } from "./FileDropzone";
type CommissionTypeWithRelations = CommissionType & { type CommissionTypeWithRelations = CommissionType & {

View File

@ -1,11 +1,8 @@
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
// import { Fraunces } from "next/font/google";
import localFont from 'next/font/local'; import localFont from 'next/font/local';
import Image from "next/image"; import Image from "next/image";
// const pacifico = Fraunces({ weight: "700", subsets: ["latin"] });
const myFont = localFont({ const myFont = localFont({
src: './Echotopia-Regular.woff2', src: './Echotopia-Regular.woff2',
}) })

View File

@ -1,7 +1,7 @@
"use client" "use client"
import { ThemeProvider as NextThemesProvider } from "next-themes" import { ThemeProvider as NextThemesProvider } from "next-themes"
import * as React from "react" import type * as React from "react"
export function ThemeProvider({ export function ThemeProvider({
children, children,

View File

@ -1,8 +1,8 @@
"use client" "use client"
import * as React from "react"
import * as AccordionPrimitive from "@radix-ui/react-accordion" import * as AccordionPrimitive from "@radix-ui/react-accordion"
import { ChevronDownIcon } from "lucide-react" import { ChevronDownIcon } from "lucide-react"
import * as React from "react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
@ -63,4 +63,5 @@ function AccordionContent({
) )
} }
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } export { Accordion, AccordionContent, AccordionItem, AccordionTrigger }

View File

@ -10,7 +10,7 @@ export function calculatePrice(source: PriceSource, base: number): number {
if (source.priceRange) { if (source.priceRange) {
const parts = source.priceRange.split("").map(Number) const parts = source.priceRange.split("").map(Number)
const max = Math.max(...parts) const max = Math.max(...parts)
return isNaN(max) ? 0 : max return Number.isNaN(max) ? 0 : max
} }
return 0 return 0
} }
@ -39,8 +39,8 @@ export function calculatePriceRange(
const min = Number(minStr) const min = Number(minStr)
const max = Number(maxStr) const max = Number(maxStr)
if (!isNaN(min)) minExtra += min if (!Number.isNaN(min)) minExtra += min
if (!isNaN(max)) maxExtra += max if (!Number.isNaN(max)) maxExtra += max
} }
} }