diff --git a/TODO.md b/TODO.md index a9a8967..6a82e0f 100644 --- a/TODO.md +++ b/TODO.md @@ -16,8 +16,8 @@ - [x] List Customer Verification - [x] Manual Approve Verification -> mendapatkan limit hutang - [x] Setting Bonus Coin (memasukan amount bonus coin yang didapat dengan level dan harga voucher) - bonus coin -- [ ] Voucher Sales (index: customer, total, jumlah voucher, detail: customer, list voucher, payment) -- [ ] Dashboard (gafik hasil penjualan : disorting tanggal, lokasi dan customer) +- [x] Voucher Sales (index: customer, total, jumlah voucher, detail: customer, list voucher, payment) +- [x] Dashboard (gafik hasil penjualan : disorting tanggal, lokasi dan customer) [total voucher] [total customer] [total customer verified] [total deposit] [total voucher terjual bulan xxx] [jumlah voucher terjual bulan xxx] [total voucher terjual hari ini ] [jumlah voucher terjual hari ini] [cart penjualan per tanggal, pilih range tanggal (range default 7), bisa filter by lokasi dan customer] diff --git a/app/Http/Controllers/Api/CustomerController.php b/app/Http/Controllers/Api/CustomerController.php new file mode 100644 index 0000000..b2f6bfe --- /dev/null +++ b/app/Http/Controllers/Api/CustomerController.php @@ -0,0 +1,23 @@ +q != '') { + $query->where('name', 'like', "%$request->q%") + ->orWhere('fullname', 'like', "%$request->q%") + ->orWhere('username', 'like', "%$request->q%"); + } + + return $query->get(); + } +} diff --git a/app/Http/Controllers/Customer/CartController.php b/app/Http/Controllers/Customer/CartController.php index f875d87..65302c8 100644 --- a/app/Http/Controllers/Customer/CartController.php +++ b/app/Http/Controllers/Customer/CartController.php @@ -178,7 +178,7 @@ class CartController extends Controller $deposit = $customer->deposites()->create([ 'credit' => $customer->deposit_balance, 'description' => $description, - 'related_type' => $sale::class, + 'related_type' => Sale::class, 'related_id' => $sale->id, 'is_valid' => DepositHistory::STATUS_VALID, ]); @@ -200,7 +200,7 @@ class CartController extends Controller $deposit = $customer->deposites()->create([ 'credit' => $total, 'description' => $description, - 'related_type' => $sale::class, + 'related_type' => Voucher::class, 'related_id' => $sale->id, 'is_valid' => DepositHistory::STATUS_VALID, ]); diff --git a/app/Http/Controllers/GeneralController.php b/app/Http/Controllers/GeneralController.php index 4ea2aa5..14f747b 100644 --- a/app/Http/Controllers/GeneralController.php +++ b/app/Http/Controllers/GeneralController.php @@ -2,14 +2,99 @@ namespace App\Http\Controllers; +use App\Models\Customer; +use App\Models\DepositHistory; +use App\Models\Sale; +use App\Models\SaleItem; +use App\Models\Voucher; use Illuminate\Http\Request; +use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; class GeneralController extends Controller { - public function index() + public function index(Request $request) { - return inertia('Dashboard'); + $total_voucher = Voucher::count(); + $total_customer = Customer::count(); + $total_customer_verified = Customer::where('identity_verified', Customer::VERIFIED)->count(); + $total_deposit = DepositHistory::where('is_valid', DepositHistory::STATUS_VALID)->sum('debit'); + + $month = now()->locale('id')->translatedFormat('F'); + $startOfMonth = now()->startOfMonth()->format('m/d/Y'); + $endOfMonth = now()->endOfMonth()->format('m/d/Y'); + $total_voucher_sale_this_month = SaleItem::whereBetween(DB::raw("strftime('%m/%d/%Y', created_at)"), [$startOfMonth, $endOfMonth]) + ->sum('price'); + $count_voucher_sale_this_month = SaleItem::whereBetween(DB::raw("strftime('%m/%d/%Y', created_at)"), [$startOfMonth, $endOfMonth]) + ->sum('quantity'); + $total_voucher_sale_this_day = SaleItem::whereDate('created_at', now()->format('Y-m-d')) + ->sum('price'); + $count_voucher_sale_this_day = SaleItem::whereDate('created_at', now()->format('Y-m-d')) + ->sum('quantity'); + + $deposites = DepositHistory::whereDate('created_at', now()->format('Y-m-d')) + ->where('is_valid', DepositHistory::STATUS_VALID) + ->where('debit', '!=', '0') + ->groupBy('customer_id') + ->orderBy('updated_at', 'desc') + ->with('customer') + ->limit(20) + ->selectRaw('sum(debit) as total, is_valid, customer_id') + ->get(); + + $sales = SaleItem::whereDate('sale_items.created_at', now()->format('Y-m-d')) + ->join('sales', 'sales.id', '=', 'sale_items.sale_id') + ->join('customers', 'customers.id', '=', 'sales.customer_id') + ->groupBy('sales.customer_id') + ->orderBy('sale_items.updated_at', 'desc') + ->limit(20) + ->selectRaw('sum(sale_items.price) as total, sum(quantity) as count, sales.customer_id, customers.name, entity_id') + ->get(); + + // charts + $startDate = now()->startOfMonth()->format('m/d/Y'); + $endDate = now()->endOfMonth()->format('m/d/Y'); + if ($request->start_date != '') { + $startDate = Carbon::parse($request->start_date)->format('m/d/Y'); + } + if ($request->end_date != '') { + $endDate = Carbon::parse($request->end_date)->format('m/d/Y'); + } + $charts = Sale::selectRaw("SUM(amount) as sale_total, strftime('%d/%m/%Y', date_time) as date") + ->whereBetween(DB::raw("strftime('%m/%d/%Y', date_time)"), [$startDate, $endDate]) + ->orderBy('date_time', 'asc') + ->groupBy(DB::raw("strftime('%m/%d/%Y', date_time)")); + + // filter lokasi + if ($request->location_id != '') { + $charts->whereHas('items', function ($q) use ($request) { + $q->join('vouchers', 'vouchers.id', '=', 'sale_items.entity_id') + ->where('vouchers.location_id', $request->location_id); + }); + } + + // filter customer + if ($request->customer_id != '') { + $charts->where('customer_id', $request->customer_id); + } + + return inertia('Dashboard', [ + 'total_voucher' => $total_voucher, + 'total_customer' => $total_customer, + 'total_customer_verified' => $total_customer_verified, + 'total_deposit' => $total_deposit, + 'total_voucher_sale_this_month' => $total_voucher_sale_this_month, + 'count_voucher_sale_this_month' => $count_voucher_sale_this_month, + 'total_voucher_sale_this_day' => $total_voucher_sale_this_day, + 'count_voucher_sale_this_day' => $count_voucher_sale_this_day, + 'month' => $month, + 'deposites' => $deposites, + 'sales' => $sales, + 'charts' => $charts->get(), + '_startDate' => $startDate, + '_endDate' => $endDate, + ]); } public function maintance() diff --git a/app/Http/Controllers/SaleController.php b/app/Http/Controllers/SaleController.php index 4735389..0f69215 100644 --- a/app/Http/Controllers/SaleController.php +++ b/app/Http/Controllers/SaleController.php @@ -7,10 +7,20 @@ use Illuminate\Http\Request; class SaleController extends Controller { - public function index() + public function index(Request $request) { $query = Sale::with(['items.voucher', 'customer.level']) - ->withCount(['items']); + ->withCount(['items']) + ->orderBy('updated_at', 'desc'); + + if ($request->q != '') { + $query->where('code', 'like', "%$request->q%") + ->orWhereHas('customer', function ($query) use ($request) { + $query->where('name', 'like', "%$request->q%") + ->orWhere('fullname', 'like', "%$request->q%") + ->orWhere('username', 'like', "%$request->q%"); + }); + } return inertia('Sale/Index', [ 'query' => $query->paginate(), diff --git a/app/Models/SaleItem.php b/app/Models/SaleItem.php index e5362fa..c0aa2d0 100644 --- a/app/Models/SaleItem.php +++ b/app/Models/SaleItem.php @@ -29,6 +29,11 @@ class SaleItem extends Model return $this->belongsTo(Voucher::class, 'entity_id'); } + public function sale() + { + return $this->belongsTo(Sale::class, 'sale_id'); + } + public function shareWord(): Attribute { return Attribute::make(get: function () { diff --git a/package-lock.json b/package-lock.json index 9badb38..a98ff39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,12 +6,14 @@ "": { "dependencies": { "@tinymce/tinymce-react": "^4.3.0", + "chart.js": "^4.3.0", "flowbite": "^1.6.3", "flowbite-react": "^0.3.8", "is": "^3.3.0", "moment": "^2.29.4", "nprogress": "^0.2.0", "qs": "^6.11.0", + "react-chartjs-2": "^5.2.0", "react-datepicker": "^4.8.0", "react-icons": "^4.7.1", "react-number-format": "^5.1.2", @@ -879,6 +881,11 @@ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1188,6 +1195,17 @@ "node": ">=4" } }, + "node_modules/chart.js": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.0.tgz", + "integrity": "sha512-ynG0E79xGfMaV2xAHdbhwiPLczxnNNnasrmPEXriXsPJGjmhOBYzFVEsB65w2qMDz+CaBJJuJD0inE/ab/h36g==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=7" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -2352,6 +2370,15 @@ "node": ">=0.10.0" } }, + "node_modules/react-chartjs-2": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", + "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==", + "peerDependencies": { + "chart.js": "^4.1.1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-datepicker": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.12.0.tgz", diff --git a/package.json b/package.json index 3f97a79..9f2c8e9 100644 --- a/package.json +++ b/package.json @@ -21,12 +21,14 @@ }, "dependencies": { "@tinymce/tinymce-react": "^4.3.0", + "chart.js": "^4.3.0", "flowbite": "^1.6.3", "flowbite-react": "^0.3.8", "is": "^3.3.0", "moment": "^2.29.4", "nprogress": "^0.2.0", "qs": "^6.11.0", + "react-chartjs-2": "^5.2.0", "react-datepicker": "^4.8.0", "react-icons": "^4.7.1", "react-number-format": "^5.1.2", diff --git a/resources/js/Components/FormInputDateRange.jsx b/resources/js/Components/FormInputDateRange.jsx new file mode 100644 index 0000000..fbf008e --- /dev/null +++ b/resources/js/Components/FormInputDateRange.jsx @@ -0,0 +1,56 @@ +import React from 'react' +import DatePicker from 'react-datepicker' +import { converToDate, dateToString } from '@/utils' + +export default function FormInputDateRanger({ + selected, + onChange, + label = '', + error, + placeholder = '', +}) { + return ( +
+ {label !== '' && ( + + )} + { + let startDate = dateToString(date[0]) + let endDate = null + if (date[1] != null) { + endDate = dateToString(date[1]) + } + onChange({ startDate, endDate }) + }} + startDate={converToDate(selected.startDate)} + endDate={converToDate(selected.endDate)} + closeOnScroll={true} + shouldCloseOnSelect={true} + dateFormat="dd/MM/yyyy" + className={`mb-2 bg-gray-50 border text-gray-900 text-sm rounded-lg block w-full p-2.5 dark:bg-gray-700 dark:placeholder-gray-400 dark:text-white ${ + error + ? 'border-red-500 dark:border-red-500 focus:ring-red-500 focus:border-red-500 dark:focus:ring-red-500 dark:focus:border-red-500' + : 'border-gray-300 dark:border-gray-600 focus:ring-blue-500 focus:border-blue-500 dark:focus:ring-blue-500 dark:focus:border-blue-500' + }`} + nextMonthButtonLabel=">" + previousMonthButtonLabel="<" + nextYearButtonLabel=">" + previousYearButtonLabel="<" + placeholderText={placeholder} + selectsRange + /> + {error && ( +

+ {error} +

+ )} +
+ ) +} diff --git a/resources/js/Pages/Customer/SelectionInput.jsx b/resources/js/Pages/Customer/SelectionInput.jsx new file mode 100644 index 0000000..f7d5d8c --- /dev/null +++ b/resources/js/Pages/Customer/SelectionInput.jsx @@ -0,0 +1,263 @@ +import React, { useRef, useEffect, useState } from 'react' +import { useDebounce } from '@/hooks' +import { usePage } from '@inertiajs/react' +import axios from 'axios' +import { HiChevronDown, HiChevronUp, HiX } from 'react-icons/hi' +import { Spinner } from 'flowbite-react' + +export default function SelectionInput(props) { + const ref = useRef() + const { + props: { auth }, + } = usePage() + + const { + label = '', + itemSelected = null, + onItemSelected = () => {}, + disabled = false, + placeholder = '', + error = '', + all = 0, + } = props + + const [showItems, setShowItem] = useState([]) + + const [isSelected, setIsSelected] = useState(true) + const [selected, setSelected] = useState(null) + + const [query, setQuery] = useState('') + const q = useDebounce(query, 300) + + const [isOpen, setIsOpen] = useState(false) + const [loading, setLoading] = useState(false) + + const toggle = () => { + setQuery('') + setIsOpen(!isOpen) + } + + const onInputMouseDown = () => { + setIsSelected(false) + setQuery('') + setIsOpen(!isOpen) + } + + const handleSelectItem = (item) => { + setIsSelected(true) + onItemSelected(item.id) + setSelected(item.name) + setIsOpen(false) + } + + const removeItem = () => { + setIsSelected(false) + setSelected('') + onItemSelected(null) + } + + const filterItems = (value) => { + setIsSelected(false) + setQuery(value) + } + + useEffect(() => { + if (isOpen === true) { + const checkIfClickedOutside = (e) => { + if (isOpen && ref.current && !ref.current.contains(e.target)) { + setIsOpen(false) + if (selected !== null) { + setIsSelected(true) + } + } + } + document.addEventListener('mousedown', checkIfClickedOutside) + return () => { + document.removeEventListener('mousedown', checkIfClickedOutside) + } + } + }, [isOpen]) + + const fetch = (q = '') => { + setLoading(true) + axios + .get(route('api.customer.index', { q: q, all: all }), { + headers: { + 'Content-Type': 'application/json', + // 'Authorization': 'Bearer ' + auth.user.jwt_token + }, + }) + .then((response) => { + setShowItem(response.data) + }) + .catch((err) => { + alert(err) + }) + .finally(() => setLoading(false)) + } + + // every select item open + useEffect(() => { + if (isOpen) { + fetch(q) + } + }, [q, isOpen]) + + // once page load + useEffect(() => { + fetch() + }, []) + + useEffect(() => { + if (disabled) { + setSelected('') + } + }, [disabled]) + + useEffect(() => { + if (itemSelected !== null) { + const item = showItems.find((item) => item.id === itemSelected) + if (item) { + setSelected(item.name) + setIsSelected(true) + } + return + } + setIsSelected(false) + }, [itemSelected, loading]) + + useEffect(() => { + if (isSelected && selected === '') { + setSelected('') + setIsSelected(false) + } + }, [isSelected]) + + return ( +
+
+
+
+ {label !== '' && ( + + )} +
+
+ + filterItems(e.target.value) + } + disabled={disabled} + /> + {isSelected && ( +
{} : removeItem + } + > + +
+ )} +
{} : toggle}> + +
+
+ {error && ( +

+ {error} +

+ )} +
+ {isOpen && ( +
+
+ {loading ? ( +
+
+
+
+ + Loading... +
+
+
+
+ ) : ( + <> + {showItems.map((item, index) => ( +
+ handleSelectItem(item) + } + > +
+
+
+ + {item.name} + +
+
+
+
+ ))} + {showItems.length <= 0 && ( +
+
+
+
+ + No Items + Found + +
+
+
+
+ )} + + )} +
+
+ )} +
+
+
+
+ ) +} diff --git a/resources/js/Pages/Dashboard.jsx b/resources/js/Pages/Dashboard.jsx index 8da35b7..255e7de 100644 --- a/resources/js/Pages/Dashboard.jsx +++ b/resources/js/Pages/Dashboard.jsx @@ -1,8 +1,96 @@ -import React from 'react'; -import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'; -import { Head } from '@inertiajs/react'; +import React, { useState, useEffect } from 'react' +import { + Chart as ChartJS, + CategoryScale, + LinearScale, + BarElement, + Title, + Tooltip, + ArcElement, + Legend, +} from 'chart.js' +import { Bar, Doughnut } from 'react-chartjs-2' +import { Head, router } from '@inertiajs/react' +import { usePrevious } from 'react-use' +import moment from 'moment' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import { formatIDR } from '@/utils' +import FormInputDateRanger from '@/Components/FormInputDateRange' +import CustomerSelectionInput from './Customer/SelectionInput' +import LocationSelectionInput from './Location/SelectionInput' + +ChartJS.register( + CategoryScale, + LinearScale, + BarElement, + Title, + Tooltip, + ArcElement, + Legend +) export default function Dashboard(props) { + const { + total_voucher, + total_customer, + total_customer_verified, + total_deposit, + total_voucher_sale_this_month, + count_voucher_sale_this_month, + total_voucher_sale_this_day, + count_voucher_sale_this_day, + month, + deposites, + sales, + charts, + _startDate, + _endDate, + } = props + + const [dates, setDates] = useState({ + startDate: _startDate, + endDate: _endDate, + }) + const [customer_id, setCustomerId] = useState(null) + const [location_id, setLocationId] = useState(null) + const preValue = usePrevious(`${dates}${customer_id}${location_id}`) + + const options = { + responsive: true, + scales: { + x: {}, + }, + } + + const data = { + labels: charts.map((item) => moment(item.date).format('DD MMM YYYY')), + datasets: [ + { + label: 'Penjualan', + data: charts.map((item) => item.sale_total), + backgroundColor: ['rgba(255, 205, 86, 1)'], + }, + ], + } + + useEffect(() => { + if (preValue) { + router.get( + route(route().current()), + { + startDate: dates.startDate, + endDate: dates.endDate, + customer_id, + location_id, + }, + { + replace: true, + preserveState: true, + } + ) + } + }, [dates, customer_id, location_id]) + return (
-
-
Dashboard
+
+
+
+ Total Voucher +
+
+ {formatIDR(total_voucher)} +
+
+
+
+ Total Customer +
+
+ {formatIDR(total_customer)} +
+
+
+
+ Total Customer Verified +
+
+ {formatIDR(total_customer_verified)} +
+
+
+
+ Total Deposit +
+
+ {formatIDR(total_deposit)} +
+
+
+
+ Total Voucher terjual bulan {month} +
+
+ {formatIDR(total_voucher_sale_this_month)} +
+
+
+
+ Jumlah Voucher terjual bulan {month} +
+
+ {formatIDR(count_voucher_sale_this_month)} +
+
+
+
+ Total Voucher terjual hari ini +
+
+ {formatIDR(total_voucher_sale_this_day)} +
+
+
+
+ Jumlah Voucher terjual hari ini +
+
+ {formatIDR(count_voucher_sale_this_day)} +
+
+
+
+
+
+
+ Penjualan +
+ +
+ + setLocationId(id) + } + placeholder="filter lokasi" + /> + + setCustomerId(id) + } + placeholder="filter customer" + /> + setDates(dates)} + /> +
+
+ +
+
+
+
+ Deposit Hari Ini +
+ + + + + + + + + + {deposites.map((deposit, index) => ( + + + + + + ))} + +
+ # + + Customer + + Deposit +
+ {index + 1} + + {deposit.customer.name} + + {formatIDR(deposit.total)} +
+
+
+
+ Penjualan Hari Ini +
+ + + + + + + + + + + {sales.map((sale, index) => ( + + + + + + + ))} + +
+ # + + Customer + + Voucher + + Total +
+ {index + 1} + + {sale.name} + + {formatIDR(sale.count)} + + {formatIDR(sale.total)} +
- ); + ) } diff --git a/resources/js/Pages/DepositHistory/Index.jsx b/resources/js/Pages/DepositHistory/Index.jsx index bf98321..e93d1a9 100644 --- a/resources/js/Pages/DepositHistory/Index.jsx +++ b/resources/js/Pages/DepositHistory/Index.jsx @@ -81,6 +81,12 @@ export default function Index(props) { > Deposit + + Tanggal + {deposit.amount} + + {deposit.format_created_at} + {deposit.description} diff --git a/resources/js/Pages/Sale/Detail.jsx b/resources/js/Pages/Sale/Detail.jsx index e9c9ed9..328a2ee 100644 --- a/resources/js/Pages/Sale/Detail.jsx +++ b/resources/js/Pages/Sale/Detail.jsx @@ -6,117 +6,106 @@ import FormInput from '@/Components/FormInput' import Button from '@/Components/Button' import { Head, useForm } from '@inertiajs/react' import FormFile from '@/Components/FormFile' +import { formatIDR } from '@/utils' -const TinyEditor = React.lazy(() => import('@/Components/TinyMCE')) - -export default function Form(props) { - const { banner } = props - - const { data, setData, post, processing, errors } = useForm({ - title: '', - description: '', - image: '', - image_url: '', - }) - - const handleOnChange = (event) => { - setData( - event.target.name, - event.target.type === 'checkbox' - ? event.target.checked - ? 1 - : 0 - : event.target.value - ) - } - const handleSubmit = () => { - if (isEmpty(banner) === false) { - post(route('banner.update', banner)) - return - } - post(route('banner.store')) - } - - useEffect(() => { - if (isEmpty(banner) === false) { - setData({ - title: banner.title, - description: banner.description, - image_url: banner.image_url, - }) - } - }, [banner]) +export default function Detail(props) { + const { sale } = props return ( - +
-
Banner
- - - setData('image', e.target.files[0]) - } - error={errors.image} - preview={ - isEmpty(data.image_url) === false && ( - preview - ) - } - /> - -
- Loading...
}> - { - setData( - 'description', - editor.getContent() - ) - }} - /> - -
-
- -
+ + + + + + + + + + + + + + + + + + +
Customer:{sale.customer.name}
+ Metode Pembayaran + :{sale.payed_with}
Total:{formatIDR(sale.amount)}
+
Voucher
+ + + + + + + + + + + + + + {sale.items.map((item, index) => ( + + + + + + + + + + ))} + +
+ No + + Lokasi + + Username + + Password + + Profile + + Comment + + Kuota +
+ {index + 1} + + {item.voucher.location.name} + + {item.voucher.username} + + {item.voucher.password} + + {item.voucher.profile} + + {item.voucher.comment} + + {item.voucher.display_quota} +
diff --git a/resources/js/Pages/Sale/Index.jsx b/resources/js/Pages/Sale/Index.jsx index abb9389..b2ab0eb 100644 --- a/resources/js/Pages/Sale/Index.jsx +++ b/resources/js/Pages/Sale/Index.jsx @@ -1,16 +1,35 @@ -import React from 'react' +import React, { useState, useEffect } from 'react' import { Head, router } from '@inertiajs/react' import { HiEye } from 'react-icons/hi' import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' import Pagination from '@/Components/Pagination' +import SearchInput from '@/Components/SearchInput' import { formatIDR } from '@/utils' +import { usePrevious } from 'react-use' export default function Info(props) { const { query: { links, data }, } = props + const [search, setSearch] = useState('') + const preValue = usePrevious(search) + + const params = { q: search } + useEffect(() => { + if (preValue) { + router.get( + route(route().current()), + { q: search }, + { + replace: true, + preserveState: true, + } + ) + } + }, [search]) + return ( Tambah )} */} +
+ setSearch(e.target.value)} + value={search} + /> +
@@ -118,7 +143,7 @@ export default function Info(props) {
- +
diff --git a/routes/api.php b/routes/api.php index 1e1d5b2..db50ee6 100644 --- a/routes/api.php +++ b/routes/api.php @@ -3,6 +3,7 @@ use App\Http\Controllers\Api\LocationController; use App\Http\Controllers\Api\RoleController; use App\Http\Controllers\Customer\DepositController; +use App\Http\Controllers\Api\CustomerController; use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; @@ -24,6 +25,7 @@ Route::middleware('auth:sanctum')->get('/user', function (Request $request) { // general Route::get('/roles', [RoleController::class, 'index'])->name('api.role.index'); Route::get('/locations', [LocationController::class, 'index'])->name('api.location.index'); +Route::get('/customers', [CustomerController::class, 'index'])->name('api.customer.index'); // midtrans Route::post('mindtrans/notification', [DepositController::class, 'mindtrans_notification'])->name('api.midtrans.notification');