diff --git a/backend-baru/app/Http/Controllers/Api/PelangganApiController.php b/backend-baru/app/Http/Controllers/Api/PelangganApiController.php index 4534f13..537f1cf 100644 --- a/backend-baru/app/Http/Controllers/Api/PelangganApiController.php +++ b/backend-baru/app/Http/Controllers/Api/PelangganApiController.php @@ -82,12 +82,13 @@ class PelangganApiController extends Controller { $pelanggan = Pelanggan::with('template') ->where('invitation_code', $code) + ->where('status', 'diterima') ->first(); if (!$pelanggan) { return response()->json([ 'success' => false, - 'message' => 'Data pelanggan dengan kode undangan tidak ditemukan.', + 'message' => 'Data undangan tidak ditemukan.', ], 404); } diff --git a/backend-baru/database/factories/PelangganFactory.php b/backend-baru/database/factories/PelangganFactory.php new file mode 100644 index 0000000..f1d8341 --- /dev/null +++ b/backend-baru/database/factories/PelangganFactory.php @@ -0,0 +1,116 @@ + + */ +class PelangganFactory extends Factory +{ + protected $model = Pelanggan::class; + + public function definition(): array + { + // Get a random template + $template = Template::inRandomOrder()->first() ?? Template::factory()->create(); + + // Generate form data based on template's form fields + $formData = $this->generateFormData($template->form['fields'] ?? []); + + // Generate unique invitation code + $invitationCode = 'INV-' . strtoupper(Str::random(6)); + while (Pelanggan::where('invitation_code', $invitationCode)->exists()) { + $invitationCode = 'INV-' . strtoupper(Str::random(6)); + } + + return [ + 'nama_pemesan' => $this->faker->name(), + 'email' => $this->faker->unique()->safeEmail(), + 'no_tlpn' => $this->faker->phoneNumber(), + 'template_id' => $template->id, + 'form' => $formData, + 'harga' => $template->harga, + 'status' => $this->faker->randomElement(['menunggu', 'diterima', 'ditolak']), + 'invitation_code' => $invitationCode, + ]; + } + + /** + * Generate form data based on template fields + * + * @param array $fields + * @return array + */ + private function generateFormData(array $fields): array + { + $formData = []; + + foreach ($fields as $field) { + $name = $field['name']; + $type = $field['type'] ?? 'text'; + + switch ($type) { + case 'text': + $formData[$name] = $this->generateTextField($name); + break; + case 'email': + $formData[$name] = $this->faker->safeEmail(); + break; + case 'date': + $formData[$name] = $this->faker->date('Y-m-d', 'now +1 month'); + break; + case 'number': + $formData[$name] = $this->faker->numberBetween(1, 100); + break; + case 'textarea': + $formData[$name] = $this->faker->paragraph(); + break; + case 'file': + $formData[$name] = 'files/' . $this->faker->uuid() . '.jpg'; + break; + default: + $formData[$name] = $this->faker->word(); + } + } + + return $formData; + } + + /** + * Generate text field data based on field name + * + * @param string $name + * @return string + */ + private function generateTextField(string $name): string + { + if (str_contains($name, 'nama_')) { + return $this->faker->name(); + } + if (str_contains($name, 'alamat')) { + return $this->faker->address(); + } + if (str_contains($name, 'link_gmaps')) { + return 'https://maps.google.com/?q=' . $this->faker->latitude() . ',' . $this->faker->longitude(); + } + if (str_contains($name, 'instagram') || str_contains($name, 'facebook') || str_contains($name, 'twitter')) { + return 'https://' . str_replace('_', '.', $name) . '/' . $this->faker->userName(); + } + if (str_contains($name, 'waktu')) { + return $this->faker->time('H:i'); + } + if (str_contains($name, 'rekening')) { + return $this->faker->bankAccountNumber(); + } + if (str_contains($name, 'link_music')) { + return 'https://music.example.com/' . $this->faker->uuid(); + } + + return $this->faker->word(); + } +} \ No newline at end of file diff --git a/backend-baru/database/seeders/PelangganSeeder.php b/backend-baru/database/seeders/PelangganSeeder.php index 5d0cf07..0a64516 100644 --- a/backend-baru/database/seeders/PelangganSeeder.php +++ b/backend-baru/database/seeders/PelangganSeeder.php @@ -10,52 +10,52 @@ class PelangganSeeder extends Seeder { public function run(): void { - // contoh beberapa pelanggan - $pelanggans = [ - [ - 'nama_pemesan' => 'Arief Dwi Wicaksono', - 'email' => 'arief@example.com', - 'no_tlpn' => '081234567890', - 'template_id' => 1, // pastikan ada template_id valid - 'form' => json_encode([ - 'nama_pria' => 'Arief', - 'nama_wanita' => 'Nisa', - 'alamat' => 'Malang', - ]), - 'harga' => 150000, - 'status' => 'menunggu', - ], - [ - 'nama_pemesan' => 'Rizky Ramadhan', - 'email' => 'rizky@example.com', - 'no_tlpn' => '081298765432', - 'template_id' => 2, - 'form' => json_encode([ - 'nama_pria' => 'Rizky', - 'nama_wanita' => 'Dinda', - 'alamat' => 'Surabaya', - ]), - 'harga' => 250000, - 'status' => 'diterima', - ], - [ - 'nama_pemesan' => 'Siti Rahmawati', - 'email' => 'siti@example.com', - 'no_tlpn' => '081212341234', - 'template_id' => 3, - 'form' => json_encode([ - 'nama_pria' => 'Andi', - 'nama_wanita' => 'Siti', - 'alamat' => 'Jakarta', - ]), - 'harga' => 300000, - 'status' => 'menunggu', - ], - ]; + Pelanggan::factory()->count(100)->create(); + // $pelanggans = [ + // [ + // 'nama_pemesan' => 'Arief Dwi Wicaksono', + // 'email' => 'arief@example.com', + // 'no_tlpn' => '081234567890', + // 'template_id' => 1, // pastikan ada template_id valid + // 'form' => json_encode([ + // 'nama_pria' => 'Arief', + // 'nama_wanita' => 'Nisa', + // 'alamat' => 'Malang', + // ]), + // 'harga' => 150000, + // 'status' => 'menunggu', + // ], + // [ + // 'nama_pemesan' => 'Rizky Ramadhan', + // 'email' => 'rizky@example.com', + // 'no_tlpn' => '081298765432', + // 'template_id' => 2, + // 'form' => json_encode([ + // 'nama_pria' => 'Rizky', + // 'nama_wanita' => 'Dinda', + // 'alamat' => 'Surabaya', + // ]), + // 'harga' => 250000, + // 'status' => 'diterima', + // ], + // [ + // 'nama_pemesan' => 'Siti Rahmawati', + // 'email' => 'siti@example.com', + // 'no_tlpn' => '081212341234', + // 'template_id' => 3, + // 'form' => json_encode([ + // 'nama_pria' => 'Andi', + // 'nama_wanita' => 'Siti', + // 'alamat' => 'Jakarta', + // ]), + // 'harga' => 300000, + // 'status' => 'menunggu', + // ], + // ]; - foreach ($pelanggans as $data) { - $data['invitation_code'] = 'INV-' . strtoupper(Str::random(6)); // 🟢 generate code unik - Pelanggan::create($data); - } + // foreach ($pelanggans as $data) { + // $data['invitation_code'] = 'INV-' . strtoupper(Str::random(6)); // 🟢 generate code unik + // Pelanggan::create($data); + // } } } diff --git a/proyek-frontend/app/components/landing-page/featuredtemplates.vue b/proyek-frontend/app/components/landing-page/featuredtemplates.vue index f409c09..0a5300a 100644 --- a/proyek-frontend/app/components/landing-page/featuredtemplates.vue +++ b/proyek-frontend/app/components/landing-page/featuredtemplates.vue @@ -10,10 +10,10 @@ const toggleDropdown = (templateId) => { openDropdownId.value = openDropdownId.value === templateId ? null : templateId } -// Paket & fitur hardcode const paketData = [ + // Paket Starter (Undangan Minimalis / Pernikahan Starter) { - paket: 'Starter', + paket: 'starter', fiturs: [ '1x Acara', 'Masa Aktif 3 Bulan', @@ -22,21 +22,10 @@ const paketData = [ 'Request Musik' ] }, + + // Paket Premium Pernikahan { - paket: 'Basic', - fiturs: [ - '1x Acara', - '6 Galeri Foto', - 'Hitung Mundur Waktu Acara', - 'Buku Tamu + Data Kehadiran', - 'Masa Aktif 6 Bulan', - 'Nama Tamu Personal', - 'Maks. 200 Tamu', - 'Request Musik' - ] - }, - { - paket: 'Premium', + paket: 'premium', fiturs: [ 'Maksimal 3x Acara (Akad, Resepsi, Syukuran)', 'Unlimited Galeri Foto', @@ -51,6 +40,40 @@ const paketData = [ 'Nama Tamu Personal Unlimited Tamu', 'Request Musik' ] + }, + + // Paket Premium Ulang Tahun + { + paket: 'premium', + fiturs: [ + '1x Acara', + 'Unlimited Galeri Foto', + 'Timeline Story', + 'Google Maps', + 'Reminder Google Calendar', + 'Amplop Digital', + 'Placement Video Cinematic', + 'Masa Aktif 12 Bulan', + 'Nama Tamu Personal Unlimited Tamu', + 'Request Musik' + ] + }, + + // Paket Premium Khitan + { + paket: 'premium', + fiturs: [ + '1x Acara', + 'Unlimited Galeri Foto', + 'Timeline Story', + 'Google Maps', + 'Reminder Google Calendar', + 'Amplop Digital', + 'Placement Video Cinematic', + 'Masa Aktif 12 Bulan', + 'Nama Tamu Personal Unlimited Tamu', + 'Request Musik' + ] } ] @@ -67,17 +90,27 @@ const formMapping = { const { data: templatesData, error } = await useFetch('http://localhost:8000/api/templates') // Mapping template: gabungkan backend + paket & fitur hardcode +const paketMapping = { + 'Undangan Minimalis': 'starter', + 'Undangan Pernikahan Premium': 'premium', + 'Undangan Ulang Tahun Premium': 'premium', + 'Undangan Khitan Premium': 'premium' +} + const templates = computed(() => (templatesData.value || []) .filter(t => selectedIds.includes(t.id)) - .map((t, index) => { + .map((t) => { + const paketKey = paketMapping[t.nama_template] || 'starter'; + const paketInfo = paketData.find(p => p.paket.toLowerCase() === paketKey.toLowerCase()) || paketData[0]; + return { id: t.id, nama_template: t.nama_template, harga: t.harga, foto: t.foto || '/default.jpg', - paket: paketData[index % paketData.length].paket, - fiturs: paketData[index % paketData.length].fiturs.map((f, i) => ({ id: i + 1, deskripsi: f })), + paket: paketInfo.paket, + fiturs: paketInfo.fiturs.map((f, i) => ({ id: i + 1, deskripsi: f })), kategori: t.kategori, formPath: t.slug } @@ -149,7 +182,7 @@ const templates = computed(() => Order - 1 + @@ -166,7 +199,7 @@ const templates = computed(() => diff --git a/proyek-frontend/app/components/undangan/undangan-minimalis.vue b/proyek-frontend/app/components/undangan/undangan-minimalis.vue new file mode 100644 index 0000000..8aba5e2 --- /dev/null +++ b/proyek-frontend/app/components/undangan/undangan-minimalis.vue @@ -0,0 +1,209 @@ + + + + + diff --git a/proyek-frontend/app/pages/form/undangan-pernikahan-premium.vue b/proyek-frontend/app/pages/form/undangan-pernikahan-premium.vue index 1a38d08..49dcacb 100644 --- a/proyek-frontend/app/pages/form/undangan-pernikahan-premium.vue +++ b/proyek-frontend/app/pages/form/undangan-pernikahan-premium.vue @@ -126,48 +126,147 @@ - - -
-

🖼️ Galeri Foto

-
+ +
+ +
-
+ Pilih Foto + + + + -
- + -
+ - - - - \ No newline at end of file +} + + +// Fungsi Batal +const batal = () => { + // Kembali ke landing page tanpa menyimpan + router.back()('/') +} + \ No newline at end of file diff --git a/proyek-frontend/app/pages/p/[code].vue b/proyek-frontend/app/pages/p/[code].vue new file mode 100644 index 0000000..2a93276 --- /dev/null +++ b/proyek-frontend/app/pages/p/[code].vue @@ -0,0 +1,125 @@ + + + + +