diff --git a/app/Http/Controllers/Api/CustomerController.php b/app/Http/Controllers/Api/CustomerController.php
new file mode 100644
index 0000000..8b866e5
--- /dev/null
+++ b/app/Http/Controllers/Api/CustomerController.php
@@ -0,0 +1,29 @@
+q) {
+ $query->where('name', 'like', "%{$request->q}%");
+ }
+
+ if ($request->except_id) {
+ $query->where('id', '!=', $request->except_id);
+ }
+
+ if ($request->all == 1) {
+ return $query->get();
+ }
+
+ return $query->get();
+ }
+}
diff --git a/app/Http/Controllers/SaleController.php b/app/Http/Controllers/SaleController.php
new file mode 100644
index 0000000..96481d6
--- /dev/null
+++ b/app/Http/Controllers/SaleController.php
@@ -0,0 +1,73 @@
+with(['items.product.category', 'customer']);
+
+ if ($request->q) {
+ $query->where('code', 'like', "%{$request->q}%");
+ }
+
+ $query->orderBy('created_at', 'desc');
+
+ return inertia('Sale/Index', [
+ 'query' => $query->paginate(10),
+ ]);
+ }
+
+ public function create(Request $request)
+ {
+ $products = Product::query()->orderBy('updated_at', 'desc');
+
+ if ($request->q != '') {
+ $products->where('name', 'like', "%$request->q%");
+ }
+
+ return inertia('Sale/Form', [
+ '_products' => $products->paginate(16),
+ '_page' => $request->page ?? 1,
+ ]);
+ }
+
+ public function store(Request $request)
+ {
+ $request->validate([
+ 'date' => 'required|date',
+ 'customer_id' => 'nullable|exists:customers,id',
+ 'items' => 'required|array',
+ 'items.*.id' => 'required|exists:products,id',
+ 'items.*.qty' => 'required|numeric'
+ ]);
+
+ DB::beginTransaction();
+ $sale = Sale::create([
+ 'code' => Str::upper(Str::random(6)),
+ 'date' => $request->date,
+ 'customer_id' => $request->customer_id,
+ 'total' => collect($request->items)->sum(fn ($item) => $item['qty'] * $item['price'])
+ ]);
+
+ foreach($request->items as $item) {
+ $sale->items()->create([
+ "product_id" => $item['id'],
+ "price" => $item['price'],
+ "cost" => $item['cost'],
+ "quantity" => $item['qty'],
+ ]);
+ }
+ DB::commit();
+
+ return redirect()->route('sale.index')
+ ->with('message', ['type' => 'success', 'message' => 'Item has beed saved']);
+ }
+}
diff --git a/resources/js/Pages/Sale/Form.jsx b/resources/js/Pages/Sale/Form.jsx
new file mode 100644
index 0000000..3e7f9ff
--- /dev/null
+++ b/resources/js/Pages/Sale/Form.jsx
@@ -0,0 +1,197 @@
+import React, { useEffect, useState } from 'react';
+import { Head, Link, router, useForm } from '@inertiajs/react';
+import { usePrevious } from 'react-use';
+import { HiXCircle } from 'react-icons/hi';
+
+import { dateToString, formatIDR } from '@/utils';
+import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
+import SearchInput from '@/Components/SearchInput';
+import Button from '@/Components/Button';
+import FormInputDate from '@/Components/FormInputDate';
+import Pagination from '@/Components/Pagination';
+import FormInput from '@/Components/FormInput';
+import CustomerSelectionInput from '../Customer/SelectionInput';
+import { Spinner } from 'flowbite-react';
+
+export default function Sale(props) {
+ const { _products: { data: products, links}, _page } = props
+
+ const [loading, setLoading] = useState(false)
+ const [search, setSearch] = useState('')
+ const preValue = usePrevious(search)
+
+ const { data, setData, post, processing, errors } = useForm({
+ date: dateToString(new Date()),
+ customer_id: null,
+ items: []
+ })
+
+ const addItem = (product) => {
+ const isExist = data.items.find(item => item.id === product.id)
+ if (isExist) {
+ return
+ }
+ setData('items', data.items.concat({
+ ...product,
+ qty: 1
+ }))
+ }
+
+ const removeItem = (id) => {
+ setData('items', data.items.filter(item => item.id !== id))
+ }
+
+ const setQuantityItem = (id, qty) => {
+ setData('items', data.items.map(item => {
+ if (item.id === id) {
+ return {
+ ...item,
+ qty: qty,
+ }
+ }
+ return item
+ }))
+ }
+
+ const handleSubmit = () => {
+ post(route('sale.store'))
+ }
+
+ const params = { q: search, page: _page }
+ useEffect(() => {
+ if (preValue) {
+ setLoading(true)
+ router.get(
+ route(route().current()),
+ { q: search, page: _page },
+ {
+ replace: true,
+ preserveState: true,
+ onSuccess: () => {
+ setLoading(false)
+ },
+ }
+ )
+ }
+ }, [search])
+
+ console.log(data)
+ const total = data.items.reduce((amt, item) => amt + (+item.qty * +item.price), 0)
+
+ return (
+
+ Kode + | ++ Tanggal + | ++ Pelanggan + | ++ Total + | ++ |
---|---|---|---|---|
+ {sale.code} + | ++ {formatDate(sale.date)} + | ++ {sale.customer?.name} + | ++ {formatIDR(sale.total)} + | +
+
+
+ Detail
+
+
+ Hapus
+ |
+