1. di create_user_table di Bawah $table->string('email')->unique(); ketik
$table->string('role')->default('cashier');
2. di databaseseeder di Bawah 'email' => '[email protected]', ketik
'password' => bcrypt('password'),
'role' => 'admin'
btw buat juga untuk cashier
3. lalu fresh --seed dlu
4. buat folder admin lalu copy dashboard.blade.php ke dlm folder admin
5. di authenticatedsessioncontroller blog yang ada home itu lalu paste ini
if (auth()->user()->role == 'admin') {
return redirect('/admin');
} else {
return redirect('/dashboard');
}
6. di web.php di Bawah ('dashboard'); ketik
Route::get('/admin', function () {
return view('admin.dashboard');
})->middleware('auth');
7. skrg buat model Product dan stock lalu ke file Product.php ketik
protected $fillable = [
'name',
'size',
'price',
'quantity',
'description',
];
di file create_product di Bawah $table->id ketik
$table->string('name');
$table->string('size')->nullable();
$table->integer('price');
$table->integer('quantity');
$table->text('description')->nullable();
lalu di create_stock ketik
$table->foreignId('product_id')->constrained()->onDelete('cascade');
$table->integer('quantity');
8. skrg buat tombol copy kode ini di atas table product
<div class="mb-4">
<a href="{{ route('admin.product.create') }}"
class="bg-blue-500 text-white px-4 py-2 rounded">
+ Product
</a>
</div>
9. skrg ketik di terminal php artisan make:controller AdminController dan isi nya
use App\Models\Product;
use Illuminate\Http\Request;
public function create()
{
return view('admin._card_create_product');
}
public function store(Request $request)
{
$request->validate([
'name' => 'required',
'size' => 'required',
'price' => 'required|numeric',
'quantity' => 'required|numeric',
'description' => 'required',
]);
Product::create($request->all());
return redirect('/admin')->with('success', 'Product berhasil ditambahkan!');
}
public function index()
{
$products = Product::all(); // ambil semua data
return view('admin.dashboard', compact('products'));
}
=========================================================================================================================================
setelah itu buat rute di web.php
Route::middleware('auth')->group(function () {
Route::get('/admin/product/create', [AdminController::class, 'create'])->name('admin.product.create');
Route::post('/admin/product/store', [AdminController::class, 'store'])->name('admin.product.store');
});
10. setelah itu buat file baru di folder admin Bernama _card_create_product.blade.php dan isi nya
<x-app-layout>
<div class="max-w-4xl mx-auto mt-10">
<div class="bg-white shadow-lg rounded-xl p-6">
<h2 class="text-2xl font-bold mb-6">Tambah Product</h2>
<form action="{{ route('admin.product.store') }}" method="POST">
@csrf
<!-- NAME & SIZE -->
<div class="grid grid-cols-2 gap-4 mb-4">
<div>
<label class="block mb-1 text-sm font-medium">Nama</label>
<input type="text" name="name"
class="w-full border rounded-lg p-2 focus:ring-2 focus:ring-blue-400 outline-none">
@error('name')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
<div>
<label class="block mb-1 text-sm font-medium">Size</label>
<select name="size"
class="w-full border rounded-lg p-2 focus:ring-2 focus:ring-blue-400 outline-none">
<option value="">-- pilih size --</option>
<option value="S">Small</option>
<option value="M">Medium</option>
<option value="XL">XL</option>
</select>
@error('size')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
</div>
<!-- PRICE & QUANTITY -->
<div class="grid grid-cols-2 gap-4 mb-4">
<div>
<label class="block mb-1 text-sm font-medium">Price</label>
<input type="number" name="price"
class="w-full border rounded-lg p-2 focus:ring-2 focus:ring-blue-400 outline-none">
@error('price')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
<div>
<label class="block mb-1 text-sm font-medium">Quantity</label>
<select name="quantity"
class="w-full border rounded-lg p-2 focus:ring-2 focus:ring-blue-400 outline-none">
<option value="">-- pilih jumlah --</option>
@for ($i = 1; $i <= 10; $i++)
<option value="{{ $i }}">{{ $i }}</option>
@endfor
</select>
@error('quantity')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
</div>
<!-- DESCRIPTION -->
<div class="mb-4">
<label class="block mb-1 text-sm font-medium">Description</label>
<textarea name="description" rows="4"
class="w-full border rounded-lg p-2 focus:ring-2 focus:ring-blue-400 outline-none"></textarea>
@error('description')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
<!-- BUTTON -->
<div class="flex justify-between mt-6">
<div class="flex gap-3">
<button type="submit"
class="bg-blue-500 hover:bg-blue-600 text-white px-5 py-2 rounded-lg shadow">
Kirim
</button>
<button type="button" onclick="confirmBack()"
class="bg-gray-400 hover:bg-gray-500 text-white px-5 py-2 rounded-lg">
Kembali
</button>
</div>
</div>
</form>
</div>
</div>
<script>
function confirmBack() {
if (confirm("Yakin mau kembali?")) {
window.location.href = "/admin";
}
}
</script>
</x-app-layout>
11. di admincontroller tambahkan fungsi
public function index()
{
$products = Product::all(); // ambil semua data
return view('admin.dashboard', compact('products'));
}
setelah itu tambahkan rute di web.php
Route::get('/admin', [AdminController::class, 'index'])->middleware('auth');
12. lalu di folder admin file dashboard blog bagian tbody lalu paste ini
<tbody>
@forelse ($products as $product)
<tr class="bg-neutral-primary-soft border-b border-default hover:bg-neutral-secondary-medium">
<td class="px-6 py-4">
{{ $product->name }}
</td>
<td class="px-6 py-4">
{{ $product->size }}
</td>
<td class="px-6 py-4">
{{ $product->price }}
</td>
<td class="px-6 py-4">
<a href="{{ route('admin.product.edit', $product->id) }}"
class="text-blue-500 underline">
{{ $product->quantity }}
</a>
</td>
<td class="px-6 py-4 text-right">
<div class="flex gap-2 justify-end">
<form action="{{ route('admin.product.delete', $product->id) }}"
method="POST"
onsubmit="return confirm('Yakin mau hapus produk ini?')">
@csrf
@method('DELETE')
<button type="submit" class="text-red-500">
Hapus
</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="text-center py-4">
Belum ada produk
</td>
</tr>
@endforelse
</tbody>
13. di Bawah tombol +produk tambahkan ini
{{-- ALERT --}}
@if(session('success'))
<div id="alert-success" class="bg-green-400 text-white px-4 py-3 rounded-lg mb-4">
{{ session('success') }}
</div>
@endif
@if($products->isEmpty())
<div class="bg-blue-400 text-white px-4 py-3 rounded-lg mb-4">
Belum ada produk
</div>
@endif
lalu tambahkan fungsi ini biar bisa hilang alert hijau
<script>
setTimeout(() => {
let alert = document.getElementById('alert-success');
if(alert){
alert.style.transition = "opacity 0.5s";
alert.style.opacity = "0";
setTimeout(() => alert.remove(), 500);
}
}, 3000); // 3 detik
</script>
14. di folder admin file dashboard cari <a href="#" class="text-blue-500">Edit</a> lalu blog ganti jadi
<div class="flex gap-2 justify-end">
{{-- DELETE --}}
<form action="{{ route('admin.product.delete', $product->id) }}" method="POST"
onsubmit="return confirm('Yakin mau hapus produk ini?')">
@csrf
@method('DELETE')
<button type="submit" class="text-red-500">
Hapus
</button>
</form>
</div>
setelah itu buat rute di web.php
Route::delete('/admin/product/{id}', [AdminController::class, 'delete'])
->name('admin.product.delete');
lalu di admin controller tambahkan ini
public function delete($id)
{
$product = Product::findOrFail($id);
$product->delete();
return redirect('/admin')->with('success', 'Produk berhasil dihapus!');
}
15. di folder admin file dashboard ganti {{ $product->quantity }} dari td sampai penutup nya jadi ini
<td class="px-6 py-4">
<a href="{{ route('admin.product.edit', $product->id) }}"
class="text-blue-500 underline">
{{ $product->quantity }}
</a>
</td>
16. tambahkan rute di web.php
Route::get('/admin/product/edit/{id}', [AdminController::class, 'edit'])
->name('admin.product.edit');
Route::post('/admin/product/update/{id}', [AdminController::class, 'update'])
->name('admin.product.update');
17. tambahkan fungsi di admincontroller
public function edit($id)
{
$product = Product::findOrFail($id);
return view('admin._card_edit_product', compact('product'));
}
public function update(Request $request, $id)
{
$request->validate([
'quantity' => 'required|numeric'
]);
$product = Product::findOrFail($id);
$product->update([
'quantity' => $request->quantity
]);
return redirect('/admin')->with('success', 'Quantity berhasil diupdate!');
}
18. buat file Bernama _card_edit_product.blade.php di folder admin
<x-app-layout>
<div class="max-w-4xl mx-auto mt-10">
<div class="bg-white shadow-lg rounded-xl p-6">
<h2 class="text-2xl font-bold mb-6">Edit Product</h2>
<form action="{{ route('admin.product.update', $product->id) }}" method="POST">
@csrf
<!-- NAME & SIZE -->
<div class="grid grid-cols-2 gap-4 mb-4">
<div>
<label class="block mb-2 text-sm font-medium">Nama</label>
<input type="text" value="{{ $product->name }}"
class="w-full border rounded-lg p-2 bg-gray-100" readonly>
</div>
<div>
<label class="block mb-2 text-sm font-medium">Size</label>
<input type="text" value="{{ $product->size }}"
class="w-full border rounded-lg p-2 bg-gray-100" readonly>
</div>
</div>
<!-- PRICE & QUANTITY -->
<div class="grid grid-cols-2 gap-4 mb-4">
<div>
<label class="block mb-1 text-sm font-medium">Price</label>
<input type="number" value="{{ $product->price }}"
class="w-full border rounded-lg p-2 bg-gray-100" readonly>
</div>
<div>
<label class="block mb-1 text-sm font-medium">Quantity</label>
<select name="quantity"
class="w-full border rounded-lg p-2 focus:ring-2 focus:ring-blue-400 outline-none">
@for ($i = 1; $i <= 10; $i++)
<option value="{{ $i }}"
{{ $product->quantity == $i ? 'selected' : '' }}>
{{ $i }}
</option>
@endfor
</select>
@error('quantity')
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror
</div>
</div>
<!-- DESCRIPTION -->
<div class="mb-4">
<label class="block mb-1 text-sm font-medium">Description</label>
<textarea rows="4"
class="w-full border rounded-lg p-2 bg-gray-100" readonly>{{ $product->description }}</textarea>
</div>
<!-- BUTTON -->
<div class="flex justify-between mt-6">
<div class="flex gap-3">
<button type="submit"
class="bg-blue-500 hover:bg-blue-600 text-white px-5 py-2 rounded-lg shadow">
Update
</button>
<button type="button" onclick="confirmBack()"
class="bg-gray-400 hover:bg-gray-500 text-white px-5 py-2 rounded-lg">
Kembali
</button>
</div>
</div>
</form>
</div>
</div>
<script>
function confirmBack() {
if (confirm("Yakin mau kembali?")) {
window.location.href = "/admin";
}
}
</script>
</x-app-layout>
19. ketik di terminal php artisan make:controller CashierController di dalam file nya isi
<?php
namespace App\Http\Controllers;
use App\Models\Product;
class CashierController extends Controller
{
public function index()
{
$products = Product::all();
return view('cashier.index', compact('products'));
}
}
lalu perbaiki rute di web.php
<?php
use App\Http\Controllers\AdminController;
use App\Http\Controllers\CashierController;
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
Route::get('/dashboard', function () {
if (auth()->user()->role == 'admin') {
return redirect('/admin');
} elseif (auth()->user()->role == 'cashier') {
return redirect('/cashier');
}
return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
// ADMIN (hapus yang function!)
Route::get('/admin', [AdminController::class, 'index'])
->middleware('auth');
// CRUD PRODUCT
Route::middleware('auth')->group(function () {
Route::get('/admin/product/create', [AdminController::class, 'create'])->name('admin.product.create');
Route::post('/admin/product/store', [AdminController::class, 'store'])->name('admin.product.store');
Route::get('/admin/product/edit/{id}', [AdminController::class, 'edit'])->name('admin.product.edit');
Route::post('/admin/product/update/{id}', [AdminController::class, 'update'])->name('admin.product.update');
Route::delete('/admin/product/{id}', [AdminController::class, 'delete'])->name('admin.product.delete');
Route::post('/checkout', [App\Http\Controllers\AdminController::class, 'checkout']);
});
Route::middleware('auth')->group(function () {
Route::get('/cashier', [CashierController::class, 'index'])->name('cashier.index');
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});
require __DIR__.'/auth.php';
20. ganti fungsi di authenticatedsession lama menajadi
public function store(LoginRequest $request)
{
$request->authenticate();
$request->session()->regenerate();
if (auth()->user()->role == 'admin') {
return redirect('/admin');
} elseif (auth()->user()->role == 'cashier') {
return redirect('/cashier');
}
return redirect('/dashboard');
}
21. lalu buat file baru Bernama index.blade.php di folder cashier
<x-app-layout>
<div class="py-12"
{{-- membuat state komponent alpine --}}
x-data="stateListProduct({{ Js::from($products) }})">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900 dark:text-gray-100">
{{-- {{ __("CASHIER DASHBOARD") }} --}}
{{-- <div class="max-w-7xl mx-auto p-2 lg:p-4">
<div class="mt-2"> --}}
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 lg:gap-8 item-start">
{{-- start card list product --}}
<div class="scale-100 p-6 bg-white dark:bg-gray-800/50 dark:bg-gradient-to-bl from-gray-700/50 via-transparent dark:ring-1 dark:ring-inset dark:ring-white/5 rounded-lg shadow-2xl shadow-gray-500/20 dark:shadow-none flex duration-250 focus:outline focus:outline-2 focus:outline-red-500 w-full">
<div class="grid grid-cols-2 gap-2 w-full">
{{-- @for ($i = 0; $i < 6; $i++) --}}
<template x-for="product in listProduct" :key="product.id">
<div class="motion-safe:hover:scale-[1.01] transition-transform w-full max-w-sm bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700">
<a href="#">
<img class="p-2 rounded-xl" src="https://flowbite.s3.amazonaws.com/docs/gallery/square/image.jpg" alt="product image" />
</a>
<div class="px-5 pb-5">⚠️Content was pasted as plain text and auto-formatted as a code block. Use the Code Block button in the editor for proper formatting.