Some refactor
This commit is contained in:
@ -18,10 +18,6 @@ RUN bunx prisma generate
|
||||
|
||||
# Uncomment the following line in case you want to disable telemetry during the build.
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
ARG DATABASE_URL
|
||||
ENV DATABASE_URL=$DATABASE_URL
|
||||
ARG BETTER_AUTH_SECRET
|
||||
ENV BETTER_AUTH_SECRET=$BETTER_AUTH_SECRET
|
||||
|
||||
# Copy the rest of the code
|
||||
RUN bun run build -d
|
||||
|
||||
22
src/actions/artworks/getArtworks.ts
Normal file
22
src/actions/artworks/getArtworks.ts
Normal file
@ -0,0 +1,22 @@
|
||||
"use server"
|
||||
|
||||
import { prisma } from "@/lib/prisma"
|
||||
|
||||
export async function getSingleArtwork(id: string) {
|
||||
return await prisma.artwork.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
// album: true,
|
||||
// type: true,
|
||||
file: true,
|
||||
gallery: true,
|
||||
metadata: true,
|
||||
albums: true,
|
||||
categories: true,
|
||||
colors: { include: { color: true } },
|
||||
// sortContexts: true,
|
||||
tags: true,
|
||||
variants: true
|
||||
}
|
||||
})
|
||||
}
|
||||
16
src/actions/categories/getCategories.ts
Normal file
16
src/actions/categories/getCategories.ts
Normal file
@ -0,0 +1,16 @@
|
||||
"use server"
|
||||
|
||||
import { prisma } from "@/lib/prisma"
|
||||
|
||||
export async function getCategoriesWithTags() {
|
||||
return await prisma.artCategory.findMany({ include: { tags: true }, orderBy: { sortIndex: "asc" } })
|
||||
}
|
||||
|
||||
export async function getCategoriesWithCount() {
|
||||
return await prisma.artCategory.findMany({
|
||||
include: {
|
||||
_count: { select: { artworks: true, tags: true } },
|
||||
},
|
||||
orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
|
||||
})
|
||||
}
|
||||
7
src/actions/tags/getTags.ts
Normal file
7
src/actions/tags/getTags.ts
Normal file
@ -0,0 +1,7 @@
|
||||
"use server"
|
||||
|
||||
import { prisma } from "@/lib/prisma"
|
||||
|
||||
export async function getTags() {
|
||||
return await prisma.artTag.findMany({ orderBy: { sortIndex: "asc" } })
|
||||
}
|
||||
@ -1,31 +1,18 @@
|
||||
import { getSingleArtwork } from "@/actions/artworks/getArtworks";
|
||||
import { getCategoriesWithTags } from "@/actions/categories/getCategories";
|
||||
import { getTags } from "@/actions/tags/getTags";
|
||||
import ArtworkColors from "@/components/artworks/single/ArtworkColors";
|
||||
import ArtworkVariants from "@/components/artworks/single/ArtworkVariants";
|
||||
import DeleteArtworkButton from "@/components/artworks/single/DeleteArtworkButton";
|
||||
import EditArtworkForm from "@/components/artworks/single/EditArtworkForm";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
export default async function ArtworkSinglePage({ params }: { params: { id: string } }) {
|
||||
export default async function ArtworkSinglePage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params;
|
||||
|
||||
const item = await prisma.artwork.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
// album: true,
|
||||
// type: true,
|
||||
file: true,
|
||||
gallery: true,
|
||||
metadata: true,
|
||||
albums: true,
|
||||
categories: true,
|
||||
colors: { include: { color: true } },
|
||||
// sortContexts: true,
|
||||
tags: true,
|
||||
variants: true
|
||||
}
|
||||
})
|
||||
const item = await getSingleArtwork(id);
|
||||
|
||||
const categories = await prisma.artCategory.findMany({ include: { tags: true }, orderBy: { sortIndex: "asc" } });
|
||||
const tags = await prisma.artTag.findMany({ orderBy: { sortIndex: "asc" } });
|
||||
const categories = await getCategoriesWithTags();
|
||||
const tags = await getTags();
|
||||
|
||||
if (!item) return <div>Artwork with this id not found</div>
|
||||
|
||||
|
||||
@ -1,15 +1,11 @@
|
||||
import { getCategoriesWithCount } from "@/actions/categories/getCategories";
|
||||
import CategoryTable from "@/components/categories/CategoryTable";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { PlusCircleIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Suspense } from "react";
|
||||
|
||||
export default async function CategoriesPage() {
|
||||
const items = await prisma.artCategory.findMany({
|
||||
include: {
|
||||
_count: { select: { artworks: true, tags: true } },
|
||||
},
|
||||
orderBy: [{ sortIndex: "asc" }, { name: "asc" }],
|
||||
});
|
||||
const items = await getCategoriesWithCount();
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -19,11 +15,13 @@ export default async function CategoriesPage() {
|
||||
<PlusCircleIcon className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all text-primary-foreground" /> Add new category
|
||||
</Link>
|
||||
</div>
|
||||
<Suspense fallback={<p>Loading...</p>}>
|
||||
{items.length > 0 ? (
|
||||
<CategoryTable categories={items} />
|
||||
) : (
|
||||
<p>There are no categories yet. Consider adding some!</p>
|
||||
)}
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
51
src/app/error.tsx
Normal file
51
src/app/error.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function Error({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error & { digest?: string };
|
||||
reset: () => void;
|
||||
}) {
|
||||
useEffect(() => {
|
||||
console.error(error);
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<main className="min-h-dvh flex items-center justify-center px-6">
|
||||
<div className="max-w-lg text-center space-y-6">
|
||||
<h1 className="text-2xl font-semibold tracking-tight">
|
||||
Something went wrong
|
||||
</h1>
|
||||
|
||||
<p className="text-muted-foreground">
|
||||
An unexpected error occurred while loading this section.
|
||||
</p>
|
||||
|
||||
<div className="flex justify-center gap-4 pt-2">
|
||||
<button
|
||||
onClick={reset}
|
||||
className="rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition"
|
||||
>
|
||||
Try again
|
||||
</button>
|
||||
|
||||
<a
|
||||
href="/"
|
||||
className="rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted transition"
|
||||
>
|
||||
Go home
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{process.env.NODE_ENV === "development" && (
|
||||
<pre className="mt-6 rounded-md bg-muted p-4 text-left text-xs overflow-auto">
|
||||
{error.message}
|
||||
</pre>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
38
src/app/global-error.tsx
Normal file
38
src/app/global-error.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
"use client";
|
||||
|
||||
export default function GlobalError({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error & { digest?: string };
|
||||
reset: () => void;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className="min-h-dvh flex items-center justify-center bg-background px-6">
|
||||
<div className="max-w-lg text-center space-y-6">
|
||||
<h1 className="text-3xl font-semibold tracking-tight">
|
||||
Application error
|
||||
</h1>
|
||||
|
||||
<p className="text-muted-foreground">
|
||||
A critical error occurred. Please reload the application.
|
||||
</p>
|
||||
|
||||
<button
|
||||
onClick={reset}
|
||||
className="rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition"
|
||||
>
|
||||
Reload
|
||||
</button>
|
||||
|
||||
{process.env.NODE_ENV === "development" && (
|
||||
<pre className="mt-6 rounded-md bg-muted p-4 text-left text-xs overflow-auto">
|
||||
{error.message}
|
||||
</pre>
|
||||
)}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@ -1,3 +1,6 @@
|
||||
export const dynamic = "force-dynamic";
|
||||
export const revalidate = 0;
|
||||
|
||||
import { ThemeProvider } from "@/components/global/ThemeProvider";
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
|
||||
17
src/app/loading.tsx
Normal file
17
src/app/loading.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
export default function Loading() {
|
||||
return (
|
||||
<main className="min-h-dvh flex items-center justify-center">
|
||||
<div className="flex flex-col items-center gap-6">
|
||||
{/* Spinner */}
|
||||
<div className="relative h-14 w-14">
|
||||
<div className="absolute inset-0 rounded-full border-4 border-muted opacity-25" />
|
||||
<div className="absolute inset-0 rounded-full border-4 border-t-primary animate-spin" />
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-muted-foreground tracking-wide">
|
||||
Loading content...
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
31
src/app/not-found.tsx
Normal file
31
src/app/not-found.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import Link from "next/link";
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<main className="min-h-dvh flex items-center justify-center px-6">
|
||||
<div className="max-w-xl text-center space-y-6">
|
||||
{/* Placeholder artwork */}
|
||||
<div className="mx-auto h-48 w-48 rounded-xl bg-linear-to-br from-muted to-muted/30 flex items-center justify-center">
|
||||
<span className="text-6xl font-serif text-muted-foreground">404</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-2xl font-semibold tracking-tight">
|
||||
This page does not exist
|
||||
</h1>
|
||||
|
||||
<p className="text-muted-foreground">
|
||||
The item you are looking for may have been moved, renamed, or never existed.
|
||||
</p>
|
||||
|
||||
<div className="flex justify-center gap-4 pt-2">
|
||||
<Link
|
||||
href="/"
|
||||
className="inline-flex items-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition"
|
||||
>
|
||||
Go home
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user