356 lines
14 KiB
Vue
356 lines
14 KiB
Vue
<template>
|
|
<div class="max-w-5xl mx-auto p-8 bg-gradient-to-b from-pink-50 to-red-50 shadow-lg rounded-xl">
|
|
<!-- Judul Form -->
|
|
<div class="text-center mb-10">
|
|
<h1 class="text-3xl md:text-4xl font-extrabold text-red-700 drop-shadow-sm">
|
|
💍 Form Pemesanan Undangan Pernikahan 💐
|
|
</h1>
|
|
<p class="text-gray-600 mt-2">
|
|
Silakan isi data berikut untuk melakukan pemesanan undangan pernikahan Anda.
|
|
</p>
|
|
</div>
|
|
|
|
<form @submit.prevent="submitForm" class="space-y-10">
|
|
|
|
<!-- Tema Undangan -->
|
|
<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>
|
|
|
|
<!-- Pemesan Undangan -->
|
|
<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> Pemesan Undangan
|
|
</h2>
|
|
<div class="space-y-4">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<input v-model="form.nama_pemesan" type="text" placeholder="Nama" class="input" required />
|
|
<input v-model="form.no_hp" type="text" placeholder="No. WhatsApp" class="input" required />
|
|
</div>
|
|
<input v-model="form.email" type="email" placeholder="Email" class="input" required />
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Mempelai -->
|
|
<section class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div 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">Mempelai Pria</h2>
|
|
<input v-model="form.nama_lengkap_pria" type="text" placeholder="Nama Lengkap" class="input" required />
|
|
<input v-model="form.nama_panggilan_pria" type="text" placeholder="Nama Panggilan" class="input" required />
|
|
<input v-model="form.bapak_pria" type="text" placeholder="Nama Bapak" class="input" />
|
|
<input v-model="form.ibu_pria" type="text" placeholder="Nama Ibu" class="input" />
|
|
<input v-model="form.instagram_pria" type="text" placeholder="Instagram" class="input" />
|
|
<input v-model="form.facebook_pria" type="text" placeholder="Facebook" class="input" />
|
|
<input v-model="form.twitter_pria" type="text" placeholder="Twitter" class="input" />
|
|
</div>
|
|
<div 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">Mempelai Wanita</h2>
|
|
<input v-model="form.nama_lengkap_wanita" type="text" placeholder="Nama Lengkap" class="input" required />
|
|
<input v-model="form.nama_panggilan_wanita" type="text" placeholder="Nama Panggilan" class="input" required />
|
|
<input v-model="form.bapak_wanita" type="text" placeholder="Nama Bapak" class="input" />
|
|
<input v-model="form.ibu_wanita" type="text" placeholder="Nama Ibu" class="input" />
|
|
<input v-model="form.instagram_wanita" type="text" placeholder="Instagram" class="input" />
|
|
<input v-model="form.facebook_wanita" type="text" placeholder="Facebook" class="input" />
|
|
<input v-model="form.twitter_wanita" type="text" placeholder="Twitter" class="input" />
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Cerita Kita -->
|
|
<section class="p-6 bg-white rounded-xl shadow-sm border border-gray-200">
|
|
<h2 class="text-lg font-bold text-gray-800 mb-10">Cerita Kita</h2>
|
|
<textarea v-model="form.cerita_kita" placeholder="Tuliskan cerita indah kalian di sini..." rows="1"
|
|
class="w-full border border-gray-300 rounded-md px-3 py-3 focus:ring-2 focus:ring-blue-500 focus:outline-none transition resize-none"
|
|
@input="e => { e.target.style.height = 'auto'; e.target.style.height = e.target.scrollHeight + 'px' }" />
|
|
|
|
|
|
</section>
|
|
|
|
<!-- Akad & Resepsi -->
|
|
<section class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div 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">Akad</h2>
|
|
<input v-model="form.hari_tanggal_akad" type="date" class="input" />
|
|
<input v-model="form.waktu_akad" type="text" placeholder="Waktu" class="input" />
|
|
<input v-model="form.alamat_akad" type="text" placeholder="Alamat" class="input" />
|
|
<input v-model="form.maps_akad" type="text" placeholder="Link Google Maps" class="input" />
|
|
</div>
|
|
<div 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">Resepsi</h2>
|
|
<input v-model="form.hari_tanggal_resepsi" type="date" class="input" />
|
|
<input v-model="form.waktu_resepsi" type="text" placeholder="Waktu" class="input" />
|
|
<input v-model="form.alamat_resepsi" type="text" placeholder="Alamat" class="input" />
|
|
<input v-model="form.maps_resepsi" type="text" placeholder="Link Google Maps" class="input" />
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Rekening, Musik, Galeri -->
|
|
<section class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div class="space-y-6">
|
|
<div 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">No. Rekening</h2>
|
|
<input v-model="form.no_rekening1" type="text" placeholder="Rekening 1" class="input" />
|
|
<input v-model="form.no_rekening2" type="text" placeholder="Rekening 2" class="input" />
|
|
</div>
|
|
<div class="p-6 bg-white rounded-xl shadow-sm border border-gray-200">
|
|
<h2 class="text-lg font-bold text-gray-800">Musik</h2>
|
|
<input v-model="form.link_musik" type="text" placeholder="Link Musik" class="input" />
|
|
</div>
|
|
</div>
|
|
<div 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 (max 5 gambar)</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"
|
|
aria-label="Hapus gambar">
|
|
×
|
|
</button>
|
|
</div>
|
|
<label v-if="previewImages.length < 5" 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>
|
|
</div>
|
|
</section>
|
|
</form>
|
|
|
|
<!-- Kategori & Fitur -->
|
|
<section v-for="kategori in kategoriFiturs" :key="kategori.id"
|
|
class="p-6 bg-white rounded-xl shadow-sm border border-gray-200">
|
|
<h2 class="text-lg font-bold text-gray-800 mb-4">
|
|
{{ kategori.nama }}
|
|
</h2>
|
|
|
|
<!-- Radio -->
|
|
<div v-if="kategori.tipe === 'radio'">
|
|
<label v-for="fitur in kategori.fiturs" :key="fitur.id" class="flex items-center gap-2 mb-2">
|
|
<input type="radio" :name="'kategori_' + kategori.id" :value="fitur.id"
|
|
v-model="form.selectedFiturs[kategori.id]" />
|
|
{{ fitur.deskripsi }}
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Checkbox -->
|
|
<div v-else>
|
|
<label v-for="fitur in kategori.fiturs" :key="fitur.id" class="flex items-center gap-2 mb-2">
|
|
<input type="checkbox" :value="fitur.id" v-model="form.selectedFiturs[kategori.id]" />
|
|
{{ fitur.deskripsi }}
|
|
</label>
|
|
</div>
|
|
</section>
|
|
|
|
|
|
<!-- Submit -->
|
|
<div class="mt-10 text-center">
|
|
<button @click="submitForm"
|
|
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! Silakan tunggu konfirmasi dari 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.
|
|
Pastikan semua data yang wajib diisi sudah lengkap.</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from "vue";
|
|
import { useRoute } from "vue-router";
|
|
|
|
const route = useRoute();
|
|
|
|
const form = ref({
|
|
template_id: "",
|
|
nama_template: "",
|
|
kategori: "",
|
|
harga: "",
|
|
tanggal_pemesanan: new Date().toLocaleDateString("id-ID", {
|
|
year: "numeric",
|
|
month: "long",
|
|
day: "numeric",
|
|
}),
|
|
|
|
nama_pemesan: "",
|
|
no_hp: "",
|
|
email: "",
|
|
|
|
nama_lengkap_pria: "",
|
|
nama_panggilan_pria: "",
|
|
bapak_pria: "",
|
|
ibu_pria: "",
|
|
instagram_pria: "",
|
|
facebook_pria: "",
|
|
twitter_pria: "",
|
|
|
|
nama_lengkap_wanita: "",
|
|
nama_panggilan_wanita: "",
|
|
bapak_wanita: "",
|
|
ibu_wanita: "",
|
|
instagram_wanita: "",
|
|
facebook_wanita: "",
|
|
twitter_wanita: "",
|
|
|
|
cerita_kita: "",
|
|
|
|
hari_tanggal_akad: "",
|
|
waktu_akad: "",
|
|
alamat_akad: "",
|
|
maps_akad: "",
|
|
|
|
hari_tanggal_resepsi: "",
|
|
waktu_resepsi: "",
|
|
alamat_resepsi: "",
|
|
maps_resepsi: "",
|
|
|
|
no_rekening1: "",
|
|
no_rekening2: "",
|
|
link_musik: "",
|
|
galeri: [],
|
|
|
|
|
|
selectedFiturs: {}, // { kategori_id: [fitur_id] }
|
|
});
|
|
|
|
const kategoriFiturs = ref([]); // 🆕 simpan kategori fitur
|
|
const previewImages = ref([]);
|
|
const loading = ref(false);
|
|
const success = ref(false);
|
|
const error = ref(false);
|
|
|
|
onMounted(async () => {
|
|
if (route.query.template_id) {
|
|
try {
|
|
const template = await $fetch(
|
|
`http://localhost:8000/api/templates/${route.query.template_id}`
|
|
);
|
|
|
|
form.value.template_id = template.id;
|
|
form.value.nama_template = template.nama_template;
|
|
form.value.kategori = template.kategori?.nama || "-";
|
|
form.value.harga = new Intl.NumberFormat("id-ID", {
|
|
style: "currency",
|
|
currency: "IDR",
|
|
}).format(template.harga);
|
|
|
|
// 🆕 simpan kategori fitur
|
|
kategoriFiturs.value = template.kategori_fiturs || [];
|
|
} catch (err) {
|
|
console.error("Gagal ambil template", err);
|
|
}
|
|
}
|
|
});
|
|
|
|
// FUNGSI UNTUK MENAMBAH GAMBAR
|
|
const handleFileUpload = (event) => {
|
|
const newFiles = Array.from(event.target.files);
|
|
const combinedFiles = [...form.value.galeri, ...newFiles];
|
|
|
|
form.value.galeri = combinedFiles.slice(0, 5);
|
|
|
|
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;
|
|
};
|
|
|
|
// FUNGSI UNTUK MENGHAPUS GAMBAR
|
|
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();
|
|
|
|
// field umum
|
|
for (const key in form.value) {
|
|
if (key === "galeri" || key === "selectedFiturs") continue;
|
|
if (form.value[key] !== null && form.value[key] !== undefined) {
|
|
body.append(key, form.value[key]);
|
|
}
|
|
}
|
|
|
|
// galeri
|
|
form.value.galeri.forEach((file) => body.append("galeri[]", file));
|
|
|
|
// 🆕 fiturs
|
|
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/pernikahan", {
|
|
method: "POST",
|
|
body,
|
|
});
|
|
|
|
success.value = true;
|
|
|
|
const adminNumber = "62895602603247";
|
|
const message = `
|
|
Halo Admin, ada pemesanan undangan pernikahan baru 🎉
|
|
|
|
Nama Pemesan: ${form.value.nama_pemesan}
|
|
No WA: ${form.value.no_hp}
|
|
Email: ${form.value.email}
|
|
|
|
Mempelai Pria: ${form.value.nama_lengkap_pria} (${form.value.nama_panggilan_pria})
|
|
Mempelai Wanita: ${form.value.nama_lengkap_wanita} (${form.value.nama_panggilan_wanita})
|
|
|
|
Akad: ${form.value.hari_tanggal_akad} | ${form.value.waktu_akad}
|
|
Alamat: ${form.value.alamat_akad}
|
|
|
|
Resepsi: ${form.value.hari_tanggal_resepsi} | ${form.value.waktu_resepsi}
|
|
Alamat: ${form.value.alamat_resepsi}
|
|
|
|
Template: ${form.value.nama_template} (${form.value.kategori})
|
|
Harga: ${form.value.harga}
|
|
Tanggal 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>
|