diff --git a/TODO.md b/TODO.md index b35473a..8a94fa0 100644 --- a/TODO.md +++ b/TODO.md @@ -6,7 +6,7 @@ - [x] CRUD Banner - [x] CRUD Rekening / Account - [x] CRUD Customer -- [ ] CRUD Lokasi +- [x] CRUD Lokasi - [ ] CRUD Voucher - [ ] Import Voucher - [ ] Deposit Menu (view daftar histori deposit) diff --git a/app/Http/Controllers/Customer/HomeController.php b/app/Http/Controllers/Customer/HomeController.php index 2c60801..ae3b66a 100644 --- a/app/Http/Controllers/Customer/HomeController.php +++ b/app/Http/Controllers/Customer/HomeController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Customer; use App\Http\Controllers\Controller; use App\Models\Banner; use App\Models\Info; +use App\Models\Location; class HomeController extends Controller { @@ -12,10 +13,12 @@ class HomeController extends Controller { $infos = Info::where('is_publish', 1)->orderBy('updated_at', 'desc')->get(); $banners = Banner::orderBy('updated_at', 'desc')->get(); + $locations = Location::get(); return inertia('Home/Index/Index', [ 'infos' => $infos, - 'banners' => $banners + 'banners' => $banners, + 'locations' => $locations ]); } diff --git a/app/Http/Controllers/LocationController.php b/app/Http/Controllers/LocationController.php index 6f75150..f8ed96b 100644 --- a/app/Http/Controllers/LocationController.php +++ b/app/Http/Controllers/LocationController.php @@ -2,9 +2,71 @@ namespace App\Http\Controllers; +use App\Models\Location; use Illuminate\Http\Request; class LocationController extends Controller { - // + public function index() + { + $query = Location::paginate(); + + return inertia('Location/Index', [ + 'query' => $query, + ]); + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|string', + 'description' => 'nullable|string', + 'logo' => 'nullable|image', + ]); + + $logo = null; + if ($request->hasFile('midtrans_logo_file')) { + $file = $request->file('midtrans_logo_file'); + $file->store('uploads', 'public'); + $logo = $file->hashName('uploads'); + } + + Location::create([ + 'name' => $request->name, + 'description' => $request->description, + 'logo' => $logo, + ]); + + session()->flash('message', ['type' => 'success', 'message' => 'Item has beed saved']); + } + + public function update(Request $request, Location $location) + { + $request->validate([ + 'name' => 'required|string', + 'description' => 'nullable|string', + 'logo' => 'nullable|image', + ]); + + if ($request->hasFile('midtrans_logo_file')) { + $file = $request->file('midtrans_logo_file'); + $file->store('uploads', 'public'); + $location->logo = $file->hashName('uploads'); + } + + $location->update([ + 'name' => $request->name, + 'description' => $request->description, + 'logo' => $location->logo, + ]); + + session()->flash('message', ['type' => 'success', 'message' => 'Item has beed updated']); + } + + public function destroy(Location $location) + { + $location->delete(); + + session()->flash('message', ['type' => 'success', 'message' => 'Item has beed deleted']); + } } diff --git a/database/seeders/DummySeeder.php b/database/seeders/DummySeeder.php index 4204bde..0806881 100644 --- a/database/seeders/DummySeeder.php +++ b/database/seeders/DummySeeder.php @@ -5,6 +5,7 @@ namespace Database\Seeders; use App\Models\Account; use App\Models\Banner; use App\Models\Info; +use App\Models\Location; use Illuminate\Database\Seeder; class DummySeeder extends Seeder @@ -19,6 +20,7 @@ class DummySeeder extends Seeder $this->info(); $this->banner(); $this->account(); + $this->location(); } public function info() @@ -57,4 +59,16 @@ class DummySeeder extends Seeder ]); } } + + public function location() + { + $locations = ['Jarbriel.id', 'Shaff.net', 'Weslycamp.net', 'Glory.net', 'Salgo.id', 'Terna.id', 'Kanza.id']; + + foreach ($locations as $location) { + Location::create([ + 'name' => $location, + 'description' => '-' + ]); + } + } } diff --git a/resources/js/Layouts/Partials/routes.js b/resources/js/Layouts/Partials/routes.js index 0dd1444..03c3d75 100644 --- a/resources/js/Layouts/Partials/routes.js +++ b/resources/js/Layouts/Partials/routes.js @@ -8,6 +8,7 @@ import { } from 'react-icons/hi' import { HiBanknotes, + HiMap, HiOutlineGlobeAlt, HiQuestionMarkCircle, HiUserCircle, @@ -46,6 +47,14 @@ export default [ active: 'setting.*', permission: 'view-setting', }, + { + name: 'Lokasi', + show: true, + icon: HiMap, + route: route('location.index'), + active: 'location.*', + permission: 'view-location', + }, { name: 'Front Home', show: true, diff --git a/resources/js/Pages/Home/Index/Index.jsx b/resources/js/Pages/Home/Index/Index.jsx index 8faf39e..ea53d34 100644 --- a/resources/js/Pages/Home/Index/Index.jsx +++ b/resources/js/Pages/Home/Index/Index.jsx @@ -29,7 +29,7 @@ const GuestBanner = () => { ) } -export default function Index({ auth: { user }, infos, banners }) { +export default function Index({ auth: { user }, infos, banners, locations }) { const handleBanner = (banner) => { router.get(route('home.banner', banner)) } @@ -73,27 +73,15 @@ export default function Index({ auth: { user }, infos, banners }) {
{/* chips */}
-
- Jarbriel.id -
-
- Shaff.net -
-
- Weslycamp.net -
-
- Glory.net -
-
- Salgo.id -
-
- Terna.id -
-
- Kanza.id -
+ {locations.map((location) => ( +
+ {location.name} +
+ ))}
{/* voucher */} diff --git a/resources/js/Pages/Location/FormModal.jsx b/resources/js/Pages/Location/FormModal.jsx new file mode 100644 index 0000000..8dc1ff3 --- /dev/null +++ b/resources/js/Pages/Location/FormModal.jsx @@ -0,0 +1,91 @@ +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 RoleSelectionInput from '../Role/SelectionInput' + +import { isEmpty } from 'lodash' + +export default function FormModal(props) { + const { modalState } = props + const { data, setData, post, put, processing, errors, reset, clearErrors } = + useForm({ + name: '', + description: '', + }) + + 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 location = modalState.data + if (location !== null) { + put(route('location.update', location), { + onSuccess: () => handleClose(), + }) + return + } + post(route('location.store'), { + onSuccess: () => handleClose(), + }) + } + + useEffect(() => { + const location = modalState.data + if (isEmpty(location) === false) { + setData({ + name: location.name, + description: location.description, + }) + return + } + }, [modalState]) + + return ( + + + + +
+ + +
+
+ ) +} diff --git a/resources/js/Pages/Location/Index.jsx b/resources/js/Pages/Location/Index.jsx new file mode 100644 index 0000000..6ef8325 --- /dev/null +++ b/resources/js/Pages/Location/Index.jsx @@ -0,0 +1,164 @@ +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 Index(props) { + const { + query: { links, data }, + auth, + } = props + + const confirmModal = useModalState() + const formModal = useModalState() + + const toggleFormModal = (location = null) => { + formModal.setData(location) + formModal.toggle() + } + + const handleDeleteClick = (location) => { + confirmModal.setData(location) + confirmModal.toggle() + } + + const onDelete = () => { + if (confirmModal.data !== null) { + router.delete(route('location.destroy', confirmModal.data.id)) + } + } + + const canCreate = hasPermission(auth, 'create-location') + const canUpdate = hasPermission(auth, 'update-location') + const canDelete = hasPermission(auth, 'delete-location') + + return ( + + + +
+
+
+
+ {canCreate && ( + + )} +
+
+
+ + + + + + + + + {data.map((location) => ( + + + + + + ))} + +
+ Name + + Description + +
+ {location.name} + + {location.description} + + + {canUpdate && ( + + toggleFormModal( + location + ) + } + > +
+ +
+ Ubah +
+
+
+ )} + {canDelete && ( + + handleDeleteClick( + location + ) + } + > +
+ +
+ Hapus +
+
+
+ )} +
+
+
+
+ +
+
+
+
+
+ + +
+ ) +} diff --git a/routes/admin.php b/routes/admin.php index 6700e9e..7424000 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -6,6 +6,7 @@ use App\Http\Controllers\BannerController; use App\Http\Controllers\CustomerController; use App\Http\Controllers\GeneralController; use App\Http\Controllers\InfoController; +use App\Http\Controllers\LocationController; use App\Http\Controllers\ProfileController; use App\Http\Controllers\RoleController; use App\Http\Controllers\SettingController; @@ -52,6 +53,12 @@ Route::middleware(['http_secure_aware', 'inertia.admin']) Route::put('/infos/{info}', [InfoController::class, 'update'])->name('info.update'); Route::delete('/infos/{info}', [InfoController::class, 'destroy'])->name('info.destroy'); + // Location + Route::get('/locations', [LocationController::class, 'index'])->name('location.index'); + Route::post('/locations', [LocationController::class, 'store'])->name('location.store'); + Route::put('/locations/{location}', [LocationController::class, 'update'])->name('location.update'); + Route::delete('/locations/{location}', [LocationController::class, 'destroy'])->name('location.destroy'); + // Account Route::get('/accounts', [AccountController::class, 'index'])->name('account.index'); Route::post('/accounts', [AccountController::class, 'store'])->name('account.store');