import { 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 pdfPath: string | null youtubeUrl: string | null } 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 pdfPath?: 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 isUploadingPdf = 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 clearElementPdf(elementId: string): Promise { return await saveElementPatch(elementId, { pdfPath: '' }, 'Could not remove 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 { 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 uploadElementPdf(elementId: string, file: File): Promise { saveError.value = null isUploadingPdf.value = true try { const formData = new FormData() formData.append('elementId', elementId) formData.append('pdf', 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 PDF') } catch { saveError.value = 'Network error - could not upload PDF' return false } finally { isUploadingPdf.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, isUploadingPdf, saveError, fetchElement, updateElement, uploadElementIconImage, uploadElementPdf, clearElementIconImage, clearElementPdf, } })