usermanagement, role, etc
This commit is contained in:
88
package-lock.json
generated
88
package-lock.json
generated
@ -12,6 +12,7 @@
|
|||||||
"@emotion/styled": "^11.14.0",
|
"@emotion/styled": "^11.14.0",
|
||||||
"@mui/icons-material": "^6.2.1",
|
"@mui/icons-material": "^6.2.1",
|
||||||
"@mui/material": "^6.2.1",
|
"@mui/material": "^6.2.1",
|
||||||
|
"@reduxjs/toolkit": "^2.5.0",
|
||||||
"@testing-library/jest-dom": "^5.17.0",
|
"@testing-library/jest-dom": "^5.17.0",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"ajv": "^8.17.1",
|
"ajv": "^8.17.1",
|
||||||
@ -23,8 +24,10 @@
|
|||||||
"react-chartjs-2": "^5.2.0",
|
"react-chartjs-2": "^5.2.0",
|
||||||
"react-circular-progressbar": "^2.1.0",
|
"react-circular-progressbar": "^2.1.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
|
"react-redux": "^9.2.0",
|
||||||
"react-router-dom": "^7.1.1",
|
"react-router-dom": "^7.1.1",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
|
"redux": "^5.0.1",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -3370,6 +3373,38 @@
|
|||||||
"url": "https://opencollective.com/popperjs"
|
"url": "https://opencollective.com/popperjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@reduxjs/toolkit": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-awNe2oTodsZ6LmRqmkFhtb/KH03hUhxOamEQy411m3Njj3BbFvoBovxo4Q1cBWnV1ErprVj9MlF0UPXkng0eyg==",
|
||||||
|
"dependencies": {
|
||||||
|
"immer": "^10.0.3",
|
||||||
|
"redux": "^5.0.1",
|
||||||
|
"redux-thunk": "^3.1.0",
|
||||||
|
"reselect": "^5.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
|
||||||
|
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-redux": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@reduxjs/toolkit/node_modules/immer": {
|
||||||
|
"version": "10.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
|
||||||
|
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/immer"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/plugin-babel": {
|
"node_modules/@rollup/plugin-babel": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
||||||
@ -4309,6 +4344,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="
|
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/use-sync-external-store": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
|
||||||
|
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="
|
||||||
|
},
|
||||||
"node_modules/@types/ws": {
|
"node_modules/@types/ws": {
|
||||||
"version": "8.5.13",
|
"version": "8.5.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz",
|
||||||
@ -13665,6 +13705,28 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
||||||
},
|
},
|
||||||
|
"node_modules/react-redux": {
|
||||||
|
"version": "9.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||||
|
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/use-sync-external-store": "^0.0.6",
|
||||||
|
"use-sync-external-store": "^1.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^18.2.25 || ^19",
|
||||||
|
"react": "^18.0 || ^19",
|
||||||
|
"redux": "^5.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"redux": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-refresh": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
|
||||||
@ -13861,6 +13923,19 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/redux": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="
|
||||||
|
},
|
||||||
|
"node_modules/redux-thunk": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"redux": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/reflect.getprototypeof": {
|
"node_modules/reflect.getprototypeof": {
|
||||||
"version": "1.0.9",
|
"version": "1.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.9.tgz",
|
||||||
@ -14017,6 +14092,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
|
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/reselect": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="
|
||||||
|
},
|
||||||
"node_modules/resolve": {
|
"node_modules/resolve": {
|
||||||
"version": "1.22.10",
|
"version": "1.22.10",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||||
@ -16070,6 +16150,14 @@
|
|||||||
"requires-port": "^1.0.0"
|
"requires-port": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-sync-external-store": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
"@emotion/styled": "^11.14.0",
|
"@emotion/styled": "^11.14.0",
|
||||||
"@mui/icons-material": "^6.2.1",
|
"@mui/icons-material": "^6.2.1",
|
||||||
"@mui/material": "^6.2.1",
|
"@mui/material": "^6.2.1",
|
||||||
|
"@reduxjs/toolkit": "^2.5.0",
|
||||||
"@testing-library/jest-dom": "^5.17.0",
|
"@testing-library/jest-dom": "^5.17.0",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"ajv": "^8.17.1",
|
"ajv": "^8.17.1",
|
||||||
@ -18,8 +19,10 @@
|
|||||||
"react-chartjs-2": "^5.2.0",
|
"react-chartjs-2": "^5.2.0",
|
||||||
"react-circular-progressbar": "^2.1.0",
|
"react-circular-progressbar": "^2.1.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
|
"react-redux": "^9.2.0",
|
||||||
"react-router-dom": "^7.1.1",
|
"react-router-dom": "^7.1.1",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
|
"redux": "^5.0.1",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -2,14 +2,14 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="%PUBLIC_URL%/logo.jpeg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="Web site created using create-react-app"
|
content="Web site created using create-react-app"
|
||||||
/>
|
/>
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo.jpeg" />
|
||||||
<!--
|
<!--
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
@ -24,7 +24,7 @@
|
|||||||
work correctly both with client-side routing and a non-root public URL.
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title>T-Pay Dashboard</title>
|
<title>T-Pay| Dashboard</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
|||||||
BIN
public/logo.jpeg
Normal file
BIN
public/logo.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@ -34,7 +34,9 @@ const Header = () => {
|
|||||||
function logoutBtn() {
|
function logoutBtn() {
|
||||||
Cookies.remove('token');
|
Cookies.remove('token');
|
||||||
Cookies.remove('role');
|
Cookies.remove('role');
|
||||||
|
Cookies.remove('userId');
|
||||||
Cookies.remove('username');
|
Cookies.remove('username');
|
||||||
|
localStorage.removeItem('roles_list');
|
||||||
navigate('/login')
|
navigate('/login')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +79,7 @@ const Header = () => {
|
|||||||
'aria-labelledby': 'basic-button',
|
'aria-labelledby': 'basic-button',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MenuItem>
|
<MenuItem onClick={() => navigate('/profile')}>
|
||||||
<Avatar sx={{ marginRight: 1 }} /> Profile
|
<Avatar sx={{ marginRight: 1 }} /> Profile
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem>
|
<MenuItem>
|
||||||
|
|||||||
@ -131,10 +131,6 @@ const Sidebar = () => {
|
|||||||
{
|
{
|
||||||
title: 'Setting',
|
title: 'Setting',
|
||||||
options: [
|
options: [
|
||||||
{
|
|
||||||
title: 'Setting',
|
|
||||||
path: '/setting'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: 'User Management',
|
title: 'User Management',
|
||||||
path: '/user-management'
|
path: '/user-management'
|
||||||
@ -142,7 +138,19 @@ const Sidebar = () => {
|
|||||||
{
|
{
|
||||||
title: 'Role Management',
|
title: 'Role Management',
|
||||||
path: '/role-management'
|
path: '/role-management'
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
title: 'Profile',
|
||||||
|
path: '/profile'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Setting',
|
||||||
|
path: '/setting'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Master Data',
|
||||||
|
path: '/master-data'
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@ -275,7 +283,9 @@ const Sidebar = () => {
|
|||||||
function logoutBtn() {
|
function logoutBtn() {
|
||||||
Cookies.remove('token');
|
Cookies.remove('token');
|
||||||
Cookies.remove('role');
|
Cookies.remove('role');
|
||||||
|
Cookies.remove('userId');
|
||||||
Cookies.remove('username');
|
Cookies.remove('username');
|
||||||
|
localStorage.removeItem('roles_list');
|
||||||
navigate('/login');
|
navigate('/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
46
src/components/WarningDialog.js
Normal file
46
src/components/WarningDialog.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// ErrorModal.js
|
||||||
|
import React from "react";
|
||||||
|
import { Modal, Box, Typography, Button } from "@mui/material";
|
||||||
|
|
||||||
|
const ErrorModal = ({ open, onClose, errorMessage }) => {
|
||||||
|
const style = {
|
||||||
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
width: 400,
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
border: "2px solid #000",
|
||||||
|
boxShadow: 24,
|
||||||
|
p: 4,
|
||||||
|
borderRadius: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={open}
|
||||||
|
onClose={onClose}
|
||||||
|
aria-labelledby="error-modal-title"
|
||||||
|
aria-describedby="error-modal-description"
|
||||||
|
>
|
||||||
|
<Box sx={style}>
|
||||||
|
<Typography id="error-modal-title" variant="h6" component="h2">
|
||||||
|
Error
|
||||||
|
</Typography>
|
||||||
|
<Typography id="error-modal-description" sx={{ mt: 2 }}>
|
||||||
|
{errorMessage}
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="error"
|
||||||
|
onClick={onClose}
|
||||||
|
sx={{ mt: 2 }}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ErrorModal;
|
||||||
@ -1,18 +1,26 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
|
||||||
|
const token = Cookies.get(`token`) || null;
|
||||||
|
const headers = {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"content-type":"application/json",
|
||||||
|
};
|
||||||
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
||||||
|
|
||||||
const instance = axios.create({
|
const instance = axios.create({
|
||||||
baseURL: 'https://api.telkomcel.tl/tpay-stg-dash-api/',
|
baseURL: 'https://api.telkomcel.tl/tpay-stg-dash-api/',
|
||||||
headers: {
|
headers: headers
|
||||||
"Access-Control-Allow-Origin": "*",
|
|
||||||
"content-type":"application/json",
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export async function fetchApi(path, method, data) {
|
export async function fetchApi(path, method, data, params) {
|
||||||
|
let query = params || {};
|
||||||
|
query.token = token
|
||||||
let response = await instance({
|
let response = await instance({
|
||||||
method: method,
|
method: method,
|
||||||
url: path,
|
url: path,
|
||||||
data: data
|
data: data,
|
||||||
|
params: query
|
||||||
});
|
});
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
14
src/index.js
14
src/index.js
@ -4,14 +4,18 @@ import "./index.css";
|
|||||||
import App from "./routes/routes";
|
import App from "./routes/routes";
|
||||||
import reportWebVitals from "./reportWebVitals";
|
import reportWebVitals from "./reportWebVitals";
|
||||||
import { BrowserRouter } from "react-router-dom";
|
import { BrowserRouter } from "react-router-dom";
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import store from './store/store';
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||||
root.render(
|
root.render(
|
||||||
<BrowserRouter>
|
<Provider store={store}>
|
||||||
<React.StrictMode>
|
<BrowserRouter>
|
||||||
<App />
|
<React.StrictMode>
|
||||||
</React.StrictMode>
|
<App />
|
||||||
</BrowserRouter>
|
</React.StrictMode>
|
||||||
|
</BrowserRouter>
|
||||||
|
</Provider>
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
// If you want to start measuring performance in your app, pass a function
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { TextField, Button, Box, Typography, Grid, Paper } from "@mui/material";
|
import { TextField, Button, Box, Typography, Grid, Paper, Avatar, Divider } from "@mui/material";
|
||||||
import {Link, useNavigate } from 'react-router-dom';
|
import {Link, useNavigate } from 'react-router-dom';
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
import { fetchApi } from '../config/axios';
|
import { fetchApi } from '../config/axios';
|
||||||
|
import logo from '../logo/logo.jpeg';
|
||||||
|
|
||||||
const LoginPage = () => {
|
const LoginPage = () => {
|
||||||
// State for email and password
|
// State for email and password
|
||||||
@ -18,17 +19,24 @@ const LoginPage = () => {
|
|||||||
// GET CREDIENTIAL
|
// GET CREDIENTIAL
|
||||||
let user = await fetchApi('/api/login', 'POST', { username, password });
|
let user = await fetchApi('/api/login', 'POST', { username, password });
|
||||||
if (user.data && user.data.data) {
|
if (user.data && user.data.data) {
|
||||||
let userLogin = user.data.data;
|
let resUserLogin = user.data.data;
|
||||||
Cookies.set('token', userLogin.token.access_token, { expires: 7, path: '/' });
|
console.log('resUserLogin', resUserLogin);
|
||||||
Cookies.set('role', userLogin.role_name);
|
let roleList = JSON.stringify(resUserLogin.roles_list);
|
||||||
Cookies.set('username', userLogin.user.username);
|
Cookies.set('token', resUserLogin.token.access_token, { expires: 1, path: '/' });
|
||||||
// localStorage.setItem('token', userLogin.token.access_token)
|
Cookies.set('role', resUserLogin.role_name, { expires: 1, path: '/' });
|
||||||
|
Cookies.set('userId', resUserLogin.user.id, { expires: 1, path: '/' });
|
||||||
|
Cookies.set('username', resUserLogin.user.username, { expires: 1, path: '/' });
|
||||||
|
// Cookies.set('roles_list', roleList, { expires: 1, path: '/' });
|
||||||
|
localStorage.setItem('roles_list', roleList)
|
||||||
|
navigate('/');
|
||||||
|
} else {
|
||||||
|
alert(user.response.data.message);
|
||||||
}
|
}
|
||||||
navigate('/');
|
|
||||||
} else {
|
} else {
|
||||||
alert("Please fill in both fields.");
|
alert("Please fill in both fields.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
alert(error.response.data.message);
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -84,6 +92,39 @@ const LoginPage = () => {
|
|||||||
</form>
|
</form>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
position: "fixed",
|
||||||
|
bottom: 0,
|
||||||
|
textAlign: "center",
|
||||||
|
bgcolor: "#f9f9f9", // Background color
|
||||||
|
py: 2, // Padding
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Divider (Horizontal Line) */}
|
||||||
|
<Divider sx={{ mb: 2 }} />
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<Box justifyItems={'center'}
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center", // Align items vertically
|
||||||
|
justifyContent: "center", // Center content horizontally
|
||||||
|
gap: 1, // Space between logo and text
|
||||||
|
mt: 2, // Optional margin-top
|
||||||
|
}}>
|
||||||
|
<Avatar
|
||||||
|
src={logo}
|
||||||
|
alt="T-PAY"
|
||||||
|
sx={{ backgroundColor: 'red', width: '50px', height: '50px' }}
|
||||||
|
/>
|
||||||
|
<Typography variant="h6" sx={{ fontWeight: "bold" }}>T-PAY</Typography>
|
||||||
|
</Box>
|
||||||
|
<Typography variant="body2" sx={{ color: "gray" }}>
|
||||||
|
© 2025 TelinDigital Solution
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
133
src/pages/MasterData.js
Normal file
133
src/pages/MasterData.js
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { Grid, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, Box, Typography } from '@mui/material';
|
||||||
|
import ReusableTable from '../components/Table';
|
||||||
|
|
||||||
|
const MasterDataPage = () => {
|
||||||
|
const tableData1 = [
|
||||||
|
{ id: 1, name: 'John Doe', age: 25 },
|
||||||
|
{ id: 2, name: 'Jane Smith', age: 30 },
|
||||||
|
{ id: 3, name: 'John Doe', age: 25 },
|
||||||
|
{ id: 4, name: 'Jane Smith', age: 30 },
|
||||||
|
{ id: 5, name: 'John Doe', age: 25 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const tableData2 = [
|
||||||
|
{ id: 1, product: 'Laptop', price: '$1000' },
|
||||||
|
{ id: 2, product: 'Phone', price: '$600' },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box p={3}>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'flex-start', pr: 2, }}>
|
||||||
|
<Typography sx={{ color: 'gray', fontSize: '30px' }}>Master Data</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box p={3}>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
{/* First Table */}
|
||||||
|
|
||||||
|
{/* <ReusableTable columns={columns} data={users} /> */}
|
||||||
|
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography sx={{ color: 'gray', fontSize: '30px' }}>Kecamatan</Typography>
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>ID</TableCell>
|
||||||
|
<TableCell>Name</TableCell>
|
||||||
|
<TableCell>Age</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{tableData1.map((row) => (
|
||||||
|
<TableRow key={row.id}>
|
||||||
|
<TableCell>{row.id}</TableCell>
|
||||||
|
<TableCell>{row.name}</TableCell>
|
||||||
|
<TableCell>{row.age}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Second Table */}
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography sx={{ color: 'gray', fontSize: '30px' }}>Kelurahan</Typography>
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>ID</TableCell>
|
||||||
|
<TableCell>Product</TableCell>
|
||||||
|
<TableCell>Price</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{tableData2.map((row) => (
|
||||||
|
<TableRow key={row.id}>
|
||||||
|
<TableCell>{row.id}</TableCell>
|
||||||
|
<TableCell>{row.product}</TableCell>
|
||||||
|
<TableCell>{row.price}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</Grid>
|
||||||
|
{/* 3rd Table */}
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography sx={{ color: 'gray', fontSize: '30px' }}>Kabupaten</Typography>
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>ID</TableCell>
|
||||||
|
<TableCell>Name</TableCell>
|
||||||
|
<TableCell>Age</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{tableData1.map((row) => (
|
||||||
|
<TableRow key={row.id}>
|
||||||
|
<TableCell>{row.id}</TableCell>
|
||||||
|
<TableCell>{row.name}</TableCell>
|
||||||
|
<TableCell>{row.age}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* 4th Table */}
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography sx={{ color: 'gray', fontSize: '30px' }}>Kota</Typography>
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>ID</TableCell>
|
||||||
|
<TableCell>Product</TableCell>
|
||||||
|
<TableCell>Price</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{tableData2.map((row) => (
|
||||||
|
<TableRow key={row.id}>
|
||||||
|
<TableCell>{row.id}</TableCell>
|
||||||
|
<TableCell>{row.product}</TableCell>
|
||||||
|
<TableCell>{row.price}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MasterDataPage;
|
||||||
362
src/pages/Profile.js
Normal file
362
src/pages/Profile.js
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import {
|
||||||
|
TextField,
|
||||||
|
Button,
|
||||||
|
Box,
|
||||||
|
Typography,
|
||||||
|
Paper,
|
||||||
|
FormControl,
|
||||||
|
Select,
|
||||||
|
InputLabel,
|
||||||
|
MenuItem,
|
||||||
|
FormControlLabel,
|
||||||
|
Checkbox,
|
||||||
|
FormLabel
|
||||||
|
} from "@mui/material";
|
||||||
|
import { fetchApi } from '../config/axios';
|
||||||
|
import WarningDialog from '../components/WarningDialog';
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
|
||||||
|
const ProfilePage = () => {
|
||||||
|
// State for form fields
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
username: '',
|
||||||
|
name: '',
|
||||||
|
email: '',
|
||||||
|
status: '',
|
||||||
|
idRole: '',
|
||||||
|
password: '',
|
||||||
|
oldPassword: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
newPassword: ''
|
||||||
|
// nik: '',
|
||||||
|
// position: '',
|
||||||
|
// telp: '',
|
||||||
|
// address: '',
|
||||||
|
// organisasi: '',
|
||||||
|
// devisi: '',
|
||||||
|
// unit: '',
|
||||||
|
});
|
||||||
|
const [roles, setRoles] = useState([]);
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false); // ERROR MSG
|
||||||
|
const [error, setError] = useState(""); // ERROR MSG
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchUser()
|
||||||
|
fetchUserRoles()
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
async function fetchUser(params) {
|
||||||
|
try {
|
||||||
|
let userId = Cookies.get(`userId`);
|
||||||
|
let token = Cookies.get(`token`);
|
||||||
|
|
||||||
|
let fetchUsers = await fetchApi(`/api/user/detail/${userId}`, 'GET', null, { token: token });
|
||||||
|
// setFormData({})
|
||||||
|
console.log(fetchUsers);
|
||||||
|
setFormData(fetchUsers.data.data)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
handleError(`${error.message}. \n ${error.response.data.error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchUserRoles(params) {
|
||||||
|
try {
|
||||||
|
let userRoles = await fetchApi('/api/user_role/list', 'GET', null, {
|
||||||
|
limit: 10,
|
||||||
|
page: 1,
|
||||||
|
with_deleted: true,
|
||||||
|
order_field: 'id',
|
||||||
|
order_direction: 'ASC'
|
||||||
|
});
|
||||||
|
console.log(userRoles);
|
||||||
|
|
||||||
|
if (userRoles.data && userRoles.data.data) setRoles(userRoles.data.data.list)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
handleError(`${error.message}. \n ${error.response.data.error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle form submission
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
// if (name && email) {
|
||||||
|
// // Handle registration logic, like calling an API to create the user
|
||||||
|
// console.log("Registered with:", { name, email });
|
||||||
|
// } else {
|
||||||
|
// alert("Please fill in all fields.");
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
let { name, value } = e.target;
|
||||||
|
if (name === 'status' && value === "Y") value = "N"
|
||||||
|
else if (name === 'status' && value === "N") value = "Y"
|
||||||
|
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleError = (msg) => { // ERROR MSG
|
||||||
|
setError(msg);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeModal = () => { // ERROR MSG
|
||||||
|
setIsModalOpen(false);
|
||||||
|
setError("");
|
||||||
|
};
|
||||||
|
|
||||||
|
async function updatePassword() {
|
||||||
|
try {
|
||||||
|
let updatePassword = await fetchApi(`/api/user/update_password`, 'PUT', {
|
||||||
|
password: formData.password,
|
||||||
|
retype_password: formData.confirmPassword
|
||||||
|
});
|
||||||
|
console.log(updatePassword);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ padding: 3 }}>
|
||||||
|
<WarningDialog // ERROR MSG
|
||||||
|
open={isModalOpen}
|
||||||
|
onClose={closeModal}
|
||||||
|
errorMessage={error}
|
||||||
|
/>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'flex-start', pr: 2, }}>
|
||||||
|
<Typography sx={{ color: 'gray', fontSize: '30px' }}>User Profile</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ padding: 3 }}>
|
||||||
|
<Box sx={{ padding: 3 }}>
|
||||||
|
<Paper sx={{ width: '100%', overflow: 'hidden' }}>
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<Box mb={2} m={2}>
|
||||||
|
<TextField
|
||||||
|
label="Username"
|
||||||
|
name="username"
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
value={formData.username}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box mb={2} m={2}>
|
||||||
|
<TextField
|
||||||
|
label="Name"
|
||||||
|
name="name"
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
value={formData.name}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box mb={2} m={2}>
|
||||||
|
<TextField
|
||||||
|
label="Email"
|
||||||
|
name="email"
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
value={formData.email}
|
||||||
|
onChange={handleChange}
|
||||||
|
type="email"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box mb={2} m={2} sx={{ display: 'flex', justifyContent: 'flex-start' }}>
|
||||||
|
<FormLabel component="legend">Status :
|
||||||
|
<FormControlLabel required label="Aktif" control={
|
||||||
|
<Checkbox value={formData.status} name="status" onClick={handleChange}
|
||||||
|
checked={ formData.status === "Y" ? true : false } />
|
||||||
|
}/>
|
||||||
|
</FormLabel>
|
||||||
|
</Box>
|
||||||
|
{/* <Box mb={2} m={2}>
|
||||||
|
<TextField
|
||||||
|
label="NIK"
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
value={formData.nik}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box mb={2} m={2}>
|
||||||
|
<TextField
|
||||||
|
label="Position"
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
value={formData.position}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box mb={2} m={2}>
|
||||||
|
<TextField
|
||||||
|
label="Telp"
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
value={formData.telp}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box mb={2} m={2}>
|
||||||
|
<TextField
|
||||||
|
label="Address"
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
value={formData.address}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box mb={2} m={2}>
|
||||||
|
<FormControl fullWidth required>
|
||||||
|
<InputLabel id="organization-label">Organization</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="organization-label"
|
||||||
|
id="organization"
|
||||||
|
value={formData.organisasi}
|
||||||
|
label="Organization"
|
||||||
|
onChange={handleChange}
|
||||||
|
name='organisasi'
|
||||||
|
>
|
||||||
|
<MenuItem value={'Koperasi'}>Koperasi</MenuItem>
|
||||||
|
<MenuItem value={'Damkar'}>Damkar</MenuItem>
|
||||||
|
<MenuItem value={'Kemasyarakatan'}>Kemasyarakatan</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Box>
|
||||||
|
<Box mb={2} m={2}>
|
||||||
|
<FormControl fullWidth required>
|
||||||
|
<InputLabel id="organization-label">Devision</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="organization-label"
|
||||||
|
id="organization"
|
||||||
|
value={formData.devisi}
|
||||||
|
label="Devision"
|
||||||
|
onChange={handleChange}
|
||||||
|
name='devisi'
|
||||||
|
>
|
||||||
|
<MenuItem value={`Staff`}>Staff</MenuItem>
|
||||||
|
<MenuItem value={`Lead`}>Lead</MenuItem>
|
||||||
|
<MenuItem value={`Manager`}>Manager</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Box>
|
||||||
|
<Box mb={2} m={2}>
|
||||||
|
<FormControl fullWidth required>
|
||||||
|
<InputLabel id="organization-label">Unit</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="organization-label"
|
||||||
|
id="organization"
|
||||||
|
value={formData.unit}
|
||||||
|
label="Unit"
|
||||||
|
onChange={handleChange}
|
||||||
|
name='unit'
|
||||||
|
>
|
||||||
|
<MenuItem value={1}>Satu</MenuItem>
|
||||||
|
<MenuItem value={2}>Dua</MenuItem>
|
||||||
|
<MenuItem value={3}>Tiga</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Box> */}
|
||||||
|
<Box mb={2} m={2}>
|
||||||
|
<FormControl fullWidth required>
|
||||||
|
<InputLabel id="organization-label">Role</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="organization-label"
|
||||||
|
id="organization"
|
||||||
|
value={formData.idRole}
|
||||||
|
label="Role"
|
||||||
|
onChange={handleChange}
|
||||||
|
name='idRole'
|
||||||
|
>
|
||||||
|
{
|
||||||
|
roles.map((el, idx) => (
|
||||||
|
<MenuItem key={idx} value={el.id}>{el.name}</MenuItem>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Box>
|
||||||
|
<Box mb={2} m={2}>
|
||||||
|
<TextField
|
||||||
|
label="Password"
|
||||||
|
name="password"
|
||||||
|
value={formData.password}
|
||||||
|
onChange={handleChange}
|
||||||
|
fullWidth
|
||||||
|
type="password"
|
||||||
|
required
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box mt={2} mb={2} textAlign="center">
|
||||||
|
<Button style={{ marginRight: '5px' }} variant="contained">Cancel</Button>
|
||||||
|
<Button variant="contained" color="success">Submit</Button>
|
||||||
|
</Box>
|
||||||
|
</form>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box sx={{ padding: 3 }}>
|
||||||
|
<Box sx={{ padding: 3 }}>
|
||||||
|
<Typography sx={{ color: 'gray', fontSize: '30px' }}>User Password</Typography>
|
||||||
|
<Paper sx={{ width: '100%', overflow: 'hidden' }}>
|
||||||
|
<Box mb={2} m={2}>
|
||||||
|
<TextField
|
||||||
|
label="Old Password"
|
||||||
|
name="oldPassword"
|
||||||
|
value={formData.oldPassword}
|
||||||
|
onChange={handleChange}
|
||||||
|
fullWidth
|
||||||
|
type="password"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box mb={2} m={2}>
|
||||||
|
<TextField
|
||||||
|
label="New Password"
|
||||||
|
name="newPassword"
|
||||||
|
value={formData.newPassword}
|
||||||
|
onChange={handleChange}
|
||||||
|
fullWidth
|
||||||
|
type="password"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box mb={2} m={2}>
|
||||||
|
<TextField
|
||||||
|
label="Confirm Password"
|
||||||
|
name="confirmPassword"
|
||||||
|
value={formData.confirmPassword}
|
||||||
|
onChange={handleChange}
|
||||||
|
fullWidth
|
||||||
|
type="password"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box mt={2} mb={2} textAlign="center">
|
||||||
|
<Button style={{ marginRight: '5px' }} variant="contained">Cancel</Button>
|
||||||
|
<Button variant="contained" onClick={() => updatePassword} color="success">Update</Button>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProfilePage;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
Typography,
|
Typography,
|
||||||
@ -20,58 +20,127 @@ import {
|
|||||||
Box
|
Box
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
|
import { fetchApi } from '../config/axios';
|
||||||
|
|
||||||
// Initial list of roles and menus
|
// Initial list of roles and menus
|
||||||
const initialRoles = ['Admin', 'Escrow', 'Agent'];
|
// const initialRoles = ['Admin', 'Escrow', 'Agent'];
|
||||||
const menus = [
|
// const menus = [
|
||||||
'Dashboard',
|
// 'Dashboard',
|
||||||
'Users',
|
// 'Users',
|
||||||
'Reports',
|
// 'Reports',
|
||||||
'Settings',
|
// 'Settings',
|
||||||
'Notifications',
|
// 'Notifications',
|
||||||
'Profile',
|
// 'Profile',
|
||||||
'Help',
|
// 'Help',
|
||||||
'Logs',
|
// 'Logs',
|
||||||
'Analytics',
|
// 'Analytics',
|
||||||
'Billing',
|
// 'Billing',
|
||||||
];
|
// ];
|
||||||
|
|
||||||
const RoleMenuTable = () => {
|
const RoleMenuTable = () => {
|
||||||
const [roles, setRoles] = useState(initialRoles);
|
const [roles, setRoles] = useState([]);
|
||||||
const [roleMenus, setRoleMenus] = useState(
|
// const [roleMenus, setRoleMenus] = useState([]);
|
||||||
initialRoles.reduce((acc, role) => ({ ...acc, [role]: [] }), {})
|
const [menus, setMenus] = useState([]);
|
||||||
);
|
|
||||||
const [openDialog, setOpenDialog] = useState(false);
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
const [newRole, setNewRole] = useState('');
|
const [newRole, setNewRole] = useState('');
|
||||||
const [confirmDialog, setConfirmDialog] = useState({ open: false, role: '', menu: '' });
|
const [confirmDialog, setConfirmDialog] = useState({
|
||||||
|
open: false, roleName: '', menuData: [], roleId: '', menuId: '', status: null, menuName: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchData()
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleCheckboxChange = (role, menu) => {
|
async function fetchData(params) {
|
||||||
setConfirmDialog({ open: true, role, menu });
|
try {
|
||||||
};
|
let userRole = await fetchApi('/api/user_role/list', 'GET', null, {
|
||||||
|
limit: 5,
|
||||||
const handleConfirm = (confirm) => {
|
page: 1,
|
||||||
const { role, menu } = confirmDialog;
|
with_deleted: true,
|
||||||
if (confirm) {
|
order_field: 'id',
|
||||||
setRoleMenus((prev) => {
|
order_direction: 'ASC'
|
||||||
const isChecked = prev[role].includes(menu);
|
|
||||||
const updatedMenus = isChecked
|
|
||||||
? prev[role].filter((m) => m !== menu)
|
|
||||||
: [...prev[role], menu];
|
|
||||||
return { ...prev, [role]: updatedMenus };
|
|
||||||
});
|
});
|
||||||
|
let userMenu = await fetchApi('/api/menus/list', 'GET', null, {
|
||||||
|
limit: 50,
|
||||||
|
page: 1,
|
||||||
|
with_deleted: true,
|
||||||
|
order_field: 'id',
|
||||||
|
order_direction: 'ASC'
|
||||||
|
});
|
||||||
|
setMenus(userMenu.data.data.list)
|
||||||
|
|
||||||
|
console.log('role',userRole);
|
||||||
|
console.log('menu',userMenu);
|
||||||
|
if (userRole.data && userRole.data.data) setRoles(userRole.data.data.list)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
}
|
}
|
||||||
setConfirmDialog({ open: false, role: '', menu: '' });
|
}
|
||||||
|
|
||||||
|
const handleCheckboxChange = (roleName, menuData, roleId, menuId, status, menuName) => {
|
||||||
|
setConfirmDialog({ open: true, roleName, menuData, roleId, menuId, status, menuName });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddRole = () => {
|
const handleConfirm = async (confirm) => {
|
||||||
if (newRole.trim() && !roles.includes(newRole)) {
|
const { roleName, menuData, roleId, menuId, status } = confirmDialog;
|
||||||
setRoles((prevRoles) => [...prevRoles, newRole]);
|
try {
|
||||||
setRoleMenus((prevMenus) => ({ ...prevMenus, [newRole]: [] }));
|
if (confirm) {
|
||||||
|
console.log(confirm,confirmDialog);
|
||||||
|
if (status) {
|
||||||
|
let generateRoles = menuData.filter(el => +el !== +menuId);
|
||||||
|
console.log(generateRoles);
|
||||||
|
let userMenu = await fetchApi(`/api/user_role/update/${roleId}`, 'PUT', {
|
||||||
|
name: roleName,
|
||||||
|
roles: generateRoles,
|
||||||
|
status: "Y"
|
||||||
|
})
|
||||||
|
console.log(userMenu);
|
||||||
|
await fetchData()
|
||||||
|
} else {
|
||||||
|
let generateRoles = menuData.filter(el => +el !== +menuId);
|
||||||
|
generateRoles.push(`${menuId}`)
|
||||||
|
console.log(generateRoles);
|
||||||
|
let userMenu = await fetchApi(`/api/user_role/update/${roleId}`, 'PUT', {
|
||||||
|
name: roleName,
|
||||||
|
roles: generateRoles,
|
||||||
|
status: "Y"
|
||||||
|
})
|
||||||
|
console.log(userMenu);
|
||||||
|
await fetchData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setConfirmDialog({ open: false, role: '', menu: '' });
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddRole = async () => {
|
||||||
|
try {
|
||||||
|
await fetchApi('/api/user_role/create', 'POST', {
|
||||||
|
"name": newRole,
|
||||||
|
"roles": [],
|
||||||
|
"status": "Y"
|
||||||
|
});
|
||||||
|
await fetchData();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
}
|
}
|
||||||
setNewRole('');
|
setNewRole('');
|
||||||
setOpenDialog(false);
|
setOpenDialog(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteRole = async (role) => {
|
||||||
|
try {
|
||||||
|
let resDeleteRole = await fetchApi(`/api/user_role/delete/${role.id}/true`, 'DELETE');
|
||||||
|
console.log(resDeleteRole);
|
||||||
|
await fetchData();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Typography variant="h4" align="center" gutterBottom marginTop={5}>
|
<Typography variant="h4" align="center" gutterBottom marginTop={5}>
|
||||||
@ -85,9 +154,9 @@ const RoleMenuTable = () => {
|
|||||||
<AddIcon />
|
<AddIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center', pr: 2, }}>
|
<Box sx={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center', pr: 2, }}>
|
||||||
<Button variant="contained" color="primary" onClick={() => setOpenDialog(true)}>Create Role</Button>
|
<Button variant="contained" color="primary" onClick={() => setOpenDialog(true)}>Create Role</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Table for roles and menus */}
|
{/* Table for roles and menus */}
|
||||||
<TableContainer component={Paper} style={{ marginTop: '20px' }}>
|
<TableContainer component={Paper} style={{ marginTop: '20px' }}>
|
||||||
@ -95,26 +164,28 @@ const RoleMenuTable = () => {
|
|||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell><strong>Role</strong></TableCell>
|
<TableCell><strong>Role</strong></TableCell>
|
||||||
{menus.map((menu) => (
|
{menus.map((menu, idx) => (
|
||||||
<TableCell key={menu} align="center"><strong>{menu}</strong></TableCell>
|
<TableCell key={idx} align="center"><strong>{menu.name}</strong></TableCell>
|
||||||
))}
|
))}
|
||||||
<TableCell><strong>Action</strong></TableCell>
|
<TableCell><strong>Action</strong></TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{roles.map((role) => (
|
{roles.map((role) => (
|
||||||
<TableRow key={role}>
|
<TableRow key={role.id}>
|
||||||
<TableCell>{role}</TableCell>
|
<TableCell>{role.name}</TableCell>
|
||||||
{menus.map((menu) => (
|
{menus.map((menu, idx) => (
|
||||||
<TableCell key={menu} align="center">
|
<TableCell key={idx} align="center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={roleMenus[role].includes(menu)}
|
checked={(role.roles.find(el => +el === menu.id)) ? true : false}
|
||||||
onChange={() => handleCheckboxChange(role, menu)}
|
onChange={() => handleCheckboxChange(
|
||||||
|
role.name, role.roles, role.id, menu.id, (role.roles.find(el => +el === menu.id)) ? true : false, menu.name
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
))}
|
||||||
<TableCell align="center">
|
<TableCell align="center">
|
||||||
<Button>Delete</Button>
|
<Button onClick={() => deleteRole(role)}>Delete</Button>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
@ -150,8 +221,8 @@ const RoleMenuTable = () => {
|
|||||||
<Dialog open={confirmDialog.open} onClose={() => handleConfirm(false)}>
|
<Dialog open={confirmDialog.open} onClose={() => handleConfirm(false)}>
|
||||||
<DialogTitle>Confirm Menu Selection</DialogTitle>
|
<DialogTitle>Confirm Menu Selection</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
Are you sure you want to {roleMenus[confirmDialog.role]?.includes(confirmDialog.menu) ? 'remove' : 'add'}
|
Are you sure you want to <strong>{confirmDialog.status ? 'remove ' : 'add '}</strong>
|
||||||
the menu <strong>{confirmDialog.menu}</strong> for role <strong>{confirmDialog.role}</strong>?
|
the menu <strong>{confirmDialog.menuName}</strong> for role <strong>{confirmDialog.roleName}</strong>?
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={() => handleConfirm(false)} color="secondary">
|
<Button onClick={() => handleConfirm(false)} color="secondary">
|
||||||
|
|||||||
@ -25,7 +25,11 @@ const SettingPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ padding: 3 }}>
|
<Box sx={{ padding: 3 }}>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'flex-start', pr: 2, }}>
|
||||||
|
<Typography sx={{ color: 'gray', fontSize: '30px' }}>Setting</Typography>
|
||||||
|
</Box>
|
||||||
<Typography sx={{ color: 'gray', size: 'xl' }}>Manage Webservice</Typography>
|
<Typography sx={{ color: 'gray', size: 'xl' }}>Manage Webservice</Typography>
|
||||||
|
|
||||||
<Box sx={{ padding: 3 }}>
|
<Box sx={{ padding: 3 }}>
|
||||||
<Box sx={{ padding: 3 }}>
|
<Box sx={{ padding: 3 }}>
|
||||||
<Paper sx={{ width: '100%', overflow: 'hidden' }}>
|
<Paper sx={{ width: '100%', overflow: 'hidden' }}>
|
||||||
|
|||||||
@ -1,65 +1,111 @@
|
|||||||
// Import React and Material-UI components
|
// Import React and Material-UI components
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
Paper, Button, Dialog, DialogActions, DialogContent, DialogTitle, TableContainer,
|
Paper, Button, Dialog, DialogActions, DialogContent, DialogTitle, TableContainer, FormLabel,
|
||||||
Checkbox, FormControlLabel, Box, Typography, TextField, Grid
|
Checkbox, FormControlLabel, Box, Typography, TextField, Grid, MenuItem, InputLabel, Select, FormControl
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import ReusableTable from '../components/Table';
|
import ReusableTable from '../components/Table';
|
||||||
|
import { fetchApi } from '../config/axios';
|
||||||
|
import WarningDialog from '../components/WarningDialog'; // ERROR MSG
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
|
{ id: 'username', label: 'Username' },
|
||||||
{ id: 'name', label: 'Name' },
|
{ id: 'name', label: 'Name' },
|
||||||
{ id: 'email', label: 'Email' },
|
{ id: 'email', label: 'Email' },
|
||||||
{ id: 'roles', label: 'Roles' },
|
{ id: 'role_name', label: 'Roles' },
|
||||||
|
{ id: 'status', label: 'Status' },
|
||||||
|
{ id: 'created_at', label: 'Created' },
|
||||||
{ id: 'actions', label: 'Actions' },
|
{ id: 'actions', label: 'Actions' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const initialUsers = [
|
const initiateForm = {
|
||||||
{ id: 1, name: 'John Doe', email: 'john@example.com', roles: ['User'] },
|
username: '',
|
||||||
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', roles: ['Admin'] },
|
name: '',
|
||||||
{ id: 3, name: 'Alice Johnson', email: 'alice@example.com', roles: [] }
|
email: '',
|
||||||
];
|
status: '',
|
||||||
|
id_role: '',
|
||||||
const availableRoles = ['User', 'Admin', 'Editor'];
|
password: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
// nik: '',
|
||||||
|
// position: '',
|
||||||
|
// telp: '',
|
||||||
|
// address: '',
|
||||||
|
// organisasi: '',
|
||||||
|
// devisi: '',
|
||||||
|
// unit: '',
|
||||||
|
}
|
||||||
|
|
||||||
export default function UserManagement() {
|
export default function UserManagement() {
|
||||||
const [users, setUsers] = useState(initialUsers);
|
const [users, setUsers] = useState([]);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [selectedUser, setSelectedUser] = useState(null);
|
const [selectedUser, setSelectedUser] = useState(null);
|
||||||
const [selectedRoles, setSelectedRoles] = useState([]);
|
const [selectedRoles, setSelectedRoles] = useState([]);
|
||||||
|
const [roles, setRoles] = useState([]);
|
||||||
const [openDialog, setOpenDialog] = useState(false);
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
const [formData, setFormData] = useState({
|
const [labelDialog, setLabelDialog] = useState('');
|
||||||
name: '',
|
const [formData, setFormData] = useState(initiateForm);
|
||||||
email: '',
|
const [errors, setErrors] = useState({ passwordMatch: false, });
|
||||||
telp: '',
|
const [isModalOpen, setIsModalOpen] = useState(false); // ERROR MSG
|
||||||
address: '',
|
const [error, setError] = useState(""); // ERROR MSG
|
||||||
password: '',
|
|
||||||
confirmPassword: '',
|
|
||||||
});
|
|
||||||
const [errors, setErrors] = useState({
|
|
||||||
passwordMatch: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
fetchUserRoles()
|
||||||
fetchUser()
|
fetchUser()
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
async function fetchUser(params) {
|
async function fetchUser(params) {
|
||||||
let temp = users.map(el => {
|
try {
|
||||||
el.actions = [
|
let fetchUsers = await fetchApi('/api/user/list', 'GET', null, {
|
||||||
{ label: 'Edit Role', color: 'primary', onClick: (row) => handleOpen(row) },
|
limit: 5,
|
||||||
{ label: 'Delete', color: 'secondary', onClick: (row) => handleDelete(row) },
|
page: 1,
|
||||||
{ label: 'View', color: 'info', onClick: (row) => alert(`Viewing ${row.name}`) },
|
with_deleted: true,
|
||||||
]
|
order_field: 'id',
|
||||||
return el
|
order_direction: 'ASC'
|
||||||
})
|
});
|
||||||
setUsers(temp)
|
console.log(fetchUsers);
|
||||||
|
if (fetchUsers.data && fetchUsers.data.data) {
|
||||||
|
let resFetchUsers = fetchUsers.data.data.list
|
||||||
|
let temp = resFetchUsers.map(el => {
|
||||||
|
if (el.status === 'Y') el.status = 'Aktif'
|
||||||
|
else el.status ='Tidak Aktif'
|
||||||
|
el.actions = [
|
||||||
|
{ label: 'Edit', color: 'info', onClick: (row) => handleOpen(row) },
|
||||||
|
{ label: 'Delete', color: 'secondary', onClick: (row) => handleDelete(row) },
|
||||||
|
]
|
||||||
|
return el
|
||||||
|
})
|
||||||
|
setUsers(temp)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
handleError(`${error.message}. \n ${error.response.data.error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchUserRoles(params) {
|
||||||
|
try {
|
||||||
|
let userRoles = await fetchApi('/api/user_role/list', 'GET', null, {
|
||||||
|
limit: 5,
|
||||||
|
page: 1,
|
||||||
|
with_deleted: true,
|
||||||
|
order_field: 'id',
|
||||||
|
order_direction: 'ASC'
|
||||||
|
});
|
||||||
|
if (userRoles.data && userRoles.data.data) setRoles(userRoles.data.data.list)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
handleError(`${error.message}. \n ${error.response.data.error}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOpen = (user) => {
|
const handleOpen = (user) => {
|
||||||
setSelectedUser(user);
|
setLabelDialog(`Edit User`)
|
||||||
setSelectedRoles(user.roles);
|
setFormData(user)
|
||||||
setOpen(true);
|
setOpenDialog(true)
|
||||||
|
// setSelectedUser(user);
|
||||||
|
// setSelectedRoles(user.role_name);
|
||||||
|
// setOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
@ -103,29 +149,66 @@ export default function UserManagement() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = async () => {
|
||||||
if (!errors.passwordMatch) {
|
try {
|
||||||
console.log('Form Data:', formData);
|
if (!errors.passwordMatch) {
|
||||||
setOpen(false);
|
console.log('Form Data:', formData);
|
||||||
setFormData({
|
// setOpen(false);
|
||||||
name: '',
|
// setFormData(initiateForm);
|
||||||
email: '',
|
}
|
||||||
telp: '',
|
console.log(formData);
|
||||||
address: '',
|
formData.status = "Y";
|
||||||
password: '',
|
if (labelDialog === `Create User`) {
|
||||||
confirmPassword: '',
|
delete formData.confirmPassword
|
||||||
});
|
let createUser = await fetchApi('/api/user/create', 'POST', formData);
|
||||||
|
console.log(createUser);
|
||||||
|
} else if (labelDialog === `Edit User`) {
|
||||||
|
delete formData.created_at
|
||||||
|
delete formData.updated_at
|
||||||
|
delete formData.role_name
|
||||||
|
delete formData.actions
|
||||||
|
delete formData.confirmPassword
|
||||||
|
console.log(formData);
|
||||||
|
let editUser = await fetchApi(`/api/user/update/${formData.id}`, 'PUT', formData);
|
||||||
|
console.log('editUser', editUser);
|
||||||
|
}
|
||||||
|
await fetchUser()
|
||||||
|
setOpenDialog(false)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
handleError(`${error.message}. \n ${error.response.data.error}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createNewUserBtn = () => {
|
||||||
|
setLabelDialog(`Create User`);
|
||||||
|
setFormData(initiateForm);
|
||||||
|
setOpenDialog(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleError = (msg) => { // ERROR MSG
|
||||||
|
setError(msg);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeModal = () => { // ERROR MSG
|
||||||
|
setIsModalOpen(false);
|
||||||
|
setError("");
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ padding: 3 }}>
|
<Box sx={{ padding: 3 }}>
|
||||||
|
<WarningDialog // ERROR MSG
|
||||||
|
open={isModalOpen}
|
||||||
|
onClose={closeModal}
|
||||||
|
errorMessage={error}
|
||||||
|
/>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'flex-start', pr: 2, }}>
|
<Box sx={{ display: 'flex', justifyContent: 'flex-start', pr: 2, }}>
|
||||||
<Typography sx={{ color: 'gray', fontSize: '30px' }}>User Management</Typography>
|
<Typography sx={{ color: 'gray', fontSize: '30px' }}>User Management</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box p={3}>
|
<Box p={3}>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center', pr: 2, }}>
|
<Box sx={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center', pr: 2, }}>
|
||||||
<Button variant="contained" color="primary" onClick={() => setOpenDialog(true)}>Create New User</Button>
|
<Button variant="contained" color="primary" onClick={() => createNewUserBtn()}>Create New User</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<TableContainer component={Paper} sx={{ marginTop: 3 }}>
|
<TableContainer component={Paper} sx={{ marginTop: 3 }}>
|
||||||
<ReusableTable columns={columns} data={users} />
|
<ReusableTable columns={columns} data={users} />
|
||||||
@ -133,16 +216,16 @@ export default function UserManagement() {
|
|||||||
<Dialog open={open} onClose={handleClose}>
|
<Dialog open={open} onClose={handleClose}>
|
||||||
<DialogTitle>Add Roles to {selectedUser?.name}</DialogTitle>
|
<DialogTitle>Add Roles to {selectedUser?.name}</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
{availableRoles.map((role) => (
|
{roles.map((role) => (
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
key={role}
|
key={role.id}
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={selectedRoles.includes(role)}
|
checked={selectedRoles.includes(role.name)}
|
||||||
onChange={() => handleRoleChange(role)}
|
onChange={() => handleRoleChange(role.name)}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label={role}
|
label={role.name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
@ -158,9 +241,20 @@ export default function UserManagement() {
|
|||||||
</TableContainer>
|
</TableContainer>
|
||||||
|
|
||||||
<Dialog open={openDialog} onClose={() => setOpenDialog(false)} maxWidth="sm" fullWidth>
|
<Dialog open={openDialog} onClose={() => setOpenDialog(false)} maxWidth="sm" fullWidth>
|
||||||
<DialogTitle>Create User</DialogTitle>
|
<DialogTitle>{labelDialog}</DialogTitle>
|
||||||
<DialogContent sx={{ marginTop: 2 }}>
|
<DialogContent sx={{ marginTop: 2 }}>
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<TextField
|
||||||
|
sx={{ marginTop: 2 }}
|
||||||
|
label="Username"
|
||||||
|
name="username"
|
||||||
|
value={formData.username}
|
||||||
|
onChange={handleChange}
|
||||||
|
fullWidth
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TextField
|
<TextField
|
||||||
sx={{ marginTop: 2 }}
|
sx={{ marginTop: 2 }}
|
||||||
@ -183,6 +277,32 @@ export default function UserManagement() {
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<FormLabel component="legend">Status</FormLabel>
|
||||||
|
<FormControlLabel control={<Checkbox name='status' defaultChecked onChange={handleChange} value={formData.status === 'Y' ? true : false}/>} label="Aktif" />
|
||||||
|
</Grid>
|
||||||
|
{/* <Grid item xs={12}>
|
||||||
|
<TextField
|
||||||
|
sx={{ marginTop: 2 }}
|
||||||
|
label="Nik"
|
||||||
|
name="nik"
|
||||||
|
value={formData.nik}
|
||||||
|
onChange={handleChange}
|
||||||
|
fullWidth
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<TextField
|
||||||
|
sx={{ marginTop: 2 }}
|
||||||
|
label="Position"
|
||||||
|
name="position"
|
||||||
|
value={formData.position}
|
||||||
|
onChange={handleChange}
|
||||||
|
fullWidth
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TextField
|
<TextField
|
||||||
label="Telephone"
|
label="Telephone"
|
||||||
@ -206,6 +326,76 @@ export default function UserManagement() {
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<FormControl fullWidth required>
|
||||||
|
<InputLabel id="organization-label">Organization</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="organization-label"
|
||||||
|
id="organization"
|
||||||
|
value={formData.organisasi}
|
||||||
|
label="Organization"
|
||||||
|
onChange={handleChange}
|
||||||
|
name='organisasi'
|
||||||
|
>
|
||||||
|
<MenuItem value={'Koperasi'}>Koperasi</MenuItem>
|
||||||
|
<MenuItem value={'Damkar'}>Damkar</MenuItem>
|
||||||
|
<MenuItem value={'Kemasyarakatan'}>Kemasyarakatan</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<FormControl fullWidth required>
|
||||||
|
<InputLabel id="organization-label">Devision</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="organization-label"
|
||||||
|
id="organization"
|
||||||
|
value={formData.devisi}
|
||||||
|
label="Devision"
|
||||||
|
onChange={handleChange}
|
||||||
|
name='devisi'
|
||||||
|
>
|
||||||
|
<MenuItem value={`Staff`}>Staff</MenuItem>
|
||||||
|
<MenuItem value={`Lead`}>Lead</MenuItem>
|
||||||
|
<MenuItem value={`Manager`}>Manager</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<FormControl fullWidth required>
|
||||||
|
<InputLabel id="organization-label">Unit</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="organization-label"
|
||||||
|
id="organization"
|
||||||
|
value={formData.unit}
|
||||||
|
label="Unit"
|
||||||
|
onChange={handleChange}
|
||||||
|
name='unit'
|
||||||
|
>
|
||||||
|
<MenuItem value={1}>Satu</MenuItem>
|
||||||
|
<MenuItem value={2}>Dua</MenuItem>
|
||||||
|
<MenuItem value={3}>Tiga</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Grid> */}
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<FormControl fullWidth required>
|
||||||
|
<InputLabel id="organization-label">Role</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="organization-label"
|
||||||
|
id="organization"
|
||||||
|
value={formData.id_role}
|
||||||
|
label="Role"
|
||||||
|
onChange={handleChange}
|
||||||
|
name='id_role'
|
||||||
|
>
|
||||||
|
{
|
||||||
|
roles.map((el, idx) => (
|
||||||
|
<MenuItem key={idx} value={el.id}>{el.name}</MenuItem>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TextField
|
<TextField
|
||||||
label="Password"
|
label="Password"
|
||||||
@ -239,7 +429,9 @@ export default function UserManagement() {
|
|||||||
<Button
|
<Button
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
color="primary"
|
color="primary"
|
||||||
disabled={!formData.name || !formData.email || !formData.telp || !formData.address || !formData.password || errors.passwordMatch}
|
disabled={!formData.name || !formData.email || !formData.password || errors.passwordMatch || !formData.id_role
|
||||||
|
|| !formData.status || !formData.username
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Routes, Route, useLocation, Navigate } from 'react-router-dom';
|
import { Routes, Route, useLocation, Navigate } from 'react-router-dom';
|
||||||
import "../pages/Dashboard.css";
|
import "../pages/Dashboard.css";
|
||||||
import Sidebar from "../components/SideBar";
|
import Sidebar from "../components/SideBar";
|
||||||
import Header from "../components/Header";
|
import Header from "../components/Header";
|
||||||
import { Box, Toolbar } from "@mui/material";
|
import { Box, Toolbar } from "@mui/material";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
// GENERAL
|
// GENERAL
|
||||||
import Dasboard from '../pages/Dashboard';
|
import Dasboard from '../pages/Dashboard';
|
||||||
import LoginPage from '../pages/Login';
|
import LoginPage from '../pages/Login';
|
||||||
import RegisterPage from '../pages/Register';
|
import RegisterPage from '../pages/Register';
|
||||||
|
import ProfilePage from '../pages/Profile';
|
||||||
// ADMIN
|
// ADMIN
|
||||||
import ManageTransfers from '../pages/ManageTransfers';
|
import ManageTransfers from '../pages/ManageTransfers';
|
||||||
import ManageNotifications from '../pages/ManageNotifications';
|
import ManageNotifications from '../pages/ManageNotifications';
|
||||||
@ -23,6 +25,7 @@ import ManageWebservice from '../pages/ManageWebservice';
|
|||||||
import SettingPage from '../pages/Setting';
|
import SettingPage from '../pages/Setting';
|
||||||
import UserManagement from '../pages/UserManagement';
|
import UserManagement from '../pages/UserManagement';
|
||||||
import RoleMenus from '../pages/RoleManagement';
|
import RoleMenus from '../pages/RoleManagement';
|
||||||
|
import MasterDataPage from '../pages/MasterData';
|
||||||
// ESCROW
|
// ESCROW
|
||||||
import TransactionHistory from '../escrowPages/TransactionHistory';
|
import TransactionHistory from '../escrowPages/TransactionHistory';
|
||||||
import TransferPage from '../escrowPages/Transfer';
|
import TransferPage from '../escrowPages/Transfer';
|
||||||
@ -36,7 +39,6 @@ import TopUpRequest from '../tdsPages/TopUpRequest';
|
|||||||
import OperationTransfer from '../tdsPages/Transfer';
|
import OperationTransfer from '../tdsPages/Transfer';
|
||||||
import RegisteredAgent from '../tdsPages/RegisteredAgent';
|
import RegisteredAgent from '../tdsPages/RegisteredAgent';
|
||||||
import RegisteredSupervisor from '../tdsPages/RegisteredSupervisor';
|
import RegisteredSupervisor from '../tdsPages/RegisteredSupervisor';
|
||||||
import Cookies from "js-cookie";
|
|
||||||
|
|
||||||
// Mock Authentication Function
|
// Mock Authentication Function
|
||||||
const isAuthenticated = () => {
|
const isAuthenticated = () => {
|
||||||
@ -80,12 +82,23 @@ const Layout = ({ children }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
let roles_list = JSON.parse(localStorage.getItem('roles_list'));
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(roles_list);
|
||||||
|
console.log(location);
|
||||||
|
|
||||||
|
}, [roles_list, location]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
{/* GENERAL */}
|
{/* GENERAL */}
|
||||||
<Route path="/login" element={<LoginPage />} />
|
<Route path="/login" element={<LoginPage />} />
|
||||||
<Route path="/register" element={<RegisterPage />} />
|
<Route path="/register" element={<RegisterPage />} />
|
||||||
<Route exact path="/" element={<ProtectedRoute><Layout><Dasboard /></Layout></ProtectedRoute>} />
|
<Route exact path="/" element={<ProtectedRoute><Layout><Dasboard /></Layout></ProtectedRoute>} />
|
||||||
|
{/* GENERAL AFTER LOGIN */}
|
||||||
|
<Route path="/profile" element={<ProtectedRoute><Layout><ProfilePage /></Layout></ProtectedRoute>} />
|
||||||
{/* ADMIN */}
|
{/* ADMIN */}
|
||||||
<Route path="/manage-groups" element={<ProtectedRoute><Layout><ManageGroups /></Layout></ProtectedRoute>} />
|
<Route path="/manage-groups" element={<ProtectedRoute><Layout><ManageGroups /></Layout></ProtectedRoute>} />
|
||||||
<Route path="/manage-members" element={<ProtectedRoute><Layout><ManageMembers /></Layout></ProtectedRoute>} />
|
<Route path="/manage-members" element={<ProtectedRoute><Layout><ManageMembers /></Layout></ProtectedRoute>} />
|
||||||
@ -101,6 +114,7 @@ function App() {
|
|||||||
<Route path="/setting" element={<ProtectedRoute><Layout><SettingPage /></Layout></ProtectedRoute>} />
|
<Route path="/setting" element={<ProtectedRoute><Layout><SettingPage /></Layout></ProtectedRoute>} />
|
||||||
<Route path="/user-management" element={<ProtectedRoute><Layout><UserManagement /></Layout></ProtectedRoute>} />
|
<Route path="/user-management" element={<ProtectedRoute><Layout><UserManagement /></Layout></ProtectedRoute>} />
|
||||||
<Route path="/role-management" element={<ProtectedRoute><Layout><RoleMenus /></Layout></ProtectedRoute>} />
|
<Route path="/role-management" element={<ProtectedRoute><Layout><RoleMenus /></Layout></ProtectedRoute>} />
|
||||||
|
<Route path="/master-data" element={<ProtectedRoute><Layout><MasterDataPage /></Layout></ProtectedRoute>} />
|
||||||
{/* ESCROW */}
|
{/* ESCROW */}
|
||||||
<Route path="/transaction-history" element={<ProtectedRoute><Layout><TransactionHistory /></Layout></ProtectedRoute>} />
|
<Route path="/transaction-history" element={<ProtectedRoute><Layout><TransactionHistory /></Layout></ProtectedRoute>} />
|
||||||
<Route path="/transfer" element={<ProtectedRoute><Layout><TransferPage /></Layout></ProtectedRoute>} />
|
<Route path="/transfer" element={<ProtectedRoute><Layout><TransferPage /></Layout></ProtectedRoute>} />
|
||||||
|
|||||||
9
src/store/store.js
Normal file
9
src/store/store.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { configureStore } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
export default configureStore({
|
||||||
|
reducer: {
|
||||||
|
users: () => {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user