add switch corporate & fix table & fix policy

This commit is contained in:
Muhammad Fajar
2022-12-08 08:51:25 +07:00
parent df34c3919d
commit 0c9362334c
17 changed files with 227 additions and 174 deletions

View File

@@ -7,7 +7,7 @@ use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
class CorporateController extends Controller
class CorporateManageController extends Controller
{
/**
* Display a listing of the resource.
@@ -18,8 +18,6 @@ class CorporateController extends Controller
$userLogin = Auth::user();
$corporate = $userLogin->managedCorporates()->select(['corporates.id', 'corporates.name'])->get();
// corporate policy, all member list, notification
return response()->json($corporate);
}

View File

@@ -8,7 +8,7 @@ use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
use Modules\Client\Transformers\DashboardResources;
class DashboardController extends Controller
class CorporatePolicyController extends Controller
{
/**
* Display a listing of the resource.
@@ -20,7 +20,7 @@ class DashboardController extends Controller
$currentCorporate = $user->managedCorporates()
->with(['currentPolicy', 'employees'])
->find($corporate_id);
$data = DashboardResources::make($currentCorporate);
return response()->json($data);

View File

@@ -15,17 +15,13 @@ class DivisionController extends Controller
* Display a listing of the resource.
* @return Renderable
*/
public function index(Request $request)
public function index(Request $request, $corporate_id)
{
$user = Auth::user();
$corporate = $user->managedCorporates()->where('active', 1)->first();
$benefits = CorporateDivision::query()
->where('corporate_id', $corporate->id)
$division = CorporateDivision::query()
->where('corporate_id', $corporate_id)
->get(['id', 'name']);
return $benefits;
return response()->json($division);
}
/**

View File

@@ -7,6 +7,7 @@ use App\Models\Member;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Modules\Client\Transformers\MemberResources;
class MemberController extends Controller
{
@@ -14,29 +15,25 @@ class MemberController extends Controller
* Display a listing of the resource.
* @return Renderable
*/
public function index(Request $request)
public function index(Request $request, $corporate_id)
{
$user = auth()->user();
$corporate = $user->managedCorporates()->first();
// $plans =
$limit = $request->has('per_page') ? $request->per_page : 10;
$members = Member::query()
->whereHas('employeds', function($corporateEmployee) use ($corporate) {
$corporateEmployee->where('corporate_id', $corporate->id);
});
if ($request->has('search')) {
$members
->where('member_id', 'like', "%" . $request->search . "%")
->orWhere('payor_id', 'like', "%" . $request->search . "%")
->orWhere('name', 'like', "%" . $request->search . "%");
}
->whereHas('employeds', function ($corporateEmployee) use ($corporate_id) {
$corporateEmployee->where('corporate_id', $corporate_id);
})->when($request->input('division'), function ($division, $division_id) {
$division->whereHas('division', function ($corporateEmployee) use ($division_id) {
$corporateEmployee->where('division_id', $division_id);
});
})->when($request->input('search'), function ($query, $search) {
$query->where('member_id', 'like', "%" . $search . "%")
->orWhere('name', 'like', "%" . $search . "%");
})->when($request->has('orderBy'), function ($query) use ($request) {
$query->orderBy($request->orderBy, $request->order);
})->paginate($limit);
$members = $members->paginate();
return response()->json([
'members' => Helper::paginateResources($members)
]);
return response()->json(Helper::paginateResources(MemberResources::collection($members)));
}
/**

View File

@@ -1,8 +1,8 @@
<?php
use Modules\Client\Http\Controllers\Api\AuthController;
use Modules\Client\Http\Controllers\Api\CorporateController;
use Modules\Client\Http\Controllers\Api\DashboardController;
use Modules\Client\Http\Controllers\Api\CorporateManageController;
use Modules\Client\Http\Controllers\Api\CorporatePolicyController;
use Modules\Client\Http\Controllers\Api\DivisionController;
use Modules\Client\Http\Controllers\Api\MemberController;
use Modules\Client\Http\Controllers\Api\UserController;
@@ -28,19 +28,13 @@ Route::prefix('client')->group(function () {
Route::middleware('auth:sanctum')->group(function () {
Route::post('logout', [AuthController::class, 'logout'])->name('logout');
Route::get('user', [UserController::class, 'index']);
Route::get('user', [UserController::class, 'index']);
Route::prefix('{corporate_id}')->group(function() {
Route::get('asd', function ($corporate_id) {
return $corporate_id;
Route::get('corporate-manage', [CorporateManageController::class, 'index']);
Route::prefix('{corporate_id}')->group(function () {
Route::get('policy', [CorporatePolicyController::class, 'index']);
Route::get('division', [DivisionController::class, 'index']);
Route::get('members', [MemberController::class, 'index']);
});
Route::get('dashboard', [DashboardController::class, 'index']);
Route::get('corporate', [CorporateController::class, 'index']);
Route::get('corporate/{corporate_id}', [CorporateController::class, 'show']);
Route::get('division', [DivisionController::class, 'index']);
Route::get('members', [MemberController::class, 'index']);
});
});
});

View File

@@ -16,9 +16,13 @@ class MemberResources extends JsonResource
{
return [
'memberId' => $this->member_id,
'full_name' => $this->full_name,
'fullName' => $this->full_name,
'division' => $this->division->name,
'employeeLimit' => '',
'limit' => [
'current' => 2000000,
'total' => 4000000,
'percentage' => (2000000 / 4000000) * 100
],
'status' => $this->active
];
}

View File

@@ -183,11 +183,6 @@ class Member extends Model
});
}
// public function corporateEmployee()
// {
// return $this->hasOne(CorporateEmployee::class, 'member_id');
// }
public function division()
{
return $this->hasOneThrough(CorporateDivision::class, CorporateEmployee::class, 'member_id', 'id', 'id', 'division_id');

View File

@@ -0,0 +1,8 @@
import { createContext } from 'react';
const UserCurrentCorporateContext = createContext({
corporateValue: '',
setCorporateValue: (value: string) => Promise<void>,
});
export { UserCurrentCorporateContext };

View File

@@ -0,0 +1,48 @@
import { FormControl, InputLabel, MenuItem, Select, SelectChangeEvent } from '@mui/material';
import { useContext, useEffect, useState } from 'react';
import { UserCurrentCorporateContext } from '../../../contexts/UserCurrentCorporate';
import axios from '../../../utils/axios';
/* ---------------------------------- types --------------------------------- */
type CorporateDataProps = {
id: number;
name: string;
};
/* -------------------------------------------------------------------------- */
export default function CorporatePopover() {
const { corporateValue, setCorporateValue } = useContext(UserCurrentCorporateContext);
const [corporateData, setCorporateData] = useState([]);
const handleCorporateChange = (event: SelectChangeEvent) => {
setCorporateValue(event.target.value as string);
};
useEffect(() => {
(async () => {
// @ts-ignore
const corporateManages = await axios.get(`/corporate-manage`);
setCorporateData(corporateManages.data);
})();
}, []);
return (
<FormControl>
<InputLabel id="simple-corporate-select-lable">Corporate</InputLabel>
<Select
labelId="simple-corporate-select-lable"
id="corporate-select-lable"
value={corporateValue}
label="Corporate"
defaultValue={corporateValue}
onChange={handleCorporateChange}
>
{corporateData.map((row: CorporateDataProps, index) => (
<MenuItem key={index} value={row.id}>
{row.name}
</MenuItem>
))}
</Select>
</FormControl>
);
}

View File

@@ -18,6 +18,7 @@ import AccountPopover from './AccountPopover';
import LanguagePopover from './LanguagePopover';
import ContactsPopover from './ContactsPopover';
import NotificationsPopover from './NotificationsPopover';
import CorporatePopover from './CorporatePopover';
// ----------------------------------------------------------------------
@@ -92,6 +93,7 @@ export default function DashboardHeader({
<Box sx={{ flexGrow: 1 }} />
<Stack direction="row" alignItems="center" spacing={{ xs: 0.5, sm: 1.5 }}>
<CorporatePopover />
<LanguagePopover />
<NotificationsPopover />
<ContactsPopover />

View File

@@ -13,6 +13,9 @@ import { HEADER, NAVBAR } from '../../config';
import DashboardHeader from './header';
import NavbarVertical from './navbar/NavbarVertical';
import NavbarHorizontal from './navbar/NavbarHorizontal';
import useLocalStorage from '../../hooks/useLocalStorage';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import useAuth from '../../hooks/useAuth';
// ----------------------------------------------------------------------
@@ -47,6 +50,7 @@ export default function DashboardLayout() {
const { collapseClick, isCollapse } = useCollapseDrawer();
const { themeLayout } = useSettings();
const { user } = useAuth();
const isDesktop = useResponsive('up', 'lg');
@@ -54,6 +58,12 @@ export default function DashboardLayout() {
const verticalLayout = themeLayout === 'vertical';
const [corporateValue, setCorporateValue] = useLocalStorage(
'corporateValue',
`${user.corporate.id}`
);
const value = { corporateValue, setCorporateValue };
if (verticalLayout) {
return (
<>
@@ -86,19 +96,21 @@ export default function DashboardLayout() {
}
return (
<Box
sx={{
display: { lg: 'flex' },
minHeight: { lg: 1 },
}}
>
<DashboardHeader isCollapse={isCollapse} onOpenSidebar={() => setOpen(true)} />
<UserCurrentCorporateContext.Provider value={value}>
<Box
sx={{
display: { lg: 'flex' },
minHeight: { lg: 1 },
}}
>
<DashboardHeader isCollapse={isCollapse} onOpenSidebar={() => setOpen(true)} />
<NavbarVertical isOpenSidebar={open} onCloseSidebar={() => setOpen(false)} />
<NavbarVertical isOpenSidebar={open} onCloseSidebar={() => setOpen(false)} />
<MainStyle collapseClick={collapseClick}>
<Outlet />
</MainStyle>
</Box>
<MainStyle collapseClick={collapseClick}>
<Outlet />
</MainStyle>
</Box>
</UserCurrentCorporateContext.Provider>
);
}

View File

@@ -1,13 +1,5 @@
// @mui
import {
Typography,
Container,
Grid,
FormControl,
InputLabel,
MenuItem,
Select,
} from '@mui/material';
import { Typography, Container, Grid } from '@mui/material';
// hooks
import useSettings from '../../hooks/useSettings';
// components
@@ -16,11 +8,10 @@ import Page from '../../components/Page';
import CardNotification from '../../sections/dashboard/CardNotification';
import CardBalance from '../../sections/dashboard/CardBalance';
import TableList from '../../sections/dashboard/TableList';
import { useEffect, useState } from 'react';
import { useContext, useEffect, useState } from 'react';
import axios from '../../utils/axios';
import { Stack } from '@mui/system';
import { SelectChangeEvent } from '@mui/material/Select';
import useAuth from '../../hooks/useAuth';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
// ----------------------------------------------------------------------
@@ -35,12 +26,7 @@ const itemList = [
/* ---------------------------------- types --------------------------------- */
type CorporateDataProps = {
id: number;
name: string;
};
type CardBalanceProps = {
type PolicyProps = {
myLimit: {
balance: number;
total: number;
@@ -54,42 +40,35 @@ type CardBalanceProps = {
/* -------------------------------------------------------------------------- */
/* ------------------------------ default data ------------------------------ */
const defaultPolicyData = {
myLimit: {
balance: 0,
total: 0,
percentage: 0,
},
lockLimit: {
balance: 0,
percentage: 0,
},
};
/* -------------------------------------------------------------------------- */
export default function Dashboard() {
const { themeStretch } = useSettings();
const { user } = useAuth();
const [currentCorporate, setCurrentCorporate] = useState({id: 1});
const { corporateValue } = useContext(UserCurrentCorporateContext);
// @ts-ignore
const [corporateValue, setCorporateValue] = useState(`${user.corporate.id}`);
const [corporateData, setCorporateData] = useState([]);
const [tableData, setTableData] = useState([]);
const [policyData, setPolicyData] = useState<CardBalanceProps>({
myLimit: {
balance: 0,
total: 0,
percentage: 0,
},
lockLimit: {
balance: 0,
percentage: 0,
},
});
const handleCorporateChange = (event: SelectChangeEvent) => {
setCorporateValue(event.target.value as string);
};
// const [tableData, setTableData] = useState([]);
const [policyData, setPolicyData] = useState<PolicyProps>(defaultPolicyData);
useEffect(() => {
(async () => {
const corporates = await axios.get(`${currentCorporate.id}/dashboard`);
const dashboard = await axios.get(`${currentCorporate.id}/dashboard`);
console.log(dashboard);
setCorporateData(corporates.data);
setPolicyData(defaultPolicyData);
await new Promise((resolve) => setTimeout(resolve, 250));
const dashboard = await axios.get(`${corporateValue}/policy`);
setPolicyData(dashboard.data.policy);
})();
}, [user, corporateValue]);
}, [corporateValue]);
return (
<Page title="Dashboard">
@@ -98,24 +77,6 @@ export default function Dashboard() {
<Typography variant="h3" component="h1" paragraph>
Dashboard
</Typography>
<FormControl>
<InputLabel id="simple-corporate-select-lable">Corporate</InputLabel>
<Select
labelId="simple-corporate-select-lable"
id="corporate-select-lable"
value={corporateValue}
label="Corporate"
defaultValue={corporateValue}
onChange={handleCorporateChange}
>
{corporateData.map((row: CorporateDataProps, index) => (
<MenuItem key={index} value={row.id}>
{row.name}
</MenuItem>
))}
</Select>
</FormControl>
</Stack>
<Grid container spacing={2}>

View File

@@ -83,11 +83,7 @@ export default function Login() {
</Box>
</Stack>
<VerifyCodeForm
setEmailOrPhoneForm={setEmailOrPhoneForm}
setLoginOrVerifyCode={setLoginOrVerifyCode}
emailOrPhone={emailOrPhone}
/>
<VerifyCodeForm emailOrPhone={emailOrPhone} />
<Stack sx={{ marginTop: 5 }} spacing={1} alignItems="center">
<Typography>Tidak mendapatkan kode?</Typography>

View File

@@ -17,8 +17,6 @@ import useAuth from '../../../hooks/useAuth';
type VerifyCodeFormProps = {
emailOrPhone: string;
setEmailOrPhoneForm: Function;
setLoginOrVerifyCode: Function;
};
type FormValuesProps = {
@@ -39,11 +37,7 @@ type responseProps = {
/* -------------------------------------------------------------------------- */
export default function VerifyCodeForm({
emailOrPhone,
setEmailOrPhoneForm,
setLoginOrVerifyCode,
}: VerifyCodeFormProps) {
export default function VerifyCodeForm({ emailOrPhone }: VerifyCodeFormProps) {
const navigate = useNavigate();
const { validateOtp } = useAuth();
const { enqueueSnackbar } = useSnackbar();

View File

@@ -13,7 +13,7 @@ import Iconify from '../../components/Iconify';
// React
import { useState } from 'react';
// utils
import { fCurrency } from '../../utils/formatNumber';
import { fCurrency, fSplit } from '../../utils/formatNumber';
/* -------------------------------- sections -------------------------------- */
import DialogTopUpLimit from './DialogTopUpLimit';
import DialogClaimSubmitMember from './DialogClaimSubmitMember';
@@ -97,7 +97,7 @@ export default function CardBalance(props: CardBalanceProps) {
{fCurrency(myLimit ? myLimit.balance : 0)}
</Typography>
<Typography sx={{ typography: 'caption', color: '#919EAB' }}>
/ {myLimit ? myLimit.total : 0}
/ {fSplit(myLimit ? myLimit.total : 0)}
</Typography>
</div>
@@ -127,7 +127,7 @@ export default function CardBalance(props: CardBalanceProps) {
</Typography>
</Typography>
<Typography sx={{ typography: 'caption', color: '#637381' }}>
{lockLimit ? lockLimit.balance : 0} / {myLimit ? myLimit.total : 0}
{fSplit(lockLimit ? lockLimit.balance : 0)} / {fSplit(myLimit ? myLimit.total : 0)}
</Typography>
</Stack>

View File

@@ -1,4 +1,5 @@
/* ---------------------------------- @mui ---------------------------------- */
import { styled } from '@mui/material/styles';
import {
Paper,
Table,
@@ -19,19 +20,25 @@ import {
Select,
MenuItem,
SelectChangeEvent,
Stack,
Typography,
LinearProgress,
linearProgressClasses,
} from '@mui/material';
import { visuallyHidden } from '@mui/utils';
import { Add as AddIcon } from '@mui/icons-material';
/* ---------------------------------- axios --------------------------------- */
import axios from '../../utils/axios';
/* ---------------------------------- react --------------------------------- */
import { useEffect, useRef, useState } from 'react';
import { useContext, useEffect, useRef, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
import BaseTablePagination from '../../components/BaseTablePagination';
/* ---------------------------------- theme --------------------------------- */
import palette from '../../theme/palette';
import { useSearchParams } from 'react-router-dom';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { fSplit } from '../../utils/formatNumber';
/* ---------------------------------- types --------------------------------- */
type PaginationTableProps = {
@@ -46,11 +53,15 @@ type PaginationTableProps = {
};
type DataTableProps = {
name: string;
member_id: string;
fullName: string;
memberId: string;
division: string;
limit: string;
status: string;
limit: {
current: number;
total: number;
percentage: number;
};
status: number;
};
type Order = 'asc' | 'desc';
@@ -74,6 +85,20 @@ type DivisionDataProps = {
};
/* -------------------------------------------------------------------------- */
/* --------------------------------- styled --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: '#D1F1F1',
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
backgroundColor: theme.palette.primary.main,
},
}));
/* -------------------------------------------------------------------------- */
/* -------------------------- enchanced table head -------------------------- */
const headCells: readonly HeadCell[] = [
{
@@ -92,16 +117,16 @@ const headCells: readonly HeadCell[] = [
id: 'division',
align: 'center',
label: 'Divisi',
isSort: true,
isSort: false,
},
{
id: 'limit',
align: 'center',
label: 'Limit',
isSort: true,
isSort: false,
},
{
id: 'status',
id: 'active',
align: 'center',
label: 'Status',
isSort: true,
@@ -150,14 +175,9 @@ function EnhancedTableHead({ order, orderBy, onRequestSort }: EnhancedTableProps
/* -------------------------------------------------------------------------- */
export default function TableList(props: any) {
const [order, setOrder] = useState<Order>('asc');
const [orderBy, setOrderBy] = useState('name');
const [searchParams, setSearchParams] = useSearchParams();
const [isLoading, setIsLoading] = useState(true);
const { corporateValue } = useContext(UserCurrentCorporateContext);
const [dataTable, setDataTable] = useState([]);
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [appliedParams, setAppliedParams] = useState({});
const [paginationTable, setPaginationTable] = useState<PaginationTableProps>({
current_page: 0,
from: 0,
@@ -169,6 +189,15 @@ export default function TableList(props: any) {
total: 0,
});
const [isLoading, setIsLoading] = useState(true);
const [searchParams, setSearchParams] = useSearchParams();
const [appliedParams, setAppliedParams] = useState({});
const [order, setOrder] = useState<Order>('asc');
const [orderBy, setOrderBy] = useState('name');
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
/* ------------------------------- handle sort ------------------------------ */
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
const isAsc = orderBy === property && order === 'asc';
@@ -211,9 +240,15 @@ export default function TableList(props: any) {
const handleSearchSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setIsLoading(true);
const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]);
if (searchText === '') {
searchParams.delete('search');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]);
setAppliedParams(params);
}
await new Promise((resolve) => setTimeout(resolve, 500));
setAppliedParams(params);
setIsLoading(false);
};
/* -------------------------------------------------------------------------- */
@@ -275,7 +310,7 @@ export default function TableList(props: any) {
(async () => {
setIsLoading(true);
const division = await axios.get('/division');
const division = await axios.get(`${corporateValue}/division`);
setDivisionData(division.data);
const params =
@@ -283,19 +318,17 @@ export default function TableList(props: any) {
? appliedParams
: Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]);
const response = await axios.get('/dashboard', {
params: params,
const corporateMembers = await axios.get(`${corporateValue}/members`, {
params,
});
console.log(response);
setSearchParams(params);
// setDataTable(response.data.data);
setDataTable(corporateMembers.data.data);
// setPaginationTable(response.data.meta);
// setRowsPerPage(response.data.meta.per_page);
setIsLoading(false);
})();
}, [appliedParams, searchParams, order, orderBy, setSearchParams]);
}, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]);
return (
<Card>
@@ -388,12 +421,23 @@ export default function TableList(props: any) {
) : dataTable.length >= 1 ? (
dataTable.map((row: DataTableProps, index) => (
<TableRow key={index}>
<TableCell align="left">{row.member_id}</TableCell>
<TableCell align="center">{row.name}</TableCell>
<TableCell align="left">{row.memberId}</TableCell>
<TableCell align="center">{row.fullName}</TableCell>
<TableCell align="center">{row.division}</TableCell>
<TableCell align="center">{row.limit}</TableCell>
<TableCell align="center" width={170}>
<Stack>
<BorderLinearProgress
variant="determinate"
value={row.limit.percentage}
sx={{ mb: 1 }}
/>
<Typography sx={{ typography: 'caption', color: '#637381' }}>
{fSplit(row.limit.current)} / {fSplit(row.limit.total)}
</Typography>
</Stack>
</TableCell>
<TableCell align="center">
{row.status.toLowerCase() === 'active' ? (
{row.status === 1 ? (
<Button
sx={{
backgroundColor: 'rgba(84, 214, 44, 0.16)',
@@ -405,7 +449,7 @@ export default function TableList(props: any) {
},
}}
>
{row.status}
Active
</Button>
) : (
<Button
@@ -419,7 +463,7 @@ export default function TableList(props: any) {
},
}}
>
{row.status}
Inactive
</Button>
)}
</TableCell>

View File

@@ -28,6 +28,10 @@ export function fCurrency(number: string | number) {
return numeral(number).format('$0,0');
}
export function fSplit(number: string | number) {
return numeral(number).format('0,0');
}
export function fPercent(number: number) {
return numeral(number / 100).format('0.0%');
}