diff --git a/TODO.md b/TODO.md index cb4fb3d..a383b78 100644 --- a/TODO.md +++ b/TODO.md @@ -2,12 +2,12 @@ ### Admin +- [x] CRUD Info +- [ ] CRUD Banner +- [ ] CRUD Rekening / Account +- [ ] CRUD Customer - [ ] CRUD Voucher - [ ] Import Voucher -- [ ] CRUD Customer -- [ ] CRUD Rekening / Account -- [ ] CRUD Info -- [ ] CRUD Banner - [ ] Deposit Menu (view daftar histori deposit) - [ ] Manual Approve Deposit - [ ] Setting Web (enable affilate, amount bonus affilate) diff --git a/app/Http/Controllers/Customer/AuthController.php b/app/Http/Controllers/Customer/AuthController.php index eb24631..21c8261 100644 --- a/app/Http/Controllers/Customer/AuthController.php +++ b/app/Http/Controllers/Customer/AuthController.php @@ -15,6 +15,17 @@ use SocialiteProviders\Manager\Config; class AuthController extends Controller { + private $config; + + public function __construct() + { + $this->config = new Config( + env('GOOGLE_CLIENT_ID'), + env('GOOGLE_CLIENT_SECRET'), + route('customer.login.callback_google') + ); + } + public function login() { return inertia('Home/Auth/Login'); @@ -38,27 +49,22 @@ class AuthController extends Controller public function signin_google() { - $config = new Config( - env('GOOGLE_CLIENT_ID'), - env('GOOGLE_CLIENT_SECRET'), - route('customer.login.callback_google') - ); - return Socialite::driver('google') - ->setConfig($config) + ->setConfig($this->config) ->redirect(); } public function callback_google() { - $config = new Config( - env('GOOGLE_CLIENT_ID'), - env('GOOGLE_CLIENT_SECRET'), - route('customer.login.callback_google') - ); - $user = Socialite::driver('google') - ->setConfig($config) - ->user(); + try { + $user = Socialite::driver('google') + ->setConfig($this->config) + ->user(); + } catch (\Exception $e) { + return redirect()->route('customer.login') + ->with('message', ['type' => 'error', 'message' => 'something went wrong']); + } + $customer = Customer::where('google_id', $user->id)->first(); if ($customer == null) { DB::beginTransaction(); diff --git a/app/Http/Controllers/Customer/HomeController.php b/app/Http/Controllers/Customer/HomeController.php index f51b472..10756c3 100644 --- a/app/Http/Controllers/Customer/HomeController.php +++ b/app/Http/Controllers/Customer/HomeController.php @@ -3,11 +3,16 @@ namespace App\Http\Controllers\Customer; use App\Http\Controllers\Controller; +use App\Models\Info; class HomeController extends Controller { public function index() { - return inertia('Home/Index/Index'); + $infos = Info::where('is_publish', 1)->orderBy('updated_at', 'desc')->get(); + + return inertia('Home/Index/Index', [ + 'infos' => $infos, + ]); } } diff --git a/app/Http/Controllers/Customer/ProfileController.php b/app/Http/Controllers/Customer/ProfileController.php index 99e3966..704ba51 100644 --- a/app/Http/Controllers/Customer/ProfileController.php +++ b/app/Http/Controllers/Customer/ProfileController.php @@ -26,7 +26,7 @@ class ProfileController extends Controller 'name' => 'string|required', 'address' => 'string|required', 'phone' => 'string|required|numeric', - 'username' => 'string|required|min:5|alpha_dash|unique:customers,username,' . $customer->id, + 'username' => 'string|required|min:5|alpha_dash|unique:customers,username,'.$customer->id, 'password' => 'nullable|string|min:8|confirmed', 'image' => 'nullable|image', ]); @@ -48,7 +48,7 @@ class ProfileController extends Controller 'phone' => $request->phone, 'username' => $request->username, 'image' => $customer->image, - 'password' => $customer->password + 'password' => $customer->password, ]); redirect()->route('customer.profile.show') diff --git a/app/Http/Controllers/InfoController.php b/app/Http/Controllers/InfoController.php new file mode 100644 index 0000000..f13e6a0 --- /dev/null +++ b/app/Http/Controllers/InfoController.php @@ -0,0 +1,55 @@ + $query, + ]); + } + + public function store(Request $request) + { + $request->validate([ + 'info' => 'required|string', + 'is_publish' => 'required|in:0,1', + ]); + + Info::create([ + 'title' => $request->info, + 'is_publish' => $request->is_publish, + ]); + + session()->flash('message', ['type' => 'success', 'message' => 'Item has beed saved']); + } + + public function update(Request $request, Info $info) + { + $request->validate([ + 'info' => 'required|string', + 'is_publish' => 'required|in:0,1', + ]); + + $info->update([ + 'title' => $request->info, + 'is_publish' => $request->is_publish, + ]); + + session()->flash('message', ['type' => 'success', 'message' => 'Item has beed updated']); + } + + public function destroy(Info $info) + { + $info->delete(); + + session()->flash('message', ['type' => 'success', 'message' => 'Item has beed deleted']); + } +} diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index 07492de..a826c51 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -14,7 +14,7 @@ class Authenticate extends Middleware */ protected function redirectTo($request) { - if (!$request->expectsJson()) { + if (! $request->expectsJson()) { return route('customer.login'); } } diff --git a/app/Models/Account.php b/app/Models/Account.php index 6c85709..2143e73 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -2,7 +2,6 @@ namespace App\Models; - class Account extends Model { protected $fillable = [ diff --git a/app/Models/Customer.php b/app/Models/Customer.php index 617528b..bf70725 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -29,12 +29,12 @@ class Customer extends Authenticatable 'identity_verified', 'identity_image', 'customer_level_id', - 'google_oauth_response' + 'google_oauth_response', ]; protected $hidden = [ 'password', - 'google_oauth_reponse' + 'google_oauth_reponse', ]; protected $appends = [ @@ -49,6 +49,7 @@ class Customer extends Authenticatable get: function () { if ($this->google_id != null && $this->image == null) { $user = json_decode($this->google_oauth_response); + return $user?->avatar; } @@ -61,7 +62,6 @@ class Customer extends Authenticatable ); } - public function displayDeposit(): Attribute { return Attribute::make(get: function () { @@ -69,7 +69,6 @@ class Customer extends Authenticatable }); } - public function displayCoin(): Attribute { return Attribute::make(get: function () { diff --git a/app/Models/DepositHistory.php b/app/Models/DepositHistory.php index 0fbd67d..425c263 100644 --- a/app/Models/DepositHistory.php +++ b/app/Models/DepositHistory.php @@ -5,7 +5,9 @@ namespace App\Models; class DepositHistory extends Model { const VALID = 0; + const WAIT = 1; + const INVALID = 2; protected $fillable = [ @@ -16,6 +18,6 @@ class DepositHistory extends Model 'related_type', 'related_id', 'is_valid', - 'image_prove' + 'image_prove', ]; } diff --git a/app/Models/Info.php b/app/Models/Info.php index cf7afc3..dd8d54c 100644 --- a/app/Models/Info.php +++ b/app/Models/Info.php @@ -2,11 +2,10 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Model; - class Info extends Model { + const TYPE_URL = 'URL'; + protected $fillable = [ 'title', 'destination', diff --git a/app/Models/Sale.php b/app/Models/Sale.php index 585980c..0108872 100644 --- a/app/Models/Sale.php +++ b/app/Models/Sale.php @@ -5,8 +5,11 @@ namespace App\Models; class Sale extends Model { const PAYED_WITH_MIDTRANS = 'midtrans'; + const PAYED_WITH_MANUAL = 'manual'; + const PAYED_WITH_DEPOSIT = 'deposit'; + const PAYED_WITH_COIN = 'coin'; protected $fillable = [ diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index e35f00b..89bd599 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -19,7 +19,7 @@ class EventServiceProvider extends ServiceProvider SendEmailVerificationNotification::class, ], \SocialiteProviders\Manager\SocialiteWasCalled::class => [ - \SocialiteProviders\Google\GoogleExtendSocialite::class . '@handle', + \SocialiteProviders\Google\GoogleExtendSocialite::class.'@handle', ], ]; diff --git a/config/filesystems.php b/config/filesystems.php index f676a1d..5b8c281 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -39,7 +39,7 @@ return [ 'public' => [ 'driver' => 'local', 'root' => base_path('public'), - 'url' => env('APP_URL') . '/upload', + 'url' => env('APP_URL').'/upload', 'visibility' => 'public', 'throw' => false, ], diff --git a/config/services.php b/config/services.php index 7675b6a..adf0929 100644 --- a/config/services.php +++ b/config/services.php @@ -34,6 +34,6 @@ return [ 'google' => [ 'client_id' => env('GOOGLE_CLIENT_ID'), 'client_secret' => env('GOOGLE_CLIENT_SECRET'), - 'redirect' => env('APP_URL') . '/login/callback_google', + 'redirect' => env('APP_URL').'/login/callback_google', ], ]; diff --git a/database/seeders/DummySeeder.php b/database/seeders/DummySeeder.php index 96dceb3..7e028ae 100644 --- a/database/seeders/DummySeeder.php +++ b/database/seeders/DummySeeder.php @@ -2,6 +2,7 @@ namespace Database\Seeders; +use App\Models\Info; use Illuminate\Database\Seeder; class DummySeeder extends Seeder @@ -13,5 +14,14 @@ class DummySeeder extends Seeder */ public function run() { + $this->info(); + } + + public function info() + { + Info::create([ + 'title' => 'Welcome to our new site', + 'is_publish' => 1, + ]); } } diff --git a/database/seeders/InstallationSeed.php b/database/seeders/InstallationSeed.php index 8bf8e82..6d7ad00 100644 --- a/database/seeders/InstallationSeed.php +++ b/database/seeders/InstallationSeed.php @@ -27,7 +27,7 @@ class InstallationSeed extends Seeder ['key' => 'MIDTRANS_CLIENT_KEY', 'value' => 'SB-Mid-client-xqqkspzoZOM10iUG', 'type' => 'text'], ['key' => 'MIDTRANS_MERCHANT_ID', 'value' => 'G561244367', 'type' => 'text'], - // + // ]; foreach ($settings as $setting) { diff --git a/resources/js/Layouts/Partials/routes.js b/resources/js/Layouts/Partials/routes.js index 53f09b7..c393f96 100644 --- a/resources/js/Layouts/Partials/routes.js +++ b/resources/js/Layouts/Partials/routes.js @@ -1,4 +1,5 @@ import { HiChartPie, HiUser, HiUsers, HiUserGroup } from 'react-icons/hi' +import { HiQuestionMarkCircle } from 'react-icons/hi2' export default [ { @@ -9,6 +10,14 @@ export default [ active: 'dashboard', permission: 'view-dashboard', }, + { + name: 'Info', + show: true, + icon: HiQuestionMarkCircle, + route: route('info.index'), + active: 'info.index', + permission: 'view-info', + }, { name: 'User', show: true, diff --git a/resources/js/Pages/Home/Index/Index.jsx b/resources/js/Pages/Home/Index/Index.jsx index 6dfc507..efdd939 100644 --- a/resources/js/Pages/Home/Index/Index.jsx +++ b/resources/js/Pages/Home/Index/Index.jsx @@ -29,12 +29,7 @@ const GuestBanner = () => { ) } -export default function Index({ status }) { - const { - props: { - auth: { user }, - }, - } = usePage() +export default function Index({ auth: { user }, infos }) { return ( @@ -66,14 +61,13 @@ export default function Index({ status }) { {/* info */}
- {[1, 2].map((x) => ( + {infos.map((info) => (
- Info! Change a - few things up and try submitting again. + {info.title}
))}
diff --git a/resources/js/Pages/Home/Profile/Form.jsx b/resources/js/Pages/Home/Profile/Form.jsx index f4b8b83..a8716ce 100644 --- a/resources/js/Pages/Home/Profile/Form.jsx +++ b/resources/js/Pages/Home/Profile/Form.jsx @@ -37,7 +37,7 @@ export default function Index({ auth: { user }, flash }) { const handleSubmit = () => { post(route('customer.profile.show'), { onSuccess: () => - setTimeout(router.get(route(route().current())), 1000), + setTimeout(router.get(route(route().current())), 3000), }) } diff --git a/resources/js/Pages/Info/FormModal.jsx b/resources/js/Pages/Info/FormModal.jsx new file mode 100644 index 0000000..5cf4064 --- /dev/null +++ b/resources/js/Pages/Info/FormModal.jsx @@ -0,0 +1,95 @@ +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({ + info: '', + is_publish: 1, + }) + + 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 info = modalState.data + if (info !== null) { + put(route('info.update', info), { + onSuccess: () => handleClose(), + }) + return + } + post(route('info.store'), { + onSuccess: () => handleClose(), + }) + } + + useEffect(() => { + const info = modalState.data + if (isEmpty(info) === false) { + setData({ + info: info.info, + is_publish: info.is_publish, + }) + return + } + }, [modalState]) + + return ( + + +
+
Publish
+ +
+
+ + +
+
+ ) +} diff --git a/resources/js/Pages/Info/Index.jsx b/resources/js/Pages/Info/Index.jsx new file mode 100644 index 0000000..c9c6c60 --- /dev/null +++ b/resources/js/Pages/Info/Index.jsx @@ -0,0 +1,166 @@ +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 Info(props) { + const { + query: { links, data }, + auth, + } = props + + const confirmModal = useModalState() + const formModal = useModalState() + + const toggleFormModal = (info = null) => { + formModal.setData(info) + formModal.toggle() + } + + const handleDeleteClick = (info) => { + confirmModal.setData(info) + confirmModal.toggle() + } + + const onDelete = () => { + if (confirmModal.data !== null) { + router.delete(route('info.destroy', confirmModal.data.id)) + } + } + + const canCreate = hasPermission(auth, 'create-info') + const canUpdate = hasPermission(auth, 'update-info') + const canDelete = hasPermission(auth, 'delete-info') + + return ( + + + +
+
+
+
+ {canCreate && ( + + )} +
+
+
+ + + + + + + + + {data.map((info) => ( + + + + + + ))} + +
+ Info + + Publish + +
+ {info.title} + + {info.is_publish === 1 + ? 'Yes' + : 'No'} + + + {canUpdate && ( + + toggleFormModal( + info + ) + } + > +
+ +
+ Ubah +
+
+
+ )} + {canDelete && ( + + handleDeleteClick( + info + ) + } + > +
+ +
+ Hapus +
+
+
+ )} +
+
+
+
+ +
+
+
+
+
+ + +
+ ) +} diff --git a/resources/js/Pages/User/Index.jsx b/resources/js/Pages/User/Index.jsx index cdd6414..761bfb1 100644 --- a/resources/js/Pages/User/Index.jsx +++ b/resources/js/Pages/User/Index.jsx @@ -1,21 +1,24 @@ -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 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'; +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 User(props) { + const { + data: { links, data }, + auth, + } = props -export default function Supplier(props) { - const { data: { links, data }, auth } = props - const [search, setSearch] = useState('') const preValue = usePrevious(search) @@ -33,7 +36,7 @@ export default function Supplier(props) { } const onDelete = () => { - if(confirmModal.data !== null) { + if (confirmModal.data !== null) { router.delete(route('user.destroy', confirmModal.data.id)) } } @@ -69,61 +72,99 @@ export default function Supplier(props) {
-
+
{canCreate && ( - + )}
setSearch(e.target.value)} + onChange={(e) => setSearch(e.target.value)} value={search} />
-
+
- - - - {data.map(user => ( - - +
+ Name + Role +
+ {data.map((user) => ( +
{user.name} - {user.role === null ? 'System' : user.role?.name} + {user.role === null + ? 'System' + : user.role?.name} {canUpdate && ( - toggleFormModal(user)}> -
- -
Ubah
+ + toggleFormModal( + user + ) + } + > +
+ +
+ Ubah +
)} {canDelete && ( - handleDeleteClick(user)}> -
- -
Hapus
+ + handleDeleteClick( + user + ) + } + > +
+ +
+ Hapus +
)} @@ -134,20 +175,15 @@ export default function Supplier(props) {
-
- +
+
- - + + - ); + ) } diff --git a/routes/admin.php b/routes/admin.php index 1c16b1b..3f9e720 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -2,6 +2,7 @@ use App\Http\Controllers\Auth\AuthenticatedSessionController; use App\Http\Controllers\GeneralController; +use App\Http\Controllers\InfoController; use App\Http\Controllers\ProfileController; use App\Http\Controllers\RoleController; use App\Http\Controllers\UserController; @@ -40,5 +41,11 @@ Route::middleware(['http_secure_aware', 'inertia.admin']) // Role Route::resource('/roles', RoleController::class); + + // Info + Route::get('/infos', [InfoController::class, 'index'])->name('info.index'); + Route::post('/infos', [InfoController::class, 'store'])->name('info.store'); + Route::put('/infos/{info}', [InfoController::class, 'update'])->name('info.update'); + Route::delete('/infos/{info}', [InfoController::class, 'destroy'])->name('info.destroy'); }); }); diff --git a/routes/web.php b/routes/web.php index 345f87f..23d3ac8 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,9 +1,8 @@