From 2503b20e33b0ad7ab2e9319fefc3cecd758fb058 Mon Sep 17 00:00:00 2001 From: Aji Kamaludin Date: Wed, 14 Jun 2023 22:00:01 +0700 Subject: [PATCH] customer crud done --- .../Controllers/Api/CustomerController.php | 29 ++ app/Http/Controllers/CustomerController.php | 74 +++++ .../Controllers/CustomerPointController.php | 24 ++ app/Http/Controllers/HomeController.php | 23 ++ app/Http/Controllers/SettingController.php | 41 +++ app/Http/Middleware/HandleInertiaRequests.php | 1 + app/Models/Customer.php | 9 +- app/Models/CustomerPoint.php | 9 +- ...23_06_14_022155_create_customers_table.php | 10 +- ...14_022203_create_customer_points_table.php | 9 +- database/seeders/DummySeeder.php | 8 +- database/seeders/PermissionSeeder.php | 12 + .../Components/Defaults/ApplicationLogo.jsx | 11 +- resources/js/Components/FormInput.jsx | 30 +- resources/js/Components/Input.jsx | 35 ++- resources/js/Layouts/Partials/SidebarNav.jsx | 58 ++-- resources/js/Layouts/Partials/helpers.js | 14 +- resources/js/Layouts/Partials/routes.js | 71 +++-- resources/js/Pages/Customer/FormModal.jsx | 102 +++++++ resources/js/Pages/Customer/Index.jsx | 196 +++++++++++++ .../js/Pages/Customer/SelectionInput.jsx | 263 ++++++++++++++++++ resources/js/Pages/Home.jsx | 69 +++++ resources/js/Pages/Setting/Index.jsx | 76 +++++ routes/api.php | 2 + routes/web.php | 29 +- 25 files changed, 1113 insertions(+), 92 deletions(-) create mode 100644 app/Http/Controllers/Api/CustomerController.php create mode 100644 app/Http/Controllers/CustomerController.php create mode 100644 app/Http/Controllers/CustomerPointController.php create mode 100644 app/Http/Controllers/HomeController.php create mode 100644 app/Http/Controllers/SettingController.php create mode 100644 resources/js/Pages/Customer/FormModal.jsx create mode 100644 resources/js/Pages/Customer/Index.jsx create mode 100644 resources/js/Pages/Customer/SelectionInput.jsx create mode 100644 resources/js/Pages/Home.jsx create mode 100644 resources/js/Pages/Setting/Index.jsx 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/CustomerController.php b/app/Http/Controllers/CustomerController.php new file mode 100644 index 0000000..7b98f8e --- /dev/null +++ b/app/Http/Controllers/CustomerController.php @@ -0,0 +1,74 @@ +q) { + $query->where('name', 'like', "%{$request->q}%") + ->orWhere('code', 'like', "%{$request->q}%"); + } + + $query->orderBy('created_at', 'desc'); + + return inertia('Customer/Index', [ + 'query' => $query->paginate(), + ]); + } + + public function store(Request $request) + { + $request->validate([ + 'code' => 'required|string|max:255', + 'name' => 'required|string|max:255', + 'point' => 'required|numeric', + ]); + + Customer::create([ + 'code' => $request->code, + 'name' => $request->name, + 'start_point' => $request->point, + 'last_point' => $request->point, + ]); + + return redirect()->route('customer.index') + ->with('message', ['type' => 'success', 'message' => 'Item has beed saved']); + } + + public function update(Request $request, Customer $customer) + { + $request->validate([ + 'code' => 'required|string|max:255', + 'name' => 'required|string|max:255', + 'point' => 'required|numeric', + ]); + + $customer->update([ + 'code' => $request->code, + 'name' => $request->name, + 'last_point' => $request->point, + ]); + + return redirect()->route('customer.index') + ->with('message', ['type' => 'success', 'message' => 'Item has beed updated']); + } + + public function destroy(Customer $customer) + { + $customer->delete(); + + return redirect()->route('customer.index') + ->with('message', ['type' => 'success', 'message' => 'Item has beed deleted']); + } + + public function import() + { + } +} diff --git a/app/Http/Controllers/CustomerPointController.php b/app/Http/Controllers/CustomerPointController.php new file mode 100644 index 0000000..00961fd --- /dev/null +++ b/app/Http/Controllers/CustomerPointController.php @@ -0,0 +1,24 @@ + Setting::where('key', 'app_name')->value('value'), + ]); + } + + public function check(Request $request) + { + $request->validate([ + 'customer_code' => 'required|string|exists:customers,code' + ]); + } +} diff --git a/app/Http/Controllers/SettingController.php b/app/Http/Controllers/SettingController.php new file mode 100644 index 0000000..322f58e --- /dev/null +++ b/app/Http/Controllers/SettingController.php @@ -0,0 +1,41 @@ + Setting::all(), + ]); + } + + public function update(Request $request) + { + $request->validate([ + 'app_name' => 'required|string', + ]); + + DB::beginTransaction(); + + foreach ($request->input() as $key => $value) { + Setting::updateOrCreate( + ['key' => $key], + [ + 'value' => $value, + 'type' => 'text' + ] + ); + } + + DB::commit(); + + return redirect()->route('setting.index') + ->with('message', ['type' => 'success', 'message' => 'Setting saved']); + } +} diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index 9dc5ef9..530351f 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -36,6 +36,7 @@ class HandleInertiaRequests extends Middleware 'flash' => [ 'message' => fn () => $request->session()->get('message'), ], + 'app_name' => env('APP_NAME', 'App Name') ]); } } diff --git a/app/Models/Customer.php b/app/Models/Customer.php index bd96440..c48a7cb 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -2,10 +2,13 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Model; class Customer extends Model { - use HasFactory; + protected $fillable = [ + 'name', + 'code', + 'start_point', + 'last_point', + ]; } diff --git a/app/Models/CustomerPoint.php b/app/Models/CustomerPoint.php index 9a20435..adaeb9b 100644 --- a/app/Models/CustomerPoint.php +++ b/app/Models/CustomerPoint.php @@ -2,10 +2,11 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Model; - class CustomerPoint extends Model { - use HasFactory; + protected $fillable = [ + 'customer_id', + 'description', + 'point', + ]; } diff --git a/database/migrations/2023_06_14_022155_create_customers_table.php b/database/migrations/2023_06_14_022155_create_customers_table.php index 5e1f0d5..f3c382b 100644 --- a/database/migrations/2023_06_14_022155_create_customers_table.php +++ b/database/migrations/2023_06_14_022155_create_customers_table.php @@ -12,8 +12,16 @@ return new class extends Migration public function up(): void { Schema::create('customers', function (Blueprint $table) { - $table->id(); + $table->ulid('id')->primary(); + $table->string('name'); + $table->string('code'); + $table->decimal('start_point', 20, 2); + $table->decimal('last_point', 20, 2); $table->timestamps(); + $table->softDeletes(); + $table->ulid('created_by')->nullable(); + $table->ulid('updated_by')->nullable(); + $table->ulid('deleted_by')->nullable(); }); } diff --git a/database/migrations/2023_06_14_022203_create_customer_points_table.php b/database/migrations/2023_06_14_022203_create_customer_points_table.php index 253fcda..04060c7 100644 --- a/database/migrations/2023_06_14_022203_create_customer_points_table.php +++ b/database/migrations/2023_06_14_022203_create_customer_points_table.php @@ -12,8 +12,15 @@ return new class extends Migration public function up(): void { Schema::create('customer_points', function (Blueprint $table) { - $table->id(); + $table->ulid('id')->primary(); + $table->ulid('customer_id'); + $table->string('description', 1000); + $table->decimal('point', 20, 2); $table->timestamps(); + $table->softDeletes(); + $table->ulid('created_by')->nullable(); + $table->ulid('updated_by')->nullable(); + $table->ulid('deleted_by')->nullable(); }); } diff --git a/database/seeders/DummySeeder.php b/database/seeders/DummySeeder.php index e237efc..61b839b 100644 --- a/database/seeders/DummySeeder.php +++ b/database/seeders/DummySeeder.php @@ -2,7 +2,9 @@ namespace Database\Seeders; +use App\Models\Setting; use Illuminate\Database\Seeder; +use Illuminate\Support\Str; class DummySeeder extends Seeder { @@ -13,6 +15,10 @@ class DummySeeder extends Seeder */ public function run() { - // + $settings = [ + ['id' => Str::ulid(), 'key' => 'app_name', 'value' => 'Customer Point', 'type' => 'text'] + ]; + + Setting::insert($settings); } } diff --git a/database/seeders/PermissionSeeder.php b/database/seeders/PermissionSeeder.php index 50d22e9..82f3e2c 100644 --- a/database/seeders/PermissionSeeder.php +++ b/database/seeders/PermissionSeeder.php @@ -30,6 +30,18 @@ class PermissionSeeder extends Seeder ['id' => Str::ulid(), 'label' => 'Update Role', 'name' => 'update-role'], ['id' => Str::ulid(), 'label' => 'View Role', 'name' => 'view-role'], ['id' => Str::ulid(), 'label' => 'Delete Role', 'name' => 'delete-role'], + + ['id' => Str::ulid(), 'label' => 'View Setting', 'name' => 'view-setting'], + + ['id' => Str::ulid(), 'label' => 'Create Customer', 'name' => 'create-customer'], + ['id' => Str::ulid(), 'label' => 'Update Customer', 'name' => 'update-customer'], + ['id' => Str::ulid(), 'label' => 'View Customer', 'name' => 'view-customer'], + ['id' => Str::ulid(), 'label' => 'Delete Customer', 'name' => 'delete-customer'], + + ['id' => Str::ulid(), 'label' => 'Create Customer Point', 'name' => 'create-customer-point'], + ['id' => Str::ulid(), 'label' => 'Update Customer Point', 'name' => 'update-customer-point'], + ['id' => Str::ulid(), 'label' => 'View Customer Point', 'name' => 'view-customer-point'], + ['id' => Str::ulid(), 'label' => 'Delete Customer Point', 'name' => 'delete-customer-point'], ]; foreach ($permissions as $permission) { diff --git a/resources/js/Components/Defaults/ApplicationLogo.jsx b/resources/js/Components/Defaults/ApplicationLogo.jsx index 08b4984..092d6da 100644 --- a/resources/js/Components/Defaults/ApplicationLogo.jsx +++ b/resources/js/Components/Defaults/ApplicationLogo.jsx @@ -1,7 +1,10 @@ -import React from 'react'; +import { usePage } from '@inertiajs/react' +import React from 'react' export default function ApplicationLogo({ className }) { - return ( -

App Name

- ); + const { + props: { app_name }, + } = usePage() + + return

{app_name}

} diff --git a/resources/js/Components/FormInput.jsx b/resources/js/Components/FormInput.jsx index 41073e8..483b005 100644 --- a/resources/js/Components/FormInput.jsx +++ b/resources/js/Components/FormInput.jsx @@ -1,11 +1,31 @@ -import React from "react"; -import Input from "./Input"; +import React from 'react' +import Input from './Input' -export default function FormInput({ type, name, onChange, value, label, className, error, autoComplete, autoFocus, placeholder, disabled, readOnly}) { +export default function FormInput({ + type, + name, + onChange, + value, + label, + className, + error, + autoComplete, + autoFocus, + placeholder, + disabled, + readOnly, +}) { + const id = `${name}-${label}` return (
- +
) -} \ No newline at end of file +} diff --git a/resources/js/Components/Input.jsx b/resources/js/Components/Input.jsx index 1e64a6a..4ae2795 100644 --- a/resources/js/Components/Input.jsx +++ b/resources/js/Components/Input.jsx @@ -1,24 +1,43 @@ import React from 'react' -export default function Input({ type = 'text', name, onChange, value, error = "", autoComplete = false, autoFocus = false, placeholder , className ,disabled, readOnly}) { +export default function Input({ + type = 'text', + name, + onChange, + value, + error = '', + autoComplete = false, + autoFocus = false, + placeholder, + className, + disabled, + readOnly, + id, +}) { return ( <> - {error && ( -

{error}

+

+ {error} +

)} - ) -} \ No newline at end of file +} diff --git a/resources/js/Layouts/Partials/SidebarNav.jsx b/resources/js/Layouts/Partials/SidebarNav.jsx index c7fb390..06ca174 100644 --- a/resources/js/Layouts/Partials/SidebarNav.jsx +++ b/resources/js/Layouts/Partials/SidebarNav.jsx @@ -1,21 +1,24 @@ -import React from 'react'; -import { router } from '@inertiajs/react'; -import { Sidebar } from 'flowbite-react'; -import { HiLogout } from 'react-icons/hi'; -import { filterOpenMenu } from './helpers'; -import routes from './routes'; -import { formatDate } from '@/utils'; +import React from 'react' +import { Link, router, usePage } from '@inertiajs/react' +import { Sidebar } from 'flowbite-react' +import { HiLogout } from 'react-icons/hi' +import { filterOpenMenu } from './helpers' +import routes from './routes' export default function SidebarNav({ user }) { - const menus = routes.filter(item => { + const { + props: { app_name }, + } = usePage() + + const menus = routes.filter((item) => { item.open = false if (!item.show) { return null } - if (+user.is_superadmin === 1) { + if (user.role === null) { return filterOpenMenu(user, item) } - if(user.role.permissions.find(p => p.name === item.permission)) { + if (user.role.permissions.find((p) => p.name === item.permission)) { return item } @@ -26,33 +29,36 @@ export default function SidebarNav({ user }) { - {menus.map(item => ( + {menus.map((item) => (
{item.items === undefined ? ( - router.visit(item.route)} - icon={item.icon} - active={route().current(item.active)} - > - {item.name} - + router.visit(item.route)} + icon={item.icon} + active={route().current(item.active)} + > + {item.name} + ) : ( - {item.items.map(item => ( - ( + router.visit(item.route)} icon={item.icon} - active={route().current(item.active)} + active={route().current( + item.active + )} + onClick={() => + router.visit(item.route) + } > {item.name} ))} - )}
@@ -64,10 +70,10 @@ export default function SidebarNav({ user }) { Logout
-

- App Name © {(new Date()).getFullYear()} +

+ {app_name} © {new Date().getFullYear()}

) -} \ No newline at end of file +} diff --git a/resources/js/Layouts/Partials/helpers.js b/resources/js/Layouts/Partials/helpers.js index 4450946..7a6fa74 100644 --- a/resources/js/Layouts/Partials/helpers.js +++ b/resources/js/Layouts/Partials/helpers.js @@ -1,21 +1,25 @@ export const filterOpenMenu = (user, item) => { - const isAdmin = +user.is_superadmin === 1 + const isAdmin = user.role === null if ('items' in item) { let items = [] if (isAdmin) { items = item.items } else { - items = item.items.filter(item => user.role.permissions.find(p => p.name === item.permission) ? item : null) + items = item.items.filter((item) => + user.role.permissions.find((p) => p.name === item.permission) + ? item + : null + ) } if (items.length > 0) { - let activeItem = items.map(item => route().current(item.active)) + let activeItem = items.map((item) => route().current(item.active)) item.open = activeItem.includes(true) - item.items = items.filter(item => item.show ? item : null) + item.items = items.filter((item) => (item.show ? item : null)) return item } } if (isAdmin) { return item } -} \ No newline at end of file +} diff --git a/resources/js/Layouts/Partials/routes.js b/resources/js/Layouts/Partials/routes.js index c4b349e..17125f1 100644 --- a/resources/js/Layouts/Partials/routes.js +++ b/resources/js/Layouts/Partials/routes.js @@ -1,56 +1,67 @@ import { HiChartPie, HiUser, - HiCollection, - HiAdjustments, - HiPlusCircle, - HiCurrencyDollar, - HiCash, - HiClipboardList, - HiHashtag, HiUsers, HiUserGroup, HiUserCircle, - HiOutlineTruck, - HiDatabase, - HiShoppingBag, - HiReceiptTax, - HiHome, - HiInboxIn, - HiOutlineCash, - HiOutlineTable -} from "react-icons/hi"; + HiCog, +} from 'react-icons/hi' +import { HiTrophy } from 'react-icons/hi2' export default [ { - name: "Dashboard", + name: 'Dashboard', show: true, icon: HiChartPie, - route: route("dashboard"), - active: "dashboard", - permission: "view-dashboard", + route: route('dashboard'), + active: 'dashboard', + permission: 'view-dashboard', }, { - name: "User", + name: 'Customers', + show: true, + icon: HiUserCircle, + route: route('customer.index'), + active: 'customer.index', + permission: 'view-customer', + }, + { + name: 'Point', + show: true, + icon: HiTrophy, + route: route('customer-point.index'), + active: 'customer-point.index', + permission: 'view-customer-point', + }, + { + name: 'Admin', show: true, icon: HiUser, items: [ { - name: "Roles", + name: 'Roles', show: true, icon: HiUserGroup, - route: route("roles.index"), - active: "roles.*", - permission: "view-role", + route: route('roles.index'), + active: 'roles.*', + permission: 'view-role', }, { - name: "Users", + name: 'Users', show: true, icon: HiUsers, - route: route("user.index"), - active: "user.index", - permission: "view-user", + route: route('user.index'), + active: 'user.index', + permission: 'view-user', }, ], }, -]; + { + name: 'Setting', + show: true, + icon: HiCog, + route: route('setting.index'), + active: 'setting.index', + permission: 'view-setting', + }, +] diff --git a/resources/js/Pages/Customer/FormModal.jsx b/resources/js/Pages/Customer/FormModal.jsx new file mode 100644 index 0000000..f9851e6 --- /dev/null +++ b/resources/js/Pages/Customer/FormModal.jsx @@ -0,0 +1,102 @@ +import React, { useEffect } from 'react' +import Modal from '@/Components/Modal' +import { useForm } from '@inertiajs/react' +import Button from '@/Components/Button' +import FormInput from '@/Components/FormInput' +import { isEmpty } from 'lodash' + +export default function FormModal(props) { + const { modalState } = props + const { data, setData, post, put, processing, errors, reset, clearErrors } = + useForm({ + code: '', + name: '', + point: 0, + }) + + const handleOnChange = (event) => { + setData( + event.target.name, + event.target.type === 'checkbox' + ? event.target.checked + ? 1 + : 0 + : event.target.value + ) + } + + const handleReset = () => { + modalState.setData(null) + reset() + clearErrors() + } + + const handleClose = () => { + handleReset() + modalState.toggle() + } + + const handleSubmit = () => { + const customer = modalState.data + if (customer !== null) { + put(route('customer.update', customer), { + onSuccess: () => handleClose(), + }) + return + } + post(route('customer.store'), { + onSuccess: () => handleClose(), + }) + } + + useEffect(() => { + const customer = modalState.data + if (isEmpty(customer) === false) { + setData({ + name: customer.name, + code: customer.code, + point: customer.last_point, + }) + return + } + }, [modalState]) + + return ( + + + + +
+ + +
+
+ ) +} diff --git a/resources/js/Pages/Customer/Index.jsx b/resources/js/Pages/Customer/Index.jsx new file mode 100644 index 0000000..7231423 --- /dev/null +++ b/resources/js/Pages/Customer/Index.jsx @@ -0,0 +1,196 @@ +import React, { useEffect, useState } from 'react' +import { router } from '@inertiajs/react' +import { usePrevious } from 'react-use' +import { Head } from '@inertiajs/react' +import { Button, Dropdown } from 'flowbite-react' +import { HiPencil, HiTrash } from 'react-icons/hi' +import { useModalState } from '@/hooks' + +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import Pagination from '@/Components/Pagination' +import ModalConfirm from '@/Components/ModalConfirm' +import FormModal from './FormModal' +import SearchInput from '@/Components/SearchInput' +import { hasPermission } from '@/utils' + +export default function Customer(props) { + const { + query: { links, data }, + auth, + } = props + + const [search, setSearch] = useState('') + const preValue = usePrevious(search) + + const confirmModal = useModalState() + const formModal = useModalState() + + const toggleFormModal = (customer = null) => { + formModal.setData(customer) + formModal.toggle() + } + + const handleDeleteClick = (customer) => { + confirmModal.setData(customer) + confirmModal.toggle() + } + + const onDelete = () => { + if (confirmModal.data !== null) { + router.delete(route('customer.destroy', confirmModal.data.id)) + } + } + + const params = { q: search } + useEffect(() => { + if (preValue) { + router.get( + route(route().current()), + { q: search }, + { + replace: true, + preserveState: true, + } + ) + } + }, [search]) + + const canCreate = hasPermission(auth, 'create-customer') + const canUpdate = hasPermission(auth, 'update-customer') + const canDelete = hasPermission(auth, 'delete-customer') + + return ( + + + +
+
+
+
+ {canCreate && ( + + )} +
+ setSearch(e.target.value)} + value={search} + /> +
+
+
+
+ + + + + + + + + + {data.map((customer) => ( + + + + + + + ))} + +
+ Code + + Name + + Point + +
+ {customer.code} + + {customer.name} + + {customer.last_point} + + + {canUpdate && ( + + toggleFormModal( + customer + ) + } + > +
+ +
+ Edit +
+
+
+ )} + {canDelete && ( + + handleDeleteClick( + customer + ) + } + > +
+ +
+ Delete +
+
+
+ )} +
+
+
+
+ +
+
+
+
+
+ + +
+ ) +} diff --git a/resources/js/Pages/Customer/SelectionInput.jsx b/resources/js/Pages/Customer/SelectionInput.jsx new file mode 100644 index 0000000..8950c8b --- /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/Home.jsx b/resources/js/Pages/Home.jsx new file mode 100644 index 0000000..4aea277 --- /dev/null +++ b/resources/js/Pages/Home.jsx @@ -0,0 +1,69 @@ +import React, { useEffect } from 'react' +import GuestLayout from '@/Layouts/GuestLayout' +import InputError from '@/Components/Defaults/InputError' +import { Head, Link, useForm } from '@inertiajs/react' +import { Button, TextInput, Label, Checkbox, Spinner } from 'flowbite-react' + +export default function Login({ app_name }) { + const { data, setData, post, processing, errors, reset } = useForm({ + customer_code: '', + }) + + const onHandleChange = (event) => { + setData( + event.target.name, + event.target.type === 'checkbox' + ? event.target.checked + : event.target.value + ) + } + + const handleKeyDown = (e) => { + if (e.code === 'Enter') { + post(route('home')) + } + } + + const submit = (e) => { + e.preventDefault() + + post(route('home')) + } + + return ( +
+ +
+
+ {app_name} +
+ +
+
+ + + +
+ +
+ +
+
+
+
+ ) +} diff --git a/resources/js/Pages/Setting/Index.jsx b/resources/js/Pages/Setting/Index.jsx new file mode 100644 index 0000000..6dc5892 --- /dev/null +++ b/resources/js/Pages/Setting/Index.jsx @@ -0,0 +1,76 @@ +import React from 'react' +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import FormInput from '@/Components/FormInput' +import Button from '@/Components/Button' +import { Head, useForm } from '@inertiajs/react' +import TextArea from '@/Components/TextArea' +import { isEmpty } from 'lodash' + +const extractValue = (set, key) => { + const find = set.find((s) => s.key === key) + if (isEmpty(find) === false) { + if (find.type === 'image') { + return find?.url + } + return find?.value + } + return '' +} + +export default function Setting(props) { + const { setting } = props + + const { data, setData, post, processing, errors } = useForm({ + app_name: extractValue(setting, 'app_name'), + }) + + const handleOnChange = (event) => { + setData( + event.target.name, + event.target.type === 'checkbox' + ? event.target.checked + ? 1 + : 0 + : event.target.value + ) + } + + const handleSubmit = () => { + post(route('setting.update')) + } + + return ( + + + +
+
+
+
Setting
+ +
+ +
+
+
+
+
+ ) +} diff --git a/routes/api.php b/routes/api.php index 1b98e1b..47fa82f 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,6 +1,7 @@ get('/user', function (Request $request) { return $request->user(); }); +Route::get('/customers', [CustomerController::class, 'index'])->name('api.customer.index'); Route::get('/roles', [RoleController::class, 'index'])->name('api.role.index'); diff --git a/routes/web.php b/routes/web.php index 0ad6d53..20f7073 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,9 +1,13 @@ route('login'); -}); +Route::get('/', [HomeController::class, 'index'])->name('home'); +Route::post('/', [HomeController::class, 'check']); Route::middleware(['auth'])->group(function () { Route::get('/dashboard', [GeneralController::class, 'index'])->name('dashboard'); @@ -33,6 +36,24 @@ Route::middleware(['auth'])->group(function () { // Role Route::resource('/roles', RoleController::class); + + // Setting + Route::get('/setting', [SettingController::class, 'index'])->name('setting.index'); + Route::post('/setting', [SettingController::class, 'update'])->name('setting.update'); + + // Customer + Route::get('/customers', [CustomerController::class, 'index'])->name('customer.index'); + Route::post('/customers/import', [CustomerController::class, 'import'])->name('customer.import'); + Route::post('/customers', [CustomerController::class, 'store'])->name('customer.store'); + Route::put('/customers/{customer}', [CustomerController::class, 'update'])->name('customer.update'); + Route::delete('/customers/{customer}', [CustomerController::class, 'destroy'])->name('customer.destroy'); + + // Customer Point + Route::get('/customers-points', [CustomerPointController::class, 'index'])->name('customer-point.index'); + Route::post('/customers-points/import', [CustomerPointController::class, 'import'])->name('customer-point.import'); + Route::post('/customers-points', [CustomerPointController::class, 'store'])->name('customer-point.store'); + Route::put('/customers-points/{customer}', [CustomerPointController::class, 'update'])->name('customer-point.update'); + Route::delete('/customers-points/{customer}', [CustomerPointController::class, 'destroy'])->name('customer-point.destroy'); }); Route::middleware('auth')->group(function () { @@ -41,4 +62,4 @@ Route::middleware('auth')->group(function () { Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); }); -require __DIR__.'/auth.php'; +require __DIR__ . '/auth.php';