Make gallery upload work; improve typing

This commit is contained in:
Maximilian Harz 2025-06-05 12:44:36 +02:00
parent 6a68300cea
commit b87a12a1d8
4 changed files with 76 additions and 23 deletions

View File

@ -2,6 +2,8 @@ import { describe, it, expect, vi } from 'vitest'
import { linkItem } from './itemFunctions'
import type { Item } from '#types/Item'
const toastErrorMock: (t: string) => void = vi.fn()
const toastSuccessMock: (t: string) => void = vi.fn()
@ -14,8 +16,45 @@ vi.mock('react-toastify', () => ({
describe('linkItem', () => {
const id = 'some-id'
let updateApi: () => void = vi.fn()
const item = { layer: { api: { updateItem: () => updateApi() } } }
let updateApi: (item: Partial<Item>) => Promise<Item> = vi.fn()
const item: Item = {
layer: {
api: {
updateItem: (item) => updateApi(item),
getItems: vi.fn(),
},
name: '',
menuIcon: '',
menuColor: '',
menuText: '',
markerIcon: {
image: '',
},
markerShape: 'square',
markerDefaultColor: '',
itemType: {
name: 'Test Item Type',
show_name_input: true,
show_profile_button: false,
show_start_end: true,
show_start_end_input: true,
show_text: true,
show_text_input: true,
custom_text: 'This is a custom text for the item type.',
profileTemplate: [
{ collection: 'users', id: null, item: {} },
{ collection: 'posts', id: '123', item: {} },
],
offers_and_needs: true,
icon_as_labels: {},
relations: true,
template: 'default',
questlog: false,
},
},
id: '',
name: '',
}
const updateItem = vi.fn()
beforeEach(() => {

View File

@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import { useEffect, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { toast } from 'react-toastify'
@ -200,8 +199,8 @@ export function ProfileForm() {
state={state}
setState={setState}
updatePermission={updatePermission}
linkItem={(id) => linkItem(id, item, updateItem)}
unlinkItem={(id) => unlinkItem(id, item, updateItem)}
linkItem={(id: string) => linkItem(id, item, updateItem)}
unlinkItem={(id: string) => unlinkItem(id, item, updateItem)}
setUrlParams={setUrlParams}
></TabsForm>
)}

View File

@ -1,3 +1,4 @@
import { useState } from 'react'
import { useDropzone } from 'react-dropzone'
import { useAppState } from '#components/AppShell/hooks/useAppState'
@ -12,7 +13,11 @@ interface Props {
export const GalleryForm = ({ state, setState }: Props) => {
const appState = useAppState()
const [isUploading, setUploading] = useState(false)
const upload = async (acceptedFiles: File[]) => {
setUploading(true)
const assets = await Promise.all(
acceptedFiles.map(async (file) => {
return appState.assetsApi.upload(file, 'gallery')
@ -22,11 +27,13 @@ export const GalleryForm = ({ state, setState }: Props) => {
const newGalleryItems = assets.map((asset) => ({
directus_files_id: {
id: asset.id,
width: 600, // TODO determine
height: 400, // TODO determine
width: 600, // TODO map to ids only
height: 400,
},
}))
setUploading(false)
setState((prevState) => ({
...prevState,
gallery: [...prevState.gallery, ...newGalleryItems],
@ -36,6 +43,9 @@ export const GalleryForm = ({ state, setState }: Props) => {
const { getRootProps, getInputProps } = useDropzone({
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onDrop: upload,
accept: {
'image/jpeg': [],
},
})
const images = state.gallery.map((i, j) => ({
@ -62,6 +72,8 @@ export const GalleryForm = ({ state, setState }: Props) => {
Drop here
</div>
{isUploading && <div className='tw:mt-2 tw:text-gray-500'>Uploading...</div>}
{images.map((image, index) => (
<div key={index} className='tw:mb-2'>
<img

View File

@ -15,6 +15,7 @@ import { encodeTag } from '#utils/FormatTags'
import { hashTagRegex } from '#utils/HashTagRegex'
import { randomColor } from '#utils/RandomColor'
import type { FormState } from '#types/FormState'
import type { Item } from '#types/Item'
// eslint-disable-next-line promise/avoid-new
@ -77,8 +78,8 @@ export const submitNewItem = async (
setAddItemPopupType('')
}
export const linkItem = async (id: string, item, updateItem) => {
const newRelations = item.relations || []
export const linkItem = async (id: string, item: Item, updateItem) => {
const newRelations = item.relations ?? []
newRelations?.push({ items_id: item.id, related_items_id: id })
const updatedItem = { id: item.id, relations: newRelations }
@ -96,7 +97,7 @@ export const linkItem = async (id: string, item, updateItem) => {
}
}
export const unlinkItem = async (id: string, item, updateItem) => {
export const unlinkItem = async (id: string, item: Item, updateItem) => {
const newRelations = item.relations?.filter((r) => r.related_items_id !== id)
const updatedItem = { id: item.id, relations: newRelations }
@ -116,7 +117,7 @@ export const unlinkItem = async (id: string, item, updateItem) => {
export const handleDelete = async (
event: React.MouseEvent<HTMLElement>,
item,
item: Item,
setLoading,
removeItem,
map,
@ -144,8 +145,8 @@ export const handleDelete = async (
}
export const onUpdateItem = async (
state,
item,
state: FormState,
item: Item,
tags,
addTag,
setLoading,
@ -159,19 +160,20 @@ export const onUpdateItem = async (
const offerUpdates: any[] = []
// check for new offers
await state.offers?.map((o) => {
state.offers?.map((o) => {
const existingOffer = item?.offers?.find((t) => t.tags_id === o.id)
existingOffer && offerUpdates.push(existingOffer.id)
if (!existingOffer && !tags.some((t) => t.id === o.id)) addTag({ ...o, offer_or_need: true })
existingOffer && offerUpdates.push(existingOffer.tags_id)
if (!existingOffer && !tags.some((t: { id: string }) => t.id === o.id))
addTag({ ...o, offer_or_need: true })
!existingOffer && offerUpdates.push({ items_id: item?.id, tags_id: o.id })
return null
})
const needsUpdates: any[] = []
await state.needs?.map((n) => {
state.needs?.map((n) => {
const existingNeed = item?.needs?.find((t) => t.tags_id === n.id)
existingNeed && needsUpdates.push(existingNeed.id)
existingNeed && needsUpdates.push(existingNeed.tags_id)
!existingNeed && needsUpdates.push({ items_id: item?.id, tags_id: n.id })
!existingNeed && !tags.some((t) => t.id === n.id) && addTag({ ...n, offer_or_need: true })
return null
@ -197,6 +199,7 @@ export const onUpdateItem = async (
...(state.offers.length > 0 && { offers: offerUpdates }),
...(state.needs.length > 0 && { needs: needsUpdates }),
...(state.openCollectiveSlug && { openCollectiveSlug: state.openCollectiveSlug }),
gallery: state.gallery,
}
const offersState: any[] = []
@ -216,7 +219,7 @@ export const onUpdateItem = async (
setLoading(true)
await state.text
state.text
.toLocaleLowerCase()
.match(hashTagRegex)
?.map((tag) => {
@ -234,7 +237,7 @@ export const onUpdateItem = async (
await sleep(200)
if (!item.new) {
item?.layer?.api?.updateItem &&
await (item?.layer?.api?.updateItem &&
toast
.promise(item?.layer?.api?.updateItem(changedItem), {
pending: 'updating Item ...',
@ -251,10 +254,10 @@ export const onUpdateItem = async (
setLoading(false)
navigate(`/item/${item.id}${params && '?' + params}`)
return null
})
}))
} else {
item.new = false
item.layer?.api?.createItem &&
await (item.layer?.api?.createItem &&
toast
.promise(item.layer?.api?.createItem(changedItem), {
pending: 'updating Item ...',
@ -280,6 +283,6 @@ export const onUpdateItem = async (
setLoading(false)
navigate(`/${params && '?' + params}`)
return null
})
}))
}
}