Dashboard Client - Portal

This commit is contained in:
ivan-sim
2024-06-18 08:51:33 +07:00
parent c886cfa801
commit 605fc72e89
9 changed files with 695 additions and 345 deletions

View File

@@ -21,6 +21,7 @@ use Modules\Client\Transformers\Dashboard\MemberEmployeeDataResources as Dashboa
use Box\Spout\Writer\Common\Creator\WriterEntityFactory; use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
use Modules\Client\Transformers\EmployeeData\UserProfile\DataMemberResource as EmployeeDataProfileMemberResource; use Modules\Client\Transformers\EmployeeData\UserProfile\DataMemberResource as EmployeeDataProfileMemberResource;
use Modules\Internal\Services\MemberEnrollmentService; use Modules\Internal\Services\MemberEnrollmentService;
use Illuminate\Support\Facades\DB;
class CorporateMemberController extends Controller class CorporateMemberController extends Controller
{ {
@@ -255,4 +256,25 @@ class CorporateMemberController extends Controller
return Helper::responseJson(DataServiceMonitoring::make($data)); return Helper::responseJson(DataServiceMonitoring::make($data));
} }
public function getDeposit($corporate_id)
{
$deposit = DB::table('corporate_policies')
->select('total_premi')
->where('corporate_id','=', $corporate_id)
->first();
$usage = DB::table('corporate_employees')
->join('request_logs', 'request_logs.member_id', '=', 'corporate_employees.member_id')
->join('request_log_benefits', 'request_log_benefits.request_log_id', '=', 'request_logs.id')
->where('corporate_employees.corporate_id', '=', $corporate_id)
->sum('request_log_benefits.amount_approved');
// Ganti dengan logika Anda untuk mendapatkan data deposit
$deposit = [
'deposit' => $deposit->total_premi,
'limit' => $deposit->total_premi - $usage,
'usage' => $usage
];
return response()->json($deposit);
}
} }

View File

@@ -18,6 +18,7 @@ use Modules\Internal\Http\Controllers\Api\FormulariumController;
use Modules\Internal\Http\Controllers\Api\FormulariumTemplateController; use Modules\Internal\Http\Controllers\Api\FormulariumTemplateController;
use Modules\Internal\Http\Controllers\Api\AuditTrailController; use Modules\Internal\Http\Controllers\Api\AuditTrailController;
use Modules\Internal\Http\Controllers\Api\CorporateController; use Modules\Internal\Http\Controllers\Api\CorporateController;
use Modules\Internal\Http\Controllers\Api\NavigationController;
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@@ -70,6 +71,7 @@ Route::prefix('client')->group(function () {
Route::get('corporate', [CorporateCurrentController::class, 'index']); Route::get('corporate', [CorporateCurrentController::class, 'index']);
Route::put('corporate-update', [CorporateCurrentController::class, 'update']); Route::put('corporate-update', [CorporateCurrentController::class, 'update']);
Route::get('get-deposits', [CorporateMemberController::class, 'getDeposit']);
}); });
Route::get('claims/{id}', [ClaimController::class, 'show']); Route::get('claims/{id}', [ClaimController::class, 'show']);
@@ -90,5 +92,8 @@ Route::prefix('client')->group(function () {
Route::get('audittrail/{corporate_id}', [AuditTrailController::class, 'index']); Route::get('audittrail/{corporate_id}', [AuditTrailController::class, 'index']);
Route::get('corporates/import-document-example/{document_type}', [CorporateController::class, 'importDocumentExample']); Route::get('corporates/import-document-example/{document_type}', [CorporateController::class, 'importDocumentExample']);
// Navigation
Route::get('navigations', [NavigationController::class, 'index']);
}); });
}); });

View File

@@ -27,12 +27,12 @@ class NavigationSeeder extends Seeder
'title' => 'DOCTORS & HOSPITALS', 'title' => 'DOCTORS & HOSPITALS',
'children' => [ 'children' => [
[ [
'title' => 'Doctors', 'title' => 'Doctors',
'path' => '/master/doctors', 'path' => '/master/doctors',
'permission' => 'doctor-list' 'permission' => 'doctor-list'
], ],
[ [
'title' => 'Hospitals', 'title' => 'Hospitals',
'path' => '/master/hospitals', 'path' => '/master/hospitals',
'permission' => 'hospital-list' 'permission' => 'hospital-list'
], ],
@@ -44,17 +44,17 @@ class NavigationSeeder extends Seeder
'title' => 'PHARMACY & DELIVERY MANAGEMENT', 'title' => 'PHARMACY & DELIVERY MANAGEMENT',
'children' => [ 'children' => [
[ [
'title' => 'Drug', 'title' => 'Drug',
'path' => '/master/drugs', 'path' => '/master/drugs',
'permission' => 'drug-list' 'permission' => 'drug-list'
], ],
[ [
'title' => 'Inventory', 'title' => 'Inventory',
'path' => '/inventory', 'path' => '/inventory',
'permission' => null 'permission' => null
], ],
[ [
'title' => 'Delivery Services', 'title' => 'Delivery Services',
'path' => '/delivery', 'path' => '/delivery',
'permission' => null 'permission' => null
], ],
@@ -67,23 +67,23 @@ class NavigationSeeder extends Seeder
'openWhen' => ['/corporates', '/formularium', '/diagnosis', '/hospitals'], 'openWhen' => ['/corporates', '/formularium', '/diagnosis', '/hospitals'],
'children' => [ 'children' => [
[ [
'title' => 'Corporate', 'title' => 'Corporate',
'path' => '/corporates', 'path' => '/corporates',
'permission' => 'corporate-list', 'permission' => 'corporate-list',
], ],
// ['title' => 'Corporate Create', 'path' => '/corporates/create'], // ['title' => 'Corporate Create', 'path' => '/corporates/create'],
[ [
'title' => 'Formularium', 'title' => 'Formularium',
'path' => '/master/formularium-template-v2', 'path' => '/master/formularium-template-v2',
'permission' => 'formularium-list', 'permission' => 'formularium-list',
], ],
[ [
'title' => 'Master ICD-10 Diagnosis', 'title' => 'Master ICD-10 Diagnosis',
'path' => '/master/diagnosis', 'path' => '/master/diagnosis',
'permission' => 'diagnosis-list', 'permission' => 'diagnosis-list',
], ],
[ [
'title' => 'Hospitals', 'title' => 'Hospitals',
'path' => '/hospitals', 'path' => '/hospitals',
'permission' => null, 'permission' => null,
], ],
@@ -92,13 +92,13 @@ class NavigationSeeder extends Seeder
], ],
// CLAIM REQUEST // CLAIM REQUEST
[ [
'title' => 'CLAIM REQUEST', 'title' => 'CLAIM REQUEST',
'path' => '/claim-requests', 'path' => '/claim-requests',
'permission' => 'claim-request-list' 'permission' => 'claim-request-list'
], ],
// CLAIM MANAGEMENT // CLAIM MANAGEMENT
[ [
'title' => 'CLAIM MANAGEMENT', 'title' => 'CLAIM MANAGEMENT',
'path' => '/claims', 'path' => '/claims',
'permission' => 'claim-management-list' 'permission' => 'claim-management-list'
], ],
@@ -107,13 +107,13 @@ class NavigationSeeder extends Seeder
'title' => 'CASE MANAGEMENT', 'title' => 'CASE MANAGEMENT',
'children' => [ 'children' => [
[ [
'title' => 'Daily Monitoring', 'title' => 'Daily Monitoring',
'path' => '/case_management/daily_monitoring', 'path' => '/case_management/daily_monitoring',
'permission' => 'daily-monitoring-list' 'permission' => 'daily-monitoring-list'
], ],
// ['title' => 'Laboratorium Result', 'path' => '/case_management/laboratorium_result'], // ['title' => 'Laboratorium Result', 'path' => '/case_management/laboratorium_result'],
[ [
'title' => 'Inpatient Monitoring', 'title' => 'Inpatient Monitoring',
'path' => '/case_management/inpatient_monitoring', 'path' => '/case_management/inpatient_monitoring',
'permission' => 'final-log-list' 'permission' => 'final-log-list'
], ],
@@ -125,13 +125,13 @@ class NavigationSeeder extends Seeder
'title' => 'CUSTOMER SERVICES', 'title' => 'CUSTOMER SERVICES',
'children' => [ 'children' => [
[ [
'title' => 'Request', 'title' => 'Request',
'path' => '/custormer-service/request', 'path' => '/custormer-service/request',
'permission' => 'request-log-list' 'permission' => 'request-log-list'
], ],
// ['title' => 'Membership', 'path' => '/cs-membership'], // ['title' => 'Membership', 'path' => '/cs-membership'],
[ [
'title' => 'Final LOG', 'title' => 'Final LOG',
'path' => '/custormer-service/final-log', 'path' => '/custormer-service/final-log',
'permission' => 'final-log-list' 'permission' => 'final-log-list'
], ],
@@ -143,33 +143,33 @@ class NavigationSeeder extends Seeder
'title' => 'REPORT', 'title' => 'REPORT',
'children' => [ 'children' => [
[ [
'title' => 'Files Provider', 'title' => 'Files Provider',
'path' => 'report/files-provider', 'path' => 'report/files-provider',
'permission' => 'report-files-provider-list' 'permission' => 'report-files-provider-list'
], ],
[ [
'title' => 'Letter of Guarantee', 'title' => 'Letter of Guarantee',
'path' => '/report/logs', 'path' => '/report/logs',
'permission' => 'report-log-list' 'permission' => 'report-log-list'
], ],
[ [
'title' => 'Appointment', 'title' => 'Appointment',
'path' => '/report/appointments', 'path' => '/report/appointments',
'permission' => 'report-appointment-list' 'permission' => 'report-appointment-list'
], ],
[ [
'title' => 'Live Chat', 'title' => 'Live Chat',
'path' => '/report/live-chat', 'path' => '/report/live-chat',
'permission' => 'report-livechat-list' 'permission' => 'report-livechat-list'
], ],
[ [
'title' => 'Linksehat Payment', 'title' => 'Linksehat Payment',
'path' => '/report/linksehat-payments', 'path' => '/report/linksehat-payments',
'permission' => 'report-livechat-payment' 'permission' => 'report-livechat-payment'
], ],
// ['title' => 'Prescription', 'path' => '/report/prescription'], // ['title' => 'Prescription', 'path' => '/report/prescription'],
[ [
'title' => 'Doctor Rating', 'title' => 'Doctor Rating',
'path' => '/report/doctorrating', 'path' => '/report/doctorrating',
'permission' => 'report-doctor-rating' 'permission' => 'report-doctor-rating'
], ],
@@ -181,7 +181,7 @@ class NavigationSeeder extends Seeder
'title' => 'MASTER', 'title' => 'MASTER',
'children' => [ 'children' => [
[ [
'title' => 'Diagnosis', 'title' => 'Diagnosis',
'path' => '/master/diagnosis', 'path' => '/master/diagnosis',
'permission' => 'diagnosis-list' 'permission' => 'diagnosis-list'
], ],
@@ -193,12 +193,12 @@ class NavigationSeeder extends Seeder
'title' => 'USER MANAGEMENT', 'title' => 'USER MANAGEMENT',
'children' => [ 'children' => [
[ [
'title' => 'User Role', 'title' => 'User Role',
'path' => '/user-role', 'path' => '/user-role',
'permission' => 'user-role-list' 'permission' => 'user-role-list'
], ],
[ [
'title' => 'User Access', 'title' => 'User Access',
'path' => '/user-access', 'path' => '/user-access',
'permission' => 'user-access-list' 'permission' => 'user-access-list'
], ],
@@ -207,21 +207,66 @@ class NavigationSeeder extends Seeder
], ],
// LINKING TOOLS // LINKING TOOLS
[ [
'title' => 'LINKING TOOLS', 'title' => 'LINKING TOOLS',
'path' => '/linking', 'path' => '/linking',
'permission' => 'linkking-list' 'permission' => 'linkking-list'
], ],
// E-PRESCRIPTION // E-PRESCRIPTION
[ [
'title' => 'E-PRESCRIPTION', 'title' => 'E-PRESCRIPTION',
'path' => '/e-prescription/live-chat', 'path' => '/e-prescription/live-chat',
'permission' => 'prescription-list' 'permission' => 'prescription-list'
], ],
####################### CLIENT PORTAL #########################
[
'title' => 'Dashboard',
'children' => [
[
'title' => 'Usage Dashboard',
'path' => '/dashboard',
'permission' => 'dashboard-list-client-portal'
],
],
'permission' => 'dashboard-client-portal'
],
[
'title' => 'Corporate',
'children' => [
[
'title' => 'Corporate',
'path' => '/corporate',
'permission' => 'corporate-list-client-portal'
],
[
'title' => 'Employee Data',
'path' => '/employee-data',
'permission' => 'employee-data-list-client-portal'
],
],
'permission' => 'corporate-client-portal'
],
[
'title' => 'Case Management',
'children' => [
[
'title' => 'Alarm Center',
'path' => '/alarm-center',
'permission' => 'alarm-center-list-client-portal'
],
[
'title' => 'Formularium',
'path' => '/master/formularium-template-v2',
'permission' => 'formularium-list-client-portal'
],
],
'permission' => 'case-management-client-portal'
],
]; ];
foreach ($menuItems as $menuItemData) { foreach ($menuItems as $menuItemData) {
$menuItem = Navigations::updateOrCreate([ $menuItem = Navigations::updateOrCreate([
'title' => $menuItemData['title'] 'title' => $menuItemData['title'],
'permission' => $menuItemData['permission']
], ],
[ [
'title' => $menuItemData['title'], 'title' => $menuItemData['title'],

View File

@@ -17,65 +17,86 @@ class PermissionTableSeeder extends Seeder
public function run() public function run()
{ {
$permissions = [ $permissions = [
'dashboard', [
'doctor-list', 'type' => 'web',
'doctor-create', 'datas' => [
'doctor-edit', 'dashboard',
'doctor-delete', 'doctor-list',
'hospital-list', 'doctor-create',
'hospital-create', 'doctor-edit',
'hospital-edit', 'doctor-delete',
'hospital-delete', 'hospital-list',
'drug-list', 'hospital-create',
'drug-create', 'hospital-edit',
'drug-edit', 'hospital-delete',
'drug-delete', 'drug-list',
'corporate-list', 'drug-create',
'corporate-create', 'drug-edit',
'corporate-edit', 'drug-delete',
'corporate-delete', 'corporate-list',
'formularium-list', 'corporate-create',
'formularium-create', 'corporate-edit',
'formularium-edit', 'corporate-delete',
'formularium-delete', 'formularium-list',
'diagnosis-list', 'formularium-create',
'diagnosis-create', 'formularium-edit',
'diagnosis-edit', 'formularium-delete',
'diagnosis-delete', 'diagnosis-list',
'claim-request-list', 'diagnosis-create',
'claim-request-create', 'diagnosis-edit',
'claim-request-edit', 'diagnosis-delete',
'claim-request-delete', 'claim-request-list',
'claim-management-list', 'claim-request-create',
'claim-management-create', 'claim-request-edit',
'claim-management-edit', 'claim-request-delete',
'claim-management-delete', 'claim-management-list',
'daily-monitoring-list', 'claim-management-create',
'request-log-list', 'claim-management-edit',
'request-log-create', 'claim-management-delete',
'request-log-edit', 'daily-monitoring-list',
'request-log-delete', 'request-log-list',
'final-log-list', 'request-log-create',
'final-log-create', 'request-log-edit',
'final-log-edit', 'request-log-delete',
'final-log-delete', 'final-log-list',
'report-files-provider-list', 'final-log-create',
'report-letter-of-guarante-list', 'final-log-edit',
'report-log-list', 'final-log-delete',
'report-appointment-list', 'report-files-provider-list',
'report-livechat-list', 'report-letter-of-guarante-list',
'report-livechat-payment', 'report-log-list',
'report-doctor-rating', 'report-appointment-list',
'user-role-list', 'report-livechat-list',
'user-access-list' 'report-livechat-payment',
'report-doctor-rating',
'user-role-list',
'user-access-list'
]
],
####################### CLIENT PORTAL #########################
[
'type' => 'client-portal',
'datas' => [
'dashboard-client-portal',
'dashboard-list-client-portal',
'corporate-list-client-portal',
'employee-data-list-client-portal',
'corporate-client-portal',
'alarm-center-list-client-portal',
'formularium-list-client-portal',
'case-management-client-portal'
]
]
]; ];
foreach ($permissions as $permission) { foreach ($permissions as $values) {
Permission::updateOrCreate(['name' => $permission], foreach ($values['datas'] as $value) {
[ Permission::updateOrCreate(['name' => $value],
'name' => $permission, [
'guard_name' => 'web' 'name' => $value,
]); 'guard_name' => $values['type']
]);
}
} }
} }
} }

View File

@@ -32,18 +32,16 @@ export default function NavSectionVertical({
<Box {...other}> <Box {...other}>
{navConfig.map((group, index) => ( {navConfig.map((group, index) => (
<List key={index} disablePadding sx={{ px: 2 }}> <List key={index} disablePadding sx={{ px: 2 }}>
{group.subheader && ( <ListSubheaderStyle
<ListSubheaderStyle key={index}
key={index} sx={{
sx={{ ...(isCollapse && {
...(isCollapse && { opacity: 0,
opacity: 0, }),
}), }}
}} >
> {group.subheader}
{group.subheader} </ListSubheaderStyle>
</ListSubheaderStyle>
)}
{group.items.map((list) => ( {group.items.map((list) => (
<NavListRoot key={list.title} list={list} isCollapse={isCollapse} /> <NavListRoot key={list.title} list={list} isCollapse={isCollapse} />

View File

@@ -1,4 +1,4 @@
import { useEffect } from 'react'; import { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
// @mui // @mui
import { styled, useTheme } from '@mui/material/styles'; import { styled, useTheme } from '@mui/material/styles';
@@ -15,9 +15,11 @@ import Logo from '../../../components/Logo';
import Scrollbar from '../../../components/Scrollbar'; import Scrollbar from '../../../components/Scrollbar';
import { NavSectionVertical } from '../../../components/nav-section'; import { NavSectionVertical } from '../../../components/nav-section';
// //
import navConfig from './NavConfig'; // import navConfig from './NavConfig';
import NavbarAccount from './NavbarAccount'; import NavbarAccount from './NavbarAccount';
import CollapseButton from './CollapseButton'; import CollapseButton from './CollapseButton';
import useAuth from '@/hooks/useAuth';
import axios from '@/utils/axios';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@@ -42,10 +44,54 @@ export default function NavbarVertical({ isOpenSidebar, onCloseSidebar }: Props)
const { pathname } = useLocation(); const { pathname } = useLocation();
const {user} = useAuth()
const isDesktop = useResponsive('up', 'lg'); const isDesktop = useResponsive('up', 'lg');
const { isCollapse, collapseClick, collapseHover, onToggleCollapse, onHoverEnter, onHoverLeave } = const { isCollapse, collapseClick, collapseHover, onToggleCollapse, onHoverEnter, onHoverLeave } =
useCollapseDrawer(); useCollapseDrawer();
const [navConfig, setNavConfig] = useState([]);
useEffect(() => {
const fetchNavConfig = async () => {
try {
const response = await axios.get('/navigations');
const data = response.data.items;
// Pastikan user dan user.permissions terdefinisi dan merupakan array
const userPermissions = user.user?.permissions?.map(permission => permission.name) || [];
// Fungsi untuk memeriksa apakah pengguna memiliki izin untuk item tertentu
const hasPermission = (permission) => {
return userPermissions.includes(permission);
};
// Filter data berdasarkan izin pengguna
const filteredNavConfig = data.map(section => {
if (section.children && section.children.length > 0) {
// Cek apakah ada satu atau lebih children yang memiliki izin
const filteredChildren = section.children.filter(child => hasPermission(child.permission));
if (filteredChildren.length > 0) {
return {
...section,
children: filteredChildren
};
} else {
return null; // Lewati bagian yang tidak memiliki children dengan izin
}
}
// Jika tidak ada children, cek izin untuk section itu sendiri
return hasPermission(section.permission) ? section : null;
}).filter(section => section !== null);
setNavConfig([{ items: filteredNavConfig }]);
} catch (error) {
console.error('Gagal mengambil konfigurasi navigasi:', error);
}
};
fetchNavConfig();
}, [user]);
useEffect(() => { useEffect(() => {
if (isOpenSidebar) { if (isOpenSidebar) {

View File

@@ -1,276 +1,130 @@
// @mui // @mui
import { Typography, Container, Grid, Button, SelectChangeEvent } from '@mui/material'; import { CardContent,Button, Container, Grid, styled, Typography, Card, Stack } from '@mui/material';
// hooks // hooks
import useSettings from '../../hooks/useSettings'; import useSettings from '../../hooks/useSettings';
// components // components
import Page from '../../components/Page'; import Page from '../../components/Page';
// theme
import { useContext, useEffect, useState } from 'react';
import axios from '../../utils/axios'; import axios from '../../utils/axios';
import { Stack } from '@mui/system'; import useAuth from '../../hooks/useAuth';
import SomethingUsage from '../../sections/dashboard/SomethingUsage';
import { fCurrency } from '../../utils/formatNumber';
import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet';
import TrendingUpIcon from '@mui/icons-material/TrendingUp';
import MonetizationOnIcon from '@mui/icons-material/MonetizationOn';
import { useContext, useEffect, useState } from 'react';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import Table from '../../components/Table';
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams } from 'react-router-dom';
import palette from '../../theme/palette';
export default function Index() { // ----------------------------------------------------------------------
export default function Dashboard() {
const { themeStretch } = useSettings(); const { themeStretch } = useSettings();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const controller = new AbortController();
const [memberData, setMemberData] = useState([]); const { logout } = useAuth();
/* -------------------------------------------------------------------------- */ const loadSomething = () => {
/* setting up for the table */ axios.get('/user')
/* -------------------------------------------------------------------------- */
const [isLoading, setIsLoading] = useState(true);
const loadings = {
isLoading: isLoading,
setIsLoading: setIsLoading,
}; };
/* ------------------------------ handle params ----------------------------- */ const Wallet = styled(AccountBalanceWalletIcon)(({ theme }) => ({
const [searchParams, setSearchParams] = useSearchParams(); color: 'orange',
const [appliedParams, setAppliedParams] = useState({}); marginRight: theme.spacing(1),
}));
const params = { const TrendUp = styled(TrendingUpIcon)(({ theme }) => ({
searchParams: searchParams, color: 'blue',
setSearchParams: setSearchParams, marginRight: theme.spacing(1),
appliedParams: appliedParams, }));
setAppliedParams: setAppliedParams,
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ handle order ------------------------------ */ const Monet = styled(MonetizationOnIcon)(({ theme }) => ({
const [order, setOrder] = useState<Order>('asc'); color: 'orange',
const [orderBy, setOrderBy] = useState('fullName'); marginRight: theme.spacing(1),
}));
const orders = { const DangerCard = styled(Card)(({ theme }) => ({
order: order, boxShadow: 'none',
setOrder: setOrder, padding: theme.spacing(3),
orderBy: orderBy, color: theme.palette.error.main,
setOrderBy: setOrderBy, backgroundColor: theme.palette.error.lighter,
}; }));
/* -------------------------------------------------------------------------- */
/* ---------------------------- handle pagination --------------------------- */ const SuccessCard = styled(Card)(({ theme }) => ({
const [page, setPage] = useState(0); boxShadow: 'none',
const [rowsPerPage, setRowsPerPage] = useState(10); padding: theme.spacing(3),
color: theme.palette.success.darker,
backgroundColor: theme.palette.success.lighter,
}));
const [paginationTable, setPaginationTable] = useState<PaginationTableProps>({ const DefaultCard = styled(Card)(({ theme }) => ({
current_page: 0, boxShadow: theme.shadows[3], // Menggunakan bayangan standar dari tema
from: 0, padding: theme.spacing(3),
last_page: 0, color: theme.palette.text.primary,
links: [], backgroundColor: theme.palette.background.paper, // Latar belakang putih
path: '', }));
per_page: 0, const { corporateValue } = useContext(UserCurrentCorporateContext);
to: 0,
total: 0,
});
const paginations = { const [depositData, setDepositData] = useState({ deposit: 0, limit: 0, usage: 0 });
page: page,
setPage: setPage,
rowsPerPage: rowsPerPage,
setRowsPerPage: setRowsPerPage,
paginationTable: paginationTable,
setPaginationTable: setPaginationTable,
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ handle search ----------------------------- */ useEffect(() => {
const [searchText, setSearchText] = useState(''); const fetchDepositData = async () => {
try {
const response = await axios.get(`${corporateValue}/get-deposits`);
setDepositData(response.data);
} catch (error) {
console.error('Failed to fetch deposit data:', error);
}
};
const handleSearchSubmit = async (event: React.FormEvent<HTMLFormElement>) => { fetchDepositData();
event.preventDefault(); }, [corporateValue]);
if (searchText === '') {
searchParams.delete('search');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]);
setAppliedParams(params);
}
};
const searchs = {
useSearchs: false,
searchText: searchText,
setSearchText: setSearchText,
handleSearchSubmit: handleSearchSubmit,
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ handle filter ----------------------------- */
const [divisionValue, setDivisionValue] = useState('all');
const [divisionData, setDivisionData] = useState([]);
const handleDivisionChange = (event: SelectChangeEvent) => {
setDivisionValue(event.target.value as string);
if (event.target.value === 'all') {
searchParams.delete('division');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([
...searchParams.entries(),
['division', event.target.value as string],
]);
setAppliedParams(params);
}
};
const filters = {
useFilter: true,
config: {
label: 'Division',
divisionValue: divisionValue,
divisionData: divisionData,
handleDivisionChange: handleDivisionChange,
},
};
/* -------------------------------------------------------------------------- */
/* -------------------------------- headCell -------------------------------- */
const headCells: HeadCell<never>[] = [
{
id: 'memberId',
align: 'left',
label: 'Member ID',
isSort: true,
},
{
id: 'fullName',
align: 'center',
label: 'Name',
isSort: true,
},
{
id: 'division',
align: 'center',
label: 'Divisi',
isSort: true,
},
{
id: 'status',
align: 'center',
label: 'Status',
isSort: true,
},
{
id: 'action',
align: 'right',
label: '',
isSort: false,
},
];
/* -------------------------------------------------------------------------- */
useEffect(() => {
(async () => {
try {
setIsLoading(true);
const parameters =
Object.keys(appliedParams).length !== 0
? appliedParams
: Object.fromEntries([
...searchParams.entries(),
['order', order],
['orderBy', orderBy],
]);
const [divisionResponse, membersResponse] = await Promise.all([
axios.get(`${corporateValue}/division`, { signal: controller.signal }),
axios.get(`${corporateValue}/members`, {
params: { ...parameters },
signal: controller.signal,
}),
]);
setSearchParams(parameters);
setDivisionData(divisionResponse.data);
setMemberData(
membersResponse.data.data.map((obj: any) => ({
...obj,
status:
obj.status === 1 ? (
<Button
sx={{
backgroundColor: 'rgba(84, 214, 44, 0.16)',
color: palette.dark.success.dark,
paddingY: 0,
'&:hover': {
backgroundColor: 'rgba(84, 214, 44, 0.32)',
color: palette.dark.success.darker,
},
}}
>
Active
</Button>
) : (
<Button
sx={{
backgroundColor: 'rgba(255, 72, 66, 0.16)',
color: palette.dark.error.dark,
paddingY: 0,
'&:hover': {
backgroundColor: 'rgba(255, 72, 66, 0.32)',
color: palette.dark.error.darker,
},
}}
>
Inactive
</Button>
),
}))
);
setPaginationTable(membersResponse.data);
setRowsPerPage(membersResponse.data.per_page);
if (searchParams.get('page')) {
// @ts-ignore
const currentPage = parseInt(searchParams.get('page')) - 1;
paginationTable.current_page = currentPage;
setPage(currentPage);
}
setIsLoading(false);
} catch (error: any) {
console.error('Error fetching data:', error.message);
}
})();
return () => {
controller.abort();
};
}, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]);
return ( return (
<Page title="Dashboard"> <Page title="Dashboard">
<Container maxWidth={themeStretch ? false : 'xl'}> <Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" justifyContent="space-between"> <Typography variant="h3" component="h1" paragraph>
<Typography variant="h3" component="h1" paragraph> Dashboard
Dashboard </Typography>
</Typography>
</Stack>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={12} lg={12} md={12}> <Grid item xs={4}>
<Table {/* <SomethingUsage /> */}
headCells={headCells} <DefaultCard>
rows={memberData} <CardContent>
orders={orders} <Stack direction="column" alignItems="flex-start" justifyContent="space-between" sx={{ mb: 0.6 }}>
paginations={paginations} <Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ width: '100%' }}>
loadings={loadings} <Typography variant='h4'>{fCurrency(depositData.deposit)}</Typography>
params={params} <Wallet />
searchs={searchs} </Stack>
filters={filters} <Typography variant='h6'>Deposit</Typography>
/> </Stack>
</CardContent>
</DefaultCard>
</Grid>
<Grid item xs={4}>
<DefaultCard>
<CardContent>
<Stack direction="column" alignItems="flex-start" justifyContent="space-between" sx={{ mb: 0.6 }}>
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ width: '100%' }}>
<Typography variant='h4'>{fCurrency(depositData.limit)}</Typography>
<TrendUp />
</Stack>
<Typography variant='h6'>Limit</Typography>
</Stack>
</CardContent>
</DefaultCard>
</Grid>
<Grid item xs={4}>
<DefaultCard>
<CardContent>
<Stack direction="column" alignItems="flex-start" justifyContent="space-between" sx={{ mb: 0.6 }}>
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ width: '100%' }}>
<Typography variant='h4'>{fCurrency(depositData.usage)}</Typography>
<Monet />
</Stack>
<Typography variant='h6'>This Year Usage</Typography>
</Stack>
</CardContent>
</DefaultCard>
</Grid> </Grid>
</Grid> </Grid>
</Container> </Container>

View File

@@ -0,0 +1,279 @@
// @mui
import { Typography, Container, Grid, Button, SelectChangeEvent } from '@mui/material';
// hooks
import useSettings from '../../hooks/useSettings';
// components
import Page from '../../components/Page';
// theme
import { useContext, useEffect, useState } from 'react';
import axios from '../../utils/axios';
import { Stack } from '@mui/system';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import Table from '../../components/Table';
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams } from 'react-router-dom';
import palette from '../../theme/palette';
export default function Index_() {
const { themeStretch } = useSettings();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const controller = new AbortController();
const [memberData, setMemberData] = useState([]);
/* -------------------------------------------------------------------------- */
/* setting up for the table */
/* -------------------------------------------------------------------------- */
const [isLoading, setIsLoading] = useState(true);
const loadings = {
isLoading: isLoading,
setIsLoading: setIsLoading,
};
/* ------------------------------ handle params ----------------------------- */
const [searchParams, setSearchParams] = useSearchParams();
const [appliedParams, setAppliedParams] = useState({});
const params = {
searchParams: searchParams,
setSearchParams: setSearchParams,
appliedParams: appliedParams,
setAppliedParams: setAppliedParams,
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ handle order ------------------------------ */
const [order, setOrder] = useState<Order>('asc');
const [orderBy, setOrderBy] = useState('fullName');
const orders = {
order: order,
setOrder: setOrder,
orderBy: orderBy,
setOrderBy: setOrderBy,
};
/* -------------------------------------------------------------------------- */
/* ---------------------------- handle pagination --------------------------- */
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [paginationTable, setPaginationTable] = useState<PaginationTableProps>({
current_page: 0,
from: 0,
last_page: 0,
links: [],
path: '',
per_page: 0,
to: 0,
total: 0,
});
const paginations = {
page: page,
setPage: setPage,
rowsPerPage: rowsPerPage,
setRowsPerPage: setRowsPerPage,
paginationTable: paginationTable,
setPaginationTable: setPaginationTable,
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ handle search ----------------------------- */
const [searchText, setSearchText] = useState('');
const handleSearchSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (searchText === '') {
searchParams.delete('search');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]);
setAppliedParams(params);
}
};
const searchs = {
useSearchs: false,
searchText: searchText,
setSearchText: setSearchText,
handleSearchSubmit: handleSearchSubmit,
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ handle filter ----------------------------- */
const [divisionValue, setDivisionValue] = useState('all');
const [divisionData, setDivisionData] = useState([]);
const handleDivisionChange = (event: SelectChangeEvent) => {
setDivisionValue(event.target.value as string);
if (event.target.value === 'all') {
searchParams.delete('division');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([
...searchParams.entries(),
['division', event.target.value as string],
]);
setAppliedParams(params);
}
};
const filters = {
useFilter: true,
config: {
label: 'Division',
divisionValue: divisionValue,
divisionData: divisionData,
handleDivisionChange: handleDivisionChange,
},
};
/* -------------------------------------------------------------------------- */
/* -------------------------------- headCell -------------------------------- */
const headCells: HeadCell<never>[] = [
{
id: 'memberId',
align: 'left',
label: 'Member ID',
isSort: true,
},
{
id: 'fullName',
align: 'center',
label: 'Name',
isSort: true,
},
{
id: 'division',
align: 'center',
label: 'Divisi',
isSort: true,
},
{
id: 'status',
align: 'center',
label: 'Status',
isSort: true,
},
{
id: 'action',
align: 'right',
label: '',
isSort: false,
},
];
/* -------------------------------------------------------------------------- */
useEffect(() => {
(async () => {
try {
setIsLoading(true);
const parameters =
Object.keys(appliedParams).length !== 0
? appliedParams
: Object.fromEntries([
...searchParams.entries(),
['order', order],
['orderBy', orderBy],
]);
const [divisionResponse, membersResponse] = await Promise.all([
axios.get(`${corporateValue}/division`, { signal: controller.signal }),
axios.get(`${corporateValue}/members`, {
params: { ...parameters },
signal: controller.signal,
}),
]);
setSearchParams(parameters);
setDivisionData(divisionResponse.data);
setMemberData(
membersResponse.data.data.map((obj: any) => ({
...obj,
status:
obj.status === 1 ? (
<Button
sx={{
backgroundColor: 'rgba(84, 214, 44, 0.16)',
color: palette.dark.success.dark,
paddingY: 0,
'&:hover': {
backgroundColor: 'rgba(84, 214, 44, 0.32)',
color: palette.dark.success.darker,
},
}}
>
Active
</Button>
) : (
<Button
sx={{
backgroundColor: 'rgba(255, 72, 66, 0.16)',
color: palette.dark.error.dark,
paddingY: 0,
'&:hover': {
backgroundColor: 'rgba(255, 72, 66, 0.32)',
color: palette.dark.error.darker,
},
}}
>
Inactive
</Button>
),
}))
);
setPaginationTable(membersResponse.data);
setRowsPerPage(membersResponse.data.per_page);
if (searchParams.get('page')) {
// @ts-ignore
const currentPage = parseInt(searchParams.get('page')) - 1;
paginationTable.current_page = currentPage;
setPage(currentPage);
}
setIsLoading(false);
} catch (error: any) {
console.error('Error fetching data:', error.message);
}
})();
return () => {
controller.abort();
};
}, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]);
return (
<Page title="Dashboard">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" justifyContent="space-between">
<Typography variant="h3" component="h1" paragraph>
Dashboard
</Typography>
</Stack>
<Grid container spacing={2}>
<Grid item xs={12} lg={12} md={12}>
<Table
headCells={headCells}
rows={memberData}
orders={orders}
paginations={paginations}
loadings={loadings}
params={params}
searchs={searchs}
filters={filters}
/>
</Grid>
</Grid>
</Container>
</Page>
);
}

View File

@@ -0,0 +1,80 @@
import merge from 'lodash/merge';
import ReactApexChart from 'react-apexcharts';
// @mui
import { styled } from '@mui/material/styles';
import { Card, Typography, Stack } from '@mui/material';
// utils
import { fCurrency, fPercent } from '../../utils/formatNumber';
// components
import Iconify from '../../components/Iconify';
import BaseOptionChart from '../../components/chart/BaseOptionChart';
// ----------------------------------------------------------------------
const RootStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: theme.spacing(3),
color: theme.palette.primary.darker,
backgroundColor: theme.palette.primary.lighter,
}));
// ----------------------------------------------------------------------
const INITIAL = 500000000
const TOTAL = 257907000;
const PERCENT = -3;
const CHART_DATA = [{ data: [100, 99, 99, 85, 74, 57, 54, 51] }];
export default function SomethingUsage() {
const chartOptions = merge(BaseOptionChart(), {
chart: { sparkline: { enabled: true } },
xaxis: { labels: { show: true } },
yaxis: { labels: { show: false } },
stroke: { width: 4 },
legend: { show: false },
grid: { show: false },
tooltip: {
marker: { show: false },
y: {
formatter: (seriesName: string) => (seriesName) + "%",
title: {
formatter: () => '',
},
},
},
fill: { gradient: { opacityFrom: 0, opacityTo: 0 } },
});
return (
<RootStyle>
<Stack direction="row" justifyContent="space-between" sx={{ mb: 3 }}>
<div>
<Typography variant="body2" component="span" sx={{ opacity: 0.72 }}>
{fCurrency(INITIAL)}
</Typography>
<Typography sx={{ typography: 'subtitle2' }}>Remaining Balance</Typography>
<Typography sx={{ typography: 'h3' }}>{fCurrency(TOTAL)}</Typography>
</div>
<div>
<Stack direction="row" alignItems="center" justifyContent="flex-end" sx={{ mb: 0.6 }}>
<Iconify
width={20}
height={20}
icon={PERCENT >= 0 ? 'eva:trending-up-fill' : 'eva:trending-down-fill'}
/>
<Typography variant="subtitle2" component="span" sx={{ ml: 0.5 }}>
{PERCENT > 0 && '+'}
{fPercent(PERCENT)}
</Typography>
</Stack>
<Typography variant="body2" component="span" sx={{ opacity: 0.72 }}>
&nbsp;than last month
</Typography>
</div>
</Stack>
<ReactApexChart type="area" series={CHART_DATA} options={chartOptions} height={100} />
</RootStyle>
);
}