This commit is contained in:
Farhaan4 2025-10-07 10:43:04 +07:00
commit 1ef83feac9
7 changed files with 348 additions and 246 deletions

View File

@ -0,0 +1,54 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Review;
class ReviewApiController extends Controller
{
// Ambil semua ulasan (JSON)
public function index()
{
$reviews = Review::latest()->get();
return response()->json($reviews, 200);
}
// Simpan ulasan baru
public function store(Request $request)
{
$validated = $request->validate([
'rating' => 'required|integer|min:1|max:5',
'message' => 'required|string',
'name' => 'required|string|max:100',
'city' => 'required|string|max:100',
]);
$review = Review::create($validated);
return response()->json([
'message' => 'Ulasan berhasil disimpan',
'data' => $review,
], 201);
}
// Tampilkan ulasan tertentu
public function show($id)
{
$review = Review::findOrFail($id);
return response()->json($review, 200);
}
// Hapus ulasan
public function destroy($id)
{
$review = Review::findOrFail($id);
$review->delete();
return response()->json([
'message' => 'Ulasan berhasil dihapus',
], 200);
}
}

View File

@ -3,35 +3,58 @@
@section('title', 'Tambah Kategori') @section('title', 'Tambah Kategori')
@section('content') @section('content')
<div class="container mt-4"> <div class="container mx-auto py-8">
<h2 class="mb-3">Tambah Kategori</h2> <div class="max-w-2xl mx-auto bg-white shadow-md rounded-xl p-8 border border-blue-100">
<h2 class="text-2xl font-semibold text-black mb-6">Tambah Kategori</h2>
<form action="{{ route('admin.kategori.store') }}" method="POST" enctype="multipart/form-data"> <form action="{{ route('admin.kategori.store') }}" method="POST" enctype="multipart/form-data" class="space-y-5">
@csrf @csrf
<div class="mb-3"> <!-- Nama Kategori -->
<label for="nama" class="form-label">Nama Kategori</label> <div>
<input type="text" class="form-control @error('nama') is-invalid @enderror" <label for="nama" class="block text-sm font-medium text-gray-700 mb-2">Nama Kategori</label>
id="nama" name="nama" value="{{ old('nama') }}" required> <input type="text" id="nama" name="nama"
@error('nama') <div class="invalid-feedback">{{ $message }}</div> @enderror value="{{ old('nama') }}"
</div> class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-400 focus:outline-none @error('nama') border-red-500 @enderror"
placeholder="Masukkan nama kategori" required>
@error('nama')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
<div class="mb-3"> <!-- Deskripsi -->
<label for="deskripsi" class="form-label">Deskripsi</label> <div>
<textarea class="form-control @error('deskripsi') is-invalid @enderror" <label for="deskripsi" class="block text-sm font-medium text-gray-700 mb-2">Deskripsi</label>
id="deskripsi" name="deskripsi">{{ old('deskripsi') }}</textarea> <textarea id="deskripsi" name="deskripsi" rows="4"
@error('deskripsi') <div class="invalid-feedback">{{ $message }}</div> @enderror class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-400 focus:outline-none @error('deskripsi') border-red-500 @enderror"
</div> placeholder="Tuliskan deskripsi kategori">{{ old('deskripsi') }}</textarea>
@error('deskripsi')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
<div class="mb-3"> <!-- Foto -->
<label for="foto" class="form-label">Foto</label> <div>
<input type="file" class="form-control @error('foto') is-invalid @enderror" <label for="foto" class="block text-sm font-medium text-gray-700 mb-2">Foto</label>
id="foto" name="foto"> <input type="file" id="foto" name="foto"
@error('foto') <div class="invalid-feedback">{{ $message }}</div> @enderror class="w-full border border-gray-300 rounded-lg px-4 py-2 bg-gray-50 cursor-pointer focus:ring-2 focus:ring-blue-400 @error('foto') border-red-500 @enderror">
</div> @error('foto')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
<button type="submit" class="btn btn-success">Simpan</button> <!-- Tombol -->
<a href="{{ route('admin.kategori.index') }}" class="btn btn-secondary">Kembali</a> <div class="flex justify-end gap-3 pt-4">
</form> <a href="{{ route('admin.kategori.index') }}"
class="bg-gray-100 hover:bg-gray-200 text-gray-700 font-medium px-5 py-2 rounded-lg transition duration-200">
Kembali
</a>
<button type="submit"
class="bg-blue-600 hover:bg-blue-700 text-white font-medium px-6 py-2 rounded-lg shadow transition duration-200">
Simpan
</button>
</div>
</form>
</div>
</div> </div>
@endsection @endsection

View File

@ -3,41 +3,64 @@
@section('title', 'Edit Kategori') @section('title', 'Edit Kategori')
@section('content') @section('content')
<div class="container mt-4"> <div class="container mx-auto py-8">
<h2 class="mb-3">Edit Kategori</h2> <div class="max-w-2xl mx-auto bg-white shadow-md rounded-xl p-8 border border-blue-100">
<h2 class="text-2xl font-semibold text-black mb-6">Edit Kategori</h2>
<form action="{{ route('admin.kategori.update', $kategori->id) }}" method="POST" enctype="multipart/form-data"> <form action="{{ route('admin.kategori.update', $kategori->id) }}" method="POST" enctype="multipart/form-data" class="space-y-5">
@csrf @csrf
@method('PUT') @method('PUT')
<div class="mb-3"> <!-- Nama Kategori -->
<label for="nama" class="form-label">Nama Kategori</label> <div>
<input type="text" class="form-control @error('nama') is-invalid @enderror" <label for="nama" class="block text-sm font-medium text-gray-700 mb-2">Nama Kategori</label>
id="nama" name="nama" value="{{ old('nama', $kategori->nama) }}" required> <input type="text" id="nama" name="nama"
@error('nama') <div class="invalid-feedback">{{ $message }}</div> @enderror value="{{ old('nama', $kategori->nama) }}"
</div> class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-400 focus:outline-none @error('nama') border-red-500 @enderror"
placeholder="Masukkan nama kategori" required>
@error('nama')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
<div class="mb-3"> <!-- Deskripsi -->
<label for="deskripsi" class="form-label">Deskripsi</label> <div>
<textarea class="form-control @error('deskripsi') is-invalid @enderror" <label for="deskripsi" class="block text-sm font-medium text-gray-700 mb-2">Deskripsi</label>
id="deskripsi" name="deskripsi">{{ old('deskripsi', $kategori->deskripsi) }}</textarea> <textarea id="deskripsi" name="deskripsi" rows="4"
@error('deskripsi') <div class="invalid-feedback">{{ $message }}</div> @enderror class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-400 focus:outline-none @error('deskripsi') border-red-500 @enderror"
</div> placeholder="Tuliskan deskripsi kategori">{{ old('deskripsi', $kategori->deskripsi) }}</textarea>
@error('deskripsi')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
<div class="mb-3"> <!-- Foto -->
<label for="foto" class="form-label">Foto</label> <div>
@if($kategori->foto) <label for="foto" class="block text-sm font-medium text-gray-700 mb-2">Foto</label>
<div class="mb-2"> @if($kategori->foto)
<img src="{{ asset('storage/'.$kategori->foto) }}" width="100" class="img-thumbnail"> <div class="mb-3">
</div> <img src="{{ asset('storage/'.$kategori->foto) }}" alt="Foto Kategori" class="w-32 h-32 object-cover rounded-lg border border-gray-200">
@endif </div>
<input type="file" class="form-control @error('foto') is-invalid @enderror" @endif
id="foto" name="foto"> <input type="file" id="foto" name="foto"
@error('foto') <div class="invalid-feedback">{{ $message }}</div> @enderror class="w-full border border-gray-300 rounded-lg px-4 py-2 bg-gray-50 cursor-pointer focus:ring-2 focus:ring-blue-400 @error('foto') border-red-500 @enderror">
</div> @error('foto')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
<button type="submit" class="btn btn-primary">Update</button> <!-- Tombol -->
<a href="{{ route('admin.kategori.index') }}" class="btn btn-secondary">Kembali</a> <div class="flex justify-end gap-3 pt-4">
</form> <a href="{{ route('admin.kategori.index') }}"
class="bg-gray-100 hover:bg-gray-200 text-gray-700 font-medium px-5 py-2 rounded-lg transition duration-200">
Kembali
</a>
<button type="submit"
class="bg-blue-600 hover:bg-blue-700 text-white font-medium px-6 py-2 rounded-lg shadow transition duration-200">
Update
</button>
</div>
</form>
</div>
</div> </div>
@endsection @endsection

View File

@ -3,44 +3,67 @@
@section('title', 'Buat Pesanan') @section('title', 'Buat Pesanan')
@section('content') @section('content')
<div class="container mt-4"> <div class="container mx-auto py-8">
<h2 class="mb-3">Buat Pesanan</h2> <div class="max-w-2xl mx-auto bg-white shadow-md rounded-xl p-8 border border-blue-100">
<h2 class="text-2xl font-semibold text-black mb-6">Buat Pesanan</h2>
<form action="{{ route('pelanggans.store') }}" method="POST"> <form action="{{ route('pelanggans.store') }}" method="POST" class="space-y-5">
@csrf @csrf
<div class="mb-3"> <!-- Nama Pemesan -->
<label>Nama Pemesan</label> <div>
<input type="text" name="nama_pemesan" class="form-control" value="{{ old('nama_pemesan') }}" required> <label class="block text-sm font-medium text-gray-700 mb-2">Nama Pemesan</label>
</div> <input type="text" name="nama_pemesan"
value="{{ old('nama_pemesan') }}"
class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-400 focus:outline-none"
required>
</div>
<div class="mb-3"> <!-- Email -->
<label>Email</label> <div>
<input type="email" name="email" class="form-control" value="{{ old('email') }}" required> <label class="block text-sm font-medium text-gray-700 mb-2">Email</label>
</div> <input type="email" name="email"
value="{{ old('email') }}"
class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-400 focus:outline-none"
required>
</div>
<div class="mb-3"> <!-- No. Telepon -->
<label>No. Telepon</label> <div>
<input type="text" name="no_tlpn" class="form-control" value="{{ old('no_tlpn') }}" required> <label class="block text-sm font-medium text-gray-700 mb-2">No. Telepon</label>
</div> <input type="text" name="no_tlpn"
value="{{ old('no_tlpn') }}"
class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-400 focus:outline-none"
required>
</div>
<div class="mb-3"> <!-- Template -->
<label>Template</label> <div>
<select name="template_id" id="template_id" class="form-select" required> <label class="block text-sm font-medium text-gray-700 mb-2">Template</label>
<option value="">-- Pilih Template --</option> <select name="template_id" id="template_id"
@foreach($templates as $template) class="w-full border border-gray-300 rounded-lg px-4 py-2 bg-gray-50 focus:ring-2 focus:ring-blue-400 focus:outline-none"
<option value="{{ $template->id }}" data-form='@json($template->form)'> required>
{{ $template->nama_template }} (Rp {{ number_format($template->harga,0,',','.') }}) <option value="">-- Pilih Template --</option>
</option> @foreach($templates as $template)
@endforeach <option value="{{ $template->id }}" data-form='@json($template->form)'>
</select> {{ $template->nama_template }} (Rp {{ number_format($template->harga,0,',','.') }})
</div> </option>
@endforeach
</select>
</div>
<!-- tempat field dinamis --> <!-- Tempat field dinamis -->
<div id="dynamic-form"></div> <div id="dynamic-form"></div>
<button type="submit" class="btn btn-success">Kirim Pesanan</button> <!-- Tombol -->
</form> <div class="flex justify-end pt-4">
<button type="submit"
class="bg-blue-600 hover:bg-blue-700 text-white font-medium px-6 py-2 rounded-lg shadow transition duration-200">
Kirim Pesanan
</button>
</div>
</form>
</div>
</div> </div>
<script> <script>
@ -58,11 +81,11 @@ document.getElementById('template_id').addEventListener('change', function () {
let input = ''; let input = '';
if (field.type === 'text') { if (field.type === 'text') {
input = `<input type="text" name="form[${key}]" class="form-control" ${field.required ? 'required' : ''}>`; input = `<input type="text" name="form[${key}]" class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-400 focus:outline-none" ${field.required ? 'required' : ''}>`;
} else if (field.type === 'textarea') { } else if (field.type === 'textarea') {
input = `<textarea name="form[${key}]" class="form-control" ${field.required ? 'required' : ''}></textarea>`; input = `<textarea name="form[${key}]" class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-400 focus:outline-none" ${field.required ? 'required' : ''}></textarea>`;
} else if (field.type === 'select') { } else if (field.type === 'select') {
input = `<select name="form[${key}]" class="form-select">`; input = `<select name="form[${key}]" class="w-full border border-gray-300 rounded-lg px-4 py-2 bg-gray-50 focus:ring-2 focus:ring-blue-400 focus:outline-none">`;
field.options.forEach(opt => { field.options.forEach(opt => {
input += `<option value="${opt}">${opt}</option>`; input += `<option value="${opt}">${opt}</option>`;
}); });
@ -70,7 +93,7 @@ document.getElementById('template_id').addEventListener('change', function () {
} }
container.innerHTML += ` container.innerHTML += `
<div class="mb-3"> <div class="mb-3">
<label>${label}</label> <label class="block text-sm font-medium text-gray-700 mb-2">${label}</label>
${input} ${input}
</div> </div>
`; `;

View File

@ -3,86 +3,101 @@
@section('title', 'Edit Template') @section('title', 'Edit Template')
@section('content') @section('content')
<div class="container mt-4"> <div class="container mx-auto py-8">
<h2>Edit Template</h2> <div class="max-w-2xl mx-auto bg-white shadow-md rounded-xl p-8 border border-blue-100">
<h2 class="text-2xl font-semibold text-black mb-6">Edit Template</h2>
{{-- Alert sukses / error --}} {{-- Alert sukses / error --}}
@if(session('success')) @if(session('success'))
<div class="alert alert-success">{{ session('success') }}</div> <div class="bg-green-100 text-green-700 px-4 py-2 rounded-lg mb-4 border border-green-200">
@endif {{ session('success') }}
@if($errors->any())
<div class="alert alert-danger">
<ul class="mb-0">
@foreach($errors->all() as $err)
<li>{{ $err }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('admin.template.update', $template->id) }}"
method="POST" enctype="multipart/form-data">
@csrf
@method('PUT')
{{-- Nama Template --}}
<div class="mb-3">
<label for="nama_template" class="form-label">Nama Template</label>
<input type="text" name="nama_template" id="nama_template"
class="form-control @error('nama_template') is-invalid @enderror"
value="{{ old('nama_template', $template->nama_template) }}" required>
@error('nama_template')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
{{-- Harga --}}
<div class="mb-3">
<label for="harga" class="form-label">Harga</label>
<input type="number" name="harga" id="harga"
class="form-control @error('harga') is-invalid @enderror"
value="{{ old('harga', $template->harga) }}" required>
@error('harga')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
{{-- Paket --}}
<div class="mb-3">
<label for="paket" class="form-label">Paket</label>
<select name="paket" id="paket" class="form-select @error('paket') is-invalid @enderror" required>
<option value="starter" {{ old('paket', $template->paket) == 'starter' ? 'selected' : '' }}>Starter</option>
<option value="basic" {{ old('paket', $template->paket) == 'basic' ? 'selected' : '' }}>Basic</option>
<option value="premium" {{ old('paket', $template->paket) == 'premium' ? 'selected' : '' }}>Premium</option>
</select>
@error('paket')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
{{-- Foto / Gambar --}}
<div class="mb-3">
<label for="foto" class="form-label">Gambar Template</label>
<div class="mb-2">
@if($template->foto)
<img src="{{ asset('storage/' . $template->foto) }}"
alt="{{ $template->nama_template }}"
class="rounded border" style="max-height: 120px">
@else
<p class="text-muted">Belum ada gambar</p>
@endif
</div> </div>
<input type="file" name="foto" id="foto" @endif
class="form-control @error('foto') is-invalid @enderror"> @if($errors->any())
<small class="text-muted">Kosongkan jika tidak ingin mengganti gambar.</small> <div class="bg-red-100 text-red-700 px-4 py-2 rounded-lg mb-4 border border-red-200">
@error('foto') <ul class="list-disc pl-5 mb-0">
<div class="invalid-feedback">{{ $message }}</div> @foreach($errors->all() as $err)
@enderror <li>{{ $err }}</li>
</div> @endforeach
</ul>
</div>
@endif
{{-- Tombol --}} <form action="{{ route('admin.template.update', $template->id) }}" method="POST" enctype="multipart/form-data" class="space-y-5">
<button type="submit" class="btn btn-primary">Simpan Perubahan</button> @csrf
<a href="{{ route('admin.template.index') }}" class="btn btn-secondary">Batal</a> @method('PUT')
</form>
{{-- Nama Template --}}
<div>
<label for="nama_template" class="block text-sm font-medium text-gray-700 mb-2">Nama Template</label>
<input type="text" id="nama_template" name="nama_template"
value="{{ old('nama_template', $template->nama_template) }}"
class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-400 focus:outline-none @error('nama_template') border-red-500 @enderror"
required>
@error('nama_template')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
{{-- Harga --}}
<div>
<label for="harga" class="block text-sm font-medium text-gray-700 mb-2">Harga</label>
<input type="number" id="harga" name="harga"
value="{{ old('harga', $template->harga) }}"
class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-400 focus:outline-none @error('harga') border-red-500 @enderror"
required>
@error('harga')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
{{-- Paket --}}
<div>
<label for="paket" class="block text-sm font-medium text-gray-700 mb-2">Paket</label>
<select id="paket" name="paket"
class="w-full border border-gray-300 rounded-lg px-4 py-2 bg-gray-50 focus:ring-2 focus:ring-blue-400 focus:outline-none @error('paket') border-red-500 @enderror"
required>
<option value="starter" {{ old('paket', $template->paket) == 'starter' ? 'selected' : '' }}>Starter</option>
<option value="basic" {{ old('paket', $template->paket) == 'basic' ? 'selected' : '' }}>Basic</option>
<option value="premium" {{ old('paket', $template->paket) == 'premium' ? 'selected' : '' }}>Premium</option>
</select>
@error('paket')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
{{-- Foto / Gambar --}}
<div>
<label for="foto" class="block text-sm font-medium text-gray-700 mb-2">Gambar Template</label>
<div class="mb-3">
@if($template->foto)
<img src="{{ asset('storage/' . $template->foto) }}"
alt="{{ $template->nama_template }}"
class="w-32 h-32 object-cover rounded-lg border border-gray-200">
@else
<p class="text-gray-500 text-sm">Belum ada gambar</p>
@endif
</div>
<input type="file" id="foto" name="foto"
class="w-full border border-gray-300 rounded-lg px-4 py-2 bg-gray-50 cursor-pointer focus:ring-2 focus:ring-blue-400 @error('foto') border-red-500 @enderror">
<p class="text-gray-500 text-sm mt-1">Kosongkan jika tidak ingin mengganti gambar.</p>
@error('foto')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
{{-- Tombol --}}
<div class="flex justify-end gap-3 pt-4">
<a href="{{ route('admin.template.index') }}"
class="bg-gray-100 hover:bg-gray-200 text-gray-700 font-medium px-5 py-2 rounded-lg transition duration-200">
Batal
</a>
<button type="submit"
class="bg-blue-600 hover:bg-blue-700 text-white font-medium px-6 py-2 rounded-lg shadow transition duration-200">
Simpan Perubahan
</button>
</div>
</form>
</div>
</div> </div>
@endsection @endsection

View File

@ -5,6 +5,7 @@ use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\TemplateApiController; use App\Http\Controllers\Api\TemplateApiController;
use App\Http\Controllers\Api\KategoriApiController; use App\Http\Controllers\Api\KategoriApiController;
use App\Http\Controllers\Api\PelangganApiController; use App\Http\Controllers\Api\PelangganApiController;
use App\Http\Controllers\Api\ReviewApiController;
Route::get('kategoris', [KategoriApiController::class, 'index']); Route::get('kategoris', [KategoriApiController::class, 'index']);
Route::get('kategoris/{kategori}', [KategoriApiController::class, 'show']); Route::get('kategoris/{kategori}', [KategoriApiController::class, 'show']);
@ -12,7 +13,11 @@ Route::get('kategoris/{kategori}', [KategoriApiController::class, 'show']);
Route::get('/templates', [TemplateApiController::class, 'index']); Route::get('/templates', [TemplateApiController::class, 'index']);
Route::get('/templates/{template}', [TemplateApiController::class, 'show']); Route::get('/templates/{template}', [TemplateApiController::class, 'show']);
Route::get('/templates/category/{id}', [TemplateApiController::class, 'getByCategory']); Route::get('/templates/category/{id}', [TemplateApiController::class, 'getByCategory']);
Route::post('/pelanggans', [PelangganApiController::class, 'store']); Route::post('/pelanggans', [PelangganApiController::class, 'store']);
Route::get('/reviews', [ReviewApiController::class, 'index']);
Route::post('/reviews', [ReviewApiController::class, 'store']);
Route::get('/reviews/{id}', [ReviewApiController::class, 'show']);
Route::delete('/reviews/{id}', [ReviewApiController::class, 'destroy']);

View File

@ -2,60 +2,16 @@
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
// id template yang mau ditampilkan // id template yang mau ditampilkan
const selectedIds = [1, 2, 3, 4, 5, 6] const selectedIds = [1, 2, 3, 5, 6]
// state dropdown // state dropdown (buat fitur paket aja sekarang)
const openDropdownId = ref(null) const openDropdownId = ref(null)
const toggleDropdown = (templateId) => { const toggleDropdown = (templateId) => {
openDropdownId.value = openDropdownId.value === templateId ? null : templateId openDropdownId.value = openDropdownId.value === templateId ? null : templateId
} }
// Paket & fitur hardcode (tidak tergantung kategori backend) // fetch API dari Laravel
const paketData = [ const { data: templatesData, error } = await useFetch('http://localhost:8000/api/templates')
{
paket: 'Starter',
fiturs: [
'1x Acara',
'Masa Aktif 3 Bulan',
'Nama Tamu Personal',
'Maks. 100 Tamu',
'Request Musik'
]
},
{
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',
fiturs: [
'Maksimal 3x Acara (Akad, Resepsi, Syukuran)',
'Unlimited Galeri Foto',
'Timeline Story',
'Google Maps',
'Reminder Google Calendar',
'Link Instagram Live Streaming',
'Amplop Digital',
'Placement Video Cinematic',
'Bonus Undangan Image Post Story',
'Masa Aktif 12 Bulan',
'Nama Tamu Personal Unlimited Tamu',
'Request Musik'
]
}
]
// fetch API dari backend (hanya untuk nama template & harga)
const { data: templatesData } = await useFetch('http://localhost:8000/api/templates')
// mapping template: nama_template & harga dari backend, paket & fiturs hardcode // mapping template: nama_template & harga dari backend, paket & fiturs hardcode
const templates = computed(() => const templates = computed(() =>
@ -84,10 +40,17 @@ const templates = computed(() =>
<!-- Grid Template --> <!-- Grid Template -->
<div v-if="templates.length" class="grid gap-8 max-w-[1100px] mx-auto grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"> <div v-if="templates.length" class="grid gap-8 max-w-[1100px] mx-auto grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
<div v-for="t in templates" :key="t.id" <div
class="bg-white border rounded-lg overflow-hidden shadow-md hover:shadow-xl transition-shadow duration-300"> v-for="t in templates"
:key="t.id"
class="bg-white border rounded-lg overflow-hidden shadow-md hover:shadow-xl transition-shadow duration-300"
>
<!-- Image --> <!-- Image -->
<img :src="t.foto" :alt="t.nama_template" class="w-full h-48 object-cover" /> <img
:src="`http://localhost:8000${t.foto}`"
:alt="t.nama_template"
class="w-full h-48 object-cover"
/>
<!-- Body --> <!-- Body -->
<div class="p-5 text-center"> <div class="p-5 text-center">
@ -95,45 +58,41 @@ const templates = computed(() =>
<p class="text-green-600 font-semibold text-xl mb-1"> <p class="text-green-600 font-semibold text-xl mb-1">
Rp {{ Number(t.harga).toLocaleString('id-ID') }} Rp {{ Number(t.harga).toLocaleString('id-ID') }}
</p> </p>
<p class="text-gray-500 mb-4 font-medium">Paket: {{ t.paket }}</p>
<!-- Dropdown fitur --> <!-- Dropdown fitur -->
<div v-if="t.fiturs && t.fiturs.length > 0" class="relative mb-4"> <div v-if="t.fiturs && t.fiturs.length > 0" class="relative mb-4">
<button @click="toggleDropdown(t.id)" <button
class="w-full bg-white border border-gray-300 rounded-md shadow-sm px-4 py-2 inline-flex justify-between items-start"> @click="toggleDropdown(t.id)"
class="w-full bg-white border border-gray-300 rounded-md shadow-sm px-4 py-2 inline-flex justify-between items-center"
>
<span class="mx-auto text-gray-700 font-semibold">FITUR YANG TERSEDIA</span> <span class="mx-auto text-gray-700 font-semibold">FITUR YANG TERSEDIA</span>
<svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" <svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
fill="currentColor"> <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
<path fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd" />
</svg> </svg>
</button> </button>
<transition name="fade"> <div v-if="openDropdownId === t.id">
<div v-if="openDropdownId === t.id" class="mt-4"> <ul class="mt-4 space-y-2 text-gray-600 text-left">
<ul <li v-for="f in t.fiturs" :key="f.id" class="flex items-center">
class="space-y-2 text-gray-600 text-left max-h-60 overflow-y-auto px-3 py-2 border border-gray-200 rounded-md shadow-inner bg-gray-50"> <svg class="h-4 w-4 text-green-500 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<li v-for="f in t.fiturs" :key="f.id" class="flex items-center"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
<svg class="h-4 w-4 text-green-500 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> </svg>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /> {{ f.deskripsi }}
</svg> </li>
{{ f.deskripsi }} </ul>
</li> </div>
</ul>
</div>
</transition>
</div> </div>
<!-- Buttons --> <!-- Tombol -->
<div class="flex items-center gap-3 mt-6"> <div class="flex items-center gap-3 mt-6">
<button <button
class="w-full bg-white border border-gray-300 text-gray-800 font-semibold py-2 px-4 rounded-lg hover:bg-gray-100 transition-colors"> class="w-full bg-white border border-gray-300 text-gray-800 font-semibold py-2 px-4 rounded-lg hover:bg-gray-100 transition-colors">
Preview Preview
</button> </button>
<NuxtLink <NuxtLink
:to="`/form/${t.kategori ? t.kategori.toLowerCase().replace(/ /g, '-') : ''}` + `?template_id=${t.id}`" :to="`/form/${t.kategori.nama.toLowerCase().replace(/ /g, '-')}` + `?template_id=${t.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
</NuxtLink> </NuxtLink>
</div> </div>
@ -141,7 +100,7 @@ const templates = computed(() =>
</div> </div>
</div> </div>
<!-- Jika tidak ada template --> <!-- Jika error -->
<div v-else class="text-gray-500">Tidak ada template yang bisa ditampilkan</div> <div v-else class="text-gray-500">Tidak ada template yang bisa ditampilkan</div>
<!-- See more --> <!-- See more -->