form ulang tahun
This commit is contained in:
parent
c2fb27454c
commit
3ae13bb064
@ -32,9 +32,21 @@ class UlangTahunApiController extends Controller
|
|||||||
'alamat_acara' => 'required|string',
|
'alamat_acara' => 'required|string',
|
||||||
'maps_acara' => 'nullable|string',
|
'maps_acara' => 'nullable|string',
|
||||||
'link_musik' => 'nullable|string',
|
'link_musik' => 'nullable|string',
|
||||||
'galeri' => 'nullable|string',
|
// --- PERBAIKAN VALIDASI GALERI ---
|
||||||
|
'galeri' => 'nullable|array|max:5',
|
||||||
|
'galeri.*' => 'image|mimes:jpeg,png,jpg,gif|max:2048',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// --- PROSES UPLOAD GAMBAR ---
|
||||||
|
$galleryPaths = [];
|
||||||
|
if ($request->hasFile('galeri')) {
|
||||||
|
foreach ($request->file('galeri') as $file) {
|
||||||
|
$path = $file->store('gallery', 'public');
|
||||||
|
$galleryPaths[] = $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$data['galeri'] = $galleryPaths;
|
||||||
|
|
||||||
// ✅ Ambil template berdasarkan template_id
|
// ✅ Ambil template berdasarkan template_id
|
||||||
$template = Template::with('kategori')->findOrFail($data['template_id']);
|
$template = Template::with('kategori')->findOrFail($data['template_id']);
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,12 @@ class TemplateController extends Controller
|
|||||||
return view('admin.templates.index', compact('templates', 'kategoris', 'fiturs'));
|
return view('admin.templates.index', compact('templates', 'kategoris', 'fiturs'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function show($id)
|
||||||
|
{
|
||||||
|
return Template::with('kategori')->findOrFail($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$data = $request->validate([
|
$data = $request->validate([
|
||||||
|
|||||||
206
proyek-frontend/app/components/forms/UlangTahunForm.vue
Normal file
206
proyek-frontend/app/components/forms/UlangTahunForm.vue
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
<template>
|
||||||
|
<div class="max-w-5xl mx-auto p-8 bg-gradient-to-b from-pink-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-purple-700 drop-shadow-sm">
|
||||||
|
🎂 Form Pemesanan Undangan Ulang Tahun 🎉
|
||||||
|
</h1>
|
||||||
|
<p class="text-gray-600 mt-2">
|
||||||
|
Isi data berikut dengan lengkap untuk melakukan pemesanan undangan ulang tahun.
|
||||||
|
</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 -->
|
||||||
|
<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="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<input v-model="form.nama_pemesan" type="text" placeholder="Nama Pemesan" class="input" required />
|
||||||
|
<input v-model="form.no_hp" type="text" placeholder="No. WhatsApp" class="input" required />
|
||||||
|
<input v-model="form.email" type="email" placeholder="Email" class="input md:col-span-2" required />
|
||||||
|
</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 flex items-center gap-2">
|
||||||
|
<span class="w-1.5 h-6 bg-blue-600 rounded-full"></span> Data Anak
|
||||||
|
</h2>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<input v-model="form.nama_lengkap_anak" type="text" placeholder="Nama Lengkap Anak" class="input" required />
|
||||||
|
<input v-model="form.nama_panggilan_anak" type="text" placeholder="Nama Panggilan Anak" class="input" required />
|
||||||
|
<input v-model="form.bapak_anak" type="text" placeholder="Nama Bapak" class="input" required />
|
||||||
|
<input v-model="form.ibu_anak" type="text" placeholder="Nama Ibu" class="input" required />
|
||||||
|
<input v-model="form.umur_dirayakan" type="text" placeholder="Ulang Tahun ke-" class="input" required />
|
||||||
|
<input v-model="form.anak_ke" type="text" placeholder="Anak ke-" class="input" required />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Jadwal 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 flex items-center gap-2">
|
||||||
|
<span class="w-1.5 h-6 bg-blue-600 rounded-full"></span> Jadwal Acara
|
||||||
|
</h2>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<input v-model="form.hari_tanggal_acara" type="date" class="input" required />
|
||||||
|
<input v-model="form.waktu_acara" type="text" placeholder="Waktu Acara (Contoh: 15:00 - Selesai)" class="input" required />
|
||||||
|
<textarea v-model="form.alamat_acara" rows="3" placeholder="Alamat Lengkap Acara" class="input md:col-span-2" required></textarea>
|
||||||
|
<input v-model="form.maps_acara" type="text" placeholder="Link Google Maps (Opsional)" class="input md:col-span-2" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Informasi Tambahan -->
|
||||||
|
<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> Informasi Tambahan
|
||||||
|
</h2>
|
||||||
|
<input v-model="form.link_musik" type="text" placeholder="Link Musik Latar (Opsional)" class="input" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Galeri -->
|
||||||
|
<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> Galeri (Max 5 Foto)
|
||||||
|
</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">
|
||||||
|
<img :src="img" alt="Preview" class="object-cover w-full h-full" />
|
||||||
|
</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>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- 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!</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();
|
||||||
|
|
||||||
|
// 1. STRUKTUR DATA DISESUAIKAN DENGAN BACKEND
|
||||||
|
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_anak: "",
|
||||||
|
nama_panggilan_anak: "",
|
||||||
|
bapak_anak: "",
|
||||||
|
ibu_anak: "",
|
||||||
|
umur_dirayakan: "", // <-- Field baru ditambahkan
|
||||||
|
anak_ke: "", // <-- Field baru ditambahkan
|
||||||
|
|
||||||
|
hari_tanggal_acara: "",
|
||||||
|
waktu_acara: "",
|
||||||
|
alamat_acara: "",
|
||||||
|
maps_acara: "",
|
||||||
|
|
||||||
|
link_musik: "",
|
||||||
|
galeri: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal ambil template", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleFileUpload = (event) => {
|
||||||
|
const files = event.target.files;
|
||||||
|
form.value.galeri = [];
|
||||||
|
previewImages.value = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < files.length && i < 5; i++) {
|
||||||
|
const file = files[i];
|
||||||
|
form.value.galeri.push(file);
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => previewImages.value.push(e.target.result);
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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((file) => body.append("galeri[]", file));
|
||||||
|
} else if (form.value[key] !== null && form.value[key] !== undefined) {
|
||||||
|
body.append(key, form.value[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. ENDPOINT API DIPERBAIKI
|
||||||
|
await $fetch("http://localhost:8000/api/form/ulang-tahun", {
|
||||||
|
method: "POST",
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
success.value = true;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
error.value = true;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@ -52,7 +52,7 @@
|
|||||||
|
|
||||||
<!-- Tombol Order langsung ke form Khitan -->
|
<!-- Tombol Order langsung ke form Khitan -->
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
to="/khitan"
|
:to="`/form/${tpl.kategori.nama.toLowerCase().replace(/ /g, '-')}` + `?template_id=${tpl.id}`"
|
||||||
class="w-full bg-blue-600 text-white font-semibold py-2 px-4 rounded-lg hover:bg-blue-700 transition-colors text-center"
|
class="w-full bg-blue-600 text-white font-semibold py-2 px-4 rounded-lg hover:bg-blue-700 transition-colors text-center"
|
||||||
>
|
>
|
||||||
Order
|
Order
|
||||||
|
|||||||
9
proyek-frontend/app/pages/form/ulang-tahun.vue
Normal file
9
proyek-frontend/app/pages/form/ulang-tahun.vue
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<FormsUlangTahunForm />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
</script>
|
||||||
Loading…
Reference in New Issue
Block a user