diff --git a/Modules/Client/Http/Controllers/Api/CorporateMemberController.php b/Modules/Client/Http/Controllers/Api/CorporateMemberController.php index ceb75d10..dd0832f7 100644 --- a/Modules/Client/Http/Controllers/Api/CorporateMemberController.php +++ b/Modules/Client/Http/Controllers/Api/CorporateMemberController.php @@ -21,6 +21,7 @@ use Modules\Client\Transformers\Dashboard\MemberEmployeeDataResources as Dashboa use Box\Spout\Writer\Common\Creator\WriterEntityFactory; use Modules\Client\Transformers\EmployeeData\UserProfile\DataMemberResource as EmployeeDataProfileMemberResource; use Modules\Internal\Services\MemberEnrollmentService; +use Illuminate\Support\Facades\DB; class CorporateMemberController extends Controller { @@ -255,4 +256,25 @@ class CorporateMemberController extends Controller 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); + } } diff --git a/Modules/Client/Routes/api.php b/Modules/Client/Routes/api.php index 6fb49e19..724ce079 100644 --- a/Modules/Client/Routes/api.php +++ b/Modules/Client/Routes/api.php @@ -18,6 +18,7 @@ use Modules\Internal\Http\Controllers\Api\FormulariumController; use Modules\Internal\Http\Controllers\Api\FormulariumTemplateController; use Modules\Internal\Http\Controllers\Api\AuditTrailController; 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::put('corporate-update', [CorporateCurrentController::class, 'update']); + Route::get('get-deposits', [CorporateMemberController::class, 'getDeposit']); }); 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('corporates/import-document-example/{document_type}', [CorporateController::class, 'importDocumentExample']); + + // Navigation + Route::get('navigations', [NavigationController::class, 'index']); }); }); diff --git a/database/seeders/NavigationSeeder.php b/database/seeders/NavigationSeeder.php index d2627976..dca07167 100644 --- a/database/seeders/NavigationSeeder.php +++ b/database/seeders/NavigationSeeder.php @@ -27,12 +27,12 @@ class NavigationSeeder extends Seeder 'title' => 'DOCTORS & HOSPITALS', 'children' => [ [ - 'title' => 'Doctors', - 'path' => '/master/doctors', + 'title' => 'Doctors', + 'path' => '/master/doctors', 'permission' => 'doctor-list' ], [ - 'title' => 'Hospitals', + 'title' => 'Hospitals', 'path' => '/master/hospitals', 'permission' => 'hospital-list' ], @@ -44,17 +44,17 @@ class NavigationSeeder extends Seeder 'title' => 'PHARMACY & DELIVERY MANAGEMENT', 'children' => [ [ - 'title' => 'Drug', + 'title' => 'Drug', 'path' => '/master/drugs', 'permission' => 'drug-list' ], [ - 'title' => 'Inventory', + 'title' => 'Inventory', 'path' => '/inventory', 'permission' => null ], [ - 'title' => 'Delivery Services', + 'title' => 'Delivery Services', 'path' => '/delivery', 'permission' => null ], @@ -67,23 +67,23 @@ class NavigationSeeder extends Seeder 'openWhen' => ['/corporates', '/formularium', '/diagnosis', '/hospitals'], 'children' => [ [ - 'title' => 'Corporate', + 'title' => 'Corporate', 'path' => '/corporates', 'permission' => 'corporate-list', ], // ['title' => 'Corporate Create', 'path' => '/corporates/create'], [ - 'title' => 'Formularium', + 'title' => 'Formularium', 'path' => '/master/formularium-template-v2', 'permission' => 'formularium-list', ], [ - 'title' => 'Master ICD-10 Diagnosis', + 'title' => 'Master ICD-10 Diagnosis', 'path' => '/master/diagnosis', 'permission' => 'diagnosis-list', ], [ - 'title' => 'Hospitals', + 'title' => 'Hospitals', 'path' => '/hospitals', 'permission' => null, ], @@ -92,13 +92,13 @@ class NavigationSeeder extends Seeder ], // CLAIM REQUEST [ - 'title' => 'CLAIM REQUEST', + 'title' => 'CLAIM REQUEST', 'path' => '/claim-requests', 'permission' => 'claim-request-list' ], // CLAIM MANAGEMENT [ - 'title' => 'CLAIM MANAGEMENT', + 'title' => 'CLAIM MANAGEMENT', 'path' => '/claims', 'permission' => 'claim-management-list' ], @@ -107,13 +107,13 @@ class NavigationSeeder extends Seeder 'title' => 'CASE MANAGEMENT', 'children' => [ [ - 'title' => 'Daily Monitoring', + 'title' => 'Daily Monitoring', 'path' => '/case_management/daily_monitoring', 'permission' => 'daily-monitoring-list' ], // ['title' => 'Laboratorium Result', 'path' => '/case_management/laboratorium_result'], [ - 'title' => 'Inpatient Monitoring', + 'title' => 'Inpatient Monitoring', 'path' => '/case_management/inpatient_monitoring', 'permission' => 'final-log-list' ], @@ -125,13 +125,13 @@ class NavigationSeeder extends Seeder 'title' => 'CUSTOMER SERVICES', 'children' => [ [ - 'title' => 'Request', + 'title' => 'Request', 'path' => '/custormer-service/request', 'permission' => 'request-log-list' ], // ['title' => 'Membership', 'path' => '/cs-membership'], [ - 'title' => 'Final LOG', + 'title' => 'Final LOG', 'path' => '/custormer-service/final-log', 'permission' => 'final-log-list' ], @@ -143,33 +143,33 @@ class NavigationSeeder extends Seeder 'title' => 'REPORT', 'children' => [ [ - 'title' => 'Files Provider', + 'title' => 'Files Provider', 'path' => 'report/files-provider', 'permission' => 'report-files-provider-list' ], [ - 'title' => 'Letter of Guarantee', + 'title' => 'Letter of Guarantee', 'path' => '/report/logs', 'permission' => 'report-log-list' ], [ - 'title' => 'Appointment', + 'title' => 'Appointment', 'path' => '/report/appointments', 'permission' => 'report-appointment-list' ], [ - 'title' => 'Live Chat', + 'title' => 'Live Chat', 'path' => '/report/live-chat', 'permission' => 'report-livechat-list' ], [ - 'title' => 'Linksehat Payment', + 'title' => 'Linksehat Payment', 'path' => '/report/linksehat-payments', 'permission' => 'report-livechat-payment' ], // ['title' => 'Prescription', 'path' => '/report/prescription'], [ - 'title' => 'Doctor Rating', + 'title' => 'Doctor Rating', 'path' => '/report/doctorrating', 'permission' => 'report-doctor-rating' ], @@ -181,7 +181,7 @@ class NavigationSeeder extends Seeder 'title' => 'MASTER', 'children' => [ [ - 'title' => 'Diagnosis', + 'title' => 'Diagnosis', 'path' => '/master/diagnosis', 'permission' => 'diagnosis-list' ], @@ -193,12 +193,12 @@ class NavigationSeeder extends Seeder 'title' => 'USER MANAGEMENT', 'children' => [ [ - 'title' => 'User Role', + 'title' => 'User Role', 'path' => '/user-role', 'permission' => 'user-role-list' ], [ - 'title' => 'User Access', + 'title' => 'User Access', 'path' => '/user-access', 'permission' => 'user-access-list' ], @@ -207,21 +207,66 @@ class NavigationSeeder extends Seeder ], // LINKING TOOLS [ - 'title' => 'LINKING TOOLS', + 'title' => 'LINKING TOOLS', 'path' => '/linking', 'permission' => 'linkking-list' ], // E-PRESCRIPTION [ - 'title' => 'E-PRESCRIPTION', + 'title' => 'E-PRESCRIPTION', 'path' => '/e-prescription/live-chat', '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) { $menuItem = Navigations::updateOrCreate([ - 'title' => $menuItemData['title'] + 'title' => $menuItemData['title'], + 'permission' => $menuItemData['permission'] ], [ 'title' => $menuItemData['title'], diff --git a/database/seeders/PermissionTableSeeder.php b/database/seeders/PermissionTableSeeder.php index a96d4894..bbcdcf89 100644 --- a/database/seeders/PermissionTableSeeder.php +++ b/database/seeders/PermissionTableSeeder.php @@ -17,65 +17,86 @@ class PermissionTableSeeder extends Seeder public function run() { $permissions = [ - 'dashboard', - 'doctor-list', - 'doctor-create', - 'doctor-edit', - 'doctor-delete', - 'hospital-list', - 'hospital-create', - 'hospital-edit', - 'hospital-delete', - 'drug-list', - 'drug-create', - 'drug-edit', - 'drug-delete', - 'corporate-list', - 'corporate-create', - 'corporate-edit', - 'corporate-delete', - 'formularium-list', - 'formularium-create', - 'formularium-edit', - 'formularium-delete', - 'diagnosis-list', - 'diagnosis-create', - 'diagnosis-edit', - 'diagnosis-delete', - 'claim-request-list', - 'claim-request-create', - 'claim-request-edit', - 'claim-request-delete', - 'claim-management-list', - 'claim-management-create', - 'claim-management-edit', - 'claim-management-delete', - 'daily-monitoring-list', - 'request-log-list', - 'request-log-create', - 'request-log-edit', - 'request-log-delete', - 'final-log-list', - 'final-log-create', - 'final-log-edit', - 'final-log-delete', - 'report-files-provider-list', - 'report-letter-of-guarante-list', - 'report-log-list', - 'report-appointment-list', - 'report-livechat-list', - 'report-livechat-payment', - 'report-doctor-rating', - 'user-role-list', - 'user-access-list' + [ + 'type' => 'web', + 'datas' => [ + 'dashboard', + 'doctor-list', + 'doctor-create', + 'doctor-edit', + 'doctor-delete', + 'hospital-list', + 'hospital-create', + 'hospital-edit', + 'hospital-delete', + 'drug-list', + 'drug-create', + 'drug-edit', + 'drug-delete', + 'corporate-list', + 'corporate-create', + 'corporate-edit', + 'corporate-delete', + 'formularium-list', + 'formularium-create', + 'formularium-edit', + 'formularium-delete', + 'diagnosis-list', + 'diagnosis-create', + 'diagnosis-edit', + 'diagnosis-delete', + 'claim-request-list', + 'claim-request-create', + 'claim-request-edit', + 'claim-request-delete', + 'claim-management-list', + 'claim-management-create', + 'claim-management-edit', + 'claim-management-delete', + 'daily-monitoring-list', + 'request-log-list', + 'request-log-create', + 'request-log-edit', + 'request-log-delete', + 'final-log-list', + 'final-log-create', + 'final-log-edit', + 'final-log-delete', + 'report-files-provider-list', + 'report-letter-of-guarante-list', + 'report-log-list', + 'report-appointment-list', + 'report-livechat-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) { - Permission::updateOrCreate(['name' => $permission], - [ - 'name' => $permission, - 'guard_name' => 'web' - ]); + foreach ($permissions as $values) { + foreach ($values['datas'] as $value) { + Permission::updateOrCreate(['name' => $value], + [ + 'name' => $value, + 'guard_name' => $values['type'] + ]); + } } } } diff --git a/frontend/client-portal/src/components/nav-section/vertical/index.tsx b/frontend/client-portal/src/components/nav-section/vertical/index.tsx index 41f0b915..de247933 100644 --- a/frontend/client-portal/src/components/nav-section/vertical/index.tsx +++ b/frontend/client-portal/src/components/nav-section/vertical/index.tsx @@ -32,18 +32,16 @@ export default function NavSectionVertical({ {navConfig.map((group, index) => ( - {group.subheader && ( - - {group.subheader} - - )} + + {group.subheader} + {group.items.map((list) => ( diff --git a/frontend/client-portal/src/layouts/dashboard/navbar/NavbarVertical.tsx b/frontend/client-portal/src/layouts/dashboard/navbar/NavbarVertical.tsx index f4002fa0..cd52fd1b 100644 --- a/frontend/client-portal/src/layouts/dashboard/navbar/NavbarVertical.tsx +++ b/frontend/client-portal/src/layouts/dashboard/navbar/NavbarVertical.tsx @@ -1,4 +1,4 @@ -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; // @mui import { styled, useTheme } from '@mui/material/styles'; @@ -15,9 +15,11 @@ import Logo from '../../../components/Logo'; import Scrollbar from '../../../components/Scrollbar'; import { NavSectionVertical } from '../../../components/nav-section'; // -import navConfig from './NavConfig'; +// import navConfig from './NavConfig'; import NavbarAccount from './NavbarAccount'; 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 {user} = useAuth() + const isDesktop = useResponsive('up', 'lg'); const { isCollapse, collapseClick, collapseHover, onToggleCollapse, onHoverEnter, onHoverLeave } = 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(() => { if (isOpenSidebar) { diff --git a/frontend/client-portal/src/pages/Dashboard/Index.tsx b/frontend/client-portal/src/pages/Dashboard/Index.tsx index ac304f66..8fe9f540 100644 --- a/frontend/client-portal/src/pages/Dashboard/Index.tsx +++ b/frontend/client-portal/src/pages/Dashboard/Index.tsx @@ -1,276 +1,130 @@ // @mui -import { Typography, Container, Grid, Button, SelectChangeEvent } from '@mui/material'; +import { CardContent,Button, Container, Grid, styled, Typography, Card, Stack } 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 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 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 { corporateValue } = useContext(UserCurrentCorporateContext); - const controller = new AbortController(); - const [memberData, setMemberData] = useState([]); + const { logout } = useAuth(); - /* -------------------------------------------------------------------------- */ - /* setting up for the table */ - /* -------------------------------------------------------------------------- */ - const [isLoading, setIsLoading] = useState(true); - - const loadings = { - isLoading: isLoading, - setIsLoading: setIsLoading, + const loadSomething = () => { + axios.get('/user') }; - /* ------------------------------ handle params ----------------------------- */ - const [searchParams, setSearchParams] = useSearchParams(); - const [appliedParams, setAppliedParams] = useState({}); + const Wallet = styled(AccountBalanceWalletIcon)(({ theme }) => ({ + color: 'orange', + marginRight: theme.spacing(1), + })); - const params = { - searchParams: searchParams, - setSearchParams: setSearchParams, - appliedParams: appliedParams, - setAppliedParams: setAppliedParams, - }; - /* -------------------------------------------------------------------------- */ + const TrendUp = styled(TrendingUpIcon)(({ theme }) => ({ + color: 'blue', + marginRight: theme.spacing(1), + })); - /* ------------------------------ handle order ------------------------------ */ - const [order, setOrder] = useState('asc'); - const [orderBy, setOrderBy] = useState('fullName'); + const Monet = styled(MonetizationOnIcon)(({ theme }) => ({ + color: 'orange', + marginRight: theme.spacing(1), + })); - const orders = { - order: order, - setOrder: setOrder, - orderBy: orderBy, - setOrderBy: setOrderBy, - }; - /* -------------------------------------------------------------------------- */ + const DangerCard = styled(Card)(({ theme }) => ({ + boxShadow: 'none', + padding: theme.spacing(3), + color: theme.palette.error.main, + backgroundColor: theme.palette.error.lighter, + })); - /* ---------------------------- handle pagination --------------------------- */ - const [page, setPage] = useState(0); - const [rowsPerPage, setRowsPerPage] = useState(10); + const SuccessCard = styled(Card)(({ theme }) => ({ + boxShadow: 'none', + padding: theme.spacing(3), + color: theme.palette.success.darker, + backgroundColor: theme.palette.success.lighter, + })); - const [paginationTable, setPaginationTable] = useState({ - current_page: 0, - from: 0, - last_page: 0, - links: [], - path: '', - per_page: 0, - to: 0, - total: 0, - }); + const DefaultCard = styled(Card)(({ theme }) => ({ + boxShadow: theme.shadows[3], // Menggunakan bayangan standar dari tema + padding: theme.spacing(3), + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.paper, // Latar belakang putih +})); +const { corporateValue } = useContext(UserCurrentCorporateContext); - const paginations = { - page: page, - setPage: setPage, - rowsPerPage: rowsPerPage, - setRowsPerPage: setRowsPerPage, - paginationTable: paginationTable, - setPaginationTable: setPaginationTable, - }; - /* -------------------------------------------------------------------------- */ +const [depositData, setDepositData] = useState({ deposit: 0, limit: 0, usage: 0 }); - /* ------------------------------ handle search ----------------------------- */ - const [searchText, setSearchText] = useState(''); + useEffect(() => { + 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) => { - 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[] = [ - { - 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 ? ( - - ) : ( - - ), - })) - ); - 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]); + fetchDepositData(); + }, [corporateValue]); return ( - - - Dashboard - - + + Dashboard + - - + + {/* */} + + + + + {fCurrency(depositData.deposit)} + + + Deposit + + + + + + + + + + {fCurrency(depositData.limit)} + + + Limit + + + + + + + + + + {fCurrency(depositData.usage)} + + + This Year Usage + + + diff --git a/frontend/client-portal/src/pages/Dashboard/Index_.tsx b/frontend/client-portal/src/pages/Dashboard/Index_.tsx new file mode 100644 index 00000000..3bf07784 --- /dev/null +++ b/frontend/client-portal/src/pages/Dashboard/Index_.tsx @@ -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('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({ + 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) => { + 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[] = [ + { + 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 ? ( + + ) : ( + + ), + })) + ); + 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 ( + + + + + Dashboard + + + + + +
+ + + + + ); +} diff --git a/frontend/client-portal/src/sections/dashboard/SomethingUsage.tsx b/frontend/client-portal/src/sections/dashboard/SomethingUsage.tsx new file mode 100644 index 00000000..4fbaafc3 --- /dev/null +++ b/frontend/client-portal/src/sections/dashboard/SomethingUsage.tsx @@ -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 ( + + +
+ + {fCurrency(INITIAL)} + + Remaining Balance + {fCurrency(TOTAL)} +
+ +
+ + = 0 ? 'eva:trending-up-fill' : 'eva:trending-down-fill'} + /> + + {PERCENT > 0 && '+'} + {fPercent(PERCENT)} + + + +  than last month + +
+
+ + +
+ ); +}