import { computed, ref } from "vue"; import { useAuth0 } from "@auth0/auth0-vue"; import { useNuxtApp, useRuntimeConfig } from "nuxt/app"; import type { $Fetch } from "ofetch"; import type { ExportedDesign } from "./useSlipmatDesigner"; type DesignRecord = { id: string; name: string; template_id: string; preview_url: string | null; preview_path?: string | null; design_json?: unknown; notes?: string | null; created_at?: string | null; updated_at?: string | null; }; export interface SavedDesign { id: string; name: string; templateId: string; previewUrl: string | null; notes?: string | null; createdAt: string | null; updatedAt: string | null; designJson?: unknown; } type SaveOptions = { id?: string; name: string; design: ExportedDesign; notes?: string; }; const mapDesignRecord = (record: DesignRecord): SavedDesign => ({ id: record.id, name: record.name, templateId: record.template_id, previewUrl: record.preview_url ?? null, notes: record.notes ?? undefined, createdAt: record.created_at ?? null, updatedAt: record.updated_at ?? null, designJson: record.design_json, }); export const useDesignPersistence = () => { const runtime = useRuntimeConfig(); const audience = runtime.public.auth0?.audience; const auth0 = process.client ? useAuth0() : null; const nuxtApp = useNuxtApp(); const fetcher = nuxtApp.$fetch as $Fetch; const designs = ref([]); const isLoading = ref(false); const isSaving = ref(false); const lastError = ref(null); const isAuthenticated = computed(() => auth0?.isAuthenticated.value ?? false); const acquireToken = async (): Promise => { if (!auth0) { throw new Error("Auth0 client is not available."); } return auth0.getAccessTokenSilently({ authorizationParams: audience ? { audience, } : undefined, }); }; const fetchDesigns = async () => { if (!auth0 || !isAuthenticated.value) { return; } isLoading.value = true; lastError.value = null; try { const token = await acquireToken(); const response = await fetcher("/api/designs", { headers: { Authorization: `Bearer ${token}`, }, }); designs.value = Array.isArray(response) ? response.map(mapDesignRecord) : []; } catch (error) { lastError.value = error instanceof Error ? error.message : String(error); throw error; } finally { isLoading.value = false; } }; const saveDesign = async ({ id, name, design, notes }: SaveOptions) => { if (!auth0 || !isAuthenticated.value) { throw new Error("User must be authenticated before saving a design."); } isSaving.value = true; lastError.value = null; try { const token = await acquireToken(); const payload = { id, name, templateId: design.templateId, previewDataUrl: design.previewUrl, productionDataUrl: design.productionUrl, designJson: design.canvasJson, notes, }; const response = await fetcher("/api/designs", { method: "POST", body: payload, headers: { Authorization: `Bearer ${token}`, }, }); const saved = mapDesignRecord(response); const next = designs.value.filter((item) => item.id !== saved.id); designs.value = [saved, ...next]; return saved; } catch (error) { lastError.value = error instanceof Error ? error.message : String(error); throw error; } finally { isSaving.value = false; } }; return { designs, isLoading, isSaving, lastError, isAuthenticated, fetchDesigns, saveDesign, }; };