Merge remote-tracking branch 'origin/staging' into origin/production
This commit is contained in:
@@ -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"
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
124
frontend/client-portal/pnpm-lock.yaml
generated
124
frontend/client-portal/pnpm-lock.yaml
generated
@@ -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:
|
||||
|
||||
7
frontend/client-portal/public/icons/ic_gmail.svg
Normal file
7
frontend/client-portal/public/icons/ic_gmail.svg
Normal 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 |
BIN
frontend/client-portal/public/images/gmail.png
Normal file
BIN
frontend/client-portal/public/images/gmail.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 638 B |
8
frontend/client-portal/src/@types/claim-submit.ts
Normal file
8
frontend/client-portal/src/@types/claim-submit.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export type CardSubmit = {
|
||||
rows?: Array<DataType>;
|
||||
id: number,
|
||||
name: string,
|
||||
member_id: string,
|
||||
usesage_limit: number,
|
||||
limit: number
|
||||
}
|
||||
198
frontend/client-portal/src/@types/corporates.ts
Normal file
198
frontend/client-portal/src/@types/corporates.ts
Normal 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;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ export type Icd = {
|
||||
id: number;
|
||||
type: string;
|
||||
rev: string;
|
||||
active: number;
|
||||
version?: string;
|
||||
code: string;
|
||||
name: string;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
274
frontend/client-portal/src/components/CardClaimSubmit.tsx
Normal file
274
frontend/client-portal/src/components/CardClaimSubmit.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
98
frontend/client-portal/src/components/Label.tsx
Normal file
98
frontend/client-portal/src/components/Label.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
128
frontend/client-portal/src/components/UploadImage.tsx
Normal file
128
frontend/client-portal/src/components/UploadImage.tsx
Normal 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;
|
||||
@@ -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}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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} />
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
}}
|
||||
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
112
frontend/client-portal/src/components/hook-form/v2/RHFUpload.tsx
Normal file
112
frontend/client-portal/src/components/hook-form/v2/RHFUpload.tsx
Normal 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}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
12
frontend/client-portal/src/components/hook-form/v2/index.ts
Normal file
12
frontend/client-portal/src/components/hook-form/v2/index.ts
Normal 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';
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
1
frontend/client-portal/src/components/table/Index.ts
Normal file
1
frontend/client-portal/src/components/table/Index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as TableMoreMenu } from './TableMoreMenu';
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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
|
||||
// ----------------------------------------------------------------------
|
||||
// {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
504
frontend/client-portal/src/pages/AlarmCenter/ListMember.tsx
Normal file
504
frontend/client-portal/src/pages/AlarmCenter/ListMember.tsx
Normal 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>
|
||||
|
||||
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
69
frontend/client-portal/src/pages/ClaimReport/Detail.tsx
Normal file
69
frontend/client-portal/src/pages/ClaimReport/Detail.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
291
frontend/client-portal/src/pages/ClaimReport/DetailHistory.tsx
Normal file
291
frontend/client-portal/src/pages/ClaimReport/DetailHistory.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
428
frontend/client-portal/src/pages/ClaimReport/DetailTimeline.tsx
Normal file
428
frontend/client-portal/src/pages/ClaimReport/DetailTimeline.tsx
Normal 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>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
276
frontend/client-portal/src/pages/ClaimSubmit/Index.tsx
Normal file
276
frontend/client-portal/src/pages/ClaimSubmit/Index.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
114
frontend/client-portal/src/pages/ClaimSubmit/List.tsx
Normal file
114
frontend/client-portal/src/pages/ClaimSubmit/List.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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`, {
|
||||
|
||||
@@ -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([]);
|
||||
}
|
||||
|
||||
499
frontend/client-portal/src/pages/Corporate/Form.tsx
Normal file
499
frontend/client-portal/src/pages/Corporate/Form.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
142
frontend/client-portal/src/pages/Corporate/Index.tsx
Normal file
142
frontend/client-portal/src/pages/Corporate/Index.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
354
frontend/client-portal/src/pages/Corporate/List.tsx
Normal file
354
frontend/client-portal/src/pages/Corporate/List.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
348
frontend/client-portal/src/pages/Corporate/ServiceMonitoring.tsx
Normal file
348
frontend/client-portal/src/pages/Corporate/ServiceMonitoring.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
473
frontend/client-portal/src/pages/Corporate/Show.tsx
Normal file
473
frontend/client-portal/src/pages/Corporate/Show.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
85
frontend/client-portal/src/pages/Corporate/UserProfile.tsx
Normal file
85
frontend/client-portal/src/pages/Corporate/UserProfile.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
124
frontend/client-portal/src/pages/EmployeeData/Index.tsx
Normal file
124
frontend/client-portal/src/pages/EmployeeData/Index.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
381
frontend/client-portal/src/pages/EmployeeData/List.tsx
Normal file
381
frontend/client-portal/src/pages/EmployeeData/List.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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')));
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
210
frontend/client-portal/src/sections/claim-submit/CardPolicy.tsx
Normal file
210
frontend/client-portal/src/sections/claim-submit/CardPolicy.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -154,7 +154,7 @@ export default function DialogTopUpLimit({
|
||||
};
|
||||
|
||||
const onTopupHandler = (value: string) => {
|
||||
console.log(!!errors);
|
||||
//console.log(!!errors);
|
||||
|
||||
let newValue;
|
||||
|
||||
|
||||
37
frontend/client-portal/src/store/claimSubmit.ts
Normal file
37
frontend/client-portal/src/store/claimSubmit.ts
Normal 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;
|
||||
12
frontend/client-portal/src/store/index.ts
Normal file
12
frontend/client-portal/src/store/index.ts
Normal 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;
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
// }
|
||||
// }
|
||||
|
||||
6
frontend/client-portal/src/utils/jsonToFormData.ts
Normal file
6
frontend/client-portal/src/utils/jsonToFormData.ts
Normal 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
@@ -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"
|
||||
|
||||
1856
frontend/dashboard/package-lock.json
generated
1856
frontend/dashboard/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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": {
|
||||
|
||||
2380
frontend/dashboard/pnpm-lock.yaml
generated
2380
frontend/dashboard/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
109
frontend/dashboard/src/@types/claims.ts
Normal file
109
frontend/dashboard/src/@types/claims.ts
Normal 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,
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user