Membuat Cart menggunakan laravel inertia

Galih Setiawan Nurohim
5 min readNov 26, 2024

--

Jika sebelumnya membuat cart pakai next js dan supabase sekarang mau bikin cart pakai laravel inertia

kasir Sederhana

Langkah 1: Membuat Proyek Laravel

Buat proyek Laravel baru:

composer create-project laravel/laravel laravel-inertia-auth
cd laravel-inertia-auth

Langkah 2: Install Laravel Breeze

Laravel Breeze menyediakan scaffolding autentikasi yang ringan dan mendukung Inertia.js dengan React.

a. Install Breeze

composer require laravel/breeze --dev
php artisan breeze:install react
npm install
npm run dev

b. Jalankan Migrasi

Laravel Breeze menyertakan migrasi bawaan untuk tabel users. Jalankan migrasi:

php artisan migrate

Step 3: Install and Configure Tailwind CSS

a. Install Tailwind CSS

Pasang Tailwind CSS:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init

b. Configure Tailwind

Update the tailwind.config.js file:

module.exports = {
content: [
'./resources/**/*.blade.php',
'./resources/**/*.js',
'./resources/**/*.jsx',
],
theme: {
extend: {},
},
plugins: [],
};

Add Tailwind directives to resources/css/app.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

Compile assets:

npm run dev

Step 4: Configure Authentication

Laravel Breeze provides default authentication routes, which include:

  • /login
  • /register
  • /forgot-password
  • /reset-password
  • /dashboard

You can view these routes in routes/web.php. Breeze also creates React components in resources/js/Pages/Auth.

Step 5: Add Middleware for Protected Routes

To restrict access to authenticated users, use the auth middleware. Update routes/web.php:

use App\Http\Controllers\BarangController;

Route::middleware(['auth'])->group(function () {
Route::get('/cart', [BarangController::class, 'index'])->name('cart');
Route::get('/dashboard', function () {
return Inertia::render('Dashboard');
})->name('dashboard');
});

Step 6: Set Up Database and Models

a. Configure Database

Update the .env file with your database connection details:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your_database
DB_USERNAME=your_username
DB_PASSWORD=your_password

b. Create Migrations

Create tables for barang and penjualan:

php artisan make:migration create_barang_table
php artisan make:migration create_penjualan_table

Define the migrations:

create_barang_table:

Schema::create('barang', function (Blueprint $table) {
$table->id();
$table->string('barang');
$table->string('kode_barang');
$table->integer('stok');
$table->decimal('harga_jual', 10, 2);
$table->timestamps();
});

create_penjualan_table:

Schema::create('penjualan', function (Blueprint $table) {
$table->id();
$table->string('nama_pelanggan');
$table->decimal('total_harga', 10, 2);
$table->timestamp('tanggal');
$table->timestamps();
});

Run the migrations:

php artisan migrate

Buat Seeder untuk Tabel Barang

php artisan make:seeder BarangSeeder

Seeder ini akan dibuat di folder database/seeders/BarangSeeder.php.

Edit file database/seeders/BarangSeeder.php dengan script berikut untuk mengisi data barang secara random:

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class BarangSeeder extends Seeder
{
public function run()
{
$barangNames = [
'Laptop ASUS ROG', 'Smartphone Samsung Galaxy', 'Kulkas Sharp',
'TV LG OLED', 'Kipas Angin Panasonic', 'Headset Logitech',
'Keyboard Mechanical Razer', 'Monitor Dell UltraSharp', 'Printer HP LaserJet',
'Smartwatch Apple', 'Tablet Lenovo', 'Mouse Gaming SteelSeries',
'Speaker JBL', 'Mesin Cuci Samsung', 'AC Daikin'
];

foreach ($barangNames as $name) {
DB::table('barang')->insert([
'barang' => $name,
'kode_barang' => strtoupper(substr(md5($name), 0, 8)), // Generate random kode_barang
'stok' => rand(10, 100), // Random stock between 10-100
'harga_jual' => rand(50000, 2000000), // Random price between 50,000 - 2,000,000
'created_at' => now(),
'updated_at' => now(),
]);
}
}
}

Jalankan seeder untuk mengisi data ke tabel barang:

php artisan db:seed --class=BarangSeeder

Step 7: Backend Logic

a. Create Models

Create models for Barang and Penjualan:

php artisan make:model Barang
php artisan make:model Penjualan

Define relationships in the models if necessary.

b. Create a Controller

Generate a controller to handle cart-related logic:

php artisan make:controller BarangController

Define methods in BarangController:

namespace App\Http\Controllers;

use App\Models\Barang;
use Illuminate\Http\Request;
use Inertia\Inertia;

class BarangController extends Controller
{
public function index()
{
$barangList = Barang::orderBy('barang')->get();
return Inertia::render('CartPage', ['barangList' => $barangList]);
}

public function store(Request $request)
{
$request->validate([
'barang' => 'required|string',
'kode_barang' => 'required|string|unique:barang',
'stok' => 'required|integer|min:0',
'harga_jual' => 'required|numeric|min:0',
]);

Barang::create($request->all());

return redirect()->route('cart')->with('success', 'Barang added successfully');
}
}

Step 8: Frontend — Cart Component

a. Create a Cart Page

Create a CartPage.jsx file in resources/js/Pages:

import React, { useState, useEffect } from "react";

export default function CartPage({ barangList }) {
const [cartItems, setCartItems] = useState([]);
const [availableStock, setAvailableStock] = useState({});
const [totalHarga, setTotalHarga] = useState(0);

// Initialize available stock based on barangList
useEffect(() => {
const stock = {};
barangList.forEach((barang) => {
stock[barang.id] = barang.stok; // Set initial stock
});
setAvailableStock(stock);
}, [barangList]);

// Add item to cart and update stock
const addToCart = (barang) => {
if (availableStock[barang.id] <= 0) {
alert("Stok habis! Tidak bisa menambahkan lebih banyak.");
return;
}

setCartItems((prev) => {
const existingItem = prev.find((item) => item.id === barang.id);
if (existingItem) {
return prev.map((item) =>
item.id === barang.id
? { ...item, jumlah: item.jumlah + 1 }
: item
);
} else {
return [...prev, { ...barang, jumlah: 1 }];
}
});

// Decrease available stock
setAvailableStock((prev) => ({
...prev,
[barang.id]: prev[barang.id] - 1,
}));
};

// Remove item from cart and update stock
const removeFromCart = (barangId) => {
const cartItem = cartItems.find((item) => item.id === barangId);
if (cartItem) {
setCartItems((prev) =>
prev
.map((item) =>
item.id === barangId
? { ...item, jumlah: item.jumlah - 1 }
: item
)
.filter((item) => item.jumlah > 0)
);

// Increase available stock
setAvailableStock((prev) => ({
...prev,
[barangId]: prev[barangId] + 1,
}));
}
};

// Calculate total price
useEffect(() => {
const total = cartItems.reduce(
(acc, item) => acc + item.harga_jual * item.jumlah,
0
);
setTotalHarga(total);
}, [cartItems]);

// Handle checkout
const handleCheckout = () => {
if (cartItems.length === 0) {
alert("Keranjang kosong. Tambahkan barang terlebih dahulu.");
return;
}
alert(`Checkout berhasil! Total Pembayaran: Rp ${totalHarga.toLocaleString()}`);
setCartItems([]); // Clear the cart
setAvailableStock((prev) => {
const updatedStock = { ...prev };
cartItems.forEach((item) => {
updatedStock[item.id] -= item.jumlah; // Deduct purchased quantities from stock
});
return updatedStock;
});
};

return (
<div className="min-h-screen bg-gray-100 p-6 flex flex-col items-center">
<h1 className="text-2xl font-bold mb-6">Keranjang Belanja</h1>

{/* Barang List */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 w-full max-w-4xl">
{barangList.map((barang) => (
<div
key={barang.id}
className="bg-white p-4 border rounded-lg shadow-md flex flex-col justify-between"
>
<div>
<h3 className="font-bold text-lg">{barang.barang}</h3>
<p className="text-gray-500">Harga: Rp {barang.harga_jual.toLocaleString()}</p>
<p className="text-gray-500">Stok Tersedia: {availableStock[barang.id] ?? barang.stok}</p>
</div>
<button
onClick={() => addToCart(barang)}
disabled={availableStock[barang.id] <= 0}
className={`mt-4 px-4 py-2 rounded-lg transition ${
availableStock[barang.id] > 0
? "bg-blue-500 text-white hover:bg-blue-600"
: "bg-gray-400 text-gray-700 cursor-not-allowed"
}`}
>
Tambah ke Keranjang
</button>
</div>
))}
</div>

{/* Cart Items */}
<div className="w-full max-w-3xl mt-8 bg-white p-6 rounded-lg shadow-md">
<h2 className="text-xl font-bold mb-4">Keranjang</h2>
{cartItems.length > 0 ? (
<>
{cartItems.map((item) => (
<div
key={item.id}
className="flex items-center justify-between mb-4 border-b pb-4"
>
<div>
<h3 className="font-semibold text-md">{item.barang}</h3>
<p className="text-sm text-gray-500">
Harga: Rp {item.harga_jual.toLocaleString()}
</p>
<p className="text-sm text-gray-500">
Jumlah: {item.jumlah}
</p>
</div>
<div className="flex items-center space-x-2">
<button
onClick={() => removeFromCart(item.id)}
className="px-2 py-1 bg-red-500 text-white rounded-lg hover:bg-red-600 transition"
>
-
</button>
<button
onClick={() => addToCart(item)}
disabled={availableStock[item.id] <= 0}
className={`px-2 py-1 rounded-lg transition ${
availableStock[item.id] > 0
? "bg-blue-500 text-white hover:bg-blue-600"
: "bg-gray-400 text-gray-700 cursor-not-allowed"
}`}
>
+
</button>
</div>
</div>
))}

{/* Total and Checkout */}
<div className="flex justify-between items-center mt-6 pt-4 border-t">
<span className="text-lg font-semibold">Total Harga:</span>
<span className="text-xl font-bold">
Rp {totalHarga.toLocaleString()}
</span>
</div>
<button
onClick={handleCheckout}
className="w-full mt-4 bg-green-500 text-white py-2 rounded-lg font-semibold hover:bg-green-600 transition"
>
Checkout
</button>
</>
) : (
<p className="text-center text-gray-500">Keranjang kosong.</p>
)}
</div>
</div>
);
}

--

--

Galih Setiawan Nurohim
Galih Setiawan Nurohim

No responses yet