- Implemented `useDesignPersistence` composable for managing design records. - Enhanced `useSlipmatDesigner` to support loading designs from JSON. - Created global authentication middleware for route protection. - Added Supabase client plugin for database interactions. - Developed API endpoints for fetching, saving, and retrieving designs. - Introduced utility functions for Auth0 token verification and Supabase client retrieval. - Updated Nuxt configuration to include Auth0 and Supabase environment variables. - Added necessary dependencies for Auth0 and Supabase. - Enhanced TypeScript configuration for improved type support.
155 lines
5.9 KiB
Vue
155 lines
5.9 KiB
Vue
<script setup lang="ts">
|
||
const props = defineProps<{
|
||
previewUrl: string | null;
|
||
templateLabel: string;
|
||
productionPixels: number;
|
||
isExporting: boolean;
|
||
projectName: string;
|
||
isSaving: boolean;
|
||
canSave: boolean;
|
||
}>();
|
||
|
||
const emit = defineEmits<{
|
||
(e: "download-preview"): void;
|
||
(e: "download-production"): void;
|
||
(e: "export"): void;
|
||
(e: "update:projectName", value: string): void;
|
||
(e: "save"): void;
|
||
}>();
|
||
|
||
const viewMode = ref<"flat" | "turntable">("flat");
|
||
|
||
const isFlat = computed(() => viewMode.value === "flat");
|
||
|
||
const handleSelectView = (mode: "flat" | "turntable") => {
|
||
viewMode.value = mode;
|
||
};
|
||
|
||
const handleExport = () => emit("export");
|
||
const handleDownloadPreview = () => emit("download-preview");
|
||
const handleDownloadProduction = () => emit("download-production");
|
||
const handleProjectNameInput = (event: Event) => {
|
||
const target = event.target as HTMLInputElement;
|
||
emit("update:projectName", target.value);
|
||
};
|
||
const handleSave = () => emit("save");
|
||
const isSaveDisabled = computed(
|
||
() => !props.previewUrl || props.isSaving || !props.canSave
|
||
);
|
||
</script>
|
||
|
||
<template>
|
||
<section class="rounded-2xl border border-slate-800/60 bg-slate-900/80 p-4 shadow-lg shadow-slate-950/40">
|
||
<header class="flex items-center justify-between">
|
||
<div>
|
||
<h3 class="text-sm font-semibold uppercase tracking-wide text-slate-400">
|
||
Output Preview
|
||
</h3>
|
||
<p class="mt-1 text-sm text-slate-300">
|
||
{{ templateLabel }} • {{ productionPixels }}×{{ productionPixels }} px
|
||
</p>
|
||
</div>
|
||
<button
|
||
type="button"
|
||
class="rounded-lg border border-sky-500/60 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-sky-100 transition hover:bg-sky-500/10 disabled:cursor-not-allowed disabled:border-slate-600 disabled:text-slate-500"
|
||
:disabled="props.isExporting"
|
||
@click="handleExport"
|
||
>
|
||
{{ props.isExporting ? "Exporting…" : "Generate Files" }}
|
||
</button>
|
||
</header>
|
||
<div class="mt-4 flex gap-2">
|
||
<button
|
||
type="button"
|
||
class="flex-1 rounded-xl border px-3 py-2 text-xs font-semibold uppercase tracking-wide transition"
|
||
:class="isFlat ? 'border-sky-500 bg-sky-500/10 text-sky-200' : 'border-slate-800 bg-slate-900 text-slate-400 hover:border-slate-700/80 hover:text-slate-200'"
|
||
@click="handleSelectView('flat')"
|
||
>
|
||
Flat View
|
||
</button>
|
||
<button
|
||
type="button"
|
||
class="flex-1 rounded-xl border px-3 py-2 text-xs font-semibold uppercase tracking-wide transition"
|
||
:class="!isFlat ? 'border-sky-500 bg-sky-500/10 text-sky-200' : 'border-slate-800 bg-slate-900 text-slate-400 hover:border-slate-700/80 hover:text-slate-200'"
|
||
@click="handleSelectView('turntable')"
|
||
>
|
||
Turntable View
|
||
</button>
|
||
</div>
|
||
<div class="mt-4 aspect-square overflow-hidden rounded-2xl border border-slate-800 bg-slate-950">
|
||
<template v-if="props.previewUrl">
|
||
<div v-if="isFlat" class="h-full w-full">
|
||
<img
|
||
:src="props.previewUrl"
|
||
alt="Slipmat preview flat"
|
||
class="h-full w-full object-cover"
|
||
/>
|
||
</div>
|
||
<div v-else class="relative h-full w-full bg-linear-to-br from-slate-900 via-slate-950 to-black">
|
||
<img
|
||
src="/turntable-mockup.svg"
|
||
alt="Turntable illustration"
|
||
class="pointer-events-none h-full w-full object-contain"
|
||
/>
|
||
<div class="absolute left-[16%] top-[18%] h-[64%] w-[48%] -rotate-2 overflow-hidden rounded-full shadow-xl shadow-black/40">
|
||
<div class="absolute inset-0 bg-slate-900/40" />
|
||
<img
|
||
:src="props.previewUrl"
|
||
alt="Slipmat preview turntable"
|
||
class="h-full w-full object-cover opacity-95 mix-blend-screen"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<div
|
||
v-else
|
||
class="flex h-full items-center justify-center text-sm text-slate-500"
|
||
>
|
||
No preview yet—start designing!
|
||
</div>
|
||
</div>
|
||
<div class="mt-4 grid gap-3 md:grid-cols-[minmax(0,1fr)_auto] md:items-end">
|
||
<label class="flex flex-col gap-2">
|
||
<span class="text-xs font-semibold uppercase tracking-wide text-slate-400">
|
||
Project Name
|
||
</span>
|
||
<input
|
||
type="text"
|
||
class="w-full rounded-xl border border-slate-800 bg-slate-900 px-3 py-2 text-sm text-slate-100 shadow-inner shadow-black/20 focus:border-sky-500 focus:outline-none focus:ring-1 focus:ring-sky-500"
|
||
:value="props.projectName"
|
||
:disabled="props.isSaving"
|
||
maxlength="120"
|
||
placeholder="e.g. Midnight Mix Vol. 1"
|
||
@input="handleProjectNameInput"
|
||
/>
|
||
</label>
|
||
<button
|
||
type="button"
|
||
class="h-11 rounded-xl bg-emerald-500 px-6 text-sm font-semibold text-emerald-50 transition hover:bg-emerald-400 disabled:cursor-not-allowed disabled:bg-slate-700/70 disabled:text-slate-400"
|
||
:disabled="isSaveDisabled"
|
||
@click="handleSave"
|
||
>
|
||
{{ props.isSaving ? "Saving…" : "Save to Library" }}
|
||
</button>
|
||
</div>
|
||
<div class="mt-4 flex flex-wrap gap-3">
|
||
<button
|
||
type="button"
|
||
class="flex-1 rounded-xl bg-slate-800 px-4 py-3 text-sm font-medium text-slate-100 transition hover:bg-slate-700 disabled:cursor-not-allowed disabled:bg-slate-800/70 disabled:text-slate-500"
|
||
:disabled="!props.previewUrl"
|
||
@click="handleDownloadPreview"
|
||
>
|
||
Download Web Preview
|
||
</button>
|
||
<button
|
||
type="button"
|
||
class="flex-1 rounded-xl bg-sky-600 px-4 py-3 text-sm font-medium text-white transition hover:bg-sky-500 disabled:cursor-not-allowed disabled:bg-slate-600/70"
|
||
:disabled="props.isExporting"
|
||
@click="handleDownloadProduction"
|
||
>
|
||
Download Print-Ready PNG
|
||
</button>
|
||
</div>
|
||
</section>
|
||
</template>
|