feat: complete TapTrack landing page — Phases 1-10

- Vite + Vue 3 + TypeScript + Tailwind CSS v4 scaffold
- lucide-vue-next icons, Inter font, design tokens
- AppNavbar: sticky, scroll shadow, mobile hamburger
- AppFooter: dark navy, 3 link columns, copyright bar
- HeroSection: pill badge, H1, CTAs, trust row, screenshot
- PrivacyBanner: dark, 3 stat icons (Server/Shield/WifiOff)
- SeeItLiveSection: gate kiosk screenshot showcase
- FeaturesSection: 6-card grid with Lucide icons
- ScreenshotsSection: Student Directory + Reports.jpg cards
- HowItWorksSection: 3 steps + billing card
- SupportSection: 5 support cards + quote card
- ContactSection: form with Formspree, validation, success state
- v-reveal directive: Intersection Observer scroll animations
- SEO meta tags + Open Graph + Twitter Card
- vercel.json SPA rewrite rule
- Build: 100KB JS / 19KB CSS, 0 errors
This commit is contained in:
kevin-asprec
2026-03-17 04:01:44 +08:00
parent 8c37e5bb45
commit 5376d1b1d5
34 changed files with 6619 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
<script setup lang="ts">
import { ref } from 'vue'
const form = ref({
school: '',
contact: '',
phone: '',
})
const submitted = ref(false)
const submitting = ref(false)
const error = ref('')
const bulletPoints = [
'✓ Free consultation call with our team',
'✓ Custom quote based on your school size',
'✓ Demo of the live system before you commit',
]
// Replace with your actual Formspree form ID: https://formspree.io
const FORMSPREE_URL = 'https://formspree.io/f/YOUR_FORM_ID'
async function handleSubmit() {
error.value = ''
if (!form.value.school || !form.value.contact || !form.value.phone) {
error.value = 'Please fill in all fields before submitting.'
return
}
submitting.value = true
try {
const res = await fetch(FORMSPREE_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
body: JSON.stringify({
school: form.value.school,
contact: form.value.contact,
phone: form.value.phone,
}),
})
if (res.ok) {
submitted.value = true
} else {
error.value = 'Something went wrong. Please try again or email us directly.'
}
} catch {
error.value = 'Network error. Please check your connection and try again.'
} finally {
submitting.value = false
}
}
</script>
<template>
<section id="contact" class="bg-[#0F172A]">
<div class="page-container flex flex-col lg:flex-row items-start lg:items-center gap-16 xl:gap-20 py-20">
<!-- Left: Copy -->
<div v-reveal class="flex-1 flex flex-col gap-5 min-w-0">
<span class="text-[#60A5FA] font-semibold text-[11px] tracking-[2px] uppercase">
Get in Touch
</span>
<h2 class="text-white font-bold text-[clamp(28px,3vw,40px)] leading-[1.15] max-w-[520px]">
Ready to bring TapTrack<br class="hidden sm:block" /> to your school?
</h2>
<p class="text-[#94A3B8] text-[16px] leading-[1.65] max-w-[520px]">
Tell us about your school and we'll get back to you with a tailored proposal — including implementation scope, NFC hardware, and SMS credit packages that fit your needs.
</p>
<div class="flex flex-col gap-2.5 mt-1">
<span
v-for="point in bulletPoints"
:key="point"
class="text-[#CBD5E1] text-[14px]"
>
{{ point }}
</span>
</div>
</div>
<!-- Right: Form card -->
<div v-reveal="{ delay: 150 }" class="w-full lg:w-[480px] shrink-0">
<div class="bg-[#1E293B] rounded-2xl p-8 lg:p-9 flex flex-col gap-5">
<div>
<h3 class="text-white font-bold text-xl">Request a Demo</h3>
<p class="text-[#64748B] text-[13px] mt-1">Fill this out and we'll reach you within 24 hours.</p>
</div>
<!-- Success state -->
<div v-if="submitted" class="flex flex-col items-center gap-3 py-6 text-center">
<div class="w-12 h-12 rounded-full bg-[#059669] flex items-center justify-center">
<span class="text-white font-bold text-xl"></span>
</div>
<p class="text-white font-semibold text-base">Request sent!</p>
<p class="text-[#94A3B8] text-sm">We'll get back to you within 24 hours.</p>
</div>
<!-- Form -->
<form v-else class="flex flex-col gap-4" @submit.prevent="handleSubmit">
<div class="flex flex-col gap-1.5">
<label class="text-[#CBD5E1] font-medium text-[13px]">School Name</label>
<input
v-model="form.school"
type="text"
placeholder="e.g. San Pedro Elementary School"
class="w-full h-[42px] bg-[#0F172A] border border-[#334155] rounded-lg px-3 text-[13px] text-white placeholder:text-[#475569] focus:outline-none focus:border-[#1D4ED8] transition-colors"
/>
</div>
<div class="flex flex-col gap-1.5">
<label class="text-[#CBD5E1] font-medium text-[13px]">Contact Person</label>
<input
v-model="form.contact"
type="text"
placeholder="Principal / Administrator name"
class="w-full h-[42px] bg-[#0F172A] border border-[#334155] rounded-lg px-3 text-[13px] text-white placeholder:text-[#475569] focus:outline-none focus:border-[#1D4ED8] transition-colors"
/>
</div>
<div class="flex flex-col gap-1.5">
<label class="text-[#CBD5E1] font-medium text-[13px]">Phone / Email</label>
<input
v-model="form.phone"
type="text"
placeholder="+63 9XX XXX XXXX or email@school.edu.ph"
class="w-full h-[42px] bg-[#0F172A] border border-[#334155] rounded-lg px-3 text-[13px] text-white placeholder:text-[#475569] focus:outline-none focus:border-[#1D4ED8] transition-colors"
/>
</div>
<p v-if="error" class="text-red-400 text-[13px]">{{ error }}</p>
<button
type="submit"
:disabled="submitting"
class="w-full bg-[#1D4ED8] hover:bg-[#1A43C0] disabled:opacity-60 text-white font-semibold text-[15px] py-[13px] rounded-lg transition-colors duration-150 mt-1 cursor-pointer"
>
{{ submitting ? 'Sending' : 'Send Request' }}
</button>
</form>
</div>
</div>
</div>
</section>
</template>