route element file uploads

Add short and long PDF paths to elements, keep pdfPath as a short PDF compatibility alias, and route generic file uploads by fileType.
This commit is contained in:
Yisroel Baum 2026-06-14 23:17:56 +03:00
parent 0d5783ba9c
commit 9bbabc7fa6
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9
28 changed files with 686 additions and 214 deletions

View file

@ -1,4 +1,4 @@
import { ref } from 'vue'
import { ref, type Ref } from 'vue'
import { defineStore } from 'pinia'
export interface ChildElement {
@ -10,10 +10,14 @@ export interface ChildElement {
export interface Element extends ChildElement {
iconImageUrl: string | null
richText: string
shortPdfPath: string | null
longPdfPath: string | null
pdfPath: string | null
youtubeUrl: string | null
}
type ElementFileType = 'iconImage' | 'shortPdf' | 'longPdf'
interface ElementResponse {
element: Element
childElements: ChildElement[]
@ -35,6 +39,8 @@ interface ElementPatchInput {
description?: string
iconImageUrl?: string
richText?: string
shortPdfPath?: string
longPdfPath?: string
pdfPath?: string
youtubeUrl?: string
}
@ -53,7 +59,8 @@ export const useElementsStore = defineStore('elements', () => {
const error = ref<string | null>(null)
const isSaving = ref(false)
const isUploadingIconImage = ref(false)
const isUploadingPdf = ref(false)
const isUploadingShortPdf = ref(false)
const isUploadingLongPdf = ref(false)
const saveError = ref<string | null>(null)
async function fetchElement(elementId: string): Promise<void> {
@ -89,16 +96,35 @@ export const useElementsStore = defineStore('elements', () => {
}
}
async function updateElement(elementId: string, input: UpdateElementInput): Promise<boolean> {
async function updateElement(
elementId: string,
input: UpdateElementInput,
): Promise<boolean> {
return await saveElementPatch(elementId, input, 'Could not save element')
}
async function clearElementIconImage(elementId: string): Promise<boolean> {
return await saveElementPatch(elementId, { iconImageUrl: '' }, 'Could not remove icon image')
return await saveElementPatch(
elementId,
{ iconImageUrl: '' },
'Could not remove icon image',
)
}
async function clearElementPdf(elementId: string): Promise<boolean> {
return await saveElementPatch(elementId, { pdfPath: '' }, 'Could not remove PDF')
async function clearElementShortPdf(elementId: string): Promise<boolean> {
return await saveElementPatch(
elementId,
{ shortPdfPath: '' },
'Could not remove short PDF',
)
}
async function clearElementLongPdf(elementId: string): Promise<boolean> {
return await saveElementPatch(
elementId,
{ longPdfPath: '' },
'Could not remove long PDF',
)
}
async function saveElementPatch(
@ -126,38 +152,64 @@ export const useElementsStore = defineStore('elements', () => {
}
}
async function uploadElementIconImage(elementId: string, file: File): Promise<boolean> {
saveError.value = null
isUploadingIconImage.value = true
try {
const formData = new FormData()
formData.append('elementId', elementId)
formData.append('iconImage', file)
const response = await fetch(ELEMENT_UPDATE_URL, {
method: 'POST',
headers: { Accept: 'application/json' },
credentials: 'include',
body: formData,
})
return await handleElementResponse(response, 'Could not upload icon image')
} catch {
saveError.value = 'Network error - could not upload icon image'
return false
} finally {
isUploadingIconImage.value = false
}
async function uploadElementIconImage(
elementId: string,
file: File,
): Promise<boolean> {
return await uploadElementFile(
elementId,
file,
'iconImage',
isUploadingIconImage,
'Could not upload icon image',
'Network error - could not upload icon image',
)
}
async function uploadElementPdf(elementId: string, file: File): Promise<boolean> {
async function uploadElementShortPdf(
elementId: string,
file: File,
): Promise<boolean> {
return await uploadElementFile(
elementId,
file,
'shortPdf',
isUploadingShortPdf,
'Could not upload short PDF',
'Network error - could not upload short PDF',
)
}
async function uploadElementLongPdf(
elementId: string,
file: File,
): Promise<boolean> {
return await uploadElementFile(
elementId,
file,
'longPdf',
isUploadingLongPdf,
'Could not upload long PDF',
'Network error - could not upload long PDF',
)
}
async function uploadElementFile(
elementId: string,
file: File,
fileType: ElementFileType,
isUploadingFile: Ref<boolean>,
failureMessage: string,
networkErrorMessage: string,
): Promise<boolean> {
saveError.value = null
isUploadingPdf.value = true
isUploadingFile.value = true
try {
const formData = new FormData()
formData.append('elementId', elementId)
formData.append('pdf', file)
formData.append('fileType', fileType)
formData.append('file', file)
const response = await fetch(ELEMENT_UPDATE_URL, {
method: 'POST',
headers: { Accept: 'application/json' },
@ -165,12 +217,12 @@ export const useElementsStore = defineStore('elements', () => {
body: formData,
})
return await handleElementResponse(response, 'Could not upload PDF')
return await handleElementResponse(response, failureMessage)
} catch {
saveError.value = 'Network error - could not upload PDF'
saveError.value = networkErrorMessage
return false
} finally {
isUploadingPdf.value = false
isUploadingFile.value = false
}
}
@ -203,7 +255,10 @@ export const useElementsStore = defineStore('elements', () => {
return true
}
async function errorMessage(response: Response, fallbackMessage: string): Promise<string> {
async function errorMessage(
response: Response,
fallbackMessage: string,
): Promise<string> {
try {
const data: ErrorResponse = await response.json()
return data.error ?? fallbackMessage
@ -219,13 +274,16 @@ export const useElementsStore = defineStore('elements', () => {
error,
isSaving,
isUploadingIconImage,
isUploadingPdf,
isUploadingShortPdf,
isUploadingLongPdf,
saveError,
fetchElement,
updateElement,
uploadElementIconImage,
uploadElementPdf,
uploadElementShortPdf,
uploadElementLongPdf,
clearElementIconImage,
clearElementPdf,
clearElementShortPdf,
clearElementLongPdf,
}
})