info integrated

dev
Aji Kamaludin 1 year ago
parent 38b3ba28d2
commit 55b8fc2fbd
No known key found for this signature in database
GPG Key ID: 19058F67F0083AD3

@ -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)

@ -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();

@ -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,
]);
}
}

@ -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')

@ -0,0 +1,55 @@
<?php
namespace App\Http\Controllers;
use App\Models\Info;
use Illuminate\Http\Request;
class InfoController extends Controller
{
public function index()
{
$query = Info::paginate();
return inertia('Info/Index', [
'query' => $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']);
}
}

@ -14,7 +14,7 @@ class Authenticate extends Middleware
*/
protected function redirectTo($request)
{
if (!$request->expectsJson()) {
if (! $request->expectsJson()) {
return route('customer.login');
}
}

@ -2,7 +2,6 @@
namespace App\Models;
class Account extends Model
{
protected $fillable = [

@ -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 () {

@ -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',
];
}

@ -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',

@ -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 = [

@ -19,7 +19,7 @@ class EventServiceProvider extends ServiceProvider
SendEmailVerificationNotification::class,
],
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
\SocialiteProviders\Google\GoogleExtendSocialite::class . '@handle',
\SocialiteProviders\Google\GoogleExtendSocialite::class.'@handle',
],
];

@ -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,
],

@ -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',
],
];

@ -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,
]);
}
}

@ -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) {

@ -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,

@ -29,12 +29,7 @@ const GuestBanner = () => {
)
}
export default function Index({ status }) {
const {
props: {
auth: { user },
},
} = usePage()
export default function Index({ auth: { user }, infos }) {
return (
<CustomerLayout>
<Head title="Home" />
@ -66,14 +61,13 @@ export default function Index({ status }) {
{/* info */}
<div className="w-full px-3">
{[1, 2].map((x) => (
{infos.map((info) => (
<div
className="p-4 mb-4 text-sm text-blue-800 rounded-lg bg-blue-50"
role="alert"
key={x}
key={info.id}
>
<span className="font-medium">Info! </span> Change a
few things up and try submitting again.
{info.title}
</div>
))}
</div>

@ -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),
})
}

@ -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 (
<Modal isOpen={modalState.isOpen} toggle={handleClose} title={'Info'}>
<FormInput
name="info"
value={data.info}
onChange={handleOnChange}
label="info"
error={errors.info}
/>
<div className="my-4">
<div className="mb-1 text-sm">Publish </div>
<select
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
onChange={handleOnChange}
value={+data.is_publish}
name="is_publish"
>
<option value={0}>No</option>
<option value={1}>Yes</option>
</select>
</div>
<div className="flex items-center">
<Button onClick={handleSubmit} processing={processing}>
Simpan
</Button>
<Button onClick={handleClose} type="secondary">
Batal
</Button>
</div>
</Modal>
)
}

@ -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 (
<AuthenticatedLayout
auth={props.auth}
errors={props.errors}
flash={props.flash}
page={'Info'}
action={''}
>
<Head title="Info" />
<div>
<div className="mx-auto sm:px-6 lg:px-8 ">
<div className="p-6 overflow-hidden shadow-sm sm:rounded-lg bg-gray-200 dark:bg-gray-800 space-y-4">
<div className="flex justify-between">
{canCreate && (
<Button
size="sm"
onClick={() => toggleFormModal()}
>
Tambah
</Button>
)}
</div>
<div className="overflow-auto">
<div>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 mb-4">
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th
scope="col"
className="py-3 px-6"
>
Info
</th>
<th
scope="col"
className="py-3 px-6"
>
Publish
</th>
<th
scope="col"
className="py-3 px-6"
/>
</tr>
</thead>
<tbody>
{data.map((info) => (
<tr
className="bg-white border-b dark:bg-gray-800 dark:border-gray-700"
key={info.id}
>
<td
scope="row"
className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white"
>
{info.title}
</td>
<td className="py-4 px-6">
{info.is_publish === 1
? 'Yes'
: 'No'}
</td>
<td className="py-4 px-6 flex justify-end">
<Dropdown
label={'Opsi'}
floatingArrow={true}
arrowIcon={true}
dismissOnClick={true}
size={'sm'}
>
{canUpdate && (
<Dropdown.Item
onClick={() =>
toggleFormModal(
info
)
}
>
<div className="flex space-x-1 items-center">
<HiPencil />
<div>
Ubah
</div>
</div>
</Dropdown.Item>
)}
{canDelete && (
<Dropdown.Item
onClick={() =>
handleDeleteClick(
info
)
}
>
<div className="flex space-x-1 items-center">
<HiTrash />
<div>
Hapus
</div>
</div>
</Dropdown.Item>
)}
</Dropdown>
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="w-full flex items-center justify-center">
<Pagination links={links} />
</div>
</div>
</div>
</div>
</div>
<ModalConfirm modalState={confirmModal} onConfirm={onDelete} />
<FormModal modalState={formModal} />
</AuthenticatedLayout>
)
}

@ -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) {
<div>
<div className="mx-auto sm:px-6 lg:px-8 ">
<div className="p-6 overflow-hidden shadow-sm sm:rounded-lg bg-gray-200 dark:bg-gray-800 space-y-4">
<div className='flex justify-between'>
<div className="flex justify-between">
{canCreate && (
<Button size="sm" onClick={() => toggleFormModal()}>Tambah</Button>
<Button
size="sm"
onClick={() => toggleFormModal()}
>
Tambah
</Button>
)}
<div className="flex items-center">
<SearchInput
onChange={e => setSearch(e.target.value)}
onChange={(e) => setSearch(e.target.value)}
value={search}
/>
</div>
</div>
<div className='overflow-auto'>
<div className="overflow-auto">
<div>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 mb-4">
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th scope="col" className="py-3 px-6">
<th
scope="col"
className="py-3 px-6"
>
Name
</th>
<th scope="col" className="py-3 px-6">
<th
scope="col"
className="py-3 px-6"
>
Role
</th>
<th scope="col" className="py-3 px-6"/>
<th
scope="col"
className="py-3 px-6"
/>
</tr>
</thead>
<tbody>
{data.map(user => (
<tr className="bg-white border-b dark:bg-gray-800 dark:border-gray-700" key={user.id}>
<td scope="row" className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white">
{data.map((user) => (
<tr
className="bg-white border-b dark:bg-gray-800 dark:border-gray-700"
key={user.id}
>
<td
scope="row"
className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white"
>
{user.name}
</td>
<td className="py-4 px-6">
{user.role === null ? 'System' : user.role?.name}
{user.role === null
? 'System'
: user.role?.name}
</td>
<td className="py-4 px-6 flex justify-end">
<Dropdown
label={"Opsi"}
label={'Opsi'}
floatingArrow={true}
arrowIcon={true}
dismissOnClick={true}
size={'sm'}
>
{canUpdate && (
<Dropdown.Item onClick={() => toggleFormModal(user)}>
<div className='flex space-x-1 items-center'>
<HiPencil/>
<div>Ubah</div>
<Dropdown.Item
onClick={() =>
toggleFormModal(
user
)
}
>
<div className="flex space-x-1 items-center">
<HiPencil />
<div>
Ubah
</div>
</div>
</Dropdown.Item>
)}
{canDelete && (
<Dropdown.Item onClick={() => handleDeleteClick(user)}>
<div className='flex space-x-1 items-center'>
<HiTrash/>
<div>Hapus</div>
<Dropdown.Item
onClick={() =>
handleDeleteClick(
user
)
}
>
<div className="flex space-x-1 items-center">
<HiTrash />
<div>
Hapus
</div>
</div>
</Dropdown.Item>
)}
@ -134,20 +175,15 @@ export default function Supplier(props) {
</tbody>
</table>
</div>
<div className='w-full flex items-center justify-center'>
<Pagination links={links} params={params}/>
<div className="w-full flex items-center justify-center">
<Pagination links={links} params={params} />
</div>
</div>
</div>
</div>
</div>
<ModalConfirm
modalState={confirmModal}
onConfirm={onDelete}
/>
<FormModal
modalState={formModal}
/>
<ModalConfirm modalState={confirmModal} onConfirm={onDelete} />
<FormModal modalState={formModal} />
</AuthenticatedLayout>
);
)
}

@ -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');
});
});

@ -1,9 +1,8 @@
<?php
use App\Http\Controllers\Customer\AuthController;
use App\Http\Controllers\Customer\ProfileController;
use App\Http\Controllers\Customer\HomeController;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Customer\ProfileController;
use Illuminate\Support\Facades\Route;
/*

Loading…
Cancel
Save