form pernikahan
This commit is contained in:
parent
a3782c5747
commit
df525ec20b
@ -10,12 +10,33 @@ class KategoriApiController extends Controller
|
||||
// Ambil semua kategori
|
||||
public function index()
|
||||
{
|
||||
return response()->json(Kategori::all());
|
||||
// 1. Ambil semua kategori dari database
|
||||
$kategoris = Kategori::all();
|
||||
|
||||
// 2. Ubah koleksi data untuk membuat URL foto yang benar
|
||||
$transformedKategoris = $kategoris->map(function($kategori) {
|
||||
return [
|
||||
'id' => $kategori->id,
|
||||
'nama' => $kategori->nama,
|
||||
'deskripsi' => $kategori->deskripsi,
|
||||
// Gunakan helper asset() untuk membuat URL lengkap
|
||||
'foto' => $kategori->foto ? asset('storage/' . $kategori->foto) : null,
|
||||
];
|
||||
});
|
||||
|
||||
// 3. Kirim data yang sudah diubah sebagai JSON
|
||||
return response()->json($transformedKategoris);
|
||||
}
|
||||
|
||||
// Ambil detail satu kategori
|
||||
public function show(Kategori $kategori)
|
||||
{
|
||||
return response()->json($kategori);
|
||||
// Sebaiknya detail juga diubah agar konsisten
|
||||
return response()->json([
|
||||
'id' => $kategori->id,
|
||||
'nama' => $kategori->nama,
|
||||
'deskripsi' => $kategori->deskripsi,
|
||||
'foto' => asset('storage/' . $kategori->foto),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ use App\Models\Pelanggan;
|
||||
use App\Models\PelangganDetail;
|
||||
use App\Models\Template; // ✅ tambahkan ini
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class PernikahanApiController extends Controller
|
||||
{
|
||||
@ -55,9 +56,23 @@ class PernikahanApiController extends Controller
|
||||
'no_rekening1' => 'nullable|string',
|
||||
'no_rekening2' => 'nullable|string',
|
||||
'link_musik' => 'nullable|string',
|
||||
'galeri' => 'nullable|string',
|
||||
'galeri' => 'nullable|array|max:10',
|
||||
'galeri.*' => 'image|mimes:jpeg,png,jpg,gif|max:2048',
|
||||
]);
|
||||
|
||||
// --- PROSES UPLOAD GAMBAR ---
|
||||
$galleryPaths = [];
|
||||
if ($request->hasFile('galeri')) {
|
||||
foreach ($request->file('galeri') as $file) {
|
||||
// Simpan file ke storage/app/public/gallery dan dapatkan path-nya
|
||||
$path = $file->store('gallery', 'public');
|
||||
$galleryPaths[] = $path;
|
||||
}
|
||||
}
|
||||
// Ganti 'galeri' di $data dengan array path yang sudah disimpan
|
||||
$data['galeri'] = $galleryPaths;
|
||||
|
||||
|
||||
// ✅ Ambil template berdasarkan template_id
|
||||
$template = Template::with('kategori')->findOrFail($data['template_id']);
|
||||
|
||||
|
||||
258
proyek-frontend/app/components/forms/PernikahanForm.vue
Normal file
258
proyek-frontend/app/components/forms/PernikahanForm.vue
Normal file
@ -0,0 +1,258 @@
|
||||
<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</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>
|
||||
</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! 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();
|
||||
|
||||
// 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_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: [],
|
||||
});
|
||||
|
||||
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 < 10; i++) { // Batas 10 gambar
|
||||
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 SUDAH BENAR
|
||||
await $fetch("http://localhost:8000/api/form/pernikahan", {
|
||||
method: "POST",
|
||||
body,
|
||||
});
|
||||
|
||||
success.value = true;
|
||||
// Optional: Reset form after success
|
||||
// Object.keys(form.value).forEach(key => form.value[key] = '');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
error.value = true;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
10
proyek-frontend/app/pages/form/pernikahan.vue
Normal file
10
proyek-frontend/app/pages/form/pernikahan.vue
Normal file
@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<FormsPernikahanForm />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// Dengan Nuxt 3, komponen akan di-import secara otomatis.
|
||||
// Anda tidak perlu menulis kode apa pun di sini.
|
||||
</script>
|
||||
Loading…
Reference in New Issue
Block a user