- Storage Integration: * Remove Firebase Storage dependency and useFirebaseStorage composable * Implement direct MinIO uploads via POST /storage/upload with multipart/form-data * Upload canvas JSON, preview PNG, and production PNG as separate objects * Store public URLs and metadata in design records - Authentication & Registration: * Add email/password registration page with validation * Integrate backend user session via /auth/login endpoint * Store backendUser.id as ownerId in design records * Auto-sync backend session on Firebase auth state changes - User Account Pages: * Create profile page showing user details and backend session info * Create orders page with transaction history filtered by customerEmail * Add server proxy /api/orders to forward GET /transactions queries - Navigation Improvements: * Replace inline auth buttons with avatar dropdown menu * Add Profile, Orders, and Logout options to dropdown * Implement outside-click and route-change handlers for dropdown * Display user initials in avatar badge - API Updates: * Update transactions endpoint to accept amount as string * Format amount with .toFixed(2) in checkout success flow * Query orders by customerEmail instead of ownerId for consistency
69 lines
2.2 KiB
Vue
69 lines
2.2 KiB
Vue
<script setup lang="ts">
|
|
import type { SlipmatTemplate } from "~/composables/useSlipmatDesigner";
|
|
|
|
const props = defineProps<{
|
|
templates: SlipmatTemplate[];
|
|
selectedTemplateId: string;
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
(e: "select", templateId: string): void;
|
|
}>();
|
|
|
|
const handleSelect = (templateId: string) => {
|
|
emit("select", templateId);
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<section>
|
|
<h2 class="text-lg font-semibold text-slate-100">Slipmat Template</h2>
|
|
<p class="mt-1 text-sm text-slate-400">
|
|
Pick the vinyl size and print spec that matches this order.
|
|
</p>
|
|
<div class="mt-4 grid gap-3 md:grid-cols-1">
|
|
<button
|
|
v-for="template in props.templates"
|
|
:key="template.id"
|
|
type="button"
|
|
class="group rounded-xl border border-slate-700/70 bg-slate-900/60 p-4 text-left transition focus:outline-none focus-visible:ring-2 focus-visible:ring-sky-400"
|
|
:class="{
|
|
'border-sky-400/80 bg-sky-500/10 shadow-lg shadow-sky-500/20':
|
|
template.id === props.selectedTemplateId,
|
|
}"
|
|
@click="handleSelect(template.id)"
|
|
>
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-base font-medium text-slate-100">
|
|
{{ template.name }}
|
|
</span>
|
|
<span
|
|
v-if="template.id === props.selectedTemplateId"
|
|
class="rounded-full bg-sky-500/20 px-2 py-0.5 text-xs font-semibold uppercase tracking-wide text-sky-200"
|
|
>
|
|
Active
|
|
</span>
|
|
</div>
|
|
<dl class="mt-3 grid grid-cols-4 gap-x-3 gap-y-2 text-xs text-slate-300">
|
|
<div>
|
|
<dt class="text-slate-500">Diameter</dt>
|
|
<dd>{{ template.diameterInches }}"</dd>
|
|
</div>
|
|
<div>
|
|
<dt class="text-slate-500">Resolution</dt>
|
|
<dd>{{ template.dpi }} DPI</dd>
|
|
</div>
|
|
<div>
|
|
<dt class="text-slate-500">Bleed</dt>
|
|
<dd>{{ template.bleedInches ?? 0 }}"</dd>
|
|
</div>
|
|
<div>
|
|
<dt class="text-slate-500">Safe Zone</dt>
|
|
<dd>{{ template.safeZoneInches ?? 0 }}"</dd>
|
|
</div>
|
|
</dl>
|
|
</button>
|
|
</div>
|
|
</section>
|
|
</template>
|