diff --git a/src/app/[subdomain]/page.tsx b/src/app/[subdomain]/page.tsx index 09665e3..2e1b99d 100644 --- a/src/app/[subdomain]/page.tsx +++ b/src/app/[subdomain]/page.tsx @@ -15,11 +15,11 @@ export async function generateMetadata({ params }: Props): Promise { }); if (!user || !user.published) { - return { title: "Memorial Not Found" }; + return { title: "Спомен страницата не е пронајдена" }; } - const title = user.title || "Memorial"; - const description = user.description?.slice(0, 160) || `In Loving Memory of ${title}`; + const title = user.title || "Спомен"; + const description = user.description?.slice(0, 160) || `Во спомен на ${title}`; return { title, diff --git a/src/app/api/check-subdomain/route.ts b/src/app/api/check-subdomain/route.ts index 62e67f5..3aac807 100644 --- a/src/app/api/check-subdomain/route.ts +++ b/src/app/api/check-subdomain/route.ts @@ -1,5 +1,5 @@ import { NextRequest, NextResponse } from "next/server"; -import { prisma } from "@/lib/prisma"; +import { auth } from "@clerk/nextjs/server"; export async function GET(req: NextRequest) { const slug = req.nextUrl.searchParams.get("slug"); @@ -8,6 +8,7 @@ export async function GET(req: NextRequest) { return NextResponse.json({ available: false }); } + const { prisma } = await import("@/lib/prisma"); const existing = await prisma.user.findUnique({ where: { subdomain: slug } }); return NextResponse.json({ available: !existing }); } \ No newline at end of file diff --git a/src/app/api/publish/route.ts b/src/app/api/publish/route.ts index e0a4893..6681512 100644 --- a/src/app/api/publish/route.ts +++ b/src/app/api/publish/route.ts @@ -7,7 +7,7 @@ import { getPublicUrl } from "@/lib/upload"; export async function POST(req: NextRequest) { const { userId } = await auth(); if (!userId) { - return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + return NextResponse.json({ error: "Неавторизирано" }, { status: 401 }); } try { @@ -15,24 +15,24 @@ export async function POST(req: NextRequest) { const { title, description, bornDate, passedDate, subdomain, templateId, images } = body; if (!title?.trim()) { - return NextResponse.json({ error: "Title is required" }, { status: 400 }); + return NextResponse.json({ error: "Името е задолжително" }, { status: 400 }); } if (!subdomain || subdomain.length < 3) { - return NextResponse.json({ error: "Subdomain must be at least 3 characters" }, { status: 400 }); + return NextResponse.json({ error: "Поддоменот мора да има најмалку 3 карактери" }, { status: 400 }); } if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(subdomain)) { - return NextResponse.json({ error: "Subdomain can only contain lowercase letters, numbers, and hyphens" }, { status: 400 }); + return NextResponse.json({ error: "Поддоменот може да содржи само мали букви, цифри и цртички" }, { status: 400 }); } if (templateId < 1 || templateId > 3) { - return NextResponse.json({ error: "Invalid template" }, { status: 400 }); + return NextResponse.json({ error: "Невалиден шаблон" }, { status: 400 }); } if (!images || images.length === 0 || images.length > 3) { - return NextResponse.json({ error: "1-3 images required" }, { status: 400 }); + return NextResponse.json({ error: "Потребни се 1-3 фотографии" }, { status: 400 }); } const existing = await prisma.user.findUnique({ where: { subdomain } }); if (existing && existing.clerkId !== userId) { - return NextResponse.json({ error: "Subdomain already taken" }, { status: 409 }); + return NextResponse.json({ error: "Поддоменот е веќе зафатен" }, { status: 409 }); } const existingUser = await prisma.user.findUnique({ where: { clerkId: userId } }); @@ -93,6 +93,6 @@ export async function POST(req: NextRequest) { }); } catch (error) { console.error("Publish error:", error); - return NextResponse.json({ error: "Internal server error" }, { status: 500 }); + return NextResponse.json({ error: "Внатрешна грешка на серверот" }, { status: 500 }); } } \ No newline at end of file diff --git a/src/app/api/upload/route.ts b/src/app/api/upload/route.ts index 949afa1..faf8f65 100644 --- a/src/app/api/upload/route.ts +++ b/src/app/api/upload/route.ts @@ -24,7 +24,7 @@ function getPublicUrl(key: string): string { export async function POST(req: NextRequest) { const { userId } = await auth(); if (!userId) { - return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + return NextResponse.json({ error: "Неавторизирано" }, { status: 401 }); } try { @@ -32,15 +32,15 @@ export async function POST(req: NextRequest) { const file = formData.get("file") as File | null; if (!file) { - return NextResponse.json({ error: "No file provided" }, { status: 400 }); + return NextResponse.json({ error: "Нема подадено датотека" }, { status: 400 }); } if (!ALLOWED_TYPES.includes(file.type)) { - return NextResponse.json({ error: "Invalid file type" }, { status: 400 }); + return NextResponse.json({ error: "Невалиден тип на датотека" }, { status: 400 }); } if (file.size > MAX_FILE_SIZE) { - return NextResponse.json({ error: "File too large (max 5MB)" }, { status: 400 }); + return NextResponse.json({ error: "Датотеката е премногу голема (макс. 5MB)" }, { status: 400 }); } const ext = file.type.split("/")[1]; @@ -61,6 +61,6 @@ export async function POST(req: NextRequest) { return NextResponse.json({ key, url: publicUrl }); } catch (error) { console.error("Upload error:", error); - return NextResponse.json({ error: "Upload failed" }, { status: 500 }); + return NextResponse.json({ error: "Не успеа качувањето" }, { status: 500 }); } } \ No newline at end of file diff --git a/src/app/api/user/monument/image/[imageId]/route.ts b/src/app/api/user/monument/image/[imageId]/route.ts index fb22264..8210fc7 100644 --- a/src/app/api/user/monument/image/[imageId]/route.ts +++ b/src/app/api/user/monument/image/[imageId]/route.ts @@ -9,7 +9,7 @@ export async function DELETE( ) { const { userId } = await auth(); if (!userId) { - return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + return NextResponse.json({ error: "Неавторизирано" }, { status: 401 }); } const { imageId } = await params; @@ -21,13 +21,13 @@ export async function DELETE( }); if (!image || image.user.clerkId !== userId) { - return NextResponse.json({ error: "Image not found" }, { status: 404 }); + return NextResponse.json({ error: "Фотографијата не е пронајдена" }, { status: 404 }); } try { await deleteS3Object(image.key); } catch (e) { - console.error("Failed to delete S3 object:", image.key, e); + console.error("Не успеа бришењето на S3 објект:", image.key, e); } await prisma.image.delete({ where: { id: imageId } }); @@ -47,6 +47,6 @@ export async function DELETE( return NextResponse.json({ success: true }); } catch (error) { console.error("Delete image error:", error); - return NextResponse.json({ error: "Failed to delete image" }, { status: 500 }); + return NextResponse.json({ error: "Не успеа бришењето на фотографијата" }, { status: 500 }); } } \ No newline at end of file diff --git a/src/app/api/user/monument/route.ts b/src/app/api/user/monument/route.ts index fb379b8..f338417 100644 --- a/src/app/api/user/monument/route.ts +++ b/src/app/api/user/monument/route.ts @@ -6,7 +6,7 @@ import { deleteS3Object } from "@/lib/s3"; export async function DELETE(req: NextRequest) { const { userId } = await auth(); if (!userId) { - return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + return NextResponse.json({ error: "Неавторизирано" }, { status: 401 }); } try { @@ -16,14 +16,14 @@ export async function DELETE(req: NextRequest) { }); if (!user) { - return NextResponse.json({ error: "Monument not found" }, { status: 404 }); + return NextResponse.json({ error: "Споменот не е пронајден" }, { status: 404 }); } for (const image of user.images) { try { await deleteS3Object(image.key); } catch (e) { - console.error("Failed to delete S3 object:", image.key, e); + console.error("Не успеа бришењето на S3 објект:", image.key, e); } } @@ -32,14 +32,14 @@ export async function DELETE(req: NextRequest) { return NextResponse.json({ success: true }); } catch (error) { console.error("Delete monument error:", error); - return NextResponse.json({ error: "Failed to delete monument" }, { status: 500 }); + return NextResponse.json({ error: "Не успеа бришењето на споменот" }, { status: 500 }); } } export async function GET() { const { userId } = await auth(); if (!userId) { - return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + return NextResponse.json({ error: "Неавторизирано" }, { status: 401 }); } const user = await prisma.user.findUnique({ @@ -48,7 +48,7 @@ export async function GET() { }); if (!user) { - return NextResponse.json({ error: "Monument not found" }, { status: 404 }); + return NextResponse.json({ error: "Споменот не е пронајден" }, { status: 404 }); } return NextResponse.json({ user }); @@ -57,7 +57,7 @@ export async function GET() { export async function PUT(req: NextRequest) { const { userId } = await auth(); if (!userId) { - return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + return NextResponse.json({ error: "Неавторизирано" }, { status: 401 }); } try { @@ -80,6 +80,6 @@ export async function PUT(req: NextRequest) { return NextResponse.json({ user }); } catch (error) { console.error("Update error:", error); - return NextResponse.json({ error: "Failed to update monument" }, { status: 500 }); + return NextResponse.json({ error: "Не успеа ажурирањето на споменот" }, { status: 500 }); } } \ No newline at end of file diff --git a/src/app/dashboard/edit/page.tsx b/src/app/dashboard/edit/page.tsx index 11af2ae..74c196c 100644 --- a/src/app/dashboard/edit/page.tsx +++ b/src/app/dashboard/edit/page.tsx @@ -72,7 +72,7 @@ export default function EditPage() { }; if (loading) { - return
Loading...
; + return
Вчитување...
; } const imagePreviews = images.map((img) => ({ url: img.url, order: img.order })); @@ -81,23 +81,22 @@ export default function EditPage() {
-

Edit Memorial

+

Уреди спомен

- {/* Form - hidden on mobile when preview is shown */}
- +
- + setData({ ...data, bornDate: e.target.value })} - placeholder="e.g. 1960" + placeholder="нпр. 1960" className="mt-1 block w-full rounded-lg border border-stone-200 px-3 py-2 text-stone-900 placeholder:text-stone-400 focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary" maxLength={50} />
- + setData({ ...data, passedDate: e.target.value })} - placeholder="e.g. 2024" + placeholder="нпр. 2024" className="mt-1 block w-full rounded-lg border border-stone-200 px-3 py-2 text-stone-900 placeholder:text-stone-400 focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary" maxLength={50} /> @@ -136,7 +135,7 @@ export default function EditPage() {
- +