Skip to content

Commit 9dfe69f

Browse files
committed
feat(Admin > Edit Album): Toggle photo or video
1 parent ff28617 commit 9dfe69f

File tree

4 files changed

+75
-21
lines changed

4 files changed

+75
-21
lines changed

src/components/AdminAlbum/AdminAlbumClient.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,12 @@ export default function AdminAlbumClient(
5858

5959
const handleItemUpdateWrapper = (updatedItem: RawXmlItem) => {
6060
// If multiple items are selected, apply the update to all of them
61-
// preserving their specific ID and filename
62-
if (selectedIndices.size > 0) {
61+
// preserving their specific ID and filename (each keeps its own filename)
62+
if (selectedIndices.size > 1) {
6363
selectedIndices.forEach(index => {
6464
const targetItem = items[index]
6565
if (!targetItem) return
6666

67-
// Create a new item that is a copy of the updated item
68-
// but restores the target's ID and filename
6967
const newItem = {
7068
...updatedItem,
7169
$: targetItem.$,

src/components/AdminAlbum/Fields.tsx

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Button from '@mui/joy/Button'
2+
import Checkbox from '@mui/joy/Checkbox'
23
import IconButton from '@mui/joy/IconButton'
34
import Input from '@mui/joy/Input'
45
import Option from '@mui/joy/Option'
@@ -67,6 +68,7 @@ export default function Fields(
6768
const allKeywords = keywordsData?.keywords ?? []
6869

6970
useEffect(() => {
71+
if (updatePendingRef.current) return
7072
setEditedItem(item)
7173
setXmlOutput('')
7274
setAutocompleteValue('')
@@ -115,17 +117,45 @@ export default function Fields(
115117
onXmlGenerated()
116118
}
117119

118-
const filename = editedItem?.filename ? (Array.isArray(editedItem.filename) ? editedItem.filename[0] : editedItem.filename) : ''
120+
const rawFilename = editedItem?.filename ? (Array.isArray(editedItem.filename) ? editedItem.filename[0] : editedItem.filename) : ''
121+
const isVideo = editedItem?.type === 'video'
122+
123+
const swapFilenameExt = (fn: string, toVideo: boolean): string => {
124+
const lastDot = fn.lastIndexOf('.')
125+
const base = lastDot === -1 ? fn : fn.slice(0, lastDot)
126+
return toVideo ? `${base}.mp4` : `${base}.jpg`
127+
}
128+
129+
const displayFilename = rawFilename ? swapFilenameExt(rawFilename, isVideo) : ''
119130

120131
return (
121132
<>
122133
<Stack direction="column" spacing={2} sx={{ width: '35rem', flexShrink: 0 }}>
123-
<Input
124-
value={filename}
125-
readOnly
126-
placeholder="Filename"
127-
title="Filename (read-only)"
128-
/>
134+
<Stack direction="row" spacing={1} alignItems="center" sx={{ minWidth: 0 }}>
135+
<Input
136+
value={displayFilename}
137+
readOnly
138+
placeholder="Filename"
139+
title="Filename (read-only)"
140+
sx={{ flex: 1, minWidth: 0 }}
141+
/>
142+
<Checkbox
143+
checked={isVideo}
144+
onChange={(e) => updateItem((prev: RawXmlItem | null) => {
145+
if (!prev) return null
146+
const checked = e.target.checked
147+
const currentFn = Array.isArray(prev.filename) ? prev.filename[0] : prev.filename
148+
const newFn = swapFilenameExt(currentFn ?? '', checked)
149+
const newFilename = Array.isArray(prev.filename) ? [newFn, ...prev.filename.slice(1)] : newFn
150+
if (checked) return { ...prev, type: 'video', filename: newFilename }
151+
const { type: _t, ...rest } = prev
152+
return { ...rest, filename: newFilename } as RawXmlItem
153+
})}
154+
label="Video item"
155+
title="Item type: photo (unchecked) or video (checked). Toggles &lt;type&gt;video&lt;/type&gt; and filename extension (.jpg ↔ .mp4)."
156+
slotProps={{ label: { sx: { color: '#E0E0E0' } } }}
157+
/>
158+
</Stack>
129159
<Input
130160
value={editedItem?.photo_city ?? ''}
131161
onChange={(e) => updateItem((prev: RawXmlItem | null) => prev ? { ...prev, photo_city: e.target.value } : null)}

src/components/AdminAlbum/useEditCountPill.tsx

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,13 @@ export function useEditCountPill(): EditCountPillHook {
4545
}
4646

4747
// Helper to remove empty/null/undefined properties recursively
48-
const removeEmpty = (obj: any): any => {
48+
function removeEmpty<T>(obj: T): T | undefined
49+
function removeEmpty(obj: unknown): unknown {
4950
if (obj === null || obj === undefined || obj === '') return undefined
5051
if (typeof obj !== 'object') return obj
5152
if (Array.isArray(obj)) return obj.map(removeEmpty).filter((v) => v !== undefined)
5253

53-
const cleaned: any = {}
54+
const cleaned: Record<string, unknown> = {}
5455
for (const [key, value] of Object.entries(obj)) {
5556
const cleanedValue = removeEmpty(value)
5657
if (cleanedValue !== undefined) {
@@ -60,24 +61,49 @@ export function useEditCountPill(): EditCountPillHook {
6061
return Object.keys(cleaned).length > 0 ? cleaned : undefined
6162
}
6263

63-
// Apply all edits from editedItems state and maintain proper field ordering
64+
// Apply all edits from editedItems state and maintain XML field order:
65+
// $, type, size, filename, then photo_*, then search, geo, ref (type before filename for video)
6466
const applyEditsToItems = (items: RawXmlItem[]): RawXmlItem[] => {
6567
return items.map((originalItem: RawXmlItem) => {
6668
const edited = editedItems[originalItem.$.id]
6769
if (!edited) return originalItem
6870

69-
// Reconstruct item with desired field order: search before geo
70-
const { search, geo, ref, ...rest } = edited
71-
const orderedItem = {
71+
const {
72+
$,
73+
type,
74+
size,
75+
filename,
76+
search,
77+
geo,
78+
ref,
79+
photo_date,
80+
photo_city,
81+
photo_loc,
82+
thumb_caption,
83+
photo_desc,
84+
...rest
85+
} = edited
86+
87+
const orderedItem: RawXmlItem = {
88+
$,
89+
...(type && { type }),
90+
...(size && { size }),
91+
filename,
92+
photo_date: photo_date ?? null,
93+
photo_city: photo_city ?? '',
94+
...(photo_loc !== undefined && photo_loc !== '' && { photo_loc }),
95+
...(thumb_caption !== undefined && thumb_caption !== '' && { thumb_caption }),
96+
...(photo_desc !== undefined && photo_desc !== '' && { photo_desc }),
7297
...rest,
7398
...(search !== undefined && search !== null && search !== '' && { search }),
7499
...(geo && { geo }),
75100
...(ref && { ref }),
76101
}
77102

78103
// Remove empty properties
79-
return removeEmpty(orderedItem)
80-
}).filter((item) => item !== undefined)
104+
const cleaned = removeEmpty(orderedItem)
105+
return cleaned ?? originalItem
106+
}).filter((item): item is RawXmlItem => item !== undefined)
81107
}
82108

83109
const EditCountPill = () => {

src/hooks/useMemory.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const useMemory = (
5050
}, [filtered, refImageGallery, autoInitialView])
5151

5252
const memoryHtml = details ? (
53-
<>
53+
<div data-type="memory-details">
5454
<h3 className={styles.city}>{details.title}</h3>
5555
{details.persons && (
5656
<h4 className={styles.person}>
@@ -65,7 +65,7 @@ const useMemory = (
6565
{decodeURI(details.reference[1]).replaceAll('_', ' ')}
6666
</Link>
6767
)}
68-
</>
68+
</div>
6969
) : null
7070

7171
return { setViewed, memoryHtml, viewedList }

0 commit comments

Comments
 (0)