Compare commits
11 Commits
main
...
production
Author | SHA1 | Date | |
---|---|---|---|
d078aab4b0 | |||
213a3874d0 | |||
e20f1fa12f | |||
ef91b38fa5 | |||
|
ddea7e321f | ||
ddae4df823 | |||
ff19646ba7 | |||
49ac69876c | |||
c11fdaf58f | |||
b28ee832d5 | |||
720b968c6b |
@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class AdminAuthController extends Controller
|
||||
{
|
||||
public function showLogin()
|
||||
{
|
||||
return view('admin.auth.login');
|
||||
}
|
||||
|
||||
public function login(Request $request)
|
||||
{
|
||||
$credentials = $request->validate([
|
||||
'email' => ['required','email'],
|
||||
'password' => ['required'],
|
||||
]);
|
||||
|
||||
$remember = $request->boolean('remember');
|
||||
|
||||
if (Auth::guard('admin')->attempt($credentials, $remember)) {
|
||||
$request->session()->regenerate();
|
||||
return redirect()->intended(route('admin.dashboard'));
|
||||
}
|
||||
|
||||
return back()->withErrors([
|
||||
'email' => 'Email atau password salah.',
|
||||
])->onlyInput('email');
|
||||
}
|
||||
|
||||
public function logout(Request $request)
|
||||
{
|
||||
Auth::guard('admin')->logout();
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
return redirect()->route('admin.login');
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Kategori;
|
||||
|
||||
class KategoriApiController extends Controller
|
||||
{
|
||||
// Ambil semua kategori
|
||||
public function index()
|
||||
{
|
||||
return response()->json(Kategori::all());
|
||||
}
|
||||
|
||||
// Ambil detail satu kategori
|
||||
public function show(Kategori $kategori)
|
||||
{
|
||||
return response()->json($kategori);
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Pelanggan;
|
||||
use App\Models\PelangganDetail;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class KhitanApiController extends Controller
|
||||
{
|
||||
public function store(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'nama_pemesan' => 'required|string|max:255',
|
||||
'no_hp' => 'required|string|max:20',
|
||||
'email' => 'required|email',
|
||||
'nama_anak' => 'required|string|max:255',
|
||||
'nama_orangtua' => 'required|string|max:255',
|
||||
'alamat' => 'required|string',
|
||||
'tanggal_acara' => 'required|date',
|
||||
'link_musik' => 'nullable|string',
|
||||
'kata_pengucapan' => 'nullable|string',
|
||||
'galeri' => 'nullable|string',
|
||||
'template_id' => 'required|exists:templates,id',
|
||||
]);
|
||||
|
||||
$pelanggan = Pelanggan::create([
|
||||
'nama_pemesan' => $data['nama_pemesan'],
|
||||
'nama_template' => 'Template Khitan',
|
||||
'kategori' => 'khitan',
|
||||
'email' => $data['email'],
|
||||
'no_tlpn' => $data['no_hp'],
|
||||
'harga' => 0,
|
||||
]);
|
||||
|
||||
PelangganDetail::create([
|
||||
'pelanggan_id' => $pelanggan->id,
|
||||
'detail_form' => $data,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Form khitan berhasil dikirim',
|
||||
'data' => $pelanggan->load('details')
|
||||
], 201);
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Pelanggan;
|
||||
use App\Models\PelangganDetail;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PernikahanApiController extends Controller
|
||||
{
|
||||
public function store(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'nama_pemesan' => 'required|string|max:255',
|
||||
'no_hp' => 'required|string|max:20',
|
||||
'email' => 'required|email',
|
||||
'nama_pria' => 'required|string|max:255',
|
||||
'nama_wanita' => 'required|string|max:255',
|
||||
'alamat' => 'required|string',
|
||||
'tanggal_acara' => 'required|date',
|
||||
'link_undangan' => 'nullable|string',
|
||||
'kata_pengucapan' => 'nullable|string',
|
||||
'galeri' => 'nullable|string',
|
||||
'link_musik' => 'nullable|string',
|
||||
'template_id' => 'required|exists:templates,id',
|
||||
]);
|
||||
|
||||
$pelanggan = Pelanggan::create([
|
||||
'nama_pemesan' => $data['nama_pemesan'],
|
||||
'nama_template' => 'Template Pernikahan',
|
||||
'kategori' => 'pernikahan',
|
||||
'email' => $data['email'],
|
||||
'no_tlpn' => $data['no_hp'],
|
||||
'harga' => 0,
|
||||
]);
|
||||
|
||||
PelangganDetail::create([
|
||||
'pelanggan_id' => $pelanggan->id,
|
||||
'detail_form' => $data,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Form pernikahan berhasil dikirim',
|
||||
'data' => $pelanggan->load('details')
|
||||
], 201);
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Review;
|
||||
|
||||
class ReviewController extends Controller
|
||||
{
|
||||
// Ambil semua ulasan
|
||||
public function index()
|
||||
{
|
||||
$reviews = Review::all();
|
||||
return view('admin.reviews.index', compact('reviews'));
|
||||
}
|
||||
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Template;
|
||||
|
||||
class TemplateApiController extends Controller
|
||||
{
|
||||
// User hanya bisa lihat semua template
|
||||
public function index()
|
||||
{
|
||||
return response()->json(Template::with(['kategori','fitur'])->get());
|
||||
}
|
||||
|
||||
// User bisa lihat detail 1 template
|
||||
public function show(Template $template)
|
||||
{
|
||||
return response()->json($template->load(['kategori','fitur']));
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Pelanggan;
|
||||
use App\Models\PelangganDetail;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class UlangTahunApiController extends Controller
|
||||
{
|
||||
public function store(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'nama_pemesan' => 'required|string|max:255',
|
||||
'no_hp' => 'required|string|max:20',
|
||||
'email' => 'required|email',
|
||||
'nama_panjang' => 'required|string|max:255',
|
||||
'nama_panggilan' => 'required|string|max:255',
|
||||
'ulang_tahun_ke' => 'required|integer',
|
||||
'anak_ke' => 'required|integer',
|
||||
'nama_orangtua' => 'required|string|max:255',
|
||||
'alamat' => 'required|string',
|
||||
'tanggal_acara' => 'required|date',
|
||||
'link_musik' => 'nullable|string',
|
||||
'kata_pengucapan' => 'nullable|string',
|
||||
'galeri' => 'nullable|string',
|
||||
'template_id' => 'required|exists:templates,id',
|
||||
]);
|
||||
|
||||
$pelanggan = Pelanggan::create([
|
||||
'nama_pemesan' => $data['nama_pemesan'],
|
||||
'nama_template' => 'Template Ulang Tahun',
|
||||
'kategori' => 'ulang_tahun',
|
||||
'email' => $data['email'],
|
||||
'no_tlpn' => $data['no_hp'],
|
||||
'harga' => 0,
|
||||
]);
|
||||
|
||||
PelangganDetail::create([
|
||||
'pelanggan_id' => $pelanggan->id,
|
||||
'detail_form' => $data,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Form ulang tahun berhasil dikirim',
|
||||
'data' => $pelanggan->load('details')
|
||||
], 201);
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Fitur;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FiturController extends Controller
|
||||
{
|
||||
// Tampilkan semua fitur (halaman admin)
|
||||
public function index()
|
||||
{
|
||||
$fitur = Fitur::all();
|
||||
return view('admin.fitur.index', compact('fitur'));
|
||||
}
|
||||
|
||||
// Form tambah fitur
|
||||
public function create()
|
||||
{
|
||||
return view('admin.fitur.create');
|
||||
}
|
||||
|
||||
// Simpan fitur baru
|
||||
public function store(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'deskripsi' => 'required|string',
|
||||
]);
|
||||
|
||||
Fitur::create($data);
|
||||
|
||||
return redirect()->route('admin.fitur.index')->with('success', 'Fitur berhasil ditambahkan!');
|
||||
}
|
||||
|
||||
public function update(Request $request, Fitur $fitur)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'deskripsi' => 'required|string',
|
||||
]);
|
||||
|
||||
$fitur->update($data);
|
||||
|
||||
return redirect()->route('admin.fitur.index')->with('success', 'Fitur berhasil diperbarui!');
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Hapus fitur
|
||||
public function destroy(Fitur $fitur)
|
||||
{
|
||||
$fitur->delete();
|
||||
|
||||
return redirect()->route('admin.fitur.index')->with('success', 'Fitur berhasil dihapus!');
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Kategori;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class KategoriController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$kategori = Kategori::all();
|
||||
return view('admin.kategori.index', compact('kategori'));
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
return view('admin.kategori.create');
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'nama' => 'required|string|max:255',
|
||||
'deskripsi' => 'nullable|string',
|
||||
'foto' => 'nullable|image|mimes:jpg,jpeg,png,gif|max:5120',
|
||||
]);
|
||||
|
||||
if ($request->hasFile('foto')) {
|
||||
$data['foto'] = $request->file('foto')->store('kategori', 'public');
|
||||
}
|
||||
|
||||
Kategori::create($data);
|
||||
return redirect()->route('admin.kategori.index')->with('success', 'Kategori berhasil ditambahkan!');
|
||||
}
|
||||
|
||||
public function edit(Kategori $kategori)
|
||||
{
|
||||
return view('admin.kategori.edit', compact('kategori'));
|
||||
}
|
||||
|
||||
public function update(Request $request, Kategori $kategori)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'nama' => 'required|string|max:255',
|
||||
'deskripsi' => 'nullable|string',
|
||||
'foto' => 'nullable|image|mimes:jpg,jpeg,png,gif|max:5120',
|
||||
]);
|
||||
|
||||
if ($request->hasFile('foto')) {
|
||||
$data['foto'] = $request->file('foto')->store('kategori', 'public');
|
||||
}
|
||||
|
||||
$kategori->update($data);
|
||||
return redirect()->route('admin.kategori.index')->with('success', 'Kategori berhasil diperbarui!');
|
||||
}
|
||||
|
||||
public function destroy(Kategori $kategori)
|
||||
{
|
||||
$kategori->delete();
|
||||
return redirect()->route('admin.kategori.index')->with('success', 'Kategori berhasil dihapus!');
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Pelanggan;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PelangganController extends Controller
|
||||
{
|
||||
// Tampilkan semua pelanggan (admin)
|
||||
public function index()
|
||||
{
|
||||
$pelanggans = Pelanggan::all();
|
||||
return view('admin.pelanggan.index', compact('pelanggans'));
|
||||
}
|
||||
|
||||
// Detail pelanggan
|
||||
public function show(Pelanggan $pelanggan)
|
||||
{
|
||||
return view('admin.pelanggan.show', compact('pelanggan'));
|
||||
}
|
||||
|
||||
// Hapus pelanggan
|
||||
public function destroy(Pelanggan $pelanggan)
|
||||
{
|
||||
$pelanggan->delete();
|
||||
return redirect()->route('admin.pelanggan.index')->with('success', 'Pelanggan berhasil dihapus!');
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Template;
|
||||
use App\Models\Kategori;
|
||||
use App\Models\Fitur;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class TemplateController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$templates = Template::with(['kategori','fitur'])->get();
|
||||
$kategoris = Kategori::all();
|
||||
$fiturs = Fitur::all();
|
||||
|
||||
return view('admin.templates.index', compact('templates', 'kategoris', 'fiturs'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'nama_template' => 'required|string|max:255',
|
||||
'kategori_id' => 'required|exists:kategoris,id',
|
||||
'fitur_id' => 'required|exists:fiturs,id',
|
||||
'foto' => 'nullable|image|mimes:jpg,jpeg,png,gif|max:5120',
|
||||
'harga' => 'required|numeric|min:0'
|
||||
]);
|
||||
|
||||
if ($request->hasFile('foto')) {
|
||||
$data['foto'] = $request->file('foto')->store('templates', 'public');
|
||||
}
|
||||
|
||||
Template::create($data);
|
||||
return redirect()->route('templates.index')->with('success', 'Template berhasil ditambahkan!');
|
||||
}
|
||||
|
||||
public function update(Request $request, Template $template)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'nama_template' => 'required|string|max:255',
|
||||
'kategori_id' => 'required|exists:kategoris,id',
|
||||
'fitur_id' => 'required|exists:fiturs,id',
|
||||
'foto' => 'nullable|image|mimes:jpg,jpeg,png,gif|max:5120',
|
||||
'harga' => 'required|numeric|min:0'
|
||||
]);
|
||||
|
||||
if ($request->hasFile('foto')) {
|
||||
if ($template->foto && Storage::disk('public')->exists($template->foto)) {
|
||||
Storage::disk('public')->delete($template->foto);
|
||||
}
|
||||
$data['foto'] = $request->file('foto')->store('templates', 'public');
|
||||
}
|
||||
|
||||
$template->update($data);
|
||||
return redirect()->route('templates.index')->with('success', 'Template berhasil diperbarui!');
|
||||
}
|
||||
|
||||
public function destroy(Template $template)
|
||||
{
|
||||
if ($template->foto && Storage::disk('public')->exists($template->foto)) {
|
||||
Storage::disk('public')->delete($template->foto);
|
||||
}
|
||||
|
||||
$template->delete();
|
||||
return redirect()->route('templates.index')->with('success', 'Template berhasil dihapus!');
|
||||
}
|
||||
|
||||
public function byKategori($id)
|
||||
{
|
||||
$kategori = Kategori::findOrFail($id);
|
||||
$templates = Template::with(['kategori','fitur'])
|
||||
->where('kategori_id', $id)
|
||||
->get();
|
||||
$kategoris = Kategori::all();
|
||||
$fiturs = Fitur::all();
|
||||
|
||||
return view('admin.templates.index', compact('templates', 'kategoris', 'fiturs', 'kategori'));
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
class Admin extends Authenticatable
|
||||
{
|
||||
use Notifiable;
|
||||
|
||||
protected $fillable = ['name','email','password'];
|
||||
|
||||
protected $hidden = ['password','remember_token'];
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
<?php
|
||||
// app/Models/Fitur.php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Fitur extends Model
|
||||
{
|
||||
protected $fillable = ['deskripsi'];
|
||||
|
||||
public function templates()
|
||||
{
|
||||
return $this->hasMany(Template::class);
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
<?php
|
||||
// app/Models/Kategori.php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Kategori extends Model
|
||||
{
|
||||
protected $fillable = ['nama', 'deskripsi', 'foto'];
|
||||
|
||||
public function templates()
|
||||
{
|
||||
return $this->hasMany(Template::class);
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
// app/Models/Khitan.php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Khitan extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'template_id', 'nama_pemesan', 'no_hp', 'email',
|
||||
'nama_panjang', 'nama_pendek', 'nama_orangtua',
|
||||
'alamat', 'tanggal_acara', 'link_musik', 'galeri',
|
||||
'kata_pengucapan'
|
||||
];
|
||||
|
||||
public function template() {
|
||||
return $this->belongsTo(Template::class);
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Pelanggan extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'nama_pemesan',
|
||||
'nama_template',
|
||||
'kategori',
|
||||
'email',
|
||||
'no_tlpn',
|
||||
'harga',
|
||||
];
|
||||
|
||||
public function details()
|
||||
{
|
||||
return $this->hasOne(PelangganDetail::class);
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class PelangganDetail extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = ['pelanggan_id', 'detail_form'];
|
||||
|
||||
protected $casts = [
|
||||
'detail_form' => 'array',
|
||||
];
|
||||
|
||||
public function pelanggan()
|
||||
{
|
||||
return $this->belongsTo(Pelanggan::class);
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
<?php
|
||||
// app/Models/Pernikahan.php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Pernikahan extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'template_id', 'nama_pemesan', 'no_hp', 'email',
|
||||
'nama_pria', 'nama_wanita', 'alamat', 'tanggal_acara',
|
||||
'link_undangan', 'kata_pengucapan', 'galeri', 'link_musik'
|
||||
];
|
||||
|
||||
public function template() {
|
||||
return $this->belongsTo(Template::class);
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Review extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'rating',
|
||||
'message',
|
||||
'name',
|
||||
'city',
|
||||
];
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
// app/Models/Template.php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Template extends Model
|
||||
{
|
||||
protected $fillable = ['nama_template', 'kategori_id', 'fitur_id', 'foto', 'harga'];
|
||||
|
||||
public function kategori() {
|
||||
return $this->belongsTo(Kategori::class);
|
||||
}
|
||||
|
||||
public function fitur() {
|
||||
return $this->belongsTo(Fitur::class);
|
||||
}
|
||||
|
||||
public function pernikahan() {
|
||||
return $this->hasOne(Pernikahan::class);
|
||||
}
|
||||
|
||||
public function ulangTahun() {
|
||||
return $this->hasOne(UlangTahun::class);
|
||||
}
|
||||
|
||||
public function khitan() {
|
||||
return $this->hasOne(Khitan::class);
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
// app/Models/UlangTahun.php
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UlangTahun extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'template_id', 'nama_pemesan', 'no_hp', 'email',
|
||||
'nama_panjang', 'nama_panggilan', 'ulang_tahun_ke', 'anak_ke',
|
||||
'nama_orangtua', 'alamat', 'tanggal_acara', 'link_musik',
|
||||
'kata_pengucapan', 'galeri'
|
||||
];
|
||||
|
||||
public function template() {
|
||||
return $this->belongsTo(Template::class);
|
||||
}
|
||||
}
|
@ -2,39 +2,94 @@
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Defaults
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default authentication "guard" and password
|
||||
| reset options for your application. You may change these defaults
|
||||
| as required, but they're a perfect start for most applications.
|
||||
|
|
||||
*/
|
||||
|
||||
'defaults' => [
|
||||
'guard' => 'web',
|
||||
'passwords' => 'users',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Guards
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Next, you may define every authentication guard for your application.
|
||||
| Of course, a great default configuration has been defined for you
|
||||
| here which uses session storage and the Eloquent user provider.
|
||||
|
|
||||
| All authentication drivers have a user provider. This defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| mechanisms used by this application to persist your user's data.
|
||||
|
|
||||
| Supported: "session"
|
||||
|
|
||||
*/
|
||||
|
||||
'guards' => [
|
||||
// Guard untuk user biasa
|
||||
'web' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'users',
|
||||
],
|
||||
|
||||
// Guard untuk admin
|
||||
'admin' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'admins',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| User Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| All authentication drivers have a user provider. This defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| mechanisms used by this application to persist your user's data.
|
||||
|
|
||||
| If you have multiple user tables or models you may configure multiple
|
||||
| sources which represent each model / table. These sources may then
|
||||
| be assigned to any extra authentication guards you have defined.
|
||||
|
|
||||
| Supported: "database", "eloquent"
|
||||
|
|
||||
*/
|
||||
|
||||
'providers' => [
|
||||
// Provider untuk user biasa
|
||||
'users' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => App\Models\User::class,
|
||||
],
|
||||
|
||||
// Provider untuk admin
|
||||
'admins' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => App\Models\Admin::class,
|
||||
],
|
||||
// 'users' => [
|
||||
// 'driver' => 'database',
|
||||
// 'table' => 'users',
|
||||
// ],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Resetting Passwords
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You may specify multiple password reset configurations if you have more
|
||||
| than one user table or model in the application and you want to have
|
||||
| separate password reset settings based on the specific user types.
|
||||
|
|
||||
| The expire time is the number of minutes that each reset token will be
|
||||
| considered valid. This security feature keeps tokens short-lived so
|
||||
| they have less time to be guessed. You may change this as needed.
|
||||
|
|
||||
| The throttle setting is the number of seconds a user must wait before
|
||||
| generating more password reset tokens. This prevents the user from
|
||||
| quickly generating a very large amount of password reset tokens.
|
||||
|
|
||||
*/
|
||||
|
||||
'passwords' => [
|
||||
'users' => [
|
||||
'provider' => 'users',
|
||||
@ -42,14 +97,19 @@ return [
|
||||
'expire' => 60,
|
||||
'throttle' => 60,
|
||||
],
|
||||
'admins' => [
|
||||
'provider' => 'admins',
|
||||
'table' => 'password_reset_tokens',
|
||||
'expire' => 60,
|
||||
'throttle' => 60,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Confirmation Timeout
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define the amount of seconds before a password confirmation
|
||||
| times out and the user is prompted to re-enter their password via the
|
||||
| confirmation screen. By default, the timeout lasts for three hours.
|
||||
|
|
||||
*/
|
||||
|
||||
'password_timeout' => 10800,
|
||||
|
||||
];
|
||||
|
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Template>
|
||||
*/
|
||||
class TemplateFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('admins', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('email')->unique();
|
||||
$table->string('password');
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('admins');
|
||||
}
|
||||
};
|
@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
// database/migrations/2025_09_08_000001_create_kategoris_table.php
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void {
|
||||
Schema::create('kategoris', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('nama');
|
||||
$table->text('deskripsi')->nullable();
|
||||
$table->string('foto')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void {
|
||||
Schema::dropIfExists('kategoris');
|
||||
}
|
||||
};
|
@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('reviews', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->tinyInteger('rating'); // nilai bintang 1–5
|
||||
$table->text('message');
|
||||
$table->string('name');
|
||||
$table->string('city');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('reviews');
|
||||
}
|
||||
};
|
@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
// database/migrations/2025_09_08_000002_create_fiturs_table.php
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void {
|
||||
Schema::create('fiturs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->text('deskripsi');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void {
|
||||
Schema::dropIfExists('fiturs');
|
||||
}
|
||||
};
|
@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
// database/migrations/2025_09_08_000003_create_templates_table.php
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void {
|
||||
Schema::create('templates', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('nama_template');
|
||||
$table->foreignId('kategori_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('fitur_id')->constrained()->cascadeOnDelete();
|
||||
$table->decimal('harga', 10, 2)->default(0);
|
||||
$table->string('foto')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void {
|
||||
Schema::dropIfExists('templates');
|
||||
}
|
||||
};
|
||||
|
@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('pelanggans', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('nama_pemesan');
|
||||
$table->string('nama_template');
|
||||
$table->string('kategori');
|
||||
$table->string('email');
|
||||
$table->string('no_tlpn');
|
||||
$table->decimal('harga', 15, 2)->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('pelanggans');
|
||||
}
|
||||
};
|
@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('pelanggan_details', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('pelanggan_id');
|
||||
$table->json('detail_form'); // data sesuai kategori
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('pelanggan_id')->references('id')->on('pelanggans')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('pelanggan_details');
|
||||
}
|
||||
};
|
@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
// database/migrations/2025_09_08_000004_create_pernikahans_table.php
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void {
|
||||
Schema::create('pernikahans', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('template_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('nama_pemesan');
|
||||
$table->string('no_hp');
|
||||
$table->string('email');
|
||||
$table->string('nama_pria');
|
||||
$table->string('nama_wanita');
|
||||
$table->string('alamat');
|
||||
$table->date('tanggal_acara');
|
||||
$table->string('link_undangan')->nullable();
|
||||
$table->text('kata_pengucapan')->nullable();
|
||||
$table->string('galeri')->nullable();
|
||||
$table->string('link_musik')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void {
|
||||
Schema::dropIfExists('pernikahans');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
// database/migrations/2025_09_08_000005_create_ulang_tahuns_table.php
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void {
|
||||
Schema::create('ulang_tahuns', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('template_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('nama_pemesan');
|
||||
$table->string('no_hp');
|
||||
$table->string('email');
|
||||
$table->string('nama_panjang');
|
||||
$table->string('nama_panggilan');
|
||||
$table->integer('ulang_tahun_ke');
|
||||
$table->integer('anak_ke');
|
||||
$table->string('nama_orangtua');
|
||||
$table->string('alamat');
|
||||
$table->date('tanggal_acara');
|
||||
$table->string('link_musik')->nullable();
|
||||
$table->text('kata_pengucapan')->nullable();
|
||||
$table->string('galeri')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void {
|
||||
Schema::dropIfExists('ulang_tahuns');
|
||||
}
|
||||
};
|
@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
// database/migrations/2025_09_08_000006_create_khitans_table.php
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void {
|
||||
Schema::create('khitans', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('template_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('nama_pemesan');
|
||||
$table->string('no_hp');
|
||||
$table->string('email');
|
||||
$table->string('nama_panjang');
|
||||
$table->string('nama_pendek');
|
||||
$table->string('nama_orangtua');
|
||||
$table->string('alamat');
|
||||
$table->date('tanggal_acara');
|
||||
$table->string('link_musik')->nullable();
|
||||
$table->string('galeri')->nullable();
|
||||
$table->text('kata_pengucapan')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void {
|
||||
Schema::dropIfExists('khitans');
|
||||
}
|
||||
};
|
||||
|
@ -2,20 +2,16 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Admin;
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class AdminSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
Admin::updateOrCreate(
|
||||
['email' => 'admin@example.com'],
|
||||
[
|
||||
'name' => 'Super Admin',
|
||||
'password' => Hash::make('password123'), // ganti setelah login
|
||||
]
|
||||
);
|
||||
//
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,16 @@ use Illuminate\Database\Seeder;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Seed the application's database.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$this->call([
|
||||
AdminSeeder::class,
|
||||
]);
|
||||
}
|
||||
{
|
||||
// \App\Models\User::factory(10)->create();
|
||||
|
||||
// \App\Models\User::factory()->create([
|
||||
// 'name' => 'Test User',
|
||||
// 'email' => 'test@example.com',
|
||||
// ]);
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 20 KiB |
@ -1,109 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="id">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Login Admin</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background: #ffffff;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
background: #EFEFEF;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 0 25px rgb(0, 123, 255);
|
||||
width: 100%;
|
||||
max-width: 400px; /* batas lebar maksimum */
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.login-logo {
|
||||
display: block;
|
||||
margin: 0 auto 15px;
|
||||
max-width: 130px;
|
||||
}
|
||||
|
||||
.btn-login {
|
||||
background-color: #3bb9ff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.btn-login:hover {
|
||||
background-color: #1a8edb;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* Responsive padding */
|
||||
@media (max-width: 576px) {
|
||||
.login-card {
|
||||
padding: 20px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.login-logo {
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="login-card">
|
||||
<div class="text-center mb-3">
|
||||
<!-- Logo -->
|
||||
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="login-logo">
|
||||
</div>
|
||||
|
||||
<h4 class="text-center mb-2 fw-bold">SELAMAT DATANG</h4>
|
||||
<p class="text-center text-muted mb-4">Silakan masukkan email dan password anda.</p>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger">
|
||||
{{ $errors->first() }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('admin.login.post') }}" method="POST">
|
||||
@csrf
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Nama</label>
|
||||
<input type="email" name="email" value="{{ old('email') }}" class="form-control"
|
||||
placeholder="Masukkan nama Anda" required autofocus>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Kata Sandi</label>
|
||||
<input type="password" name="password" class="form-control"
|
||||
placeholder="Masukkan kata sandi Anda" required>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="remember" id="remember">
|
||||
<label class="form-check-label" for="remember">Ingat saya</label>
|
||||
</div>
|
||||
<a href="#" class="text-decoration-none mt-2 mt-sm-0">Lupa kata sandi</a>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-login text-white w-100">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,54 +0,0 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Halaman Dasbor')
|
||||
|
||||
@section('content')
|
||||
<div class="container mx-auto py-4">
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-xl font-bold">Halaman Dasbor</h3>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-blue-100 text-blue-600 px-3 py-2 rounded-lg flex items-center gap-2 text-sm">
|
||||
<i class="bi bi-clock-history"></i>
|
||||
{{ \Carbon\Carbon::now()->translatedFormat('l, d F Y') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cards -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class="bg-white rounded-lg shadow-md p-4 flex justify-between items-center hover:shadow-lg transition-transform duration-300 transform hover:-translate-y-2">
|
||||
<div>
|
||||
<h5 class="text-gray-500 text-sm">Kategori</h5>
|
||||
<h3 class="font-bold text-xl">10</h3>
|
||||
</div>
|
||||
<div class="w-12 h-12 bg-blue-100 text-blue-600 rounded-lg flex items-center justify-center text-xl">
|
||||
<i class="bi bi-diagram-3"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-lg shadow-md p-4 flex justify-between items-center hover:shadow-lg transition-transform duration-300 transform hover:-translate-y-2">
|
||||
<div>
|
||||
<h5 class="text-gray-500 text-sm">Templat</h5>
|
||||
<h3 class="font-bold text-xl">20</h3>
|
||||
</div>
|
||||
<div class="w-12 h-12 bg-blue-100 text-blue-600 rounded-lg flex items-center justify-center text-xl">
|
||||
<i class="bi bi-card-list"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-lg shadow-md p-4 flex justify-between items-center hover:shadow-lg transition-transform duration-300 transform hover:-translate-y-2">
|
||||
<div>
|
||||
<h5 class="text-gray-500 text-sm">Pelanggan</h5>
|
||||
<h3 class="font-bold text-xl">24</h3>
|
||||
</div>
|
||||
<div class="w-12 h-12 bg-blue-100 text-blue-600 rounded-lg flex items-center justify-center text-xl">
|
||||
<i class="bi bi-person"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pesan login -->
|
||||
<div class="bg-green-100 text-green-700 p-4 rounded-lg mt-4">
|
||||
Berhasil login sebagai <strong>{{ auth('admin')->user()->name }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
@ -1,164 +0,0 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Manajemen Fitur')
|
||||
|
||||
@section('content')
|
||||
<div class="container mx-auto py-4">
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-xl font-bold">Manajemen Fitur</h3>
|
||||
<button id="openTambahModal" class="bg-blue-600 text-white px-3 py-2.5 rounded flex items-center">
|
||||
<i class="bi bi-plus-lg mr-1"></i> Tambah Fitur
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Tabel Fitur -->
|
||||
<div class="bg-white rounded-lg shadow-sm">
|
||||
<div class="p-4 overflow-x-auto">
|
||||
<table class="w-full table-fixed border border-gray-300 text-left">
|
||||
<thead class="bg-gray-100">
|
||||
<tr>
|
||||
<th class="w-[10%] p-2 border border-gray-300 text-center">Nomor</th>
|
||||
<th class="w-[70%] p-2 border border-gray-300 text-center">Fitur</th>
|
||||
<th class="w-[20%] p-2 border border-gray-300 text-center">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse ($fitur as $key => $item)
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="p-2 border border-gray-300">{{ $key + 1 }}</td>
|
||||
<td class="p-2 border border-gray-300 truncate whitespace-nowrap">{{ $item->deskripsi }}
|
||||
</td>
|
||||
<td class="p-2 border border-gray-300 text-center">
|
||||
<div class="flex justify-center space-x-2">
|
||||
<button class="text-blue-600 flex items-center pr-4 openEditModalBtn"
|
||||
data-id="{{ $item->id }}">
|
||||
<i class="bi bi-pencil mr-1"></i> Ubah
|
||||
</button>
|
||||
<form action="{{ route('admin.fitur.destroy', $item->id) }}" method="POST"
|
||||
class="inline" onsubmit="return confirm('Yakin mau hapus fitur ini?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="text-red-600 flex items-center">
|
||||
<i class="bi bi-trash mr-1"></i> Hapus
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="3" class="p-2 text-center text-gray-500">Belum ada fitur</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Tambah -->
|
||||
<div id="modalTambah" class="fixed inset-0 hidden items-center justify-center z-50">
|
||||
<div class="absolute inset-0 bg-black opacity-50" id="closeTambahModal"></div>
|
||||
<div class="bg-white rounded-lg shadow-lg w-full max-w-md z-50 overflow-hidden">
|
||||
<form action="{{ route('admin.fitur.store') }}" method="POST">
|
||||
@csrf
|
||||
<div class="p-4 border-b">
|
||||
<h5 class="text-lg font-medium">Tambah Fitur</h5>
|
||||
</div>
|
||||
<div class="p-4 space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Nama Fitur</label>
|
||||
<input type="text" name="deskripsi" class="w-full p-2 border rounded" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 border-t flex justify-end space-x-2">
|
||||
<button type="button" id="closeTambahBtn"
|
||||
class="bg-gray-300 text-black px-3 py-1 rounded">Batal</button>
|
||||
<button class="bg-blue-600 text-white px-3 py-1 rounded">Simpan</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Edit -->
|
||||
@foreach ($fitur as $item)
|
||||
<div id="modalEdit{{ $item->id }}" class="fixed inset-0 hidden items-center justify-center z-50">
|
||||
<div class="absolute inset-0 bg-black opacity-50 closeEditOverlay" data-id="{{ $item->id }}"></div>
|
||||
<div class="bg-white rounded-lg shadow-lg w-full max-w-md z-50 overflow-hidden">
|
||||
<form action="{{ route('admin.fitur.update', $item->id) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="p-4 border-b">
|
||||
<h5 class="text-lg font-medium">Edit Fitur</h5>
|
||||
</div>
|
||||
<div class="p-4 space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Nama Fitur</label>
|
||||
<input type="text" name="deskripsi" value="{{ $item->deskripsi }}"
|
||||
class="w-full p-2 border rounded" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 border-t flex justify-end space-x-2">
|
||||
<button type="button" class="bg-gray-300 text-black px-3 py-1 rounded closeEditBtn"
|
||||
data-id="{{ $item->id }}">Batal</button>
|
||||
<button class="bg-blue-600 text-white px-3 py-1 rounded">Simpan Perubahan</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
<script>
|
||||
// Modal Tambah
|
||||
const openTambahModal = document.getElementById('openTambahModal');
|
||||
const modalTambah = document.getElementById('modalTambah');
|
||||
const closeTambahBtn = document.getElementById('closeTambahBtn');
|
||||
const closeTambahOverlay = document.getElementById('closeTambahModal');
|
||||
|
||||
openTambahModal.addEventListener('click', () => {
|
||||
modalTambah.classList.remove('hidden');
|
||||
modalTambah.classList.add('flex');
|
||||
});
|
||||
closeTambahBtn.addEventListener('click', () => {
|
||||
modalTambah.classList.add('hidden');
|
||||
modalTambah.classList.remove('flex');
|
||||
});
|
||||
closeTambahOverlay.addEventListener('click', () => {
|
||||
modalTambah.classList.add('hidden');
|
||||
modalTambah.classList.remove('flex');
|
||||
});
|
||||
|
||||
// Modal Edit
|
||||
const openEditBtns = document.querySelectorAll('.openEditModalBtn');
|
||||
const closeEditBtns = document.querySelectorAll('.closeEditBtn');
|
||||
const closeEditOverlays = document.querySelectorAll('.closeEditOverlay');
|
||||
|
||||
openEditBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const id = btn.dataset.id;
|
||||
const modal = document.getElementById('modalEdit' + id);
|
||||
modal.classList.remove('hidden');
|
||||
modal.classList.add('flex');
|
||||
});
|
||||
});
|
||||
|
||||
closeEditBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const id = btn.dataset.id;
|
||||
const modal = document.getElementById('modalEdit' + id);
|
||||
modal.classList.add('hidden');
|
||||
modal.classList.remove('flex');
|
||||
});
|
||||
});
|
||||
|
||||
closeEditOverlays.forEach(overlay => {
|
||||
overlay.addEventListener('click', () => {
|
||||
const id = overlay.dataset.id;
|
||||
const modal = document.getElementById('modalEdit' + id);
|
||||
modal.classList.add('hidden');
|
||||
modal.classList.remove('flex');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
@ -1,231 +0,0 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Manajemen Kategori')
|
||||
|
||||
@section('content')
|
||||
<div class="container mx-auto py-4">
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-xl font-bold">Manajemen Kategori</h3>
|
||||
<button id="openTambahModal" class="bg-blue-600 text-white px-3 py-2.5 rounded flex items-center">
|
||||
<i class="bi bi-plus-lg mr-1"></i> Tambah Kategori
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Flash Message -->
|
||||
@if (session('success'))
|
||||
<div id="toast-success" class="mb-4 p-3 rounded bg-green-100 text-green-800 border border-green-300 shadow">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
<script>
|
||||
setTimeout(() => {
|
||||
document.getElementById('toast-success')?.remove();
|
||||
}, 3000);
|
||||
</script>
|
||||
@endif
|
||||
|
||||
@if ($errors->any())
|
||||
<div id="toast-error" class="mb-4 p-3 rounded bg-red-100 text-red-800 border border-red-300 shadow">
|
||||
<ul class="list-disc ml-5">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
<script>
|
||||
setTimeout(() => {
|
||||
document.getElementById('toast-error')?.remove();
|
||||
}, 5000);
|
||||
</script>
|
||||
@endif
|
||||
|
||||
<!-- Tabel Kategori -->
|
||||
<div class="bg-white rounded-lg shadow-sm">
|
||||
<div class="p-4 overflow-x-auto">
|
||||
<table class="w-full table-fixed text-left border border-gray-300 border-collapse">
|
||||
<thead class="bg-gray-100">
|
||||
<tr>
|
||||
<th class="p-2 border border-gray-300 w-[50px] text-center">Nomor</th>
|
||||
<th class="p-2 border border-gray-300 w-[150px] text-center">Nama Kategori</th>
|
||||
<th class="p-2 border border-gray-300 w-[300px] text-center">Keterangan</th>
|
||||
<th class="p-2 border border-gray-300 w-[90px] text-center">Foto</th>
|
||||
<th class="p-2 border border-gray-300 w-[110px] text-center">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse ($kategori as $key => $item)
|
||||
<tr>
|
||||
<td class="p-2 border border-gray-300 text-center truncate">{{ $key + 1 }}</td>
|
||||
<td class="p-2 border border-gray-300 truncate">{{ $item->nama }}</td>
|
||||
<td class="p-2 border border-gray-300 truncate">{{ $item->deskripsi ?? '-' }}</td>
|
||||
<td class="p-2 border border-gray-300 text-center">
|
||||
<div
|
||||
class="w-12 h-12 overflow-hidden rounded bg-gray-100 flex items-center justify-center mx-auto">
|
||||
<img src="{{ $item->foto ? asset('storage/' . $item->foto) : asset('default-image.png') }}"
|
||||
alt="foto" class="max-w-full max-h-full object-contain">
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-2 border border-gray-300 text-center">
|
||||
<div class="flex justify-center space-x-2">
|
||||
<button
|
||||
class="text-blue-600 hover:underline flex items-center pr-4 openEditModalBtn"
|
||||
data-id="{{ $item->id }}">
|
||||
<i class="bi bi-pencil mr-1"></i> Ubah
|
||||
</button>
|
||||
<form action="{{ route('admin.kategori.destroy', $item->id) }}" method="POST"
|
||||
class="inline" onsubmit="return confirm('Yakin mau hapus kategori ini?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="text-red-600 hover:underline flex items-center">
|
||||
<i class="bi bi-trash mr-1"></i> Hapus
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="p-2 text-center text-gray-500 border border-gray-300">Belum ada
|
||||
kategori</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Tambah -->
|
||||
<div id="modalTambah" class="fixed inset-0 hidden items-center justify-center z-50">
|
||||
<div class="absolute inset-0 bg-black opacity-50" id="closeTambahModal"></div>
|
||||
<div class="bg-white rounded-lg shadow-lg w-full max-w-md z-50 overflow-hidden">
|
||||
<form action="{{ route('admin.kategori.store') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
<div class="p-4 border-b">
|
||||
<h5 class="text-lg font-medium">Tambah Kategori</h5>
|
||||
</div>
|
||||
<div class="p-4 space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Nama</label>
|
||||
<input type="text" name="nama" class="w-full p-2 border rounded" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Deskripsi</label>
|
||||
<textarea name="deskripsi" class="w-full p-2 border rounded" rows="3"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Foto</label>
|
||||
<input type="file" name="foto" class="w-full p-2 border rounded" accept="image/*">
|
||||
<small class="text-gray-500">Format yang didukung: JPG, PNG, GIF. Maksimal 5MB.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 border-t flex justify-end space-x-2">
|
||||
<button type="button" id="closeTambahBtn"
|
||||
class="bg-gray-300 text-black px-3 py-1 rounded">Batal</button>
|
||||
<button class="bg-blue-600 text-white px-3 py-1 rounded">Simpan</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Edit -->
|
||||
@foreach ($kategori as $item)
|
||||
<div id="modalEdit{{ $item->id }}" class="fixed inset-0 hidden items-center justify-center z-50">
|
||||
<div class="absolute inset-0 bg-black opacity-50 closeEditOverlay" data-id="{{ $item->id }}"></div>
|
||||
<div class="bg-white rounded-lg shadow-lg w-full max-w-md z-50 overflow-hidden">
|
||||
<form action="{{ route('admin.kategori.update', $item->id) }}" method="POST"
|
||||
enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="p-4 border-b">
|
||||
<h5 class="text-lg font-medium">Edit Kategori</h5>
|
||||
</div>
|
||||
<div class="p-4 space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Nama</label>
|
||||
<input type="text" name="nama" value="{{ $item->nama }}"
|
||||
class="w-full p-2 border rounded" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Deskripsi</label>
|
||||
<textarea name="deskripsi" class="w-full p-2 border rounded" rows="3">{{ $item->deskripsi }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Foto</label>
|
||||
<input type="file" name="foto" class="w-full p-2 border rounded" accept="image/*">
|
||||
<small class="text-gray-500">Format yang didukung: JPG, PNG, GIF. Maksimal 5MB.</small>
|
||||
@if ($item->foto)
|
||||
<div class="mt-2">
|
||||
<small class="text-gray-500">Foto saat ini:</small><br>
|
||||
<div
|
||||
class="w-20 h-20 mt-1 overflow-hidden rounded bg-gray-100 flex items-center justify-center border">
|
||||
<img src="{{ asset('storage/' . $item->foto) }}" alt="foto"
|
||||
class="max-w-full max-h-full object-contain">
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 border-t flex justify-end space-x-2">
|
||||
<button type="button" class="bg-gray-300 text-black px-3 py-1 rounded closeEditBtn"
|
||||
data-id="{{ $item->id }}">Batal</button>
|
||||
<button class="bg-blue-600 text-white px-3 py-1 rounded">Simpan Perubahan</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
<script>
|
||||
// Modal Tambah
|
||||
const openTambahModal = document.getElementById('openTambahModal');
|
||||
const modalTambah = document.getElementById('modalTambah');
|
||||
const closeTambahBtn = document.getElementById('closeTambahBtn');
|
||||
const closeTambahOverlay = document.getElementById('closeTambahModal');
|
||||
|
||||
openTambahModal.addEventListener('click', () => {
|
||||
modalTambah.classList.remove('hidden');
|
||||
modalTambah.classList.add('flex');
|
||||
});
|
||||
closeTambahBtn.addEventListener('click', () => {
|
||||
modalTambah.classList.add('hidden');
|
||||
modalTambah.classList.remove('flex');
|
||||
});
|
||||
closeTambahOverlay.addEventListener('click', () => {
|
||||
modalTambah.classList.add('hidden');
|
||||
modalTambah.classList.remove('flex');
|
||||
});
|
||||
|
||||
// Modal Edit
|
||||
const openEditBtns = document.querySelectorAll('.openEditModalBtn');
|
||||
const closeEditBtns = document.querySelectorAll('.closeEditBtn');
|
||||
const closeEditOverlays = document.querySelectorAll('.closeEditOverlay');
|
||||
|
||||
openEditBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const id = btn.dataset.id;
|
||||
const modal = document.getElementById('modalEdit' + id);
|
||||
modal.classList.remove('hidden');
|
||||
modal.classList.add('flex');
|
||||
});
|
||||
});
|
||||
|
||||
closeEditBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const id = btn.dataset.id;
|
||||
const modal = document.getElementById('modalEdit' + id);
|
||||
modal.classList.add('hidden');
|
||||
modal.classList.remove('flex');
|
||||
});
|
||||
});
|
||||
|
||||
closeEditOverlays.forEach(overlay => {
|
||||
overlay.addEventListener('click', () => {
|
||||
const id = overlay.dataset.id;
|
||||
const modal = document.getElementById('modalEdit' + id);
|
||||
modal.classList.add('hidden');
|
||||
modal.classList.remove('flex');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
@ -1,133 +0,0 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Manajemen Pelanggan')
|
||||
|
||||
@section('content')
|
||||
<div class="container mx-auto py-4">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-xl font-bold">Manajemen Pelanggan</h3>
|
||||
</div>
|
||||
|
||||
<!-- Flash Message -->
|
||||
@if (session('success'))
|
||||
<div id="toast-success" class="mb-4 p-3 rounded bg-green-100 text-green-800 border border-green-300 shadow">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
<script>
|
||||
setTimeout(() => document.getElementById('toast-success')?.remove(), 3000);
|
||||
</script>
|
||||
@endif
|
||||
|
||||
<!-- Tabel Pelanggan -->
|
||||
<div class="bg-white rounded-lg shadow-sm">
|
||||
<div class="p-4 overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse border border-gray-300">
|
||||
<thead class="bg-gray-100">
|
||||
<tr>
|
||||
<th class="p-2 border border-gray-300 text-center w-16">Nomor</th>
|
||||
<th class="p-2 border border-gray-300 text-center">Nama</th>
|
||||
<th class="p-2 border border-gray-300 text-center">Email</th>
|
||||
<th class="p-2 border border-gray-300 text-center">No. Telepon</th>
|
||||
<th class="p-2 border border-gray-300 text-center">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($pelanggans as $key => $pelanggan)
|
||||
<tr>
|
||||
<td class="p-2 border border-gray-300 text-center">{{ $key + 1 }}</td>
|
||||
<td class="p-2 border border-gray-300 truncate">{{ $pelanggan->nama_pemesan }}</td>
|
||||
<td class="p-2 border border-gray-300 truncate">{{ $pelanggan->email }}</td>
|
||||
<td class="p-2 border border-gray-300 truncate">{{ $pelanggan->no_tlpn ?? '-' }}</td>
|
||||
<td class="p-2 border border-gray-300 text-center">
|
||||
<div class="flex justify-center space-x-2">
|
||||
<a href="{{ route('admin.pelanggan.show', $pelanggan->id) }}"
|
||||
class="text-blue-600 hover:underline flex items-center">
|
||||
<i class="bi bi-eye mr-1"></i> Detail
|
||||
</a>
|
||||
<button class="text-red-600 hover:underline flex items-center openDeleteModalBtn"
|
||||
data-id="{{ $pelanggan->id }}">
|
||||
<i class="bi bi-trash mr-1"></i> Hapus
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="p-2 text-center text-gray-500 border border-gray-300">
|
||||
Belum ada pelanggan
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Hapus Pelanggan -->
|
||||
@foreach ($pelanggans as $pelanggan)
|
||||
<div id="modalDelete{{ $pelanggan->id }}" class="fixed inset-0 hidden items-center justify-center z-50">
|
||||
<div class="absolute inset-0 bg-black opacity-50 closeDeleteOverlay" data-id="{{ $pelanggan->id }}"></div>
|
||||
<div class="bg-white rounded-lg shadow-lg w-full max-w-md z-50 overflow-hidden">
|
||||
<div class="p-4 border-b">
|
||||
<h5 class="text-lg font-medium">Hapus Pelanggan</h5>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<p>Apakah Anda yakin ingin menghapus pelanggan <strong>{{ $pelanggan->nama }}</strong>?</p>
|
||||
</div>
|
||||
<div class="p-4 border-t flex justify-end space-x-2">
|
||||
<button type="button" class="bg-gray-300 text-black px-3 py-1 rounded closeDeleteBtn"
|
||||
data-id="{{ $pelanggan->id }}">Batal</button>
|
||||
<form action="{{ route('admin.pelanggan.destroy', $pelanggan->id) }}" method="POST" class="inline">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="bg-red-600 text-white px-3 py-1 rounded">Hapus</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const openDeleteBtns = document.querySelectorAll('.openDeleteModalBtn');
|
||||
const closeDeleteBtns = document.querySelectorAll('.closeDeleteBtn');
|
||||
const closeDeleteOverlays = document.querySelectorAll('.closeDeleteOverlay');
|
||||
|
||||
openDeleteBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const id = btn.dataset.id;
|
||||
const modal = document.getElementById('modalDelete' + id);
|
||||
if (modal) {
|
||||
modal.classList.remove('hidden');
|
||||
modal.classList.add('flex');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
closeDeleteBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const id = btn.dataset.id;
|
||||
const modal = document.getElementById('modalDelete' + id);
|
||||
if (modal) {
|
||||
modal.classList.add('hidden');
|
||||
modal.classList.remove('flex');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
closeDeleteOverlays.forEach(overlay => {
|
||||
overlay.addEventListener('click', () => {
|
||||
const id = overlay.dataset.id;
|
||||
const modal = document.getElementById('modalDelete' + id);
|
||||
if (modal) {
|
||||
modal.classList.add('hidden');
|
||||
modal.classList.remove('flex');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
@ -1,119 +0,0 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Manajemen Ulasan')
|
||||
|
||||
@section('content')
|
||||
<div class="container mx-auto py-4">
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-xl font-bold">Manajemen Ulasan</h3>
|
||||
</div>
|
||||
|
||||
<!-- Alert sukses -->
|
||||
@if (session('success'))
|
||||
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded mb-4" role="alert">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Tabel Ulasan -->
|
||||
<div class="bg-white rounded-lg shadow-sm">
|
||||
<div class="p-4 overflow-x-auto">
|
||||
<table class="w-full table-fixed text-left border border-gray-300 border-collapse">
|
||||
<thead class="bg-gray-100">
|
||||
<tr>
|
||||
<th class="p-2 border border-gray-300 w-[50px] text-center">Nomor</th>
|
||||
<th class="p-2 border border-gray-300 w-[150px] text-center">Nama</th>
|
||||
<th class="p-2 border border-gray-300 w-[120px] text-center">Kota</th>
|
||||
<th class="p-2 border border-gray-300 w-[120px] text-center">Penilaian</th>
|
||||
<th class="p-2 border border-gray-300 w-[350px] text-center">Pesan</th>
|
||||
<th class="p-2 border border-gray-300 w-[120px] text-center">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse ($reviews as $key => $review)
|
||||
<tr>
|
||||
<td class="p-2 border border-gray-300 text-center truncate">{{ $key + 1 }}</td>
|
||||
<td class="p-2 border border-gray-300 truncate">{{ $review->name }}</td>
|
||||
<td class="p-2 border border-gray-300 truncate">{{ $review->city }}</td>
|
||||
<td class="p-2 border border-gray-300 text-center">
|
||||
@for ($s = 1; $s <= 5; $s++)
|
||||
<i
|
||||
class="bi {{ $s <= $review->rating ? 'bi-star-fill text-yellow-500' : 'bi-star text-gray-400' }}"></i>
|
||||
@endfor
|
||||
</td>
|
||||
<td class="p-2 border border-gray-300 truncate" title="{{ $review->message }}">
|
||||
{{ $review->message }}
|
||||
</td>
|
||||
<td class="p-2 border border-gray-300 text-center">
|
||||
<div class="flex justify-center space-x-2">
|
||||
<button class="text-red-600 hover:underline flex items-center btn-delete"
|
||||
data-action="{{ route('admin.reviews.destroy', $review->id) }}"
|
||||
data-name="{{ $review->name }}">
|
||||
<i class="bi bi-trash mr-1"></i> Hapus
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="p-2 text-center text-gray-500 border border-gray-300">
|
||||
Belum ada ulasan
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Konfirmasi Hapus Tailwind -->
|
||||
<div id="modalDelete" class="fixed inset-0 hidden items-center justify-center z-50">
|
||||
<div class="absolute inset-0 bg-black opacity-50" id="modalDeleteOverlay"></div>
|
||||
<div class="bg-white rounded-lg shadow-lg w-full max-w-md z-50 overflow-hidden">
|
||||
<form id="deleteForm" method="POST">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<div class="p-4 border-b">
|
||||
<h5 class="text-lg font-medium">Hapus Ulasan</h5>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
Apakah Anda yakin ingin menghapus ulasan dari <strong id="deleteName">—</strong>?
|
||||
</div>
|
||||
<div class="p-4 border-t flex justify-end space-x-2">
|
||||
<button type="button" id="closeDeleteModal"
|
||||
class="bg-gray-300 text-black px-3 py-1 rounded">Batal</button>
|
||||
<button type="submit" class="bg-red-600 text-white px-3 py-1 rounded">Ya, Hapus</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const modal = document.getElementById('modalDelete');
|
||||
const overlay = document.getElementById('modalDeleteOverlay');
|
||||
const closeBtn = document.getElementById('closeDeleteModal');
|
||||
const deleteForm = document.getElementById('deleteForm');
|
||||
const deleteName = document.getElementById('deleteName');
|
||||
|
||||
document.querySelectorAll('.btn-delete').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
deleteForm.action = btn.dataset.action;
|
||||
deleteName.textContent = btn.dataset.name || 'pengguna ini';
|
||||
modal.classList.remove('hidden');
|
||||
modal.classList.add('flex');
|
||||
});
|
||||
});
|
||||
|
||||
const closeModal = () => {
|
||||
modal.classList.add('hidden');
|
||||
modal.classList.remove('flex');
|
||||
}
|
||||
|
||||
closeBtn.addEventListener('click', closeModal);
|
||||
overlay.addEventListener('click', closeModal);
|
||||
});
|
||||
</script>
|
||||
@endsection
|
@ -1,279 +0,0 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Manajemen Template')
|
||||
|
||||
@section('content')
|
||||
<div class="container mx-auto py-4">
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-xl font-bold">
|
||||
@isset($kategori)
|
||||
Template Kategori: {{ $kategori->nama }}
|
||||
@else
|
||||
Semua Template
|
||||
@endisset
|
||||
</h3>
|
||||
|
||||
@if (!isset($kategori))
|
||||
<button id="openTambahModal" class="bg-blue-600 text-white px-3 py-2.5 rounded flex items-center">
|
||||
<i class="bi bi-plus-lg mr-1"></i> Tambah Template
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Flash Message -->
|
||||
@if (session('success'))
|
||||
<div id="toast-success" class="mb-4 p-3 rounded bg-green-100 text-green-800 border border-green-300 shadow">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
<script>setTimeout(() => document.getElementById('toast-success')?.remove(), 3000);</script>
|
||||
@endif
|
||||
|
||||
@if ($errors->any())
|
||||
<div id="toast-error" class="mb-4 p-3 rounded bg-red-100 text-red-800 border border-red-300 shadow">
|
||||
<ul class="list-disc ml-5">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
<script>setTimeout(() => document.getElementById('toast-error')?.remove(), 5000);</script>
|
||||
@endif
|
||||
|
||||
<!-- Tabel Template -->
|
||||
<div class="bg-white rounded-lg shadow-sm">
|
||||
<div class="p-4 overflow-x-auto">
|
||||
<table class="w-full table-fixed text-left border border-gray-300 border-collapse">
|
||||
<thead class="bg-gray-100">
|
||||
<tr>
|
||||
<th class="p-2 border border-gray-300 w-[50px] text-center">Nomor</th>
|
||||
<th class="p-2 border border-gray-300 w-[200px] text-center">Nama Template</th>
|
||||
<th class="p-2 border border-gray-300 w-[150px] text-center">Kategori</th>
|
||||
<th class="p-2 border border-gray-300 w-[150px] text-center">Fitur</th>
|
||||
<th class="p-2 border border-gray-300 w-[90px] text-center">Foto</th>
|
||||
<th class="p-2 border border-gray-300 w-[100px] text-center">Harga</th>
|
||||
<th class="p-2 border border-gray-300 w-[130px] text-center">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($templates as $key => $template)
|
||||
<tr>
|
||||
<td class="p-2 border border-gray-300 text-center">{{ $key + 1 }}</td>
|
||||
<td class="p-2 border border-gray-300 truncate">{{ $template->nama_template }}</td>
|
||||
<td class="p-2 border border-gray-300 truncate">{{ $template->kategori->nama ?? '-' }}</td>
|
||||
<td class="p-2 border border-gray-300 truncate">{{ $template->fitur->deskripsi ?? '-' }}</td>
|
||||
<td class="p-2 border border-gray-300 text-center">
|
||||
<div class="w-12 h-12 overflow-hidden rounded bg-gray-100 flex items-center justify-center mx-auto">
|
||||
<img src="{{ $template->foto ? asset('storage/' . $template->foto) : asset('default-image.png') }}"
|
||||
alt="foto" class="max-w-full max-h-full object-contain">
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-2 border border-gray-300 text-right font-semibold">
|
||||
Rp {{ number_format($template->harga, 0, ',', '.') }}
|
||||
</td>
|
||||
<td class="p-2 border border-gray-300 text-center">
|
||||
<div class="flex justify-center space-x-2">
|
||||
<button class="text-blue-600 hover:underline flex items-center openEditModalBtn"
|
||||
data-id="{{ $template->id }}">
|
||||
<i class="bi bi-pencil mr-1"></i> Edit
|
||||
</button>
|
||||
<form action="{{ route('templates.destroy', $template->id) }}" method="POST"
|
||||
class="inline" onsubmit="return confirm('Hapus template ini?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="text-red-600 hover:underline flex items-center">
|
||||
<i class="bi bi-trash mr-1"></i> Hapus
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="p-2 text-center text-gray-500 border border-gray-300">Belum ada template</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Tambah Template -->
|
||||
@if (!isset($kategori))
|
||||
<div id="modalTambah" class="fixed inset-0 hidden items-center justify-center z-50">
|
||||
<div class="absolute inset-0 bg-black opacity-50" id="closeTambahModal"></div>
|
||||
<div class="bg-white rounded-lg shadow-lg w-full max-w-md z-50 overflow-hidden">
|
||||
<form action="{{ route('templates.store') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
<div class="p-4 border-b">
|
||||
<h5 class="text-lg font-medium">Tambah Template</h5>
|
||||
</div>
|
||||
<div class="p-4 space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Nama Template</label>
|
||||
<input type="text" name="nama_template" class="w-full p-2 border rounded" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Kategori</label>
|
||||
<select name="kategori_id" class="w-full p-2 border rounded" required>
|
||||
@foreach ($kategoris as $kategori)
|
||||
<option value="{{ $kategori->id }}">{{ $kategori->nama }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Fitur</label>
|
||||
<select name="fitur_id" class="w-full p-2 border rounded" required>
|
||||
@foreach ($fiturs as $fitur)
|
||||
<option value="{{ $fitur->id }}">{{ $fitur->deskripsi }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Harga</label>
|
||||
<input type="number" name="harga" class="w-full p-2 border rounded" required min="0" step="1000">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Foto (opsional)</label>
|
||||
<input type="file" name="foto" class="w-full p-2 border rounded" accept="image/*">
|
||||
<small class="text-gray-500">Format yang didukung: JPG, PNG, GIF. Maksimal 5MB.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 border-t flex justify-end space-x-2">
|
||||
<button type="button" id="closeTambahBtn" class="bg-gray-300 text-black px-3 py-1 rounded">Batal</button>
|
||||
<button class="bg-blue-600 text-white px-3 py-1 rounded">Simpan</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Modal Edit Template -->
|
||||
@foreach ($templates as $template)
|
||||
<div id="modalEdit{{ $template->id }}" class="fixed inset-0 hidden items-center justify-center z-50">
|
||||
<div class="absolute inset-0 bg-black opacity-50 closeEditOverlay" data-id="{{ $template->id }}"></div>
|
||||
<div class="bg-white rounded-lg shadow-lg w-full max-w-md z-50 overflow-hidden">
|
||||
<form action="{{ route('templates.update', $template->id) }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf @method('PUT')
|
||||
<div class="p-4 border-b">
|
||||
<h5 class="text-lg font-medium">Edit Template</h5>
|
||||
</div>
|
||||
<div class="p-4 space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Nama Template</label>
|
||||
<input type="text" name="nama_template" value="{{ $template->nama_template }}"
|
||||
class="w-full p-2 border rounded" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Kategori</label>
|
||||
<select name="kategori_id" class="w-full p-2 border rounded" required>
|
||||
@foreach ($kategoris as $kategori)
|
||||
<option value="{{ $kategori->id }}" @selected($kategori->id == $template->kategori_id)>{{ $kategori->nama }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Fitur</label>
|
||||
<select name="fitur_id" class="w-full p-2 border rounded" required>
|
||||
@foreach ($fiturs as $fitur)
|
||||
<option value="{{ $fitur->id }}" @selected($fitur->id == $template->fitur_id)>{{ $fitur->deskripsi }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Harga</label>
|
||||
<input type="number" name="harga" value="{{ $template->harga }}"
|
||||
class="w-full p-2 border rounded" required min="0" step="1000">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium">Foto (opsional)</label>
|
||||
@if ($template->foto)
|
||||
<div class="mb-2">
|
||||
<small class="text-gray-500">Foto saat ini:</small>
|
||||
<div class="w-20 h-20 mt-1 overflow-hidden rounded bg-gray-100 flex items-center justify-center border">
|
||||
<img src="{{ asset('storage/' . $template->foto) }}" alt="foto"
|
||||
class="max-w-full max-h-full object-contain">
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<input type="file" name="foto" class="w-full p-2 border rounded" accept="image/*">
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 border-t flex justify-end space-x-2">
|
||||
<button type="button" class="bg-gray-300 text-black px-3 py-1 rounded closeEditBtn"
|
||||
data-id="{{ $template->id }}">Batal</button>
|
||||
<button class="bg-blue-600 text-white px-3 py-1 rounded">Simpan Perubahan</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Modal Tambah
|
||||
const openTambahModal = document.getElementById('openTambahModal');
|
||||
const modalTambah = document.getElementById('modalTambah');
|
||||
const closeTambahBtn = document.getElementById('closeTambahBtn');
|
||||
const closeTambahOverlay = document.getElementById('closeTambahModal');
|
||||
|
||||
if (openTambahModal) {
|
||||
openTambahModal.addEventListener('click', () => {
|
||||
modalTambah.classList.remove('hidden');
|
||||
modalTambah.classList.add('flex');
|
||||
});
|
||||
}
|
||||
if (closeTambahBtn) {
|
||||
closeTambahBtn.addEventListener('click', () => {
|
||||
modalTambah.classList.add('hidden');
|
||||
modalTambah.classList.remove('flex');
|
||||
});
|
||||
}
|
||||
if (closeTambahOverlay) {
|
||||
closeTambahOverlay.addEventListener('click', () => {
|
||||
modalTambah.classList.add('hidden');
|
||||
modalTambah.classList.remove('flex');
|
||||
});
|
||||
}
|
||||
|
||||
// Modal Edit
|
||||
const openEditBtns = document.querySelectorAll('.openEditModalBtn');
|
||||
const closeEditBtns = document.querySelectorAll('.closeEditBtn');
|
||||
const closeEditOverlays = document.querySelectorAll('.closeEditOverlay');
|
||||
|
||||
openEditBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const id = btn.dataset.id;
|
||||
const modal = document.getElementById('modalEdit' + id);
|
||||
if (modal) {
|
||||
modal.classList.remove('hidden');
|
||||
modal.classList.add('flex');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
closeEditBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const id = btn.dataset.id;
|
||||
const modal = document.getElementById('modalEdit' + id);
|
||||
if (modal) {
|
||||
modal.classList.add('hidden');
|
||||
modal.classList.remove('flex');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
closeEditOverlays.forEach(overlay => {
|
||||
overlay.addEventListener('click', () => {
|
||||
const id = overlay.dataset.id;
|
||||
const modal = document.getElementById('modalEdit' + id);
|
||||
if (modal) {
|
||||
modal.classList.add('hidden');
|
||||
modal.classList.remove('flex');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
@endsection
|
@ -1,149 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="id">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@yield('title', 'Admin Panel')</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css">
|
||||
<style>
|
||||
body {
|
||||
background-color: #f1f5f9;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
height: 100vh;
|
||||
background: #fff;
|
||||
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.05);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.submenu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.submenu.show {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="flex">
|
||||
<!-- Sidebar -->
|
||||
<div class="sidebar flex flex-col">
|
||||
<!-- LOGO -->
|
||||
<div class="text-center py-4 border-b">
|
||||
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="mx-auto mb-2" style="max-height: 80px;">
|
||||
</div>
|
||||
|
||||
<!-- MENU -->
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
<p class="text-gray-500 uppercase text-xs font-semibold px-3 mt-4 mb-2">Menu Utama</p>
|
||||
<ul class="px-2 space-y-1">
|
||||
<li>
|
||||
<a href="{{ route('admin.dashboard') }}"
|
||||
class="flex items-center py-2 px-3 rounded hover:bg-blue-50 {{ request()->is('admin/dashboard') ? 'bg-blue-100 text-blue-600' : 'text-gray-700' }}">
|
||||
<i class="bi bi-house-door me-2"></i> Dasbor
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('admin.kategori.index') }}"
|
||||
class="flex items-center py-2 px-3 rounded hover:bg-blue-50 {{ request()->is('admin/kategori*') ? 'bg-blue-100 text-blue-600' : 'text-gray-700' }}">
|
||||
<i class="bi bi-diagram-3 me-2"></i> Kategori
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('admin.fitur.index') }}"
|
||||
class="flex items-center py-2 px-3 rounded hover:bg-blue-50 {{ request()->is('admin/fitur*') ? 'bg-blue-100 text-blue-600' : 'text-gray-700' }}">
|
||||
<i class="bi bi-grid me-2"></i> Fitur
|
||||
</a>
|
||||
</li>
|
||||
<!-- Dropdown Templat -->
|
||||
<li>
|
||||
@php
|
||||
$isTemplatePage = request()->is('templates*'); // cek apakah sedang di halaman template
|
||||
@endphp
|
||||
<button
|
||||
class="w-full flex items-center justify-between py-2 px-3 text-gray-700 rounded hover:bg-blue-50 {{ $isTemplatePage ? 'bg-blue-100 text-blue-600' : '' }}"
|
||||
id="templatBtn">
|
||||
<span><i class="bi bi-card-list me-2"></i> Templat</span>
|
||||
<i class="bi bi-chevron-down transition-transform {{ $isTemplatePage ? 'rotate-180' : '' }}"
|
||||
id="templatIcon"></i>
|
||||
</button>
|
||||
<ul class="submenu pl-6 space-y-1 {{ $isTemplatePage ? 'show' : '' }}" id="templatSubmenu">
|
||||
<li>
|
||||
<a href="{{ route('templates.index') }}"
|
||||
class="block py-2 px-2 rounded {{ request()->is('templates') ? 'bg-blue-100 text-blue-600' : 'text-gray-500 hover:text-blue-600 hover:bg-blue-50' }}">
|
||||
Semua Template
|
||||
</a>
|
||||
</li>
|
||||
@foreach (\App\Models\Kategori::all() as $kategori)
|
||||
<li>
|
||||
<a href="{{ route('templates.byKategori', $kategori->id) }}"
|
||||
class="block py-2 px-2 rounded {{ request()->is('templates/kategori/' . $kategori->id) ? 'bg-blue-100 text-blue-600' : 'text-gray-500 hover:text-blue-600 hover:bg-blue-50' }}">
|
||||
{{ $kategori->nama }}
|
||||
</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('admin.pelanggan.index') }}"
|
||||
class="flex items-center py-2 px-3 rounded hover:bg-blue-50 text-gray-700">
|
||||
<i class="bi bi-people me-2"></i> Pelanggan
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="{{ route('admin.reviews.index') }}"
|
||||
class="flex items-center py-2 px-3 rounded hover:bg-blue-50 {{ request()->is('admin/ulasan') ? 'bg-blue-100 text-blue-600' : 'text-gray-700' }}">
|
||||
<i class="bi bi-chat-dots me-2"></i> Ulasan
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p class="text-gray-500 uppercase text-xs font-semibold px-3 mt-4 mb-2">Akun</p>
|
||||
<ul class="px-2 space-y-1">
|
||||
<li>
|
||||
<a href="{{ route('admin.logout') }}"
|
||||
onclick="event.preventDefault(); document.getElementById('logout-form').submit();"
|
||||
class="flex items-center py-2 px-3 text-red-600 font-semibold hover:bg-red-50 rounded">
|
||||
<i class="bi bi-box-arrow-right me-2"></i> Keluar
|
||||
</a>
|
||||
<form id="logout-form" action="{{ route('admin.logout') }}" method="POST" class="hidden">
|
||||
@csrf
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MAIN CONTENT -->
|
||||
<div class="flex-1 ml-[250px] p-6">
|
||||
@yield('content')
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Dropdown Templat
|
||||
const templatBtn = document.getElementById('templatBtn');
|
||||
const templatSubmenu = document.getElementById('templatSubmenu');
|
||||
const templatIcon = document.getElementById('templatIcon');
|
||||
|
||||
templatBtn.addEventListener('click', () => {
|
||||
templatSubmenu.classList.toggle('show');
|
||||
templatIcon.classList.toggle('rotate-180');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -2,26 +2,18 @@
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use App\Http\Controllers\Api\ReviewController;
|
||||
use App\Http\Controllers\Api\KategoriApiController;
|
||||
use App\Http\Controllers\Api\PernikahanApiController;
|
||||
use App\Http\Controllers\Api\UlangTahunApiController;
|
||||
use App\Http\Controllers\Api\KhitanApiController;
|
||||
use App\Http\Controllers\Api\TemplateApiController;
|
||||
|
||||
// Form API (user)
|
||||
Route::post('form/pernikahan', [PernikahanApiController::class, 'store']);
|
||||
Route::post('form/ulang-tahun', [UlangTahunApiController::class, 'store']);
|
||||
Route::post('form/khitan', [KhitanApiController::class, 'store']);
|
||||
|
||||
// API Kategori hanya read-only
|
||||
Route::get('kategoris', [KategoriApiController::class, 'index']);
|
||||
Route::get('kategoris/{kategori}', [KategoriApiController::class, 'show']);
|
||||
|
||||
// API Reviews
|
||||
Route::apiResource('reviews', ReviewController::class);
|
||||
|
||||
// API Templates
|
||||
Route::get('templates', [TemplateApiController::class, 'index']);
|
||||
Route::get('templates/{template}', [TemplateApiController::class, 'show']);
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| API Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you can register API routes for your application. These
|
||||
| routes are loaded by the RouteServiceProvider and all of them will
|
||||
| be assigned to the "api" middleware group. Make something great!
|
||||
|
|
||||
*/
|
||||
|
||||
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
|
||||
return $request->user();
|
||||
});
|
||||
|
@ -1,83 +1,18 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use App\Http\Controllers\AdminAuthController;
|
||||
use App\Http\Controllers\KategoriController;
|
||||
use App\Http\Controllers\FiturController;
|
||||
use App\Http\Controllers\TemplateController;
|
||||
use App\Http\Controllers\PelangganController;
|
||||
|
||||
//Login
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Web Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you can register web routes for your application. These
|
||||
| routes are loaded by the RouteServiceProvider and all of them will
|
||||
| be assigned to the "web" middleware group. Make something great!
|
||||
|
|
||||
*/
|
||||
|
||||
Route::get('/', function () {
|
||||
return redirect()->route('admin.login');
|
||||
});
|
||||
Route::prefix('admin')->name('admin.')->group(function () {
|
||||
Route::middleware('guest:admin')->group(function () {
|
||||
Route::get('/login', [AdminAuthController::class, 'showLogin'])->name('login');
|
||||
Route::post('/login', [AdminAuthController::class, 'login'])->name('login.post');
|
||||
});
|
||||
|
||||
Route::middleware('auth:admin')->group(function () {
|
||||
Route::get('/dashboard', function () {
|
||||
return view('admin.dashboard');
|
||||
})->name('dashboard');
|
||||
|
||||
Route::post('/logout', [AdminAuthController::class, 'logout'])->name('logout');
|
||||
});
|
||||
});
|
||||
|
||||
//Kategori
|
||||
Route::prefix('admin')->name('admin.')->group(function () {
|
||||
Route::resource('kategori', KategoriController::class);
|
||||
});
|
||||
|
||||
// Route Admin Fitur
|
||||
Route::prefix('admin')->name('admin.')->group(function () {
|
||||
Route::resource('fitur', FiturController::class);
|
||||
});
|
||||
|
||||
|
||||
// Template
|
||||
Route::prefix('admin')->group(function () {
|
||||
Route::get('templates', [TemplateController::class, 'index'])->name('templates.index');
|
||||
Route::post('templates', [TemplateController::class, 'store'])->name('templates.store');
|
||||
Route::put('templates/{template}', [TemplateController::class, 'update'])->name('templates.update');
|
||||
Route::delete('templates/{template}', [TemplateController::class, 'destroy'])->name('templates.destroy');
|
||||
Route::get('templates/kategori/{id}', [TemplateController::class, 'byKategori'])->name('templates.byKategori');
|
||||
});
|
||||
|
||||
|
||||
// Route Admin Pelanggan
|
||||
Route::prefix('admin')->name('admin.')->group(function () {
|
||||
Route::resource('pelanggan', PelangganController::class)->only([
|
||||
'index',
|
||||
'show',
|
||||
'destroy'
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
use App\Http\Controllers\Api\ReviewController;
|
||||
use App\Models\Review;
|
||||
|
||||
Route::prefix('admin')->name('admin.')->middleware('auth:admin')->group(function () {
|
||||
// Halaman daftar ulasan
|
||||
Route::get('/ulasan', function () {
|
||||
$reviews = \App\Models\Review::latest()->get();
|
||||
return view('admin.reviews.index', compact('reviews'));
|
||||
})->name('reviews.index');
|
||||
|
||||
// Tambah ulasan
|
||||
Route::post('/ulasan', [ReviewController::class, 'store'])->name('reviews.store');
|
||||
|
||||
// Update ulasan
|
||||
Route::put('/ulasan/{review}', [ReviewController::class, 'update'])->name('reviews.update');
|
||||
|
||||
// Hapus ulasan
|
||||
Route::delete('/ulasan/{review}', function (Review $review) {
|
||||
$review->delete();
|
||||
return redirect()->route('admin.reviews.index')->with('success', 'Ulasan berhasil dihapus');
|
||||
})->name('reviews.destroy');
|
||||
return view('welcome');
|
||||
});
|
||||
|
@ -7,7 +7,7 @@
|
||||
<!-- Layout gambar + teks -->
|
||||
<div class="about-layout">
|
||||
<div class="about-image">
|
||||
<img src="/logo1.png" alt="Tentang Kami - Undangan Digital" />
|
||||
<img src="/Rectangle.png" alt="Tentang Kami - Undangan Digital" />
|
||||
</div>
|
||||
<div class="about-text">
|
||||
<p>
|
||||
|
@ -30,7 +30,7 @@
|
||||
</div>
|
||||
|
||||
<div class="see-more">
|
||||
<NuxtLink to="/template">Lihat Selengkapnya...</NuxtLink>
|
||||
<a href="#">Lihat Selengkapnya...</a>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
@ -2,13 +2,12 @@
|
||||
<header class="main-header">
|
||||
<nav class="container">
|
||||
<div class="logo">
|
||||
<NuxtLink to="/" class="logo-link"> <img src="/ABBAUF.png" alt="Abbauf Tech Logo" class="logo-icon">
|
||||
<span>ABBAUF TECH</span>
|
||||
</NuxtLink>
|
||||
<img src="/abbauflogo.png" alt="Abbauf Tech Logo" class="logo-icon">
|
||||
<span>ABBAUF TECH</span>
|
||||
</div>
|
||||
<ul class="nav-links">
|
||||
<li v-for="link in navLinks" :key="link.name">
|
||||
<NuxtLink :to="link.path">{{ link.name }}</NuxtLink>
|
||||
<a :href="link.path">{{ link.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
@ -19,15 +18,12 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
const navLinks = ref([
|
||||
// Path untuk Beranda diubah menjadi / agar selalu kembali ke halaman utama
|
||||
{ name: 'Beranda', path: '/' },
|
||||
// Path ini agar berfungsi dari halaman lain (seperti /template)
|
||||
{ name: 'Tentang Kami', path: '/#tentang-kami' },
|
||||
// INI BAGIAN YANG PALING PENTING: Path diubah ke /template
|
||||
{ name: 'Templat', path: '/template' },
|
||||
{ name: 'Panduan', path: '/#cara' },
|
||||
{ name: 'Keistimewaan', path: '/#keistimewaan' },
|
||||
{ name: 'Testimoni', path: '/#testi' },
|
||||
{ name: 'Beranda', path: '#beranda' },
|
||||
{ name: 'Tentang Kami', path: '#tentang-kami' },
|
||||
{ name: 'Templat', path: '#template' },
|
||||
{ name: 'Panduan', path: '#cara' },
|
||||
{ name: 'Keistimewaan', path: '#keistimewaan' },
|
||||
{ name: 'Testimoni', path: '#testimoni' },
|
||||
]);
|
||||
</script>
|
||||
|
||||
@ -48,17 +44,26 @@ const navLinks = ref([
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo a.logo-link { /* Style untuk logo link */
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
font-size: 1.5rem;
|
||||
color: #0d6efd;
|
||||
text-decoration: none; /* Hilangkan garis bawah default */
|
||||
}
|
||||
|
||||
|
||||
|
||||
.main-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.logo-icon {
|
||||
width: 40px;
|
||||
width: 55px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<div class="w-full text-center lg:w-1/2">
|
||||
|
||||
<h1 class="text-5xl font-extrabold leading-tight text-gray-800 lg:text-6xl">
|
||||
<h1 class="text-5xl font-extrabold leading-tight text-gray-800 lg:text-6xl ml-14">
|
||||
Buat Undangan Digital Praktis Untuk
|
||||
|
||||
<div class="h-24 flex items-center justify-center">
|
||||
@ -16,10 +16,10 @@
|
||||
Tanpa Ribet
|
||||
</h1>
|
||||
|
||||
<p class="mt-4 mb-8 text-lg text-gray-600">
|
||||
<p class="mt-4 mb-8 text-lg text-gray-600 ml-14">
|
||||
Coba undangan digital PRAKTIS untuk berbagai acara. Pilih template praktis atau premium sesuai kebutuhanmu. Praktis, cepat, dan bisa langsung digunakan.
|
||||
</p>
|
||||
<div class="flex flex-col justify-center gap-4 sm:flex-row">
|
||||
<div class="flex flex-col justify-center gap-4 sm:flex-row ml-14">
|
||||
<a href="#" class="inline-flex items-center justify-center rounded-lg border-2 border-green-500 bg-white px-8 py-3 font-bold text-gray-800 shadow-sm transition-transform duration-300 hover:-translate-y-1 hover:shadow-md">
|
||||
<svg class="mr-2" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.2239 4.7761C17.1659 2.7181 14.6599 1.5 11.9999 1.5...Z" fill="#25D366"/>
|
||||
@ -34,7 +34,7 @@
|
||||
</div>
|
||||
|
||||
<div class="w-full lg:w-1/2">
|
||||
<img src="/logo2.png" alt="Tampilan Undangan Digital di Ponsel" class="mx-auto max-w-full">
|
||||
<img src="/iphone.png" alt="Tampilan Undangan Digital di Ponsel" class="mx-auto max-w-full">
|
||||
<div class="mt-6 text-center italic text-gray-500">
|
||||
<p>"Abadikan momen indahmu dengan undangan digital yang elegan."</p>
|
||||
<p>"Satu aplikasi untuk semua momen spesialmu."</p>
|
||||
|
@ -1,58 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="mb-8">
|
||||
<NuxtLink
|
||||
to="/"
|
||||
class="text-blue-600 hover:text-blue-800 font-semibold inline-flex items-center"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
Kembali ke Beranda
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<h1 class="text-3xl md:text-4xl font-bold text-center text-gray-800">
|
||||
Pilih Template Favoritmu
|
||||
</h1>
|
||||
<p class="mt-2 text-center text-gray-500">
|
||||
"Tersedia berbagai desain undangan pernikahan, khitan, ulang tahun, dan lainnya."
|
||||
</p>
|
||||
|
||||
<div class="mt-12 grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<div
|
||||
v-for="category in categories"
|
||||
:key="category.id"
|
||||
@click="onCategoryClick(category.nama)"
|
||||
class="group cursor-pointer overflow-hidden rounded-lg shadow-lg hover:shadow-2xl transition-all duration-300"
|
||||
>
|
||||
<img :src="category.foto" :alt="category.nama" class="w-full h-48 object-cover group-hover:scale-110 transition-transform duration-300">
|
||||
<div class="p-6 bg-white">
|
||||
<h3 class="text-xl font-semibold text-gray-800">{{ category.nama }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const emit = defineEmits(['category-selected']);
|
||||
|
||||
const categories = ref([])
|
||||
|
||||
// Ambil data kategori dari Laravel API pakai $fetch
|
||||
const fetchCategories = async () => {
|
||||
try {
|
||||
categories.value = await $fetch('http://localhost:8000/api/kategoris')
|
||||
} catch (error) {
|
||||
console.error('Gagal ambil kategori:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const onCategoryClick = (categoryName) => {
|
||||
emit('category-selected', categoryName);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchCategories()
|
||||
})
|
||||
</script>
|
@ -1,49 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex items-center mb-8">
|
||||
<button @click="$emit('back')" class="text-blue-600 hover:text-blue-800 font-semibold mr-4">
|
||||
← Kembali ke Kategori
|
||||
</button>
|
||||
<h1 class="text-3xl md:text-4xl font-bold text-gray-800">
|
||||
Pilih Template {{ category }} Favoritmu
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div v-for="tpl in templates" :key="tpl.id" class="border rounded-lg p-4 text-center">
|
||||
<img :src="tpl.foto" :alt="tpl.nama" class="bg-gray-200 h-40 mb-4 rounded-md object-cover w-full" />
|
||||
<h4 class="font-semibold">{{ tpl.nama }}</h4>
|
||||
<p class="text-gray-500">Rp {{ tpl.harga }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
category: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
id_category: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const templates = ref([])
|
||||
|
||||
const fetchTemplates = async () => {
|
||||
try {
|
||||
templates.value = await $fetch(`http://localhost:8000/api/templates/category/${props.id_category}`)
|
||||
} catch (error) {
|
||||
console.error('Gagal ambil template:', error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchTemplates()
|
||||
})
|
||||
|
||||
defineEmits(['back']);
|
||||
</script>
|
@ -1,31 +0,0 @@
|
||||
<template>
|
||||
<div class="container mx-auto px-4 py-16">
|
||||
<TemplatePageCategorySelection
|
||||
v-if="!selectedCategory"
|
||||
@category-selected="handleCategorySelect"
|
||||
/>
|
||||
|
||||
<TemplatePageTemplateGrid
|
||||
v-else
|
||||
:category="selectedCategory"
|
||||
@back="goBack"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
// State untuk menyimpan kategori yang sedang dipilih
|
||||
const selectedCategory = ref(null);
|
||||
|
||||
// Fungsi ini akan menangkap event dari CategorySelection
|
||||
const handleCategorySelect = (categoryName) => {
|
||||
selectedCategory.value = categoryName;
|
||||
};
|
||||
|
||||
// Fungsi ini akan menangkap event 'back' dari TemplateGrid
|
||||
const goBack = () => {
|
||||
selectedCategory.value = null;
|
||||
};
|
||||
</script>
|
BIN
proyek-frontend/public/Rectangle.png
Normal file
BIN
proyek-frontend/public/Rectangle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 228 KiB |
BIN
proyek-frontend/public/abbauflogo.png
Normal file
BIN
proyek-frontend/public/abbauflogo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
proyek-frontend/public/iphone.png
Normal file
BIN
proyek-frontend/public/iphone.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 233 KiB |
Loading…
Reference in New Issue
Block a user