Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 19 additions & 22 deletions src/components/item-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export function ItemForm({
})
const [stagedFiles, setStagedFiles] = useState<File[]>([])

// Staged marks & notes (create mode only)
// Staged marks & notes
const [stagedMarks, setStagedMarks] = useState<StagedMark[]>([])
const [stagedNotes, setStagedNotes] = useState<StagedNote[]>([])

Expand All @@ -144,7 +144,8 @@ export function ItemForm({
"provenance",
...(typeDef && typeDef.fields.length > 0 ? ["typeFields"] : []),
"dimensions",
...(!isEdit ? ["marks", "notes"] : []),
"marks",
"notes",
]
const allExpanded = sectionKeys.every((k) => openSections[k])
const toggleAll = () => {
Expand Down Expand Up @@ -517,27 +518,23 @@ export function ItemForm({
</div>
</CollapsibleSection>

{/* Marks section (create only) */}
{!isEdit && (
<CollapsibleSection
title="Marks"
open={!!openSections.marks}
onOpenChange={(v) => toggleSection("marks", v)}
>
<StagedMarkList marks={stagedMarks} onChange={setStagedMarks} />
</CollapsibleSection>
)}
{/* Marks section */}
<CollapsibleSection
title="Marks"
open={!!openSections.marks}
onOpenChange={(v) => toggleSection("marks", v)}
>
<StagedMarkList marks={stagedMarks} onChange={setStagedMarks} />
</CollapsibleSection>

{/* Notes section (create only) */}
{!isEdit && (
<CollapsibleSection
title="Notes"
open={!!openSections.notes}
onOpenChange={(v) => toggleSection("notes", v)}
>
<StagedNoteList notes={stagedNotes} onChange={setStagedNotes} />
</CollapsibleSection>
)}
{/* Notes section */}
<CollapsibleSection
title="Notes"
open={!!openSections.notes}
onOpenChange={(v) => toggleSection("notes", v)}
>
<StagedNoteList notes={stagedNotes} onChange={setStagedNotes} />
</CollapsibleSection>

<div className="flex justify-end gap-2">
<Button type="submit" disabled={isPending}>
Expand Down
86 changes: 80 additions & 6 deletions src/pages/items/item-detail-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import {
useUploadItemImageItemsItemIdImagesPost,
useDeleteItemImageItemsItemIdImagesImageIdDelete,
} from "@/api/generated/hooks/item-images/item-images"
import { useCreateMarkItemsItemIdMarksPost } from "@/api/generated/hooks/marks/marks"
import { useUploadMarkImageItemsItemIdMarksMarkIdImagesPost } from "@/api/generated/hooks/mark-images/mark-images"
import { useCreateItemNoteItemsItemIdNotesPost } from "@/api/generated/hooks/notes/notes"
import {
getGetCollectionCollectionsCollectionIdGetQueryKey,
useGetCollectionCollectionsCollectionIdGet,
Expand All @@ -24,7 +27,11 @@ import { AppLayout } from "@/components/app-layout"
import { ImageCarousel } from "@/components/image-carousel"
import { ImageLightbox } from "@/components/image-lightbox"
import { ImageUpload } from "@/components/image-upload"
import { ItemForm } from "@/components/item-form"
import {
ItemForm,
type StagedMark,
type StagedNote,
} from "@/components/item-form"
import { MarkList } from "@/components/mark-list"
import { ProvenanceList } from "@/components/provenance-list"
import { ItemNoteList } from "@/components/item-note-list"
Expand Down Expand Up @@ -347,11 +354,25 @@ function EditItemDialog({
const queryClient = useQueryClient()
const [uploading, setUploading] = useState(false)
const imageUpload = useUploadItemImageItemsItemIdImagesPost()
const createMark = useCreateMarkItemsItemIdMarksPost()
const markImageUpload = useUploadMarkImageItemsItemIdMarksMarkIdImagesPost()
const createNote = useCreateItemNoteItemsItemIdNotesPost()

const handleSuccess = useCallback(
async (_updatedItem: ItemRead, stagedFiles: File[]) => {
async (
_updatedItem: ItemRead,
stagedFiles: File[],
stagedMarks: StagedMark[],
stagedNotes: StagedNote[]
) => {
const hasUploads =
stagedFiles.length > 0 ||
stagedMarks.length > 0 ||
stagedNotes.length > 0
if (hasUploads) setUploading(true)

// 1. Upload item images
if (stagedFiles.length > 0) {
setUploading(true)
const failed: string[] = []
for (const file of stagedFiles) {
try {
Expand All @@ -363,12 +384,56 @@ function EditItemDialog({
failed.push(file.name)
}
}
setUploading(false)
if (failed.length > 0) {
toast.error(`Failed to upload: ${failed.join(", ")}`)
}
}

// 2. Create marks + upload mark images
for (const sm of stagedMarks) {
try {
const markRes = await createMark.mutateAsync({
itemId: item.id,
data: {
title: sm.title || undefined,
description: sm.description || undefined,
},
})
if (markRes.status === 201 && sm.files.length > 0) {
for (const file of sm.files) {
try {
await markImageUpload.mutateAsync({
itemId: item.id,
markId: markRes.data.id,
data: { file },
})
} catch {
toast.error(`Failed to upload mark image: ${file.name}`)
}
}
}
} catch {
toast.error(`Failed to create mark: ${sm.title || "Untitled"}`)
}
}

// 3. Create notes
for (const sn of stagedNotes) {
try {
await createNote.mutateAsync({
itemId: item.id,
data: {
title: sn.title || undefined,
body: sn.body,
},
})
} catch {
toast.error(`Failed to create note: ${sn.title || "Untitled"}`)
}
}

if (hasUploads) setUploading(false)

queryClient.invalidateQueries({
queryKey: getGetItemItemsItemIdGetQueryKey(item.id),
})
Expand All @@ -386,7 +451,16 @@ function EditItemDialog({
}
onOpenChange(false)
},
[item.id, item.collection_id, onOpenChange, queryClient, imageUpload]
[
item.id,
item.collection_id,
onOpenChange,
queryClient,
imageUpload,
createMark,
markImageUpload,
createNote,
]
)

return (
Expand All @@ -397,7 +471,7 @@ function EditItemDialog({
</DialogHeader>
{uploading ? (
<p className="text-muted-foreground py-4 text-center text-sm">
Uploading images&hellip;
Saving&hellip;
</p>
) : (
<ItemForm
Expand Down