Improve artwork edit form

This commit is contained in:
2025-12-29 21:46:01 +01:00
parent eaf822d73a
commit e1e000c8e5
7 changed files with 529 additions and 162 deletions

View File

@ -1,7 +1,6 @@
"use client"
import { updateArtwork } from "@/actions/artworks/updateArtwork";
// import { updateImage } from "@/actions/portfolio/images/updateImage";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
@ -11,10 +10,8 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover
import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
import { ArtTag } from "@/generated/prisma/client";
// import { Color, ImageColor, ImageMetadata, ImageVariant, PortfolioAlbum, PortfolioCategory, PortfolioImage, PortfolioSortContext, PortfolioTag, PortfolioType } from "@/generated/prisma";
import { cn } from "@/lib/utils";
import { artworkSchema } from "@/schemas/artworks/imageSchema";
// import { imageSchema } from "@/schemas/portfolio/imageSchema";
import { ArtworkWithRelations, CategoryWithTags } from "@/types/Artwork";
import { zodResolver } from "@hookform/resolvers/zod";
import { format } from "date-fns";
@ -45,15 +42,10 @@ export default function EditArtworkForm({ artwork, categories, tags }:
month: artwork.month || undefined,
year: artwork.year || undefined,
creationDate: artwork.creationDate ? new Date(artwork.creationDate) : undefined,
// albumId: image.albumId ?? undefined,
// typeId: image.typeId ?? undefined,
metadataId: artwork.metadata?.id ?? undefined,
categoryIds: artwork.categories?.map(cat => cat.id) ?? [],
colorIds: artwork.colors?.map(color => color.id) ?? [],
// sortContextIds: image.sortContexts?.map(sortContext => sortContext.id) ?? [],
tagIds: artwork.tags?.map(tag => tag.id) ?? [],
variantIds: artwork.variants?.map(variant => variant.id) ?? [],
newCategoryNames: [],
newTagNames: []
}
})
@ -109,6 +101,19 @@ export default function EditArtworkForm({ artwork, categories, tags }:
</FormItem>
)}
/>
<FormField
control={form.control}
name="notes"
render={({ field }) => (
<FormItem>
<FormLabel>Internal notes</FormLabel>
<FormControl>
<Textarea {...field} placeholder="Any note to the image" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Number */}
<FormField
control={form.control}
@ -192,89 +197,62 @@ export default function EditArtworkForm({ artwork, categories, tags }:
)}
/>
{/* Select */}
{/* <FormField
control={form.control}
name="albumId"
render={({ field }) => (
<FormItem>
<FormLabel>Album</FormLabel>
<Select
onValueChange={(value) => field.onChange(value === "" ? undefined : value)}
value={field.value ?? ""}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select an album" />
</SelectTrigger>
</FormControl>
<SelectContent>
{albums.map((album) => (
<SelectItem key={album.id} value={album.id}>
{album.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/> */}
{/* <FormField
control={form.control}
name="typeId"
render={({ field }) => (
<FormItem>
<FormLabel>Art Type</FormLabel>
<Select
onValueChange={(value) => field.onChange(value === "" ? undefined : value)}
value={field.value ?? ""}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select an art type" />
</SelectTrigger>
</FormControl>
<SelectContent>
{types.map((type) => (
<SelectItem key={type.id} value={type.id}>
{type.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/> */}
<FormField
control={form.control}
name="categoryIds"
render={({ field }) => {
const existingOptions = categories.map((cat) => ({
label: cat.name,
value: cat.id,
}));
const selectedCategoryIds = field.value ?? [];
const selectedOptions = categories
.filter(cat => field.value?.includes(cat.id))
.map(cat => ({ label: cat.name, value: cat.id }));
.filter((cat) => selectedCategoryIds.includes(cat.id))
.map((cat) => ({ label: cat.name, value: cat.id }));
// Also include any "new" selections so they stay visible after selection
const newCategoryNames = form.watch("newCategoryNames") ?? [];
const newSelectedOptions = newCategoryNames.map((name) => ({
label: `Create: ${name}`,
value: `__new__:${name}`,
}));
return (
<FormItem>
<FormLabel>Categories</FormLabel>
<FormControl>
<MultipleSelector
defaultOptions={categories.map(cat => ({
label: cat.name,
value: cat.id,
}))}
placeholder="Select categories"
options={existingOptions}
placeholder="Select or type to create categories"
hidePlaceholderWhenSelected
selectFirstItem
value={selectedOptions}
value={[...selectedOptions, ...newSelectedOptions]}
creatable
createOption={(raw) => ({
value: `__new__:${raw}`,
label: `Create: ${raw}`,
})}
onChange={(options) => {
const ids = options.map(option => option.value);
field.onChange(ids);
const values = options.map((o) => o.value);
const existingIds = values.filter((v) => !v.startsWith("__new__:"));
const newNames = values
.filter((v) => v.startsWith("__new__:"))
.map((v) => v.replace("__new__:", "").trim())
.filter(Boolean);
field.onChange(existingIds);
form.setValue("newCategoryNames", Array.from(new Set(newNames)), {
shouldDirty: true,
shouldValidate: true,
});
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)
);
}}
/>
@ -284,6 +262,7 @@ export default function EditArtworkForm({ artwork, categories, tags }:
render={({ field }) => {
const selectedTagIds = field.value ?? [];
const selectedCategoryIds = form.watch("categoryIds") ?? [];
const newTagNames = form.watch("newTagNames") ?? [];
// Tag IDs connected to selected categories
const preferredTagIds = new Set<string>();
@ -292,27 +271,29 @@ export default function EditArtworkForm({ artwork, categories, tags }:
for (const t of cat.tags) preferredTagIds.add(t.id);
}
// Build grouped options: Selected -> Category -> Other
// Existing tag options with groups
const tagOptions = tags
.map((t) => {
let group = "Other tags";
if (selectedTagIds.includes(t.id)) group = "Selected";
else if (preferredTagIds.has(t.id)) group = "From selected categories";
return {
label: t.name,
value: t.id,
group, // IMPORTANT: groupBy will use this
};
return { label: t.name, value: t.id, group };
})
// Optional: stable ordering within each group
.sort((a, b) => a.label.localeCompare(b.label));
// Selected value objects
const selectedOptions = tags
// Selected existing tags
const selectedExistingOptions = tags
.filter((t) => selectedTagIds.includes(t.id))
.map((t) => ({ label: t.name, value: t.id }));
// Selected "new" tags (so they remain visible)
const selectedNewOptions = newTagNames.map((name) => ({
label: `Create: ${name}`,
value: `__new__:${name}`,
group: "Selected",
}));
return (
<FormItem>
<FormLabel>Tags</FormLabel>
@ -322,11 +303,31 @@ export default function EditArtworkForm({ artwork, categories, tags }:
groupBy="group"
groupOrder={["Selected", "From selected categories", "Other tags"]}
showSelectedInDropdown
placeholder="Select tags"
placeholder="Select or type to create tags"
hidePlaceholderWhenSelected
selectFirstItem
value={selectedOptions}
onChange={(options) => field.onChange(options.map((o) => o.value))}
value={[...selectedExistingOptions, ...selectedNewOptions]}
creatable
createOption={(raw) => ({
value: `__new__:${raw}`,
label: `Create: ${raw}`,
group: "Selected",
})}
onChange={(options) => {
const values = options.map((o) => o.value);
const existingIds = values.filter((v) => !v.startsWith("__new__:"));
const newNames = values
.filter((v) => v.startsWith("__new__:"))
.map((v) => v.replace("__new__:", "").trim())
.filter(Boolean);
field.onChange(existingIds);
form.setValue("newTagNames", Array.from(new Set(newNames)), {
shouldDirty: true,
shouldValidate: true,
});
}}
/>
</FormControl>
<FormMessage />