diff --git a/TODO.md b/TODO.md index ccb4872..1b4ed9f 100644 --- a/TODO.md +++ b/TODO.md @@ -15,13 +15,13 @@ # Back Office -- [ ] tambah biaya admin di deposit manual transfer +- [x] tambah biaya admin di deposit manual transfer - [x] info di ubah jadi html - [ ] tambahan detail customer untuk detail mitra wbb - [ ] detail customer level untuk tampilan screen level customer di depan - [ ] rombak fitur affiliasi -- [ ] tambah detail di user admin -- [ ] tambah logo bank -- [ ] tambah setor tunai +- [x] tambah detail di user admin +- [x] tambah logo bank +- [x] tambah setor tunai - [ ] pengaturan share dapat menggunakan html - [ ] menu mitrawbb diff --git a/app/Http/Controllers/Api/CustomerLevelController.php b/app/Http/Controllers/Api/CustomerLevelController.php new file mode 100644 index 0000000..eb8e136 --- /dev/null +++ b/app/Http/Controllers/Api/CustomerLevelController.php @@ -0,0 +1,15 @@ +with('message', ['type' => 'error', 'message' => 'Akun belum aktif, Silahkan klik link verifikasi di email anda']); } - if ($user->status == Customer::STATUS_SUSPEND) { - return redirect()->route('customer.login') - ->with('message', ['type' => 'error', 'message' => 'Akun anda telah disuspend, silahkan hubungi penyedia layanan']); - } + // Akun Suspend + // if ($user->status == Customer::STATUS_SUSPEND) { + // return redirect()->route('customer.login') + // ->with('message', ['type' => 'error', 'message' => 'Akun anda telah disuspend, silahkan hubungi penyedia layanan']); + // } $isAuth = Auth::guard('customer')->login($user); if ($isAuth) { @@ -111,10 +112,11 @@ class AuthController extends Controller $customer->update(['google_oauth_response' => json_encode($user)]); } - if ($customer->status == Customer::STATUS_SUSPEND) { - return redirect()->route('customer.login') - ->with('message', ['type' => 'error', 'message' => 'Akun anda telah disuspend, silahkan hubungi penyedia layanan']); - } + // Akun Suspend + // if ($customer->status == Customer::STATUS_SUSPEND) { + // return redirect()->route('customer.login') + // ->with('message', ['type' => 'error', 'message' => 'Akun anda telah disuspend, silahkan hubungi penyedia layanan']); + // } Auth::guard('customer')->loginUsingId($customer->id); diff --git a/app/Http/Controllers/CustomerController.php b/app/Http/Controllers/CustomerController.php index 8c066df..fc8bf0a 100644 --- a/app/Http/Controllers/CustomerController.php +++ b/app/Http/Controllers/CustomerController.php @@ -11,17 +11,41 @@ class CustomerController extends Controller { public function index(Request $request) { - $query = Customer::query()->with(['level'])->orderBy('updated_at', 'desc'); + $stats = [ + 'basic_count' => Customer::whereHas('level', fn ($q) => $q->where('key', CustomerLevel::BASIC))->count(), + 'silver_count' => Customer::whereHas('level', fn ($q) => $q->where('key', CustomerLevel::SILVER))->count(), + 'gold_count' => Customer::whereHas('level', fn ($q) => $q->where('key', CustomerLevel::GOLD))->count(), + 'platinum_count' => Customer::whereHas('level', fn ($q) => $q->where('key', CustomerLevel::PLATINUM))->count(), + ]; + + $query = Customer::query()->with(['level', 'paylater', 'locationFavorites']); if ($request->q != '') { - $query->where('name', 'like', "%$request->q%") - ->orWhere('fullname', 'like', "%$request->q%") - ->orWhere('email', 'like', "%$request->q%") - ->orWhere('phone', 'like', "%$request->q%"); + $query->where(function ($query) use ($request) { + $query->where('name', 'like', "%$request->q%") + ->orWhere('fullname', 'like', "%$request->q%") + ->orWhere('email', 'like', "%$request->q%") + ->orWhere('phone', 'like', "%$request->q%"); + }); + } + + if ($request->location_id != '') { + $query->whereHas('locationFavorites', fn ($q) => $q->where('id', $request->location_id)); + } + + if ($request->level_id != '') { + $query->where('customer_level_id', $request->level_id); + } + + if ($request->sortBy != '' && $request->sortRule != '') { + $query->orderBy($request->sortBy, $request->sortRule); + } else { + $query->orderBy('updated_at', 'desc'); } return inertia('Customer/Index', [ 'query' => $query->paginate(), + 'stats' => $stats, ]); } diff --git a/app/Http/Controllers/CustomerLevelController.php b/app/Http/Controllers/CustomerLevelController.php index 1bc80fc..fae6741 100644 --- a/app/Http/Controllers/CustomerLevelController.php +++ b/app/Http/Controllers/CustomerLevelController.php @@ -16,6 +16,13 @@ class CustomerLevelController extends Controller ]); } + public function edit(CustomerLevel $customerLevel) + { + return inertia('CustomerLevel/Form', [ + 'customer_level' => $customerLevel + ]); + } + public function update(Request $request, CustomerLevel $customerLevel) { $request->validate([ @@ -23,15 +30,24 @@ class CustomerLevelController extends Controller 'description' => 'nullable|string', 'min_amount' => 'required|numeric|min:0', 'max_amount' => 'required|numeric|min:0', + 'logo' => 'nullable|image', ]); + if ($request->hasFile('logo')) { + $file = $request->file('logo'); + $file->store('uploads', 'public'); + $customerLevel->logo = $file->hashName('uploads'); + } + $customerLevel->update([ 'name' => $request->name, 'description' => $request->description, 'min_amount' => $request->min_amount, 'max_amount' => $request->max_amount, + 'logo' => $customerLevel->logo, ]); - session()->flash('message', ['type' => 'success', 'message' => 'Item has beed updated']); + return redirect()->route('customer-level.index') + ->with('message', ['type' => 'success', 'message' => 'Item has beed updated']); } } diff --git a/app/Http/Controllers/SettingController.php b/app/Http/Controllers/SettingController.php index b5d42c3..0830d6f 100644 --- a/app/Http/Controllers/SettingController.php +++ b/app/Http/Controllers/SettingController.php @@ -61,7 +61,6 @@ class SettingController extends Controller ]); } - public function updatePayment(Request $request) { $request->validate([ @@ -90,4 +89,43 @@ class SettingController extends Controller session()->flash('message', ['type' => 'success', 'message' => 'Setting has beed saved']); } + + public function affilate() + { + $setting = Setting::all(); + + return inertia('Setting/Payment', [ + 'setting' => $setting, + 'midtrans_notification_url' => route('api.midtrans.notification'), + ]); + } + + public function updateAffilate(Request $request) + { + $request->validate([ + 'MIDTRANS_SERVER_KEY' => 'required|string', + 'MIDTRANS_CLIENT_KEY' => 'required|string', + 'MIDTRANS_MERCHANT_ID' => 'required|string', + 'MIDTRANS_ADMIN_FEE' => 'required|numeric', + 'MIDTRANS_ENABLED' => 'required|in:0,1', + 'midtrans_logo_file' => 'nullable|image', + ]); + + DB::beginTransaction(); + foreach ($request->except(['midtrans_logo_file']) as $key => $value) { + Setting::where('key', $key)->update(['value' => $value]); + } + + if ($request->hasFile('midtrans_logo_file')) { + $file = $request->file('midtrans_logo_file'); + $file->store('uploads', 'public'); + Setting::where('key', 'MIDTRANS_LOGO')->update(['value' => $file->hashName('uploads')]); + } + + Cache::flush(); + + DB::commit(); + + session()->flash('message', ['type' => 'success', 'message' => 'Setting has beed saved']); + } } diff --git a/app/Models/Customer.php b/app/Models/Customer.php index 93e5f89..a6e1b90 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -65,6 +65,7 @@ class Customer extends Authenticatable 'display_deposit', 'display_poin', 'display_phone', + 'paylater_remain', 'paylater_limit', 'is_allow_paylater', 'verification_status', @@ -160,7 +161,7 @@ class Customer extends Authenticatable }); } - public function paylaterLimit(): Attribute + public function paylaterRemain(): Attribute { return Attribute::make(get: function () { if ($this->is_allow_paylater) { @@ -171,6 +172,17 @@ class Customer extends Authenticatable }); } + public function paylaterLimit(): Attribute + { + return Attribute::make(get: function () { + if ($this->is_allow_paylater) { + return $this->paylater->limit; + } + + return ''; + }); + } + public function isAllowPaylater(): Attribute { return Attribute::make(get: function () { diff --git a/app/Models/CustomerLevel.php b/app/Models/CustomerLevel.php index a80f937..8e7a732 100644 --- a/app/Models/CustomerLevel.php +++ b/app/Models/CustomerLevel.php @@ -2,6 +2,8 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Casts\Attribute; + class CustomerLevel extends Model { const BASIC = 'basic'; @@ -25,11 +27,22 @@ class CustomerLevel extends Model 'name', 'description', 'key', + 'logo', 'min_amount', 'max_amount', 'max_loan', ]; + protected $appends = [ + 'logo_url', + ]; + + protected function logoUrl(): Attribute + { + return Attribute::make(get: function () { + return asset($this->logo); + }); + } public static function getByKey($key) { return CustomerLevel::where('key', $key)->first(); diff --git a/database/migrations/2023_05_24_130630_create_customer_levels_table.php b/database/migrations/2023_05_24_130630_create_customer_levels_table.php index 63e531b..97b3173 100644 --- a/database/migrations/2023_05_24_130630_create_customer_levels_table.php +++ b/database/migrations/2023_05_24_130630_create_customer_levels_table.php @@ -15,7 +15,8 @@ return new class extends Migration $table->ulid('id')->primary(); $table->string('name')->nullable(); - $table->string('description')->nullable(); + $table->string('logo')->nullable(); + $table->text('description')->nullable(); $table->string('key')->nullable(); $table->decimal('min_amount', 20, 2)->default(0); $table->decimal('max_amount', 20, 2)->default(0); diff --git a/database/seeders/InstallationSeed.php b/database/seeders/InstallationSeed.php index e6850d6..aec83af 100644 --- a/database/seeders/InstallationSeed.php +++ b/database/seeders/InstallationSeed.php @@ -51,10 +51,39 @@ class InstallationSeed extends Seeder public function customer_levels() { $levels = [ - ['name' => 'Basic', 'key' => 'basic', 'description' => '-', 'min_amount' => '100000', 'max_amount' => '500000'], - ['name' => 'Silver', 'key' => 'silver', 'description' => '-', 'min_amount' => '100000', 'max_amount' => '1000000'], - ['name' => 'Gold', 'key' => 'gold', 'description' => '-', 'min_amount' => '100000', 'max_amount' => '2000000'], - ['name' => 'Platinum', 'key' => 'platinum', 'description' => '-', 'min_amount' => '100000', 'max_amount' => '3000000'], + [ + 'name' => 'Basic', + 'key' => 'basic', + 'logo' => 'sample/basic.png', + 'description' => '-', + 'min_amount' => + '100000', + 'max_amount' => '500000' + ], + [ + 'name' => 'Silver', + 'key' => 'silver', + 'logo' => 'sample/silver.png', + 'description' => '-', + 'min_amount' => '100000', + 'max_amount' => '1000000' + ], + [ + 'name' => 'Gold', + 'key' => 'gold', + 'logo' => 'sample/gold.png', + 'description' => '-', + 'min_amount' => '100000', + 'max_amount' => '2000000' + ], + [ + 'name' => 'Platinum', + 'key' => 'platinum', + 'logo' => 'sample/platinum.png', + 'description' => '-', + 'min_amount' => '100000', + 'max_amount' => '3000000' + ], ]; foreach ($levels as $level) { diff --git a/database/seeders/PermissionSeeder.php b/database/seeders/PermissionSeeder.php index 965d250..f68faba 100644 --- a/database/seeders/PermissionSeeder.php +++ b/database/seeders/PermissionSeeder.php @@ -58,6 +58,8 @@ class PermissionSeeder extends Seeder ['id' => Str::ulid(), 'label' => 'View Customer Verification', 'name' => 'view-customer-verification'], ['id' => Str::ulid(), 'label' => 'View Setting', 'name' => 'view-setting'], + ['id' => Str::ulid(), 'label' => 'View Setting Payment Gatewat', 'name' => 'view-setting-payment-gateway'], + ['id' => Str::ulid(), 'label' => 'View Setting Affilate', 'name' => 'view-setting-affilate'], ['id' => Str::ulid(), 'label' => 'View Deposit', 'name' => 'view-deposit'], ['id' => Str::ulid(), 'label' => 'Update Deposit', 'name' => 'update-deposit'], diff --git a/public/sample/basic.png b/public/sample/basic.png new file mode 100644 index 0000000..2077443 Binary files /dev/null and b/public/sample/basic.png differ diff --git a/public/sample/gold.png b/public/sample/gold.png new file mode 100644 index 0000000..f11e5e9 Binary files /dev/null and b/public/sample/gold.png differ diff --git a/public/sample/platinum.png b/public/sample/platinum.png new file mode 100644 index 0000000..d5b6d2a Binary files /dev/null and b/public/sample/platinum.png differ diff --git a/public/sample/silver.png b/public/sample/silver.png new file mode 100644 index 0000000..f591051 Binary files /dev/null and b/public/sample/silver.png differ diff --git a/resources/js/Pages/Voucher/ThSortComponent.jsx b/resources/js/Components/ThSortComponent.jsx similarity index 100% rename from resources/js/Pages/Voucher/ThSortComponent.jsx rename to resources/js/Components/ThSortComponent.jsx diff --git a/resources/js/Customer/Index/Partials/BalanceBanner.jsx b/resources/js/Customer/Index/Partials/BalanceBanner.jsx index f8e38e6..e26ffac 100644 --- a/resources/js/Customer/Index/Partials/BalanceBanner.jsx +++ b/resources/js/Customer/Index/Partials/BalanceBanner.jsx @@ -10,7 +10,7 @@ export default function BalanceBanner({ user }) { onClick={() => router.get(route('customer.deposit.index'))} >
-
+
Saldo
diff --git a/resources/js/Layouts/Partials/routes.js b/resources/js/Layouts/Partials/routes.js index 1f58a94..d0e342b 100644 --- a/resources/js/Layouts/Partials/routes.js +++ b/resources/js/Layouts/Partials/routes.js @@ -8,11 +8,13 @@ import { HiOutlineCash, HiOutlineTable, HiCash, + HiArrowCircleUp, } from 'react-icons/hi' import { HiArchiveBox, HiBanknotes, HiCheckBadge, + HiCog8Tooth, HiCreditCard, HiCurrencyDollar, HiMap, @@ -111,15 +113,7 @@ export default [ icon: HiUser, items: [ { - name: 'Verifikasi', - show: true, - icon: HiCheckBadge, - route: route('customer-verification.index'), - active: 'customer-verification.*', - permission: 'view-customer-verification', - }, - { - name: 'Customer', + name: 'List', show: true, icon: HiUserCircle, route: route('customer.index'), @@ -127,13 +121,29 @@ export default [ permission: 'view-customer', }, { - name: 'Level', + name: 'Atur Level', show: true, - icon: HiUserCircle, + icon: HiArrowCircleUp, route: route('customer-level.index'), active: 'customer-level.*', permission: 'view-customer-level', }, + { + name: 'Atur Affilate', + show: true, + icon: HiCog8Tooth, + route: route('setting.affilate'), + active: 'setting.affilate', + permission: 'view-setting-affilate', + }, + { + name: 'Verifikasi', + show: true, + icon: HiCheckBadge, + route: route('customer-verification.index'), + active: 'customer-verification.*', + permission: 'view-customer-verification', + }, ], }, { @@ -175,10 +185,10 @@ export default [ { name: 'Payment Gateway', show: true, - icon: HiOutlineTable, + icon: HiCog8Tooth, route: route('setting.payment'), active: 'setting.payment', - permission: 'view-setting', + permission: 'view-setting-payment-gateway', }, { name: 'Cash / Setor Tunai', diff --git a/resources/js/Pages/Customer/Index.jsx b/resources/js/Pages/Customer/Index.jsx index e95477c..337e9ec 100644 --- a/resources/js/Pages/Customer/Index.jsx +++ b/resources/js/Pages/Customer/Index.jsx @@ -6,20 +6,33 @@ import { Button, Dropdown } from 'flowbite-react' import { HiPencil, HiTrash } from 'react-icons/hi' import { useModalState } from '@/hooks' +import { formatIDR, hasPermission } from '@/utils' import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' import Pagination from '@/Components/Pagination' import ModalConfirm from '@/Components/ModalConfirm' import SearchInput from '@/Components/SearchInput' -import { hasPermission } from '@/utils' +import LocationSelectionInput from '../Location/SelectionInput' +import LevelSelectionInput from '../CustomerLevel/SelectionInput' +import ThSort from '@/Components/ThSortComponent' export default function Customer(props) { const { query: { links, data }, + stats, auth, + _search, + _sortBy, + _sortOrder, } = props - const [search, setSearch] = useState('') - const preValue = usePrevious(search) + const [location, setLocation] = useState(null) + const [level, setLevel] = useState(null) + const [search, setSearch] = useState({ + q: _search, + sortBy: _sortBy, + sortOrder: _sortOrder, + }) + const preValue = usePrevious(`${search}${location}${level}`) const confirmModal = useModalState() @@ -34,19 +47,42 @@ export default function Customer(props) { } } - const params = { q: search } + const handleSearchChange = (e) => { + setSearch({ + ...search, + q: e.target.value, + }) + } + + const sort = (key, sort = null) => { + if (sort !== null) { + setSearch({ + ...search, + sortBy: key, + sortRule: sort, + }) + return + } + setSearch({ + ...search, + sortBy: key, + sortRule: search.sortRule == 'asc' ? 'desc' : 'asc', + }) + } + + const params = { q: search, location_id: location, level_id: level } useEffect(() => { if (preValue) { router.get( route(route().current()), - { q: search }, + { ...search, location_id: location, level_id: level }, { replace: true, preserveState: true, } ) } - }, [search]) + }, [search, location, level]) const canCreate = hasPermission(auth, 'create-customer') const canUpdate = hasPermission(auth, 'update-customer') @@ -56,8 +92,36 @@ export default function Customer(props) { -
-
+
+
+
+
+
Basic
+
+ {formatIDR(stats.basic_count)} Orang +
+
+
+
Silver
+
+ {formatIDR(stats.silver_count)} Orang +
+
+
+
Gold
+
+ {formatIDR(stats.gold_count)} Orang +
+
+
+
+ Platinum +
+
+ {formatIDR(stats.platinum_count)} Orang +
+
+
{canCreate && ( @@ -65,14 +129,36 @@ export default function Customer(props) { )} -
- setSearch(e.target.value)} - value={search} - /> +
+
+ +
+
+
+ + setLevel(id) + } + placeholder={'filter level'} + /> +
+
+ + setLocation(id) + } + placeholder={'filter lokasi'} + /> +
+
-
+
@@ -89,17 +175,31 @@ export default function Customer(props) { > Level + + Deposit + + + Poin + + + + + + + - - - @@ -119,7 +93,6 @@ export default function Info(props) { - ) } diff --git a/resources/js/Pages/CustomerLevel/SelectionInput.jsx b/resources/js/Pages/CustomerLevel/SelectionInput.jsx new file mode 100644 index 0000000..bddbc6b --- /dev/null +++ b/resources/js/Pages/CustomerLevel/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-level.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/Role/Index.jsx b/resources/js/Pages/Role/Index.jsx index e664580..dfffe16 100644 --- a/resources/js/Pages/Role/Index.jsx +++ b/resources/js/Pages/Role/Index.jsx @@ -116,7 +116,7 @@ export default function Product(props) { key={ user.id } - className="px-2 py-1 bg-blue-600 text-white border rounded-full border-b-blue-900" + className="px-2 py-1 bg-blue-600 text-white border rounded-full border-blue-900" onClick={() => router.visit( route( diff --git a/resources/js/Pages/Voucher/Index.jsx b/resources/js/Pages/Voucher/Index.jsx index c6efcb7..b749f3f 100644 --- a/resources/js/Pages/Voucher/Index.jsx +++ b/resources/js/Pages/Voucher/Index.jsx @@ -11,7 +11,7 @@ import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' import Pagination from '@/Components/Pagination' import ModalConfirm from '@/Components/ModalConfirm' import SearchInput from '@/Components/SearchInput' -import ThSort from './ThSortComponent' +import ThSort from '@/Components/ThSortComponent' import ModalFilter from './ModalFilter' import ModalDelete from './ModalDelete' diff --git a/routes/admin.php b/routes/admin.php index 06b5f94..b1497a0 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -101,7 +101,8 @@ Route::middleware(['http_secure_aware', 'inertia.admin']) // customer level Route::get('/customer-levels', [CustomerLevelController::class, 'index'])->name('customer-level.index'); - Route::put('/customer-levels/{customerLevel}', [CustomerLevelController::class, 'update'])->name('customer-level.update'); + Route::get('/customer-levels/{customerLevel}', [CustomerLevelController::class, 'edit'])->name('customer-level.edit'); + Route::post('/customer-levels/{customerLevel}', [CustomerLevelController::class, 'update'])->name('customer-level.update'); // verification Route::get('/customers-verifications', [VerificationController::class, 'index'])->name('customer-verification.index'); @@ -133,6 +134,8 @@ Route::middleware(['http_secure_aware', 'inertia.admin']) // setting Route::get('/payment-gateway', [SettingController::class, 'payment'])->name('setting.payment'); Route::post('/payment-gateway', [SettingController::class, 'updatePayment']); + Route::get('/affilate', [SettingController::class, 'affilate'])->name('setting.affilate'); + Route::post('/affilate', [SettingController::class, 'updateAffilate']); Route::get('/settings', [SettingController::class, 'index'])->name('setting.index'); Route::post('/settings', [SettingController::class, 'update'])->name('setting.update'); diff --git a/routes/api.php b/routes/api.php index 9f01be6..e73cc4d 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,6 +1,7 @@ name('api.role.index'); Route::get('/locations', [LocationController::class, 'index'])->name('api.location.index'); Route::get('/location-profiles', [LocationProfileController::class, 'index'])->name('api.location-profile.index'); Route::get('/customers', [CustomerController::class, 'index'])->name('api.customer.index'); +Route::get('/customer-levels', [CustomerLevelController::class, 'index'])->name('api.customer-level.index'); Route::get('/notifications/{notif?}', [NotificationController::class, 'update'])->name('api.notification.update'); // midtrans
- Deposit + Sisa Saldo Hutang - poin + Limit Hutang Referral Code + Lokasi + + Whatsapp + - {customer.name} + + {customer.name} + {customer.display_poin} + {formatIDR( + customer.paylater_remain + )} + + {formatIDR( + customer.paylater_limit + )} + {customer.referral_code} + {customer.location_favorites.map( + (location) => ( +
+ {location.name} +
+ ) + )} +
+ {customer.phone !== + null && ( + + +62{customer.phone} + + )} + import('@/Components/TinyMCE')) + +export default function Form(props) { + const { customer_level } = props + + const { data, setData, post, processing, errors } = useForm({ + name: '', + description: '', + min_amount: 0, + max_amount: 0, + logo: null, + logo_url: '', + }) + + const handleOnChange = (event) => { + setData( + event.target.name, + event.target.type === 'checkbox' + ? event.target.checked + ? 1 + : 0 + : event.target.value + ) + } + const handleSubmit = () => { + post(route('customer-level.update', customer_level.id)) + } + + useEffect(() => { + if (isEmpty(customer_level) === false) { + setData({ + name: customer_level.name, + description: customer_level.description, + min_amount: customer_level.min_amount, + max_amount: customer_level.max_amount, + logo_url: customer_level.logo_url, + }) + } + }, [customer_level]) + + return ( + + + +
+
+
+
Atur Level
+ + setData('logo', e.target.files[0])} + error={errors.logo} + preview={ + isEmpty(data.logo_url) === false && ( + preview + ) + } + /> +
+ Loading...
}> + { + setData( + 'description', + editor.getContent() + ) + }} + /> + +
+ +
+ +
+
+
+ +
+ ) +} diff --git a/resources/js/Pages/CustomerLevel/FormModal.jsx b/resources/js/Pages/CustomerLevel/FormModal.jsx deleted file mode 100644 index 3bcfe88..0000000 --- a/resources/js/Pages/CustomerLevel/FormModal.jsx +++ /dev/null @@ -1,104 +0,0 @@ -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: '', - min_amount: 0, - max_amount: 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 customerLevel = modalState.data - put(route('customer-level.update', customerLevel.id), { - onSuccess: () => handleClose(), - }) - } - - useEffect(() => { - const customerLevel = modalState.data - if (isEmpty(customerLevel) === false) { - setData({ - name: customerLevel.name, - description: customerLevel.description, - min_amount: customerLevel.min_amount, - max_amount: customerLevel.max_amount, - }) - return - } - }, [modalState]) - - return ( - - - - - -
- - -
-
- ) -} diff --git a/resources/js/Pages/CustomerLevel/Index.jsx b/resources/js/Pages/CustomerLevel/Index.jsx index cf73821..a30518d 100644 --- a/resources/js/Pages/CustomerLevel/Index.jsx +++ b/resources/js/Pages/CustomerLevel/Index.jsx @@ -1,13 +1,11 @@ import React from 'react' -import { Head } from '@inertiajs/react' -import { Dropdown } from 'flowbite-react' +import { Head, Link } from '@inertiajs/react' import { HiPencil } from 'react-icons/hi' import { useModalState } from '@/hooks' +import { formatIDR, hasPermission } from '@/utils' import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' import Pagination from '@/Components/Pagination' -import FormModal from './FormModal' -import { formatIDR, hasPermission } from '@/utils' export default function Info(props) { const { @@ -17,11 +15,6 @@ export default function Info(props) { const formModal = useModalState() - const toggleFormModal = (customerlevel = null) => { - formModal.setData(customerlevel) - formModal.toggle() - } - const canUpdate = hasPermission(auth, 'update-customer-level') return ( @@ -41,25 +34,13 @@ export default function Info(props) { scope="col" className="py-3 px-6" > - Name - -
- Description + Logo - Minimal Deposit - - Maximal Deposit + Name - {level.name} - - - {level.description} + logo alt - {formatIDR( - level.min_amount - )} - - {formatIDR( - level.max_amount - )} + {level.name} + {canUpdate && ( -
- toggleFormModal( - level - ) - } + href={route( + 'customer-level.edit', + level.id + )} >
Ubah
-
+ )}