smallengine/src/components/ReviewStep.vue
Michael Mainguy 3b6c296385 Initial commit: lead capture app for small engine repair shops
Vue 3 + TypeScript + Vite + Tailwind CSS v4 multi-step lead capture form
with config-driven white-labeling, externalized content (content.json),
and "starting at" pricing estimates. Mobile-first with camera photo upload.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-11 11:14:57 -05:00

118 lines
5.1 KiB
Vue

<script setup lang="ts">
import { computed } from 'vue'
import { useLeadForm } from '../composables/useLeadForm'
import { useShopConfig } from '../composables/useShopConfig'
import { useContent } from '../composables/useContent'
const { formData } = useLeadForm()
const { config } = useShopConfig()
const { content } = useContent()
const t = content.review
const tp = content.pricing
const selectedService = computed(() =>
config.services.find((s) => s.name === formData.serviceType)
)
function formatDate(dateStr: string) {
if (!dateStr) return ''
const d = new Date(dateStr + 'T00:00:00')
return d.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })
}
</script>
<template>
<div>
<h2 class="text-xl font-semibold text-gray-900 mb-1">{{ t.title }}</h2>
<p class="text-sm text-gray-500 mb-6">{{ t.subtitle }}</p>
<div class="space-y-5">
<!-- Equipment -->
<div class="rounded-lg border border-gray-200 p-4">
<h3 class="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-2">{{ t.equipmentSection }}</h3>
<dl class="grid grid-cols-2 gap-x-4 gap-y-1 text-sm">
<dt class="text-gray-500">{{ t.typeLabel }}</dt>
<dd class="text-gray-900">{{ formData.equipmentType || t.emptyValue }}</dd>
<dt class="text-gray-500">{{ t.makeLabel }}</dt>
<dd class="text-gray-900">{{ formData.make || t.emptyValue }}</dd>
<dt class="text-gray-500">{{ t.modelLabel }}</dt>
<dd class="text-gray-900">{{ formData.model || t.emptyValue }}</dd>
<dt class="text-gray-500">{{ t.serviceLabel }}</dt>
<dd class="text-gray-900">{{ formData.serviceType || t.emptyValue }}</dd>
</dl>
<p v-if="formData.problemDescription" class="text-sm text-gray-600 mt-2 border-t border-gray-100 pt-2">
{{ formData.problemDescription }}
</p>
</div>
<!-- Pricing -->
<div v-if="selectedService" class="rounded-lg bg-primary-light/20 border border-primary/20 px-4 py-3">
<div class="flex items-center justify-between">
<span class="text-sm font-medium text-gray-900">{{ t.estimatedPriceLabel }}</span>
<span v-if="selectedService.startingAt != null" class="text-sm font-semibold text-primary">
{{ tp.startingAt }} ${{ selectedService.startingAt }}
</span>
<span v-else class="text-sm text-gray-500 italic">{{ tp.quoteUponInspection }}</span>
</div>
</div>
<!-- Photos -->
<div v-if="formData.photos.length > 0" class="rounded-lg border border-gray-200 p-4">
<h3 class="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-2">
{{ t.photosSection }} ({{ formData.photos.length }})
</h3>
<div class="flex gap-2 overflow-x-auto">
<img
v-for="(photo, i) in formData.photos"
:key="i"
:src="photo.preview"
:alt="`${content.photos.photoAlt} ${i + 1}`"
class="w-16 h-16 rounded-md object-cover shrink-0"
/>
</div>
</div>
<!-- Schedule -->
<div class="rounded-lg border border-gray-200 p-4">
<h3 class="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-2">{{ t.scheduleSection }}</h3>
<dl class="grid grid-cols-2 gap-x-4 gap-y-1 text-sm">
<dt class="text-gray-500">{{ t.preferenceLabel }}</dt>
<dd class="text-gray-900">{{ t.pickupLabels[formData.pickupOrDropoff] }}</dd>
<dt class="text-gray-500">{{ t.dateRangeLabel }}</dt>
<dd class="text-gray-900">
<template v-if="formData.preferredDateStart">
{{ formatDate(formData.preferredDateStart) }}
<template v-if="formData.preferredDateEnd"> — {{ formatDate(formData.preferredDateEnd) }}</template>
</template>
<template v-else>{{ t.emptyValue }}</template>
</dd>
</dl>
<p v-if="formData.scheduleNotes" class="text-sm text-gray-600 mt-2 border-t border-gray-100 pt-2">
{{ formData.scheduleNotes }}
</p>
</div>
<!-- Contact -->
<div class="rounded-lg border border-gray-200 p-4">
<h3 class="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-2">{{ t.contactSection }}</h3>
<dl class="grid grid-cols-2 gap-x-4 gap-y-1 text-sm">
<dt class="text-gray-500">{{ t.nameLabel }}</dt>
<dd class="text-gray-900">{{ formData.firstName }} {{ formData.lastName }}</dd>
<dt class="text-gray-500">{{ t.phoneLabel }}</dt>
<dd class="text-gray-900">{{ formData.phone || t.emptyValue }}</dd>
<dt class="text-gray-500">{{ t.emailLabel }}</dt>
<dd class="text-gray-900">{{ formData.email || t.emptyValue }}</dd>
<dt class="text-gray-500">{{ t.addressLabel }}</dt>
<dd class="text-gray-900">
<template v-if="formData.address">
{{ formData.address }}<br />
{{ formData.city }}, {{ formData.state }} {{ formData.zip }}
</template>
<template v-else>{{ t.emptyValue }}</template>
</dd>
</dl>
</div>
</div>
</div>
</template>