import { ref, type Ref } from 'vue' import { defineStore } from 'pinia' export interface ChildElement { id: number title: string description: string } export interface Element extends ChildElement { iconImageUrl: string | null richText: string shortPdfPath: string | null longPdfPath: string | null youtubeUrl: string | null } type ElementFileType = 'iconImage' | 'shortPdf' | 'longPdf' interface ElementResponse { element: Element childElements: ChildElement[] } export interface UpdateElementInput { title: string description: string richText: string youtubeUrl: string } interface UpdateElementResponse { element: Element } interface ElementPatchInput { title?: string description?: string iconImageUrl?: string richText?: string shortPdfPath?: string longPdfPath?: string youtubeUrl?: string } interface ErrorResponse { error?: string } const API_BASE_URL = import.meta.env.VITE_API_BASE_URL as string const ELEMENT_UPDATE_URL = `${API_BASE_URL}/api/element/update` export const useElementsStore = defineStore('elements', () => { const element = ref(null) const childElements = ref([]) const isLoading = ref(false) const error = ref(null) const isSaving = ref(false) const isUploadingIconImage = ref(false) const isUploadingShortPdf = ref(false) const isUploadingLongPdf = ref(false) const saveError = ref(null) async function fetchElement(elementId: string): Promise { element.value = null childElements.value = [] error.value = null saveError.value = null isLoading.value = true try { const encodedElementId = encodeURIComponent(elementId) const elementUrl = `${API_BASE_URL}/api/elements/${encodedElementId}` const response = await fetch(elementUrl) if (response.status === 404) { error.value = 'Element not found' return } if (!response.ok) { error.value = 'Could not load element' return } const data: ElementResponse = await response.json() element.value = data.element childElements.value = data.childElements } catch { childElements.value = [] error.value = 'Network error - could not load element' } finally { isLoading.value = false } } async function updateElement( elementId: string, input: UpdateElementInput, ): Promise { return await saveElementPatch(elementId, input, 'Could not save element') } async function clearElementIconImage(elementId: string): Promise { return await saveElementPatch( elementId, { iconImageUrl: '' }, 'Could not remove icon image', ) } async function clearElementShortPdf(elementId: string): Promise { return await saveElementPatch( elementId, { shortPdfPath: '' }, 'Could not remove short PDF', ) } async function clearElementLongPdf(elementId: string): Promise { return await saveElementPatch( elementId, { longPdfPath: '' }, 'Could not remove long PDF', ) } async function saveElementPatch( elementId: string, input: ElementPatchInput, failureMessage: string, ): Promise { saveError.value = null isSaving.value = true try { const response = await fetch(ELEMENT_UPDATE_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ elementId, ...input }), }) return await handleElementResponse(response, failureMessage) } catch { saveError.value = `Network error - ${failureMessage.toLowerCase()}` return false } finally { isSaving.value = false } } async function uploadElementIconImage( elementId: string, file: File, ): Promise { return await uploadElementFile( elementId, file, 'iconImage', isUploadingIconImage, 'Could not upload icon image', 'Network error - could not upload icon image', ) } async function uploadElementShortPdf( elementId: string, file: File, ): Promise { 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 { 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, failureMessage: string, networkErrorMessage: string, ): Promise { saveError.value = null isUploadingFile.value = true try { const formData = new FormData() formData.append('elementId', elementId) formData.append('fileType', fileType) formData.append('file', file) const response = await fetch(ELEMENT_UPDATE_URL, { method: 'POST', headers: { Accept: 'application/json' }, credentials: 'include', body: formData, }) return await handleElementResponse(response, failureMessage) } catch { saveError.value = networkErrorMessage return false } finally { isUploadingFile.value = false } } async function handleElementResponse( response: Response, failureMessage: string, ): Promise { if (response.status === 401) { saveError.value = 'Please log in again' return false } if (response.status === 404) { saveError.value = 'Element not found' return false } if (response.status === 400) { saveError.value = await errorMessage(response, failureMessage) return false } if (!response.ok) { saveError.value = failureMessage return false } const data: UpdateElementResponse = await response.json() element.value = data.element return true } async function errorMessage( response: Response, fallbackMessage: string, ): Promise { try { const data: ErrorResponse = await response.json() return data.error ?? fallbackMessage } catch { return fallbackMessage } } return { element, childElements, isLoading, error, isSaving, isUploadingIconImage, isUploadingShortPdf, isUploadingLongPdf, saveError, fetchElement, updateElement, uploadElementIconImage, uploadElementShortPdf, uploadElementLongPdf, clearElementIconImage, clearElementShortPdf, clearElementLongPdf, } })