"use client"; import type { PortfolioProjectViewMode, PortfolioSectionType } from "@prisma/client"; import { ArrowDown, ArrowUp, BookOpenText, BriefcaseBusiness, CalendarDays, CheckCircle2, ChevronLeft, ChevronRight, CircleAlert, CircleDashed, Files, FolderTree, Grid2x2, ImagePlus, Layers3, Link2, Plus, Text, Trash2, UserRound, } from "lucide-react"; import Link from "next/link"; import { type ReactNode, useEffect, useRef, useState } from "react"; import { useFormStatus } from "react-dom"; import { MediaFieldPicker, type MediaFieldState } from "@/components/admin/media-field-picker"; import { StatsCard } from "@/components/dashboard/stats-card"; import { AppCard } from "@/components/ui/app-card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Textarea } from "@/components/ui/textarea"; import { moveArrayItem } from "@/lib/array"; import { getAdminAppPath } from "@/lib/admin-routing"; import { getFirstIncompleteWizardStep, getPortfolioWizardProgress, isPortfolioAssetReady, isPortfolioSectionReady, type PortfolioWizardStep, } from "@/lib/portfolio-form-progress"; import type { MediaOption } from "@/lib/media"; import type { PortfolioCategoryView, PortfolioProjectView } from "@/lib/portfolio"; import { cn } from "@/lib/utils"; type SectionFormValue = { id?: string; type: PortfolioSectionType; titleAr: string; titleEn: string; titleDe: string; bodyAr: string; bodyEn: string; bodyDe: string; imagePath: string; media: MediaFieldState; linkUrl: string; sortOrder: number; }; type AssetFormValue = { id?: string; kind: "IMAGE"; filePath: string; fileFieldName: string; media: MediaFieldState; altAr: string; altEn: string; altDe: string; sortOrder: number; }; type ProjectFormState = { categoryId: string; slug: string; clientName: string; projectYear: string; previewUrl: string; sortOrder: string; viewMode: PortfolioProjectViewMode; isFeatured: boolean; isPublished: boolean; titleAr: string; titleEn: string; titleDe: string; serviceLabelAr: string; serviceLabelEn: string; serviceLabelDe: string; summaryAr: string; summaryEn: string; summaryDe: string; }; type PortfolioProjectFormProps = { action: (formData: FormData) => void | Promise; categories: PortfolioCategoryView[]; mediaOptions: MediaOption[]; project?: PortfolioProjectView | null; formId: string; redirectPath: string; }; const locales = [ { suffix: "Ar" as const, label: "Arabic" }, { suffix: "En" as const, label: "English" }, { suffix: "De" as const, label: "German" }, ] as const; const sectionTypeOptions: PortfolioSectionType[] = [ "RICH_TEXT", "GALLERY", "STATS", "DELIVERABLES", "LINK", ]; const sectionTypeLabels: Record = { RICH_TEXT: "Rich Text", GALLERY: "Gallery (image)", STATS: "Stats (text)", DELIVERABLES: "Deliverables (text)", LINK: "Link", }; const viewModeOptions: Array<{ value: PortfolioProjectViewMode; label: string; description: string; icon: typeof Grid2x2; }> = [ { value: "GRID", label: "Grid", description: "Balanced modular layout.", icon: Grid2x2 }, { value: "STORY", label: "Story", description: "Narrative section flow.", icon: BookOpenText }, { value: "CASE_STUDY", label: "Case Study", description: "Structured challenge/solution/outcome view. Sections 1–3 become the lead panels.", icon: BriefcaseBusiness }, ]; const wizardSteps: Array<{ key: PortfolioWizardStep; label: string; description: string; }> = [ { key: "basics", label: "Basics", description: "Category, slug, client, year, and status.", }, { key: "content", label: "Localized Content", description: "Title, service label, and summary in all locales.", }, { key: "sections", label: "Sections", description: "Build the project story with ordered content blocks.", }, { key: "assets", label: "Assets & Media", description: "Cover and gallery assets from the media library.", }, ]; function createMediaFieldState(params: { kind: "IMAGE"; assetId?: string | null; url?: string | null; label?: string | null; }): MediaFieldState { return { mode: params.assetId ? "library" : "upload", assetId: params.assetId ?? "", url: params.url ?? "", label: params.label ?? "", kind: params.kind, }; } function createAvailableCategories( categories: PortfolioCategoryView[], project: PortfolioProjectView | null | undefined, ) { if (!project) { return categories; } if (categories.some((category) => category.id === project.category.id)) { return categories; } return [project.category, ...categories]; } function createInitialState( project: PortfolioProjectView | null | undefined, categories: PortfolioCategoryView[], ): ProjectFormState { return { categoryId: project?.category.id ?? categories[0]?.id ?? "", slug: project?.slug ?? "", clientName: project?.clientName ?? "", projectYear: String(project?.projectYear ?? new Date().getFullYear()), previewUrl: project?.previewUrl ?? "", sortOrder: String(project?.sortOrder ?? 0), viewMode: project?.viewMode ?? "GRID", isFeatured: project?.isFeatured ?? false, isPublished: project?.isPublished ?? false, titleAr: project?.title.ar ?? "", titleEn: project?.title.en ?? "", titleDe: project?.title.de ?? "", serviceLabelAr: project?.serviceLabel.ar ?? "", serviceLabelEn: project?.serviceLabel.en ?? "", serviceLabelDe: project?.serviceLabel.de ?? "", summaryAr: project?.summary.ar ?? "", summaryEn: project?.summary.en ?? "", summaryDe: project?.summary.de ?? "", }; } function createInitialSections(project: PortfolioProjectView | null | undefined): SectionFormValue[] { if (!project?.sections.length) { return [createEmptySection(0)]; } return project.sections.map((section, index) => ({ id: section.id, type: section.type, titleAr: section.title.ar, titleEn: section.title.en, titleDe: section.title.de, bodyAr: section.body.ar, bodyEn: section.body.en, bodyDe: section.body.de, imagePath: section.imagePath ?? "", media: createMediaFieldState({ kind: "IMAGE", assetId: section.mediaAssetId, url: section.imagePath, label: section.title.de || section.title.en || section.title.ar, }), linkUrl: section.linkUrl ?? "", sortOrder: index, })); } function createInitialAssets(project: PortfolioProjectView | null | undefined): AssetFormValue[] { if (!project?.assets.length) { return [createEmptyAsset(0)]; } return project.assets.map((asset, index) => ({ id: asset.id, kind: "IMAGE", filePath: asset.filePath, fileFieldName: `asset-upload-${index}`, media: createMediaFieldState({ kind: "IMAGE", assetId: asset.mediaAssetId, url: asset.filePath, label: asset.alt.de || asset.alt.en || asset.alt.ar, }), altAr: asset.alt.ar, altEn: asset.alt.en, altDe: asset.alt.de, sortOrder: index, })); } function createEmptySection(index: number): SectionFormValue { return { type: "RICH_TEXT", titleAr: "", titleEn: "", titleDe: "", bodyAr: "", bodyEn: "", bodyDe: "", imagePath: "", media: createMediaFieldState({ kind: "IMAGE" }), linkUrl: "", sortOrder: index, }; } function createEmptyAsset(index: number): AssetFormValue { return { kind: "IMAGE", filePath: "", fileFieldName: `asset-upload-${index}`, media: createMediaFieldState({ kind: "IMAGE" }), altAr: "", altEn: "", altDe: "", sortOrder: index, }; } function LocaleInputs({ title, namePrefix, values, onChange, multiline = false, }: { title: string; namePrefix?: string; values: { Ar: string; En: string; De: string }; onChange: (key: "Ar" | "En" | "De", value: string) => void; multiline?: boolean; }) { return (
{locales.map((locale) => (

{locale.label}

{multiline ? (