276 lines
10 KiB
Vue
276 lines
10 KiB
Vue
<template>
|
|
<div class="max-w-6xl mx-auto p-8 bg-gradient-to-b from-blue-50 to-indigo-50 shadow-lg rounded-xl">
|
|
<!-- Judul -->
|
|
<div class="text-center mb-10">
|
|
<h1 class="text-3xl md:text-4xl font-extrabold text-indigo-700 drop-shadow-sm">
|
|
🎂 Form Pemesanan Undangan Ulang Tahun 🎉
|
|
</h1>
|
|
<p class="text-gray-600 mt-2">Isi data berikut untuk membuat undangan ulang tahun anak Anda</p>
|
|
</div>
|
|
|
|
<form @submit.prevent="submitForm" class="space-y-10">
|
|
|
|
<!-- Pilih Paket -->
|
|
<section class="p-6 bg-white rounded-xl shadow-sm border border-gray-200">
|
|
<h2 class="text-lg font-bold text-gray-800 mb-4">Pilih Paket</h2>
|
|
<div class="flex flex-col md:flex-row gap-4">
|
|
<label v-for="paket in paketList" :key="paket.id"
|
|
class="flex-1 p-4 border rounded-xl cursor-pointer hover:shadow-lg transition-all"
|
|
:class="{'border-blue-600 bg-blue-50': form.selectedPaket === paket.id}">
|
|
<input type="radio" class="hidden" v-model="form.selectedPaket" :value="paket.id" />
|
|
<div class="font-semibold text-gray-800">{{ paket.nama }}</div>
|
|
<div class="text-gray-600">{{ paket.deskripsi }}</div>
|
|
<div class="font-bold mt-2 text-blue-700">{{ formatRupiah(paket.harga) }}</div>
|
|
</label>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Info Template -->
|
|
<section class="p-6 bg-white rounded-xl shadow-sm border border-gray-200">
|
|
<h2 class="text-lg font-bold text-gray-800 mb-4">Template</h2>
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
<input v-model="form.nama_template" type="text" placeholder="Nama Template" class="input-readonly" readonly />
|
|
<input v-model="form.kategori" type="text" placeholder="Kategori" class="input-readonly" readonly />
|
|
<input v-model="form.harga" type="text" placeholder="Harga" class="input-readonly" readonly />
|
|
<input v-model="form.tanggal_pemesanan" type="text" placeholder="Tanggal Pemesanan" class="input-readonly" readonly />
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Data Pemesan -->
|
|
<section class="p-6 bg-white rounded-xl shadow-sm border border-gray-200">
|
|
<h2 class="text-lg font-bold text-gray-800 mb-4">Data Pemesan</h2>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-sm text-gray-600 mb-1">Nama Pemesan</label>
|
|
<input v-model="form.nama_pemesan" type="text" class="input" required />
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm text-gray-600 mb-1">No. WhatsApp</label>
|
|
<input v-model="form.no_hp" type="text" class="input" required />
|
|
</div>
|
|
<div class="md:col-span-2">
|
|
<label class="block text-sm text-gray-600 mb-1">Email</label>
|
|
<input v-model="form.email" type="email" class="input" required />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Data Anak -->
|
|
<section class="p-6 bg-white rounded-xl shadow-sm border border-gray-200">
|
|
<h2 class="text-lg font-bold text-gray-800 mb-4">Data Anak</h2>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-sm mb-1">Nama Lengkap Anak</label>
|
|
<input v-model="form.nama_lengkap_anak" type="text" class="input" required />
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm mb-1">Nama Panggilan Anak</label>
|
|
<input v-model="form.nama_panggilan_anak" type="text" class="input" required />
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm mb-1">Nama Bapak</label>
|
|
<input v-model="form.bapak_anak" type="text" class="input" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm mb-1">Nama Ibu</label>
|
|
<input v-model="form.ibu_anak" type="text" class="input" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm mb-1">Ulang Tahun ke-</label>
|
|
<input v-model="form.umur_dirayakan" type="number" class="input" required />
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm mb-1">Anak ke-</label>
|
|
<input v-model="form.anak_ke" type="number" class="input" />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Detail Acara -->
|
|
<section class="p-6 bg-white rounded-xl shadow-sm border border-gray-200">
|
|
<h2 class="text-lg font-bold text-gray-800 mb-4">Detail Acara</h2>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-sm mb-1">Hari & Tanggal</label>
|
|
<input v-model="form.hari_tanggal_acara" type="date" class="input" required />
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm mb-1">Waktu Acara</label>
|
|
<input v-model="form.waktu_acara" type="text" class="input" required />
|
|
</div>
|
|
<div class="md:col-span-2">
|
|
<label class="block text-sm mb-1">Alamat Lengkap</label>
|
|
<textarea v-model="form.alamat_acara" class="input"></textarea>
|
|
</div>
|
|
<div class="md:col-span-2">
|
|
<label class="block text-sm mb-1">Link Google Maps (opsional)</label>
|
|
<input v-model="form.maps_acara" type="text" class="input" />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Galeri Foto -->
|
|
<section v-if="form.selectedPaket === 'basic' || form.selectedPaket === 'premium'" class="p-6 bg-white rounded-xl shadow-sm border border-gray-200">
|
|
<h2 class="text-lg font-bold text-gray-800 mb-4">
|
|
Galeri Foto
|
|
<span v-if="form.selectedPaket === 'basic'">(max 6 gambar)</span>
|
|
<span v-if="form.selectedPaket === 'premium'">(unlimited)</span>
|
|
</h2>
|
|
|
|
<label for="gallery-upload"
|
|
class="cursor-pointer bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-medium inline-block mb-4">
|
|
+ Tambah Foto
|
|
</label>
|
|
<input type="file" multiple accept="image/*" @change="handleFileUpload" class="hidden" id="gallery-upload" />
|
|
|
|
<div class="grid grid-cols-2 md:grid-cols-3 gap-3">
|
|
<div v-for="(img, i) in previewImages" :key="i"
|
|
class="relative w-full aspect-square rounded-lg overflow-hidden shadow-sm group">
|
|
<img :src="img" alt="Preview" class="object-cover w-full h-full" />
|
|
<button type="button" @click="removeImage(i)"
|
|
class="absolute top-1 right-1 bg-red-600 text-white rounded-full w-6 h-6 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity font-bold">
|
|
×
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Upload Video (Premium Only) -->
|
|
<section v-if="form.selectedPaket === 'premium'" class="p-6 bg-white rounded-xl shadow-sm border border-gray-200">
|
|
<h2 class="text-lg font-bold text-gray-800 mb-4">Video Ucapan (Premium)</h2>
|
|
<input type="file" accept="video/*" @change="handleVideoUpload" class="block w-full text-sm text-gray-600" />
|
|
<div v-if="previewVideo" class="mt-4">
|
|
<video controls class="w-full rounded-lg shadow">
|
|
<source :src="previewVideo" type="video/mp4" />
|
|
Browser Anda tidak mendukung video.
|
|
</video>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Submit -->
|
|
<div class="mt-10 text-center">
|
|
<button type="submit"
|
|
class="bg-blue-600 text-white px-10 py-3 rounded-xl font-semibold shadow-md hover:bg-blue-700 hover:shadow-lg transition"
|
|
:disabled="loading">
|
|
{{ loading ? "Mengirim..." : "Kirim & Konfirmasi Admin" }}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Alert -->
|
|
<div v-if="success" class="mt-6 p-4 text-green-800 bg-green-100 rounded-lg text-center font-medium">
|
|
✅ Form berhasil dikirim! Tunggu konfirmasi admin.
|
|
</div>
|
|
<div v-if="error" class="mt-6 p-4 text-red-800 bg-red-100 rounded-lg text-center font-medium">
|
|
❌ Gagal mengirim form. Cek kembali inputan Anda.
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref } from "vue"
|
|
|
|
const paketList = ref([
|
|
{ id: "starter", nama: "Starter", deskripsi: "Fitur dasar (tanpa galeri)", harga: 100000 },
|
|
{ id: "basic", nama: "Basic", deskripsi: "Tambahan galeri (max 6 foto)", harga: 200000 },
|
|
{ id: "premium", nama: "Premium", deskripsi: "Unlimited galeri + upload video", harga: 350000 },
|
|
])
|
|
|
|
const form = ref({
|
|
selectedPaket: "",
|
|
nama_template: "",
|
|
kategori: "Ulang Tahun",
|
|
harga: "",
|
|
tanggal_pemesanan: new Date().toLocaleDateString("id-ID"),
|
|
nama_pemesan: "",
|
|
no_hp: "",
|
|
email: "",
|
|
nama_lengkap_anak: "",
|
|
nama_panggilan_anak: "",
|
|
bapak_anak: "",
|
|
ibu_anak: "",
|
|
umur_dirayakan: "",
|
|
anak_ke: "",
|
|
hari_tanggal_acara: "",
|
|
waktu_acara: "",
|
|
alamat_acara: "",
|
|
maps_acara: "",
|
|
galeri: [],
|
|
video: null
|
|
})
|
|
|
|
const previewImages = ref([])
|
|
const previewVideo = ref(null)
|
|
|
|
const loading = ref(false)
|
|
const success = ref(false)
|
|
const error = ref(false)
|
|
|
|
const formatRupiah = (num) => new Intl.NumberFormat("id-ID", { style: "currency", currency: "IDR" }).format(num)
|
|
|
|
const handleFileUpload = (event) => {
|
|
const files = Array.from(event.target.files)
|
|
|
|
if (form.value.selectedPaket === "basic" && (form.value.galeri.length + files.length) > 6) {
|
|
alert("Paket Basic hanya bisa upload maksimal 6 foto!")
|
|
return
|
|
}
|
|
|
|
form.value.galeri.push(...files)
|
|
files.forEach(file => {
|
|
const reader = new FileReader()
|
|
reader.onload = e => previewImages.value.push(e.target.result)
|
|
reader.readAsDataURL(file)
|
|
})
|
|
}
|
|
|
|
const handleVideoUpload = (event) => {
|
|
const file = event.target.files[0]
|
|
if (file) {
|
|
form.value.video = file
|
|
const reader = new FileReader()
|
|
reader.onload = e => previewVideo.value = e.target.result
|
|
reader.readAsDataURL(file)
|
|
}
|
|
}
|
|
|
|
const removeImage = (index) => {
|
|
form.value.galeri.splice(index, 1)
|
|
previewImages.value.splice(index, 1)
|
|
}
|
|
|
|
const submitForm = async () => {
|
|
try {
|
|
loading.value = true
|
|
success.value = false
|
|
error.value = false
|
|
|
|
console.log("Data dikirim:", form.value)
|
|
await new Promise(res => setTimeout(res, 1000))
|
|
|
|
success.value = true
|
|
} catch (err) {
|
|
error.value = true
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.input {
|
|
border: 1px solid #d1d5db;
|
|
padding: 0.5rem 0.75rem;
|
|
border-radius: 0.5rem;
|
|
width: 100%;
|
|
font-size: 0.875rem;
|
|
}
|
|
.input-readonly {
|
|
border: 1px solid #d1d5db;
|
|
padding: 0.5rem 0.75rem;
|
|
border-radius: 0.5rem;
|
|
background-color: #f9fafb;
|
|
color: #4b5563;
|
|
}
|
|
</style>
|