Merge remote-tracking branch 'origin/staging' into origin/production

This commit is contained in:
Linksehat Staging Server
2023-12-25 09:43:39 +07:00
577 changed files with 64446 additions and 14205 deletions

View File

@@ -2,6 +2,8 @@ GENERATE_SOURCEMAP=false
PORT=8083
REACT_APP_HOST_API_URL="http://localhost:8000"
REACT_APP_HOST_API_URL="https://aso-api.linksehat.dev/api/client"
# VITE_API_URL="https://aso-api.linksehat.dev/api/client"
VITE_API_URL="http://localhost:8000/api/client"

View File

@@ -9,6 +9,7 @@
"lint:fix": "eslint --fix --ext .ts,.tsx ./src",
"start": "vite --port=3000",
"build": "vite build --mode production && cp .htaccess build/.htaccess && rm -f -r ../../public/client-portal && cp -r build ../../public/client-portal",
"build-staging": "vite build --mode staging && cp .htaccess build/.htaccess && rm -f -r ../../public/client-portal-staging && cp -r build ../../public/client-portal-staging",
"serve": "vite preview",
"clear-all": "rm -rf build node_modules",
"re-start": "rm -rf build node_modules && yarn install && yarn start",
@@ -37,6 +38,7 @@
]
},
"dependencies": {
"@ajoelp/json-to-formdata": "^1.5.0",
"@date-io/date-fns": "^2.16.0",
"@emotion/cache": "^11.10.5",
"@emotion/react": "^11.10.6",
@@ -50,6 +52,7 @@
"@mui/utils": "^5.11.13",
"@mui/x-data-grid": "^5.17.26",
"@mui/x-date-pickers": "5.0.0-beta.2",
"@reduxjs/toolkit": "^1.9.7",
"@vitejs/plugin-react": "^1.3.2",
"apexcharts": "^3.37.2",
"axios": "^0.27.2",
@@ -76,6 +79,7 @@
"react-lazy-load-image-component": "^1.5.6",
"react-number-format": "^5.1.4",
"react-quill": "2.0.0-beta.4",
"react-redux": "^8.1.3",
"react-router": "^6.9.0",
"react-router-dom": "^6.9.0",
"simplebar": "^5.3.9",

View File

@@ -1,6 +1,13 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies:
'@ajoelp/json-to-formdata':
specifier: ^1.5.0
version: 1.5.0
'@date-io/date-fns':
specifier: ^2.16.0
version: 2.16.0(date-fns@2.29.3)
@@ -40,6 +47,9 @@ dependencies:
'@mui/x-date-pickers':
specifier: 5.0.0-beta.2
version: 5.0.0-beta.2(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@mui/material@5.11.14)(@mui/system@5.11.14)(date-fns@2.29.3)(react-dom@17.0.2)(react@17.0.2)
'@reduxjs/toolkit':
specifier: ^1.9.7
version: 1.9.7(react-redux@8.1.3)(react@17.0.2)
'@vitejs/plugin-react':
specifier: ^1.3.2
version: 1.3.2
@@ -82,6 +92,9 @@ dependencies:
numeral:
specifier: ^2.0.6
version: 2.0.6
pnpm:
specifier: ^8.6.9
version: 8.8.0
pusher-js:
specifier: ^8.0.2
version: 8.0.2
@@ -115,6 +128,9 @@ dependencies:
react-quill:
specifier: 2.0.0-beta.4
version: 2.0.0-beta.4(react-dom@17.0.2)(react@17.0.2)
react-redux:
specifier: ^8.1.3
version: 8.1.3(@types/react-dom@17.0.19)(@types/react@17.0.53)(react-dom@17.0.2)(react@17.0.2)(redux@4.2.1)
react-router:
specifier: ^6.9.0
version: 6.9.0(react@17.0.2)
@@ -228,6 +244,12 @@ devDependencies:
packages:
/@ajoelp/json-to-formdata@1.5.0:
resolution: {integrity: sha512-nrlfeTSL0X0dtx5r2KpzPiqLSIQquiiJjUKsQAKzWaCmO2QoYZCyb5ENZwF3YoffKronOCJr25mxaD8JRJmK8w==}
dependencies:
lodash: 4.17.21
dev: false
/@ampproject/remapping@2.2.0:
resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==}
engines: {node: '>=6.0.0'}
@@ -1676,6 +1698,7 @@ packages:
/@emotion/memoize@0.7.4:
resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==}
requiresBuild: true
dev: false
optional: true
@@ -2319,6 +2342,25 @@ packages:
resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==}
dev: false
/@reduxjs/toolkit@1.9.7(react-redux@8.1.3)(react@17.0.2):
resolution: {integrity: sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==}
peerDependencies:
react: ^16.9.0 || ^17.0.0 || ^18
react-redux: ^7.2.1 || ^8.0.2
peerDependenciesMeta:
react:
optional: true
react-redux:
optional: true
dependencies:
immer: 9.0.21
react: 17.0.2
react-redux: 8.1.3(@types/react-dom@17.0.19)(@types/react@17.0.53)(react-dom@17.0.2)(react@17.0.2)(redux@4.2.1)
redux: 4.2.1
redux-thunk: 2.4.2(redux@4.2.1)
reselect: 4.1.8
dev: false
/@remix-run/router@1.4.0:
resolution: {integrity: sha512-BJ9SxXux8zAg991UmT8slpwpsd31K1dHHbD3Ba4VzD+liLQ4WAMSxQp2d2ZPRPfN0jN2NPRowcSSoM7lCaF08Q==}
engines: {node: '>=14'}
@@ -2555,6 +2597,13 @@ packages:
'@types/range-parser': 1.2.4
dev: false
/@types/hoist-non-react-statics@3.3.2:
resolution: {integrity: sha512-YIQtIg4PKr7ZyqNPZObpxfHsHEmuB8dXCxd6qVcGuQVDK2bpsF7bYNnBJ4Nn7giuACZg+WewExgrtAJ3XnA4Xw==}
dependencies:
'@types/react': 17.0.53
hoist-non-react-statics: 3.3.2
dev: false
/@types/json-schema@7.0.11:
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
dev: true
@@ -2602,7 +2651,6 @@ packages:
resolution: {integrity: sha512-PiYG40pnQRdPHnlf7tZnp0aQ6q9tspYr72vD61saO6zFCybLfMqwUCN0va1/P+86DXn18ZWeW30Bk7xlC5eEAQ==}
dependencies:
'@types/react': 17.0.53
dev: true
/@types/react-is@17.0.3:
resolution: {integrity: sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==}
@@ -2651,6 +2699,10 @@ packages:
resolution: {integrity: sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==}
dev: true
/@types/use-sync-external-store@0.0.3:
resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==}
dev: false
/@typescript-eslint/eslint-plugin@5.56.0(@typescript-eslint/parser@5.56.0)(eslint@8.36.0)(typescript@4.9.5):
resolution: {integrity: sha512-ZNW37Ccl3oMZkzxrYDUX4o7cnuPgU+YrcaYXzsRtLB16I1FR5SHMqga3zGsaSliZADCWo2v8qHWqAYIj8nWCCg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -4450,6 +4502,10 @@ packages:
engines: {node: '>= 4'}
dev: true
/immer@9.0.21:
resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==}
dev: false
/import-fresh@3.3.0:
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
engines: {node: '>=6'}
@@ -5102,6 +5158,12 @@ packages:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
/pnpm@8.8.0:
resolution: {integrity: sha512-eY5rMiZpzmPI2oVr1irR97bzb036oKwCWvK91wDQndXcqUPlytPtrF0bO668Syw/uA+7hTf5NnM8Mr4ux4BRRA==}
engines: {node: '>=16.14'}
hasBin: true
dev: false
/popmotion@11.0.3:
resolution: {integrity: sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==}
dependencies:
@@ -5318,6 +5380,40 @@ packages:
react-dom: 17.0.2(react@17.0.2)
dev: false
/react-redux@8.1.3(@types/react-dom@17.0.19)(@types/react@17.0.53)(react-dom@17.0.2)(react@17.0.2)(redux@4.2.1):
resolution: {integrity: sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==}
peerDependencies:
'@types/react': ^16.8 || ^17.0 || ^18.0
'@types/react-dom': ^16.8 || ^17.0 || ^18.0
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
react-native: '>=0.59'
redux: ^4 || ^5.0.0-beta.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
react-dom:
optional: true
react-native:
optional: true
redux:
optional: true
dependencies:
'@babel/runtime': 7.21.0
'@types/hoist-non-react-statics': 3.3.2
'@types/react': 17.0.53
'@types/react-dom': 17.0.19
'@types/use-sync-external-store': 0.0.3
hoist-non-react-statics: 3.3.2
react: 17.0.2
react-dom: 17.0.2(react@17.0.2)
react-is: 18.2.0
redux: 4.2.1
use-sync-external-store: 1.2.0(react@17.0.2)
dev: false
/react-refresh@0.13.0:
resolution: {integrity: sha512-XP8A9BT0CpRBD+NYLLeIhld/RqG9+gktUjW1FkE+Vm7OCinbG1SshcK5tb9ls4kzvjZr9mOQc7HYgBngEyPAXg==}
engines: {node: '>=0.10.0'}
@@ -5368,6 +5464,20 @@ packages:
object-assign: 4.1.1
dev: false
/redux-thunk@2.4.2(redux@4.2.1):
resolution: {integrity: sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==}
peerDependencies:
redux: ^4
dependencies:
redux: 4.2.1
dev: false
/redux@4.2.1:
resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==}
dependencies:
'@babel/runtime': 7.21.0
dev: false
/regenerate-unicode-properties@10.1.0:
resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==}
engines: {node: '>=4'}
@@ -5424,6 +5534,10 @@ packages:
resolution: {integrity: sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==}
dev: false
/reselect@4.1.8:
resolution: {integrity: sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==}
dev: false
/resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -5986,6 +6100,14 @@ packages:
punycode: 2.3.0
dev: true
/use-sync-external-store@1.2.0(react@17.0.2):
resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
react: 17.0.2
dev: false
/vite-plugin-pwa@0.12.8(vite@3.2.5)(workbox-build@6.5.4)(workbox-window@6.5.4):
resolution: {integrity: sha512-pSiFHmnJGMQJJL8aJzQ8SaraZBSBPMGvGUkCNzheIq9UQCEk/eP3UmANNmS9eupuhIpTK8AdxTOHcaMcAqAbCA==}
peerDependencies:

View File

@@ -0,0 +1,7 @@
<svg width="24" height="18" viewBox="0 0 24 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.45456 17.9116V8.68687L2.57878 6.06959L0 4.61719V16.2837C0 17.1845 0.733594 17.9116 1.63641 17.9116H5.45456Z" fill="#4285F4"/>
<path d="M18.5469 17.9115H22.365C23.2706 17.9115 24.0014 17.1817 24.0014 16.2836V4.61719L21.0806 6.28074L18.5469 8.68677V17.9115Z" fill="#34A853"/>
<path d="M5.45381 8.68695L5.0625 5.08256L5.45381 1.63281L11.9993 6.5165L18.5447 1.63281L18.9824 4.89631L18.5447 8.68695L11.9993 13.5706L5.45381 8.68695Z" fill="#EA4335"/>
<path d="M18.5469 1.63247V8.6866L24.0014 4.61693V2.44639C24.0014 0.433277 21.6914 -0.71434 20.0743 0.492966L18.5469 1.63247Z" fill="#FBBC04"/>
<path d="M0 4.61697L2.50866 6.48878L5.45456 8.68665V1.63251L3.92719 0.493009C2.30719 -0.71439 0 0.43332 0 2.44633V4.61687V4.61697Z" fill="#C5221F"/>
</svg>

After

Width:  |  Height:  |  Size: 852 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

View File

@@ -0,0 +1,8 @@
export type CardSubmit = {
rows?: Array<DataType>;
id: number,
name: string,
member_id: string,
usesage_limit: number,
limit: number
}

View File

@@ -0,0 +1,198 @@
// ----------------------------------------------------------------------
export type Corporate = {
id: number;
code: string;
name?: string;
reason?: string;
payor_id?: string;
welcome_message?: string;
automatic_linking?: boolean;
linking_rules?: string;
type?: string;
avatar_url?: string;
help_text?: string;
logo?: any;
logo_url?: string;
active: boolean | number;
divisions?: Division[];
employees?: Employee[];
current_policy?: Policy;
};
export type Division = {
id: number;
corporate_id: number;
code: string;
name?: string;
}
export type Employee = {
id: number;
name: string;
}
export type Policy = {
id: number;
corporate_id: number;
code: string;
total_premi: number;
minimal_deposit_percentage: number;
minimal_deposit_net: number;
minimal_alert_percentage: number;
minimal_alert_net: number;
minimal_stop_service_percentage: number;
minimal_stop_service_net: number;
start: string | Date;
end: string | Date;
}
export type CorporatePlan = {
id: number;
corporate_id: number;
code: string;
name: string;
description: string | null;
active: boolean | number;
}
export type Plan = {
id: number;
corporate_plan: CorporatePlan | null;
service_code: string;
corporate_plan_id: string;
code: string;
type: string;
start: string;
end: string;
require_referral: string;
referral_source: string;
referral_duration: string;
family_plan: string;
family_plan_share_rules: string;
limit_rules: string;
layer: string;
layer_conditions: string;
budget_type: string;
budget_code: string;
budget_conditions: string;
surgery_limit: string;
non_surgery_limit: string;
max_claim_limit: string;
max_claim_count: string;
area_limit: string;
limit_shared_plans: string;
limit_shared_plan_type: string;
cashless_percentage: string;
reimbursement_percentage: string;
digital_percentage: string;
co_share_m_percentage: string;
co_share_s_percentage: string;
co_share_c_percentage: string;
cashless_deductible: string;
reimbursement_deductible: string;
digital_deductible: string;
co_share_m_deductible: string;
co_share_s_deductible: string;
co_share_c_deductible: string;
co_share_deductible_condition: string;
msc: string;
genders: string;
min_age: string;
max_age: string;
rule_of_excess: string;
max_excess_covered: string;
prorate_type: string;
prorate_lookup: string;
currency: string;
max_surgery_reinstatement_days: string;
max_surgery_periode_days: string;
}
export type CorporateBenefit = {
id: number;
corporate_id: number;
code: string;
name: string;
description: string | null;
active: boolean | number;
}
export type Benefit = {
service_code : string;
plan_code : string;
benefit_code : string;
code : string;
description : string;
budget : string;
budget_conditions : string;
budget_code : string;
primary_benefit_code : string;
benefit_mode : string;
room_class_coverage : string;
max_bed_coverage : string;
tolerance_parameter : string;
max_room_class : string;
limit_amount : string;
area_limit : string;
shared_benefit : string;
shared_benefit_type : string;
msc : string;
genders : string;
min_age : string;
max_age : string;
max_frequency_period : string;
daily_frequency : string;
weekly_frequency : string;
monthly_frequency : string;
yearly_frequency : string;
custom_frequency_days : string;
custom_duration_value : string;
allowed_transaction_types : string;
high_plan_factor : string;
pre_post_treatment : string;
pre_treatment_days : string;
post_treatment_days : string;
layer_type_1 : string;
layer_value_1 : string;
layer_type_2 : string;
layer_value_2 : string;
cashless_percentage : string;
reimbursement_percentage : string;
digital_percentage : string;
co_share_m_percentage : string;
co_share_s_percentage : string;
co_share_c_percentage : string;
cashless_deductible : string;
reimbursement_deductible : string;
digital_deductible : string;
co_share_m_deductible : string;
co_share_s_deductible : string;
co_share_c_deductible : string;
prorate_type : string;
prorate_lookup : string;
max_days_for_disability : string;
max_period_for_disability : string;
currency : string;
show_benefit_item : string;
show_benefit_value : string;
}
export type CorporateService = {
id?: string | number;
corporate_id?: string | number;
description?: string;
name?: string;
service_code: string;
reason: string;
status: string;
configurations: any;
}
export type MasterExclusion = {
id?: string | number;
name?: string;
code: string;
description?: string;
}

View File

@@ -2,6 +2,7 @@ export type Icd = {
id: number;
type: string;
rev: string;
active: number;
version?: string;
code: string;
name: string;

View File

@@ -23,6 +23,11 @@ export type DivisionDataProps = {
id: number;
name: string;
};
export type StatusDataProps = {
id: number;
name: string;
};
/* -------------------------------------------------------------------------- */
/* -------------------------------- headcell -------------------------------- */
@@ -42,6 +47,13 @@ export type DivisionData = {
};
/* -------------------------------------------------------------------------- */
/* ----------------------------- status filter ---------------------------- */
export type Status = {
id: number;
name: string;
};
/* -------------------------------------------------------------------------- */
/* ----------------------------------- row ---------------------------------- */
export type TableListProps<DataType> = {
headCells?: HeadCell<DataType>[];
@@ -71,6 +83,7 @@ export type TableListProps<DataType> = {
setAppliedParams: Dispatch<SetStateAction<{}>>;
};
searchs: {
useSearchs: boolean;
searchText: string;
setSearchText: Dispatch<SetStateAction<string>>;
handleSearchSubmit: (event: FormEvent<HTMLFormElement>) => void;
@@ -84,5 +97,33 @@ export type TableListProps<DataType> = {
handleDivisionChange: (event: SelectChangeEvent) => void;
};
};
filterStatus?: {
useFilter: boolean;
config: {
label: string;
statusValue: string;
statusData: Status[];
handleStatusChange: (event: SelectChangeEvent) => void;
};
};
filterStartDate: {
useFilter: boolean;
startDate: string;
setStartDate: Dispatch<SetStateAction<string>>;
handleStartDateChange: (event: FormEvent<HTMLFormElement>) => void;
};
filterEndDate: {
useFilter: boolean;
endDate: string;
setEndDate: Dispatch<SetStateAction<string>>;
handleEndDateChange: (event: FormEvent<HTMLFormElement>) => void;
};
exportReport: {
useExport: boolean;
startDate: string;
endDate: string;
status: string;
handleExportReport: (event: FormEvent<HTMLFormElement>) => void;
};
};
/* -------------------------------------------------------------------------- */

View File

@@ -9,27 +9,31 @@ import { ProgressBarStyle } from './components/ProgressBar';
import ThemeColorPresets from './components/ThemeColorPresets';
import MotionLazyContainer from './components/animate/MotionLazyContainer';
import { SnackbarProvider } from 'notistack';
import { Provider } from 'react-redux';
import store from './store';
// ----------------------------------------------------------------------
export default function App() {
return (
<ThemeProvider>
<SnackbarProvider
autoHideDuration={2000}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
>
<ThemeColorPresets>
<RtlLayout>
<MotionLazyContainer>
<ProgressBarStyle />
{/* <Settings /> */}
<ScrollToTop />
<Router />
</MotionLazyContainer>
</RtlLayout>
</ThemeColorPresets>
</SnackbarProvider>
</ThemeProvider>
<Provider store={store}>
<ThemeProvider>
<SnackbarProvider
autoHideDuration={2000}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
>
<ThemeColorPresets>
<RtlLayout>
<MotionLazyContainer>
<ProgressBarStyle />
{/* <Settings /> */}
<ScrollToTop />
<Router />
</MotionLazyContainer>
</RtlLayout>
</ThemeColorPresets>
</SnackbarProvider>
</ThemeProvider>
</Provider>
);
}

View File

@@ -0,0 +1,274 @@
/* ---------------------------------- @mui ---------------------------------- */
import { styled } from '@mui/material/styles';
import {
Paper,
Table as TableContent,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Button,
TableSortLabel,
Box,
Card,
Grid,
FormControl,
InputLabel,
Select,
MenuItem,
SelectChangeEvent,
Stack,
Typography,
LinearProgress,
linearProgressClasses,
InputAdornment,
} from '@mui/material';
import { visuallyHidden } from '@mui/utils';
/* ---------------------------------- axios --------------------------------- */
import axios from '../utils/axios';
/* ---------------------------------- react --------------------------------- */
import { Fragment, useContext, useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
/* -------------------------------- component ------------------------------- */
import BaseTablePagination from './BaseTablePagination';
/* ---------------------------------- theme --------------------------------- */
import palette from '../theme/palette';
/* ---------------------------------- utils --------------------------------- */
import { UserCurrentCorporateContext } from '../contexts/UserCurrentCorporate';
import { fSplit } from '../utils/formatNumber';
/* ---------------------------------- types --------------------------------- */
import { DivisionDataProps, Order, PaginationTableProps, TableListProps } from '../@types/table';
/* ----------------------------------- icon --------------------------------- */
import SearchIcon from '@mui/icons-material/Search';
import { FormControlLabel } from '@mui/material';
import { Checkbox } from '@mui/material';
import HistoryRoundedIcon from '@mui/icons-material/HistoryRounded';
import KeyboardArrowRightRoundedIcon from '@mui/icons-material/KeyboardArrowRightRounded';
import { LoadingButton } from '@mui/lab';
/* --------------------------------- styled --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: '#D1F1F1',
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
backgroundColor: '#54D62C',
},
}));
/* -------------------------------------------------------------------------- */
export default function Table<T>({
rows,
loadings,
params,
searchs,
}: TableListProps<T>) {
/* ------------------------------ handle checkbox ----------------------------*/
const handleCheckboxChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
// Anda bisa menambahkan logika di sini
if (event.target.checked) {
// Checkbox dicentang
console.log('Checkbox dicentang');
// Tambahkan kode lain yang ingin Anda jalankan saat checkbox dicentang
} else {
// Checkbox tidak dicentang
console.log('Checkbox tidak dicentang');
// Tambahkan kode lain yang ingin Anda jalankan saat checkbox tidak dicentang
}
};
return (
// <Card>
<Grid container>
{/* Field 1 */}
<Grid item xs={12} paddingX="10px" paddingY="20px">
{searchs && searchs.useSearchs ? (
<Fragment>
<Grid item xs={12} lg={12} xl={12}>
<form onSubmit={searchs.handleSearchSubmit}>
<TextField
id="search-input"
variant="outlined"
onChange={(event) => searchs.setSearchText(event.target.value)}
value={searchs.searchText}
fullWidth
placeholder='Search Name or Member ID... '
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
/>
</form>
</Grid>
</Fragment>
) : null }
</Grid>
{/* End Field 1 */}
{/* Field 2 */}
<Grid item xs={12} paddingX="10px" paddingY="10px">
<Card>
<Fragment>
<Stack direction='row' alignItems='center'>
<Grid item xs={1} lg={1} xl={1} >
<form onSubmit={searchs.handleSearchSubmit}>
<FormControlLabel
value="end"
control={<Checkbox />}
label=""
labelPlacement="end"
sx={{marginLeft: '20px'}}
/>
</form>
</Grid>
<div style={{ position: 'relative', flex: 'none', height: 'fit-content', margin: '15px'}}>
<img
width={52}
height={52}
src="/images/user-profile.png"
alt="user-profile"
style={{ borderRadius: '50%' }}
/>
</div>
<Grid item xs={7} lg={7} xl={7} >
<Typography variant='subtitle1'>Alexandra Rhea Putranto</Typography>
<Typography color={'#637381'}>KM002-01</Typography>
</Grid>
<Grid item xs={3} lg={3} xl={3} >
<BorderLinearProgress
variant="determinate"
value={80}
// color='success'
sx={{ mb: 1 }}
/>
<Stack direction={'row'}>
<Grid item xs={3}>
<Typography variant='overline' sx={{textAlign:'left'}}>
LIMIT
</Typography>
</Grid>
<Grid item xs={7} sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<Stack direction={'row'}>
<Typography variant='overline'>
{fSplit(8000000)}
</Typography>
<Typography variant='overline'>
/ {fSplit(10000000000)}
</Typography>
</Stack>
</Grid>
</Stack>
</Grid>
<Grid item xs={2} lg={2} xl={2} style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<Stack direction={'row'}>
<HistoryRoundedIcon/>
<KeyboardArrowRightRoundedIcon
sx={{marginLeft: '30px'}}
/>
</Stack>
</Grid>
{/* <Grid item xs={1} lg={1} xl={1} style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<ArrowForwardIosRoundedIcon></ArrowForwardIosRoundedIcon>
</Grid> */}
</Stack>
</Fragment>
</Card>
</Grid>
<Grid item xs={12} paddingX="10px" paddingY="10px">
<Card>
<Fragment>
<Stack direction='row' alignItems='center'>
<Grid item xs={1} lg={1} xl={1} >
<form onSubmit={searchs.handleSearchSubmit}>
<FormControlLabel
value="end"
control={<Checkbox />}
label=""
labelPlacement="end"
sx={{marginLeft: '20px'}}
/>
</form>
</Grid>
<div style={{ position: 'relative', flex: 'none', height: 'fit-content', margin: '15px'}}>
<img
width={52}
height={52}
src="/images/user-profile.png"
alt="user-profile"
style={{ borderRadius: '50%' }}
/>
</div>
<Grid item xs={7} lg={7} xl={7} >
<Typography variant='subtitle1'>Alexandra Rhea Putranto</Typography>
<Typography color={'#637381'}>KM002-01</Typography>
</Grid>
<Grid item xs={3} lg={3} xl={3} >
<BorderLinearProgress
variant="determinate"
value={80}
// color='success'
sx={{ mb: 1 }}
/>
<Stack direction={'row'}>
<Grid item xs={5}>
<Typography variant='overline' sx={{textAlign:'left'}}>
LIMIT
</Typography>
</Grid>
<Grid item xs={7}>
<Stack direction={'row'}>
<Typography variant='overline' style={{ textAlign: 'right' }}>
{fSplit(8000000)}
</Typography>
<Typography variant='overline' style={{ textAlign: 'right' }}>
/ {fSplit(10000000000)}
</Typography>
</Stack>
</Grid>
</Stack>
</Grid>
<Grid item xs={2} lg={2} xl={2} style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<Stack direction={'row'}>
<HistoryRoundedIcon/>
<KeyboardArrowRightRoundedIcon
sx={{marginLeft: '30px'}}
/>
</Stack>
</Grid>
{/* <Grid item xs={1} lg={1} xl={1} style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<ArrowForwardIosRoundedIcon></ArrowForwardIosRoundedIcon>
</Grid> */}
</Stack>
</Fragment>
</Card>
</Grid>
{/* End Field 2 */}
<LoadingButton
variant="contained"
sx={{ marginTop: 2, p: 2, margin: '10px', color:'#212B36', backgroundColor:'#DFE3E8' }}
fullWidth
>
Claim Submit Selected
</LoadingButton>
</Grid>
// </Card>
);
}

View File

@@ -0,0 +1,98 @@
// @mui
import { alpha, Theme, useTheme, styled } from '@mui/material/styles';
import { BoxProps } from '@mui/material';
// theme
import { ColorSchema } from '../theme/palette';
// ----------------------------------------------------------------------
type LabelColor = 'default' | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error';
type LabelVariant = 'filled' | 'outlined' | 'ghost';
const RootStyle = styled('span')(
({
theme,
ownerState,
}: {
theme: Theme;
ownerState: {
color: LabelColor;
variant: LabelVariant;
};
}) => {
const isLight = theme.palette.mode === 'light';
const { color, variant } = ownerState;
const styleFilled = (color: ColorSchema) => ({
color: theme.palette[color].contrastText,
backgroundColor: theme.palette[color].main,
});
const styleOutlined = (color: ColorSchema) => ({
color: theme.palette[color].main,
backgroundColor: 'transparent',
border: `1px solid ${theme.palette[color].main}`,
});
const styleGhost = (color: ColorSchema) => ({
color: theme.palette[color][isLight ? 'dark' : 'light'],
backgroundColor: alpha(theme.palette[color].main, 0.16),
});
return {
height: 22,
minWidth: 22,
lineHeight: 0,
borderRadius: 6,
// cursor: 'default',
alignItems: 'center',
whiteSpace: 'nowrap',
display: 'inline-flex',
justifyContent: 'center',
padding: theme.spacing(0, 1),
color: theme.palette.grey[800],
fontSize: theme.typography.pxToRem(12),
fontFamily: theme.typography.fontFamily,
backgroundColor: theme.palette.grey[300],
fontWeight: theme.typography.fontWeightBold,
...(color !== 'default'
? {
...(variant === 'filled' && { ...styleFilled(color) }),
...(variant === 'outlined' && { ...styleOutlined(color) }),
...(variant === 'ghost' && { ...styleGhost(color) }),
}
: {
...(variant === 'outlined' && {
backgroundColor: 'transparent',
color: theme.palette.text.primary,
border: `1px solid ${theme.palette.grey[500_32]}`,
}),
...(variant === 'ghost' && {
color: isLight ? theme.palette.text.secondary : theme.palette.common.white,
backgroundColor: theme.palette.grey[500_16],
}),
}),
};
}
);
// ----------------------------------------------------------------------
interface Props extends BoxProps {
color?: LabelColor;
variant?: LabelVariant;
}
export default function Label({ color = 'default', variant = 'ghost', children, sx }: Props) {
const theme = useTheme();
return (
<RootStyle ownerState={{ color, variant }} sx={sx} theme={theme}>
{children}
</RootStyle>
);
}

View File

@@ -37,9 +37,11 @@ import palette from '../theme/palette';
/* ---------------------------------- utils --------------------------------- */
import { UserCurrentCorporateContext } from '../contexts/UserCurrentCorporate';
import { fSplit } from '../utils/formatNumber';
import { Download, Search as SearchIcon, Upload } from '@mui/icons-material';
/* ---------------------------------- types --------------------------------- */
import { DivisionDataProps, Order, PaginationTableProps, TableListProps } from '../@types/table';
import { InputAdornment } from '@mui/material';
import GetAppIcon from '@mui/icons-material/GetApp';
/* --------------------------------- styled --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
@@ -62,7 +64,11 @@ export default function Table<T>({
loadings,
params,
filters,
filterStatus,
filterStartDate,
filterEndDate,
searchs,
exportReport,
}: TableListProps<T>) {
/* ------------------------------- handle sort ------------------------------ */
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
@@ -130,6 +136,7 @@ export default function Table<T>({
const parameters = Object.fromEntries([
...params.searchParams.entries(),
['page', newPage + 1],
['per_page', paginations.rowsPerPage]
]);
paginations.setPage(newPage);
await new Promise((resolve) => setTimeout(resolve, 500));
@@ -153,7 +160,7 @@ export default function Table<T>({
/* -------------------------------------------------------------------------- */
return (
<Card>
// <Card>
<Grid container>
{/* Field 1 */}
<Grid item xs={12} paddingX="24px" paddingY="20px">
@@ -192,20 +199,132 @@ export default function Table<T>({
</form>
</Grid>
</Fragment>
) : (
<Grid item xs={12}>
<form onSubmit={searchs.handleSearchSubmit}>
<TextField
id="search-input"
label="Search"
variant="outlined"
onChange={(event) => searchs.setSearchText(event.target.value)}
value={searchs.searchText}
fullWidth
/>
</form>
</Grid>
)}
) : null }
{searchs && searchs.useSearchs ? (
<Fragment>
{filterStatus && filterStatus.useFilter ? (
<Grid item xs={12} lg={4} xl={4}>
<form onSubmit={searchs.handleSearchSubmit}>
<TextField
id="search-input"
variant="outlined"
onChange={(event) => searchs.setSearchText(event.target.value)}
value={searchs.searchText}
fullWidth
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
placeholder="Search Name or Member ID... "
/>
</form>
</Grid>
) :
<Grid item xs={12} lg={6} xl={6}>
<form onSubmit={searchs.handleSearchSubmit}>
<TextField
id="search-input"
variant="outlined"
onChange={(event) => searchs.setSearchText(event.target.value)}
value={searchs.searchText}
fullWidth
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
placeholder="Search Name or Member ID... "
/>
</form>
</Grid>
}
</Fragment>
) : null }
{/* Start date */}
{filterStartDate && filterStartDate.useFilter ? (
<Grid item xs={12} lg={2} xl={2}>
<form onChange={filterStartDate.handleStartDateChange}>
<TextField
id="date-input"
type="date"
variant="outlined"
value={filterStartDate.startDate}
onChange={(event) => filterStartDate.setStartDate(event.target.value)}
fullWidth
label="Start Date"
InputLabelProps={{
shrink: true,
}}
/>
</form>
</Grid>
) : null }
{/* End Date */}
{filterEndDate && filterEndDate.useFilter ? (
<Grid item xs={12} lg={2} xl={2}>
<form onChange={filterEndDate.handleEndDateChange}>
<TextField
id="date-input"
type="date"
variant="outlined"
value={filterEndDate.endDate}
onChange={(event) => filterEndDate.setEndDate(event.target.value)}
fullWidth
label="End Date"
InputLabelProps={{
shrink: true,
}}
/>
</form>
</Grid>
) : null }
{/* Filter status */}
{filterStatus && filterStatus.useFilter ? (
<Grid item xs={12} lg={2} xl={2}>
<FormControl fullWidth>
<InputLabel id="simple-status-select-lable">Status</InputLabel>
<Select
labelId="simple-status-select-lable"
id="status-select-lable"
value={filterStatus.config.statusValue}
label="Status"
onChange={filterStatus.config.handleStatusChange}
>
<MenuItem value="all">All</MenuItem>
{filterStatus.config.filterData.map((row: StatusDataProps, index) => (
<MenuItem key={index} value={row.id}>
{row.name}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
) : null }
{/* Export Report */}
{exportReport && exportReport.useExport ? (
<Grid item xs={12} lg={2} xl={2}>
<FormControl fullWidth>
<Button variant='contained' sx={{p:2}} onClick={exportReport.handleExportReport}>
<Download />
<Typography variant='inherit' sx={{marginLeft: 1}}>Export</Typography>
</Button>
</FormControl>
</Grid>
) : null }
</Grid>
</Grid>
{/* End Field 1 */}
@@ -219,7 +338,7 @@ export default function Table<T>({
{/* End Table Header */}
{/* Table Body */}
<TableBody>
{loadings.isLoading ? (
{loadings.isLoading && rows.length >= 1 ? (
<TableRow>
<TableCell colSpan={headCells?.length} align="center">
Loading . . .
@@ -263,6 +382,6 @@ export default function Table<T>({
</Grid>
{/* End Field 2 */}
</Grid>
</Card>
// </Card>
);
}

View File

@@ -0,0 +1,128 @@
import React, { Dispatch, FunctionComponent, useCallback, useState } from 'react';
import { useDropzone } from 'react-dropzone';
import { Box, Stack, Typography } from '@mui/material';
import BlockContent from './upload/BlockContent';
import { styled } from '@mui/material/styles';
import { UploadIllustration } from '../assets';
import Iconify from './Iconify';
const RootStyle = styled('div')(({ theme }) => ({
width: 144,
height: 144,
margin: 'auto',
borderRadius: '50%',
padding: theme.spacing(1),
border: `2px dashed ${theme.palette.grey[500_32]}`,
}));
const DropZoneStyle = styled('div')({
zIndex: 0,
width: '100%',
height: '100%',
outline: 'none',
display: 'flex',
overflow: 'hidden',
borderRadius: '50%',
position: 'relative',
alignItems: 'center',
justifyContent: 'center',
'& > *': { width: '100%', height: '100%' },
'&:hover': {
cursor: 'pointer',
'& .placeholder': {
zIndex: 9,
},
},
});
const PlaceholderStyle = styled('div')(({ theme }) => ({
display: 'flex',
position: 'absolute',
alignItems: 'center',
flexDirection: 'column',
justifyContent: 'center',
color: theme.palette.text.secondary,
// backgroundColor: theme.palette.background.neutral,
transition: theme.transitions.create('opacity', {
easing: theme.transitions.easing.easeInOut,
duration: theme.transitions.duration.shorter,
}),
'&:hover': { opacity: 0.72 },
}));
const UploadImage: FunctionComponent<{
setFile: Dispatch<any>;
currentImage: string;
}> = ({ setFile, currentImage, setSave, error, file, helperText, sx, ...other }) => {
const onDrop = useCallback(
(acceptedFiles) => {
// Do something with the files
console.log(acceptedFiles);
setFile(acceptedFiles[0]);
setImage(acceptedFiles[0]);
},
[setFile, setSave]
);
const { getRootProps, getInputProps, isDragActive, isDragReject } = useDropzone({
onDrop,
multiple: false,
});
const [image, setImage] = useState<File | null>(null);
return (
<RootStyle
sx={{
...((isDragReject || error) && {
borderColor: 'error.light',
}),
...sx,
}}
>
<DropZoneStyle
{...getRootProps()}
sx={{
...(isDragActive && { opacity: 2.72 }),
}}
>
<input {...getInputProps()} />
{/* <Stack
spacing={2}
alignItems="center"
justifyContent="center"
direction={{ xs: 'column', md: 'row' }}
sx={{ width: 1, textAlign: { xs: 'center', md: 'left' } }}
> */}
{image ? (
<img src={URL.createObjectURL(image)} alt="preview" width={220} />
) : currentImage ? (
<img src={currentImage} alt="preview" width={220} />
) : (
<PlaceholderStyle
className="placeholder"
sx={{
...((isDragReject || error) && {
bgcolor: 'error.lighter',
}),
}}
>
<Iconify icon={'ic:round-add-a-photo'} sx={{ width: 24, height: 24, mb: 1 }} />
<Typography variant="caption">{image ? 'Update photo' : 'Upload photo'}</Typography>
</PlaceholderStyle>
)}
<PlaceholderStyle className="placeholder">
<Iconify icon={'ic:round-add-a-photo'} sx={{ width: 24, height: 24, mb: 1 }} />
<Typography variant="caption">{image ? 'Update photo' : 'Upload photo'}</Typography>
</PlaceholderStyle>
{/* </Stack> */}
{/* <BlockContent file={image} /> */}
{isDragReject && <p>Unsupported file type...</p>}
</DropZoneStyle>
</RootStyle>
);
};
export default UploadImage;

View File

@@ -0,0 +1,41 @@
import { Autocomplete, TextField, TextFieldProps } from '@mui/material';
import { useState } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
interface IProps {
options: any[];
name: string;
label?: string;
}
export default function RHFAutocomplete({ name, options, label, ...other }: IProps & TextFieldProps) {
const { control } = useFormContext();
console.log(control)
const [value, setValue] = useState<string | null>(options[0]);
const [inputValue, setInputValue] = useState('');
return (
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => (
<Autocomplete
{...field}
options={options}
value={value}
onChange={(event: any, newValue: string | null) => {
// console.log('fuck', newValue)
setValue(newValue.id);
}}
inputValue={inputValue}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
}}
renderInput={(params) => <TextField {...params} label={label ?? name} />}
{...other}
/>
)}
/>
);
}

View File

@@ -9,9 +9,10 @@ type IProps = Omit<FormControlLabelProps, 'control'>;
interface Props extends IProps {
name: string;
disabled?: boolean;
}
export default function RHFSwitch({ name, ...other }: Props) {
export default function RHFSwitch({ name, disabled = false, ...other }: Props) {
const { control } = useFormContext();
return (
@@ -20,7 +21,7 @@ export default function RHFSwitch({ name, ...other }: Props) {
<Controller
name={name}
control={control}
render={({ field }) => <Switch {...field} checked={field.value} />}
render={({ field }) => <Switch {...field} checked={field.value} disabled={disabled} />}
/>
}
{...other}

View File

@@ -0,0 +1,26 @@
import { ReactNode } from 'react';
// form
import { FormProvider as Form, UseFormReturn } from 'react-hook-form';
// ----------------------------------------------------------------------
type Props = {
children: ReactNode;
methods: UseFormReturn<any>;
onSubmit?: VoidFunction;
preventEnterSubmit?: boolean;
};
export default function FormProvider({ children, onSubmit, methods, preventEnterSubmit }: Props) {
const checkKeyDown = (e: any) => {
if (e.key === 'Enter' && preventEnterSubmit){
e.preventDefault();
}
};
return (
<Form {...methods}>
<form onSubmit={onSubmit} onKeyDown={(e) => checkKeyDown(e)}>{children}</form>
</Form>
);
}

View File

@@ -0,0 +1,102 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
// @mui
import { Autocomplete, AutocompleteProps, FormHelperText, TextField, UseAutocompleteProps } from '@mui/material';
import { useState } from 'react';
import { FilterOptionsState } from '@mui/material/useAutocomplete';
// ----------------------------------------------------------------------
interface IProps {
name: string,
label: string,
options: any,
getOptionLabel: (option: any) => string,
isOptionEqualToValue: any,
disableClearable: boolean,
freeSolo: boolean,
renderOption?: any,
onInputChange?: any,
onKeyDown?: any,
onKeyPress?: any,
noOptionsText?: string,
popupIcon?: any,
disabled?: boolean,
sx?: any,
sxTextField?: any,
InputProps?: any,
onSelect?: (option: any) => void,
filterOptions?: (options: any[], state: FilterOptionsState<any>) => any[];
}
const RHFAutocomplete = ( {name, label, options, ...rest} : IProps ) => {
const { control } = useFormContext();
const {
sx, sxTextField, InputProps,
onSelect,
getOptionLabel, filterOptions,
popupIcon,
isOptionEqualToValue, disableClearable,
freeSolo, renderOption,
onInputChange, onKeyDown, onKeyPress,
noOptionsText, disabled } = rest;
return (
<Controller
name={name}
control={control}
render={({ field: { onChange, value }, fieldState: { error } }) => {
return (
<>
<Autocomplete
disabled={disabled}
handleHomeEndKeys
getOptionLabel={getOptionLabel}
options={options}
freeSolo={freeSolo}
disableClearable={disableClearable}
isOptionEqualToValue={isOptionEqualToValue}
value={value}
filterOptions={filterOptions}
renderOption={renderOption}
onInputChange={onInputChange}
onKeyDown={onKeyDown}
onKeyPress={onKeyPress}
sx={sx}
popupIcon={popupIcon}
noOptionsText={noOptionsText}
renderInput={(params) => (
<TextField
{...params}
label={label}
sx={sxTextField}
InputProps={{
...params.InputProps,
...InputProps
}}
/>
)}
onChange={(e, newValue) => {
onChange(newValue);
onSelect && onSelect(newValue);
}}
/>
{!!error && (
<FormHelperText error sx={{ px: 2 }}>
{error.message}
</FormHelperText>
)}
</>
);
}}
/>
);
}
export default RHFAutocomplete;

View File

@@ -0,0 +1,75 @@
import * as React from 'react';
import Chip from '@mui/material/Chip';
import TextField from '@mui/material/TextField';
import Autocomplete from '@mui/material/Autocomplete';
import { FormHelperText } from '@mui/material';
import { Controller, useFormContext } from 'react-hook-form';
import { useEffect } from 'react';
interface IProps {
name: string,
label: string,
options: any,
defaultValue: any
}
const RHFAutocompleteTags = ({ name, label, options, defaultValue, ...rest }: IProps) => {
const { control } = useFormContext();
const fixedOptions: any = [];
const [value, setValue] = React.useState([...fixedOptions]);
useEffect(() => {
setValue(defaultValue)
}, [options, defaultValue])
return (
<Controller
name={name}
control={control}
render={({ field: { onChange }, fieldState: { error } }) => {
return (
<>
<Autocomplete
multiple
id="fixed-tags-demo"
value={value}
onChange={(event, newValue) => {
setValue([
...fixedOptions,
...newValue.filter((option) => fixedOptions.indexOf(option) === -1),
]);
onChange(newValue);
}}
isOptionEqualToValue={(option, value)=>{
return option.optionID === value.optionID
}}
options={options}
getOptionLabel={(option: { optionID: string, optionLabel: string }) => `${option.optionLabel}` || ""}
renderTags={(tagValue, getTagProps) =>
tagValue.map((option, index) => (
<React.Fragment key={index}>
<Chip
label={option?.optionLabel}
{...getTagProps({ index })}
/>
</React.Fragment>
))
}
renderInput={(params) => (
<TextField {...params} label={label} placeholder={label} />
)}
/>
{!!error && (
<FormHelperText error sx={{ px: 2 }}>
{error.message}
</FormHelperText>
)}
</>
);
}}
/>
);
}
export default RHFAutocompleteTags;

View File

@@ -0,0 +1,111 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
// @mui
import { Checkbox, FormControlLabel, FormGroup, FormControlLabelProps, Grid, FormHelperText } from '@mui/material';
import RHFDatePicker from './RHFDatePicker';
// ----------------------------------------------------------------------
interface RHFCheckboxProps extends Omit<FormControlLabelProps, 'control'> {
name: string;
}
export function RHFCheckbox({ name, ...other }: RHFCheckboxProps) {
const { control } = useFormContext();
return (
<FormControlLabel
control={
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => (
<>
<Checkbox {...field} checked={field.value} />
{!!error && (
<FormHelperText error sx={{ px: 2 }}>
{error.message}
</FormHelperText>
)}
</>
)}
/>
}
{...other}
/>
);
}
interface RHFCheckboxEndDateProps extends Omit<FormControlLabelProps, 'control'> {
name: string;
endPeriodProp: any;
idx?: any;
}
export function RHFCheckboxEndDate({ name, endPeriodProp, idx, ...other }: RHFCheckboxEndDateProps) {
const { control } = useFormContext();
return (
<FormControlLabel
control={
<Controller
name={name}
control={control}
render={({ field }) =>
<Checkbox
{...field} checked={field.value}
onChange={e => {
endPeriodProp(e.target.checked, idx)
}}
/>
}
/>
}
{...other}
/>
);
}
// ----------------------------------------------------------------------
interface RHFMultiCheckboxProps extends Omit<FormControlLabelProps, 'control' | 'label'> {
name: string;
options: string[];
}
export function RHFMultiCheckbox({ name, options, ...other }: RHFMultiCheckboxProps) {
const { control } = useFormContext();
return (
<Controller
name={name}
control={control}
render={({ field }) => {
const onSelected = (option: string) =>
field.value.includes(option)
? field.value.filter((value: string) => value !== option)
: [...field.value, option];
return (
<FormGroup>
{options.map((option) => (
<FormControlLabel
key={option}
control={
<Checkbox
checked={field.value.includes(option)}
onChange={() => field.onChange(onSelected(option))}
/>
}
label={option}
{...other}
/>
))}
</FormGroup>
);
}}
/>
);
}

View File

@@ -0,0 +1,54 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
// @mui
import { FormHelperText, TextField } from '@mui/material';
import { DesktopDatePicker, LocalizationProvider } from '@mui/lab';
import AdapterDateFns from '@mui/lab/AdapterDateFns';
// ----------------------------------------------------------------------
interface IProps {
name: string;
label: string;
dateFormat: string;
fullWidth?: boolean;
minDate?: any;
maxDate?: any;
disabled?: boolean;
}
export default function RHFDatePicker({ name, label, dateFormat, minDate, maxDate, disabled, ...other }: IProps) {
const { control } = useFormContext();
const { fullWidth } = other;
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => (
<>
<DesktopDatePicker
allowSameDateSelection={true}
disabled={disabled}
label={label}
onChange={(date) => field.onChange(date)}
inputFormat={dateFormat}
value={field.value}
mask={''}
minDate={(minDate) ? new Date(minDate) : null}
maxDate={(maxDate) ? new Date(maxDate) : null}
renderInput={(params) => <TextField fullWidth={fullWidth} {...params} />}
/>
{!!error && (
<FormHelperText error sx={{ px: 2 }}>
{error.message}
</FormHelperText>
)}
</>
)}
/>
</LocalizationProvider>
);
}

View File

@@ -0,0 +1,53 @@
// form
import { useFormContext, Controller, useForm } from 'react-hook-form';
// @mui
import { FormHelperText, TextField } from '@mui/material';
import { DesktopDateTimePicker, LocalizationProvider } from '@mui/lab';
import AdapterDateFns from '@mui/lab/AdapterDateFns';
// ----------------------------------------------------------------------
interface IProps {
name: string;
label: string;
dateFormat: string;
fullWidth?: boolean;
onChange?: (date: any) => void;
disabled?: boolean;
}
export default function RHFDateTimePicker({ name, label, dateFormat, ...other }: IProps) {
const { control } = useFormContext();
const { fullWidth, onChange, disabled } = other;
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => (
<>
<DesktopDateTimePicker
label={label}
onChange={(date) => {
field.onChange(date);
onChange && onChange(date);
}}
inputFormat={dateFormat}
value={field.value}
mask={''}
disabled={disabled}
renderInput={(params) => <TextField fullWidth={fullWidth} {...params} />}
/>
{!!error && (
<FormHelperText error sx={{ px: 2 }}>
{error.message}
</FormHelperText>
)}
</>
)}
/>
</LocalizationProvider>
);
}

View File

@@ -0,0 +1,37 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
// @mui
import { FormHelperText } from '@mui/material';
//
import Editor, { Props as EditorProps } from '../../editor';
// ----------------------------------------------------------------------
interface Props extends EditorProps {
name: string;
}
export default function RHFEditor({ name, ...other }: Props) {
const { control } = useFormContext();
return (
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => (
<Editor
id={name}
value={field.value}
onChange={field.onChange}
error={!!error}
helperText={
<FormHelperText error sx={{ px: 2, textTransform: 'capitalize' }}>
{error?.message}
</FormHelperText>
}
{...other}
/>
)}
/>
);
}

View File

@@ -0,0 +1,61 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
// @mui
import {
Radio,
RadioGroup,
FormHelperText,
RadioGroupProps,
FormControlLabel,
} from '@mui/material';
// ----------------------------------------------------------------------
interface IProps {
name: string;
options: string[];
getOptionLabel?: string[];
fullWidth?: boolean;
disabled?: boolean;
}
export default function RHFRadioGroup({
name,
options,
getOptionLabel,
fullWidth,
disabled,
...other
}: IProps & RadioGroupProps) {
const { control } = useFormContext();
return (
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => (
<div>
<RadioGroup {...field} row {...other}>
{options.map((option, index) => (
<FormControlLabel
style={{width: (fullWidth)?'100%':''}}
key={option}
value={option}
control={<Radio />}
label={getOptionLabel?.length ? getOptionLabel[index] : option}
disabled={disabled}
/>
))}
</RadioGroup>
{!!error && (
<FormHelperText error sx={{ px: 2 }}>
{error.message}
</FormHelperText>
)}
</div>
)}
/>
);
}

View File

@@ -0,0 +1,35 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
// @mui
import { TextField, TextFieldProps } from '@mui/material';
// ----------------------------------------------------------------------
interface IProps {
name: string;
children: any;
}
export default function RHFSelect({ name, children, ...other }: IProps & TextFieldProps) {
const { control } = useFormContext();
return (
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => (
<TextField
{...field}
select
fullWidth
SelectProps={{ native: true }}
error={!!error}
helperText={error?.message}
{...other}
>
{children}
</TextField>
)}
/>
);
}

View File

@@ -0,0 +1,32 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
// @mui
import { FormControl, FormHelperText, InputLabel, Select, SelectProps } from '@mui/material';
// ----------------------------------------------------------------------
interface IProps {
name: string;
id: string;
children: any;
}
export default function RHFSelectV2({ name, id, children, ...other }: IProps & SelectProps) {
const { control } = useFormContext();
return (
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl fullWidth error={error !== undefined ? true : false}>
<InputLabel id={`${id}-label`}>{other.label}</InputLabel>
<Select {...field} labelId={`${id}-label`} id={id} fullWidth {...other}>
{children}
</Select>
{error && <FormHelperText>{error.message}</FormHelperText>}
</FormControl>
)}
/>
);
}

View File

@@ -0,0 +1,29 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
// @mui
import { Switch, FormControlLabel, FormControlLabelProps } from '@mui/material';
// ----------------------------------------------------------------------
type IProps = Omit<FormControlLabelProps, 'control'>;
interface Props extends IProps {
name: string;
}
export default function RHFSwitch({ name, ...other }: Props) {
const { control } = useFormContext();
return (
<FormControlLabel
control={
<Controller
name={name}
control={control}
render={({ field }) => <Switch {...field} checked={field.value} />}
/>
}
{...other}
/>
);
}

View File

@@ -0,0 +1,29 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
// @mui
import { TextField, TextFieldProps, Typography } from '@mui/material';
// ----------------------------------------------------------------------
interface IProps {
name: string;
}
export default function RHFTextField({ name, ...other }: IProps & TextFieldProps) {
const { control } = useFormContext();
return (
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => (
<>
{/* <Typography>
*
</Typography> */}
<TextField {...field} autoComplete="off" fullWidth error={!!error} helperText={error?.message} {...other} />
</>
)}
/>
);
}

View File

@@ -0,0 +1,42 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
// @mui
import { InputAdornment, TextField, TextFieldProps, Typography } from '@mui/material';
import MoneyFormat from '../../numeric_format/MoneyFormat';
// ----------------------------------------------------------------------
interface IProps {
name: string;
}
export default function RHFTextFieldMoney({ name, ...other }: IProps & TextFieldProps) {
const { control, watch, setValue } = useFormContext();
const values = watch();
return (
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => (
<>
<TextField
{...field}
autoComplete="off"
fullWidth error={!!error}
helperText={error?.message}
onFocus={() => { (values[name] === '0') && setValue(name, '') }}
onBlur={() => { (values[name] === '') && setValue(name, '0') }}
{...other}
inputProps={{ min: 0, max: 5, style: { textAlign: 'right' } }}
InputProps={{
startAdornment: <InputAdornment position="start">Rp</InputAdornment>,
inputComponent: MoneyFormat as any,
}}
/>
</>
)}
/>
);
}

View File

@@ -0,0 +1,61 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
// @mui
import { InputAdornment, TextField, TextFieldProps, Typography } from '@mui/material';
import MoneyFormat from '../../numeric_format/MoneyFormat';
// import AutoNumeric from "autonumeric"
// import { useEffect, useRef } from 'react';
// import React from 'react';
// ----------------------------------------------------------------------
interface IProps {
name: string;
endAdornment?: React.ReactNode;
}
export default function RHFTextFieldNumber({ name, endAdornment, ...other }: IProps & TextFieldProps) {
const { control, watch, setValue } = useFormContext();
const values = watch();
// const ref = React.createRef();
// const mountedRef = useRef(false);
// useEffect(() => {
// mountedRef.current = true;
// new AutoNumeric(ref.current as HTMLElement)
// return () => {
// mountedRef.current = false;
// }
// }, [])
return (
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => (
<>
<TextField
{...field}
autoComplete="off"
fullWidth error={!!error}
helperText={error?.message}
onFocus={() => { (watch(name) == '0') && setValue(name, '') }}
onBlur={() => { (watch(name) == '') && setValue(name, '0') }}
{...other}
inputProps={{ min: 0, max: 5, style: { textAlign: 'right' } }}
InputProps={{
inputComponent: MoneyFormat as any,
endAdornment: endAdornment,
}}
/>
</>
)}
/>
);
}

View File

@@ -0,0 +1,39 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
// @mui
import { InputAdornment, TextField, TextFieldProps, Typography } from '@mui/material';
import MoneyFormat from '../../numeric_format/MoneyFormat';
// ----------------------------------------------------------------------
interface IProps {
name: string;
}
export default function RHFTextFieldPercentage({ name, ...other }: IProps & TextFieldProps) {
const { control } = useFormContext();
return (
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => (
<>
<TextField
{...field}
autoComplete="off"
fullWidth error={!!error}
helperText={error?.message}
{...other}
inputProps={{ min: 0, max: 5, style: { textAlign: 'right' } }}
InputProps={{
endAdornment: <InputAdornment position="end">%</InputAdornment>,
inputComponent: MoneyFormat as any,
}}
/>
</>
)}
/>
);
}

View File

@@ -0,0 +1,46 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
// @mui
import { FormHelperText, TextField } from '@mui/material';
import { DesktopDatePicker, LocalizationProvider, TimePicker } from '@mui/lab';
import AdapterDateFns from '@mui/lab/AdapterDateFns';
// ----------------------------------------------------------------------
interface IProps {
name: string;
label: string;
fullWidth?: boolean;
disabled?: boolean;
}
export default function RHFTimePicker({ name, label, disabled, ...other }: IProps) {
const { control } = useFormContext();
const { fullWidth } = other;
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => (
<>
<TimePicker
disabled={disabled}
label={label}
onChange={(date) => field.onChange(date)}
value={field.value}
renderInput={(params) => <TextField fullWidth={fullWidth} {...params} />}
/>
{!!error && (
<FormHelperText error sx={{ px: 2 }}>
{error.message}
</FormHelperText>
)}
</>
)}
/>
</LocalizationProvider>
);
}

View File

@@ -0,0 +1,112 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
// @mui
import { FormHelperText } from '@mui/material';
// type
import {
UploadAvatar,
UploadMultiFile,
UploadSingleFile,
UploadProps,
UploadMultiFileProps,
} from '../../upload';
import { Accept } from 'react-dropzone';
// ----------------------------------------------------------------------
interface Props extends Omit<UploadProps, 'file'> {
name: string;
}
export function RHFUploadAvatar({ name, ...other }: Props) {
const { control } = useFormContext();
return (
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => {
const checkError = !!error && !field.value;
return (
<div>
<UploadAvatar error={checkError} {...other} file={field.value} />
{checkError && (
<FormHelperText error sx={{ px: 2, textAlign: 'center' }}>
{error.message}
</FormHelperText>
)}
</div>
);
}}
/>
);
}
// ----------------------------------------------------------------------
export function RHFUploadSingleFile({ name, ...other }: Props) {
const { control } = useFormContext();
return (
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => {
const checkError = !!error && !field.value;
return (
<UploadSingleFile
accept={"image/*" as unknown as Accept}
file={field.value}
error={checkError}
helperText={
checkError && (
<FormHelperText error sx={{ px: 2 }}>
{error.message}
</FormHelperText>
)
}
{...other}
/>
);
}}
/>
);
}
// ----------------------------------------------------------------------
interface RHFUploadMultiFileProps extends Omit<UploadMultiFileProps, 'files'> {
name: string;
}
export function RHFUploadMultiFile({ name, ...other }: RHFUploadMultiFileProps) {
const { control } = useFormContext();
return (
<Controller
name={name}
control={control}
render={({ field, fieldState: { error } }) => {
const checkError = !!error && field.value?.length === 0;
return (
<UploadMultiFile
accept={"image/*" as unknown as Accept}
files={field.value}
error={checkError}
helperText={
checkError && (
<FormHelperText error sx={{ px: 2 }}>
{error?.message}
</FormHelperText>
)
}
{...other}
/>
);
}}
/>
);
}

View File

@@ -0,0 +1,12 @@
export * from './RHFCheckbox';
export * from './RHFUpload';
export { default as FormProvider } from './FormProvider';
export { default as RHFSwitch } from './RHFSwitch';
export { default as RHFAutocomplete } from './RHFAutocomplete';
export { default as RHFSelect } from './RHFSelect';
export { default as RHFEditor } from './RHFEditor';
export { default as RHFTextField } from './RHFTextField';
export { default as RHFRadioGroup } from './RHFRadioGroup';
export { default as RHFSelectV2 } from './RHFSelectV2';

View File

@@ -0,0 +1,34 @@
import React from "react";
import { InputAttributes, NumericFormat, NumericFormatProps } from "react-number-format";
interface CustomProps {
onChange: (event: { target: { name: string; value: string } }) => void;
name: string;
}
const DiscountPctFormat = React.forwardRef<
NumericFormatProps<InputAttributes>,
CustomProps
>(function DiscountPctFormat(props, ref) {
const { onChange, ...other } = props;
return (
<NumericFormat
{...other}
getInputRef={ref}
onValueChange={(values) => {
onChange({
target: {
name: props.name,
value: values.value,
},
});
}}
thousandSeparator
valueIsNumericString
allowLeadingZeros={false}
/>
);
});
export default DiscountPctFormat;

View File

@@ -0,0 +1,34 @@
import React from "react";
import { InputAttributes, NumericFormat, NumericFormatProps } from "react-number-format";
interface CustomProps {
onChange: (event: { target: { name: string; value: string } }) => void;
name: string;
}
const MoneyFormat = React.forwardRef<
NumericFormatProps<InputAttributes>,
CustomProps
>(function MoneyFormat(props, ref) {
const { onChange, ...other } = props;
return (
<NumericFormat
{...other}
getInputRef={ref}
onValueChange={(values) => {
onChange({
target: {
name: props.name,
value: values.value,
},
});
}}
thousandSeparator
valueIsNumericString
allowLeadingZeros={false}
/>
);
});
export default MoneyFormat;

View File

@@ -0,0 +1 @@
export { default as TableMoreMenu } from './TableMoreMenu';

View File

@@ -0,0 +1,60 @@
import { useEffect, useState } from 'react';
// @mui
import { IconButton } from '@mui/material';
//
import Iconify from '../Iconify';
import MenuPopover from '../MenuPopover';
// ----------------------------------------------------------------------
type Props = {
actions: React.ReactNode;
disableRipple?: boolean;
};
export default function TableMoreMenu({ actions, disableRipple }: Props) {
const [open, setOpen] = useState<HTMLElement | null>(null);
// Close menu popover
useEffect(() => {
setOpen(null);
}, [actions])
const handleOpen = (event: React.MouseEvent<HTMLElement>) => {
setOpen(event.currentTarget);
};
const handleClose = () => {
setOpen(null);
};
return (
<>
<IconButton onClick={handleOpen} disableRipple={disableRipple}>
<Iconify icon={'eva:more-vertical-fill'} width={20} height={20} />
</IconButton>
<MenuPopover
open={Boolean(open)}
anchorEl={open}
onClose={handleClose}
anchorOrigin={{ vertical: 'top', horizontal: 'left' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
arrow="right-top"
sx={{
mt: -1,
width: 'auto',
minWidth: 160,
'& .MuiMenuItem-root': {
px: 1,
typography: 'body2',
borderRadius: 0.75,
'& svg': { mr: 2, width: 20, height: 20 },
},
}}
>
{actions}
</MenuPopover>
    </>
  );
}

View File

@@ -2,6 +2,7 @@ import { FormControl, InputLabel, MenuItem, Select, SelectChangeEvent } from '@m
import { useContext, useEffect, useState } from 'react';
import { UserCurrentCorporateContext } from '../../../contexts/UserCurrentCorporate';
import axios from '../../../utils/axios';
import { useNavigate } from 'react-router-dom';
/* ---------------------------------- types --------------------------------- */
type CorporateDataProps = {
@@ -13,9 +14,17 @@ type CorporateDataProps = {
export default function CorporatePopover() {
const { corporateValue, setCorporateValue } = useContext(UserCurrentCorporateContext);
const [corporateData, setCorporateData] = useState([]);
//Check route in profile
const navigate = useNavigate();
const currentPathname = window.location.pathname;
const desiredPart = currentPathname.split('/')[1];
const handleCorporateChange = (event: SelectChangeEvent) => {
setCorporateValue(event.target.value as string);
if(desiredPart === 'user-profile')
{
navigate('/alarm-center');
}
};
useEffect(() => {

View File

@@ -6,15 +6,38 @@ const navConfig = [
{
items: [{ title: 'Dashboard', path: '/dashboard' }],
},
// Corporate
// ----------------------------------------------------------------------
{
subheader: 'Corporate',
items: [
{
title: 'Corporate',
path: '/corporate',
// icon: ICONS.default,
},
],
},
// Alarm Center
// ----------------------------------------------------------------------
{
subheader: 'Case Management',
items: [
{
title: 'Employee Data',
path: '/employee-data',
},
{
title: 'Alarm Center',
path: '/alarm-center',
},
{
title: 'Claim Submit',
path: '/claim-submit',
},
{
title: 'Claim Report',
path: '/claim-report',
@@ -22,6 +45,7 @@ const navConfig = [
],
},
// User Management
// ----------------------------------------------------------------------
// {

View File

@@ -26,7 +26,7 @@ export default function NavbarAccount({ isCollapse }: Props) {
const { user } = useAuth();
console.log('current user is ', user)
// console.log('current user is ', user)
return (
<Link underline="none" color="inherit">
<RootStyle

View File

@@ -10,6 +10,7 @@ import useSettings from '../../hooks/useSettings';
import List from './List';
import ServiceMonitoring from './ServiceMonitoring';
import UserProfile from './UserProfile';
import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs';
/* ------------------------------ tabs setting ------------------------------ */
@@ -102,16 +103,23 @@ export default function Drugs() {
return (
<Page title="Alarm Center">
<Container maxWidth={themeStretch ? false : 'xl'}>
<HeaderBreadcrumbs
heading={'Alarm Center'}
links={[
{ name: 'Case Management', href: '/alarm-center' },
{ name: 'Alarm Center', href: '/alarm-center'}
]}
/>
<Grid container>
<Grid item xs={12} lg={12} md={12}>
<Card>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
{/* <Card> */}
{/* <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<StyledTabs value={value} onChange={handleChange} aria-label="basic tabs example">
<StyledTab label="All Data" {...a11yProps(0)} />
<StyledTab label="Ongoing" {...a11yProps(1)} />
<StyledTab label="Done" {...a11yProps(2)} />
</StyledTabs>
</Box>
</Box> */}
<TabPanel value={value} index={0}>
<List />
</TabPanel>
@@ -121,7 +129,7 @@ export default function Drugs() {
<TabPanel value={value} index={2}>
<UserProfile />
</TabPanel> */}
</Card>
{/* </Card> */}
</Grid>
</Grid>
</Container>

View File

@@ -12,6 +12,9 @@ import {
Button,
TableSortLabel,
Box,
SelectChangeEvent,
Typography,
MenuItem
} from '@mui/material';
import { visuallyHidden } from '@mui/utils';
/* ---------------------------------- axios --------------------------------- */
@@ -32,6 +35,13 @@ import palette from '../../theme/palette';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams, useNavigate, Link } from 'react-router-dom';
import { fDateSuffix } from '../../utils/formatTime';
import TableMoreMenu from '../../components/table/TableMoreMenu';
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
import { enqueueSnackbar } from 'notistack';
import DetailDataMember from './ListMember';
import Label from '../../components/Label';
/* ---------------------------------- types --------------------------------- */
@@ -221,11 +231,117 @@ export default function List() {
};
const searchs = {
useSearchs: true,
searchText: searchText,
setSearchText: setSearchText,
handleSearchSubmit: handleSearchSubmit,
};
/* ------------------------------ handle filter ----------------------------- */
const [statusValue, setStatusValue] = useState('all');
const [filterData, setStatusData] = useState([]);
// handle status
const handleStatusChanges = (event: SelectChangeEvent) => {
setStatusValue(event.target.value as string);
if (event.target.value === 'all') {
searchParams.delete('status');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([
...searchParams.entries(),
['status', event.target.value as string],
]);
setAppliedParams(params);
}
};
const filterStatus = {
useFilter: false,
config: {
label: 'Status',
statusValue: statusValue,
filterData: filterData,
handleStatusChange: handleStatusChanges,
},
};
// handle start date
const [startDateValue, setStartDateValue] = useState('');
const handleStartDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
console.log(startDateValue)
if (startDateValue === '') {
searchParams.delete('start_date');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['start_date', startDateValue]]);
setAppliedParams(params);
}
};
const filterStartDate = {
useFilter: true,
startDate: startDateValue,
setStartDate: setStartDateValue,
handleStartDateChange: handleStartDateChanges,
};
// handle end date
const [endDateValue, setEndDateValue] = useState('');
const handleEndDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (endDateValue === '') {
searchParams.delete('end_date');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['end_date', endDateValue]]);
setAppliedParams(params);
}
};
const filterEndDate = {
useFilter: true,
endDate: endDateValue,
setEndDate: setEndDateValue,
handleEndDateChange: handleEndDateChanges,
};
/* -------------------------------- handle export --------------------------- */
const handleExportReport = async () => {
var filter = Object.fromEntries([...searchParams.entries()]);
await axios
.get(corporateValue + '/claims/export', { params: filter })
.then((res) => {
enqueueSnackbar('Data berhasil di Export', {
variant: 'success',
anchorOrigin: { horizontal: 'right', vertical: 'top' },
});
document.location.href = res.data.data.file_url;
})
.catch((err) =>
enqueueSnackbar('Data Gagal di Export', {
variant: 'error',
anchorOrigin: { horizontal: 'right', vertical: 'top' },
})
);
};
const exportReport = {
useExport: true,
startDate: startDateValue,
endDate: endDateValue,
status: statusValue,
handleExportReport: handleExportReport
}
/* -------------------------------- headCell -------------------------------- */
const headCells: HeadCell<never>[] = [
{
@@ -251,14 +367,20 @@ export default function List() {
id: 'end_date',
align: 'center',
label: 'End Date',
isSort: false,
},
{
id: 'status',
align: 'center',
label: 'Status',
isSort: true,
},
// {
// id: 'status',
// align: 'center',
// label: 'Status',
// isSort: false,
// },
{
id: 'action',
align: 'center',
label: '',
isSort: false,
},
];
/* -------------------------------------------------------------------------- */
@@ -277,50 +399,64 @@ export default function List() {
params: { ...parameters },
});
const status = [
{"id": 1, "name": "Done" },
{"id": 0, "name": "On Going" },
]
setStatusData(status)
setData(
response.data.data.map((obj: any) => {
return {
...obj,
memberId:
// <Link to={'/user-profile/'+obj.personId} >
<Button
onClick={() => navigate ('/user-profile/'+obj.personId)}
>{obj.memberId}</Button>
// memberId:
// // <Link to={'/user-profile/'+obj.personId} >
// <Button
// onClick={() => navigate ('/user-profile/'+obj.personId)}
// >{obj.memberId}</Button>
// ,
start_date:
<Label>{ fDateSuffix(obj.start_date) }</Label>
,
status:
obj.status === 1 ? (
<Button onClick={() => navigate('service-monitoring/'+obj.personId )}
startIcon={<Iconify icon="ic:round-check" />}
sx={{
backgroundColor: palette.light.grey[300],
color: palette.light.grey[800],
paddingX: 1.5,
paddingY: 1,
'&:hover': {
backgroundColor: palette.light.grey[400],
color: palette.light.grey[800],
},
}}
>
done
</Button>
) : (
<Button
startIcon={<Iconify icon="fa6-solid:clock" />}
sx={{
backgroundColor: '#CD7B2E',
color: '#FFFF',
paddingX: 1.5,
paddingY: 1,
'&:hover': {
backgroundColor: '#BF6919',
color: '#FFFF',
},
}}
>
Ongoing
</Button>
),
end_date:
<Label> { fDateSuffix(obj.end_date) }</Label>
,
// status:
// obj.status === 1 ? (
// <Typography
// sx={{
// background: 'rgba(84, 214, 44, 0.16)',
// color: '#229A16',
// paddingX: 1.5,
// paddingY: 1,
// borderRadius: 3,
// }} variant='overline'
// >
// Done
// </Typography>
// ) : (
// <Typography
// sx={{
// background: 'rgba(255, 193, 7, 0.16)',
// color: '#BF6919',
// paddingX: 1.5,
// paddingY: 1,
// borderRadius: 3,
// }} variant='overline'
// >
// Ongoing
// </Typography>
// ),
action:
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate('member/'+obj.id )}>
<VisibilityOutlinedIcon />
View
</MenuItem>
</>
} />
};
})
);
@@ -353,6 +489,10 @@ export default function List() {
params={params}
searchs={searchs}
// filters={filters}
filterStatus={filterStatus}
filterStartDate={filterStartDate}
filterEndDate={filterEndDate}
exportReport={exportReport}
/>
</Stack>
);

View File

@@ -0,0 +1,504 @@
/* ---------------------------------- @mui ---------------------------------- */
import {
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Stack,
Button,
TableSortLabel,
Box,
SelectChangeEvent,
Typography,
MenuItem,
Grid
} from '@mui/material';
import { visuallyHidden } from '@mui/utils';
/* ---------------------------------- axios --------------------------------- */
// import axios from 'axios';
import axios from '../../utils/axios';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
import BaseTablePagination from '../../components/BaseTablePagination';
import TableComponent from '../../components/Table';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
/* ---------------------------------- hooks --------------------------------- */
import useMap from '../../hooks/useMap';
/* ---------------------------------- theme --------------------------------- */
import palette from '../../theme/palette';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams, useNavigate, Link, useParams } from 'react-router-dom';
import { fDateSuffix, fPostFormat } from '../../utils/formatTime';
import TableMoreMenu from '../../components/table/TableMoreMenu';
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
import Label from '../../components/Label';
/* ---------------------------------- types --------------------------------- */
type DataList = {
name: string;
};
// type PaginationTableProps = {
// current_page: number;
// from: number;
// last_page: number;
// links: [];
// path: string;
// per_page: number;
// to: number;
// total: number;
// };
// type DataTableProps = {
// fullName: string;
// memberId: string;
// service: string;
// start_date: string;
// end_date: string;
// status: boolean | number;
// };
// /* -------------------------------------------------------------------------- */
// /* -------------------------- enchanced table head -------------------------- */
// type Order = 'asc' | 'desc';
// interface HeadCell {
// id: string;
// label: string;
// }
// const headCells: readonly HeadCell[] = [
// {
// id: 'name',
// label: 'Name',
// },
// {
// id: 'member_id',
// label: 'Member ID',
// },
// {
// id: 'service',
// label: 'Service',
// },
// {
// id: 'start_date',
// label: 'Start Date',
// },
// {
// id: 'end_date',
// label: 'End Date',
// },
// {
// id: 'status',
// label: 'Status',
// },
// ];
// interface EnhancedTableProps {
// onRequestSort: (event: React.MouseEvent<unknown>, property: string) => void;
// order: Order;
// orderBy: string;
// }
// function EnhancedTableHead(props: EnhancedTableProps) {
// const { order, orderBy, onRequestSort } = props;
// const createSortHandler = (property: string) => (event: React.MouseEvent<unknown>) => {
// onRequestSort(event, property);
// };
// return (
// <TableHead>
// <TableRow>
// <TableCell align="center">No</TableCell>
// {headCells.map((headCell) => (
// <TableCell
// key={headCell.id}
// sortDirection={orderBy === headCell.id ? order : false}
// align="center"
// >
// <TableSortLabel
// active={orderBy === headCell.id}
// direction={orderBy === headCell.id ? order : 'asc'}
// onClick={createSortHandler(headCell.id)}
// >
// {headCell.label}
// {orderBy === headCell.id ? (
// <Box component="span" sx={visuallyHidden}>
// {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
// </Box>
// ) : null}
// </TableSortLabel>
// </TableCell>
// ))}
// </TableRow>
// </TableHead>
// );
// }
/* -------------------------------------------------------------------------- */
export default function List() {
const navigate = useNavigate();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const [data, setData] = useState([]);
const { id } = useParams();
/* -------------------------------------------------------------------------- */
/* 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 [name, setName] = 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 [statusValue, setStatusValue] = useState('all');
const [filterData, setStatusData] = useState([]);
// handle status
const handleStatusChanges = (event: SelectChangeEvent) => {
setStatusValue(event.target.value as string);
if (event.target.value === 'all') {
searchParams.delete('status');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([
...searchParams.entries(),
['status', event.target.value as string],
]);
setAppliedParams(params);
}
};
const filterStatus = {
useFilter: false,
config: {
label: 'Status',
statusValue: statusValue,
filterData: filterData,
handleStatusChange: handleStatusChanges,
},
};
// handle start date
const [startDateValue, setStartDateValue] = useState('');
const handleStartDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
console.log(startDateValue)
if (startDateValue === '') {
searchParams.delete('start_date');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['start_date', startDateValue]]);
setAppliedParams(params);
}
};
const filterStartDate = {
useFilter: false,
startDate: startDateValue,
setStartDate: setStartDateValue,
handleStartDateChange: handleStartDateChanges,
};
// handle end date
const [endDateValue, setEndDateValue] = useState('');
const handleEndDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (endDateValue === '') {
searchParams.delete('end_date');
const params = Object.fromEntries([...searchParams.entries()]);
setAppliedParams(params);
} else {
const params = Object.fromEntries([...searchParams.entries(), ['end_date', endDateValue]]);
setAppliedParams(params);
}
};
const filterEndDate = {
useFilter: false,
endDate: endDateValue,
setEndDate: setEndDateValue,
handleEndDateChange: handleEndDateChanges,
};
/* -------------------------------- handle export --------------------------- */
const handleExportReport = async () => {
var filter = Object.fromEntries([...searchParams.entries()]);
await axios
.get('claims/export', { params: filter })
.then((res) => {
enqueueSnackbar('Data berhasil di Export', {
variant: 'success',
anchorOrigin: { horizontal: 'right', vertical: 'top' },
});
document.location.href = res.data.data.file_url;
})
.catch((err) =>
enqueueSnackbar('Data Gagal di Export', {
variant: 'error',
anchorOrigin: { horizontal: 'right', vertical: 'top' },
})
);
};
const exportReport = {
useExport: false,
startDate: startDateValue,
endDate: endDateValue,
status: statusValue,
handleExportReport: handleExportReport
}
/* -------------------------------- headCell -------------------------------- */
const headCells: HeadCell<never>[] = [
{
id: 'admission_date',
align: 'center',
label: 'Admission Date',
isSort: true,
},
{
id: 'discharge_date',
align: 'center',
label: 'Discharge Date',
isSort: true,
},
{
id: 'code',
align: 'left',
label: 'Code',
isSort: true,
},
{
id: 'status',
align: 'center',
label: 'Status',
isSort: false,
},
{
id: 'action',
align: 'center',
label: '',
isSort: false,
},
];
/* -------------------------------------------------------------------------- */
useEffect(() => {
(async () => {
setIsLoading(true);
await new Promise((resolve) => setTimeout(resolve, 250));
const parameters =
Object.keys(appliedParams).length !== 0
? appliedParams
: Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]);
const response = await axios.get(`${corporateValue}/alarm-center-members/${id}`, {
params: { ...parameters },
});
const status = [
{"id": 1, "name": "Done" },
{"id": 0, "name": "On Going" },
]
setStatusData(status)
const datatable = response.data.data;
// if (response.data.data.length > 0){
// setIsLoading(true);
// } else {
// setIsLoading(false);
// }
const dataName = response.data.data[0].fullName
setName(dataName)
setData(
datatable.map((obj: any) => {
return {
...obj,
admission_date:
<Label>{ fDateSuffix(obj.admission_date) }</Label>
,
discharge_date:
<Label>{ fDateSuffix(obj.discharge_date) }</Label>
,
status:
obj.status === 'Done' ? (
<Label color='success'>
Done
</Label>
) : (
<Label color='warning'>
Ongoing
</Label>
),
action:
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate('service-monitoring/'+obj.claim_id )}>
<VisibilityOutlinedIcon />
View
</MenuItem>
</>
} />
};
})
);
setPaginationTable(response.data);
setRowsPerPage(response.data.per_page);
if (searchParams.get('page')) {
//@ts-ignore
const currentPage = parseInt(searchParams.get('page')) - 1;
paginationTable.current_page = currentPage;
setPage(currentPage);
}
setIsLoading(false);
})();
}, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]);
console.log(loadings);
return (
<Grid container spacing={8}>
<Grid item xs={12} paddingX="24px" >
<Stack direction="row" alignItems="center">
<ArrowBackIosIcon
onClick={() => navigate(`/alarm-center`)}
sx={{ cursor: 'pointer' }}
/>
<Typography variant="h5" sx={{ flexGrow: 1 }}>
{name}
</Typography>
</Stack>
</Grid>
<Grid item xs={12}>
<Stack>
<TableComponent
headCells={headCells}
rows={data}
orders={orders}
paginations={paginations}
loadings={loadings}
params={params}
searchs={searchs}
// filters={filters}
filterStatus={filterStatus}
filterStartDate={filterStartDate}
filterEndDate={filterEndDate}
exportReport={exportReport}
/>
</Stack>
</Grid>
</Grid>
);
}

View File

@@ -9,9 +9,15 @@ import {
Card,
Stack,
Typography,
TableHead,
TableCell,
TableBody,
Table,
TableRow,
Link,
} from '@mui/material';
import { styled } from '@mui/material/styles';
import { Favorite } from '@mui/icons-material';
import DownloadIcon from '@mui/icons-material/Download';
// components
import Page from '../../components/Page';
import Iconify from '../../components/Iconify';
@@ -21,8 +27,16 @@ import { useState, SyntheticEvent, useContext, useEffect } from 'react';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { useNavigate, useParams } from 'react-router-dom';
import axios from '../../utils/axios';
import { fDate } from '../../utils/formatTime';
import { fDate, fDateSuffix, fDateTime, fDateTimeSuffix } from '../../utils/formatTime';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import TimelineItem, { timelineItemClasses } from '@mui/lab/TimelineItem';
import { Timeline, TimelineConnector, TimelineContent, TimelineDot, TimelineSeparator } from '@mui/lab';
import Select from '../../theme/overrides/Select';
import TableMoreMenu from '../../components/table/TableMoreMenu';
import { MenuItem } from '@mui/material';
import Label from '../../components/Label';
// sections
// import ListTable from '../../sections/claimreports/ListTable';
// import ClaimStatusCard from '../../sections/claimreports/ClaimStatusCard';
@@ -42,7 +56,7 @@ function TabPanel(props: TabPanelProps) {
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
style={{ backgroundColor: '#F9FAFB' }}
// style={{ backgroundColor: '#F9FAFB' }}
{...other}
>
{value === index && (
@@ -61,6 +75,8 @@ function a11yProps(index: number) {
};
}
interface StyledTabsProps {
children?: React.ReactNode;
value: number;
@@ -71,7 +87,7 @@ const StyledTabs = styled((props: StyledTabsProps) => <Tabs {...props} />)({
'& .MuiTabs-indicator': {
display: 'flex',
justifyContent: 'center',
backgroundColor: 'transparent',
backgroundColor: '19BBBB',
},
'& .MuiTabs-indicatorSpan': {
maxWidth: 40,
@@ -85,27 +101,73 @@ interface StyledTabProps {
icon?: string | React.ReactElement;
}
const StyledTab = styled((props: StyledTabProps) => <Tab disableRipple {...props} />)(
({ theme }) => ({
textTransform: 'none',
fontWeight: 500,
fontSize: theme.typography.pxToRem(20),
color: theme.palette.primary.main,
maxWidth: '100%',
color: '#637381',
maxWidth: '15%',
flex: 1,
margin: '0 !important',
'&.Mui-selected': {
color: '#FFF',
backgroundColor: theme.palette.primary.main,
color: '#212B36',
},
'&:hover': {
backgroundColor: theme.palette.primary.dark,
color: '#FFF',
color: '#212B36',
opacity: 1,
},
})
);
type Data = {
id: number,
company_name: string,
member_name: string,
member_code: string,
member_id: number,
phone: string,
email: string,
birth_date: string,
symptoms: string,
sign: string,
main_diagnose: string,
main_diagnose_code: string,
comparative_diagnosis: string,
comparative_diagnosis_code: string,
service_name: string,
benefit_name: string,
hospital: string,
admission_date: string,
discharge_date: string,
dialy_monitoring: DailyMonitoring[],
laboratorium_result: LaboratoriumResult[],
}
type DailyMonitoring = {
date: string,
time: string,
status: string,
subject_title: string,
body_temperature: string,
sistole: string,
diastole: string,
respiration_rate: string,
analisis_title: string,
Perencanaan: string,
}
type LaboratoriumResult = {
datetime: string,
reimbursement_code: string,
examination: string,
location: string,
files: string,
}
export default function ServiceMonitoring() {
const { themeStretch } = useSettings();
const navigate = useNavigate();
@@ -115,234 +177,389 @@ export default function ServiceMonitoring() {
setValue(newValue);
};
const [data, setData] = useState({});
const [data, setData] = useState<Data | null>(null);
const [corporate, setCorporate] = useState();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const { id } = useParams();
const claimId = '2';
console.log('id', id);
useEffect(() => {
console.log('fetching data...');
axios
.get('/data/' + id)
.then((response) => {
console.log('data fetched...', response.data);
setData(response.data);
})
.catch((error) => {
console.error('error fetching data...', error);
});
(async () => {
axios
.get('/corporate-manage/' + corporateValue)
.then((response) => {
console.log('corporate fetched...', response.data);
setCorporate(response.data);
})
.catch((error) => {
console.error('error fetching corporate...', error);
});
}, []);
await new Promise((resolve) => setTimeout(resolve, 250));
console.log('Data:', data);
const [encounterData, setEncounterData] = useState({});
useEffect(() => {
console.log('fetching encounter data...');
axios
.get('/claims/${claim_id}/encounters')
.then((response) => {
console.log('encounter data fetched...', response.data);
setEncounterData(response.data);
})
.catch((error) => {
console.error('error fetching encounter data...', error);
});
}, []);
const response = await axios.get(`${corporateValue}/service-monitoring/${id}`);
setData(response.data.data);
})();
}, [corporateValue]);
function handleDownloadClick(fileUrl: string) {
const tempLink = document.createElement('a');
tempLink.href = fileUrl;
tempLink.setAttribute('download', 'nama-file-yang-diunduh.pdf'); // Ganti 'nama-file-yang-diunduh.pdf' sesuai kebutuhan
tempLink.style.display = 'none';
document.body.appendChild(tempLink);
tempLink.click();
document.body.removeChild(tempLink);
}
return (
<Page title="Service Monitoring 123456">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center" sx={{ marginBottom: 2 }}>
<IconButton
onClick={() => navigate('/alarm-center')}
sx={{ marginRight: '10px', color: '#424242' }}
>
<Iconify icon="heroicons-outline:arrow-narrow-left" />
</IconButton>
<Typography variant="h5">Service Monitoring</Typography>
<Stack
direction="row"
alignItems="center"
sx={{
backgroundColor: '#DFE3E8',
color: '#212B36',
paddingX: 2,
paddingY: 1,
marginLeft: 3,
borderRadius: 1,
}}
>
<Iconify icon="akar-icons:check" sx={{ marginRight: 1 }} />
<Typography variant="caption">Done</Typography>
</Stack>
</Stack>
<Grid container spacing={3} sx={{ marginBottom: 5 }}>
<Page title="Service Monitoring">
<Grid container spacing={3} paddingX={2} sx={{ marginBottom: 5, marginTop: 1 }} >
<Grid item xs={12} paddingX="24px" >
<Stack direction="row" alignItems="center">
<ArrowBackIosIcon
onClick={() => navigate(`/alarm-center/member/${data.member_id}}`)}
sx={{ cursor: 'pointer' }}
/>
<Typography variant="h5" sx={{ flexGrow: 1 }}>
Service Monitoring
</Typography>
</Stack>
</Grid>
{/* Item 1 */}
<Grid item xs={4} lg={4} md={6}>
<Grid item xs={12} lg={12} md={12}>
<Card sx={{ borderRadius: '6px' }}>
<Stack>
<Stack
direction="row"
alignItems="center"
sx={{ backgroundColor: '#F5F5F5', paddingY: 1, paddingX: 2, color: '#19BBBB' }}
sx={{ paddingY: 2, paddingX: 2, marginTop: 2 }}
>
<Iconify icon="bxs:user" width={22} height={18} sx={{ marginRight: '10px' }} />
<Typography>Employee Profiles</Typography>
<Typography variant='h5'>Employee Profiles</Typography>
</Stack>
<Stack direction="row" spacing={1} sx={{ paddingY: 2, paddingX: 2 }}>
<Grid item xs={6} lg={6} md={6}>
<Stack>
<Typography variant="inherit">Company Name</Typography>
<Typography variant="h6">{data?.company_name}</Typography>
</Stack>
</Grid>
<Grid item xs={6} lg={6} md={6}>
<Stack>
<Typography variant="inherit">Member ID</Typography>
<Typography variant="h6">{data?.member_code || 'Loading...'}</Typography>
</Stack>
</Grid>
</Stack>
<Stack direction="row" spacing={1} sx={{ paddingY: 1, paddingX: 2 }}>
<Stack spacing={2}>
<Grid item xs={6} lg={6} md={6}>
<Stack>
<Typography variant="caption">Nama perusahaan</Typography>
<Typography variant="body2">{corporate?.name}</Typography>
<Typography variant="inherit">Full Name</Typography>
<Typography variant="h6">{data?.member_name}</Typography>
</Stack>
</Grid>
<Grid item xs={6} lg={6} md={6}>
<Stack>
<Typography variant="caption">Nama Lengkap</Typography>
<Typography variant="body2">{data?.name || 'Loading...'}</Typography>
<Typography variant="inherit">Date of Birth</Typography>
<Typography variant="h6">{data?.birth_date ? fDateSuffix(data?.birth_date) : '-' || 'Loading...'}</Typography>
</Stack>
</Grid>
</Stack>
<Stack direction="row" spacing={1} sx={{ paddingY: 2, paddingX: 2 }}>
<Grid item xs={6} lg={6} md={6}>
<Stack>
<Typography variant="caption">Tanggal lahir</Typography>
<Typography variant="body2">
{data?.birth_date ? fDate(data?.birth_date) : ''}
</Typography>
<Typography variant="inherit">Phone Number</Typography>
<Typography variant="h6">{data?.phone}</Typography>
</Stack>
</Grid>
<Grid item xs={6} lg={6} md={6}>
<Stack>
<Typography variant="caption">Email</Typography>
<Typography variant="body2">{data?.email}</Typography>
<Typography variant="inherit">Email</Typography>
<Typography variant="h6">{data?.email || 'Loading...'}</Typography>
</Stack>
<Stack>
<Typography variant="caption">No telepon</Typography>
<Typography variant="body2">{data?.phone}</Typography>
</Stack>
</Stack>
<Stack>
<Typography variant="caption">ID Karyawan</Typography>
<Typography variant="body2">12345678</Typography>
</Stack>
</Grid>
</Stack>
</Stack>
</Card>
</Grid>
{/* Item 2 */}
<Grid item xs={4} lg={4} md={6}>
<Card sx={{ borderRadius: '6px', height: '100%' }}>
<Grid item xs={6} lg={6} md={6}>
<Card sx={{ borderRadius: '6px', paddingLeft:2 }}>
<Stack>
<Stack
direction="row"
alignItems="center"
sx={{ backgroundColor: '#F5F5F5', paddingY: 1, paddingX: 2, color: '#19BBBB' }}
sx={{ paddingY: 2, paddingX: 2, marginTop: 2 }}
>
<Iconify
icon="heroicons-solid:clipboard-list"
width={22}
height={18}
sx={{ marginRight: '10px' }}
/>
<Typography>Diagnose Summary</Typography>
<Typography variant='h5'>Diagnose Summary</Typography>
</Stack>
<Stack spacing={2} sx={{ paddingY: 1, paddingX: 2 }}>
<Stack>
<Typography variant="caption">Gejala</Typography>
<Typography variant="body2">Nyeri dada</Typography>
</Stack>
<Stack>
<Typography variant="caption">Tanda</Typography>
<Typography variant="body2">Sesak Napas</Typography>
</Stack>
<Stack>
<Typography variant="caption">Main Diagnose</Typography>
<Typography variant="body2">
J46 Status asthmaticus, Acute severe asthma
</Typography>
</Stack>
<Stack>
<Typography variant="caption">Diagnosis pembanding</Typography>
<Typography variant="body2">K21 Gastro-oesophageal reflux disease</Typography>
</Stack>
<Stack spacing={3} sx={{ paddingY: 2, paddingX: 2 }}>
<Grid item xs={12} lg={12} md={12}>
<Stack spacing={1}>
<Typography variant="inherit">Symptoms</Typography>
<Typography variant="h6">{data?.symptoms}</Typography>
</Stack>
</Grid>
<Grid item xs={12} lg={12} md={12}>
<Stack spacing={1}>
<Typography variant="inherit">Sign</Typography>
<Typography variant="h6">{data?.sign || 'Loading...'}</Typography>
</Stack>
</Grid>
<Grid item xs={12} lg={12} md={12}>
<Stack spacing={1}>
<Typography variant="inherit">Main Diagnosis</Typography>
<Stack direction={'row'} gap={1}>
<Typography variant="h6">{data?.main_diagnose}</Typography>
<Label>{data?.main_diagnose_code}</Label>
</Stack>
</Stack>
</Grid>
<Grid item xs={12} lg={12} md={12}>
<Stack spacing={1}>
<Typography variant="inherit">Comparative Diagnosis</Typography>
<Stack direction={'row'} gap={1}>
<Typography variant="h6">{data?.comparative_diagnosis}</Typography>
<Label>{data?.comparative_diagnosis_code}</Label>
</Stack>
</Stack>
</Grid>
<Grid item xs={12} lg={12} md={12}>
<Stack spacing={8}>
<Typography variant="inherit"></Typography>
<Typography variant="h6"></Typography>
</Stack>
</Grid>
</Stack>
</Stack>
</Card>
</Grid>
{/* Item 3 */}
<Grid item xs={4} lg={4} md={6}>
<Card sx={{ borderRadius: '6px', height: '100%' }}>
<Grid item xs={6} lg={6} md={6}>
<Card sx={{ borderRadius: '6px', paddingLeft:2 }}>
<Stack>
<Stack
direction="row"
alignItems="center"
sx={{ backgroundColor: '#F5F5F5', paddingY: 1, paddingX: 2, color: '#19BBBB' }}
sx={{ paddingY: 2, paddingX: 2, marginTop: 2 }}
>
<Iconify
icon="iconoir:healthcare"
width={22}
height={18}
sx={{ marginRight: '10px' }}
/>
<Typography>Services</Typography>
<Typography variant='h5'>Services</Typography>
</Stack>
<Stack direction="row" spacing={1} sx={{ paddingY: 1, paddingX: 2 }}>
<Stack spacing={2}>
<Stack>
<Typography variant="caption">Evakuasi medis</Typography>
<Typography variant="body2">Land Transportation</Typography>
<Stack spacing={3} sx={{ paddingY: 2, paddingX: 2 }}>
<Grid item xs={12} lg={12} md={12}>
<Stack spacing={1}>
<Typography variant="inherit">Code</Typography>
<Typography variant="h6">{data?.service_name}</Typography>
</Stack>
<Stack>
<Typography variant="caption">Rumah sakit</Typography>
<Typography variant="body2">Primaya Hospital</Typography>
</Grid>
<Grid item xs={12} lg={12} md={12}>
<Stack spacing={1}>
<Typography variant="inherit">Benefit Name</Typography>
<Typography variant="h6">{data?.benefit_name || 'Loading...'}</Typography>
</Stack>
<Stack direction="row" spacing={16}>
<Stack>
<Typography variant="caption">Tanggal mulai</Typography>
<Typography variant="body2">17 Aug 2022</Typography>
</Stack>
<Stack>
<Typography variant="caption">Selesai</Typography>
<Typography variant="body2">18 Aug 2022</Typography>
</Stack>
</Grid>
<Grid item xs={12} lg={12} md={12}>
<Stack spacing={1}>
<Typography variant="inherit">Hospital</Typography>
<Typography variant="h6">{data?.hospital}</Typography>
</Stack>
<Stack>
<Typography variant="caption">Daftar layanan</Typography>
<Typography variant="body2">
Inpatient, Medivac (Medical Evacuation)
</Typography>
</Grid>
<Grid item xs={12} lg={12} md={12}>
<Stack spacing={1}>
<Typography variant="inherit">Admission Date</Typography>
<Typography variant="h6">{data?.admission_date ? fDateSuffix(data?.admission_date) : '-'}</Typography>
</Stack>
</Stack>
</Grid>
<Grid item xs={12} lg={12} md={12}>
<Stack spacing={1}>
<Typography variant="inherit">Discharge Date</Typography>
<Typography variant="h6">{ data?.discharge_date ? fDateSuffix(data?.discharge_date) : '-'}</Typography>
</Stack>
</Grid>
</Stack>
</Stack>
</Card>
</Grid>
<Grid item sm>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<StyledTabs value={value} onChange={handleChange} aria-label="basic tabs example">
<StyledTab icon={<Favorite />} label="Daily Monitoring" {...a11yProps(0)} />
<StyledTab
icon={<Iconify icon="heroicons-solid:beaker" />}
label="Laboratorium Result"
{...a11yProps(1)}
/>
</StyledTabs>
</Box>
<TabPanel value={value} index={0}>
Item One
</TabPanel>
<TabPanel value={value} index={1}>
Item Two
</TabPanel>
<Grid item xs={12} lg={12} md={12}>
<Card sx={{ borderRadius: '6px' }}>
<Box sx={{ margin:5, borderBottom: 1, borderColor: 'divider' }}>
<StyledTabs value={value} onChange={handleChange} aria-label="basic tabs example">
<StyledTab label="Daily Monitoring" {...a11yProps(0)} />
<StyledTab
label="Laboratorium Result"
{...a11yProps(1)}
/>
</StyledTabs>
</Box>
<TabPanel value={value} index={0}>
<Grid container>
<Grid item xs={12} md={12}>
<Timeline sx={{
[`& .${timelineItemClasses.root}:before`]: {
flex: 0,
padding: 0,
},
}}>
{data?.dialy_monitoring.length > 0 ? data?.dialy_monitoring.map((row, index) => (
<TimelineItem key={index}>
<TimelineSeparator>
<TimelineDot />
<TimelineConnector sx={{border: '0.5px dashed rgba(145, 158, 171, 0.32)', backgrounSize: '4px 4px' }} />
</TimelineSeparator>
<TimelineContent>
<Typography variant='h5' sx={{marginBottom: 2}}> {fDateSuffix(row.date)}</Typography>
<Card sx={{paddinX:2, paddingY:2}} >
<Stack direction={'row'} sx={{ alignItems: 'center', padding: 2, justifyContent: 'space-between' }}>
<Label> {row.time} </Label>
<Label color='success' sx={{marginRight: 0}}> {row.status} </Label>
</Stack>
<hr style={{margin:10, marginLeft:15, marginRight:15, color: 'rgba(145, 158, 171, 0.32)' }}/>
<Stack spacing={3} sx={{ paddingY: 1, paddingX: 3 }}>
<Grid item xs={12} lg={12} md={12}>
<Stack spacing={1}>
<Typography variant="h6">Subject</Typography>
<Typography variant="inherit">{row.subject_title}</Typography>
</Stack>
</Grid>
<Grid item xs={12} lg={12} md={12}>
<Stack spacing={1}>
<Typography variant="h6" sx={{ paddingBottom: 2}}>Objektif</Typography>
</Stack>
<Stack direction={'row'} sx={{ paddingY: 1}} spacing={2}>
<Grid item xs={6} lg={6} md={6}>
<Stack>
<Typography variant='inherit'>Body Temperature</Typography>
</Stack>
</Grid>
<Grid item xs={6} lg={6} md={6}>
<Stack>
<Typography variant='subtitle1'>{row.body_temperature}</Typography>
</Stack>
</Grid>
</Stack>
<Stack direction={'row'} sx={{ paddingY: 1}} spacing={2}>
<Grid item xs={6} lg={6} md={6}>
<Stack>
<Typography variant='inherit'>Sistole</Typography>
</Stack>
</Grid>
<Grid item xs={6} lg={6} md={6}>
<Stack >
<Typography variant='subtitle1' >{row.sistole}</Typography>
</Stack>
</Grid>
</Stack>
<Stack direction={'row'} sx={{ paddingY: 1}} spacing={2}>
<Grid item xs={6} lg={6} md={6}>
<Stack>
<Typography variant='inherit'>Diastole</Typography>
</Stack>
</Grid>
<Grid item xs={6} lg={6} md={6}>
<Stack>
<Typography variant='subtitle1'>{row.diastole}</Typography>
</Stack>
</Grid>
</Stack>
<Stack direction={'row'} sx={{ paddingY: 1}} spacing={2}>
<Grid item xs={6} lg={6} md={6}>
<Stack>
<Typography variant='inherit'>Respiration Rate</Typography>
</Stack>
</Grid>
<Grid item xs={6} lg={6} md={6}>
<Stack>
<Typography variant='subtitle1'>{row.respiration_rate}</Typography>
</Stack>
</Grid>
</Stack>
<Stack spacing={1}>
<Typography variant="h6" sx={{ paddingTop: 2}}>Analysis</Typography>
</Stack>
<Stack direction={'row'} sx={{ paddingY: 2,}} spacing={2}>
<Grid item xs={12} lg={12} md={12}>
<Stack>
<Typography variant='inherit'>{row.analisis_title}</Typography>
</Stack>
</Grid>
</Stack>
<Stack spacing={1}>
<Typography variant="h6" sx={{ paddingTop: 2}}>Perencanaan</Typography>
</Stack>
<Stack direction={'row'} spacing={2}>
<Grid item xs={12} lg={12} md={12}>
<Stack marginLeft={5}>
<ul>
{row.Perencanaan.length > 0 ? row.Perencanaan.map((r, index) => (
<li key={index}>{r}</li>
)) : <li>-</li>}
</ul>
</Stack>
</Grid>
</Stack>
</Grid>
</Stack>
</Card>
</TimelineContent>
</TimelineItem>
)) :
<Typography variant='subtitle1'>
No Data Found
</Typography>
}
</Timeline>
</Grid>
</Grid>
</TabPanel>
<TabPanel value={value} index={1}>
<Grid container>
<Grid item xs={12} md={12}>
{data?.laboratorium_result.length > 0 ? data?.laboratorium_result.map((row, index) => (
<Card sx={{paddinX:2, paddingY:2, paddingX:1, marginTop:2 }} key={index} >
<Stack paddingX={5}>
<Typography variant='subtitle1'>{row[index]?.datetime ? fDateSuffix(row[index].datetime) : ''}</Typography>
{row.length > 0 ? row.map((list, i) => (
<Card key={i} sx={{marginTop:2, paddingX:2, paddingY:2}}>
<Label> {list.reimbursement_code}
</Label>
<Table sx={{marginY:2}}>
<TableHead>
<TableRow>
<TableCell>Date</TableCell>
<TableCell>Examination</TableCell>
<TableCell>Location</TableCell>
<TableCell></TableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<TableCell>
<Label> {fDateTime(list.datetime)}</Label>
</TableCell>
<TableCell>{list.examination}</TableCell>
<TableCell>{list.location}</TableCell>
<TableMoreMenu actions={
<>
<MenuItem onClick={() => handleDownloadClick(list.file)} >
<DownloadIcon />
Download
</MenuItem>
</>
} />
</TableRow>
</TableBody>
</Table>
</Card>
)) : <Typography variant='subtitle1'>Data Not Found</Typography>}
</Stack>
</Card>
)) :
<Typography variant='subtitle1'>
Data Not Found
</Typography>
}
</Grid>
</Grid>
</TabPanel>
</Card>
</Grid>
</Grid>
</Container>
</Page>
);
}

View File

@@ -24,7 +24,6 @@ export default function UserProfile() {
const { themeStretch } = useSettings();
// const navigate = useNavigate();
const [data, setData] = useState();
const [data1, setData1] = useState();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const { id } = useParams();
@@ -40,10 +39,10 @@ export default function UserProfile() {
});
}, []);
console.log('data', data);
// console.log('data', data);
return (
<Page title="Profile Peserta Jessica Lie">
<Page title="Profile">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center" sx={{ marginBottom: 2 }}>
{/* <IconButton sx={{ marginRight: '10px', color: '#424242' }} onClick={() => navigate()}>
@@ -62,7 +61,7 @@ export default function UserProfile() {
</Grid>
{/* Item 2 */}
<Grid item xs={12} md={12}>
<CardFamilyInformation data={data1} />
<CardFamilyInformation data={data} />
</Grid>
</Grid>
</Grid>
@@ -75,7 +74,7 @@ export default function UserProfile() {
</Grid>
{/* Item 2 */}
<Grid item xs={12}>
<CardClaimHistory />
<CardClaimHistory data={data}/>
</Grid>
</Grid>
</Grid>

View File

@@ -0,0 +1,69 @@
// mui
import { Container, Grid, Stack, Typography } from '@mui/material';
// components
import Page from '../../components/Page';
// utils
import useSettings from '../../hooks/useSettings';
// section
import CardFamilyInformation from '../../sections/alarm-center/user-profile/CardFamilyInformation';
// react
import { useNavigate, useParams } from 'react-router-dom';
import ButtonBack from '../../components/ButtonBack';
import { useEffect, useState, useContext } from 'react';
import axios from '../../utils/axios';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
// pages
import DetailTimeline from '../../pages/ClaimReport/DetailTimeline';
import DetailStepper from '../../pages/ClaimReport/DetailStepper';
import { format } from 'date-fns';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
// ----------------------------------------------------------------------
export default function Detail() {
const navigate = useNavigate();
const { themeStretch } = useSettings();
const [data, setData] = useState();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const { id } = useParams();
useEffect(() => {
axios
.get(corporateValue + '/claim-report/detail/' + id)
.then((response) => {
setData(response.data);
})
.catch((error) => {
console.error(error);
});
}, []);
return (
<Page title="Detail">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center" sx={{ marginBottom: 3 }}>
<ArrowBackIosIcon onClick={() => navigate(-1)} sx={{cursor:'pointer'}}/>
<Typography variant="h5" sx={{marginLeft:2}}>Detail</Typography>
{data ? (
<Stack direction="row" spacing={2} ml="auto">
<Typography variant="body2" sx={{color: '#757575'}}>Submission Date</Typography>
<Typography variant="body2" fontWeight="bold">{(data && data.data) ? format(new Date(data.data.status.submission_date), "d MMM yyyy") : ''}</Typography>
</Stack>
) : ''}
</Stack>
{data ? (
<Grid container spacing={2}>
<Grid item xs={12} md={12}>
<DetailStepper data={data}/>
</Grid>
<Grid item xs={12} md={12}>
<DetailTimeline data={data}/>
</Grid>
</Grid>
) : ''}
</Container>
</Page>
);
}

View File

@@ -0,0 +1,291 @@
// mui
import {
Container,
Grid,
Stack,
Typography,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Button,
Box,
TableSortLabel,
Avatar } from '@mui/material';
// components
import Page from '../../components/Page';
// utils
import useSettings from '../../hooks/useSettings';
// section
import CardFamilyInformation from '../../sections/alarm-center/user-profile/CardFamilyInformation';
// react
import { useNavigate, useParams } from 'react-router-dom';
import ButtonBack from '../../components/ButtonBack';
import { useEffect, useState, useContext } from 'react';
import axios from '../../utils/axios';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { format } from 'date-fns';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import { fCurrency } from '../../utils/formatNumber';
// ----------------------------------------------------------------------
export default function DetailHistory() {
const navigate = useNavigate();
const { themeStretch } = useSettings();
const [data, setData] = useState();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const { id } = useParams();
useEffect(() => {
axios
.get(corporateValue + '/claim-report/detail-history/' + id)
.then((response) => {
setData(response.data);
})
.catch((error) => {
console.error(error);
});
}, []);
return (
<Page title="Detail">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center" sx={{ marginBottom: 3 }}>
<ArrowBackIosIcon onClick={() => navigate(-1)} sx={{cursor:'pointer'}}/>
<Typography variant="h5" sx={{marginLeft:2}}>History</Typography>
</Stack>
{data ? (
<Grid container spacing={3}>
<Grid item xs={12} md={12}>
<Stack direction="row" sx={{marginBottom:2}}>
<Typography variant="body2" sx={{color:'#637381', marginRight:1, flexBasis: '10%'}}>Name</Typography>
<Typography variant="body2" sx={{fontWeight: 'bold'}}>{data.data.member.name}</Typography>
</Stack>
<Stack direction="row" sx={{marginBottom:2}}>
<Typography variant="body2" sx={{color:'#637381', marginRight:1, flexBasis: '10%'}}>Member ID</Typography>
<Typography variant="body2" sx={{fontWeight: 'bold'}}>{data.data.member.member_id}</Typography>
</Stack>
<Stack direction="row" sx={{marginBottom:2}}>
<Typography variant="body2" sx={{color:'#637381', marginRight:1, flexBasis: '10%'}}>Claim Code</Typography>
<Typography variant="body2" sx={{fontWeight: 'bold'}}>{data.data.member.code}</Typography>
</Stack>
</Grid>
<Grid item xs={12} md={12}>
<TableContainer>
<Table aria-label="collapsible table" size="small">
{/* Table Header */}
<TableHead>
<TableRow>
<TableCell
align= 'left'
sx={{ padding: 2 }}
width= '5%'
>
<Typography variant="body2" sx={{color:'#637381'}}>No</Typography>
</TableCell>
<TableCell
align= 'left'
sx={{ padding: 2 }}
width= '15%'
>
<Typography variant="body2" sx={{color:'#637381'}}>Date</Typography>
</TableCell>
<TableCell
align= 'left'
sx={{ padding: 2 }}
width= '20%'
>
<Typography variant="body2" sx={{color:'#637381'}}>Requirment</Typography>
</TableCell>
<TableCell
align= 'left'
sx={{ padding: 2 }}
width= '20%'
>
<Typography variant="body2" sx={{color:'#637381'}}>Request Claim</Typography>
</TableCell>
<TableCell
align= 'left'
sx={{ padding: 2 }}
width= '20%'
>
<Typography variant="body2" sx={{color:'#637381'}}>Approval Claim</Typography>
</TableCell>
</TableRow>
</TableHead>
{/* End Table Header */}
{/* Table Body */}
<TableBody>
{data?.data?.claim_item?.length === 0 ? (
<TableRow>
<TableCell colSpan={5} align="center">
<Typography variant="body2">No data available</Typography>
</TableCell>
</TableRow>
) : (
data.data.claim_item?.map((dataItem, index) => (
<TableRow key={index}>
<TableCell align="left">
<Typography variant="body2">{parseInt(index)+1}</Typography>
</TableCell>
<TableCell align="center">
<Typography
sx={{
backgroundColor: '#919EAB29',
color: '#637381',
borderRadius: '4px',
width: '70%',
}}
variant="body2"
>
{format(new Date(dataItem.submission_date), "d MMM yyyy")}
</Typography>
</TableCell>
<TableCell align="left">
{dataItem.description}
</TableCell>
<TableCell align="left">
{fCurrency(dataItem.nominal_ditagihkan)}
</TableCell>
<TableCell align="left">
{fCurrency(dataItem.nominal_dicover)}
</TableCell>
</TableRow>
))
)}
</TableBody>
{/* End Table Body */}
</Table>
</TableContainer>
</Grid>
{data.data.tot_claim_item ? (
<Grid item xs={12} md={12}>
<TableContainer>
<Table aria-label="collapsible table" size="small">
{/* Table Body */}
<TableBody>
<TableRow sx={{ borderBottom: 0 }}>
<TableCell
align= 'left'
width= '5%'
>
</TableCell>
<TableCell
align= 'left'
width= '15%'
>
</TableCell>
<TableCell
align= 'left'
width= '20%'
>
</TableCell>
<TableCell
align= 'left'
width= '20%'
>
<Typography variant="body2" sx={{color:'#637381'}}>Request Claim</Typography>
</TableCell>
<TableCell
align= 'left'
width= '20%'
>
<Typography variant="body2" sx={{fontWeight: 'bold'}}>{data.data.tot_claim_item.nominal_ditagihkan ? fCurrency(data.data.tot_claim_item.nominal_ditagihkan) : '-'}</Typography>
</TableCell>
</TableRow>
<TableRow sx={{ borderBottom: 0 }}>
<TableCell
align= 'left'
width= '5%'
>
</TableCell>
<TableCell
align= 'left'
width= '15%'
>
</TableCell>
<TableCell
align= 'left'
width= '20%'
>
</TableCell>
<TableCell
align= 'left'
width= '20%'
>
<Typography variant="body2" sx={{color:'#637381'}}>Approval Claim</Typography>
</TableCell>
<TableCell
align= 'left'
width= '20%'
>
<Typography variant="body2" sx={{fontWeight: 'bold', color: '#FF4842'}}>{data.data.tot_claim_item.nominal_dicover ? fCurrency(data.data.tot_claim_item.nominal_dicover) : '-'}</Typography>
</TableCell>
</TableRow>
<TableRow sx={{ borderBottom: 0 }}>
<TableCell
align= 'left'
width= '5%'
>
</TableCell>
<TableCell
align= 'left'
width= '15%'
>
</TableCell>
<TableCell
align= 'left'
width= '20%'
>
</TableCell>
<TableCell
align= 'left'
width= '20%'
>
<Typography variant="body2" sx={{fontWeight: 'bold'}}>Difference</Typography>
</TableCell>
<TableCell
align= 'left'
width= '20%'
>
<Typography variant="body2" sx={{fontWeight: 'bold'}}>{data.data.tot_claim_item.difference ? fCurrency(data.data.tot_claim_item.difference) : '-'}</Typography>
</TableCell>
</TableRow>
</TableBody>
{/* End Table Body */}
</Table>
</TableContainer>
<Stack
direction="row"
justifyContent="flex-end"
alignItems="flex-end"
sx={{ marginTop: 15, padding: 2 }}
spacing={1}
>
<Typography variant="body2" sx={{ fontStyle: 'italic', color: '#637381' }}>Note : Apabila terdapat perbedaan nominal silahkan hubungi kami </Typography>
<img alt="Gmail Icon" sx={{ height: 32, width: 30 }} src="/images/gmail.png" />
</Stack>
</Grid>
) : ''}
</Grid>
) : ''}
</Container>
</Page>
);
}

View File

@@ -0,0 +1,58 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Stepper from '@mui/material/Stepper';
import Step from '@mui/material/Step';
import StepLabel from '@mui/material/StepLabel';
import { useEffect, useState } from 'react';
import ClearIcon from '@mui/icons-material/Clear';
const steps = [
'Request',
'Review',
'Approval',
'Decline',
];
export default function HorizontalLinearAlternativeLabelStepper({data}) {
const [active, setActive] = useState(0);
const [status, SetStatus] = useState(null);
let updatedSteps = [...steps];
useEffect(() => {
if (data && data.data) {
if (data.data.status.status === 'requested') {
setActive(1);
updatedSteps = updatedSteps.filter(step => step !== 'Decline');
}
else if (data.data.status.status === 'reviewed') {
setActive(2);
updatedSteps = updatedSteps.filter(step => step !== 'Decline');
}
else if (data.data.status.status === 'approved')
{
setActive(3);
updatedSteps = updatedSteps.filter(step => step !== 'Decline');
}
else if(data.data.status.status === 'declined')
{
setActive(4)
updatedSteps = updatedSteps.filter(step => step !== 'Approval');
}
}
SetStatus(updatedSteps);
}, [data]);
return (
<Box sx={{ width: '100%', marginBottom: 2 }}>
<Stepper activeStep={active} alternativeLabel>
{status?.map((label) => (
<Step key={label}>
<StepLabel icon={label==='Decline' ? <ClearIcon sx={{ color: 'white', backgroundColor: 'red', borderRadius: '50%' }} /> : ''}>{label}</StepLabel>
</Step>
))}
</Stepper>
</Box>
);
}

View File

@@ -0,0 +1,428 @@
import * as React from 'react';
import Timeline from '@mui/lab/Timeline';
import TimelineItem, { timelineItemClasses } from '@mui/lab/TimelineItem';
import TimelineSeparator from '@mui/lab/TimelineSeparator';
import TimelineConnector from '@mui/lab/TimelineConnector';
import TimelineContent from '@mui/lab/TimelineContent';
import TimelineDot from '@mui/lab/TimelineDot';
import {Typography, Card, Stack, ButtonBase, Box, Divider} from '@mui/material';
import { styled } from '@mui/material/styles';
import Paper from '@mui/material/Paper';
import Button from '@mui/material/Button';
import AddIcon from '@mui/icons-material/Add';
import Iconify from '../../components/Iconify';
import { useEffect, useState, useRef, useContext } from 'react';
import { format } from 'date-fns';
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
import DescriptionIcon from '@mui/icons-material/Description';
import { LoadingButton } from '@mui/lab';
import axios from '../../utils/axios';
import { makeFormData } from '../../utils/jsonToFormData';
import { enqueueSnackbar } from 'notistack';
import { useParams} from 'react-router-dom';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
const Item1 = styled(Paper)(({ theme }) => ({
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: 'center',
backgroundColor: '#919EAB29',
color: '#637381',
width: 'fit-content',
marginRight: 'auto',
}));
const Item2 = styled(Paper)(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: 'center',
color: theme.palette.text.secondary,
width: 'fit-content',
marginLeft: 'auto',
}));
export default function NoOppositeContent({data}) {
const [timeline, setTimeline] = useState(null);
const [requestFile, setRequestFile] = useState(null);
const [document, setDocument] = useState(null);
useEffect(() => {
if (data && data.data) {
setTimeline(data.data.timeline);
setRequestFile(data.data.request_files);
setDocument(data.data.documents);
}
}, [data]);
// Diagnosis
const fileRequestDocumentInputDiagnosis = useRef<HTMLInputElement>(null);
const [fileDiagnosis, setFileDiagnosis] = useState([]);
const handleRequestDocumentInputChangeDiagnosis = (event) => {
if (event.target.files[0]) {
setFileDiagnosis([...fileDiagnosis, ...event.target.files]);
}
};
const removeFileDiagnois = (filesState, index) => {
setFileDiagnosis(
filesState.filter((file, fileIndex) => {
return fileIndex != index;
})
);
};
// Kondisi
const fileRequestDocumentInputKondisi = useRef<HTMLInputElement>(null);
const [fileKondisi, setFileKondisi] = useState([]);
const handleRequestDocumentInputChangeKondisi = (event) => {
if (event.target.files[0]) {
setFileKondisi([...fileKondisi, ...event.target.files]);
}
};
const removeFileKondisi = (filesState, index) => {
setFileKondisi(
filesState.filter((file, fileIndex) => {
return fileIndex != index;
})
);
};
// Result
const fileRequestDocumentInputResult = useRef<HTMLInputElement>(null);
const [fileResult, setFileResult] = useState([]);
const handleRequestDocumentInputChangeResult = (event) => {
if (event.target.files[0]) {
setFileResult([...fileResult, ...event.target.files]);
}
};
const removeFileResult = (filesState, index) => {
setFileResult(
filesState.filter((file, fileIndex) => {
return fileIndex != index;
})
);
};
const { corporateValue } = useContext(UserCurrentCorporateContext);
const { id } = useParams();
const [submitLoading, setSubmitLoading] = useState(false);
const submitRequestFiles = () => {
setSubmitLoading(true);
const formData = makeFormData({
fileDiagnosis: fileDiagnosis,
fileKondisis: fileKondisi,
fileResults: fileResult
});
axios
.post(corporateValue + '/claim-report/'+id+'/request-files', formData)
.then((response) => {
window.location.reload();
})
.catch(({ response }) => {
enqueueSnackbar(response.data.message ?? 'Something Went Wrong', { variant: 'error' });
});
}
const submitButton = requestFile?.find((dataRequestFile) => dataRequestFile.check_files === null);
return (
<>
{timeline?.map((dataTimeline, index) => (
<Timeline
sx={{
[`& .${timelineItemClasses.root}:before`]: {
flex: 0,
padding: 0,
},
}}
key={index}
>
<Typography variant="body2" gutterBottom fontWeight="bold">{dataTimeline.date ? format(new Date(dataTimeline.date), "d MMM yyyy") : ''}</Typography>
<TimelineItem>
<TimelineSeparator>
<TimelineDot />
<TimelineConnector />
</TimelineSeparator>
<TimelineContent spacing={3}>
<Card sx={{ borderRadius: '6px', paddingY: 2 }}>
<Stack sx={{marginLeft: 2, marginRight: 2, marginTop: 2 }}>
<Stack direction="row" sx={{marginBottom: 2, paddingBottom: 2, borderBottom: '1px solid #919EAB52' }}>
<Item1>{dataTimeline.date ? format(new Date(dataTimeline.date), "HH : mm") : ''}</Item1>
<Item2 sx={{backgroundColor: dataTimeline.txt_status_backgroundColor, color: dataTimeline.txt_status_color}}>{dataTimeline.txt_status}</Item2>
</Stack>
<Stack direction="row" spacing={2} sx={{marginBottom: 2}}>
<Typography variant="body2" gutterBottom>Detail:</Typography>
<Typography variant="body2" gutterBottom>{dataTimeline.description}</Typography>
</Stack>
{dataTimeline.status === 'reviewed' && requestFile ? (
<>
{submitButton ? (
<Typography variant="body2" gutterBottom>Request Document</Typography>
) : (
<Typography sx={{color: '#19BBBB'}} variant="body2" gutterBottom>Request Document Success Uploaded</Typography>
)}
{/* Diagnosis */}
{requestFile?.map((dataRequestFile, index) => {
if(dataRequestFile.type !== 'claim-diagnosis' || dataRequestFile.check_files !== null){
return null;
}
return (
<Stack spacing={2} sx={{marginBottom: 2}} key={index}>
<Typography variant="body2" gutterBottom fontWeight="bold">
Diagnosis
</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileDiagnosis &&
fileDiagnosis.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
<InsertDriveFileIcon />
<Typography variant="body2" gutterBottom>{file.name ? file.name : '-'}</Typography>
</Stack>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeFileDiagnois(fileDiagnosis, index);
}}
sx={{cursor: 'pointer'}}
></Iconify>
</Stack>
))}
</Stack>
<ButtonBase
sx={{
p: 4,
border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
width: '100%',
height: '60px',
}}
onClick={() => fileRequestDocumentInputDiagnosis.current?.click()}
>
<Box
sx={{
display: 'flex',
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
<Typography variant="body1" fontWeight="bold">
Add Result
</Typography>
</Box>
<input
type="file"
id={`file-${index}`}
ref={fileRequestDocumentInputDiagnosis}
style={{ display: 'none' }}
multiple
onChange={(event) => handleRequestDocumentInputChangeDiagnosis(event)}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
</ButtonBase>
</Stack>
);
})}
{/* Kondisi */}
{requestFile?.map((dataRequestFile, index) => {
if(dataRequestFile.type !== 'claim-kondisi' || dataRequestFile.check_files !== null){
return null;
}
return (
<Stack spacing={2} sx={{marginBottom: 2}} key={index}>
<Typography variant="body2" gutterBottom fontWeight="bold">
Condition
</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileKondisi &&
fileKondisi.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
<InsertDriveFileIcon />
<Typography variant="body2" gutterBottom>{file.name ? file.name : '-'}</Typography>
</Stack>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeFileKondisi(fileKondisi, index);
}}
sx={{cursor: 'pointer'}}
></Iconify>
</Stack>
))}
</Stack>
<ButtonBase
sx={{
p: 4,
border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
width: '100%',
height: '60px',
}}
onClick={() => fileRequestDocumentInputKondisi.current?.click()}
>
<Box
sx={{
display: 'flex',
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
<Typography variant="body1" fontWeight="bold">
Add Result
</Typography>
</Box>
<input
type="file"
id={`file-${index}`}
ref={fileRequestDocumentInputKondisi}
style={{ display: 'none' }}
multiple
onChange={(event) => handleRequestDocumentInputChangeKondisi(event)}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
</ButtonBase>
</Stack>
);
})}
{/* Supporting Result */}
{requestFile?.map((dataRequestFile, index) => {
if(dataRequestFile.type !== 'claim-result' || dataRequestFile.check_files !== null){
return null;
}
return (
<Stack spacing={2} sx={{marginBottom: 2}} key={index}>
<Typography variant="body2" gutterBottom fontWeight="bold">
Supporting Result
</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileResult &&
fileResult.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
<InsertDriveFileIcon />
<Typography variant="body2" gutterBottom>{file.name ? file.name : '-'}</Typography>
</Stack>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeFileResult(fileResult, index);
}}
sx={{cursor: 'pointer'}}
></Iconify>
</Stack>
))}
</Stack>
<ButtonBase
sx={{
p: 4,
border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
width: '100%',
height: '60px',
}}
onClick={() => fileRequestDocumentInputResult.current?.click()}
>
<Box
sx={{
display: 'flex',
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
<Typography variant="body1" fontWeight="bold">
Add Result
</Typography>
</Box>
<input
type="file"
id={`file-${index}`}
ref={fileRequestDocumentInputResult}
style={{ display: 'none' }}
multiple
onChange={(event) => handleRequestDocumentInputChangeResult(event)}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
</ButtonBase>
</Stack>
);
})}
{submitButton ? (
<LoadingButton
variant="contained"
sx={{ marginTop: 2, p: 2, backgroundColor: '#19BBBB' }}
onClick={() => {
submitRequestFiles();
}}
loading={submitLoading}
>
Submit
</LoadingButton>
) : ''}
</>
) : ''}
</Stack>
</Card>
{dataTimeline.status === 'requested' ? (
<Card sx={{marginTop: 2 }}>
<Stack sx={{marginLeft: 2, marginRight: 2, marginTop: 2 }}>
<Stack direction="row" spacing={2} sx={{marginBottom: 2, paddingBottom: 2, borderBottom: '1px solid #919EAB52' }} alignItems="center">
<DescriptionIcon />
<Typography variant="Subtitle2" sx={{fontWeight: 'bold'}}>Documents</Typography>
</Stack>
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
{document?.map((dataDocument, index) => (
<Stack direction="column" spacing={2} key={index}>
<Typography variant="Subtitle2" gutterBottom>
{dataDocument.type === 'claim-diagnosis' ?
'Diagnosis'
: dataDocument.type === 'claim-kondisi' ?
'Condition'
: dataDocument.type === 'claim-result' ?
'Supporting Result'
: dataDocument.type === 'claim-invoice' ?
'Invoice'
: ''}
</Typography>
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
<InsertDriveFileIcon />
<a
href={dataDocument.path}
style={{ cursor: 'pointer', textDecoration: 'underline', color: '#19BBBB' }}
target="_blank"
>
<Typography variant="body2" gutterBottom>{dataDocument.original_name ? dataDocument.original_name : '-'}</Typography>
</a>
</Stack>
</Stack>
))}
</Stack>
</Stack>
</Card>
) : ''}
</TimelineContent>
</TimelineItem>
</Timeline>
))}
</>
);
}

View File

@@ -1,183 +1,211 @@
// @mui
import {
Button,
Box,
Stepper,
Step,
StepLabel,
Card,
Typography,
Divider,
Stack,
} from '@mui/material';
import { Add } from '@mui/icons-material';
// components
import MuiDialog from '../../components/MuiDialog';
// theme
import palette from '../../theme/palette';
// React
import { ReactElement } from 'react';
type DataContent = {
info: string;
date: string;
time: string;
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data?: DataContent[];
};
const steps = ['Review', 'Approval', 'Disbursement'];
const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => {
function clickHandler(arg0: string) {
throw new Error('Function not implemented.');
}
import { Box, Stepper, Step, StepLabel, Card, Typography, Divider, Stack } from '@mui/material';
// React
import { useContext, useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router';
// axios
import axios from '../../utils/axios';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { fDate, fPostFormat } from '../../utils/formatTime';
import Iconify from '../../components/Iconify';
// const getContent = () => (
// );
return (
<>
<Stack
alignItems="center"
justifyContent="space-between"
direction="row"
sx={{ marginTop: 1 }}
>
<Typography variant="subtitle1" sx={{ height: 'max-content' }}>
Claim Request
</Typography>
<Stack>
<Typography variant="caption">Submission date</Typography>
<Typography variant="caption">15 / 05 / 2022</Typography>
</Stack>
</Stack>
<Box sx={{ width: '100%', marginTop: 2 }}>
<Stepper alternativeLabel>
{steps.map((label) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
</Box>
<Stack marginTop={2}>
<Typography variant="subtitle1" paddingY={2}>
17 Mei 2022
</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Divider orientation="vertical" flexItem sx={{ borderStyle: 'dashed' }} />
<Stack spacing={2} sx={{ flex: 1, maxWidth: '100%' }}>
{/* Item 1 */}
<Card sx={{ paddingY: 2, paddingX: 3 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="body1">09:10 WIB</Typography>
<Typography
sx={{
backgroundColor: palette.light.warning.lighter,
color: palette.light.warning.dark,
borderColor: palette.light.warning.dark,
border: '1px solid',
borderRadius: '6px',
padding: 1,
}}
variant="caption"
>
Approval
</Typography>
</Stack>
<Divider sx={{ marginY: 2 }} />
<Stack>
<Typography variant="subtitle2" color="#404040">
Details : mohon melengkapi kekurangan dokumen
</Typography>
<Typography variant="caption" color="#757575" sx={{ marginTop: 2, marginBottom: 1 }}>
Lab pemeriksaan darah
</Typography>
<Button
variant="outlined"
startIcon={<Add />}
fullWidth
sx={{ typography: 'subtitle2', borderColor: '#F5F5F5' }}
>
onClick={() => clickHandler('topUpLimit')}
Hasil Pemeriksaan Laboratorium
</Button>
</Stack>
</Card>
{/* Item 2 */}
<Card sx={{ flex: 1, maxWidth: '100%', paddingY: 2, paddingX: 3 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="body1">09:00 WIB</Typography>
<Typography
sx={{
backgroundColor: palette.light.warning.lighter,
color: palette.light.warning.dark,
borderColor: palette.light.warning.dark,
border: '1px solid',
borderRadius: '6px',
padding: 1,
}}
variant="caption"
>
Approval
</Typography>
</Stack>
<Divider sx={{ marginY: 2 }} />
<Stack>
<Typography variant="subtitle2" color="#404040">
Details : Penilaian Dokter
</Typography>
</Stack>
</Card>
{/* Item 3 */}
<Card sx={{ flex: 1, maxWidth: '100%', paddingY: 2, paddingX: 3 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="body1">08:00 WIB</Typography>
<Typography
sx={{
backgroundColor: '#F5F5F5',
color: '#757575',
borderColor: '#757575',
border: '1px solid',
borderRadius: '6px',
padding: 1,
}}
variant="caption"
>
Review
</Typography>
</Stack>
<Divider sx={{ marginY: 2 }} />
<Stack>
<Typography variant="subtitle2" color="#404040">
Details : Klaim Diajukan
</Typography>
</Stack>
</Card>
</Stack>
</Stack>
</>
// <MuiDialog
// title={title}
// openDialog={openDialog}
// setOpenDialog={setOpenDialog}
// content={getContent()}
// />
);
const steps = ['Review', 'Approval', 'Disbursement'];
interface DataHistoriesClaimRequest {
title: string;
description: string;
created_at: string;
}
interface DataFilesClaimRequest {
id: number;
fileName: string;
fileUrl: string;
}
interface DataClaimRequest {
claimId: number;
claimRequestId: number;
fullName: string;
histories: DataHistoriesClaimRequest[];
status: string;
submissionDate: string | Date;
files: {
claimConditions: DataFilesClaimRequest[];
claimResults: DataFilesClaimRequest[];
claimDiagnosis: DataFilesClaimRequest[];
};
export default DialogDetailClaim;
}
const DialogDetailClaim = () => {
const { id } = useParams();
const navigate = useNavigate();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const [corporateValueBefore, setCorporateValueBefore] = useState(corporateValue);
const [data, setData] = useState<DataClaimRequest | null>(null);
// inisialisasi pertama
useEffect(() => {
axios
.get(`${corporateValue}/claim-report/${id}`)
.then((response) => {
setData(response.data.data);
})
.catch((error) => {
console.error('Terjadi kesalahan:', error);
});
}, []);
// Navigasi ke claim-report ketika corporateValue berubah
useEffect(() => {
if (corporateValueBefore !== corporateValue) {
navigate(`/claim-report`);
}
}, [corporateValue]);
return (
<>
<Stack
alignItems="center"
justifyContent="space-between"
direction="row"
sx={{ marginTop: 1 }}
>
<Typography variant="subtitle1" sx={{ height: 'max-content' }}>
Claim Request for {data?.fullName}
</Typography>
<Stack>
<Typography variant="caption">Submission date</Typography>
<Typography variant="caption">
{fPostFormat(data ? data.submissionDate : new Date(), 'dd / MM / yyyy')}
</Typography>
</Stack>
</Stack>
<Box sx={{ width: '100%', marginTop: 2 }}>
<Stepper
activeStep={data?.status === 'approved' ? 1 : data?.status === 'requested' ? 0 : 2}
alternativeLabel
>
{steps.map((label) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
</Box>
<Stack marginTop={2}>
<Typography variant="subtitle1" paddingY={2}>
{fDate(data ? data?.histories[0].created_at : new Date())}
</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Divider orientation="vertical" flexItem sx={{ borderStyle: 'dashed' }} />
<Stack spacing={2} sx={{ flex: 1, maxWidth: '100%' }}>
{data?.histories.map((history, index) => (
<Card sx={{ paddingY: 2, paddingX: 3 }} key={`${history.title}-${index}`}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="body1">
{fPostFormat(history.created_at, 'HH:mm')} WIB
</Typography>
</Stack>
<Divider sx={{ marginY: 2 }} />
<Stack>
<Typography variant="subtitle2" color="#404040">
Details : {history.description}
</Typography>
<Typography
variant="caption"
color="#757575"
sx={{ marginTop: 2, marginBottom: 1 }}
>
{history.title}
</Typography>
</Stack>
</Card>
))}
<Card sx={{ paddingY: 2, paddingX: 3 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill" /> Dokumen Kelengkapan
</Typography>
</Stack>
<Divider sx={{ marginY: 2 }} />
<Typography fontWeight="600">Kondisi</Typography>
<Stack>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{data &&
data.files.claimConditions.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<a
href={file.fileUrl}
target="_blank"
style={{ textDecoration: 'none' }}
rel="noreferrer"
>
<Typography sx={{ color: 'text.secondary' }} variant="subtitle2">
- {file.fileName}
</Typography>
</a>
</Stack>
))}
</Stack>
<Typography fontWeight="600">Diagnosa</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{data &&
data.files.claimDiagnosis.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<a
href={file.fileUrl}
target="_blank"
style={{ textDecoration: 'none' }}
rel="noreferrer"
>
<Typography sx={{ color: 'text.secondary' }} variant="subtitle2">
- {file.fileName}
</Typography>
</a>
</Stack>
))}
</Stack>
<Typography fontWeight="600">Hasil</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{data &&
data.files.claimResults.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<a
href={file.fileUrl}
target="_blank"
style={{ textDecoration: 'none' }}
rel="noreferrer"
>
<Typography sx={{ color: 'text.secondary' }} variant="subtitle2">
- {file.fileName}
</Typography>
</a>
</Stack>
))}
</Stack>
</Stack>
</Card>
</Stack>
</Stack>
</>
);
};
export default DialogDetailClaim;

View File

@@ -4,7 +4,6 @@ import { useContext, useEffect, useState } from 'react';
import { Container, Grid } from '@mui/material';
/* ------------------------------- components ------------------------------- */
import Page from '../../components/Page';
import TableList from '../../components/Table';
/* ---------------------------------- hooks --------------------------------- */
import useSettings from '../../hooks/useSettings';
/* -------------------------------- sections -------------------------------- */
@@ -13,161 +12,67 @@ import CardClaimStatus from '../../sections/claim-report/CardClaimStatus';
import axios from '../../utils/axios';
/* --------------------------------- context -------------------------------- */
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
/* --------------------------------- orders --------------------------------- */
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams } from 'react-router-dom';
import List from './List';
import ClaimItems from '../Claims/components/ClaimItems';
import DiagnosisHistory from '../Claims/components/DiagnosisHistory';
import Documents from '../Claims/components/Documents';
// theme
import palette from '../../theme/palette';
import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs';
interface ClaimStatusType {
name: string;
value: number;
color: string;
}
export default function Drugs() {
const { themeStretch } = useSettings();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const [listClaimStatusItems, setListClaimStatusItems] = useState([]);
const [listAllMemberByClaimStatus, setListAllMemberByClaimStatus] = useState([]);
/* -------------------------------------------------------------------------- */
/* setTable */
/* -------------------------------------------------------------------------- */
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,
};
/* -------------------------------------------------------------------------- */
/* -------------------------------- 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: 'submissionDate',
align: 'center',
label: 'Submission Date',
isSort: true,
},
{
id: 'status',
align: 'center',
label: 'Status',
isSort: true,
},
];
/* -------------------------------------------------------------------------- */
const [listClaimStatusItems, setListClaimStatusItems] = useState<ClaimStatusType[]>([]);
useEffect(() => {
(async () => {
setIsLoading(true);
const claimStatus = await axios.get(`${corporateValue}/claim-report/claim-status`);
const parameters =
Object.keys(appliedParams).length !== 0
? appliedParams
: Object.fromEntries([
...searchParams.entries(),
['order', orders.order],
['orderBy', orders.orderBy],
]);
const claim = await axios.get(`${corporateValue}/members`, {
params: { ...parameters, type: 'claim-report' },
});
setSearchParams(parameters);
setListClaimStatusItems(claim.data.data.allClaimStatus);
setListAllMemberByClaimStatus(claim.data.data.allMembersByClaimStatus.data);
setPaginationTable(claim.data.data.allMembersByClaimStatus);
setIsLoading(false);
setListClaimStatusItems([
{
name: 'Requested',
value: claimStatus.data.data.requesteds,
color: '#159C9C',
},
{
name: 'Approval',
value: claimStatus.data.data.approveds,
color: '#229A16',
},
// {
// name: 'Disbrusment',
// value: claimStatus.data.data.disbrusments,
// color: '#BF6919',
// },
{
name: 'Decline',
value: claimStatus.data.data.rejecteds,
color: '#B72136',
},
]);
})();
}, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]);
}, [corporateValue]);
return (
<Page title="Claim Reports">
<Container maxWidth={themeStretch ? false : 'xl'}>
<HeaderBreadcrumbs
heading={'Claim Report'}
links={[
{ name: 'Case Management', href: '/claim-report' },
{ name: 'Claim Report', href: '/claim-report'}
]}
/>
<Grid container spacing={2}>
<Grid item xs={12} lg={12} md={12}>
<CardClaimStatus data={listClaimStatusItems} />
</Grid>
<Grid item xs={12} lg={12} md={12}>
<List/>
{/* <TableList
headCells={headCells}
rows={listAllMemberByClaimStatus}
orders={orders}
paginations={paginations}
loadings={loadings}
params={params}
/> */}
<List />
</Grid>
</Grid>
</Container>

View File

@@ -1,404 +1,268 @@
/* ---------------------------------- @mui ---------------------------------- */
import {
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Stack,
IconButton,
Button,
TableSortLabel,
Box,
} from '@mui/material';
import DialogDetailClaim from '../../sections/dashboard/DialogDetailClaim';
import { visuallyHidden } from '@mui/utils';
import { MoreVert as MoreVertIcon } from '@mui/icons-material';
/* ---------------------------------- axios --------------------------------- */
// import axios from 'axios';
import axios from '../../utils/axios';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
import BaseTablePagination from '../../components/BaseTablePagination';
import TableComponent from '../../components/Table';
import { Navigate } from 'react-router-dom';
/* ---------------------------------- hooks --------------------------------- */
import useMap from '../../hooks/useMap';
/* ---------------------------------- theme --------------------------------- */
import palette from '../../theme/palette';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams, useNavigate } from 'react-router-dom';
/* ---------------------------------- types --------------------------------- */
// type PaginationTableProps = {
// current_page: number;
// from: number;
// last_page: number;
// links: [];
// path: string;
// per_page: number;
// to: number;
// total: number;
// };
// type DataTableProps = {
// fullName: string;
// memberId: string;
// service: string;
// start_date: string;
// end_date: string;
// status: boolean | number;
// };
// /* -------------------------------------------------------------------------- */
// /* -------------------------- enchanced table head -------------------------- */
// type Order = 'asc' | 'desc';
// interface HeadCell {
// id: string;
// label: string;
// }
// const headCells: readonly HeadCell[] = [
// {
// id: 'name',
// label: 'Name',
// },
// {
// id: 'member_id',
// label: 'Member ID',
// },
// {
// id: 'service',
// label: 'Service',
// },
// {
// id: 'start_date',
// label: 'Start Date',
// },
// {
// id: 'end_date',
// label: 'End Date',
// },
// {
// id: 'status',
// label: 'Status',
// },
// ];
// interface EnhancedTableProps {
// onRequestSort: (event: React.MouseEvent<unknown>, property: string) => void;
// order: Order;
// orderBy: string;
// }
// function EnhancedTableHead(props: EnhancedTableProps) {
// const { order, orderBy, onRequestSort } = props;
// const createSortHandler = (property: string) => (event: React.MouseEvent<unknown>) => {
// onRequestSort(event, property);
// };
// return (
// <TableHead>
// <TableRow>
// <TableCell align="center">No</TableCell>
// {headCells.map((headCell) => (
// <TableCell
// key={headCell.id}
// sortDirection={orderBy === headCell.id ? order : false}
// align="center"
// >
// <TableSortLabel
// active={orderBy === headCell.id}
// direction={orderBy === headCell.id ? order : 'asc'}
// onClick={createSortHandler(headCell.id)}
// >
// {headCell.label}
// {orderBy === headCell.id ? (
// <Box component="span" sx={visuallyHidden}>
// {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
// </Box>
// ) : null}
// </TableSortLabel>
// </TableCell>
// ))}
// </TableRow>
// </TableHead>
// );
// }
import { Stack, Button, MenuItem } from '@mui/material';
/* ---------------------------------- axios --------------------------------- */
// import axios from 'axios';
import axios from '../../utils/axios';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
import TableComponent from '../../components/Table';
/* ---------------------------------- theme --------------------------------- */
import palette from '../../theme/palette';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams, useNavigate } from 'react-router-dom';
import { fDate, fDateSuffix } from '../../utils/formatTime';
import Typography from '@mui/material/Typography';
import { format } from 'date-fns';
import TableMoreMenu from '../../components/table/TableMoreMenu';
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
import HistoryIcon from '@mui/icons-material/History';
import SearchIcon from '@mui/icons-material/Search';
import Label from '../../components/Label';
export default function List() {
const navigate = useNavigate();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const [data, setData] = useState([]);
/* -------------------------------------------------------------------------- */
export default function List() {
const navigate = useNavigate();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const [data, setData] = 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);
/* 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>('desc');
const [orderBy, setOrderBy] = useState('member_id');
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: true,
searchText: searchText,
setSearchText: setSearchText,
handleSearchSubmit: handleSearchSubmit,
};
/* -------------------------------- headCell -------------------------------- */
const headCells: HeadCell<never>[] = [
{
id: 'submission_date',
align: 'center',
label: 'Request Date',
isSort: true,
},
{
id: 'member_id',
align: 'left',
label: 'Member ID',
isSort: true,
},
{
id: 'code',
align: 'left',
label: 'Claim Code',
isSort: true,
},
{
id: 'full_name',
align: 'left',
label: 'Name',
isSort: true,
},
{
id: 'division_name',
align: 'left',
label: 'Division',
isSort: true,
},
{
id: 'status',
align: 'center',
label: 'Status',
isSort: true,
},
{
id: 'action',
align: 'right',
label: '',
isSort: false,
},
];
useEffect(() => {
(async () => {
setIsLoading(true);
await new Promise((resolve) => setTimeout(resolve, 250));
const parameters =
Object.keys(appliedParams).length !== 0
? appliedParams
: Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]);
const response = await axios.get(`${corporateValue}/members`, {
params: { ...parameters, type: 'claim-report' },
});
setData(
response.data.data.map((obj: any) => ({
...obj,
status:
obj.status === 'requested' ? (
<Label color='primary'>
Request
</Label>
) : obj.status === 'approved' ? (
<Label color='success' >
Approval
</Label>
) : obj.status === 'declined' ? (
<Label color='error'>
Decline
</Label>
) : obj.status === 'pending' ? (
<Label color='primary'>
Pending
</Label>
) : obj.status === 'reviewed' ? (
<Label color='info'>
Review
</Label>
) : (
<Button
startIcon={<Iconify icon="fa6-solid:clock" />}
sx={{
backgroundColor: '#CD7B2E',
color: '#FFFF',
padding: '1px, 8px',
paddingY: 1,
'&:hover': {
backgroundColor: '#BF6919',
color: '#FFFF',
},
}}
>
Ongoing
</Button>
),
submission_date:
<Label>
{obj.submission_date ? fDateSuffix(obj.submission_date) : ''}
</Label>
,
action:
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate ('/claim-report/detail/'+obj.claimRequestId)}>
<SearchIcon />
Detail
</MenuItem>
<MenuItem onClick={() => navigate ('/claim-report/detail-history/'+obj.claimRequestId)}>
<HistoryIcon />
History
</MenuItem>
</>
} />
}))
);
setPaginationTable(response.data);
setRowsPerPage(response.data.per_page);
if (searchParams.get('page')) {
//@ts-ignore
const currentPage = parseInt(searchParams.get('page')) - 1;
paginationTable.current_page = currentPage;
setPage(currentPage);
}
};
const searchs = {
searchText: searchText,
setSearchText: setSearchText,
handleSearchSubmit: handleSearchSubmit,
};
/* -------------------------------- headCell -------------------------------- */
const headCells: HeadCell<never>[] = [
{
id: 'memberId',
align: 'left',
label: 'Member ID',
isSort: true,
},
{
id: 'fullName',
align: 'left',
label: 'Name',
isSort: true,
},
{
id: 'division',
align: 'left',
label: 'Divisi',
isSort: true,
},
/* {
id: 'end_date',
align: 'center',
label: 'End Date',
isSort: false,
}, */
{
id: 'status',
align: 'center',
label: 'Status',
isSort: true,
},
{
id: 'action',
align: 'right',
label: '',
isSort: false,
},
];
setIsLoading(false);
})();
}, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]);
const [open, setOpen] = useState<HTMLElement | null>(null);
const handleOpen = (event: React.MouseEvent<HTMLElement>) => {
setOpen(event.currentTarget);
};
const handleClose = () => {
setOpen(null);
};
/* const clickHandler = (isDialog: string) => {
switch (isDialog) {
case 'infoDetail':
setDialogTitle('Claim Details');
setIsDialog(isDialog);
setOpenDialog(true);
break;
default:
break;
}
}; */
/* -------------------------------------------------------------------------- */
useEffect(() => {
(async () => {
setIsLoading(true);
await new Promise((resolve) => setTimeout(resolve, 250));
const parameters =
Object.keys(appliedParams).length !== 0
? appliedParams
: Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]);
const response = await axios.get(`${corporateValue}/members`, {
params: { ...parameters },
});
setData(
response.data.data.map((obj: any) => {
return {
...obj,
/* memberId: <Button onClick={() => navigate ('user-profile/:id')}>{obj.memberId}</Button>, */
status:
obj.status === 1 ? (
<Button onClick={() => navigate('dialog-detail')}
/* startIcon={<Iconify icon="ic:round-check" />} */
sx={{
backgroundColor: 'rgba(84, 214, 44, 0.16)',
color: palette.dark.success.dark,
paddingX: 1.5,
paddingY: 1,
'&:hover': {
backgroundColor: 'rgba(84, 214, 44, 0.16)',
color: palette.dark.success.dark,
},
}}
>
Request
</Button>
) : (
<Button
startIcon={<Iconify icon="fa6-solid:clock" />}
sx={{
backgroundColor: '#CD7B2E',
color: '#FFFF',
paddingX: 1.5,
paddingY: 1,
'&:hover': {
backgroundColor: '#BF6919',
color: '#FFFF',
},
}}
>
Ongoing
</Button>
),
/* action: (
<IconButton onClick={() => clickHandler('infoDetail')}>
<MoreVertIcon />
</IconButton>
), */
};
})
);
setPaginationTable(response.data);
setRowsPerPage(response.data.per_page);
if (searchParams.get('page')) {
//@ts-ignore
const currentPage = parseInt(searchParams.get('page')) - 1;
paginationTable.current_page = currentPage;
setPage(currentPage);
}
setIsLoading(false);
})();
}, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]);
return (
<Stack>
<TableComponent
headCells={headCells}
rows={data}
orders={orders}
paginations={paginations}
loadings={loadings}
params={params}
searchs={searchs}
// filters={filters}
/>
</Stack>
);
}
return (
<Stack>
<TableComponent
headCells={headCells}
rows={data}
orders={orders}
paginations={paginations}
loadings={loadings}
params={params}
searchs={searchs}
/>
</Stack>
);
}

View File

@@ -0,0 +1,505 @@
// @mui
import { styled } from '@mui/material/styles';
import {
Button,
Box,
LinearProgress,
linearProgressClasses,
Stepper,
Step,
StepLabel,
Card,
Typography,
Divider,
Stack,
Grid,
Avatar,
ButtonBase,
} from '@mui/material';
import { Add } from '@mui/icons-material';
// components
import MuiDialog from '../../components/MuiDialog';
/*------------------------------------ icon ----------------------------------- */
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
// theme
import palette from '../../theme/palette';
// React
import { Fragment, ReactElement, useEffect, useRef, useState } from 'react';
import { useSearchParams, useNavigate, Link } from 'react-router-dom';
import { fPostFormat } from '../../utils/formatTime';
import { fCurrency } from '../../utils/formatNumber';
import { LoadingButton } from '@mui/lab';
import Iconify from '../../components/Iconify';
import { useSelector } from 'react-redux';
import { RootState } from '../../store';
import { makeFormData } from '../../utils/jsonToFormData';
import { useSnackbar } from 'notistack';
import axiosInstance from '../../utils/axios';
// -------------------------------- type --------------------------------------
type DataContentType = {
id: number;
fullName: string;
memberId: string;
limit: {
current: number;
total: number;
percentage: number;
};
avatar?: {
url?: string;
title?: string;
};
};
type ClaimSubmission = {
id: number;
personID: string;
personName: string;
typePatient: string;
limit: {
current: number;
total: number;
percentage: number;
};
avatar: {
url: string;
};
fileRealInvoice: any[];
anotherDocument: any[];
laboratoryResult: any[];
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data: DataContentType;
};
/* --------------------------------- styles --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800],
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
background: 'linear-gradient(270deg, #19BBBB 38.42%, #FF9565 76.21%, #FE7253 104.02%)',
},
}));
/* -------------------------------------------------------------------------- */
const steps = ['Review', 'Approval', 'Disbursement'];
const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => {
const navigate = useNavigate();
const { enqueueSnackbar } = useSnackbar();
const [submitLoading, setSubmitLoading] = useState(false);
const selectedData = useSelector((state: RootState) => state.claims.data);
const [dataContent, setDataContent] = useState<ClaimSubmission[]>([]);
useEffect(() => {
if (selectedData.length > 0) {
let temp: ClaimSubmission[] = selectedData.map((item) => ({
id: item.id,
avatar: {
url: '',
},
limit: item.limit,
personID: item.memberId,
personName: item.fullName,
typePatient: 'IP',
service_type: item.service_type,
anotherDocument: [],
fileRealInvoice: [],
laboratoryResult: [],
}));
setDataContent(temp);
} else {
navigate('/claim-submit', { replace: true });
}
}, [selectedData]);
const handleServiceCode = (data: ClaimSubmission, index?: number) => {
let temp = dataContent.map((item) => {
if (item.personID === data.personID) {
return {
...item,
typePatient: item.typePatient === 'IP' ? 'OP' : 'IP',
};
} else {
return item;
}
});
setDataContent(temp);
};
const handleInputFile = (
event: any,
data: ClaimSubmission,
typeFile: 'invoice' | 'another' | 'lab'
) => {
if (event.target.files[0]) {
let temp = dataContent.map((item) => {
if (item.personID === data.personID) {
if (typeFile === 'invoice') {
return {
...item,
fileRealInvoice: [...item.fileRealInvoice, event.target.files[0]],
};
} else if (typeFile === 'another') {
return {
...item,
anotherDocument: [...item.anotherDocument, event.target.files[0]],
};
} else {
return {
...item,
laboratoryResult: [...item.laboratoryResult, event.target.files[0]],
};
}
} else {
return item;
}
});
setDataContent(temp);
} else {
console.log('NO FILE');
}
};
const handleRemoveFile = (
data: ClaimSubmission,
typeFile: 'invoice' | 'another' | 'lab',
index: number
) => {
let temp = dataContent.map((item) => {
if (item.personID === data.personID) {
if (typeFile === 'invoice') {
return {
...item,
fileRealInvoice: item.fileRealInvoice.filter((file, fileIndex) => fileIndex != index),
};
} else if (typeFile === 'another') {
return {
...item,
anotherDocument: item.anotherDocument.filter((file, fileIndex) => fileIndex != index),
};
} else {
return {
...item,
laboratoryResult: item.laboratoryResult.filter((file, fileIndex) => fileIndex != index),
};
}
} else {
return item;
}
});
setDataContent(temp);
};
const onSubmit = () => {
setSubmitLoading(true);
const mapArrayToFormData = (claims: ClaimSubmission[]): FormData => {
const formData = new FormData();
claims.forEach((claim, index) => {
formData.append(`member_id[${index}]`, claim.id.toString());
formData.append(`service_code[${index}]`, claim.typePatient);
claim.laboratoryResult.forEach((file, fileIndex) => {
formData.append(`laboratorium[member_${claim.id}][${fileIndex}]`, file);
});
claim.anotherDocument.forEach((file, fileIndex) => {
formData.append(`prescription[member_${claim.id}][${fileIndex}]`, file);
});
claim.fileRealInvoice.forEach((file, fileIndex) => {
formData.append(`invoice[member_${claim.id}][${fileIndex}]`, file);
});
});
return formData;
};
const formData = mapArrayToFormData(dataContent);
axiosInstance
.post('/claim-requests', formData)
.then((response) => {
enqueueSnackbar(response.data.message ?? 'Berhasil membuat data', { variant: 'success' });
navigate('/claim-submit', { replace: true });
})
.catch(({ response }) => {
enqueueSnackbar(response.data.message ?? 'Something Went Wrong', { variant: 'error' });
})
.finally(() => {
setSubmitLoading(false);
});
};
return (
<>
<Grid container spacing={8}>
{/* Field 1 */}
<Grid item xs={12} paddingX="24px" paddingY="20px">
<Stack direction="row" alignItems="center">
<ArrowBackIosIcon
onClick={() => navigate(`/claim-submit`)}
sx={{ cursor: 'pointer' }}
/>
<Typography variant="h5" sx={{ flexGrow: 1 }}>
Claim Submission{' '}
</Typography>
<Typography variant="inherit" sx={{ textAlign: 'center', flexBasis: '15%' }}>
Submission Date{' '}
</Typography>
<Typography textAlign={'right'} variant="h6" sx={{ textAlign: 'right' }}>
{fPostFormat(new Date(), 'dd MMM yyyy')}
</Typography>
</Stack>
</Grid>
{dataContent.map((row, index) => {
return (
<Grid item xs={12} key={index}>
<Card sx={{ p: 3 }}>
<Grid container spacing={4} key={index}>
<Grid item xs={12} paddingX="24px" paddingY="20px">
<Stack direction="row" spacing={4}>
{row.service_type.map((r, i) => {
return(
<Button
sx={{
padding: 2,
width: '50%',
border:
row.typePatient === r.service_code ? '1px solid #919EAB52' : '1px solid #19BBBB',
}}
variant="outlined"
color={row.typePatient === r.service_code ? 'primary' : 'inherit'}
onClick={() => {
handleServiceCode(row);
}}
>
{r.service_code == 'IP' ? 'Inpatient' : 'Outpatient'}
</Button>
)
})}
{/* <Button
sx={{
padding: 2,
width: '50%',
border:
row.typePatient === 'IP' ? '1px solid #919EAB52' : '1px solid #19BBBB',
}}
variant="outlined"
color={row.typePatient === 'OP' ? 'primary' : 'inherit'}
onClick={() => {
handleServiceCode(row);
}}
>
Outpatient
</Button> */}
</Stack>
</Grid>
{/* REAL INVOICE */}
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="h6">Real Invoice</Typography>
</Grid>
{row.fileRealInvoice &&
row.fileRealInvoice.map((file, fileIndex) => (
<Grid item xs={12} key={fileIndex}>
<Stack direction="row" justifyContent={'space-between'}>
<Typography sx={{ color: 'text.secondary' }}>{file.name}</Typography>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
handleRemoveFile(row, 'invoice', fileIndex);
}}
/>
</Stack>
</Grid>
))}
<Grid item xs={12}>
<Box
sx={{
display: 'flex',
placeContent: 'center',
placeItems: 'center',
border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
p: 0,
}}
>
<Buttons handle={handleInputFile} row={row} type="invoice" />
</Box>
</Grid>
</Grid>
</Grid>
{/* DOCTOR'S PRESCRIPTION AND ANOTHER DOCUMENTS */}
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="h6">
Doctor's Prescription and Another Documents
</Typography>
</Grid>
{row.anotherDocument &&
row.anotherDocument.map((file, fileIndex) => (
<Grid item xs={12} key={fileIndex}>
<Stack direction="row" justifyContent={'space-between'}>
<Typography sx={{ color: 'text.secondary' }}>{file.name}</Typography>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
handleRemoveFile(row, 'another', fileIndex);
}}
/>
</Stack>
</Grid>
))}
<Grid item xs={12}>
<Box
sx={{
display: 'flex',
placeContent: 'center',
placeItems: 'center',
border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
p: 0,
}}
>
<Buttons handle={handleInputFile} row={row} type="another" />
</Box>
</Grid>
</Grid>
</Grid>
{/* LABORATORY RESULTS */}
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="h6">Laboraroty Results</Typography>
</Grid>
{row.laboratoryResult &&
row.laboratoryResult.map((file, fileIndex) => (
<Grid item xs={12} key={fileIndex}>
<Stack direction="row" justifyContent={'space-between'}>
<Typography sx={{ color: 'text.secondary' }}>{file.name}</Typography>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
handleRemoveFile(row, 'lab', fileIndex);
}}
/>
</Stack>
</Grid>
))}
</Grid>
<Grid item xs={12}>
<Box
sx={{
display: 'flex',
placeContent: 'center',
placeItems: 'center',
border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
p: 0,
}}
>
<Buttons handle={handleInputFile} row={row} type="lab" />
</Box>
</Grid>
</Grid>
</Grid>
</Card>
</Grid>
);
})}
<Grid item xs={12}>
<LoadingButton
variant="contained"
sx={{ marginTop: 2, p: 2, margin: '10px' }}
fullWidth
onClick={onSubmit}
loading={submitLoading}
>
Claim Submit
</LoadingButton>
</Grid>
</Grid>
</>
);
};
// let temp = [
// {
// member: "",
// result_file: [
// "file.pdf",
// "file2.pdf"
// ]
// }
// ]
export default DialogDetailClaim;
type ButtonIProp = {
row: ClaimSubmission;
type: 'invoice' | 'another' | 'lab';
handle: (event: any, row: any, type: any) => void;
};
const Buttons = ({ handle, row, type }: ButtonIProp) => {
const ref = useRef<HTMLInputElement>(null);
return (
<ButtonBase sx={{ p: 4 }} onClick={() => ref.current?.click()}>
<Box
sx={{
display: 'flex',
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
<Typography variant="body1" fontWeight="bold">
Add {type}
</Typography>
</Box>
<input
ref={ref}
hidden
accept="application/pdf"
type="file"
name="file"
multiple
onChange={(event) => handle(event, row, type)}
/>
</ButtonBase>
);
};

View File

@@ -0,0 +1,276 @@
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
/* ----------------------------------- mui ---------------------------------- */
import { Container, Grid, Typography } from '@mui/material';
/* ------------------------------- components ------------------------------- */
import Page from '../../components/Page';
import TableList from '../../components/Table';
/* ---------------------------------- hooks --------------------------------- */
import useSettings from '../../hooks/useSettings';
/* -------------------------------- sections -------------------------------- */
import CardClaimStatus from '../../sections/claim-submit/CardClaimStatus';
/* ---------------------------------- utils --------------------------------- */
import axios from '../../utils/axios';
/* --------------------------------- context -------------------------------- */
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
/* --------------------------------- orders --------------------------------- */
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams } from 'react-router-dom';
import List from './List';
import ClaimItems from '../Claims/components/ClaimItems';
import DiagnosisHistory from '../Claims/components/DiagnosisHistory';
import Documents from '../Claims/components/Documents';
// theme
import palette from '../../theme/palette';
import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs';
import { Stack } from '@mui/system';
import { fDateSuffix } from '../../utils/formatTime';
import DialogClaimSubmitMember from '../../sections/claim-submit/DialogClaimSubmitMember';
interface ClaimStatusType {
name: string;
value: number;
color: string;
}
/* ------------------------------ default data ------------------------------ */
type DataMember = {
id: number;
fullName: string;
memberId: string;
limit: {
current: number;
total: number;
percentage: number;
};
avatar?: {
url?: string;
title?: string;
};
};
type CardPolicyProps = {
limit: {
myLimit: {
balance: number;
total: number;
percentage: number;
};
lockLimit: {
balance: number;
percentage: number;
};
};
topUpLimit: {
companyName: string;
policyNumber: number;
totalMembers: number;
totalCases: number;
totalPersen: number;
myLimit: {
balance: number;
total: number;
percentage: number;
};
maxTopUp: number;
};
members?: DataMember[];
};
export default function Drugs() {
const { themeStretch } = useSettings();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const [listClaimStatusItems, setListClaimStatusItems] = useState<ClaimStatusType[]>([]);
const [listAllMemberByClaimStatus, setListAllMemberByClaimStatus] = useState([]);
/* -------------------------------------------------------------------------- */
/* setTable */
/* -------------------------------------------------------------------------- */
const [isLoading, setIsLoading] = useState(true);
const loadings = {
isLoading: isLoading,
setIsLoading: setIsLoading,
};
/* ------------------------------ handle params ----------------------------- */
const [searchParams, setSearchParams] = useSearchParams();
const [appliedParams, setAppliedParams] = useState({});
const [policyData, setPolicyData] = useState<CardPolicyProps>();
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,
};
/* ---------------------------- Get Current Date ---------------------------- */
const current = new Date();
const date = fDateSuffix(current);
/* -------------------------------------------------------------------------- */
/* ---------------------------- 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,
};
/* -------------------------------------------------------------------------- */
/* -------------------------------- headCell -------------------------------- */
const headCells: HeadCell<never>[] = [
{
id: 'memberId',
align: 'left',
label: 'Member ID',
isSort: true,
},
{
id: 'codeRequest',
align: 'left',
label: 'Code Request',
isSort: true,
},
{
id: 'fullName',
align: 'center',
label: 'Name',
isSort: true,
},
{
id: 'division',
align: 'center',
label: 'Divisi',
isSort: true,
},
{
id: 'submissionDate',
align: 'center',
label: 'Submission Date',
isSort: true,
},
{
id: 'status',
align: 'center',
label: 'Status',
isSort: true,
},
];
/* -------------------------------------------------------------------------- */
useEffect(() => {
(async () => {
setIsLoading(true);
const claimStatus = await axios.get(`${corporateValue}/claim-report/claim-status`);
setListClaimStatusItems([
{
name: 'Requested',
value: claimStatus.data.data.requesteds,
color: palette.dark.primary.dark,
},
{
name: 'Approval',
value: claimStatus.data.data.approveds,
color: palette.dark.warning.dark,
},
{
name: 'Rejected',
value: claimStatus.data.data.rejecteds,
color: palette.dark.error.dark,
},
]);
const parameters =
Object.keys(appliedParams).length !== 0
? appliedParams
: Object.fromEntries([
...searchParams.entries(),
['order', orders.order],
['orderBy', orders.orderBy],
]);
const claim = await axios.get(`${corporateValue}/members`, {
params: { ...parameters, type: 'claim-report' },
});
setSearchParams(parameters);
// setListAllMemberByClaimStatus(claim.data.data.allMembersByClaimStatus.data);
setPaginationTable(claim.data.data.allMembersByClaimStatus);
setIsLoading(false);
})();
}, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]);
return (
<Page title="Claim Reports">
<HeaderBreadcrumbs
heading={'Claim Submit'}
links={[
{ name: 'Case Management', href: '/dashboard' },
{
name: 'Claim Submit',
href: '/claim-submit',
},
]}
/>
<Container maxWidth={themeStretch ? false : 'xl'}>
<Grid container spacing={2}>
<Grid item xs={12} lg={6} md={6}>
<Typography variant="h6" sx={{marginLeft:'10px'}}> Select Employee</Typography>
</Grid>
<Grid item xs={12} lg={6} md={6} sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<Stack direction='row' alignItems='center'>
<Typography variant="inherit" align='right' sx={{ marginRight: '10px' }}>Submission Date</Typography>
<Typography variant="subtitle1" align='right' sx={{ marginRight: '10px' }}>{date}</Typography>
</Stack>
</Grid>
<Grid item xs={12} lg={12} md={12}>
<DialogClaimSubmitMember
openDialog={true}
setOpenDialog={false}
title={{ name: 'te' }}
/>
</Grid>
</Grid>
</Container>
</Page>
);
}

View File

@@ -0,0 +1,114 @@
/* ---------------------------------- @mui ---------------------------------- */
import { Stack, Button } from '@mui/material';
/* ---------------------------------- axios --------------------------------- */
// import axios from 'axios';
import axios from '../../utils/axios';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
import CardClaimSubmit from '../../components/CardClaimSubmit';
/* ---------------------------------- theme --------------------------------- */
import palette from '../../theme/palette';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams, useNavigate } from 'react-router-dom';
import { fDate } from '../../utils/formatTime';
export default function List() {
const navigate = useNavigate();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const [data, setData] = 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>('desc');
const [orderBy, setOrderBy] = useState('codeRequest');
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: true,
searchText: searchText,
setSearchText: setSearchText,
handleSearchSubmit: handleSearchSubmit,
};
return (
<Stack>
<CardClaimSubmit rows={data} loadings={loadings} params={params} searchs={searchs} />
</Stack>
);
}

View File

@@ -85,7 +85,7 @@ export default function ClaimForm({ isEdit, currentClaim }: Props) {
useEffect(() => {
console.log('defaultValues', defaultValues);
// console.log('defaultValues', defaultValues);
if (isEdit && currentClaim) {
reset(defaultValues);
setMember(defaultValues.member)
@@ -102,7 +102,7 @@ export default function ClaimForm({ isEdit, currentClaim }: Props) {
setValue(`uploaded_files.${type}`, [...currentFiles, ...files]);
console.log('currentFiles', getValues('uploaded_files'));
// console.log('currentFiles', getValues('uploaded_files'));
};
const memberSelected = (member) => {
@@ -110,7 +110,7 @@ export default function ClaimForm({ isEdit, currentClaim }: Props) {
};
const checkLimit = async () => {
console.log('CHECKING LIMIT');
// console.log('CHECKING LIMIT');
};
const onSubmit = async (data: any) => {

View File

@@ -68,7 +68,7 @@ export default function ClaimsCreateUpdate() {
};
const handleSaveClaimItems = () => {
console.log('Storing ', claimItems);
// console.log('Storing ', claimItems);
setLoadingClaimItems(true);
axios
.post(`claims/${id}/update-items`, {

View File

@@ -12,17 +12,17 @@ export default function DialogMemberBenefit({member, setOpenDialog, openDialog,
const toggleBenefit = (benefit) => {
if (selectedBenefits.includes(benefit)) {
console.log('removing', benefit)
// console.log('removing', benefit)
setSelectedBenefits(selectedBenefits.filter((throughBenefit) => benefit.id != throughBenefit.id))
} else {
console.log('adding', benefit)
// console.log('adding', benefit)
setSelectedBenefits([...selectedBenefits, benefit])
}
}
const handleSubmit = () => {
onSubmit(selectedBenefits);
console.log ('submitting')
// console.log ('submitting')
setOpenDialog(false);
setSelectedBenefits([]);
}

View File

@@ -0,0 +1,499 @@
import * as Yup from 'yup';
import { useSnackbar } from 'notistack';
import { useNavigate } from 'react-router-dom';
import { useContext, useCallback, useEffect, useMemo, useState } from 'react';
// form
import { Controller, useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
// @mui
import { styled } from '@mui/material/styles';
import { LoadingButton } from '@mui/lab';
import {
Box,
Button,
Card,
FormControl,
FormControlLabel,
FormHelperText,
FormLabel,
Grid,
InputLabel,
Menu,
MenuItem,
OutlinedInput,
Radio,
RadioGroup,
Select,
SelectChangeEvent,
Stack,
Typography,
} from '@mui/material';
// components
import {
FormProvider,
RHFTextField,
RHFRadioGroup,
RHFUploadAvatar,
RHFSwitch,
RHFEditor,
RHFDatepicker,
RHFMultiCheckbox,
RHFCheckbox,
RHFCustomMultiCheckbox,
RHFSelect,
} from '../../components/hook-form';
import { Corporate } from '../../@types/corporates';
import axios from '../../utils/axios';
import { fCurrency } from '../../utils/formatNumber';
// import RHFAutocomplete from '../../components/hook-form/RHFAutocomplete';
import UploadImage from '../../components/UploadImage';
import { fPostFormat } from '../../utils/formatTime';
import AddIcon from '@mui/icons-material/Add';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
// import Link from '@/theme/overrides/Link';
const LabelStyle = styled(Typography)(({ theme }) => ({
...theme.typography.subtitle2,
color: theme.palette.text.secondary,
marginBottom: theme.spacing(1),
}));
interface FormValuesProps extends Partial<Corporate> {
taxes: boolean;
inStock: boolean;
}
type Props = {
// isEdit: boolean;
currentCorporate?: Corporate;
};
export default function CorporateForm({currentCorporate }: Props) {
const navigate = useNavigate();
const [corporate_groups, setCorporateGroups] = useState([]);
const { corporateValue } = useContext(UserCurrentCorporateContext);
// const [ errors, setErrors ] = useState<{ [key: string]: string }>({});
const { enqueueSnackbar } = useSnackbar();
let NewCorporateSchema = Yup.object().shape({
welcome_message: Yup.string().required('Welcome Message is required'),
// reason: Yup.string().required('Reason for update is required when editing data'),
});
const defaultValues = useMemo(
() => ({
code: currentCorporate?.code || '',
name: currentCorporate?.name || '',
reason: currentCorporate?.reason || '',
payor_id: currentCorporate?.payor_id || '',
welcome_message: currentCorporate?.welcome_message || '',
help_text: currentCorporate?.help_text || '',
active: currentCorporate?.id ? currentCorporate?.active === 1 : true,
automatic_linking: currentCorporate?.id ? currentCorporate?.automatic_linking === 1 : true,
policy_id: currentCorporate?.current_policy?.id || '',
policy_code: currentCorporate?.current_policy?.code || '',
policy_total_premi: currentCorporate?.current_policy?.total_premi || 0,
policy_minimal_deposit_percentage:
currentCorporate?.current_policy?.minimal_deposit_percentage || 50,
policy_minimal_deposit_net: currentCorporate?.current_policy?.minimal_deposit_net || 0,
policy_minimal_alert_percentage:
currentCorporate?.current_policy?.minimal_alert_percentage || 25,
policy_minimal_alert_net: currentCorporate?.current_policy?.minimal_alert_net || 0,
policy_stop_service_percentage:
currentCorporate?.current_policy?.minimal_stop_service_percentage || 25,
policy_stop_service_net: currentCorporate?.current_policy?.minimal_stop_service_net || 0,
policy_start: currentCorporate?.current_policy?.start || '',
policy_end: currentCorporate?.current_policy?.end || '',
linking_rules: currentCorporate?.linking_rules || ['nric', 'nik', 'member_id'],
type: currentCorporate?.type || 'corporate',
logo: currentCorporate?.logo || '',
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[currentCorporate]
);
const methods = useForm<FormValuesProps>({
resolver: yupResolver(NewCorporateSchema),
defaultValues,
});
const {
reset,
watch,
control,
setValue,
getValues,
setError,
handleSubmit,
formState: { isSubmitting },
} = methods;
const values = watch();
useEffect(() => {
axios
.get(`/${corporateValue}/corporate`)
.then((res) => {
// console.log(res.data.data[0], 'tussr')
const data = res.data.data[0];
setCorporateGroups(res.data.data[0]);
reset({
code: data?.code || '',
name: data?.name || '',
reason: data?.reason || '',
payor_id: data?.payor_id || '',
welcome_message: data?.welcome_message || '',
help_text: data?.help_text || '',
active: data?.id ? data?.active === 1 : true,
automatic_linking: data?.id ? data?.automatic_linking === 1 : true,
policy_id: data?.current_policy?.id || '',
policy_code: data?.current_policy?.code || '',
policy_total_premi: data?.current_policy?.total_premi || 0,
policy_minimal_deposit_percentage:
data?.current_policy?.minimal_deposit_percentage || 50,
policy_minimal_deposit_net: data?.current_policy?.minimal_deposit_net || 0,
policy_minimal_alert_percentage:
data?.current_policy?.minimal_alert_percentage || 25,
policy_minimal_alert_net: data?.current_policy?.minimal_alert_net || 0,
policy_stop_service_percentage:
data?.current_policy?.minimal_stop_service_percentage || 25,
policy_stop_service_net: data?.current_policy?.minimal_stop_service_net || 0,
policy_start: data?.current_policy?.start || '',
policy_end: data?.current_policy?.end || '',
linking_rules: data?.linking_rules || ['nric', 'nik', 'member_id'],
type: data?.type || 'corporate',
logo: data?.logo || '',
});
})
.catch((err) => {
enqueueSnackbar('Opps, failed to get Corporate Group List', { variant: 'error' });
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentCorporate]);
const currentImage = currentCorporate?.avatar_url;
const [file, setFile] = useState(null);
const [save, setSave] = useState(null);
// console.log('save', save);
const onSubmit = async (data: FormValuesProps) => {
try {
const formData = new FormData();
formData.append('logo', file);
formData.append('name', data.name);
formData.append('automatic_linking', data.automatic_linking ? 1 : 0);
formData.append('welcome_message', data.welcome_message);
formData.append('reason', data.reason);
formData.append('help_text', data.help_text);
formData.append('linking_rules', data.linking_rules);
// console.log('MOTHERFUCKER', data.linking_rules)
formData.append('_method', 'PUT');
const response = await axios.post(`/${corporateValue}/corporate-update`, formData);
reset();
enqueueSnackbar(
'Corporate Updated Successfully!',
{ variant: 'success' }
);
navigate('/corporate');
} catch (error: any) {
if (error && error.response.status === 422) {
// for (const [key, value] of Object.entries(error.response.data.errors)) {
// setError(key, { message: value[0] });
// enqueueSnackbar(value[0] ?? 'Failed Processing Request', { variant: 'error' });
// }
for (const [key, value] of Object.entries(error.response.data.errors)) {
// setError(key, { message: value[0] });
enqueueSnackbar(value ?? 'Failed Processing Request', { variant: 'error' });
}
} else {
enqueueSnackbar(error.message ?? 'Failed Processing Request', { variant: 'error' });
}
}
};
const handleDrop = useCallback(
(acceptedFiles) => {
setValue(
'logo',
acceptedFiles.map((file: Blob | MediaSource) =>
Object.assign(file, {
preview: URL.createObjectURL(file),
})
)
);
},
[setValue]
);
const handleRemove = (file: File | string) => {
setValue('logo', null);
};
// Only Handle Change on Policy Total Premi
useEffect(() => {
let calc_policy_minimal_deposit_net =
(values.policy_total_premi * values.policy_minimal_deposit_percentage) / 100;
setValue('policy_minimal_deposit_net', calc_policy_minimal_deposit_net);
let calc_policy_minimal_alert_net =
(values.policy_total_premi * values.policy_minimal_alert_percentage) / 100;
setValue('policy_minimal_alert_net', calc_policy_minimal_alert_net);
let calc_policy_stop_service_net =
(values.policy_total_premi * values.policy_stop_service_percentage) / 100;
setValue('policy_stop_service_net', calc_policy_stop_service_net);
}, [values.policy_total_premi]);
// Only Handle on Change Policy Minimal Deposit
const handleMinimalDepositNetChange = (e) => {
setValue('policy_minimal_deposit_net', e.target.value);
setValue(
'policy_minimal_deposit_percentage',
(e.target.value / values.policy_total_premi) * 100
);
};
const handleMinimalDepositPercentageChange = (e) => {
setValue('policy_minimal_deposit_percentage', e.target.value);
setValue('policy_minimal_deposit_net', (values.policy_total_premi * e.target.value) / 100);
};
// Only Handle on Change Minimal Alert
const handleMinimalAlertNetChange = (e) => {
setValue('policy_minimal_alert_net', e.target.value);
setValue('policy_minimal_alert_percentage', (e.target.value / values.policy_total_premi) * 100);
};
const handleMinimalAlertPercentageChange = (e) => {
setValue('policy_minimal_alert_percentage', e.target.value);
setValue('policy_minimal_alert_net', (values.policy_total_premi * e.target.value) / 100);
};
// Only Handle on Change Minimum Stop Service
const handleStopServiceNetChange = (e) => {
setValue('policy_stop_service_net', e.target.value);
setValue('policy_stop_service_percentage', (e.target.value / values.policy_total_premi) * 100);
};
const handleStopServicePercentageChange = (e) => {
setValue('policy_stop_service_percentage', e.target.value);
setValue('policy_stop_service_net', (values.policy_total_premi * e.target.value) / 100);
};
const linking_rules_checkbox_name = 'linking_rules';
const linking_tools = [
{
value: 'nric',
label: 'No. KTP',
},
{
value: 'nik',
label: 'Nomor Induk Karyawan (NIK)',
},
{
value: 'member_id',
label: 'Member ID',
},
{
value: 'policy_code',
label: 'Policy Number',
},
{
value: 'phone',
label: 'Nomor Telepon',
},
{
value: 'email',
label: 'E-Mail',
},
{
value: 'name',
label: 'Nama Lengkap',
},
{
value: 'dob',
label: 'Tanggal Lahir',
},
];
const types = [
{
value: 'corporate',
label: 'Corporate',
},
{
value: 'subcorporate',
label: 'Sub Corporate',
},
];
const options = [
{
label: 'Something',
id: 'Something',
},
{
label: 'Syalalalal',
id: 'Syalalalal',
},
{
label: 'Lilili',
id: 'Lilili',
},
];
const [isDisabled, setIsDisabled] = useState(true);
const handleTypeChange = (event: SelectChangeEvent) => {
setValue('type', event.target.value);
};
return (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
{/* <Card sx={{ p:3, mb:3, background: 'gray', color: 'white' }}><Typography>Corporate Detail</Typography></Card> */}
<Grid container spacing={3}>
<Grid item xs={12} md={8}>
<Card sx={{ p: 3 }}>
<Stack spacing={3}>
<Grid item xs={12}>
<Typography variant="h5" color="#19BBBB">Corporate Profile</Typography>
</Grid>
<Typography variant='subtitle1' color="#637381">Corporate Profile*</Typography>
<RHFSelect name="type" label="Type" placeholder="Type" disabled>
<option value="" />
{types.map((option, index) => (
<option key={index} value={option.value}>
{option.label}
</option>
))}
</RHFSelect>
{/* // TODO Use Autocomplete */}
{/* {values.type == 'subcorporate' && (
<RHFSelect
name="parent_id"
label="Parent Corporate Group"
placeholder="Parent Corporate Group"
>
<option value="" />
{corporate_groups
.filter((option) => option.value != values.id)
.map((option, index) => (
<option key={index} value={option.value}>
{option.label}
</option>
))}
</RHFSelect>
)} */}
<Typography variant='subtitle1' color="#637381">Corporate Code*</Typography>
<RHFTextField name="code" label="Corporate Code" disabled={isDisabled} />
<Typography variant='subtitle1' color="#637381">Corporate Name*</Typography>
<RHFTextField name="name" label="Corporate Name" disabled={isDisabled} />
<Typography variant='subtitle1' color="#637381">Payor ID*</Typography>
<RHFTextField name="payor_id" label="Payor ID" disabled={isDisabled} />
{/* <RHFSelect name="reason" label="Reason for update">
<option value=""></option>
<option value="Agreement changed">Agreement changed</option>
<option value="Endorsement">Endorsement</option>
<option value="Renewal">Renewal</option>
<option value="Worng Setting">Worng Setting</option>
</RHFSelect> */}
<Stack spacing={1}>
<Typography variant="subtitle1" color="#637381">
Welcome Message *
</Typography>
<RHFEditor name="welcome_message" placeholder="Akun anda telah terverifikasi" />
</Stack>
<Stack spacing={1}>
<Typography variant="subtitle1" color="#637381">
Help Text
</Typography>
<RHFEditor name="help_text" />
</Stack>
{/* <div>
<LabelStyle>Images</LabelStyle>
<RHFUploadMultiFile
name="images"
files={[]}
showPreview
accept="image/*"
maxSize={3145728}
onDrop={handleDrop}
onRemove={handleRemove}
onRemoveAll={handleRemoveAll}
/>
</div> */}
</Stack>
</Card>
</Grid>
<Grid item xs={12} md={4}>
<Stack spacing={3}>
<Card sx={{ p: 3 }}>
<Stack spacing={3} mt={2}>
<Stack spacing={3} alignItems="center">
<Typography align="center">Company Logo</Typography>
<UploadImage setFile={setFile} currentImage={currentImage} />
</Stack>
{/* <Box>
<Box
sx={{ display: 'flex', placeContent: 'space-between', placeItems: 'center' }}
>
<Typography>Company Active</Typography>
<RHFSwitch name="active" label="" labelPlacement="start" disabled />
</Box>
<Box
sx={{ display: 'flex', placeContent: 'space-between', placeItems: 'center' }}
>
<Typography>Company Automatic Linking</Typography>
<RHFSwitch name="automatic_linking" label="" labelPlacement="start" />
</Box>
</Box> */}
</Stack>
</Card>
{/* <Card sx={{ p: 3 }}>
<Stack>
<Typography variant="subtitle2" sx={{ color: 'text.secondary' }}>
Linking Rules
</Typography>
<Stack>
<RHFCustomMultiCheckbox name="linking_rules" options={linking_tools} />
</Stack>
</Stack>
</Card> */}
</Stack>
</Grid>
<Grid item xs={12} md={11}>
<Stack direction="row" spacing={2} justifyContent="flex-end">
<Typography>
<Button
variant="outlined"
size="small"
color="inherit"
onClick={() => navigate(`/corporate`)}
>
{'Cancel'}
</Button>
</Typography>
<Typography>
<LoadingButton
type="submit"
variant="contained"
size="small"
loading={isSubmitting}
>
{'Save'}
</LoadingButton>
</Typography>
</Stack>
</Grid>
</Grid>
</FormProvider>
);
}

View File

@@ -0,0 +1,142 @@
/* ---------------------------------- react --------------------------------- */
import { useState, SyntheticEvent } from 'react';
/* ---------------------------------- @mui ---------------------------------- */
import { Box, Tabs, Tab, Container, Grid, Card } from '@mui/material';
import { styled } from '@mui/material/styles';
/* ------------------------------- components ------------------------------- */
import Page from '../../components/Page';
/* ---------------------------------- hooks --------------------------------- */
import useSettings from '../../hooks/useSettings';
import List from './List';
import ServiceMonitoring from './ServiceMonitoring';
import UserProfile from './UserProfile';
import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs';
import TableMoreMenu from '../../components/table/TableMoreMenu';
/* ------------------------------ tabs setting ------------------------------ */
/* ---------------------------------- types --------------------------------- */
interface TabPanelProps {
children?: React.ReactNode;
index: number;
value: number;
}
interface StyledTabsProps {
children?: React.ReactNode;
value: number;
onChange: (event: React.SyntheticEvent, newValue: number) => void;
}
interface StyledTabProps {
label: string;
icon?: string | React.ReactElement;
}
/* -------------------------------- tab style ------------------------------- */
function TabPanel(props: TabPanelProps) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && <Box>{children}</Box>}
</div>
);
}
function a11yProps(index: number) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
const StyledTabs = styled((props: StyledTabsProps) => <Tabs {...props} />)({
backgroundColor: '#F4F6F8',
padding: '0 24px',
'& .MuiTabs-indicator': {
display: 'flex',
justifyContent: 'space-between',
backgroundColor: 'transparent',
},
'& .MuiTabs-indicatorSpan': {
maxWidth: 40,
backgroundColor: '#635ee7',
},
});
const StyledTab = styled((props: StyledTabProps) => <Tab disableRipple {...props} />)(
({ theme }) => ({
textTransform: 'none',
fontWeight: 600,
color: theme.palette.grey[600],
marginRight: '5rem',
'&.Mui-selected': {
color: '#212B36',
borderBottom: '2px solid ' + theme.palette.primary.main,
},
'&:hover': {
color: '#212B36',
opacity: 1,
borderBottom: '2px solid ' + theme.palette.primary.main,
},
})
);
/* -------------------------------------------------------------------------- */
export default function Drugs() {
const { themeStretch } = useSettings();
const [value, setValue] = useState(0);
const handleChange = (event: SyntheticEvent, newValue: number) => {
setValue(newValue);
};
return (
<Page title="Corporate">
<Container maxWidth={themeStretch ? false : 'xl'}>
<HeaderBreadcrumbs
heading={'Corporate'}
links={[
{ name: 'Dashboard', href: '/dashboard' },
{
name: 'Corporates',
href: '/corporates',
},
]}
/>
<Grid container>
<Grid item xs={12} lg={12} md={12}>
{/* <Card> */}
{/* <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<StyledTabs value={value} onChange={handleChange} aria-label="basic tabs example">
<StyledTab label="All Data" {...a11yProps(0)} />
<StyledTab label="Ongoing" {...a11yProps(1)} />
<StyledTab label="Done" {...a11yProps(2)} />
</StyledTabs>
</Box> */}
{/* <TabPanel value={value} index={0}> */}
<List />
{/* </TabPanel> */}
{/* <TabPanel value={value} index={1}>
<ServiceMonitoring/>
</TabPanel>
<TabPanel value={value} index={2}>
<UserProfile />
</TabPanel> */}
{/* </Card> */}
</Grid>
</Grid>
</Container>
</Page>
);
}

View File

@@ -0,0 +1,354 @@
/* ---------------------------------- @mui ---------------------------------- */
import {
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Stack,
Button,
TableSortLabel,
Typography,
Box,
MenuItem,
} from '@mui/material';
import { visuallyHidden } from '@mui/utils';
/* ---------------------------------- axios --------------------------------- */
// import axios from 'axios';
import axios from '../../utils/axios';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
import BaseTablePagination from '../../components/BaseTablePagination';
import TableComponent from '../../components/Table';
/* ---------------------------------- hooks --------------------------------- */
import useMap from '../../hooks/useMap';
/* ---------------------------------- theme --------------------------------- */
import palette from '../../theme/palette';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams, useNavigate, Link } from 'react-router-dom';
import TableMoreMenu from '../../components/table/TableMoreMenu';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
/* ---------------------------------- types --------------------------------- */
// type PaginationTableProps = {
// current_page: number;
// from: number;
// last_page: number;
// links: [];
// path: string;
// per_page: number;
// to: number;
// total: number;
// };
// type DataTableProps = {
// fullName: string;
// memberId: string;
// service: string;
// start_date: string;
// end_date: string;
// status: boolean | number;
// };
// /* -------------------------------------------------------------------------- */
// /* -------------------------- enchanced table head -------------------------- */
// type Order = 'asc' | 'desc';
// interface HeadCell {
// id: string;
// label: string;
// }
// const headCells: readonly HeadCell[] = [
// {
// id: 'name',
// label: 'Name',
// },
// {
// id: 'member_id',
// label: 'Member ID',
// },
// {
// id: 'service',
// label: 'Service',
// },
// {
// id: 'start_date',
// label: 'Start Date',
// },
// {
// id: 'end_date',
// label: 'End Date',
// },
// {
// id: 'status',
// label: 'Status',
// },
// ];
// interface EnhancedTableProps {
// onRequestSort: (event: React.MouseEvent<unknown>, property: string) => void;
// order: Order;
// orderBy: string;
// }
// function EnhancedTableHead(props: EnhancedTableProps) {
// const { order, orderBy, onRequestSort } = props;
// const createSortHandler = (property: string) => (event: React.MouseEvent<unknown>) => {
// onRequestSort(event, property);
// };
// return (
// <TableHead>
// <TableRow>
// <TableCell align="center">No</TableCell>
// {headCells.map((headCell) => (
// <TableCell
// key={headCell.id}
// sortDirection={orderBy === headCell.id ? order : false}
// align="center"
// >
// <TableSortLabel
// active={orderBy === headCell.id}
// direction={orderBy === headCell.id ? order : 'asc'}
// onClick={createSortHandler(headCell.id)}
// >
// {headCell.label}
// {orderBy === headCell.id ? (
// <Box component="span" sx={visuallyHidden}>
// {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
// </Box>
// ) : null}
// </TableSortLabel>
// </TableCell>
// ))}
// </TableRow>
// </TableHead>
// );
// }
/* -------------------------------------------------------------------------- */
export default function List() {
const navigate = useNavigate();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const [data, setData] = 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,
};
/* -------------------------------- headCell -------------------------------- */
const headCells: HeadCell<never>[] = [
{
id: 'code',
align: 'left',
label: 'Code',
isSort: true,
},
{
id: 'name',
align: 'left',
label: 'Name',
isSort: true,
},
{
id: 'active',
align: 'center',
label: 'Status',
isSort: true,
},
{
id: 'action',
align: 'center',
label: '',
isSort: true,
},
];
/* -------------------------------------------------------------------------- */
useEffect(() => {
(async () => {
setIsLoading(true);
await new Promise((resolve) => setTimeout(resolve, 250));
const parameters =
Object.keys(appliedParams).length !== 0
? appliedParams
: Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]);
const response = await axios.get(`${corporateValue}/corporate`, {
params: { ...parameters },
});
setData(
response.data.data.map((obj: any) => {
return {
...obj,
active:
obj.active === 1 ? (
<Typography variant="overline"
sx={{
backgroundColor: 'rgba(84, 214, 44, 0.16)',
color: '#229A16',
paddingX: 1.5,
paddingY: 1,
display: 'inline-flex', // Mengatur elemen menjadi inline-flex
alignItems: 'center', // Untuk align vertical
borderRadius: '10px'
,
}}
>
Active
</Typography>
) : (
<Button variant="outlined" color="error">
Inactive
</Button>
),
action:
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate(`/corporate/edit`)}>
<EditOutlinedIcon />
Edit
</MenuItem>
<MenuItem onClick={() => navigate(`/corporate/view`)}>
<VisibilityOutlinedIcon />
View
</MenuItem>
</>
} />
,
};
})
);
setPaginationTable(response.data);
setRowsPerPage(response.data.per_page);
if (searchParams.get('page')) {
//@ts-ignore
const currentPage = parseInt(searchParams.get('page')) - 1;
paginationTable.current_page = currentPage;
setPage(currentPage);
}
setIsLoading(false);
})();
}, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]);
return (
<Stack>
<TableComponent
headCells={headCells}
rows={data}
orders={orders}
paginations={paginations}
loadings={loadings}
params={params}
searchs={searchs}
// filters={filters}
/>
</Stack>
);
}

View File

@@ -0,0 +1,348 @@
// mui
import {
Box,
Tabs,
Tab,
IconButton,
Container,
Grid,
Card,
Stack,
Typography,
} from '@mui/material';
import { styled } from '@mui/material/styles';
import { Favorite } from '@mui/icons-material';
// components
import Page from '../../components/Page';
import Iconify from '../../components/Iconify';
// utils
import useSettings from '../../hooks/useSettings';
import { useState, SyntheticEvent, useContext, useEffect } from 'react';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { useNavigate, useParams } from 'react-router-dom';
import axios from '../../utils/axios';
import { fDate } from '../../utils/formatTime';
// sections
// import ListTable from '../../sections/claimreports/ListTable';
// import ClaimStatusCard from '../../sections/claimreports/ClaimStatusCard';
interface TabPanelProps {
children?: React.ReactNode;
index: number;
value: number;
}
function TabPanel(props: TabPanelProps) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
style={{ backgroundColor: '#F9FAFB' }}
{...other}
>
{value === index && (
<Box sx={{ p: 3 }}>
<Typography>{children}</Typography>
</Box>
)}
</div>
);
}
function a11yProps(index: number) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
interface StyledTabsProps {
children?: React.ReactNode;
value: number;
onChange: (event: React.SyntheticEvent, newValue: number) => void;
}
const StyledTabs = styled((props: StyledTabsProps) => <Tabs {...props} />)({
'& .MuiTabs-indicator': {
display: 'flex',
justifyContent: 'center',
backgroundColor: 'transparent',
},
'& .MuiTabs-indicatorSpan': {
maxWidth: 40,
width: '100%',
backgroundColor: '#635ee7',
},
});
interface StyledTabProps {
label: string;
icon?: string | React.ReactElement;
}
const StyledTab = styled((props: StyledTabProps) => <Tab disableRipple {...props} />)(
({ theme }) => ({
textTransform: 'none',
fontWeight: 500,
fontSize: theme.typography.pxToRem(20),
color: theme.palette.primary.main,
maxWidth: '100%',
flex: 1,
margin: '0 !important',
'&.Mui-selected': {
color: '#FFF',
backgroundColor: theme.palette.primary.main,
},
'&:hover': {
backgroundColor: theme.palette.primary.dark,
color: '#FFF',
opacity: 1,
},
})
);
export default function ServiceMonitoring() {
const { themeStretch } = useSettings();
const navigate = useNavigate();
const [value, setValue] = useState(0);
const handleChange = (event: SyntheticEvent, newValue: number) => {
setValue(newValue);
};
const [data, setData] = useState({});
const [corporate, setCorporate] = useState();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const { id } = useParams();
const claimId = '2';
// console.log('id', id);
useEffect(() => {
console.log('fetching data...');
axios
.get('/data/' + id)
.then((response) => {
// console.log('data fetched...', response.data);
setData(response.data);
})
.catch((error) => {
console.error('error fetching data...', error);
});
axios
.get('/corporate-manage/' + corporateValue)
.then((response) => {
// console.log('corporate fetched...', response.data);
setCorporate(response.data);
})
.catch((error) => {
console.error('error fetching corporate...', error);
});
}, []);
// console.log('Data:', data);
const [encounterData, setEncounterData] = useState({});
useEffect(() => {
// console.log('fetching encounter data...');
axios
.get('/claims/${claim_id}/encounters')
.then((response) => {
// console.log('encounter data fetched...', response.data);
setEncounterData(response.data);
})
.catch((error) => {
console.error('error fetching encounter data...', error);
});
}, []);
return (
<Page title="Service Monitoring 123456">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center" sx={{ marginBottom: 2 }}>
<IconButton
onClick={() => navigate('/alarm-center')}
sx={{ marginRight: '10px', color: '#424242' }}
>
<Iconify icon="heroicons-outline:arrow-narrow-left" />
</IconButton>
<Typography variant="h5">Service Monitoring</Typography>
<Stack
direction="row"
alignItems="center"
sx={{
backgroundColor: '#DFE3E8',
color: '#212B36',
paddingX: 2,
paddingY: 1,
marginLeft: 3,
borderRadius: 1,
}}
>
<Iconify icon="akar-icons:check" sx={{ marginRight: 1 }} />
<Typography variant="caption">Done</Typography>
</Stack>
</Stack>
<Grid container spacing={3} sx={{ marginBottom: 5 }}>
{/* Item 1 */}
<Grid item xs={4} lg={4} md={6}>
<Card sx={{ borderRadius: '6px' }}>
<Stack>
<Stack
direction="row"
alignItems="center"
sx={{ backgroundColor: '#F5F5F5', paddingY: 1, paddingX: 2, color: '#19BBBB' }}
>
<Iconify icon="bxs:user" width={22} height={18} sx={{ marginRight: '10px' }} />
<Typography>Employee Profiles</Typography>
</Stack>
<Stack direction="row" spacing={1} sx={{ paddingY: 1, paddingX: 2 }}>
<Stack spacing={2}>
<Stack>
<Typography variant="caption">Nama perusahaan</Typography>
<Typography variant="body2">{corporate?.name}</Typography>
</Stack>
<Stack>
<Typography variant="caption">Nama Lengkap</Typography>
<Typography variant="body2">{data?.name || 'Loading...'}</Typography>
</Stack>
<Stack>
<Typography variant="caption">Tanggal lahir</Typography>
<Typography variant="body2">
{data?.birth_date ? fDate(data?.birth_date) : ''}
</Typography>
</Stack>
<Stack>
<Typography variant="caption">Email</Typography>
<Typography variant="body2">{data?.email}</Typography>
</Stack>
<Stack>
<Typography variant="caption">No telepon</Typography>
<Typography variant="body2">{data?.phone}</Typography>
</Stack>
</Stack>
<Stack>
<Typography variant="caption">ID Karyawan</Typography>
<Typography variant="body2">12345678</Typography>
</Stack>
</Stack>
</Stack>
</Card>
</Grid>
{/* Item 2 */}
<Grid item xs={4} lg={4} md={6}>
<Card sx={{ borderRadius: '6px', height: '100%' }}>
<Stack>
<Stack
direction="row"
alignItems="center"
sx={{ backgroundColor: '#F5F5F5', paddingY: 1, paddingX: 2, color: '#19BBBB' }}
>
<Iconify
icon="heroicons-solid:clipboard-list"
width={22}
height={18}
sx={{ marginRight: '10px' }}
/>
<Typography>Diagnose Summary</Typography>
</Stack>
<Stack spacing={2} sx={{ paddingY: 1, paddingX: 2 }}>
<Stack>
<Typography variant="caption">Gejala</Typography>
<Typography variant="body2">Nyeri dada</Typography>
</Stack>
<Stack>
<Typography variant="caption">Tanda</Typography>
<Typography variant="body2">Sesak Napas</Typography>
</Stack>
<Stack>
<Typography variant="caption">Main Diagnose</Typography>
<Typography variant="body2">
J46 Status asthmaticus, Acute severe asthma
</Typography>
</Stack>
<Stack>
<Typography variant="caption">Diagnosis pembanding</Typography>
<Typography variant="body2">K21 Gastro-oesophageal reflux disease</Typography>
</Stack>
</Stack>
</Stack>
</Card>
</Grid>
{/* Item 3 */}
<Grid item xs={4} lg={4} md={6}>
<Card sx={{ borderRadius: '6px', height: '100%' }}>
<Stack>
<Stack
direction="row"
alignItems="center"
sx={{ backgroundColor: '#F5F5F5', paddingY: 1, paddingX: 2, color: '#19BBBB' }}
>
<Iconify
icon="iconoir:healthcare"
width={22}
height={18}
sx={{ marginRight: '10px' }}
/>
<Typography>Services</Typography>
</Stack>
<Stack direction="row" spacing={1} sx={{ paddingY: 1, paddingX: 2 }}>
<Stack spacing={2}>
<Stack>
<Typography variant="caption">Evakuasi medis</Typography>
<Typography variant="body2">Land Transportation</Typography>
</Stack>
<Stack>
<Typography variant="caption">Rumah sakit</Typography>
<Typography variant="body2">Primaya Hospital</Typography>
</Stack>
<Stack direction="row" spacing={16}>
<Stack>
<Typography variant="caption">Tanggal mulai</Typography>
<Typography variant="body2">17 Aug 2022</Typography>
</Stack>
<Stack>
<Typography variant="caption">Selesai</Typography>
<Typography variant="body2">18 Aug 2022</Typography>
</Stack>
</Stack>
<Stack>
<Typography variant="caption">Daftar layanan</Typography>
<Typography variant="body2">
Inpatient, Medivac (Medical Evacuation)
</Typography>
</Stack>
</Stack>
</Stack>
</Stack>
</Card>
</Grid>
<Grid item sm>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<StyledTabs value={value} onChange={handleChange} aria-label="basic tabs example">
<StyledTab icon={<Favorite />} label="Daily Monitoring" {...a11yProps(0)} />
<StyledTab
icon={<Iconify icon="heroicons-solid:beaker" />}
label="Laboratorium Result"
{...a11yProps(1)}
/>
</StyledTabs>
</Box>
<TabPanel value={value} index={0}>
Item One
</TabPanel>
<TabPanel value={value} index={1}>
Item Two
</TabPanel>
</Grid>
</Grid>
</Container>
</Page>
);
}

View File

@@ -0,0 +1,473 @@
/* ---------------------------------- @mui ---------------------------------- */
import {
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Stack,
Button,
TableSortLabel,
Typography,
Grid,
Box,
Card,
MenuItem,
} from '@mui/material';
import { visuallyHidden } from '@mui/utils';
/* ---------------------------------- axios --------------------------------- */
// import axios from 'axios';
import axios from '../../utils/axios';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
import BaseTablePagination from '../../components/BaseTablePagination';
import TableComponent from '../../components/Table';
import Page from '../../components/Page';
/* ---------------------------------- hooks --------------------------------- */
import useMap from '../../hooks/useMap';
/* ---------------------------------- theme --------------------------------- */
import palette from '../../theme/palette';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams, useNavigate, Link } from 'react-router-dom';
import TableMoreMenu from '../../components/table/TableMoreMenu';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
/*------------------------------------ icon ----------------------------------- */
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import UploadImage from '../../components/UploadImage';
import { Corporate } from '../../@types/corporates';
import { fDateSuffix } from '../../utils/formatTime';
import { fSplit } from '../../utils/formatNumber';
/* ---------------------------------- types --------------------------------- */
// type PaginationTableProps = {
// current_page: number;
// from: number;
// last_page: number;
// links: [];
// path: string;
// per_page: number;
// to: number;
// total: number;
// };
// type DataTableProps = {
// fullName: string;
// memberId: string;
// service: string;
// start_date: string;
// end_date: string;
// status: boolean | number;
// };
// /* -------------------------------------------------------------------------- */
// /* -------------------------- enchanced table head -------------------------- */
// type Order = 'asc' | 'desc';
// interface HeadCell {
// id: string;
// label: string;
// }
// const headCells: readonly HeadCell[] = [
// {
// id: 'name',
// label: 'Name',
// },
// {
// id: 'member_id',
// label: 'Member ID',
// },
// {
// id: 'service',
// label: 'Service',
// },
// {
// id: 'start_date',
// label: 'Start Date',
// },
// {
// id: 'end_date',
// label: 'End Date',
// },
// {
// id: 'status',
// label: 'Status',
// },
// ];
// interface EnhancedTableProps {
// onRequestSort: (event: React.MouseEvent<unknown>, property: string) => void;
// order: Order;
// orderBy: string;
// }
// function EnhancedTableHead(props: EnhancedTableProps) {
// const { order, orderBy, onRequestSort } = props;
// const createSortHandler = (property: string) => (event: React.MouseEvent<unknown>) => {
// onRequestSort(event, property);
// };
// return (
// <TableHead>
// <TableRow>
// <TableCell align="center">No</TableCell>
// {headCells.map((headCell) => (
// <TableCell
// key={headCell.id}
// sortDirection={orderBy === headCell.id ? order : false}
// align="center"
// >
// <TableSortLabel
// active={orderBy === headCell.id}
// direction={orderBy === headCell.id ? order : 'asc'}
// onClick={createSortHandler(headCell.id)}
// >
// {headCell.label}
// {orderBy === headCell.id ? (
// <Box component="span" sx={visuallyHidden}>
// {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
// </Box>
// ) : null}
// </TableSortLabel>
// </TableCell>
// ))}
// </TableRow>
// </TableHead>
// );
// }
/* -------------------------------------------------------------------------- */
export default function List() {
const navigate = useNavigate();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const [currentCorporate, setCurrentCorporate] = useState<Corporate | null>(null);
/* -------------------------------------------------------------------------- */
/* setting up for the table */
/* -------------------------------------------------------------------------- */
const [isLoading, setIsLoading] = useState(true);
const loadings = {
isLoading: isLoading,
setIsLoading: setIsLoading,
};
// useEffect(() => {
// (async () => {
// setIsLoading(true);
// await new Promise((resolve) => setTimeout(resolve, 250));
// const parameters =
// Object.keys(appliedParams).length !== 0
// ? appliedParams
// : Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]);
// const response = await axios.get(`${corporateValue}/corporate`, {
// params: { ...parameters },
// });
// setCurrentCorporate(response.data[0]);
// setPaginationTable(response.data);
// setRowsPerPage(response.data.per_page);
// if (searchParams.get('page')) {
// //@ts-ignore
// const currentPage = parseInt(searchParams.get('page')) - 1;
// paginationTable.current_page = currentPage;
// setPage(currentPage);
// }
// setIsLoading(false);
// })();
// }, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]);
useEffect(() => {
axios.get(`${corporateValue}/corporate`)
.then((response) => {
const data = response.data.data[0];
setCurrentCorporate(data);
})
.catch((error) => {
console.error('Error fetching corporate data:', error);
// Handle error here
});
}, [corporateValue]);
return (
<Stack>
<Grid container>
{/* Field 1 */}
<Grid item xs={12} paddingX="24px" paddingY="20px">
<Stack direction="row" alignItems="center">
<ArrowBackIosIcon onClick={() => navigate(`/corporate`)} sx={{ cursor: "pointer" }} />
<Typography variant="h5">Profile Corporate </Typography>
</Stack>
</Grid>
</Grid>
<Card>
<Grid container>
{/* Field 1 */}
<Grid item xs={12} paddingX="24px" paddingY="20px">
<Stack direction="row" alignItems="center">
<Typography variant="h6" color='#19BBBB'>Corporate Profile </Typography>
</Stack>
<Stack direction="row" alignItems="center">
<Grid container alignItems="flex-start" sx={{ marginTop: '50px' }}>
<Grid item xs={12} md={3}>
<div style={{ position: 'relative', flex: 'center', height: 'fit-content' }}>
<img
width={160}
height={160}
src={currentCorporate?.avatar_url}
alt="user-profile"
style={{ borderRadius: '50%' }}
/>
</div>
</Grid>
<Grid item xs={12} md={8}>
<Grid container gap={2}>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Corporate Type</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6">{currentCorporate?.type}</Typography>
</Grid>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Corporate Name</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6">{currentCorporate?.name}</Typography>
</Grid>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Corporate Code</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6">{currentCorporate?.code}</Typography>
</Grid>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Payor ID</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6">{currentCorporate?.payor_id}</Typography>
</Grid>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Welcome Messages</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6">{currentCorporate?.welcome_message ? currentCorporate?.welcome_message.replace(/<[^>]*>/g, '') : '-'}</Typography>
</Grid>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Help Text</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6">{currentCorporate?.help_text ? currentCorporate?.help_text.replace(/<[^>]*>/g, '') : '-'}</Typography>
</Grid>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Linking Rules</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6" sx={{marginLeft: '20px'}}>
<ul>
{currentCorporate?.linking_rules ? (
currentCorporate.linking_rules
.filter(rule => rule)
.map((rule, index) => (
<li key={index}>{rule}</li>
))
) : (
<li>-</li>
)}
</ul>
</Typography>
</Grid>
</Grid>
</Grid>
</Grid>
</Stack>
</Grid>
</Grid>
</Card>
{/* <Card sx={{marginTop:'30px'}}>
<Grid container>
<Grid item xs={12} paddingX="24px" paddingY="20px">
<Stack direction="row" alignItems="center">
<Typography variant="h6" color='#19BBBB'>Policy Detail </Typography>
</Stack>
<Stack direction="row" alignItems="center">
<Grid container alignItems="flex-start" sx={{ marginTop: '50px' }}>
<Grid container gap={2}>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Corporate Type</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6">{currentCorporate?.type}</Typography>
</Grid>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Corporate Name</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6">{currentCorporate?.name}</Typography>
</Grid>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Corporate Code</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6">{currentCorporate?.code}</Typography>
</Grid>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Payor ID</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6">{currentCorporate?.payor_id}</Typography>
</Grid>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Welcome Messages</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6">{currentCorporate?.welcome_message.replace(/<[^>]*>/g, '')}</Typography>
</Grid>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Help Text</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6">{currentCorporate?.help_text.replace(/<[^>]*>/g, '')}</Typography>
</Grid>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Linking Rules</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6" sx={{marginLeft: '20px'}}>
<ul>
{currentCorporate?.linking_rules
.filter(rule => rule) // Menghapus elemen yang kosong atau null
.map((rule, index) => (
<li key={index}>{rule}</li>
))}
</ul>
</Typography>
</Grid>
</Grid>
</Grid>
</Stack>
</Grid>
</Grid>
</Card> */}
<Card sx={{marginTop:'30px'}}>
<Grid container>
<Grid item xs={12} paddingX="24px" paddingY="20px">
<Stack direction="row" alignItems="center">
<Typography variant="h6" color='#19BBBB'>Policy Detail </Typography>
</Stack>
<Stack direction="row" alignItems="center">
<Grid container alignItems="flex-start" sx={{ marginTop: '50px' }}>
<Grid container gap={2}>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Policy Number</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6">{currentCorporate?.current_policy?.code}</Typography>
</Grid>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Cooperation Start Date</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6">{currentCorporate?.current_policy?.start && fDateSuffix(currentCorporate?.current_policy?.start)}</Typography>
</Grid>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Cooperation End Date</Typography>
</Grid>
<Grid item xs={12} md={8} >
<Typography variant="h6">{currentCorporate?.current_policy?.end && fDateSuffix(currentCorporate?.current_policy?.end)}</Typography>
</Grid>
<Grid item xs={12} md={3} >
<Typography variant="h6" color={'#637381'} >Deposit Initial Fund</Typography>
</Grid>
<Grid item xs={12} md={12} >
</Grid>
</Grid>
<Grid container gap={2}>
<Grid item xs={12} md={12} >
</Grid>
<Grid item xs={12} md={12} >
<Typography variant="h6">Minimal Depost Policy Level</Typography>
</Grid>
<Grid item xs={3} md={3} >
<Typography variant="h6" color={'#637381'} >Percentage (%)</Typography>
</Grid>
<Grid item xs={3} md={2} >
<Typography variant="h6">{currentCorporate?.current_policy?.minimal_deposit_percentage}</Typography>
</Grid>
<Grid item xs={2} md={2} >
<Typography variant="h6" color={'#637381'} >Net</Typography>
</Grid>
<Grid item xs={3} md={3} >
<Typography variant="h6" color={'#637381'} >(Rp. {currentCorporate?.current_policy?.minimal_deposit_net && fSplit(currentCorporate?.current_policy?.minimal_deposit_net)})</Typography>
</Grid>
<Grid item xs={12} md={12} >
<Typography variant="h6">Minimal Alert Level</Typography>
</Grid>
<Grid item xs={3} md={3} >
<Typography variant="h6" color={'#637381'} >Percentage (%)</Typography>
</Grid>
<Grid item xs={3} md={2} >
<Typography variant="h6">{currentCorporate?.current_policy?.minimal_alert_percentage}</Typography>
</Grid>
<Grid item xs={2} md={2} >
<Typography variant="h6" color={'#637381'} >Net</Typography>
</Grid>
<Grid item xs={3} md={2} >
<Typography variant="h6" color={'#637381'} >(Rp. {currentCorporate?.current_policy?.minimal_alert_net && fSplit(currentCorporate?.current_policy?.minimal_alert_net)})</Typography>
</Grid>
<Grid item xs={12} md={12} >
<Typography variant="h6">Minimal Stop Service Level</Typography>
</Grid>
<Grid item xs={3} md={3} >
<Typography variant="h6" color={'#637381'} >Percentage (%)</Typography>
</Grid>
<Grid item xs={3} md={2} >
<Typography variant="h6">{currentCorporate?.current_policy?.minimal_stop_service_percentage}</Typography>
</Grid>
<Grid item xs={2} md={2} >
<Typography variant="h6" color={'#637381'} >Net</Typography>
</Grid>
<Grid item xs={3} md={2} >
<Typography variant="h6" color={'#637381'} >(Rp. {currentCorporate?.current_policy?.minimal_stop_service_net && fSplit(currentCorporate?.current_policy?.minimal_stop_service_net)})</Typography>
</Grid>
</Grid>
</Grid>
</Stack>
</Grid>
</Grid>
</Card>
</Stack>
);
}

View File

@@ -0,0 +1,85 @@
// mui
import { IconButton, Container, Grid, Stack, Typography } from '@mui/material';
// components
import Page from '../../components/Page';
import Iconify from '../../components/Iconify';
// utils
import useSettings from '../../hooks/useSettings';
// section
import CardPersonalInformation from '../../sections/alarm-center/user-profile/CardPersonalInformation';
import CardFamilyInformation from '../../sections/alarm-center/user-profile/CardFamilyInformation';
import CardPolicyNumber from '../../sections/alarm-center/user-profile/CardPolicyNumber';
import CardBenefitSummary from '../../sections/alarm-center/user-profile/CardBenefitSummary';
import CardClaimHistory from '../../sections/alarm-center/user-profile/CardClaimHistory';
// react
import { useNavigate, useParams } from 'react-router-dom';
import ButtonBack from '../../components/ButtonBack';
import { useEffect, useState, useContext } from 'react';
import axios from '../../utils/axios';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
// ----------------------------------------------------------------------
export default function UserProfile() {
const { themeStretch } = useSettings();
// const navigate = useNavigate();
const [data, setData] = useState();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const { id } = useParams();
useEffect(() => {
axios
.get(corporateValue + '/members/' + id)
.then((response) => {
setData(response.data);
})
.catch((error) => {
console.error(error);
});
}, []);
// console.log('data', data);
return (
<Page title="Profile">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center" sx={{ marginBottom: 2 }}>
{/* <IconButton sx={{ marginRight: '10px', color: '#424242' }} onClick={() => navigate()}>
<Iconify icon="heroicons-outline:arrow-narrow-left" />
</IconButton> */}
<ButtonBack />
<Typography variant="h5">Profil Peserta</Typography>
</Stack>
<Grid container spacing={2}>
{/* Row 1 */}
<Grid item xs={12} md={6}>
<Grid container spacing={2}>
{/* Item 1 */}
<Grid item xs={12} md={12}>
<CardPersonalInformation data={data} />
</Grid>
{/* Item 2 */}
<Grid item xs={12} md={12}>
<CardFamilyInformation data={data} />
</Grid>
</Grid>
</Grid>
{/* Row 2 */}
<Grid item xs={12} md={6}>
<Grid container spacing={2}>
{/* Item 1 */}
<Grid item xs={12}>
<CardPolicyNumber data={data} />
</Grid>
{/* Item 2 */}
<Grid item xs={12}>
<CardClaimHistory data={data}/>
</Grid>
</Grid>
</Grid>
</Grid>
</Container>
</Page>
);
}

View File

@@ -177,6 +177,7 @@ export default function Index() {
};
const searchs = {
// useSearchs: true,
searchText: searchText,
setSearchText: setSearchText,
handleSearchSubmit: handleSearchSubmit,
@@ -278,7 +279,7 @@ export default function Index() {
params: { ...parameters },
});
console.log('member', corporateMembers);
// console.log('member', corporateMembers);
const corporateTopUpLimit = await axios.get(`${corporateValue}/topup`);
setSearchParams(parameters);
@@ -356,7 +357,7 @@ export default function Index() {
})();
}, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]);
console.log(policyData);
// console.log(policyData);
return (
<Page title="Dashboard">

View File

@@ -0,0 +1,124 @@
/* ---------------------------------- react --------------------------------- */
import { useState, SyntheticEvent } from 'react';
/* ---------------------------------- @mui ---------------------------------- */
import { Box, Tabs, Tab, Container, Grid, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
/* ------------------------------- components ------------------------------- */
import Page from '../../components/Page';
/* ---------------------------------- hooks --------------------------------- */
import useSettings from '../../hooks/useSettings';
import List from './List';
import ServiceMonitoring from './ServiceMonitoring';
import UserProfile from './UserProfile';
import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs';
/* ------------------------------ tabs setting ------------------------------ */
/* ---------------------------------- types --------------------------------- */
interface TabPanelProps {
children?: React.ReactNode;
index: number;
value: number;
}
interface StyledTabsProps {
children?: React.ReactNode;
value: number;
onChange: (event: React.SyntheticEvent, newValue: number) => void;
}
interface StyledTabProps {
label: string;
icon?: string | React.ReactElement;
}
/* -------------------------------- tab style ------------------------------- */
function TabPanel(props: TabPanelProps) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && <Box>{children}</Box>}
</div>
);
}
function a11yProps(index: number) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
const StyledTabs = styled((props: StyledTabsProps) => <Tabs {...props} />)({
backgroundColor: '#F4F6F8',
padding: '0 24px',
'& .MuiTabs-indicator': {
display: 'flex',
justifyContent: 'space-between',
backgroundColor: 'transparent',
},
'& .MuiTabs-indicatorSpan': {
maxWidth: 40,
backgroundColor: '#635ee7',
},
});
const StyledTab = styled((props: StyledTabProps) => <Tab disableRipple {...props} />)(
({ theme }) => ({
textTransform: 'none',
fontWeight: 600,
color: theme.palette.grey[600],
marginRight: '5rem',
'&.Mui-selected': {
color: '#212B36',
borderBottom: '2px solid ' + theme.palette.primary.main,
},
'&:hover': {
color: '#212B36',
opacity: 1,
borderBottom: '2px solid ' + theme.palette.primary.main,
},
})
);
/* -------------------------------------------------------------------------- */
export default function Drugs() {
const { themeStretch } = useSettings();
const [value, setValue] = useState(0);
const handleChange = (event: SyntheticEvent, newValue: number) => {
setValue(newValue);
};
return (
<Page title="Employee Data">
<Container maxWidth={themeStretch ? false : 'xl'}>
<HeaderBreadcrumbs
heading={'Employee Data'}
links={[
{ name: 'Case Management', href: '/employee-data' },
{ name: 'Employee Data', href: '/employee-data'}
]}
/>
<Grid container>
<Grid item xs={12} lg={12} md={12}>
<TabPanel value={value} index={0}>
<List />
</TabPanel>
</Grid>
</Grid>
</Container>
</Page>
);
}

View File

@@ -0,0 +1,381 @@
/* ---------------------------------- @mui ---------------------------------- */
import {
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Stack,
Button,
TableSortLabel,
Box,
MenuItem
} from '@mui/material';
import { visuallyHidden } from '@mui/utils';
/* ---------------------------------- axios --------------------------------- */
// import axios from 'axios';
import axios from '../../utils/axios';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
import BaseTablePagination from '../../components/BaseTablePagination';
import TableComponent from '../../components/Table';
/* ---------------------------------- hooks --------------------------------- */
import useMap from '../../hooks/useMap';
/* ---------------------------------- theme --------------------------------- */
import palette from '../../theme/palette';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { HeadCell, Order, PaginationTableProps } from '../../@types/table';
import { useSearchParams, useNavigate, Link } from 'react-router-dom';
import { fDate, fDateSuffix } from '../../utils/formatTime';
import { format } from 'date-fns';
import Typography from '@mui/material/Typography';
import TableMoreMenu from '../../components/table/TableMoreMenu';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
import Label from '../../components/Label';
/* ---------------------------------- types --------------------------------- */
// type PaginationTableProps = {
// current_page: number;
// from: number;
// last_page: number;
// links: [];
// path: string;
// per_page: number;
// to: number;
// total: number;
// };
// type DataTableProps = {
// fullName: string;
// memberId: string;
// service: string;
// start_date: string;
// end_date: string;
// status: boolean | number;
// };
// /* -------------------------------------------------------------------------- */
// /* -------------------------- enchanced table head -------------------------- */
// type Order = 'asc' | 'desc';
// interface HeadCell {
// id: string;
// label: string;
// }
// const headCells: readonly HeadCell[] = [
// {
// id: 'name',
// label: 'Name',
// },
// {
// id: 'member_id',
// label: 'Member ID',
// },
// {
// id: 'service',
// label: 'Service',
// },
// {
// id: 'start_date',
// label: 'Start Date',
// },
// {
// id: 'end_date',
// label: 'End Date',
// },
// {
// id: 'status',
// label: 'Status',
// },
// ];
// interface EnhancedTableProps {
// onRequestSort: (event: React.MouseEvent<unknown>, property: string) => void;
// order: Order;
// orderBy: string;
// }
// function EnhancedTableHead(props: EnhancedTableProps) {
// const { order, orderBy, onRequestSort } = props;
// const createSortHandler = (property: string) => (event: React.MouseEvent<unknown>) => {
// onRequestSort(event, property);
// };
// return (
// <TableHead>
// <TableRow>
// <TableCell align="center">No</TableCell>
// {headCells.map((headCell) => (
// <TableCell
// key={headCell.id}
// sortDirection={orderBy === headCell.id ? order : false}
// align="center"
// >
// <TableSortLabel
// active={orderBy === headCell.id}
// direction={orderBy === headCell.id ? order : 'asc'}
// onClick={createSortHandler(headCell.id)}
// >
// {headCell.label}
// {orderBy === headCell.id ? (
// <Box component="span" sx={visuallyHidden}>
// {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
// </Box>
// ) : null}
// </TableSortLabel>
// </TableCell>
// ))}
// </TableRow>
// </TableHead>
// );
// }
/* -------------------------------------------------------------------------- */
export default function List() {
const navigate = useNavigate();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const [data, setData] = 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 = {
searchText: searchText,
setSearchText: setSearchText,
handleSearchSubmit: handleSearchSubmit,
};
/* -------------------------------- headCell -------------------------------- */
const headCells: HeadCell<never>[] = [
{
id: 'memberId',
align: 'left',
label: 'Member ID',
isSort: true,
},
{
id: 'fullName',
align: 'left',
label: 'Name',
isSort: true,
},
{
id: 'start_date',
align: 'center',
label: 'Start Date',
isSort: true,
},
{
id: 'end_date',
align: 'center',
label: 'End Date',
isSort: true,
},
{
id: 'status',
align: 'center',
label: 'Status',
isSort: true,
},
{
id: 'action',
align: 'center',
label: '',
isSort: true,
},
];
/* -------------------------------------------------------------------------- */
useEffect(() => {
(async () => {
setIsLoading(true);
await new Promise((resolve) => setTimeout(resolve, 250));
const parameters =
Object.keys(appliedParams).length !== 0
? appliedParams
: Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]);
const response = await axios.get(`${corporateValue}/members?type=employee-data`, {
params: { ...parameters },
});
setData(
response.data.data.map((obj: any) => {
return {
...obj,
// memberId:
// <Button
// onClick={() => navigate ('/employee-data/user-profile/'+obj.personId)}
// >{obj.memberId}</Button>
// ,
status:
obj.status === 1 ? (
<Label color='success'>
Active
</Label>
) : (
<Label color='error'>
Inactive
</Label>
),
start_date:
<Label>
{obj.start_date ? fDateSuffix(obj.start_date) : ''}
</Label>
,
end_date:
<Label>
{obj.end_date ? fDateSuffix(obj.end_date) : ''}
</Label>
,
fullName:
<Typography
variant="body2"
>
{obj.fullName}
</Typography>
,
memberId:
<Typography
variant="body2"
>
{obj.memberId}
</Typography>
,
action:
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate ('/employee-data/user-profile/'+obj.personId)}>
<VisibilityOutlinedIcon />
View
</MenuItem>
</>
} />
};
})
);
setPaginationTable(response.data);
setRowsPerPage(response.data.per_page);
if (searchParams.get('page')) {
//@ts-ignore
const currentPage = parseInt(searchParams.get('page')) - 1;
paginationTable.current_page = currentPage;
setPage(currentPage);
}
setIsLoading(false);
})();
}, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]);
return (
<Stack>
<TableComponent
headCells={headCells}
rows={data}
orders={orders}
paginations={paginations}
loadings={loadings}
params={params}
searchs={searchs}
// filters={filters}
/>
</Stack>
);
}

View File

@@ -0,0 +1,69 @@
// mui
import { IconButton, Container, Grid, Stack, Typography } from '@mui/material';
// components
import Page from '../../components/Page';
import Iconify from '../../components/Iconify';
// utils
import useSettings from '../../hooks/useSettings';
// section
import CardPersonalInformation from '../../sections/alarm-center/user-profile/CardPersonalInformation';
import CardFamilyInformation from '../../sections/alarm-center/user-profile/CardFamilyInformation';
import CardPolicyNumber from '../../sections/alarm-center/user-profile/CardPolicyNumber';
import CardBenefitSummary from '../../sections/alarm-center/user-profile/CardBenefitSummary';
import CardClaimHistory from '../../sections/alarm-center/user-profile/CardClaimHistory';
// react
import { useNavigate, useParams } from 'react-router-dom';
import ButtonBack from '../../components/ButtonBack';
import { useEffect, useState, useContext } from 'react';
import axios from '../../utils/axios';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
// ----------------------------------------------------------------------
export default function UserProfile() {
const { themeStretch } = useSettings();
const navigate = useNavigate();
const [data, setData] = useState();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const { id } = useParams();
useEffect(() => {
axios
.get(corporateValue + '/members/' + id)
.then((response) => {
setData(response.data);
})
.catch((error) => {
console.error(error);
});
}, []);
// console.log('data', data);
return (
<Page title="Profile">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center" sx={{ marginBottom: 3 }}>
{/* <IconButton sx={{ marginRight: '10px', color: '#424242' }} onClick={() => navigate()}>
<Iconify icon="heroicons-outline:arrow-narrow-left" />
</IconButton> */}
<ArrowBackIosIcon sx={{cursor:'pointer'}} onClick={() => navigate(-1)}/>
<Typography variant="h5" sx={{marginLeft:2}}>Profile</Typography>
</Stack>
{data ? (
<Grid container spacing={2}>
{/* Row 1 */}
<Grid item xs={12} md={12}>
<CardPersonalInformation data={data} />
</Grid>
<Grid item xs={12} md={12}>
<CardFamilyInformation data={data} />
</Grid>
</Grid>
) : ''}
</Container>
</Page>
);
}

View File

@@ -73,6 +73,26 @@ export default function Router() {
},
],
},
{
path: '/employee-data',
element: (
<AuthProvider>
<AuthGuard>
<DashboardLayout />
</AuthGuard>
</AuthProvider>
),
children: [
{
element: <EmployeeData />,
index: true,
},
{
path: '/employee-data/user-profile/:id',
element: <EmployeeDataUserProfile/>,
}
],
},
{
path: '/alarm-center',
element: (
@@ -88,11 +108,57 @@ export default function Router() {
index: true,
},
{
path: 'service-monitoring/:id',
path: 'member/:id',
element: <AlarmCenterMemberPerList />,
},
{
path: 'member/:id/service-monitoring/:id',
element: <AlarmCenterServiceMonitoring />,
},
],
},
{
path: '/claim-submit',
element: (
<AuthProvider>
<AuthGuard>
<DashboardLayout />
</AuthGuard>
</AuthProvider>
),
children: [
{
element: <ClaimSubmit />,
index: true,
},
{
path: 'dialog-detail',
element: <DialogDetailClaim/>
},
],
},
{
path: '/claim-request/:id',
element: (
<AuthProvider>
<AuthGuard>
<DashboardLayout />
</AuthGuard>
</AuthProvider>
),
children: [
{
element: <ClaimRequest />,
index: true,
},
{
path: 'dialog-detail',
element: <DialogDetailClaim/>
},
],
},
{
path: '/claim-report',
element: (
@@ -108,9 +174,13 @@ export default function Router() {
index: true,
},
{
path: 'dialog-detail',
element: <DialogDetailClaim/>
path: '/claim-report/detail/:id',
element: <DetailClaimReport />,
},
{
path: '/claim-report/detail-history/:id',
element: <DetailHitoryClaimReport />,
}
],
},
{
@@ -133,6 +203,30 @@ export default function Router() {
},
],
},
{
path: '/corporate',
element: (
<AuthProvider>
<AuthGuard>
<DashboardLayout />
</AuthGuard>
</AuthProvider>
),
children: [
{
element: <Corporate />,
index: true,
},
{
path: 'edit',
element: <CorporateEdit />,
},
{
path: 'view',
element: <CorporateShow />,
},
],
},
{
path: '*',
element: <LogoOnlyLayout />,
@@ -152,8 +246,13 @@ const Login = Loadable(lazy(() => import('../pages/auth/Login')));
const Dashboard = Loadable(lazy(() => import('../pages/Dashboard/Index')));
const NotFound = Loadable(lazy(() => import('../pages/Page404')));
// Employee Data
const EmployeeData = Loadable(lazy(() => import('../pages/EmployeeData/Index')));
const EmployeeDataUserProfile = Loadable(lazy(() => import('../pages/EmployeeData/UserProfile')));
// Alarm Center
const AlarmCenter = Loadable(lazy(() => import('../pages/AlarmCenter/Index')));
const AlarmCenterMemberPerList = Loadable(lazy(() => import('../pages/AlarmCenter/ListMember')));
const AlarmCenterServiceMonitoring = Loadable(
lazy(() => import('../pages/AlarmCenter/ServiceMonitoring'))
);
@@ -163,4 +262,17 @@ const AlarmCenterUserProfile = Loadable(lazy(() => import('../pages/AlarmCenter/
const ClaimReport = Loadable(lazy(() => import('../pages/ClaimReport/Index')));
const Claims = Loadable(lazy(() => import('../pages/Claims/Index')));
const ClaimShow = Loadable(lazy(() => import('../pages/Claims/Show')));
const DialogDetailClaim = Loadable(lazy(()=> import('../pages/ClaimReport/DialogDetailClaim')));
const DialogDetailClaim = Loadable(lazy(()=> import('../pages/ClaimReport/DialogDetailClaim')));
const DetailClaimReport = Loadable(lazy(()=> import('../pages/ClaimReport/Detail')));
const DetailHitoryClaimReport = Loadable(lazy(()=> import('../pages/ClaimReport/DetailHistory')));
// Claim submit
const ClaimSubmit = Loadable(lazy(() => import('../pages/ClaimSubmit/Index')));
// Claim Request
const ClaimRequest = Loadable(lazy(() => import('../pages/ClaimSubmit/DialogDetailClaim')));
// Corporate
const Corporate = Loadable(lazy(() => import('../pages/Corporate/Index')));
const CorporateEdit = Loadable(lazy(() => import('../pages/Corporate/Form')));
const CorporateShow = Loadable(lazy(() => import('../pages/Corporate/Show')));

View File

@@ -29,7 +29,7 @@ const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
export default function CardBenefitSummary({ data }) {
const [benefits, setBenefits] = useState([]);
console.log('data', data);
// console.log('data', data);
useEffect(() => {
setBenefits(data);
}, [data]);
@@ -58,7 +58,7 @@ export default function CardBenefitSummary({ data }) {
<Typography variant="body2" color="#0A0A0A">
Yearly Limits
</Typography>
<BorderLinearProgress variant="determinate" value={100} />
<BorderLinearProgress variant="determinate" value={0} />
<Stack direction="row" spacing={0.25}>
<Typography variant="body2">0</Typography>
<Typography>/</Typography>

View File

@@ -13,6 +13,7 @@ import {
} from '@mui/material';
// react
import { useState } from 'react';
import { fDate } from '../../../utils/formatTime';
// ----------------------------------------------------------------------
@@ -20,27 +21,22 @@ function createData(benefitType: string, submissionDate: string, status: string)
return { benefitType, submissionDate, status };
}
const rows = [
createData('Rawat Jalan', '15-10-2022', 'Request'),
createData('Rawat Inap', '15-10-2022', 'Request'),
createData('Manfaat Special', '15-10-2022', 'Request'),
createData('Perobatan Mata', '15-10-2022', 'Request'),
createData('Perawatan Gigi', '15-10-2022', 'Request'),
createData('Kehamilan', '15-10-2022', 'Request'),
createData('Laboratorium', '15-10-2022', 'Request'),
createData('Manfaat Farmasi', '15-10-2022', 'Request'),
];
// ----------------------------------------------------------------------
export default function CardClaimHistory(benefitMember) {
export default function CardClaimHistory({ data }) {
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
console.log('benefitMember', benefitMember);
const handleChangePage = (event: React.MouseEvent<HTMLButtonElement> | null, newPage: number) => {
setPage(newPage);
};
// Data claim history
const claimHistory = data?.claim_history;
const rows = claimHistory ? claimHistory.map(history => {
return createData(history.description, fDate(history.submission_date), history.status);
}) : [];
return (
<Card sx={{ padding: 2 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">

View File

@@ -1,9 +1,106 @@
// mui
import { Button, Card, Stack, Typography, Grid, Switch } from '@mui/material';
import { FormHelperText, FormControl, Button, Card, Stack, Typography, Grid, Switch, TextField, IconButton, Select, MenuItem, InputLabel } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import { DatePicker, LocalizationProvider, MobileDatePicker } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
// components
import Iconify from '../../../components/Iconify';
import { fDate, fPostFormat } from '../../../utils/formatTime';
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import { useContext, useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import axios from '../../../utils/axios';
import { enqueueSnackbar } from 'notistack';
export default function CardFamilyInformation() {
export default function CardFamilyInformation({ data }) {
const [openDialog, setOpenDialog] = useState(false);
const [editIndex, setEditIndex] = useState(null);
const [editedFamilyData, setEditedFamilyData] = useState({});
const { id } = useParams();
//Check Required
//State Field
const [nameField, setNameField] = useState('');
const [relationshipField, setRelationshipField] = useState('');
const [birthDateField, setBirthDateField] = useState('');
const [birthDateFieldCheck, setBirthDateFieldCheck] = useState(1);
const [emailField, setEmailField] = useState('');
const [emailFieldCheck, setEmailFieldCheck] = useState(false);
const [phoneField, setPhoneField] = useState('');
//State Field Error
const [nameFieldError, setNameFieldError] = useState('');
const [relationshipFieldError, setRelationshipFieldError] = useState('');
const [birthDateFieldError, setBirthDateFieldError] = useState('');
const [emailFieldError, setEmailFieldError] = useState('');
const [phoneFieldError, setPhoneFieldError] = useState('');
const handleEditData = (index) => {
setEditIndex(index);
setEditedFamilyData(data?.family[index] || {});
setOpenDialog(true);
setNameField(data?.family[index].name);
setRelationshipField(data?.family[index].relation_with_principal);
setBirthDateField(data?.family[index].birth_date);
setEmailField(data?.family[index].email);
setEmailFieldCheck(isValidEmail(data?.family[index].email));
setPhoneField(data?.family[index].phone);
setNameFieldError('');
setRelationshipFieldError('');
setBirthDateFieldError('');
setEmailFieldError('');
setPhoneFieldError('');
};
const handleCloseDialog = () => {
setOpenDialog(false);
};
const handleSaveData = () => {
if (editIndex !== null) {
try {
// Salin data keluarga saat ini
const updatedFamily = [...data?.family];
// Perbarui data keluarga dengan data yang diedit
updatedFamily[editIndex] = editedFamilyData;
// Perbarui data utama dengan data keluarga yang telah diperbarui
const updatedData = { ...data, family: updatedFamily[editIndex]};
updatedData.family.birth_date = fPostFormat(updatedData.family.birth_date, 'yyyy-MM-dd') ;
const familyArray = [updatedData.family];
axios
.post('/update-family', familyArray)
.then((response) => {
enqueueSnackbar('Data updated successfully', { variant: 'success' });
setOpenDialog(false);
window.location.reload();
})
.catch((error) => {
enqueueSnackbar('Failed to update data', { variant: 'error' });
});
} catch (error) {
console.error('Terjadi kesalahan saat menyimpan data:', error);
}
}
};
const isValidEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const isRequiredFieldsFilled = () => {
return nameField.trim() !== '' && relationshipField.trim() !== '' && birthDateField !== '' && birthDateFieldCheck !== 0 && emailField.trim() !== '' && emailFieldCheck && phoneField.trim() !== '';
};
return (
<Card sx={{ borderRadius: '6px', paddingY: 2 }}>
{/* Stack 1 */}
@@ -14,17 +111,17 @@ export default function CardFamilyInformation() {
sx={{ paddingY: 1, paddingX: 3 }}
>
<Typography variant="subtitle2">Beneficiary / Family</Typography>
<Button startIcon={<Iconify icon="ic:round-add" />}>Add Member</Button>
{/*<Button startIcon={<Iconify icon="ic:round-add" />} disabled>Add Member</Button>*/}
</Stack>
{/* Stack 2 */}
<Grid container maxHeight="307px" spacing={2} paddingX={2} sx={{ overflowY: 'auto' }}>
{/* Card 1 */}
<Grid item xs={12} sm={6} md={6}>
<Card sx={{ paddingX: 1.5, paddingY: 1 }}>
<Grid container maxHeight="584px" spacing={2} paddingX={2} sx={{ overflowY: 'auto' }}>
{data?.family.map((familyMember, index) => (
<Grid item xs={12} sm={6} md={6} key={index}>
<Card sx={{ paddingX: 1.5, paddingY: 1, marginBottom: 2 }}>
{/* Stack 1 */}
<Stack
direction="row"
alignItems="center"
alignItems="left"
justifyContent="space-between"
spacing={2}
sx={{ flex: '100%' }}
@@ -32,328 +129,228 @@ export default function CardFamilyInformation() {
{/* Row 1 */}
<Stack direction="row" spacing={1}>
<img
width={24}
height={24}
width={34}
height={34}
src="/images/husband-user-profile.png"
alt="user-profile"
style={{ borderRadius: '50%' }}
/>
<Stack>
<Typography variant="body2" sx={{ fontWeight: 500 }}>
Husband
{familyMember?.name}
</Typography>
<Typography variant="body2" color="#757575">
{familyMember.relation_with_principal === 'H'
? 'Husband'
: familyMember.relation_with_principal === 'W'
? 'Wife'
: familyMember.relation_with_principal === 'S'
? 'Son'
: familyMember.relation_with_principal === 'D'
? 'Daughter'
: ''}
</Typography>
</Stack>
</Stack>
{/* Row 2 */}
<Stack alignItems="center">
<Stack sx={{display:'none'}} alignItems="center">
<Typography variant="caption">Suspend</Typography>
<Switch aria-label="switch demo" />
<Switch aria-label="switch demo" disabled />
</Stack>
</Stack>
<Typography variant="body2" color="#757575">
Octa Xavier
</Typography>
<Typography variant="body2" color="#757575">
14 Jan 1986
</Typography>
<Typography variant="body2" color="#757575">
082113256754
</Typography>
{/* Stack 2 */}
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
marginTop={1.25}
>
<Button color="error" startIcon={<Iconify icon="ic:round-close" />}>
Remove
</Button>
<Button variant="contained" startIcon={<Iconify icon="heroicons:pencil-solid" />}>
Edit Data
</Button>
</Stack>
</Card>
</Grid>
{/* Card 2 */}
<Grid item xs={12} sm={6} md={6}>
<Card sx={{ paddingX: 1.5, paddingY: 1 }}>
{/* Stack 1 */}
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
spacing={2}
alignItems="left"
spacing={10}
sx={{ flex: '100%' }}
>
{/* Row 1 */}
<Stack direction="row" spacing={1}>
<div
style={{
borderRadius: '50%',
width: '24px',
height: '24px',
backgroundColor: '#D9D9D9',
}}
/>
<Typography variant="body2" sx={{ fontWeight: 500 }}>
Kid
</Typography>
</Stack>
{/* Row 2 */}
<Stack alignItems="center">
<Typography variant="caption">Suspend</Typography>
<Switch aria-label="switch demo" />
</Stack>
<Typography sx={{width: '20%'}} variant="body2" color="#757575">
Date of Birth
</Typography>
<Typography variant="body2" sx={{ fontWeight: 500 }}>
{familyMember?.birth_date != '0000-00-00' ? fDate(familyMember?.birth_date) : '-'}
</Typography>
</Stack>
<Typography variant="body2" color="#757575">
Celine Claudia
</Typography>
<Typography variant="body2" color="#757575">
15 Oct 2000
</Typography>
<Typography variant="body2" color="#757575">
082113256754
</Typography>
{/* Stack 2 */}
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
marginTop={1.25}
>
<Button color="error" startIcon={<Iconify icon="ic:round-close" />}>
Remove
</Button>
<Button variant="contained" startIcon={<Iconify icon="heroicons:pencil-solid" />}>
Edit Data
</Button>
</Stack>
</Card>
</Grid>
{/* Card 3 */}
<Grid item xs={12} sm={6} md={6}>
<Card sx={{ paddingX: 1.5, paddingY: 1 }}>
{/* Stack 1 */}
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
spacing={2}
alignItems="left"
spacing={10}
sx={{ flex: '100%' }}
>
{/* Row 1 */}
<Stack direction="row" spacing={1}>
<div
style={{
borderRadius: '50%',
width: '24px',
height: '24px',
backgroundColor: '#D9D9D9',
}}
/>
<Typography variant="body2" sx={{ fontWeight: 500 }}>
Kid
</Typography>
</Stack>
{/* Row 2 */}
<Stack alignItems="center">
<Typography variant="caption">Suspend</Typography>
<Switch aria-label="switch demo" />
</Stack>
<Typography sx={{width: '20%'}} variant="body2" color="#757575">
Email
</Typography>
<Typography variant="body2" sx={{ fontWeight: 500 }}>
{familyMember?.email}
</Typography>
</Stack>
<Typography variant="body2" color="#757575">
Celine Claudia
</Typography>
<Typography variant="body2" color="#757575">
15 Oct 2000
</Typography>
<Typography variant="body2" color="#757575">
082113256754
</Typography>
{/* Stack 2 */}
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
marginTop={1.25}
>
<Button color="error" startIcon={<Iconify icon="ic:round-close" />}>
Remove
</Button>
<Button variant="contained" startIcon={<Iconify icon="heroicons:pencil-solid" />}>
Edit Data
</Button>
</Stack>
</Card>
</Grid>
{/* Card 4 */}
<Grid item xs={12} sm={6} md={6}>
<Card sx={{ paddingX: 1.5, paddingY: 1 }}>
{/* Stack 1 */}
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
spacing={2}
alignItems="left"
spacing={10}
sx={{ flex: '100%' }}
>
{/* Row 1 */}
<Stack direction="row" spacing={1}>
<div
style={{
borderRadius: '50%',
width: '24px',
height: '24px',
backgroundColor: '#D9D9D9',
}}
/>
<Typography variant="body2" sx={{ fontWeight: 500 }}>
Kid
</Typography>
</Stack>
{/* Row 2 */}
<Stack alignItems="center">
<Typography variant="caption">Suspend</Typography>
<Switch aria-label="switch demo" />
</Stack>
<Typography sx={{width: '20%'}} variant="body2" color="#757575">
Phone Number
</Typography>
<Typography variant="body2" sx={{ fontWeight: 500 }}>
{familyMember?.phone}
</Typography>
</Stack>
<Typography variant="body2" color="#757575">
Celine Claudia
</Typography>
<Typography variant="body2" color="#757575">
15 Oct 2000
</Typography>
<Typography variant="body2" color="#757575">
082113256754
</Typography>
{/* Stack 2 */}
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
marginTop={1.25}
>
<Button color="error" startIcon={<Iconify icon="ic:round-close" />}>
Remove
</Button>
<Button variant="contained" startIcon={<Iconify icon="heroicons:pencil-solid" />}>
Edit Data
</Button>
</Stack>
</Card>
</Grid>
{/* Card 5 */}
<Grid item xs={12} sm={6} md={6}>
<Card sx={{ paddingX: 1.5, paddingY: 1 }}>
{/* Stack 1 */}
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
spacing={2}
alignItems="left"
spacing={10}
sx={{ flex: '100%' }}
>
{/* Row 1 */}
<Stack direction="row" spacing={1}>
<div
style={{
borderRadius: '50%',
width: '24px',
height: '24px',
backgroundColor: '#D9D9D9',
}}
/>
<Typography variant="body2" sx={{ fontWeight: 500 }}>
Kid
</Typography>
</Stack>
{/* Row 2 */}
<Stack alignItems="center">
<Typography variant="caption">Suspend</Typography>
<Switch aria-label="switch demo" />
</Stack>
<Typography sx={{width: '20%'}} variant="body2" color="#757575">
Status
</Typography>
<Typography variant="body2" sx={{ fontWeight: 500 }}>
{
familyMember?.phone === '1'
? 'Active'
: 'Inactive'
}
</Typography>
</Stack>
<Typography variant="body2" color="#757575">
Celine Claudia
</Typography>
<Typography variant="body2" color="#757575">
15 Oct 2000
</Typography>
<Typography variant="body2" color="#757575">
082113256754
</Typography>
{/* Stack 2 */}
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
marginTop={1.25}
>
<Button color="error" startIcon={<Iconify icon="ic:round-close" />}>
<Stack sx={{display:'none'}} direction="row" alignItems="center" justifyContent="space-between" marginTop={1.25}>
<Button color="error" startIcon={<Iconify icon="ic:round-close" />} disabled>
Remove
</Button>
<Button variant="contained" startIcon={<Iconify icon="heroicons:pencil-solid" />}>
Edit Data
</Button>
</Stack>
</Card>
</Grid>
{/* Card 6 */}
<Grid item xs={12} sm={6} md={6}>
<Card sx={{ paddingX: 1.5, paddingY: 1 }}>
{/* Stack 1 */}
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
spacing={2}
sx={{ flex: '100%' }}
>
{/* Row 1 */}
<Stack direction="row" spacing={1}>
<div
style={{
borderRadius: '50%',
width: '24px',
height: '24px',
backgroundColor: '#D9D9D9',
}}
/>
<Typography variant="body2" sx={{ fontWeight: 500 }}>
Kid
</Typography>
</Stack>
{/* Row 2 */}
<Stack alignItems="center">
<Typography variant="caption">Suspend</Typography>
<Switch aria-label="switch demo" />
</Stack>
</Stack>
<Typography variant="body2" color="#757575">
Celine Claudia
</Typography>
<Typography variant="body2" color="#757575">
15 Oct 2000
</Typography>
<Typography variant="body2" color="#757575">
082113256754
</Typography>
{/* Stack 2 */}
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
marginTop={1.25}
>
<Button color="error" startIcon={<Iconify icon="ic:round-close" />}>
Remove
</Button>
<Button variant="contained" startIcon={<Iconify icon="heroicons:pencil-solid" />}>
<Button variant="contained" startIcon={<Iconify icon="heroicons:pencil-solid" />} onClick={ () => handleEditData(index)}>
Edit Data
</Button>
</Stack>
</Card>
</Grid>
))}
</Grid>
{/* Dialog */}
<Dialog open={openDialog} onClose={handleCloseDialog} fullWidth={true}>
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
<Stack direction="row" alignItems="center" justifyContent="space-between">
<Stack direction="row">
<Iconify width={25} height={25} sx={{ marginRight: '10px' }} />
<Typography variant="h6">Edit Data</Typography>
</Stack>
<IconButton sx={{ color: '#FFF' }} onClick={handleCloseDialog}>
<CloseIcon />
</IconButton>
</Stack>
</DialogTitle>
<DialogContent>
<Stack spacing={2}>
<TextField
label="Name"
required
value={editedFamilyData?.name || ''}
onChange={(e) => {
setEditedFamilyData({ ...editedFamilyData, name: e.target.value })
setNameField(e.target.value);
setNameFieldError(e.target.value.trim() === '' ? 'This field is required' : '');
}}
fullWidth
sx={{ marginTop: '16px' }}
error={!!nameFieldError}
helperText={nameFieldError}
/>
<FormControl>
<InputLabel htmlFor="relationship" required>
Relationship
</InputLabel>
<Select
id="relationship"
value={editedFamilyData?.relation_with_principal || ''}
onChange={(e) => {
setEditedFamilyData({ ...editedFamilyData, relation_with_principal: e.target.value })
setRelationshipField(e.target.value);
setRelationshipFieldError(e.target.value.trim() === '' ? 'This field is required' : '');
}}
fullWidth
label="Relationship"
error={!!relationshipFieldError}
>
<MenuItem value="H">Husband</MenuItem>
<MenuItem value="W">Wife</MenuItem>
<MenuItem value="S">Son</MenuItem>
<MenuItem value="D">Daughter</MenuItem>
</Select>
<FormHelperText style={{ color: 'red' }}>{relationshipFieldError}</FormHelperText>
</FormControl>
<FormControl>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Date of Birth"
value={editedFamilyData?.birth_date || ''}
onChange={(newValue) => {
setEditedFamilyData({ ...editedFamilyData, birth_date: newValue });
setBirthDateField(newValue);
setBirthDateFieldError(newValue === '' || newValue === null ? 'This field is required' : '');
if(newValue !== null)
{
newValue = newValue.toString();
setBirthDateFieldCheck(newValue === 'Invalid Date' ? 0 : 1);
}
}}
inputFormat="dd-MM-yyyy"
renderInput={(params) => <TextField {...params} required/>}
/>
</LocalizationProvider>
<FormHelperText style={{ color: 'red' }}>{birthDateFieldError}</FormHelperText>
</FormControl>
<TextField
label="Email Address"
required
value={editedFamilyData?.email || ''}
onChange={(e) => {
setEditedFamilyData({ ...editedFamilyData, email: e.target.value })
setEmailField(e.target.value);
setEmailFieldError(
e.target.value.trim() === '' ? 'This field is required' : !isValidEmail(e.target.value) ? 'Invalid email address' : ''
);
setEmailFieldCheck(isValidEmail(e.target.value));
}}
fullWidth
sx={{ marginTop: '16px' }}
error={!!emailFieldError}
helperText={emailFieldError}
/>
<TextField
label="Phone No."
value={editedFamilyData?.phone || ''}
onChange={(e) => {
let input = e.target.value;
// Hanya izinkan angka dan karakter '+'
input = input.replace(/[^0-9+]/g, '');
// Batasi panjang input menjadi 15 digit
const maxLength = 15;
const sanitizedInput = input.slice(0, maxLength);
setEditedFamilyData({ ...editedFamilyData, phone: sanitizedInput });
setPhoneField(sanitizedInput);
setPhoneFieldError(e.target.value.trim() === '' ? 'This field is required' : '');
}}
fullWidth
sx={{ marginTop: '16px' }}
inputProps={{
inputMode: 'tel',
pattern: '^\\+?[0-9]*$', // Memungkinkan karakter '+' di awal, diikuti oleh angka
}}
error={!!phoneFieldError}
helperText={phoneFieldError}
/>
</Stack>
</DialogContent>
<DialogActions>
<Button onClick={handleCloseDialog}>Cancel</Button>
<Button onClick={handleSaveData} variant="contained" color="primary" disabled={!isRequiredFieldsFilled()}>
Save
</Button>
</DialogActions>
</Dialog>
</Card>
);
}

View File

@@ -1,5 +1,6 @@
// mui
import { Button, IconButton, Card, Stack, Typography, TextField } from '@mui/material';
import { Button, IconButton, Card, Stack, Typography, TextField, InputLabel } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import { CardMembership, Visibility as VisibilityIcon } from '@mui/icons-material';
// components
import Iconify from '../../../components/Iconify';
@@ -22,7 +23,42 @@ export default function CardPersonalInformation({ data }) {
const [phone, setPhone] = useState(data?.phone || '');
const [address, setAddress] = useState(data?.main_address_id || '');
/* const [updatedData, setUpdatedData] = useState(data); */
//Check Required
const [nameField, setNameField] = useState('');
const [weightField, setWeightField] = useState('');
const [heightField, setHeightField] = useState('');
const [emailField, setEmailField] = useState('');
const [emailFieldCheck, setEmailFieldCheck] = useState(false);
const [phoneField, setPhoneField] = useState('');
const [nameFieldError, setNameFieldError] = useState('');
const [weightFieldError, setWeightFieldError] = useState('');
const [heightFieldError, setHeightFieldError] = useState('');
const [emailFieldError, setEmailFieldError] = useState('');
const [phoneFieldError, setPhoneFieldError] = useState('');
useEffect(() => {
// Periksa apakah data sudah terdefinisi
if (data) {
// Atur state sesuai dengan data yang diterima
setNameField(data?.name || '');
setWeightField((data?.last_weight_kg || '').toString());
setHeightField((data?.last_height_cm || '').toString());
setEmailField(data?.email || '');
setEmailFieldCheck(isValidEmail(data?.email));
setPhoneField(data?.phone || '');
}
}, [data]);
const isValidEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const isRequiredFieldsFilled = () => {
return nameField.trim() !== '' && weightField.trim() !== '' && heightField.trim() !== '' && emailField.trim() !== '' && emailFieldCheck && phoneField.trim() !== '';
};
const handleEditData = () => {
setWeight(data?.last_weight_kg || '');
@@ -32,6 +68,20 @@ export default function CardPersonalInformation({ data }) {
setAddress(data?.main_address_id || '');
setEditedData(data);
setOpenDialog(true);
if (data) {
// Atur state sesuai dengan data yang diterima
setNameField(data?.name || '');
setWeightField((data?.last_weight_kg || '').toString());
setHeightField((data?.last_height_cm || '').toString());
setEmailField(data?.email || '');
setPhoneField(data?.phone || '');
setNameFieldError('');
setWeightFieldError('');
setHeightFieldError('');
setEmailFieldError('');
setPhoneFieldError('');
}
};
const handleCloseDialog = () => {
@@ -59,6 +109,7 @@ export default function CardPersonalInformation({ data }) {
// Handle the successful update
enqueueSnackbar('Data updated successfully', { variant: 'success' });
setOpenDialog(false);
window.location.reload();
})
.catch((error) => {
// Handle the error
@@ -75,131 +126,121 @@ export default function CardPersonalInformation({ data }) {
justifyContent="space-between"
sx={{ paddingY: 1, paddingX: 3 }}
>
<Typography variant="subtitle2">Informasi Pribadi</Typography>
<Button startIcon={<Iconify icon="heroicons:pencil-solid" />} onClick={handleEditData}>
<Typography variant="subtitle2">Personal Information</Typography>
{/*<Button startIcon={<Iconify icon="heroicons:pencil-solid" />} onClick={handleEditData}>
Edit Data
</Button>
</Button>*/}
</Stack>
{/* Stack 2 */}
<Stack direction="row" spacing={2} paddingX={2}>
<div style={{ position: 'relative', flex: 'none', height: 'fit-content' }}>
<img
width={52}
height={52}
src="/images/user-profile.png"
alt="user-profile"
style={{ borderRadius: '50%' }}
/>
<IconButton
color="primary"
sx={{
position: 'absolute',
bottom: 0,
right: 0,
width: '20px',
height: '20px',
padding: '4px',
backgroundColor: 'rgba(255,255,255,0.9)',
}}
>
<Iconify icon="material-symbols:photo-camera" />
</IconButton>
</div>
<Stack direction="row" paddingY={1} spacing={2} sx={{ flex: '100%' }}>
<Stack sx={{ width: '60%' }}>
<Typography variant="caption">Nama Lengkap</Typography>
<Typography variant="body2"> {data?.name} </Typography>
</Stack>
<Stack sx={{ width: '20%' }}>
<Typography variant="caption">Berat Badan </Typography>
<Typography variant="body2">{data?.last_weight_kg} kg</Typography>
</Stack>
<Stack sx={{ width: '20%' }}>
<Typography variant="caption">Tinggi Badan </Typography>
<Typography variant="body2">{data?.last_height_cm} cm</Typography>
</Stack>
<Stack maxHeight="584px" paddingX={2} sx={{ overflowY: 'auto' }}>
{/* Stack 2.1 */}
<Stack marginTop={2} spacing={1}>
{/*<Typography variant="subtitle2">Informasi Dasar</Typography>*/}
<Stack direction="row" spacing={2} sx={{ flex: '100%' }}>
<Stack direction="row" spacing={2} sx={{ width: '40%' }}>
<img
width={52}
height={52}
src="/images/user-profile.png"
alt="user-profile"
style={{ borderRadius: '50%' }}
/>
<Stack>
<Typography variant="caption">Full Name</Typography>
<Typography variant="body2"> {data?.name} </Typography>
</Stack>
</Stack>
<Stack sx={{ width: '30%' }}>
<Typography variant="caption">Weight</Typography>
<Typography variant="body2">{data?.last_weight_kg} kg</Typography>
</Stack>
<Stack sx={{ width: '30%' }}>
<Typography variant="caption">Height</Typography>
<Typography variant="body2">{data?.last_height_cm} cm</Typography>
</Stack>
</Stack>
</Stack>
</Stack>
{/* Stack 3 */}
<Stack maxHeight="338px" paddingX={2} sx={{ overflowY: 'auto' }}>
<Stack maxHeight="584px" paddingX={2} sx={{ overflowY: 'auto' }}>
{/* Stack 3.1 */}
<Stack marginTop={2} spacing={1}>
<Typography variant="subtitle2">Informasi Dasar</Typography>
{/*<Typography variant="subtitle2">Informasi Dasar</Typography>*/}
<Stack direction="row" spacing={2} sx={{ flex: '100%' }}>
<Stack sx={{ width: '100%' }}>
<Typography variant="caption">Tempat Lahir</Typography>
<Stack sx={{ width: '40%' }}>
<Typography variant="caption">Place of Birth</Typography>
<Typography variant="body2"> {data?.birth_place} </Typography>
</Stack>
<Stack sx={{ width: '100%' }}>
<Typography variant="caption">Tanggal Lahir</Typography>
<Stack sx={{ width: '30%' }}>
<Typography variant="caption">Date of Birth</Typography>
<Typography variant="body2">
{' '}
{data?.birth_date ? fDate(data?.birth_date) : ''}
</Typography>
</Stack>
<Stack sx={{ width: '100%' }}>
<Typography variant="caption">Jenis Kelamin</Typography>
<Typography variant="body2">{data?.gender}</Typography>
<Stack sx={{ width: '30%' }}>
<Typography variant="caption">Gender</Typography>
<Typography variant="body2">{data?.gender ? data.gender.charAt(0).toUpperCase() + data.gender.slice(1) : ''}</Typography>
</Stack>
</Stack>
</Stack>
{/* Stack 3.2 */}
<Stack marginTop={2} spacing={1}>
<Typography variant="subtitle2">Informasi Kontak</Typography>
{/*<Typography variant="subtitle2">Informasi Kontak</Typography>*/}
<Stack direction="row" spacing={2} sx={{ flex: '100%' }}>
<Stack sx={{ width: '100%' }}>
<Typography variant="caption">Nomor Telpon</Typography>
<Stack sx={{ width: '40%' }}>
<Typography variant="caption">Phone Number</Typography>
<Typography variant="body2">{data?.phone}</Typography>
</Stack>
<Stack sx={{ width: '100%' }}>
<Stack sx={{ width: '30%' }}>
<Typography variant="caption">Email</Typography>
<Typography variant="body2">{data?.email}</Typography>
</Stack>
<Stack sx={{ width: '30%' }}>
</Stack>
</Stack>
<Stack>
<Typography variant="caption">Alamat</Typography>
<Typography variant="caption">Address</Typography>
<Typography variant="body2">{data?.main_address_id}</Typography>
</Stack>
</Stack>
{/* Stack 3.3 */}
<Stack marginTop={2} spacing={1}>
<Typography variant="subtitle2">Identitas Diri</Typography>
{/*<Typography variant="subtitle2">Identitas Diri</Typography>*/}
<Stack
direction="row"
justifyContent="space-between"
alignItems="center"
spacing={2}
sx={{ flex: '100%' }}
>
<Stack>
<Typography variant="caption">Nomor NIK</Typography>
<Stack sx={{ width: '40%' }}>
<Typography variant="caption">ID Member</Typography>
<Typography variant="body2">{data?.nik}</Typography>
</Stack>
<Stack>
<Button variant="contained" startIcon={<VisibilityIcon />}>
<Stack sx={{ width: '30%' }}>
<Typography variant="caption">Agama</Typography>
<Typography variant="body2">{data?.religion}</Typography>
</Stack>
<Stack sx={{ width: '30%' }}>
{/*<Button variant="contained" startIcon={<VisibilityIcon />} disabled>
Lihat Foto
</Button>
</Button>*/}
</Stack>
</Stack>
</Stack>
{/* Stack 3.4 */}
<Stack marginTop={2} spacing={1}>
<Typography variant="subtitle2">Informasi Lainnya</Typography>
{/*<Typography variant="subtitle2">Informasi Lainnya</Typography>*/}
<Stack direction="row" justifyContent="space-between" spacing={2} sx={{ flex: '100%' }}>
<Stack>
<Typography variant="caption">Agama</Typography>
<Typography variant="body2">{data?.religion}</Typography>
</Stack>
<Stack>
<Typography variant="caption">Status</Typography>
<Stack sx={{ width: '40%' }}>
<Typography variant="caption">Marital Status</Typography>
<Typography variant="body2">{data?.marital_status}</Typography>
</Stack>
<Stack>
<Typography variant="caption">Pendidikan</Typography>
<Stack sx={{ width: '30%' }}>
<Typography variant="caption">Education</Typography>
<Typography variant="body2">{data?.last_education}</Typography>
</Stack>
<Stack>
<Typography variant="caption">Pekerjaan</Typography>
<Stack sx={{ width: '30%' }}>
<Typography variant="caption">Occupation</Typography>
<Typography variant="body2">{data?.current_employment}</Typography>
</Stack>
</Stack>
@@ -207,44 +248,124 @@ export default function CardPersonalInformation({ data }) {
</Stack>
{/* Dialog */}
<Dialog open={openDialog} onClose={handleCloseDialog}>
<DialogTitle>Edit Data</DialogTitle>
<Dialog open={openDialog} onClose={handleCloseDialog} fullWidth={true}>
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
<Stack direction="row" alignItems="center" justifyContent="space-between">
<Stack direction="row">
<Iconify width={25} height={25} sx={{ marginRight: '10px' }} />
<Typography variant="h6">Edit Data</Typography>
</Stack>
<IconButton sx={{ color: '#FFF' }} onClick={handleCloseDialog}>
<CloseIcon />
</IconButton>
</Stack>
</DialogTitle>
<DialogContent>
<Stack spacing={2}>
<TextField
label="Full Name"
required
value={editedData ? editedData.name : ''}
onChange={(e) => setEditedData({ ...editedData, name: e.target.value })}
onChange={(e) =>{
setEditedData({ ...editedData, name: e.target.value });
setNameField(e.target.value);
setNameFieldError(e.target.value.trim() === '' ? 'This field is required' : '');
}}
fullWidth
sx={{ marginTop: '16px' }}
inputProps={{ maxLength: 50 }}
error={!!nameFieldError}
helperText={nameFieldError}
/>
<TextField
label="Weight (kg)"
required
value={weight}
onChange={(e) => setWeight(e.target.value)}
onChange={(e) => {
let input = e.target.value;
// Hanya izinkan angka
input = input.replace(/[^0-9]/g, '');
// Batasi panjang input menjadi 3 digit
const maxLength = 3;
const sanitizedInput = input.slice(0, maxLength);
setWeight(sanitizedInput);
setWeightField(sanitizedInput);
setWeightFieldError(e.target.value.trim() === '' ? 'This field is required' : '');
}}
fullWidth
sx={{ marginTop: '16px' }}
inputProps={{
inputMode: 'numeric',
pattern: '[0-9]*', // Memungkinkan hanya karakter angka
}}
error={!!weightFieldError}
helperText={weightFieldError}
/>
<TextField
label="Height (cm)"
required
value={height}
onChange={(e) => setHeight(e.target.value)}
onChange={(e) => {
let input = e.target.value;
// Hanya izinkan angka
input = input.replace(/[^0-9]/g, '');
// Batasi panjang input menjadi 3 digit
const maxLength = 3;
const sanitizedInput = input.slice(0, maxLength);
setHeight(sanitizedInput);
setHeightField(sanitizedInput);
setHeightFieldError(e.target.value.trim() === '' ? 'This field is required' : '');
}}
fullWidth
sx={{ marginTop: '16px' }}
inputProps={{
inputMode: 'numeric',
pattern: '[0-9]*', // Memungkinkan hanya karakter angka
}}
error={!!heightFieldError}
helperText={heightFieldError}
/>
<TextField
label="Email Address"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
onChange={(e) => {
setEmail(e.target.value)
setEmailField(e.target.value);
setEmailFieldError(
e.target.value.trim() === '' ? 'This field is required' : !isValidEmail(e.target.value) ? 'Invalid email address' : ''
);
setEmailFieldCheck(isValidEmail(e.target.value));
}}
fullWidth
sx={{ marginTop: '16px' }}
error={!!emailFieldError}
helperText={emailFieldError}
/>
<TextField
label="Phone No."
required
value={phone}
onChange={(e) => setPhone(e.target.value)}
onChange={(e) => {
let input = e.target.value;
// Hanya izinkan angka dan karakter '+'
input = input.replace(/[^0-9+]/g, '');
// Batasi panjang input menjadi 13 digit
const maxLength = 15;
const sanitizedInput = input.slice(0, maxLength);
setPhone(sanitizedInput);
setPhoneField(sanitizedInput);
setPhoneFieldError(e.target.value.trim() === '' ? 'This field is required' : '');
}}
fullWidth
sx={{ marginTop: '16px' }}
inputProps={{
inputMode: 'tel',
pattern: '^\\+?[0-9]*$', // Memungkinkan karakter '+' di awal, diikuti oleh angka
}}
error={!!phoneFieldError}
helperText={phoneFieldError}
/>
{/* <TextField
label="Address"
@@ -260,7 +381,7 @@ export default function CardPersonalInformation({ data }) {
<DialogActions>
<Button onClick={handleCloseDialog}>Cancel</Button>
<Button onClick={handleSaveData} variant="contained" color="primary">
<Button onClick={handleSaveData} variant="contained" color="primary" disabled={!isRequiredFieldsFilled()}>
Save
</Button>
</DialogActions>

View File

@@ -30,7 +30,7 @@ const RootStyle = styled(Card)(({ theme }) => ({
const defaultData = [
{ name: 'Requested', value: 5, color: palette.dark.primary.dark },
{ name: 'Approval', value: 1, color: palette.dark.warning.dark },
{ name: 'Disbrusment', value: 0, color: palette.dark.success.dark },
//{ name: 'Disbrusment', value: 0, color: palette.dark.success.dark },
{ name: 'Rejected', value: 3, color: palette.dark.error.dark },
];
@@ -39,13 +39,13 @@ const defaultData = [
export default function CardClaimStatus({ data }: PropsCardClaimStatus) {
return (
<RootStyle>
<Stack sx={{ mb: 1 }}>
{/*<Stack sx={{ mb: 1 }}>
<Typography variant="body2">Claim Status</Typography>
</Stack>
</Stack>*/}
<Grid container spacing={2}>
{data
? data.map(({ name, value, color }: ClaimStatusType, key) => (
<Grid item key={key} xs={6} sm={3}>
<Grid item key={key} xs={12} sm={4}>
<Card
sx={{
paddingX: 1,
@@ -71,7 +71,7 @@ export default function CardClaimStatus({ data }: PropsCardClaimStatus) {
</Grid>
))
: defaultData.map(({ name, value, color }: ClaimStatusType, key) => (
<Grid item key={key} xs={6} sm={3}>
<Grid item key={key} xs={12} sm={3}>
<Card
sx={{
paddingX: 1,

View File

@@ -0,0 +1,102 @@
// @mui
import { Grid, Card, Typography, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
// theme
import palette from '../../theme/palette';
// ----------------------------------------------------------------------
interface ClaimStatusType {
name: string;
value: number;
color: string;
}
interface PropsCardClaimStatus {
data?: ClaimStatusType[];
}
// ----------------------------------------------------------------------
const RootStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: theme.spacing(2),
color: 'black',
backgroundColor: theme.palette.grey[200],
}));
// ----------------------------------------------------------------------
const defaultData = [
{ name: 'Requested', value: 5, color: palette.dark.primary.dark },
{ name: 'Approval', value: 1, color: palette.dark.warning.dark },
{ name: 'Disbrusment', value: 0, color: palette.dark.success.dark },
{ name: 'Rejected', value: 3, color: palette.dark.error.dark },
];
// ----------------------------------------------------------------------
export default function CardClaimStatus({ data }: PropsCardClaimStatus) {
return (
<RootStyle>
<Stack sx={{ mb: 1 }}>
<Typography variant="body2">Claim Status</Typography>
</Stack>
<Grid container spacing={2}>
{data
? data.map(({ name, value, color }: ClaimStatusType, key) => (
<Grid item key={key} xs={6} sm={4}>
<Card
sx={{
paddingX: 1,
borderRadius: 0.75,
borderColor: color,
borderStyle: 'solid',
borderWidth: '1px',
padding: 2,
flex: 1,
textAlign: 'center',
}}
>
<Typography component="p" variant="body2">
{name}
</Typography>
<Typography component="p" variant="h5" sx={{ marginTop: 2 }}>
{value}
</Typography>
<Typography component="p" variant="body2" sx={{ marginTop: 2 }}>
Cases
</Typography>
</Card>
</Grid>
))
: defaultData.map(({ name, value, color }: ClaimStatusType, key) => (
<Grid item key={key} xs={6} sm={4}>
<Card
sx={{
paddingX: 1,
borderRadius: 0.75,
borderColor: color,
borderStyle: 'solid',
borderWidth: '1px',
padding: 2,
flex: 1,
textAlign: 'center',
}}
>
<Typography component="p" variant="body2">
{name}
</Typography>
<Typography component="p" variant="h5" sx={{ marginTop: 2 }}>
{value}
</Typography>
<Typography component="p" variant="body2" sx={{ marginTop: 2 }}>
Cases
</Typography>
</Card>
</Grid>
))}
</Grid>
</RootStyle>
);
}

View File

@@ -0,0 +1,148 @@
// @mui
import { styled } from '@mui/material/styles';
import { Button, Card, Typography, Link, Divider, Stack } from '@mui/material';
import { ChevronRight } from '@mui/icons-material';
// components
import Iconify from '../../components/Iconify';
// Section
import DialogNotification from './DialogNotification';
import DialogDetailClaim from './DialogDetailClaim';
// React
import { useState } from 'react';
// ----------------------------------------------------------------------
type DataContent = {
info: string;
date: string;
time: string;
};
type NotificationProps = {
data?: DataContent[];
};
// ----------------------------------------------------------------------
const RootNotificationStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: '1.5rem',
color: 'black',
backgroundColor: theme.palette.grey[200],
height: '100%',
maxHeight: '240px',
}));
const ItemNotificationStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: theme.spacing(1),
borderRadius: 0.5,
color: 'black',
marginTop: 2,
overflowY: 'auto',
maxHeight: '154px',
gap: '0.5rem',
}));
// ----------------------------------------------------------------------
export default function CardNotification({ data }: NotificationProps) {
const [openDialog, setOpenDialog] = useState(false);
const [dialogTitle, setDialogTitle] = useState('');
const [isDialog, setIsDialog] = useState('');
const clickHandler = (isDialog: string) => {
switch (isDialog) {
case 'allNotification':
setDialogTitle('Notification');
setIsDialog(isDialog);
setOpenDialog(true);
break;
case 'infoDetail':
setDialogTitle('Claim Details');
setIsDialog(isDialog);
setOpenDialog(true);
break;
default:
break;
}
};
return (
<RootNotificationStyle>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography>
<Typography
variant="body2"
component="span"
sx={{ display: 'flex', alignItems: 'center' }}
>
<Iconify icon="eva:bell-fill" marginRight={0.75} />
Notification
<span
style={{
width: '12px',
height: '12px',
backgroundColor: '#19BBBB',
marginLeft: '0.5rem',
borderRadius: '50%',
}}
/>
</Typography>
</Typography>
<Button
sx={{ typography: 'body2' }}
endIcon={<ChevronRight />}
onClick={() => clickHandler('allNotification')}
>
View All
</Button>
</Stack>
<ItemNotificationStyle>
{data
? data.map(({ info, date, time }, index) => (
<div key={index}>
{index >= 1 ? <Divider sx={{ marginY: 0.5 }} /> : ''}
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Stack direction="column" justifyContent="flex-start" alignItems="flex-start">
<Typography sx={{ typography: 'caption' }}>{info}</Typography>
<Link
component="button"
variant="caption"
underline="always"
onClick={() => clickHandler('infoDetail')}
>
Info Detail
</Link>
</Stack>
<Stack direction="column" justifyContent="flex-start" alignItems="flex-start">
<Typography sx={{ typography: 'caption', color: '#656565' }}>{date}</Typography>
<Typography sx={{ typography: 'caption', color: '#656565' }}>{time}</Typography>
</Stack>
</Stack>
</div>
))
: ''}
</ItemNotificationStyle>
{isDialog === 'allNotification' && (
<DialogNotification
openDialog={openDialog}
setOpenDialog={setOpenDialog}
title={{ name: dialogTitle }}
data={data}
/>
)}
{isDialog === 'infoDetail' && (
<DialogDetailClaim
openDialog={openDialog}
setOpenDialog={setOpenDialog}
title={{ name: dialogTitle }}
/>
)}
</RootNotificationStyle>
);
}

View File

@@ -0,0 +1,210 @@
// @mui
import { styled } from '@mui/material/styles';
import {
Button,
Card,
Typography,
LinearProgress,
linearProgressClasses,
Stack,
} from '@mui/material';
// components
import Iconify from '../../components/Iconify';
// React
import { useState } from 'react';
// utils
import { fCurrency, fSplit } from '../../utils/formatNumber';
/* -------------------------------- sections -------------------------------- */
import DialogTopUpLimit from './DialogTopUpLimit';
import DialogClaimSubmitMember from './DialogClaimSubmitMember';
/* ---------------------------------- types --------------------------------- */
type DataMember = {
id: number;
fullName: string;
memberId: string;
limit: {
current: number;
total: number;
percentage: number;
};
avatar?: {
url?: string;
title?: string;
};
};
type CardPolicyProps = {
data: {
limit: {
myLimit: {
balance: number;
total: number;
percentage: number;
};
lockLimit: {
balance: number;
percentage: number;
};
};
topUpLimit: {
companyName: string;
policyNumber: number;
totalMembers: number;
totalCases: number;
totalPersen: number;
myLimit: {
balance: number;
total: number;
percentage: number;
};
maxTopUp: number;
};
members: {
memberId: string;
memberFullName: string;
};
};
};
/* -------------------------------------------------------------------------- */
/* --------------------------------- styled --------------------------------- */
const RootBalanceStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: theme.spacing(3),
color: 'black',
backgroundColor: theme.palette.grey[200],
maxHeight: '240px',
}));
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800],
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
backgroundColor: theme.palette.primary.main,
},
}));
/* -------------------------------------------------------------------------- */
export default function CardPolicy(props: CardPolicyProps) {
const [openDialog, setOpenDialog] = useState(false);
const [dialogTitle, setDialogTitle] = useState('');
const [isDialog, setIsDialog] = useState('');
const { limit, topUpLimit, members } = props.data || {};
if (!limit || !topUpLimit) {
return null;
}
const clickHandler = (isDialog: string) => {
switch (isDialog) {
case 'submitClaim':
setDialogTitle('Add Claim');
setIsDialog(isDialog);
setOpenDialog(true);
break;
case 'topUpLimit':
setDialogTitle('Top Up Limit');
setIsDialog(isDialog);
setOpenDialog(true);
break;
default:
break;
}
};
return (
<RootBalanceStyle>
<>
<Stack direction="row" justifyContent="space-between" sx={{ mb: 1 }}>
<div>
<Typography variant="body2" component="span" sx={{ opacity: 0.72 }}>
Total Limit
</Typography>
<Typography sx={{ typography: 'body2' }}>
{fCurrency(limit.myLimit ? limit.myLimit.balance : 0)}
</Typography>
<Typography sx={{ typography: 'caption', color: '#919EAB' }}>
/ {fSplit(limit.myLimit ? limit.myLimit.total : 0)}
</Typography>
</div>
<Stack direction="row" alignItems="center" justifyContent="center">
<Typography variant="h5" sx={{ ml: 0.5 }}>
{limit.myLimit ? limit.myLimit.percentage : 0}%
</Typography>
</Stack>
</Stack>
<BorderLinearProgress
variant="determinate"
value={limit.myLimit ? limit.myLimit.percentage : 0}
sx={{ mb: 1 }}
/>
<Stack sx={{ backgroundColor: '#B2E8E8', paddingY: 1, paddingX: 1.5, mb: 2 }}>
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
<Iconify
icon="bxs:lock-alt"
width={12}
height={13}
sx={{ color: '#424242', marginRight: '6px' }}
/>
<Typography variant="caption" component="span">
Lock Fund ( {limit.lockLimit ? limit.lockLimit.percentage : 0}% )
</Typography>
</Typography>
<Typography sx={{ typography: 'caption', color: '#637381' }}>
{fSplit(limit.lockLimit ? limit.lockLimit.balance : 0)} /{' '}
{fSplit(limit.myLimit ? limit.myLimit.total : 0)}
</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Button
variant="outlined"
startIcon={<Iconify icon="bi:clipboard-check-fill" />}
fullWidth={true}
onClick={() => clickHandler('submitClaim')}
>
Submit Claim
</Button>
<Button
variant="contained"
startIcon={<Iconify icon="heroicons-solid:cash" />}
fullWidth={true}
onClick={() => clickHandler('topUpLimit')}
>
Top Up
</Button>
</Stack>
</>
{isDialog === 'submitClaim' && (
<DialogClaimSubmitMember
openDialog={openDialog}
setOpenDialog={setOpenDialog}
title={{ name: dialogTitle }}
/>
)}
{isDialog === 'topUpLimit' && (
<DialogTopUpLimit
openDialog={openDialog}
setOpenDialog={setOpenDialog}
title={{ name: dialogTitle, icon: 'heroicons-solid:cash' }}
data={topUpLimit}
/>
)}
</RootBalanceStyle>
);
}

View File

@@ -0,0 +1,281 @@
// @mui
import { styled } from '@mui/material/styles';
import {
Typography,
LinearProgress,
linearProgressClasses,
Stack,
TextField,
InputAdornment,
Card,
Grid,
IconButton,
FormControlLabel,
Checkbox,
} from '@mui/material';
import { Search as SearchIcon } from '@mui/icons-material';
// components
import MuiDialog from '../../components/MuiDialog';
import Iconify from '../../components/Iconify';
import HistoryRoundedIcon from '@mui/icons-material/HistoryRounded';
// React
import { ReactElement, useContext, useEffect, useState } from 'react';
import DialogRequestLog from './DialogRequestLog';
import axios from '../../utils/axios';
import { useSearchParams, useNavigate, Link } from 'react-router-dom';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { fSplit } from '../../utils/formatNumber';
import { LoadingButton } from '@mui/lab';
import { useDispatch, useSelector } from 'react-redux';
import { RootState } from '../../store';
import { claimSubmitAction, claimSubmitType } from '../../store/claimSubmit';
// ----------------------------------------------------------------------
type DataContentType = {
id: number;
fullName: string;
memberId: string;
limit: {
current: number;
total: number;
percentage: number;
};
avatar?: {
url?: string;
title?: string;
};
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
// data?: DataContent[];
};
// ----------------------------------------------------------------------
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: '#D1F1F1',
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
backgroundColor: '#54D62C',
},
}));
// ----------------------------------------------------------------------
export default function DialogClaimSubmitMember({
title,
openDialog,
setOpenDialog,
}: MuiDialogProps) {
const { corporateValue } = useContext(UserCurrentCorporateContext);
/* ---------------------------------- data ---------------------------------- */
const [data, setData] = useState([]);
const dispatch = useDispatch();
const selectedData = useSelector((state: RootState) => state.claims.data);
const [dataMemberClaim, setDataMemberClaim] = useState<DataContentType>({
id: 0,
fullName: '',
memberId: '',
limit: {
current: 0,
total: 0,
percentage: 0,
},
});
const navigate = useNavigate();
/* -------------------------------------------------------------------------- */
/* --------------------------------- Search --------------------------------- */
const [searchText, setSearchText] = useState('');
const [appliedParams, setAppliedParams] = useState({});
const handleSearchSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (searchText === '') {
setAppliedParams({});
} else {
setAppliedParams({ search: searchText });
}
await new Promise((resolve) => setTimeout(resolve, 500));
};
/* -------------------------------------------------------------------------- */
/* ------------------------------ Icon On Click ----------------------------- */
const handleCheck = (data: DataContentType, isChecked: boolean) => {
if (isChecked) {
dispatch(claimSubmitAction.patch([...selectedData, data]));
} else {
let temp = selectedData.filter((row) => row.memberId !== data.memberId);
dispatch(claimSubmitAction.patch(temp));
}
};
/* -------------------------------------------------------------------------- */
useEffect(() => {
(async () => {
if (openDialog === true) {
const response = await axios.get(`${corporateValue}/members`, {
params: { ...appliedParams, type: 'claim-submit' },
});
setData(response.data.data);
}
})();
}, [corporateValue, openDialog, appliedParams]);
useEffect(() => {
dispatch(claimSubmitAction.dispatch());
}, [dispatch]);
const getContent = () => (
<Stack>
<Stack marginTop={2} spacing={1}>
{data.map((row: DataContentType, key) => (
<Card
key={key}
sx={{bgcolor: (theme) => {
return selectedData.some((item) => item.memberId === row.memberId) ? theme.palette.primary.lighter : theme.palette.background.default
}
}}
>
<Stack direction="row" alignItems="center">
<Grid item xs={1} lg={1} xl={1}>
<form>
<FormControlLabel
value="end"
control={<Checkbox onChange={(e) => handleCheck(row, e.target.checked)} />}
label=""
labelPlacement="end"
sx={{ marginLeft: '20px' }}
/>
</form>
</Grid>
<div
style={{
position: 'relative',
flex: 'none',
height: 'fit-content',
margin: '15px',
}}
>
<img
width={52}
height={52}
src="/images/user-profile.png"
alt="user-profile"
style={{ borderRadius: '50%' }}
/>
</div>
<Grid item xs={11} lg={11} xl={11}>
<Typography variant="subtitle1">{row.fullName}</Typography>
<Typography color="#637381" variant="body2" sx={{ fontWeight: 500 }}>
{row.memberId}
</Typography>
</Grid>
{/* <Grid item xs={3} lg={3} xl={3}> */}
{/* <BorderLinearProgress
variant="determinate"
value={row.limit && row.limit.percentage}
// color='success'
sx={{ mb: 1 }}
/> */}
{/* <Stack direction={'row'}>
<Grid item xs={3}>
<Typography variant="overline" sx={{ textAlign: 'left' }}>
LIMIT
</Typography>
</Grid>
<Grid item xs={7} sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<Stack direction={'row'}>
<Typography variant="overline">
{fSplit(row.limit && row.limit.current)}
</Typography>
<Typography variant="overline">
/ {fSplit(row.limit && row.limit.total)}
</Typography>
</Stack>
</Grid>
</Stack> */}
{/* </Grid> */}
<Grid
item
xs={1}
lg={1}
xl={1}
style={{ display: 'flex', justifyContent: 'right', alignItems: 'right' }}
>
{/* <IconButton>
<Iconify icon="ic:history" />
</IconButton> */}
<IconButton
disabled={selectedData.length > 0}
sx={{ marginLeft: '10px' }}
onClick={() => {
dispatch(claimSubmitAction.patch([row]));
navigate(`/claim-request/${row.id}`);
}}
>
<Iconify icon="ic:round-chevron-right" />
</IconButton>
</Grid>
</Stack>
</Card>
))}
</Stack>
</Stack>
);
return (
<Grid container>
<Grid item xs={12} paddingX="10px" paddingY="20px">
<form onSubmit={handleSearchSubmit}>
<TextField
id="search-input"
variant="outlined"
fullWidth
onChange={(event) => setSearchText(event?.target.value)}
value={searchText}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
placeholder="Search Name or Member ID... "
sx={{ marginTop: 2 }}
/>
</form>
{getContent()}
</Grid>
<Grid item xs={12}>
<LoadingButton
variant="contained"
// sx={{ marginTop: 2, p: 2, margin: '10px', color: '#212B36', backgroundColor: '#DFE3E8' }}
sx={{ marginTop: 2, p: 2, margin: '10px' }}
fullWidth
disabled={selectedData.length === 0}
onClick={() => navigate('/claim-request/bulk')}
>
Claim Submit Selected
</LoadingButton>
</Grid>
</Grid>
);
}

View File

@@ -0,0 +1,175 @@
// @mui
import {
Button,
Box,
Stepper,
Step,
StepLabel,
Card,
Typography,
Divider,
Stack,
} from '@mui/material';
import { Add } from '@mui/icons-material';
// components
import MuiDialog from '../../components/MuiDialog';
// theme
import palette from '../../theme/palette';
// React
import { ReactElement } from 'react';
type DataContent = {
info: string;
date: string;
time: string;
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data?: DataContent[];
};
const steps = ['Review', 'Approval', 'Disbursement'];
const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => {
const getContent = () => (
<>
<Stack
alignItems="center"
justifyContent="space-between"
direction="row"
sx={{ marginTop: 1 }}
>
<Typography variant="subtitle1" sx={{ height: 'max-content' }}>
Claim Request
</Typography>
<Stack>
<Typography variant="caption">Submission date</Typography>
<Typography variant="caption">15 / 05 / 2022</Typography>
</Stack>
</Stack>
<Box sx={{ width: '100%', marginTop: 2 }}>
<Stepper alternativeLabel>
{steps.map((label) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
</Box>
<Stack marginTop={2}>
<Typography variant="subtitle1" paddingY={2}>
17 Mei 2022
</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Divider orientation="vertical" flexItem sx={{ borderStyle: 'dashed' }} />
<Stack spacing={2} sx={{ flex: 1, maxWidth: '100%' }}>
{/* Item 1 */}
<Card sx={{ paddingY: 2, paddingX: 3 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="body1">09:10 WIB</Typography>
<Typography
sx={{
backgroundColor: palette.light.warning.lighter,
color: palette.light.warning.dark,
borderColor: palette.light.warning.dark,
border: '1px solid',
borderRadius: '6px',
padding: 1,
}}
variant="caption"
>
Approval
</Typography>
</Stack>
<Divider sx={{ marginY: 2 }} />
<Stack>
<Typography variant="subtitle2" color="#404040">
Details : mohon melengkapi kekurangan dokumen
</Typography>
<Typography variant="caption" color="#757575" sx={{ marginTop: 2, marginBottom: 1 }}>
Lab pemeriksaan darah
</Typography>
<Button
variant="outlined"
startIcon={<Add />}
fullWidth
sx={{ typography: 'subtitle2', borderColor: '#F5F5F5' }}
>
Hasil Pemeriksaan Laboratorium
</Button>
</Stack>
</Card>
{/* Item 2 */}
<Card sx={{ flex: 1, maxWidth: '100%', paddingY: 2, paddingX: 3 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="body1">09:00 WIB</Typography>
<Typography
sx={{
backgroundColor: palette.light.warning.lighter,
color: palette.light.warning.dark,
borderColor: palette.light.warning.dark,
border: '1px solid',
borderRadius: '6px',
padding: 1,
}}
variant="caption"
>
Approval
</Typography>
</Stack>
<Divider sx={{ marginY: 2 }} />
<Stack>
<Typography variant="subtitle2" color="#404040">
Details : Penilaian Dokter
</Typography>
</Stack>
</Card>
{/* Item 3 */}
<Card sx={{ flex: 1, maxWidth: '100%', paddingY: 2, paddingX: 3 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="body1">08:00 WIB</Typography>
<Typography
sx={{
backgroundColor: '#F5F5F5',
color: '#757575',
borderColor: '#757575',
border: '1px solid',
borderRadius: '6px',
padding: 1,
}}
variant="caption"
>
Review
</Typography>
</Stack>
<Divider sx={{ marginY: 2 }} />
<Stack>
<Typography variant="subtitle2" color="#404040">
Details : Klaim Diajukan
</Typography>
</Stack>
</Card>
</Stack>
</Stack>
</>
);
return (
<MuiDialog
title={title}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
/>
);
};
export default DialogDetailClaim;

View File

@@ -0,0 +1,93 @@
// react
import { ReactElement, useState } from 'react';
// mui
import { Card, Divider, Link, Stack, Typography } from '@mui/material';
import { styled } from '@mui/material/styles';
// Component
import MuiDialog from '../../components/MuiDialog';
// Sections
import DialogDetailClaim from './DialogDetailClaim';
type DataContent = {
info: string;
date: string;
time: string;
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data?: DataContent[];
};
const ItemNotificationStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: theme.spacing(1),
borderRadius: 0.5,
color: 'black',
}));
const DialogNotification = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => {
const [openDialogClaim, setOpenDialogClaim] = useState(false);
const [dialogTitleClaim, setDialogTitleClaim] = useState('');
const clickHandler = () => {
setDialogTitleClaim('Claim Details');
setOpenDialogClaim(true);
};
const getContent = () => (
<Stack sx={{ marginTop: 2 }}>
<ItemNotificationStyle>
{data
? data.map(({ info, date, time }: DataContent, key) => (
<div key={key}>
{key >= 1 ? <Divider sx={{ marginY: 0.5 }} /> : ''}
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Stack direction="column" justifyContent="flex-start" alignItems="flex-start">
<Typography sx={{ typography: 'caption' }}>{info}</Typography>
<Link
component="button"
variant="caption"
underline="always"
onClick={clickHandler}
>
Info Detail
</Link>
</Stack>
<Stack direction="column" justifyContent="flex-start" alignItems="flex-start">
<Typography sx={{ typography: 'caption', color: '#656565' }}>{date}</Typography>
<Typography sx={{ typography: 'caption', color: '#656565' }}>{time}</Typography>
</Stack>
</Stack>
</div>
))
: ''}
</ItemNotificationStyle>
</Stack>
);
return (
<>
<MuiDialog
title={title}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
/>
<DialogDetailClaim
openDialog={openDialogClaim}
setOpenDialog={setOpenDialogClaim}
title={{ name: dialogTitleClaim }}
/>
</>
);
};
export default DialogNotification;

View File

@@ -0,0 +1,355 @@
// @mui
import { styled } from '@mui/material/styles';
import {
Typography,
LinearProgress,
linearProgressClasses,
Stack,
Card,
Button,
Divider,
Avatar,
} from '@mui/material';
// components
import MuiDialog from '../../components/MuiDialog';
import Iconify from '../../components/Iconify';
// React
import { ReactElement, useRef, useState } from 'react';
// form
import { LoadingButton } from '@mui/lab';
import axios from '../../utils/axios';
import { enqueueSnackbar } from 'notistack';
import { fPostFormat } from '../../utils/formatTime';
import { fCurrency } from '../../utils/formatNumber';
import { makeFormData } from '../../utils/jsonToFormData';
/* ---------------------------------- types --------------------------------- */
type DataContentType = {
id: number;
fullName: string;
memberId: string;
limit: {
current: number;
total: number;
percentage: number;
};
avatar?: {
url?: string;
title?: string;
};
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data: DataContentType;
};
/* -------------------------------------------------------------------------- */
/* --------------------------------- styles --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800],
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
background: 'linear-gradient(270deg, #19BBBB 38.42%, #FF9565 76.21%, #FE7253 104.02%)',
},
}));
/* -------------------------------------------------------------------------- */
const DialogRequestLog = ({ openDialog, setOpenDialog, data }: MuiDialogProps) => {
const [serviceCode, setServiceCode] = useState('IP');
const fileDiagnosaInput = useRef<HTMLInputElement>(null);
const [fileDiagnosas, setFileDiagnosas] = useState([]);
const handleDiagnosaInputChange = (event) => {
if (event.target.files[0]) {
setFileDiagnosas([...fileDiagnosas, ...event.target.files]);
} else {
console.log('NO FILE');
}
};
const removeDiagnosaFiles = (filesState, index) => {
setFileDiagnosas(filesState.filter((file, fileIndex) => fileIndex != index));
};
const fileKondisiInput = useRef<HTMLInputElement>(null);
const [fileKondisis, setFileKondisis] = useState([]);
const handleKondisiInputChange = (event) => {
if (event.target.files[0]) {
setFileKondisis([...fileKondisis, ...event.target.files]);
} else {
console.log('NO FILE');
}
};
const removeKondisiFiles = (filesState, index) => {
setFileKondisis(filesState.filter((file, fileIndex) => fileIndex != index));
};
const fileHasilPenunjangInput = useRef<HTMLInputElement>(null);
const [fileHasilPenunjangs, setFileHasilPenunjangs] = useState([]);
const handleResultInputChange = (event) => {
if (event.target.files[0]) {
setFileHasilPenunjangs([...fileHasilPenunjangs, ...event.target.files]);
} else {
console.log('NO FILE');
}
};
const removeFiles = (filesState, index) => {
setFileHasilPenunjangs(filesState.filter((file, fileIndex) => fileIndex != index));
};
const [submitLoading, setSubmitLoading] = useState(false);
function submitRequest() {
setSubmitLoading(true);
const formData = makeFormData({
member_id: data.id,
result_files: fileHasilPenunjangs,
diagnosa_files: fileDiagnosas,
kondisi_files: fileKondisis,
service_code: serviceCode,
});
axios
.post('/claim-requests', formData)
.then((response) => {
enqueueSnackbar(response.data.message ?? 'Berhasil membuat data', { variant: 'success' });
})
.catch(({ response }) => {
enqueueSnackbar(response.data.message ?? 'Something Went Wrong', { variant: 'error' });
})
.then(() => {
setSubmitLoading(false);
});
}
const getContent = () => (
<Stack paddingY={1}>
<Stack direction="row" justifyContent={'end'} sx={{ marginBottom: 2 }}>
<Typography textAlign={'right'} variant="body2">
Submission Date : <br /> {fPostFormat(new Date(), 'dd/MM/yyyy')}
</Typography>
</Stack>
<Card sx={{ p: 1, background: '#f4f6f8', marginBottom: 2 }}>
<Stack direction="row" spacing={2}>
<Button
sx={{ padding: 2, width: '100%' }}
variant={serviceCode == 'IP' ? 'contained' : ''}
onClick={() => {
setServiceCode('IP');
}}
>
Rawat Inap
</Button>
<Button
sx={{ padding: 2, width: '100%' }}
variant={serviceCode == 'OP' ? 'contained' : ''}
onClick={() => {
setServiceCode('OP');
}}
>
Rawat Jalan
</Button>
</Stack>
</Card>
<Card sx={{ p: 1, background: '#f4f6f8', marginBottom: 2 }}>
<Stack direction="row">
<Avatar
src="https://minimal-assets-api.vercel.app/assets/images/avatars/avatar_5.jpg"
alt={data.fullName}
sx={{ marginTop: 1, width: 48, height: 48 }}
/>
<Stack sx={{ p: 1 }}>
<Typography>{data.fullName}</Typography>
<Typography>{data.memberId}</Typography>
</Stack>
</Stack>
</Card>
<Card sx={{ paddingY: 1, paddingX: 2 }}>
<Typography variant="body1" sx={{ marginBottom: 1, fontWeight: 600 }}>
Total Limit
</Typography>
<BorderLinearProgress variant="determinate" value={data.limit.percentage} />
<Typography sx={{ textAlign: 'right', marginTop: 1 }}>
{fCurrency(data.limit.current)} / {fCurrency(data.limit.total)}
</Typography>
</Card>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={4}
sx={{ marginY: 2 }}
>
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill" /> Dokumen Kondisi
</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileKondisis &&
fileKondisis.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Typography sx={{ color: 'text.secondary' }}>{file.name}</Typography>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeKondisiFiles(fileKondisis, index);
}}
/>
</Stack>
))}
</Stack>
<input
type="file"
id="file"
ref={fileKondisiInput}
style={{ display: 'none' }}
multiple
onChange={handleKondisiInputChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileKondisiInput?.current?.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add Result
</LoadingButton>
</Stack>
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill" /> Dokumen Diagnosa
</Typography>
{/* <Typography variant="body2">Hasil Lab, </Typography> */}
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileDiagnosas &&
fileDiagnosas.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Typography sx={{ color: 'text.secondary' }}>{file.name}</Typography>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeDiagnosaFiles(fileDiagnosas, index);
}}
/>
</Stack>
))}
</Stack>
<input
type="file"
id="file"
ref={fileDiagnosaInput}
style={{ display: 'none' }}
multiple
onChange={handleDiagnosaInputChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileDiagnosaInput?.current?.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add Result
</LoadingButton>
</Stack>
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill" /> Dokumen Hasil Penunjang
</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileHasilPenunjangs &&
fileHasilPenunjangs.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Typography sx={{ color: 'text.secondary' }}>{file.name}</Typography>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeFiles(fileHasilPenunjangs, index);
}}
/>
</Stack>
))}
</Stack>
<input
type="file"
id="file"
ref={fileHasilPenunjangInput}
style={{ display: 'none' }}
multiple
onChange={handleResultInputChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileHasilPenunjangInput?.current?.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add File
</LoadingButton>
</Stack>
</Stack>
<LoadingButton
variant="contained"
sx={{ marginTop: 2, p: 2 }}
onClick={() => {
submitRequest();
}}
loading={submitLoading}
>
LOG Request
</LoadingButton>
</Stack>
);
return (
// <>
// <MuiDialog
// title={{ name: data.fullName }}
// openDialog={openDialog}
// setOpenDialog={setOpenDialog}
// content={getContent()}
// maxWidth="sm"
// />
// </>
getContent()
);
};
export default DialogRequestLog;

View File

@@ -0,0 +1,265 @@
// @mui
import { styled } from '@mui/material/styles';
import {
Typography,
LinearProgress,
linearProgressClasses,
Stack,
FormControlLabel,
} from '@mui/material';
import { LoadingButton } from '@mui/lab';
import Checkbox from '@mui/material/Checkbox';
// components
import MuiDialog from '../../components/MuiDialog';
import { FormProvider, RHFTextField } from '../../components/hook-form';
// React
import { useContext, ReactElement, useEffect, useState } from 'react';
import { fCurrency } from '../../utils/formatNumber';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
// yup
import * as Yup from 'yup';
// form
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import axios from '../../utils/axios';
import { enqueueSnackbar } from 'notistack';
/* ---------------------------------- types --------------------------------- */
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data?: DataProps;
};
type DataProps = {
companyName: string;
policyNumber: number;
totalMembers: number;
totalCases: number;
totalPersen: number;
myLimit: {
balance: number;
total: number;
percentage: number;
};
maxTopUp: number;
};
type FormValuesProps = {
topup: string;
};
/* -------------------------------------------------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800],
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
background: 'linear-gradient(270deg, #19BBBB 38.42%, #FF9565 76.21%, #FE7253 104.02%)',
},
}));
// ----------------------------------------------------------------------
export default function DialogTopUpLimit({
title,
openDialog,
setOpenDialog,
data,
}: MuiDialogProps) {
const [isDisabledInput, setIsDisabledInput] = useState(false);
const [isDisabledButton, setIsDisabledButton] = useState(true);
const [isCheckboxChecked, setIsCheckboxChecked] = useState(false);
const [ message, setMessage ] = useState ('');
const { corporateValue } = useContext(UserCurrentCorporateContext);
const TopUpSchema = Yup.object().shape({
topup: Yup.number().max(
data?.maxTopUp,
`Maximum top-up amount is ${fCurrency(data?.maxTopUp)}`
),
});
const defaultValues = {
topup: 0,
};
const methods = useForm<FormValuesProps>({
resolver: yupResolver(TopUpSchema),
// @ts-ignore
defaultValues,
});
const {
setValue,
reset,
handleSubmit,
formState: { errors, isSubmitting },
} = methods;
useEffect(() => {
if (openDialog === false) {
setIsDisabledInput(false);
setIsDisabledButton(true);
setIsCheckboxChecked(false);
reset();
}
}, [openDialog, reset]);
const onSubmit = async (data: FormValuesProps) => {
await new Promise((resolve) => setTimeout(resolve, 500));
setIsDisabledInput(false);
setIsDisabledButton(true);
setIsCheckboxChecked(false);
try {
// Send the HTTP POST request to the backend
await axios.post(corporateValue + '/topup', {
topup: data.topup,
});
// Show a success notification
enqueueSnackbar('The request has been sent', { variant: 'success' });
setOpenDialog(false);
reset();
} catch (error) {
// Show an error notification
enqueueSnackbar('An error occurred', { variant: 'error' });
setOpenDialog(false);
}
};
const onCheckHandler = (value: string) => {
setIsDisabledInput(!isDisabledInput);
value === '0' || value === '' ? setIsDisabledButton(true) : setIsDisabledButton(false);
setIsCheckboxChecked(!isCheckboxChecked);
// @ts-ignore
setValue('topup', data.maxTopUp.toString());
};
const onTopupHandler = (value: string) => {
//console.log(!!errors);
let newValue;
if (value.startsWith('0')) {
newValue = '0';
} else {
newValue = value;
}
newValue === '0' || newValue === '' ? setIsDisabledButton(true) : setIsDisabledButton(false);
setValue('topup', newValue);
};
const getContent = () => (
<Stack spacing={1} marginTop={2}>
<Stack>
<Typography variant="caption" color="#637381">
Company Name
</Typography>
<Typography variant="body2">{data ? data.companyName : ''}</Typography>
</Stack>
<Stack>
<Typography variant="caption" color="#637381">
Policy Number
</Typography>
<Typography variant="body2">{data ? data.policyNumber : 0}</Typography>
</Stack>
<Stack direction="row" spacing={22}>
<Stack>
<Typography variant="caption" color="#637381">
Total Member
</Typography>
<Typography variant="body2">{data ? data.totalMembers : 0} Person</Typography>
</Stack>
<Stack>
<Typography variant="caption" color="#637381">
Total Cases
</Typography>
<Typography variant="body2">{data ? data.totalCases : 0} Cases</Typography>
</Stack>
</Stack>
<Stack spacing={1} sx={{ backgroundColor: '#F4F6F8', borderRadius: 1.5, padding: 2 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Stack>
<Typography variant="body2">Company Pooled Fund</Typography>
<Typography variant="body2">{fCurrency(data ? data.myLimit.balance : 0)}</Typography>
<Typography variant="caption" color="#919EAB">
/ {data ? data.myLimit.total : 0}
</Typography>
</Stack>
<Stack>
<Typography variant="h5">{data ? data.myLimit.percentage : 0}%</Typography>
</Stack>
</Stack>
<BorderLinearProgress variant="determinate" value={data ? data.myLimit.percentage : 0} />
</Stack>
<Stack spacing={2}>
<Typography variant="subtitle1" marginTop={3}>
Top Up Limit
</Typography>
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<RHFTextField
name="topup"
label="Top Up"
type="number"
disabled={isDisabledInput}
onChange={(e) => onTopupHandler(e.target.value)}
error={!!errors.topup}
helperText={errors.topup?.message}
/>
<FormControlLabel
name="checkboxTopUp"
sx={{ typography: 'caption' }}
control={
<Checkbox
checked={isCheckboxChecked}
onChange={(e) => onCheckHandler(e.target.value)}
/>
}
label={'Max ' + fCurrency(data ? data.maxTopUp : 0)}
/>
<LoadingButton
fullWidth
size="large"
type="submit"
variant="contained"
loading={isSubmitting}
sx={{ marginTop: 2 }}
disabled={isDisabledButton}
>
Ajukan Permintaan
</LoadingButton>
</FormProvider>
</Stack>
</Stack>
);
return (
<MuiDialog
title={title}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
maxWidth="xs"
/>
);
}

View File

@@ -61,6 +61,10 @@ type CardPolicyProps = {
};
maxTopUp: number;
};
members: {
memberId: string;
memberFullName: string;
};
};
};
@@ -95,7 +99,7 @@ export default function CardPolicy(props: CardPolicyProps) {
const [dialogTitle, setDialogTitle] = useState('');
const [isDialog, setIsDialog] = useState('');
const { limit, topUpLimit } = props.data || {};
const { limit, topUpLimit, members } = props.data || {};
if (!limit || !topUpLimit) {
return null;
}
@@ -185,14 +189,13 @@ export default function CardPolicy(props: CardPolicyProps) {
</Stack>
</>
{/* {isDialog === 'submitClaim' && (
{isDialog === 'submitClaim' && (
<DialogClaimSubmitMember
openDialog={openDialog}
setOpenDialog={setOpenDialog}
title={{ name: dialogTitle }}
data={members}
/>
)} */}
)}
{isDialog === 'topUpLimit' && (
<DialogTopUpLimit

View File

@@ -15,8 +15,10 @@ import { Search as SearchIcon } from '@mui/icons-material';
import MuiDialog from '../../components/MuiDialog';
import Iconify from '../../components/Iconify';
// React
import { ReactElement, useState } from 'react';
import DialogClaimSubmitMemberSubmission from './DialogClaimSubmitMemberSubmission';
import { ReactElement, useContext, useEffect, useState } from 'react';
import DialogRequestLog from './DialogRequestLog';
import axios from '../../utils/axios';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
// ----------------------------------------------------------------------
@@ -43,7 +45,7 @@ type MuiDialogProps = {
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data?: DataContentType[];
// data?: DataContent[];
};
// ----------------------------------------------------------------------
@@ -66,9 +68,11 @@ export default function DialogClaimSubmitMember({
title,
openDialog,
setOpenDialog,
data,
}: MuiDialogProps) {
const { corporateValue } = useContext(UserCurrentCorporateContext);
/* ---------------------------------- data ---------------------------------- */
const [data, setData] = useState([]);
const [dataMemberClaim, setDataMemberClaim] = useState<DataContentType>({
id: 0,
fullName: '',
@@ -83,10 +87,15 @@ export default function DialogClaimSubmitMember({
/* --------------------------------- Search --------------------------------- */
const [searchText, setSearchText] = useState('');
const [appliedParams, setAppliedParams] = useState({});
const handleSearchSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (searchText === '') {
setAppliedParams({});
} else {
setAppliedParams({ search: searchText });
}
await new Promise((resolve) => setTimeout(resolve, 500));
};
/* -------------------------------------------------------------------------- */
@@ -97,7 +106,7 @@ export default function DialogClaimSubmitMember({
/* -------------------------------------------------------------------------- */
/* ------------------------------ Icon On Click ----------------------------- */
const [openDialogClaimMember, setOpenDialogMemberClaim] = useState(false);
const [openDialogRequestLog, setOpenDialogRequestLog] = useState(false);
const clickHandler = ({ id, fullName, memberId, limit, avatar }: DataContentType) => {
setDataMemberClaim({
@@ -114,10 +123,22 @@ export default function DialogClaimSubmitMember({
title: avatar && avatar.title,
},
});
setOpenDialogMemberClaim(true);
setOpenDialogRequestLog(true);
};
/* -------------------------------------------------------------------------- */
useEffect(() => {
(async () => {
if (openDialog === true) {
const response = await axios.get(`${corporateValue}/members`, {
params: { ...appliedParams, type: 'claim-submit' },
});
//console.log(response.data.data);
setData(response.data.data);
}
})();
}, [corporateValue, openDialog, appliedParams]);
const getContent = () => (
<Stack>
<Stack direction="row" justifyContent="space-between" alignItems="center" paddingY={1}>
@@ -146,57 +167,58 @@ export default function DialogClaimSubmitMember({
/>
</form>
<Stack marginTop={2} spacing={1}>
{data &&
data.map((row: DataContentType, key) => (
<Card key={key} sx={{ paddingY: 1, paddingX: 2 }}>
<Stack direction="row" alignItems="center" spacing={2}>
<img
width={40}
height={40}
src={row.avatar ? row.avatar.url : '/images/member.png'}
alt={row.avatar ? row.avatar.url : 'user-profile'}
style={{ borderRadius: '50%' }}
/>
<Stack sx={{ flex: '45%' }}>
<Typography variant="subtitle1">{row.fullName}</Typography>
<Typography color="#637381" variant="body2" sx={{ fontWeight: 500 }}>
Member ID : {row.memberId}
</Typography>
</Stack>
<Stack spacing={1} paddingY={1}>
<Typography color="#0A0A0A" variant="caption">
Total Limit
</Typography>
<BorderLinearProgress
variant="determinate"
value={row.limit && row.limit.percentage}
/>
<Typography variant="subtitle2" sx={{ fontWeight: 500 }}>
{row.limit && row.limit.current} /{' '}
<Typography variant="body2" color="#757575" component="span">
{row.limit && row.limit.total}
</Typography>
</Typography>
</Stack>
<IconButton
onClick={() =>
clickHandler({
id: row.id,
fullName: row.fullName,
memberId: row.memberId,
limit: {
current: row.limit.current,
total: row.limit.total,
percentage: row.limit.percentage,
},
})
}
>
<Iconify icon="ic:round-chevron-right" />
</IconButton>
{data.map((row: DataContentType, key) => (
<Card
key={key}
sx={{ paddingY: 1, paddingX: 2 }}
onClick={() =>
clickHandler({
id: row.id,
fullName: row.fullName,
memberId: row.memberId,
limit: {
current: row.limit.current,
total: row.limit.total,
percentage: row.limit.percentage,
},
})
}
>
<Stack direction="row" alignItems="center" spacing={2}>
<img
width={40}
height={40}
src={row.avatar ? row.avatar.url : '/images/member.png'}
alt={row.avatar ? row.avatar.url : 'user-profile'}
style={{ borderRadius: '50%' }}
/>
<Stack sx={{ flex: '45%' }}>
<Typography variant="subtitle1">{row.fullName}</Typography>
<Typography color="#637381" variant="body2" sx={{ fontWeight: 500 }}>
Member ID : {row.memberId}
</Typography>
</Stack>
</Card>
))}
<Stack spacing={1} paddingY={1}>
<Typography color="#0A0A0A" variant="caption">
Total Limit
</Typography>
<BorderLinearProgress
variant="determinate"
value={row.limit && row.limit.percentage}
/>
<Typography variant="subtitle2" sx={{ fontWeight: 500 }}>
{row.limit && row.limit.current} /{' '}
<Typography variant="body2" color="#757575" component="span">
{row.limit && row.limit.total}
</Typography>
</Typography>
</Stack>
<IconButton>
<Iconify icon="ic:round-chevron-right" />
</IconButton>
</Stack>
</Card>
))}
</Stack>
</Stack>
);
@@ -211,10 +233,9 @@ export default function DialogClaimSubmitMember({
maxWidth="sm"
/>
<DialogClaimSubmitMemberSubmission
title={title}
openDialog={openDialogClaimMember}
setOpenDialog={setOpenDialogMemberClaim}
<DialogRequestLog
openDialog={openDialogRequestLog}
setOpenDialog={setOpenDialogRequestLog}
data={dataMemberClaim}
/>
</>

View File

@@ -1,371 +0,0 @@
// @mui
import { styled } from '@mui/material/styles';
import {
Typography,
LinearProgress,
linearProgressClasses,
Stack,
Card,
Button,
Link,
Switch,
SwitchProps,
ButtonGroup,
} from '@mui/material';
import { Add as AddIcon, Cancel as CancelIcon } from '@mui/icons-material';
// components
import MuiDialog from '../../components/MuiDialog';
import Iconify from '../../components/Iconify';
import { FormProvider } from '../../components/hook-form';
// React
import { ReactElement, useEffect, useState } from 'react';
// yup
import * as Yup from 'yup';
// form
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { LoadingButton } from '@mui/lab';
import { fSplit } from '../../utils/formatNumber';
/* ---------------------------------- types --------------------------------- */
type DataContentType = {
id: number;
fullName: string;
memberId: string;
limit: {
current: number;
total: number;
percentage: number;
};
avatar?: {
url?: string;
title?: string;
};
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data: DataContentType;
};
type BorderLinearProgressProps = {
percentage: number;
};
type FormValuesProps = {
invoice: '';
};
/* -------------------------------------------------------------------------- */
/* --------------------------------- styles --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)<BorderLinearProgressProps>(
({ theme, percentage }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800],
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
background: 'linear-gradient(270deg, #19BBBB 38.42%, #FF9565 76.21%, #FE7253 104.02%)',
'&::before': {
content: '""',
position: 'absolute',
right: 0,
top: 0,
width: `${100 - percentage}%`,
zIndex: 1,
bottom: 0,
background: '#DFE3E8',
},
},
})
);
/* -------------------------------------------------------------------------- */
const DialogClaimSubmitMemberSubmission = ({
title,
openDialog,
setOpenDialog,
data,
}: MuiDialogProps) => {
/* ---------------------------- Get Current Date ---------------------------- */
const current = new Date();
const date = `${current.getDate()} / ${current.getMonth() + 1} / ${current.getFullYear()}`;
/* -------------------------------------------------------------------------- */
/* ------------------------------- file input ------------------------------- */
// const [multipleImages, setMultipleImages] = useState([]);
// Functions to preview multiple images
// const changeMultipleFiles = (e) => {
// if (e.target.files) {
// const imageArray = Array.from(e.target.files).map((file) => URL.createObjectURL(file));
// setMultipleImages((prevImages) => prevImages.concat(imageArray));
// }
// };
// const render = (data) => {
// data.map((image) => {
// <Typography key={image}>{image}</Typography>;
// });
// };
// const FileForm = (props: any) => (
// <input
// type="file"
// multiple
// {...register('invoice', { required: true })}
// onChange={changeMultipleFiles}
// />
// );
/* -------------------------------------------------------------------------- */
/* ------------------------------- Form Submit ------------------------------ */
const ClaimSubmitSchema = Yup.object().shape({
invoice: Yup.mixed()
.required('You need to provide a file')
// @ts-ignore
.test('fileSize', 'The file is too large', (value) => {
for (let index = 0; index < value.length; index++) {
return value ? value[index].size <= 2000000 : false;
}
}),
});
const methods = useForm<FormValuesProps>({
resolver: yupResolver(ClaimSubmitSchema),
});
const {
register,
reset,
handleSubmit,
formState: { isSubmitting, errors },
} = methods;
// const {
// register,
// reset,
// handleSubmit,
// formState: { isSubmitting },
// } = useForm({ resolver: yupResolver(ClaimSubmitSchema) });
const onSubmit = ({ invoice }: FormValuesProps) => {
// console.log(invoice);
};
/* -------------------------------------------------------------------------- */
/* ---------------------------- Ios Switch Style ---------------------------- */
const IosSwitch = styled((props: SwitchProps) => (
<Switch focusVisibleClassName=".Mui-focusVisible" disableRipple {...props} />
))(({ theme }) => ({
width: 28,
height: 16,
padding: 0,
marginRight: '10px',
'& .MuiSwitch-switchBase': {
padding: 0,
margin: 2,
transitionDuration: '300ms',
'&.Mui-checked': {
transform: 'translateX(12px)',
color: '#fff',
'& + .MuiSwitch-track': {
opacity: 1,
border: 0,
},
'&.Mui-disabled + .MuiSwitch-track': {
opacity: 0.5,
},
},
'&.Mui-focusVisible .MuiSwitch-thumb': {
color: '#33cf4d',
border: '6px solid #fff',
},
'&.Mui-disabled .MuiSwitch-thumb': {
color: theme.palette.mode === 'light' ? theme.palette.grey[100] : theme.palette.grey[600],
},
'&.Mui-disabled + .MuiSwitch-track': {
opacity: theme.palette.mode === 'light' ? 0.7 : 0.3,
},
},
'& .MuiSwitch-thumb': {
boxSizing: 'border-box',
width: 12,
height: 12,
},
'& .MuiSwitch-track': {
borderRadius: 26 / 2,
opacity: 1,
transition: theme.transitions.create(['background-color'], {
duration: 500,
}),
},
}));
/* -------------------------------------------------------------------------- */
useEffect(() => {
if (openDialog === false) {
reset();
}
}, [openDialog, reset]);
const getContent = () => (
<Stack>
<Stack direction="row" justifyContent="space-between" alignItems="center" paddingY={1}>
<Typography variant="subtitle1">Claim Submission</Typography>
<Stack sx={{ color: '#757575' }}>
<Typography variant="caption">Submission date</Typography>
<Typography variant="caption">{date}</Typography>
</Stack>
</Stack>
<Card sx={{ paddingY: 1, paddingX: 2, marginTop: 2, backgroundColor: '#F4F6F8' }}>
<Stack direction="row" alignItems="center" spacing={2}>
<img
width={40}
height={40}
src="/images/member.png"
alt="user-profile"
style={{ borderRadius: '50%' }}
/>
<Stack sx={{ flex: '45%' }}>
<Typography variant="subtitle1">{data && data.fullName}</Typography>
<Typography color="#637381" variant="body2" sx={{ fontWeight: 500 }}>
Member ID : {data && data.memberId}
</Typography>
</Stack>
</Stack>
</Card>
<Card sx={{ paddingY: 1, paddingX: 2, marginTop: 2 }}>
<Stack spacing={1} paddingY={1}>
<Stack direction="row" justifyContent="space-between">
<Typography color="#0A0A0A" variant="caption">
Total Limit
</Typography>
<Link variant="caption" textAlign="center" href="#">
Details Benefits <Iconify icon="ic:round-chevron-right" />
</Link>
</Stack>
<BorderLinearProgress
variant="determinate"
value={100}
percentage={data && data.limit ? data.limit.percentage : 100}
/>
<Typography variant="subtitle2" sx={{ fontWeight: 500 }}>
{fSplit(data && data.limit ? data.limit.current : 0)} /{' '}
<Typography variant="body2" color="#757575" component="span">
{fSplit(data && data.limit ? data.limit.total : 0)}
</Typography>
</Typography>
</Stack>
</Card>
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
{/* Invoice */}
<Stack marginTop={2} spacing={1}>
<Stack>
<Typography variant="subtitle2">Real Invoice</Typography>
<Typography color="#9E9E9E" variant="caption">
Real invoice required
</Typography>
</Stack>
<input {...register('invoice')} type="file" />
{errors.invoice && errors.invoice.message ? <p>{errors.invoice.message}</p> : ''}
</Stack>
{/* Prescription */}
{/* <Stack marginTop={2} spacing={1}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Stack>
<Typography variant="subtitle2">
Doctor's Prescription and Another Documents
</Typography>
<Typography color="#9E9E9E" variant="caption">
Doctor's Prescription required
</Typography>
</Stack>
<Stack
direction="row"
padding={1}
alignItems="center"
sx={{
backgroundColor: 'white',
border: '1px solid #E0E0E0',
borderRadius: '6px',
height: 32,
}}
>
<IosSwitch defaultChecked />
<Typography color="#404040" variant="body2">
Yes
</Typography>
</Stack>
</Stack>
<ImportForm label="Add Prescription" />
</Stack> */}
{/* Laboratory */}
{/* <Stack marginTop={2} spacing={1}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Stack>
<Typography variant="subtitle2">
Doctor's Prescription and Another Documents
</Typography>
<Typography color="#9E9E9E" variant="caption">
Doctor's Prescription required
</Typography>
</Stack>
<Stack
direction="row"
padding={1}
alignItems="center"
sx={{
backgroundColor: 'white',
border: '1px solid #E0E0E0',
borderRadius: '6px',
height: 32,
}}
>
<IosSwitch defaultChecked />
<Typography color="#404040" variant="body2">
Yes
</Typography>
</Stack>
</Stack>
<ImportForm label="Add Result" />
</Stack> */}
{/* Submit */}
<Stack marginTop={1}>
<LoadingButton
fullWidth
size="large"
type="submit"
variant="contained"
loading={isSubmitting}
sx={{ marginTop: 2 }}
>
Ajukan Permintaan
</LoadingButton>
</Stack>
</FormProvider>
</Stack>
);
return (
<>
<MuiDialog
title={title}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
maxWidth="sm"
/>
</>
);
};
export default DialogClaimSubmitMemberSubmission;

View File

@@ -0,0 +1,355 @@
// @mui
import { styled } from '@mui/material/styles';
import {
Typography,
LinearProgress,
linearProgressClasses,
Stack,
Card,
Button,
Divider,
Avatar,
} from '@mui/material';
// components
import MuiDialog from '../../components/MuiDialog';
import Iconify from '../../components/Iconify';
// React
import { ReactElement, useRef, useState } from 'react';
// form
import { LoadingButton } from '@mui/lab';
import axios from '../../utils/axios';
import { enqueueSnackbar } from 'notistack';
import { fPostFormat } from '../../utils/formatTime';
import { fCurrency } from '../../utils/formatNumber';
import { makeFormData } from '../../utils/jsonToFormData';
/* ---------------------------------- types --------------------------------- */
type DataContentType = {
id: number;
fullName: string;
memberId: string;
limit: {
current: number;
total: number;
percentage: number;
};
avatar?: {
url?: string;
title?: string;
};
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data: DataContentType;
};
/* -------------------------------------------------------------------------- */
/* --------------------------------- styles --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 6,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: theme.palette.grey[theme.palette.mode === 'light' ? 300 : 800],
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 6,
background: 'linear-gradient(270deg, #19BBBB 38.42%, #FF9565 76.21%, #FE7253 104.02%)',
},
}));
/* -------------------------------------------------------------------------- */
const DialogRequestLog = ({ openDialog, setOpenDialog, data }: MuiDialogProps) => {
const [serviceCode, setServiceCode] = useState('IP');
const fileDiagnosaInput = useRef<HTMLInputElement>(null);
const [fileDiagnosas, setFileDiagnosas] = useState([]);
const handleDiagnosaInputChange = (event) => {
if (event.target.files[0]) {
setFileDiagnosas([...fileDiagnosas, ...event.target.files]);
} else {
console.log('NO FILE');
}
};
const removeDiagnosaFiles = (filesState, index) => {
setFileDiagnosas(filesState.filter((file, fileIndex) => fileIndex != index));
};
const fileKondisiInput = useRef<HTMLInputElement>(null);
const [fileKondisis, setFileKondisis] = useState([]);
const handleKondisiInputChange = (event) => {
if (event.target.files[0]) {
setFileKondisis([...fileKondisis, ...event.target.files]);
} else {
console.log('NO FILE');
}
};
const removeKondisiFiles = (filesState, index) => {
setFileKondisis(filesState.filter((file, fileIndex) => fileIndex != index));
};
const fileHasilPenunjangInput = useRef<HTMLInputElement>(null);
const [fileHasilPenunjangs, setFileHasilPenunjangs] = useState([]);
const handleResultInputChange = (event) => {
if (event.target.files[0]) {
setFileHasilPenunjangs([...fileHasilPenunjangs, ...event.target.files]);
} else {
console.log('NO FILE');
}
};
const removeFiles = (filesState, index) => {
setFileHasilPenunjangs(filesState.filter((file, fileIndex) => fileIndex != index));
};
const [submitLoading, setSubmitLoading] = useState(false);
function submitRequest() {
setSubmitLoading(true);
console.log(data.id);
const formData = makeFormData({
member_id: data.id,
result_files: fileHasilPenunjangs,
diagnosa_files: fileDiagnosas,
kondisi_files: fileKondisis,
service_code: serviceCode,
});
axios
.post('/claim-requests', formData)
.then((response) => {
enqueueSnackbar(response.data.message ?? 'Berhasil membuat data', { variant: 'success' });
})
.catch(({ response }) => {
enqueueSnackbar(response.data.message ?? 'Something Went Wrong', { variant: 'error' });
})
.then(() => {
setSubmitLoading(false);
});
}
const getContent = () => (
<Stack paddingY={1}>
<Stack direction="row" justifyContent={'end'} sx={{ marginBottom: 2 }}>
<Typography textAlign={'right'} variant="body2">
Submission Date : <br /> {fPostFormat(new Date(), 'dd/MM/yyyy')}
</Typography>
</Stack>
<Card sx={{ p: 1, background: '#f4f6f8', marginBottom: 2 }}>
<Stack direction="row" spacing={2}>
<Button
sx={{ padding: 2, width: '100%' }}
variant={serviceCode == 'IP' ? 'contained' : ''}
onClick={() => {
setServiceCode('IP');
}}
>
Rawat Inap
</Button>
<Button
sx={{ padding: 2, width: '100%' }}
variant={serviceCode == 'OP' ? 'contained' : ''}
onClick={() => {
setServiceCode('OP');
}}
>
Rawat Jalan
</Button>
</Stack>
</Card>
<Card sx={{ p: 1, background: '#f4f6f8', marginBottom: 2 }}>
<Stack direction="row">
<Avatar
src="https://minimal-assets-api.vercel.app/assets/images/avatars/avatar_5.jpg"
alt={data.fullName}
sx={{ marginTop: 1, width: 48, height: 48 }}
/>
<Stack sx={{ p: 1 }}>
<Typography>{data.fullName}</Typography>
<Typography>{data.memberId}</Typography>
</Stack>
</Stack>
</Card>
<Card sx={{ paddingY: 1, paddingX: 2 }}>
<Typography variant="body1" sx={{ marginBottom: 1, fontWeight: 600 }}>
Total Limit
</Typography>
<BorderLinearProgress variant="determinate" value={data.limit.percentage} />
<Typography sx={{ textAlign: 'right', marginTop: 1 }}>
{fCurrency(data.limit.current)} / {fCurrency(data.limit.total)}
</Typography>
</Card>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={4}
sx={{ marginY: 2 }}
>
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill" /> Dokumen Kondisi
</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileKondisis &&
fileKondisis.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Typography sx={{ color: 'text.secondary' }}>{file.name}</Typography>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeKondisiFiles(fileKondisis, index);
}}
/>
</Stack>
))}
</Stack>
<input
type="file"
id="file"
ref={fileKondisiInput}
style={{ display: 'none' }}
multiple
onChange={handleKondisiInputChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileKondisiInput?.current?.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add Result
</LoadingButton>
</Stack>
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill" /> Dokumen Diagnosa
</Typography>
{/* <Typography variant="body2">Hasil Lab, </Typography> */}
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileDiagnosas &&
fileDiagnosas.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Typography sx={{ color: 'text.secondary' }}>{file.name}</Typography>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeDiagnosaFiles(fileDiagnosas, index);
}}
/>
</Stack>
))}
</Stack>
<input
type="file"
id="file"
ref={fileDiagnosaInput}
style={{ display: 'none' }}
multiple
onChange={handleDiagnosaInputChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileDiagnosaInput?.current?.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add Result
</LoadingButton>
</Stack>
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill" /> Dokumen Hasil Penunjang
</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileHasilPenunjangs &&
fileHasilPenunjangs.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Typography sx={{ color: 'text.secondary' }}>{file.name}</Typography>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeFiles(fileHasilPenunjangs, index);
}}
/>
</Stack>
))}
</Stack>
<input
type="file"
id="file"
ref={fileHasilPenunjangInput}
style={{ display: 'none' }}
multiple
onChange={handleResultInputChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileHasilPenunjangInput?.current?.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add File
</LoadingButton>
</Stack>
</Stack>
<LoadingButton
variant="contained"
sx={{ marginTop: 2, p: 2 }}
onClick={() => {
submitRequest();
}}
loading={submitLoading}
>
LOG Request
</LoadingButton>
</Stack>
);
return (
<>
<MuiDialog
title={{ name: data.fullName }}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
maxWidth="sm"
/>
</>
);
};
export default DialogRequestLog;

View File

@@ -154,7 +154,7 @@ export default function DialogTopUpLimit({
};
const onTopupHandler = (value: string) => {
console.log(!!errors);
//console.log(!!errors);
let newValue;

View File

@@ -0,0 +1,37 @@
import { createSlice } from '@reduxjs/toolkit';
export type claimSubmitType = {
id: number;
fullName: string;
memberId: string;
limit: {
current: number;
total: number;
percentage: number;
};
};
type initState = {
data: claimSubmitType[];
};
let initState: initState = {
data: [],
};
const claimSubmitSlice = createSlice({
name: 'claimsubmit',
initialState: initState,
reducers: {
patch(state, action) {
state.data = action.payload;
},
dispatch(state) {
state.data = [];
},
},
});
export const claimSubmitAction = claimSubmitSlice.actions;
export default claimSubmitSlice.reducer;

View File

@@ -0,0 +1,12 @@
import { configureStore } from '@reduxjs/toolkit';
import claimSubmit from './claimSubmit';
const store = configureStore({
reducer: {
claims: claimSubmit,
},
});
export type RootState = ReturnType<typeof store.getState>;
export default store;

View File

@@ -4,21 +4,21 @@ import numeral from 'numeral';
// load a locale
numeral.register('locale', 'id', {
delimiters: {
thousands: '.',
decimal: ','
thousands: '.',
decimal: ',',
},
abbreviations: {
thousand: 'k',
million: 'm',
billion: 'b',
trillion: 't'
thousand: 'k',
million: 'm',
billion: 'b',
trillion: 't',
},
ordinal : function (number: number) {
return number === 1 ? 'er' : 'ème';
ordinal: function (number: number) {
return number === 1 ? 'er' : 'ème';
},
currency: {
symbol: 'Rp '
}
symbol: 'Rp ',
},
});
// switch between locales

View File

@@ -1,14 +1,14 @@
import { format,parseISO, getTime, setHours, setMinutes , formatDistanceToNow } from 'date-fns';
import { format, parseISO, getTime, setHours, setMinutes, formatDistanceToNow } from 'date-fns';
// ----------------------------------------------------------------------
export function fDate(date: Date | string | number) {
console.log(date);
//console.log(date);
return format(new Date(date), 'dd MMMM yyyy');
}
export function fDateTime(date: Date | string | number) {
return format(new Date(date), 'dd MMM yyyy p');
return format(new Date(date), 'dd MMM yyyy hh:mm');
}
export function fTimestamp(date: Date | string | number) {
@@ -19,12 +19,21 @@ export function fDateTimeSuffix(date: Date | string | number) {
return format(new Date(date), 'dd/MM/yyyy hh:mm p');
}
export function fDateSuffix(date: Date | string | number) {
return format(new Date(date), 'dd MMM yyyy');
}
export function fToNow(date: Date | string | number) {
return formatDistanceToNow(new Date(date), {
addSuffix: true
addSuffix: true,
});
}
export function fPostFormat(date: Date | string | number, dateFormat = 'yyyy-MM-dd HH:mm:ss') {
return format(new Date(date), dateFormat);
}
// export function fDateString(date) {
// const dateObj = parseISO(date);
// const formattedDate = format(dateObj, 'dd MMMM yyyy');
@@ -36,4 +45,4 @@ export function fToNow(date: Date | string | number) {
// const datePart = date.split(' ')[0]; // Memisahkan bagian tanggal
// const formattedDate = fDateString(datePart); // Menggunakan fungsi sebelumnya untuk memformat tanggal
// return formattedDate;
// }
// }

View File

@@ -0,0 +1,6 @@
import jsonToFormData from '@ajoelp/json-to-formdata';
export function makeFormData(object: any) {
return jsonToFormData(object)
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,8 @@
GENERATE_SOURCEMAP=false
PORT=8083
PORT=8000
REACT_APP_HOST_API_URL="http://lms.test"
VITE_API_URL="http://127.0.0.1:8000/api/internal"
# VITE_API_URL="https://aso-api.linksehat.dev/api/internal"
VITE_API_URL="http://localhost:8000/api/internal"

File diff suppressed because it is too large Load Diff

View File

@@ -51,6 +51,7 @@
"@mui/system": "^5.14.6",
"@mui/x-data-grid": "^5.17.26",
"@mui/x-date-pickers": "5.0.0-beta.2",
"@reduxjs/toolkit": "^1.9.6",
"@vitejs/plugin-react": "^1.3.2",
"apexcharts": "^3.42.0",
"axios": "^0.27.2",
@@ -75,7 +76,9 @@
"react-hook-form": "^7.45.4",
"react-intersection-observer": "^8.34.0",
"react-lazy-load-image-component": "^1.6.0",
"react-number-format": "^5.3.1",
"react-quill": "2.0.0-beta.4",
"react-redux": "^8.1.2",
"react-router": "^6.15.0",
"react-router-dom": "^6.15.0",
"simplebar": "^5.3.9",
@@ -84,6 +87,7 @@
"stylis-plugin-rtl": "^2.1.1",
"vite": "^3.2.7",
"vite-plugin-svgr": "^2.4.0",
"yarn": "^1.22.19",
"yup": "^0.32.11"
},
"devDependencies": {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,109 @@
import { Benefit } from "./corporates";
import { Member } from "./member";
export type ClaimRequest = {
id: number;
code: string;
name: string;
submission_date: string;
payment_type: string;
service_code: string;
claim_method: string;
service_type: string;
code_provider: string;
file_condition: Files;
member: Member;
claim: {
organization: Organizations
}
};
export type Claims = {
id: number;
code: string;
plan: Plan;
payor_id: string;
corporate_id: string;
policy_number: string;
benefit_desc: string;
member: Member;
benefit: Benefit | boolean;
status: string;
claim_request: ClaimRequest;
};
export type ClaimsEdit = {
id: number;
plan_id: string;
payor_id: string;
corporate_id: string;
policy_number: string;
member_id: string;
benefit_code: string;
benefit_desc: string;
amount_incurred: number;
amount_approved: number;
amount_not_approved: number;
excess_paid: number;
}
export type Files = {
name: string;
url: string;
path: string;
}
export type Plan = {
code: string;
}
export type ClaimHistoryCare = {
id: number;
claim_id: number;
service_code: string;
admission_date: string;
discharge_date: string;
main_diagnosis_id: number;
main_diagnosis_name: string;
medical_record_number: string;
organization_id: number;
practitioner_id: number;
organization_name: string;
practitioner_name: string;
secondary_diagnosis_id: number[];
sign: string;
symptoms: string;
name: any;
}
export type Organizations = {
id: number;
code: string;
name: string;
address: string;
type: string;
lat: string;
lng: string;
phone: string;
timezone: string;
active: boolean | number;
province_id: number;
city_id: number;
district_id: number;
village_id: number;
postal_code: string;
description: string;
technology: string;
support_services: string;
merchant_code: string;
merchant_key: string;
image_url: string;
region_groups: string;
};
export type Import = {
result_file: {
url: string,
name: string,
}
}

View File

@@ -5,6 +5,7 @@ export type Corporate = {
code: string;
name?: string;
welcome_message?: string;
payor_id: string;
help_text?: string;
logo?: any;
logo_url?: string;
@@ -12,6 +13,10 @@ export type Corporate = {
divisions?: Division[];
employees?: Employee[];
current_policy?: Policy;
corporate_plans_count: number;
corporate_benefits_count: number;
employees_count: number;
};
export type Division = {
@@ -21,6 +26,14 @@ export type Division = {
name?: string;
}
export type Hospital = {
id: number;
corporate_id: number;
code: string;
name?: string;
active: number;
}
export type Employee = {
id: number;
name: string;
@@ -39,14 +52,19 @@ export type Policy = {
minimal_stop_service_net: number;
start: string | Date;
end: string | Date;
limit_balance: number;
}
export type CorporatePlan = {
id: number;
corporate_id: number;
code: string;
service_code: string;
limit_rules: number;
corporate_plan_id: number;
name: string;
description: string | null;
type: number;
active: boolean | number;
}
@@ -101,6 +119,7 @@ export type Plan = {
currency: string;
max_surgery_reinstatement_days: string;
max_surgery_periode_days: string;
active: number
}
export type CorporateBenefit = {
@@ -113,6 +132,7 @@ export type CorporateBenefit = {
}
export type Benefit = {
id : number;
service_code : string;
plan_code : string;
benefit_code : string;
@@ -170,6 +190,11 @@ export type Benefit = {
currency : string;
show_benefit_item : string;
show_benefit_value : string;
plan : Plan;
benefit: Benefit;
corporate_benefit_code: string;
active: number;
limit_free_tc: number;
}
export type CorporateService = {
@@ -182,3 +207,14 @@ export type CorporateService = {
status: string;
configurations: any;
}
export type MasterExclusion = {
id?: string | number;
name?: string;
code: string;
description?: string;
}
export type CorporateId = {
corporate_id?: number
}

Some files were not shown because too many files have changed in this diff Show More