Dashboard Client - Portal
This commit is contained in:
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -217,11 +217,56 @@ class NavigationSeeder extends Seeder
|
|||||||
'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'],
|
||||||
|
|||||||
@@ -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']
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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} />
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
279
frontend/client-portal/src/pages/Dashboard/Index_.tsx
Normal file
279
frontend/client-portal/src/pages/Dashboard/Index_.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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 }}>
|
||||||
|
than last month
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<ReactApexChart type="area" series={CHART_DATA} options={chartOptions} height={100} />
|
||||||
|
</RootStyle>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user