283 lines
15 KiB
Vue
283 lines
15 KiB
Vue
<template>
|
|
<div class="max-w-5xl mx-auto p-8 bg-gradient-to-b from-green-50 to-blue-50 shadow-lg rounded-xl">
|
|
<!-- Judul Form -->
|
|
<div class="text-center mb-10">
|
|
<h1 class="text-3xl md:text-4xl font-extrabold text-green-700 drop-shadow-sm">
|
|
🕌 Form Pemesanan Undangan Khitan Premium ✨
|
|
</h1>
|
|
<p class="text-gray-600 mt-2">
|
|
Isi data berikut dengan lengkap untuk pemesanan undangan khitan.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Info Paket Premium -->
|
|
<section class="p-6 bg-white rounded-xl shadow-sm border border-gray-200 mb-10">
|
|
<h2 class="text-lg font-bold text-gray-800 mb-4">Paket Premium (Informasi)</h2>
|
|
<ul class="list-disc pl-5 space-y-1 text-gray-700 text-sm">
|
|
<li>Maksimal 3x Acara (Akad, Resepsi, Syukuran)</li>
|
|
<li>Unlimited Galeri Foto</li>
|
|
<li>Timeline Story</li>
|
|
<li>Google Maps</li>
|
|
<li>Reminder Google Calendar</li>
|
|
<li>Link Instagram Live Streaming</li>
|
|
<li>Amplop Digital</li>
|
|
<li>Placement Video Cinematic</li>
|
|
<li>Bonus Undangan Image Post Story</li>
|
|
<li>Masa Aktif 12 Bulan</li>
|
|
<li>Fitur standar: Nama Tamu Personal Unlimited Tamu, Request Musik</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<!-- Form Pemesanan -->
|
|
<form @submit.prevent="submitForm" class="space-y-10">
|
|
|
|
<!-- Tema Undangan (otomatis isi) -->
|
|
<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 flex items-center gap-2">
|
|
<span class="w-1.5 h-6 bg-blue-600 rounded-full"></span> Tema Undangan
|
|
</h2>
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
<input :value="form.nama_template" type="text" placeholder="Nama Template" class="input-readonly" readonly />
|
|
<input :value="form.kategori" type="text" placeholder="Kategori" class="input-readonly" readonly />
|
|
<input :value="form.harga" type="text" placeholder="Harga" class="input-readonly" readonly />
|
|
<input :value="form.tanggal_pemesanan" type="text" placeholder="Tanggal Pemesanan" class="input-readonly" readonly />
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Anak yang dikhitan -->
|
|
<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">Anak yang Dikhitan</h2>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div class="relative">
|
|
<input v-model="form.nama_anak" id="nama_anak" type="text" placeholder=" " required class="peer w-full border border-gray-300 rounded-lg px-3 pt-5 pb-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500" />
|
|
<label for="nama_anak" class="absolute left-2 top-1 text-gray-500 text-xs transition-all duration-200 peer-placeholder-shown:top-5 peer-placeholder-shown:text-sm peer-placeholder-shown:text-gray-400 peer-focus:top-1 peer-focus:text-xs peer-focus:text-green-600">Nama Anak</label>
|
|
</div>
|
|
<div class="relative">
|
|
<input v-model="form.umur_anak" id="umur_anak" type="number" placeholder=" " class="peer w-full border border-gray-300 rounded-lg px-3 pt-5 pb-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500" />
|
|
<label for="umur_anak" class="absolute left-2 top-1 text-gray-500 text-xs transition-all duration-200 peer-placeholder-shown:top-5 peer-placeholder-shown:text-sm peer-placeholder-shown:text-gray-400 peer-focus:top-1 peer-focus:text-xs peer-focus:text-green-600">Umur Anak</label>
|
|
</div>
|
|
<div class="relative md:col-span-2">
|
|
<input v-model="form.tempat_lahir" id="tempat_lahir" type="text" placeholder=" " class="peer w-full border border-gray-300 rounded-lg px-3 pt-5 pb-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500" />
|
|
<label for="tempat_lahir" class="absolute left-2 top-1 text-gray-500 text-xs transition-all duration-200 peer-placeholder-shown:top-5 peer-placeholder-shown:text-sm peer-placeholder-shown:text-gray-400 peer-focus:top-1 peer-focus:text-xs peer-focus:text-green-600">Tempat Lahir</label>
|
|
</div>
|
|
|
|
<!-- Informasi Orang Tua -->
|
|
<div class="relative md:col-span-2">
|
|
<input v-model="form.nama_orang_tua" type="text" placeholder="Nama Orang Tua" class="peer w-full border border-gray-300 rounded-lg px-3 pt-5 pb-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500" />
|
|
<label class="absolute left-2 top-1 text-gray-500 text-xs transition-all duration-200 peer-placeholder-shown:top-5 peer-placeholder-shown:text-sm peer-placeholder-shown:text-gray-400 peer-focus:top-1 peer-focus:text-xs peer-focus:text-green-600">Nama Orang Tua</label>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Jadwal Acara -->
|
|
<section class="p-6 bg-white rounded-xl shadow-sm border border-gray-200 space-y-4">
|
|
<h2 class="text-lg font-bold text-gray-800">Jadwal Acara & Countdown</h2>
|
|
<div v-for="(acara, index) in form.jadwal_acara" :key="index" class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-3">
|
|
<input v-model="acara.nama_acara" type="text" placeholder="Nama Acara" class="peer w-full border border-gray-300 rounded-lg px-3 pt-5 pb-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500" />
|
|
<input v-model="acara.tanggal" type="date" placeholder="Tanggal" class="peer w-full border border-gray-300 rounded-lg px-3 pt-5 pb-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500" />
|
|
<input v-model="acara.waktu" type="text" placeholder="Waktu" class="peer w-full border border-gray-300 rounded-lg px-3 pt-5 pb-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500" />
|
|
<div class="text-sm font-medium text-red-600 flex items-center justify-center">
|
|
{{ countdowns[index] }}
|
|
</div>
|
|
</div>
|
|
<button type="button" @click="addAcara" class="text-green-700 font-semibold">+ Tambah Acara</button>
|
|
</section>
|
|
|
|
<!-- Fitur Premium & Amplop Digital -->
|
|
<!-- Bagian Fitur Premium & Amplop Digital diperbarui -->
|
|
<section class="p-6 bg-white rounded-xl shadow-sm border border-gray-200 space-y-4">
|
|
<h2 class="text-lg font-bold text-gray-800">Fitur Premium</h2>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div class="relative">
|
|
<input v-model="form.maps_acara" type="url" placeholder="" class="peer w-full border border-gray-300 rounded-lg px-3 pt-5 pb-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500" />
|
|
<label class="absolute left-2 top-1 text-gray-500 text-xs transition-all duration-200 peer-placeholder-shown:top-5 peer-placeholder-shown:text-sm peer-placeholder-shown:text-gray-400 peer-focus:top-1 peer-focus:text-xs peer-focus:text-green-600">Link Google Maps</label>
|
|
</div>
|
|
<div class="relative">
|
|
<input v-model="form.link_live" type="url" placeholder="" class="peer w-full border border-gray-300 rounded-lg px-3 pt-5 pb-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500" />
|
|
<label class="absolute left-2 top-1 text-gray-500 text-xs transition-all duration-200 peer-placeholder-shown:top-5 peer-placeholder-shown:text-sm peer-placeholder-shown:text-gray-400 peer-focus:top-1 peer-focus:text-xs peer-focus:text-green-600">Link Instagram Live</label>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-2">
|
|
<input type="checkbox" v-model="form.amplop_digital" /> Amplop Digital
|
|
</div>
|
|
<div class="relative" v-if="form.amplop_digital">
|
|
<input v-model="form.no_rekening" type="text" placeholder="Nomor Rekening" class="peer w-full border border-gray-300 rounded-lg px-3 pt-5 pb-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500" />
|
|
<label class="absolute left-2 top-1 text-gray-500 text-xs transition-all duration-200 peer-placeholder-shown:top-5 peer-placeholder-shown:text-sm peer-placeholder-shown:text-gray-400 peer-focus:top-1 peer-focus:text-xs peer-focus:text-green-600">No. Rekening</label>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-2">
|
|
<input type="checkbox" v-model="form.placement_video" /> Placement Video Cinematic
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<input type="checkbox" v-model="form.bonus_image_post" /> Bonus Undangan Image Post Story
|
|
</div>
|
|
|
|
<!-- Reminder Google Calendar -->
|
|
<div class="flex items-center gap-2">
|
|
<input type="checkbox" v-model="form.reminder_calendar" /> Reminder Google Calendar
|
|
</div>
|
|
<div class="relative" v-if="form.reminder_calendar">
|
|
<input v-model="form.reminder_notes" type="text" placeholder="Catatan Reminder (opsional)" class="peer w-full border border-gray-300 rounded-lg px-3 pt-5 pb-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500" />
|
|
<label class="absolute left-2 top-1 text-gray-500 text-xs transition-all duration-200 peer-placeholder-shown:top-5 peer-placeholder-shown:text-sm peer-placeholder-shown:text-gray-400 peer-focus:top-1 peer-focus:text-xs peer-focus:text-green-600">Catatan Reminder</label>
|
|
</div>
|
|
|
|
<div class="relative md:col-span-2">
|
|
<textarea v-model="form.timeline_story" rows="3" placeholder="" class="peer w-full border border-gray-300 rounded-lg px-3 pt-5 pb-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500 resize-none"></textarea>
|
|
<label class="absolute left-2 top-1 text-gray-500 text-xs transition-all duration-200 peer-placeholder-shown:top-5 peer-placeholder-shown:text-sm peer-placeholder-shown:text-gray-400 peer-focus:top-1 peer-focus:text-xs peer-focus:text-green-600">Timeline Story</label>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Galeri Upload -->
|
|
<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">Galeri</h2>
|
|
<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>
|
|
<label for="gallery-upload" class="flex items-center justify-center w-full aspect-square bg-gray-50 border-2 border-dashed border-gray-300 rounded-lg cursor-pointer hover:bg-gray-100 transition">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
|
</svg>
|
|
</label>
|
|
</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!
|
|
</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. Pastikan semua data yang wajib diisi sudah lengkap.
|
|
</div>
|
|
|
|
</form>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from "vue";
|
|
|
|
const form = ref({
|
|
template_id: "",
|
|
nama_template: "",
|
|
kategori: "",
|
|
harga: "",
|
|
tanggal_pemesanan: new Date().toISOString().split("T")[0],
|
|
nama_pemesan: "",
|
|
no_hp: "",
|
|
email: "",
|
|
nama_anak: "",
|
|
umur_anak: "",
|
|
tempat_lahir: "",
|
|
nama_orang_tua: "",
|
|
jadwal_acara: [{ nama_acara: "", tanggal: "", waktu: "" }],
|
|
maps_acara: "",
|
|
link_live: "",
|
|
amplop_digital: false,
|
|
no_rekening: "",
|
|
placement_video: false,
|
|
bonus_image_post: false,
|
|
timeline_story: "",
|
|
galeri: [],
|
|
selectedFiturs: {},
|
|
});
|
|
|
|
const previewImages = ref([]);
|
|
const loading = ref(false);
|
|
const success = ref(false);
|
|
const error = ref(false);
|
|
const countdowns = ref([""]); // array countdown tiap acara
|
|
|
|
const addAcara = () => {
|
|
if (form.value.jadwal_acara.length < 3) {
|
|
form.value.jadwal_acara.push({ nama_acara: "", tanggal: "", waktu: "" });
|
|
countdowns.value.push("");
|
|
}
|
|
};
|
|
|
|
// Countdown real-time
|
|
const updateCountdowns = () => {
|
|
form.value.jadwal_acara.forEach((acara, i) => {
|
|
if (!acara.tanggal) {
|
|
countdowns.value[i] = "-";
|
|
return;
|
|
}
|
|
const acaraTime = new Date(`${acara.tanggal}T${acara.waktu || "00:00"}`).getTime();
|
|
const now = Date.now();
|
|
const diff = acaraTime - now;
|
|
|
|
if (diff <= 0) {
|
|
countdowns.value[i] = "Acara sedang berlangsung / selesai";
|
|
} else {
|
|
const d = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
const h = Math.floor((diff / (1000 * 60 * 60)) % 24);
|
|
const m = Math.floor((diff / (1000 * 60)) % 60);
|
|
const s = Math.floor((diff / 1000) % 60);
|
|
countdowns.value[i] = `${d}d ${h}h ${m}m ${s}s`;
|
|
}
|
|
});
|
|
};
|
|
|
|
onMounted(() => {
|
|
setInterval(updateCountdowns, 1000);
|
|
});
|
|
|
|
const handleFileUpload = (event) => {
|
|
const newFiles = Array.from(event.target.files);
|
|
form.value.galeri.push(...newFiles);
|
|
previewImages.value = [];
|
|
form.value.galeri.forEach(file => {
|
|
const reader = new FileReader();
|
|
reader.onload = e => previewImages.value.push(e.target.result);
|
|
reader.readAsDataURL(file);
|
|
});
|
|
event.target.value = null;
|
|
};
|
|
|
|
const removeImage = (index) => {
|
|
form.value.galeri.splice(index, 1);
|
|
previewImages.value.splice(index, 1);
|
|
};
|
|
|
|
const submitForm = async () => {
|
|
loading.value = true;
|
|
success.value = false;
|
|
error.value = false;
|
|
|
|
try {
|
|
const body = new FormData();
|
|
for (const key in form.value) {
|
|
if (key === "galeri") form.value.galeri.forEach(f => body.append("galeri[]", f));
|
|
else if (key === "jadwal_acara") form.value.jadwal_acara.forEach(a => body.append("jadwal_acara[]", JSON.stringify(a)));
|
|
else if (key !== "selectedFiturs") body.append(key, form.value[key]);
|
|
}
|
|
for (const kategoriId in form.value.selectedFiturs) {
|
|
const fiturs = Array.isArray(form.value.selectedFiturs[kategoriId]) ? form.value.selectedFiturs[kategoriId] : [form.value.selectedFiturs[kategoriId]];
|
|
fiturs.forEach(fiturId => body.append("fiturs[]", fiturId));
|
|
}
|
|
|
|
await $fetch("http://localhost:8000/api/form", { method: "POST", body, headers: { Accept: "application/json" } });
|
|
success.value = true;
|
|
|
|
const adminNumber = "62895602603247";
|
|
const message = `Halo Admin, ada pemesanan undangan khitan baru 🎉\nNama Pemesan: ${form.value.nama_pemesan}\nNo WA: ${form.value.no_hp}\nEmail: ${form.value.email}\nTemplate: ${form.value.nama_template} (${form.value.kategori})\nHarga: ${form.value.harga}\nTanggal Pemesanan: ${form.value.tanggal_pemesanan}`;
|
|
window.location.href = `https://wa.me/${adminNumber}?text=${encodeURIComponent(message)}`;
|
|
} catch (err) {
|
|
console.error(err);
|
|
error.value = true;
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
</script>
|