rsabhk-fe
This commit is contained in:
commit
ad7d2aedd4
25
.eslintrc.cjs
Normal file
25
.eslintrc.cjs
Normal file
@ -0,0 +1,25 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react/jsx-runtime",
|
||||
"plugin:react-hooks/recommended",
|
||||
],
|
||||
ignorePatterns: ["dist", ".eslintrc.cjs"],
|
||||
parserOptions: { ecmaVersion: "latest", sourceType: "module" },
|
||||
settings: { react: { version: "18.2" } },
|
||||
plugins: ["react-refresh"],
|
||||
rules: {
|
||||
"react-refresh/only-export-components": [
|
||||
"warn",
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
"react/prop-types": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"react/jsx-uses-react": "off",
|
||||
"react/display-name": "off",
|
||||
"react-refresh/only-export-components": "off",
|
||||
},
|
||||
};
|
||||
28
.gitignore
vendored
Normal file
28
.gitignore
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
|
||||
# Contentlayer
|
||||
.contentlayer
|
||||
8
README.md
Normal file
8
README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# React + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
19
index.html
Normal file
19
index.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Rumah Sakit</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Poppins:wght@100;300;400;500;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
12864
package-lock.json
generated
Normal file
12864
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
42
package.json
Normal file
42
package.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "poliklinik_bk",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^2.0.1",
|
||||
"axios": "^1.6.2",
|
||||
"flowbite-react": "^0.7.2",
|
||||
"framer-motion": "^10.17.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.12.0",
|
||||
"react-redux": "^9.0.4",
|
||||
"react-router-dom": "^6.21.1",
|
||||
"react-select": "^5.8.0",
|
||||
"react-toastify": "^9.1.3",
|
||||
"redux": "^5.0.1",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"sweetalert2": "^11.10.1",
|
||||
"swiper": "^11.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.43",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"eslint": "^8.55.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.5",
|
||||
"postcss": "^8.4.32",
|
||||
"tailwindcss": "^3.4.0",
|
||||
"vite": "^5.0.8"
|
||||
}
|
||||
}
|
||||
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
70
src/App.jsx
Normal file
70
src/App.jsx
Normal file
@ -0,0 +1,70 @@
|
||||
import "./index.css";
|
||||
import { Route, Routes, useLocation } from "react-router-dom";
|
||||
import Sidebar from "./components/Sidebar";
|
||||
import DashboardAdmin from "./pages/Admin/DashboardAdmin";
|
||||
import Dokter from "./pages/Admin/Dokter";
|
||||
import Pasien from "./pages/Admin/Pasien";
|
||||
import Poli from "./pages/Admin/Poli";
|
||||
import Obat from "./pages/Admin/Obat";
|
||||
import JadwalPeriksa from "./pages/Dokter/JadwalPeriksa/JadwalPeriksa";
|
||||
import DaftarPeriksa from "./pages/Dokter/PeriksaPasien/DaftarPeriksa";
|
||||
import RiwayatPasien from "./pages/Dokter/RiwayatPasien";
|
||||
import ProfileDokter from "./pages/Dokter/ProfileDokter";
|
||||
import KelolaJadwalPeriksa from "./pages/Dokter/JadwalPeriksa/KelolaJadwalPeriksa";
|
||||
import PeriksaPasien from "./pages/Dokter/PeriksaPasien/PeriksaPasien";
|
||||
import Home from "./pages/Home";
|
||||
// Import Swiper styles
|
||||
import "swiper/css";
|
||||
import "swiper/css/effect-coverflow";
|
||||
import "swiper/css/pagination";
|
||||
import LoginPortal from "./pages/Auth/LoginPortal";
|
||||
import { Provider } from "react-redux";
|
||||
import { store } from "./config";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import { AnimatePresence } from "framer-motion";
|
||||
import DashboardDokter from "./pages/Dokter/DashboardDokter";
|
||||
|
||||
function App() {
|
||||
const location = useLocation();
|
||||
return (
|
||||
<>
|
||||
<Provider store={store}>
|
||||
<AnimatePresence mode="wait" initial={false}>
|
||||
<Routes location={location} key={location.pathname}>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/:id" element={<Home />} />
|
||||
<Route path="/login" element={<LoginPortal />} />
|
||||
<Route path="/admin" element={<Sidebar />}>
|
||||
<Route path=":id" element={<DashboardAdmin />} />
|
||||
<Route path="doctors/:id" element={<Dokter />} />
|
||||
<Route path="pasien/:id" element={<Pasien />} />
|
||||
<Route path="poli/:id" element={<Poli />} />
|
||||
<Route path="obat/:id" element={<Obat />} />
|
||||
</Route>
|
||||
<Route path="/dokter" element={<Sidebar />}>
|
||||
<Route path=":id" element={<DashboardDokter />} />
|
||||
<Route path="jadwal-periksa/:id" element={<JadwalPeriksa />} />
|
||||
<Route
|
||||
path="jadwal-periksa/kelola-jadwal/:id"
|
||||
element={<KelolaJadwalPeriksa />}
|
||||
/>
|
||||
<Route
|
||||
path="jadwal-periksa/kelola-jadwal/:idJadwal/:id"
|
||||
element={<KelolaJadwalPeriksa />}
|
||||
/>
|
||||
<Route path="daftar-periksa/:id" element={<DaftarPeriksa />} />
|
||||
<Route
|
||||
path="daftar-periksa/periksa/:idPasien/:id"
|
||||
element={<PeriksaPasien />}
|
||||
/>
|
||||
<Route path="riwayat-pasien/:id" element={<RiwayatPasien />} />
|
||||
<Route path="profile/:id" element={<ProfileDokter />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</AnimatePresence>
|
||||
</Provider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
BIN
src/assets/avatar/avatar1.jpg
Normal file
BIN
src/assets/avatar/avatar1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
BIN
src/assets/logo/udinus.png
Normal file
BIN
src/assets/logo/udinus.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
1
src/assets/react.svg
Normal file
1
src/assets/react.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
33
src/components/Input.jsx
Normal file
33
src/components/Input.jsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { Label, TextInput } from "flowbite-react";
|
||||
|
||||
const Input = ({
|
||||
id,
|
||||
label,
|
||||
type,
|
||||
placeholder,
|
||||
name,
|
||||
onChange,
|
||||
value,
|
||||
disabled,
|
||||
}) => {
|
||||
return (
|
||||
<div className="my-3">
|
||||
<div className="mb-1 block">
|
||||
<Label htmlFor={id} value={label} />
|
||||
</div>
|
||||
<TextInput
|
||||
id={id}
|
||||
type={type}
|
||||
sizing="md"
|
||||
placeholder={placeholder}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Input;
|
||||
28
src/components/Modals.jsx
Normal file
28
src/components/Modals.jsx
Normal file
@ -0,0 +1,28 @@
|
||||
import { Button, Modal } from "flowbite-react";
|
||||
|
||||
const Modals = ({
|
||||
openModal,
|
||||
setOpenModal,
|
||||
title,
|
||||
body,
|
||||
size,
|
||||
buttonClose = true,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
{openModal && (
|
||||
<Modal show={openModal} onClose={() => setOpenModal(false)} size={size}>
|
||||
<Modal.Header>{title}</Modal.Header>
|
||||
<Modal.Body>{body}</Modal.Body>
|
||||
{buttonClose && (
|
||||
<Modal.Footer className="flex justify-end">
|
||||
<Button onClick={() => setOpenModal(false)}>Close</Button>
|
||||
</Modal.Footer>
|
||||
)}
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Modals;
|
||||
83
src/components/NavbarDashboard.jsx
Normal file
83
src/components/NavbarDashboard.jsx
Normal file
@ -0,0 +1,83 @@
|
||||
import { Dropdown } from "flowbite-react";
|
||||
import { GiHamburgerMenu } from "react-icons/gi";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { logoutAdmin } from "../config/Redux/Action/adminAction";
|
||||
import { logoutDokter } from "../config/Redux/Action/dokterAction";
|
||||
import { FaAngleDown } from "react-icons/fa6";
|
||||
import { HiLogout } from "react-icons/hi";
|
||||
|
||||
const NavbarDashboard = ({ isSidebarOpen, setSidebarOpen }) => {
|
||||
const { admin } = useSelector((state) => state.adminReducer);
|
||||
const { dokter } = useSelector((state) => state.dokterReducer);
|
||||
const dispatch = useDispatch();
|
||||
const nav = useNavigate();
|
||||
const token = localStorage.getItem("token");
|
||||
const role = localStorage.getItem("role");
|
||||
|
||||
const handleLogoutAdmin = () => {
|
||||
dispatch(logoutAdmin(token, nav));
|
||||
};
|
||||
|
||||
const handleLogoutDokter = () => {
|
||||
dispatch(logoutDokter(token, nav));
|
||||
};
|
||||
|
||||
return (
|
||||
<nav className="flex items-center justify-between flex-wrap bg-white p-6 w-full h-[5rem] shadow-md">
|
||||
<div className="flex items-center">
|
||||
<GiHamburgerMenu
|
||||
size={20}
|
||||
onClick={() => setSidebarOpen(!isSidebarOpen)}
|
||||
className="cursor-pointer"
|
||||
/>
|
||||
<Link to="/" className="ml-5 font-bold text-gray-800">
|
||||
Home
|
||||
</Link>
|
||||
<Link to="/" className="ml-3 font-bold text-gray-800">
|
||||
Contact
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
<Dropdown
|
||||
renderTrigger={() => (
|
||||
<div className="flex items-center cursor-pointer">
|
||||
<div className="flex flex-col justify-center items-end">
|
||||
<h3 className=" font-bold text-black">
|
||||
{admin.username ? admin.username : dokter.username}
|
||||
</h3>
|
||||
<span className=" text-[12px] text-black">
|
||||
{admin.role ? admin.role : dokter.role}
|
||||
</span>
|
||||
</div>
|
||||
<FaAngleDown className="text-black mr-2 ml-1" />
|
||||
</div>
|
||||
)}
|
||||
dismissOnClick={false}
|
||||
className="flex flex-col items-center py-3 px-5"
|
||||
>
|
||||
<button
|
||||
className="flex items-center bg-red-500 hover:bg-red-600 p-2 px-3 text-white rounded-md w-full"
|
||||
onClick={
|
||||
role === "admin"
|
||||
? () => handleLogoutAdmin()
|
||||
: () => handleLogoutDokter()
|
||||
}
|
||||
>
|
||||
<HiLogout className="mr-2" />
|
||||
Logout
|
||||
</button>
|
||||
</Dropdown>
|
||||
|
||||
<img
|
||||
src="https://i.pravatar.cc/150?img=3"
|
||||
alt="user"
|
||||
className="w-10 h-10 rounded-full"
|
||||
/>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavbarDashboard;
|
||||
20
src/components/ReactSelect.jsx
Normal file
20
src/components/ReactSelect.jsx
Normal file
@ -0,0 +1,20 @@
|
||||
import Select from "react-select";
|
||||
|
||||
const ReactSelect = ({ data, title, onChange, disabled, isMulti, value }) => {
|
||||
return (
|
||||
<>
|
||||
<div className="my-3">
|
||||
<label className="text-black text-sm font-normal mb-5">{title}</label>
|
||||
<Select
|
||||
options={data}
|
||||
onChange={onChange}
|
||||
isDisabled={disabled}
|
||||
isMulti={isMulti}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReactSelect;
|
||||
18
src/components/Selects.jsx
Normal file
18
src/components/Selects.jsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { Label, Select } from "flowbite-react";
|
||||
|
||||
const Selects = ({ id, label, selectData }) => {
|
||||
return (
|
||||
<div className="w-full my-2">
|
||||
<div className="mb-2 block">
|
||||
<Label htmlFor={id} value={label} />
|
||||
</div>
|
||||
<Select id={id} required>
|
||||
{selectData?.map((data, index) => (
|
||||
<option key={index}>{data}</option>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Selects;
|
||||
191
src/components/Sidebar.jsx
Normal file
191
src/components/Sidebar.jsx
Normal file
@ -0,0 +1,191 @@
|
||||
import SidebarItem from "./SidebarItems";
|
||||
import { MdSpaceDashboard } from "react-icons/md";
|
||||
import {
|
||||
FaHospital,
|
||||
FaNotesMedical,
|
||||
FaPills,
|
||||
FaStethoscope,
|
||||
FaUser,
|
||||
FaUserInjured,
|
||||
} from "react-icons/fa";
|
||||
import { FaUserDoctor } from "react-icons/fa6";
|
||||
import udinus from "../assets/logo/udinus.png";
|
||||
import NavbarDashboard from "./NavbarDashboard";
|
||||
import { Outlet, useLocation, useNavigate, useParams } from "react-router-dom";
|
||||
import { CiMedicalClipboard } from "react-icons/ci";
|
||||
import { useEffect, useState } from "react";
|
||||
import { getAdmin } from "../config/Redux/Action/adminAction";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { getDokter } from "../config/Redux/Action/dokterAction";
|
||||
import { motion } from "framer-motion";
|
||||
import { ToastContainer } from "react-toastify";
|
||||
|
||||
const Sidebars = () => {
|
||||
const pathName = useLocation().pathname;
|
||||
const { id } = useParams();
|
||||
const { admin, isLogin } = useSelector((state) => state.adminReducer);
|
||||
const { dokter, isLoginDokter } = useSelector((state) => state.dokterReducer);
|
||||
const nav = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const token = localStorage.getItem("token");
|
||||
const role = localStorage.getItem("role");
|
||||
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (role === "admin") {
|
||||
if (!token || id === undefined) {
|
||||
dispatch(getAdmin(token));
|
||||
if (isLogin) {
|
||||
nav("/" + admin.id);
|
||||
}
|
||||
} else {
|
||||
dispatch(getAdmin(token));
|
||||
}
|
||||
}
|
||||
}, [dispatch, id, isLogin, nav, token, admin.id, role]);
|
||||
|
||||
useEffect(() => {
|
||||
if (role === "dokter") {
|
||||
if (!token || id === undefined) {
|
||||
dispatch(getDokter(token));
|
||||
if (isLoginDokter) {
|
||||
nav("/" + dokter.id);
|
||||
}
|
||||
} else {
|
||||
dispatch(getDokter(token));
|
||||
}
|
||||
}
|
||||
}, [dispatch, id, isLoginDokter, nav, token, dokter.id, role]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!admin || !dokter) {
|
||||
window.location.href = "/login";
|
||||
}
|
||||
}, [admin, dokter]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ToastContainer />
|
||||
<div className="flex h-screen">
|
||||
<motion.nav
|
||||
className={`sidebar bg-[#00008B] w-fit p-2 h-screen`}
|
||||
initial={{ width: "auto" }}
|
||||
animate={isSidebarOpen ? { width: "auto" } : { width: 0, padding: 0 }}
|
||||
>
|
||||
<div className="flex flex-row items-center justify-start p-4">
|
||||
<img src={udinus} alt="udinus" className="w-10 h-10" />
|
||||
<h1 className="ml-3 text-white font-bold">Poliklinik BK</h1>
|
||||
</div>
|
||||
<hr className="border-[#5C8374]" />
|
||||
<ul className="flex flex-col p-2">
|
||||
{role === "admin" && (
|
||||
<>
|
||||
<SidebarItem
|
||||
icons={<MdSpaceDashboard color="white" size={20} />}
|
||||
text="Dashboard"
|
||||
role="Admin"
|
||||
to={"/admin/" + id}
|
||||
className={pathName == "/admin/" + id ? "bg-[#5C8374]" : ""}
|
||||
/>
|
||||
<SidebarItem
|
||||
icons={<FaUserDoctor color="white" size={20} />}
|
||||
text="Dokter"
|
||||
role="Admin"
|
||||
to={"/admin/doctors/" + id}
|
||||
className={
|
||||
pathName == "/admin/doctors/" + id ? "bg-[#5C8374]" : ""
|
||||
}
|
||||
/>
|
||||
<SidebarItem
|
||||
icons={<FaUserInjured color="white" size={20} />}
|
||||
text="Pasien"
|
||||
role="Admin"
|
||||
to={"/admin/pasien/" + id}
|
||||
className={
|
||||
pathName == "/admin/pasien/" + id ? "bg-[#5C8374]" : ""
|
||||
}
|
||||
/>
|
||||
<SidebarItem
|
||||
icons={<FaHospital color="white" size={20} />}
|
||||
text="Poli"
|
||||
role="Admin"
|
||||
to={"/admin/poli/" + id}
|
||||
className={
|
||||
pathName == "/admin/poli/" + id ? "bg-[#5C8374]" : ""
|
||||
}
|
||||
/>
|
||||
<SidebarItem
|
||||
icons={<FaPills color="white" size={20} />}
|
||||
text="Obat"
|
||||
role="Admin"
|
||||
to={"/admin/obat/" + id}
|
||||
className={
|
||||
pathName == "/admin/obat/" + id ? "bg-[#5C8374]" : ""
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{role === "dokter" && (
|
||||
<>
|
||||
<SidebarItem
|
||||
icons={<MdSpaceDashboard color="white" size={20} />}
|
||||
text="Dashboard"
|
||||
role="Dokter"
|
||||
to={"/dokter/" + id}
|
||||
className={pathName == "/dokter" ? "bg-[#5C8374]" : ""}
|
||||
/>
|
||||
<SidebarItem
|
||||
icons={<CiMedicalClipboard color="white" size={20} />}
|
||||
text="Jadwal Periksa"
|
||||
role="Dokter"
|
||||
to={"/dokter/jadwal-periksa/" + id}
|
||||
className={
|
||||
pathName == "/dokter/jadwal-periksa" ? "bg-[#5C8374]" : ""
|
||||
}
|
||||
/>
|
||||
<SidebarItem
|
||||
icons={<FaStethoscope color="white" size={20} />}
|
||||
text="Memeriksa Pasien"
|
||||
role="Dokter"
|
||||
to={"/dokter/daftar-periksa/" + id}
|
||||
className={
|
||||
pathName == "/dokter/daftar-periksa" ? "bg-[#5C8374]" : ""
|
||||
}
|
||||
/>
|
||||
<SidebarItem
|
||||
icons={<FaNotesMedical color="white" size={20} />}
|
||||
text="Riwayat Pasien"
|
||||
role="Dokter"
|
||||
to={"/dokter/riwayat-pasien/" + id}
|
||||
className={
|
||||
pathName == "/dokter/riwayat-pasien" ? "bg-[#5C8374]" : ""
|
||||
}
|
||||
/>
|
||||
<SidebarItem
|
||||
icons={<FaUser color="white" size={20} />}
|
||||
text="Profil"
|
||||
role="Dokter"
|
||||
to={"/dokter/profile/" + id}
|
||||
className={
|
||||
pathName == "/dokter/profile" ? "bg-[#5C8374]" : ""
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</ul>
|
||||
</motion.nav>
|
||||
<div className={`flex flex-col overflow-auto w-full`}>
|
||||
<NavbarDashboard
|
||||
isSidebarOpen={isSidebarOpen}
|
||||
setSidebarOpen={setIsSidebarOpen}
|
||||
/>
|
||||
<main className="bg-[#F0F4F8] overflow-y-auto max-h-[100vh] w-full flex justify-center">
|
||||
<Outlet context={[role]} />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sidebars;
|
||||
30
src/components/SidebarItems.jsx
Normal file
30
src/components/SidebarItems.jsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
const SidebarItems = ({ icons, text, role, to, className }) => {
|
||||
return (
|
||||
<Link to={to}>
|
||||
<li
|
||||
className={
|
||||
"flex flex-row items-center justify-between w-[18rem] p-4 my-1 hover:bg-[#1E90FF] rounded-md cursor-pointer " +
|
||||
className
|
||||
}
|
||||
>
|
||||
<div className="flex">
|
||||
{icons}
|
||||
<h2 className="text-white font-bold ml-2">{text}</h2>
|
||||
</div>
|
||||
<span
|
||||
className={`${
|
||||
role == "Dokter"
|
||||
? "bg-red-500 text-white text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-red-900 dark:text-red-300"
|
||||
: "bg-green-500 text-white text-sm font-medium px-1 rounded dark:bg-green-900 dark:text-green-300 text-[10px]"
|
||||
}`}
|
||||
>
|
||||
{role}
|
||||
</span>
|
||||
</li>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidebarItems;
|
||||
131
src/components/SwiperCoverflow.jsx
Normal file
131
src/components/SwiperCoverflow.jsx
Normal file
@ -0,0 +1,131 @@
|
||||
import { EffectCoverflow, Pagination } from "swiper/modules";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
|
||||
const SwiperCoverflow = () => {
|
||||
return (
|
||||
<Swiper
|
||||
effect={"coverflow"}
|
||||
grabCursor={true}
|
||||
centeredSlides={true}
|
||||
slidesPerView={"auto"}
|
||||
loop={true}
|
||||
coverflowEffect={{
|
||||
rotate: 50,
|
||||
stretch: 0,
|
||||
depth: 100,
|
||||
modifier: 1,
|
||||
slideShadows: true,
|
||||
}}
|
||||
modules={[EffectCoverflow, Pagination]}
|
||||
className="mySwiper my-4"
|
||||
>
|
||||
<SwiperSlide>
|
||||
<div className="card border p-5 rounded hover:shadow-lg">
|
||||
<div className="card-body">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam,
|
||||
voluptatibus.
|
||||
</p>
|
||||
<div className="flex justify-center items-center mt-4">
|
||||
<img
|
||||
src="https://www.w3schools.com/howto/img_avatar.png"
|
||||
className="rounded-full h-20 w-20"
|
||||
alt="avatar"
|
||||
/>
|
||||
<div className="ml-4">
|
||||
<h1 className="text-xl font-medium">El Macho</h1>
|
||||
<p className="text-gray-700">Pasien</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="card border p-5 rounded hover:shadow-lg">
|
||||
<div className="card-body">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam,
|
||||
voluptatibus.
|
||||
</p>
|
||||
<div className="flex justify-center items-center mt-4">
|
||||
<img
|
||||
src="https://www.w3schools.com/howto/img_avatar.png"
|
||||
className="rounded-full h-20 w-20"
|
||||
alt="avatar"
|
||||
/>
|
||||
<div className="ml-4">
|
||||
<h1 className="text-xl font-medium">El Macho</h1>
|
||||
<p className="text-gray-700">Pasien</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="card border p-5 rounded hover:shadow-lg">
|
||||
<div className="card-body">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam,
|
||||
voluptatibus.
|
||||
</p>
|
||||
<div className="flex justify-center items-center mt-4">
|
||||
<img
|
||||
src="https://www.w3schools.com/howto/img_avatar.png"
|
||||
className="rounded-full h-20 w-20"
|
||||
alt="avatar"
|
||||
/>
|
||||
<div className="ml-4">
|
||||
<h1 className="text-xl font-medium">El Macho</h1>
|
||||
<p className="text-gray-700">Pasien</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="card border p-5 rounded hover:shadow-lg">
|
||||
<div className="card-body">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam,
|
||||
voluptatibus.
|
||||
</p>
|
||||
<div className="flex justify-center items-center mt-4">
|
||||
<img
|
||||
src="https://www.w3schools.com/howto/img_avatar.png"
|
||||
className="rounded-full h-20 w-20"
|
||||
alt="avatar"
|
||||
/>
|
||||
<div className="ml-4">
|
||||
<h1 className="text-xl font-medium">El Macho</h1>
|
||||
<p className="text-gray-700">Pasien</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="card border p-5 rounded hover:shadow-lg">
|
||||
<div className="card-body">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam,
|
||||
voluptatibus.
|
||||
</p>
|
||||
<div className="flex justify-center items-center mt-4">
|
||||
<img
|
||||
src="https://www.w3schools.com/howto/img_avatar.png"
|
||||
className="rounded-full h-20 w-20"
|
||||
alt="avatar"
|
||||
/>
|
||||
<div className="ml-4">
|
||||
<h1 className="text-xl font-medium">El Macho</h1>
|
||||
<p className="text-gray-700">Pasien</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
);
|
||||
};
|
||||
|
||||
export default SwiperCoverflow;
|
||||
22
src/components/TextArea.jsx
Normal file
22
src/components/TextArea.jsx
Normal file
@ -0,0 +1,22 @@
|
||||
import { Label, Textarea } from "flowbite-react";
|
||||
|
||||
const TextArea = ({ id, label, placeholder, name, onChange, value }) => {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="mb-2 block">
|
||||
<Label htmlFor={id} value={label} />
|
||||
</div>
|
||||
<Textarea
|
||||
id={id}
|
||||
placeholder={placeholder}
|
||||
required
|
||||
rows={4}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextArea;
|
||||
61
src/config/Redux/Action/adminAction.js
Normal file
61
src/config/Redux/Action/adminAction.js
Normal file
@ -0,0 +1,61 @@
|
||||
import axios from "axios";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
export const getAdmin = (token, isLogin = false, nav) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
||||
const res = await axios.post(
|
||||
`${import.meta.env.VITE_API_URL}api/auth/admin/me`
|
||||
);
|
||||
dispatch({ type: "SET_ADMIN", payload: res.data.data });
|
||||
dispatch({ type: "SET_IS_LOGIN", payload: true });
|
||||
localStorage.setItem("role", res.data.data.role);
|
||||
if (isLogin) {
|
||||
nav("/" + res.data.data.id);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const loginAdmin = (data, nav, setLoading, setAdmin) => {
|
||||
return async (dispatch) => {
|
||||
setLoading(true);
|
||||
const timer = 2000;
|
||||
try {
|
||||
const res = await axios.post(
|
||||
`${import.meta.env.VITE_API_URL}api/auth/admin/login`,
|
||||
data
|
||||
);
|
||||
localStorage.setItem("token", res.data.access_token);
|
||||
toast.success("Login Berhasil", { autoClose: timer });
|
||||
setTimeout(() => {
|
||||
dispatch(getAdmin(res.data.access_token, true, nav));
|
||||
setLoading(false);
|
||||
setAdmin(false);
|
||||
}, timer);
|
||||
} catch (err) {
|
||||
console.log(err.response.data.error);
|
||||
toast.error(err.response.data.error);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const logoutAdmin = (token, nav) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
||||
await axios.post(`${import.meta.env.VITE_API_URL}api/auth/admin/logout`);
|
||||
localStorage.removeItem("token");
|
||||
dispatch({ type: "SET_IS_LOGIN", payload: false });
|
||||
dispatch({ type: "SET_ADMIN", payload: {} });
|
||||
nav("/");
|
||||
localStorage.removeItem("role");
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
110
src/config/Redux/Action/daftarPoliAction.js
Normal file
110
src/config/Redux/Action/daftarPoliAction.js
Normal file
@ -0,0 +1,110 @@
|
||||
import axios from "axios";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
export const getDaftarPoli = () => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/daftar_poli/`
|
||||
);
|
||||
|
||||
dispatch({ type: "SET_DAFTAR_POLI", payload: data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getDaftarPoliById = (id) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/daftar_poli/${id}`
|
||||
);
|
||||
|
||||
dispatch({ type: "SET_DAFTAR_POLI_BY_ID", payload: data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const addDaftarPoli = (data) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const res = await axios.post(
|
||||
`${import.meta.env.VITE_API_URL}api/daftar_poli/`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log(res);
|
||||
dispatch(getDaftarPoli());
|
||||
toast.success("Data berhasil ditambahkan");
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
toast.error("Data gagal ditambahkan");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const updateDaftarPoli = (id, data, nav) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const res = await axios.put(
|
||||
`${import.meta.env.VITE_API_URL}api/daftar_poli/${id}`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log(res);
|
||||
nav("/admin/daftar-poli");
|
||||
dispatch(getDaftarPoli());
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const deleteDaftarPoli = (id, nav) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const res = await axios.delete(
|
||||
`${import.meta.env.VITE_API_URL}api/daftar_poli/${id}`
|
||||
);
|
||||
console.log(res);
|
||||
nav("/admin/daftar-poli");
|
||||
dispatch(getDaftarPoli());
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getDaftarPoliByPasienId = (id) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/daftar_poli/pasien/${id}`
|
||||
);
|
||||
|
||||
console.log(data);
|
||||
|
||||
dispatch({ type: "SET_DAFTAR_POLI_BY_PASIEN_ID", payload: data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
93
src/config/Redux/Action/detailPriksaAction.js
Normal file
93
src/config/Redux/Action/detailPriksaAction.js
Normal file
@ -0,0 +1,93 @@
|
||||
import axios from "axios";
|
||||
|
||||
export const getDetailPriksa = () => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/detail_periksa/`
|
||||
);
|
||||
|
||||
dispatch({ type: "SET_DETAIL_PRIKSA", payload: data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getDetailPriksaByPeriksaId = (id) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/detail_periksa/periksa/${id}`
|
||||
);
|
||||
|
||||
dispatch({ type: "SET_DETAIL_PRIKSA_BY_PERIKSA_ID", payload: data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getDetailPriksaById = (id) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/detail_periksa/${id}`
|
||||
);
|
||||
|
||||
dispatch({ type: "SET_DETAIL_PRIKSA_BY_ID", payload: data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const addDetailPriksa = (values) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
await axios.post(
|
||||
`${import.meta.env.VITE_API_URL}api/detail_periksa/`,
|
||||
values
|
||||
);
|
||||
|
||||
dispatch(getDetailPriksa());
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const updateDetailPriksa = (id, values) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const { data } = await axios.put(
|
||||
`${import.meta.env.VITE_API_URL}api/detail_periksa/${id}`,
|
||||
values
|
||||
);
|
||||
|
||||
dispatch({ type: "SET_DETAIL_PRIKSA", payload: data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const deleteDetailPriksa = (id) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const { data } = await axios.delete(
|
||||
`${import.meta.env.VITE_API_URL}api/detail_periksa/${id}`
|
||||
);
|
||||
|
||||
dispatch({ type: "SET_DETAIL_PRIKSA", payload: data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
149
src/config/Redux/Action/dokterAction.js
Normal file
149
src/config/Redux/Action/dokterAction.js
Normal file
@ -0,0 +1,149 @@
|
||||
import axios from "axios";
|
||||
import { toast } from "react-toastify";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
export const getAllDokter = () => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
const res = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/auth/dokter/getAll`
|
||||
);
|
||||
dispatch({ type: "SET_ALL_DOKTER", payload: res.data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getDokter = (token, isLogin = false, nav) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
||||
const res = await axios.post(
|
||||
`${import.meta.env.VITE_API_URL}api/auth/dokter/me`
|
||||
);
|
||||
dispatch({ type: "SET_DOKTER", payload: res.data });
|
||||
dispatch({ type: "SET_IS_LOGIN", payload: true });
|
||||
localStorage.setItem("role", res.data.role);
|
||||
if (isLogin) {
|
||||
nav("/" + res.data.id);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const registerDokter = (data, loading) => {
|
||||
return async (dispatch) => {
|
||||
if (loading) loading(true);
|
||||
try {
|
||||
await axios.post(`${import.meta.env.VITE_API_URL}api/auth/dokter/`, data);
|
||||
toast.success("Registrasi Berhasil");
|
||||
if (loading) loading(false);
|
||||
dispatch(getAllDokter());
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
toast.error(err.response.data.error);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const loginDokter = (data, nav, setLoading, setDokter) => {
|
||||
return async (dispatch) => {
|
||||
setLoading(true);
|
||||
const timer = 2000;
|
||||
try {
|
||||
const res = await axios.post(
|
||||
`${import.meta.env.VITE_API_URL}api/auth/dokter/login`,
|
||||
data
|
||||
);
|
||||
localStorage.setItem("token", res.data.access_token);
|
||||
toast.success("Login Berhasil", { autoClose: timer });
|
||||
setTimeout(() => {
|
||||
dispatch(getDokter(res.data.access_token, true, nav));
|
||||
setLoading(false);
|
||||
setDokter(false);
|
||||
}, timer);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
toast.error(err.response.data.error);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const logoutDokter = (token, nav) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
||||
await axios.post(`${import.meta.env.VITE_API_URL}api/auth/dokter/logout`);
|
||||
localStorage.removeItem("token");
|
||||
dispatch({ type: "SET_IS_LOGIN", payload: false });
|
||||
dispatch({ type: "SET_DOKTER", payload: {} });
|
||||
nav("/");
|
||||
localStorage.removeItem("role");
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getDokterById = (id) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
const res = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/auth/dokter/get/${id}`
|
||||
);
|
||||
dispatch({ type: "SET_DOKTER_BY_ID", payload: res.data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const updateDokter = (id, data, loading, edit) => {
|
||||
return async (dispatch) => {
|
||||
if (loading) loading(true);
|
||||
try {
|
||||
await axios.put(
|
||||
`${import.meta.env.VITE_API_URL}api/auth/dokter/update/${id}`,
|
||||
data
|
||||
);
|
||||
if (loading && edit) {
|
||||
loading(false);
|
||||
edit(false);
|
||||
}
|
||||
toast.success("Berhasil diupdate");
|
||||
dispatch(getAllDokter());
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
toast.error(err.response.data.error);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const deleteDokter = (id) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
Swal.fire({
|
||||
title: "Apakah anda yakin?",
|
||||
text: "Anda akan menghapus dokter ini",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
}).then(async (result) => {
|
||||
if (result.isConfirmed) {
|
||||
await axios.delete(
|
||||
`${import.meta.env.VITE_API_URL}api/auth/dokter/delete/${id}`
|
||||
);
|
||||
dispatch(getAllDokter());
|
||||
Swal.fire("Terhapus!", "Dokter berhasil dihapus.", "success");
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
9
src/config/Redux/Action/index.js
Normal file
9
src/config/Redux/Action/index.js
Normal file
@ -0,0 +1,9 @@
|
||||
export * from "./adminAction";
|
||||
export * from "./obatAction";
|
||||
export * from "./dokterAction";
|
||||
export * from "./pasienAction";
|
||||
export * from "./poliAction";
|
||||
export * from "./jadwalPeriksaAction";
|
||||
export * from "./daftarPoliAction";
|
||||
export * from "./periksaAction";
|
||||
export * from "./detailPriksaAction";
|
||||
106
src/config/Redux/Action/jadwalPeriksaAction.js
Normal file
106
src/config/Redux/Action/jadwalPeriksaAction.js
Normal file
@ -0,0 +1,106 @@
|
||||
import axios from "axios";
|
||||
import Swal from "sweetalert2";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
export const getJadwalPeriksa = () => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/jadwal_periksa/`
|
||||
);
|
||||
|
||||
dispatch({ type: "SET_JADWAL_PERIKSA", payload: data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getJadwalPeriksaById = (id) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/jadwal_periksa/${id}`
|
||||
);
|
||||
|
||||
dispatch({ type: "SET_JADWAL_PERIKSA_BY_ID", payload: data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const addJadwalPeriksa = (data, nav) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const res = await axios.post(
|
||||
`${import.meta.env.VITE_API_URL}api/jadwal_periksa/`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log(res);
|
||||
nav("/dokter/jadwal-periksa/" + data.id_dokter);
|
||||
dispatch(getJadwalPeriksa());
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
toast.error(err.response.data.error);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const updateJadwalPeriksa = (id, data, nav) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const res = await axios.put(
|
||||
`${import.meta.env.VITE_API_URL}api/jadwal_periksa/${id}`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log(res);
|
||||
nav("/dokter/jadwal-periksa/" + data.id_dokter);
|
||||
dispatch(getJadwalPeriksa());
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
toast.error(err.response.data.error);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const deleteJadwalPeriksa = (id) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
Swal.fire({
|
||||
title: "Apakah anda yakin?",
|
||||
text: "Data yang dihapus tidak dapat dikembalikan!",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonText: "Ya, hapus!",
|
||||
cancelButtonText: "Batal",
|
||||
}).then(async (result) => {
|
||||
if (result.isConfirmed) {
|
||||
const res = await axios.delete(
|
||||
`${import.meta.env.VITE_API_URL}api/jadwal_periksa/${id}`
|
||||
);
|
||||
console.log(res);
|
||||
dispatch(getJadwalPeriksa());
|
||||
Swal.fire("Terhapus!", "Data berhasil dihapus.", "success");
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
110
src/config/Redux/Action/obatAction.js
Normal file
110
src/config/Redux/Action/obatAction.js
Normal file
@ -0,0 +1,110 @@
|
||||
import axios from "axios";
|
||||
import { toast } from "react-toastify";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
export const getObat = () => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/obat/`
|
||||
);
|
||||
dispatch({ type: "SET_OBAT", payload: data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getObatById = (id) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/obat/${id}`
|
||||
);
|
||||
dispatch({ type: "SET_OBAT_BY_ID", payload: data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const storeObat = (data) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
await axios.post(
|
||||
`${import.meta.env.VITE_API_URL}api/obat/`,
|
||||
{
|
||||
nama_obat: data.nama_obat,
|
||||
kemasan: data.kemasan,
|
||||
harga: data.harga,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
toast.success("Berhasil ditambahkan");
|
||||
dispatch(getObat());
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
toast.error(err.response.data.error);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const updateObat = (id, data) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
await axios.put(
|
||||
`${import.meta.env.VITE_API_URL}api/obat/${id}`,
|
||||
{
|
||||
nama_obat: data.nama_obat,
|
||||
kemasan: data.kemasan,
|
||||
harga: data.harga,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
toast.success("Berhasil diupdate");
|
||||
dispatch(getObat());
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
toast.error(err.response.data.error);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const deleteObat = (id) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
Swal.fire({
|
||||
title: "Apakah anda yakin?",
|
||||
text: "Obat akan dihapus secara permanen",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonText: "Hapus",
|
||||
cancelButtonText: "Batal",
|
||||
}).then(async (result) => {
|
||||
if (result.isConfirmed) {
|
||||
await axios.delete(`${import.meta.env.VITE_API_URL}api/obat/${id}`);
|
||||
dispatch(getObat());
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
139
src/config/Redux/Action/pasienAction.js
Normal file
139
src/config/Redux/Action/pasienAction.js
Normal file
@ -0,0 +1,139 @@
|
||||
import axios from "axios";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
export const getAllPasien = () => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
const res = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/auth/pasien/getAll`
|
||||
);
|
||||
dispatch({ type: "SET_PASIEN_ALL", payload: res.data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getPasienById = (id) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
const res = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/auth/pasien/get/${id}`
|
||||
);
|
||||
|
||||
dispatch({ type: "SET_PASIEN_BY_ID", payload: res.data.data });
|
||||
console.log(res.data.data);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const updatePasien = (id, data, setLoading, setPasien) => {
|
||||
return async (dispatch) => {
|
||||
if (setLoading) {
|
||||
setLoading(true);
|
||||
}
|
||||
try {
|
||||
await axios.put(
|
||||
`${import.meta.env.VITE_API_URL}api/auth/pasien/update/${id}`,
|
||||
data
|
||||
);
|
||||
if (setLoading && setPasien) {
|
||||
setLoading(false);
|
||||
setPasien(false);
|
||||
}
|
||||
toast.success("Update Berhasil");
|
||||
dispatch(getAllPasien());
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
toast.error("Update Gagal");
|
||||
if (setLoading) {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const registerPasien = (data, setPasien, setLoading) => {
|
||||
return async () => {
|
||||
if (setLoading) {
|
||||
setLoading(true);
|
||||
}
|
||||
try {
|
||||
await axios.post(`${import.meta.env.VITE_API_URL}api/auth/pasien/`, data);
|
||||
if (setLoading && setPasien) {
|
||||
setLoading(false);
|
||||
setPasien(false);
|
||||
}
|
||||
toast.success("Registrasi Berhasil");
|
||||
getAllPasien();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
toast.error("Registrasi Gagal");
|
||||
if (setLoading) {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getPasien = (token, isLogin = false, nav) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
||||
const res = await axios.post(
|
||||
`${import.meta.env.VITE_API_URL}api/auth/pasien/me`
|
||||
);
|
||||
dispatch({ type: "SET_PASIEN", payload: res.data.data });
|
||||
dispatch({ type: "SET_IS_LOGIN", payload: true });
|
||||
localStorage.setItem("role", res.data.data.role);
|
||||
if (isLogin) {
|
||||
nav("/" + res.data.data.id);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const loginPasien = (data, nav, setLoading, setPasien) => {
|
||||
return async (dispatch) => {
|
||||
setLoading(true);
|
||||
const timer = 2000;
|
||||
try {
|
||||
const res = await axios.post(
|
||||
`${import.meta.env.VITE_API_URL}api/auth/pasien/login`,
|
||||
data
|
||||
);
|
||||
localStorage.setItem("token", res.data.access_token);
|
||||
|
||||
toast.success("Login Berhasil", { autoClose: timer });
|
||||
setTimeout(() => {
|
||||
dispatch(getPasien(res.data.access_token, true, nav));
|
||||
setLoading(false);
|
||||
setPasien(false);
|
||||
}, timer);
|
||||
} catch (err) {
|
||||
console.log(err.response.data.error);
|
||||
toast.error(err.response.data.error);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const logoutPasien = (token, nav) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
||||
await axios.post(`${import.meta.env.VITE_API_URL}api/auth/pasien/logout`);
|
||||
localStorage.removeItem("token");
|
||||
dispatch({ type: "SET_IS_LOGIN", payload: false });
|
||||
dispatch({ type: "SET_PASIEN", payload: {} });
|
||||
nav("/");
|
||||
localStorage.removeItem("role");
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
151
src/config/Redux/Action/periksaAction.js
Normal file
151
src/config/Redux/Action/periksaAction.js
Normal file
@ -0,0 +1,151 @@
|
||||
import axios from "axios";
|
||||
import { addDetailPriksa } from "./detailPriksaAction";
|
||||
|
||||
export const getPeriksa = () => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/periksa/`
|
||||
);
|
||||
dispatch({ type: "SET_PERIKSA", payload: data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getPeriksaByDafPol = (id) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/periksa/daftar_poli/${id}`
|
||||
);
|
||||
dispatch({ type: "SET_PERIKSA_BY_DAF_POL_ID", payload: data.data[0] });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getPeriksaById = (id) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/periksa/${id}`
|
||||
);
|
||||
dispatch({ type: "SET_PERIKSA_BY_ID", payload: data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const storePeriksa = (data, nav, id, obat) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
const res = await axios.post(
|
||||
`${import.meta.env.VITE_API_URL}api/periksa/`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
nav("/dokter/daftar-periksa/" + id);
|
||||
dispatch(getPeriksa());
|
||||
dispatch({ type: "SET_ID_DATA_ADD", payload: res.data.data.id });
|
||||
|
||||
if (obat.length > 0) {
|
||||
obat.map((item) => {
|
||||
const data = {
|
||||
id_periksa: res.data.data.id,
|
||||
id_obat: item.value,
|
||||
};
|
||||
dispatch(addDetailPriksa(data));
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const updatePeriksa = (id, data, obat) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
await axios.put(
|
||||
`${import.meta.env.VITE_API_URL}api/periksa/${id}`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const detailPriksaResponse = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/detail_periksa/periksa/${id}`
|
||||
);
|
||||
|
||||
const detailPriksa = detailPriksaResponse.data.data;
|
||||
|
||||
// Mengumpulkan hanya ID obat dari detailPriksa dan obat
|
||||
const detailObatIDs = detailPriksa.map((detail) => detail.obat.id);
|
||||
const obatIDs = obat.map((item) => item.value);
|
||||
|
||||
// Menghapus ID obat yang ada di detailPeriksa tetapi tidak ada di obat
|
||||
const obatToDelete = detailObatIDs.filter(
|
||||
(obatID) => !obatIDs.includes(obatID)
|
||||
);
|
||||
obatToDelete.forEach(async (obatID) => {
|
||||
// Lakukan operasi hapus data di database untuk ID obat yang tidak ada di obat
|
||||
await axios.delete(
|
||||
`${
|
||||
import.meta.env.VITE_API_URL
|
||||
}api/detail_periksa/obat/${obatID}/${id}`
|
||||
);
|
||||
});
|
||||
|
||||
// Menambahkan ID obat yang ada di obat tetapi tidak ada di detailPeriksa
|
||||
const obatToAdd = obatIDs.filter(
|
||||
(obatID) => !detailObatIDs.includes(obatID)
|
||||
);
|
||||
obatToAdd.forEach(async (obatID) => {
|
||||
// Lakukan operasi tambah data di database untuk ID obat yang tidak ada di detailPeriksa
|
||||
const newData = {
|
||||
id_periksa: id,
|
||||
id_obat: obatID,
|
||||
// Jika ada fields tambahan, sesuaikan di sini
|
||||
};
|
||||
await axios.post(
|
||||
`${import.meta.env.VITE_API_URL}api/detail_periksa`,
|
||||
newData
|
||||
);
|
||||
});
|
||||
|
||||
dispatch(getPeriksa());
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const deletePeriksa = (id) => {
|
||||
return async (dispatch) => {
|
||||
dispatch({ type: "SET_IS_LOADING", payload: true });
|
||||
try {
|
||||
await axios.delete(`${import.meta.env.VITE_API_URL}api/periksa/${id}`);
|
||||
|
||||
dispatch(getPeriksa());
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
85
src/config/Redux/Action/poliAction.js
Normal file
85
src/config/Redux/Action/poliAction.js
Normal file
@ -0,0 +1,85 @@
|
||||
import axios from "axios";
|
||||
import { toast } from "react-toastify";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
export const getPoli = () => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
dispatch({ type: "SET_LOADING", payload: true });
|
||||
const res = await axios.get(`${import.meta.env.VITE_API_URL}api/poli`);
|
||||
dispatch({ type: "SET_POLI", payload: res.data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
dispatch({ type: "SET_LOADING", payload: false });
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getPoliById = (id) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
dispatch({ type: "SET_LOADING", payload: true });
|
||||
const res = await axios.get(
|
||||
`${import.meta.env.VITE_API_URL}api/poli/${id}`
|
||||
);
|
||||
dispatch({ type: "SET_POLI_BY_ID", payload: res.data.data });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
dispatch({ type: "SET_LOADING", payload: false });
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const addPoli = (data) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
dispatch({ type: "SET_LOADING", payload: true });
|
||||
await axios.post(`${import.meta.env.VITE_API_URL}api/poli/`, data);
|
||||
toast.success("Berhasil ditambahkan");
|
||||
dispatch(getPoli());
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
dispatch({ type: "SET_LOADING", payload: false });
|
||||
toast.error(err.response.data.error);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const deletePoli = (id) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
Swal.fire({
|
||||
title: "Are you sure?",
|
||||
text: "You will delete this data",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
}).then(async (result) => {
|
||||
if (result.isConfirmed) {
|
||||
dispatch({ type: "SET_LOADING", payload: true });
|
||||
await axios.delete(`${import.meta.env.VITE_API_URL}api/poli/${id}`);
|
||||
dispatch(getPoli());
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
dispatch({ type: "SET_LOADING", payload: false });
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const updatePoli = (id, data) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
dispatch({ type: "SET_LOADING", payload: true });
|
||||
await axios.put(`${import.meta.env.VITE_API_URL}api/poli/${id}`, data);
|
||||
dispatch(getPoli());
|
||||
toast.success("Berhasil diupdate");
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
dispatch({ type: "SET_LOADING", payload: false });
|
||||
toast.error(err.response.data.error);
|
||||
}
|
||||
};
|
||||
};
|
||||
29
src/config/Redux/Reducer/adminReducer.js
Normal file
29
src/config/Redux/Reducer/adminReducer.js
Normal file
@ -0,0 +1,29 @@
|
||||
const initialState = {
|
||||
admin: {},
|
||||
isLogin: false,
|
||||
errorMessageAdmin: "",
|
||||
};
|
||||
|
||||
const adminReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case "SET_ADMIN":
|
||||
return {
|
||||
...state,
|
||||
admin: action.payload,
|
||||
};
|
||||
case "SET_IS_LOGIN":
|
||||
return {
|
||||
...state,
|
||||
isLogin: action.payload,
|
||||
};
|
||||
case "SET_ERROR_MESSAGE_ADMIN":
|
||||
return {
|
||||
...state,
|
||||
errorMessageAdmin: action.payload,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default adminReducer;
|
||||
31
src/config/Redux/Reducer/daftarPoliReducer.js
Normal file
31
src/config/Redux/Reducer/daftarPoliReducer.js
Normal file
@ -0,0 +1,31 @@
|
||||
const initialState = {
|
||||
daftarPoli: [],
|
||||
daftarPoliById: {},
|
||||
daftarPoliByPasienId: {},
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
const daftarPoliReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case "SET_DAFTAR_POLI":
|
||||
return {
|
||||
...state,
|
||||
daftarPoli: action.payload,
|
||||
};
|
||||
case "SET_DAFTAR_POLI_BY_ID":
|
||||
return {
|
||||
...state,
|
||||
daftarPoliById: action.payload,
|
||||
};
|
||||
case "SET_DAFTAR_POLI_BY_PASIEN_ID":
|
||||
return {
|
||||
...state,
|
||||
daftarPoliByPasienId: action.payload,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default daftarPoliReducer;
|
||||
29
src/config/Redux/Reducer/detailPeriksaReducer.js
Normal file
29
src/config/Redux/Reducer/detailPeriksaReducer.js
Normal file
@ -0,0 +1,29 @@
|
||||
const initialState = {
|
||||
detailPriksa: [],
|
||||
detailPriksaById: [],
|
||||
detailPriksaByPriksaId: [],
|
||||
};
|
||||
|
||||
const detailPeriksaReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case "SET_DETAIL_PRIKSA":
|
||||
return {
|
||||
...state,
|
||||
detailPriksa: action.payload,
|
||||
};
|
||||
case "SET_DETAIL_PRIKSA_BY_ID":
|
||||
return {
|
||||
...state,
|
||||
detailPriksaById: action.payload,
|
||||
};
|
||||
case "SET_DETAIL_PRIKSA_BY_PERIKSA_ID":
|
||||
return {
|
||||
...state,
|
||||
detailPriksaByPriksaId: action.payload,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default detailPeriksaReducer;
|
||||
35
src/config/Redux/Reducer/dokterReducer.js
Normal file
35
src/config/Redux/Reducer/dokterReducer.js
Normal file
@ -0,0 +1,35 @@
|
||||
const initialState = {
|
||||
dokter: {},
|
||||
allDokter: [],
|
||||
dokterById: {},
|
||||
isLoginDokter: false,
|
||||
};
|
||||
|
||||
const dokterReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case "SET_DOKTER":
|
||||
return {
|
||||
...state,
|
||||
dokter: action.payload,
|
||||
};
|
||||
case "SET_IS_LOGIN":
|
||||
return {
|
||||
...state,
|
||||
isLoginDokter: action.payload,
|
||||
};
|
||||
case "SET_ALL_DOKTER":
|
||||
return {
|
||||
...state,
|
||||
allDokter: action.payload,
|
||||
};
|
||||
case "SET_DOKTER_BY_ID":
|
||||
return {
|
||||
...state,
|
||||
dokterById: action.payload,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default dokterReducer;
|
||||
25
src/config/Redux/Reducer/jadwalPeriksaReducer.js
Normal file
25
src/config/Redux/Reducer/jadwalPeriksaReducer.js
Normal file
@ -0,0 +1,25 @@
|
||||
const initialState = {
|
||||
jadwalPeriksa: [],
|
||||
jadwalPeriksaById: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
const jadwalPeriksaReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case "SET_JADWAL_PERIKSA":
|
||||
return {
|
||||
...state,
|
||||
jadwalPeriksa: action.payload,
|
||||
};
|
||||
case "SET_JADWAL_PERIKSA_BY_ID":
|
||||
return {
|
||||
...state,
|
||||
jadwalPeriksaById: action.payload,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default jadwalPeriksaReducer;
|
||||
23
src/config/Redux/Reducer/obatReducer.js
Normal file
23
src/config/Redux/Reducer/obatReducer.js
Normal file
@ -0,0 +1,23 @@
|
||||
const initialState = {
|
||||
obat: [],
|
||||
obatById: {},
|
||||
};
|
||||
|
||||
const obatReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case "SET_OBAT":
|
||||
return {
|
||||
...state,
|
||||
obat: action.payload,
|
||||
};
|
||||
case "SET_OBAT_BY_ID":
|
||||
return {
|
||||
...state,
|
||||
obatById: action.payload,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default obatReducer;
|
||||
41
src/config/Redux/Reducer/pasienReducer.js
Normal file
41
src/config/Redux/Reducer/pasienReducer.js
Normal file
@ -0,0 +1,41 @@
|
||||
const initialState = {
|
||||
pasien: {},
|
||||
pasienAll: [],
|
||||
pasienById: {},
|
||||
isLogin: false,
|
||||
errorMessagePasien: "",
|
||||
};
|
||||
|
||||
const pasienReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case "SET_PASIEN":
|
||||
return {
|
||||
...state,
|
||||
pasien: action.payload,
|
||||
};
|
||||
case "SET_IS_LOGIN":
|
||||
return {
|
||||
...state,
|
||||
isLogin: action.payload,
|
||||
};
|
||||
case "SET_PASIEN_ALL":
|
||||
return {
|
||||
...state,
|
||||
pasienAll: action.payload,
|
||||
};
|
||||
case "SET_PASIEN_BY_ID":
|
||||
return {
|
||||
...state,
|
||||
pasienById: action.payload,
|
||||
};
|
||||
case "SET_ERROR_MESSAGE_PASIEN":
|
||||
return {
|
||||
...state,
|
||||
errorMessagePasien: action.payload,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default pasienReducer;
|
||||
35
src/config/Redux/Reducer/periksaReducer.js
Normal file
35
src/config/Redux/Reducer/periksaReducer.js
Normal file
@ -0,0 +1,35 @@
|
||||
const initialState = {
|
||||
periksa: [],
|
||||
periksaById: [],
|
||||
idDataAdd: "",
|
||||
periksaByDafPolId: {},
|
||||
};
|
||||
|
||||
const periksaReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case "SET_PERIKSA":
|
||||
return {
|
||||
...state,
|
||||
periksa: action.payload,
|
||||
};
|
||||
case "SET_PERIKSA_BY_ID":
|
||||
return {
|
||||
...state,
|
||||
periksaById: action.payload,
|
||||
};
|
||||
case "SET_ID_DATA_ADD":
|
||||
return {
|
||||
...state,
|
||||
idDataAdd: action.payload,
|
||||
};
|
||||
case "SET_PERIKSA_BY_DAF_POL_ID":
|
||||
return {
|
||||
...state,
|
||||
periksaByDafPolId: action.payload,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default periksaReducer;
|
||||
30
src/config/Redux/Reducer/poliReducer.js
Normal file
30
src/config/Redux/Reducer/poliReducer.js
Normal file
@ -0,0 +1,30 @@
|
||||
const initialState = {
|
||||
poli: [],
|
||||
poliById: {},
|
||||
isLoading: false,
|
||||
};
|
||||
|
||||
const poli = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case "SET_POLI":
|
||||
return {
|
||||
...state,
|
||||
poli: action.payload,
|
||||
};
|
||||
case "SET_POLI_BY_ID":
|
||||
return {
|
||||
...state,
|
||||
poliById: action.payload,
|
||||
};
|
||||
case "SET_LOADING":
|
||||
return {
|
||||
...state,
|
||||
isLoading: action.payload,
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default poli;
|
||||
24
src/config/Redux/Reducer/reducer.js
Normal file
24
src/config/Redux/Reducer/reducer.js
Normal file
@ -0,0 +1,24 @@
|
||||
import { combineReducers } from "redux";
|
||||
import adminReducer from "./adminReducer";
|
||||
import obatReducer from "./obatReducer";
|
||||
import dokterReducer from "./dokterReducer";
|
||||
import pasienReducer from "./pasienReducer";
|
||||
import poliReducer from "./poliReducer";
|
||||
import jadwalPeriksaReducer from "./jadwalPeriksaReducer";
|
||||
import daftarPoliReducer from "./daftarPoliReducer";
|
||||
import periksaReducer from "./periksaReducer";
|
||||
import detailPeriksaReducer from "./detailPeriksaReducer";
|
||||
|
||||
const reducer = combineReducers({
|
||||
adminReducer,
|
||||
obatReducer,
|
||||
dokterReducer,
|
||||
pasienReducer,
|
||||
poliReducer,
|
||||
jadwalPeriksaReducer,
|
||||
daftarPoliReducer,
|
||||
periksaReducer,
|
||||
detailPeriksaReducer,
|
||||
});
|
||||
|
||||
export default reducer;
|
||||
6
src/config/Redux/store.js
Normal file
6
src/config/Redux/store.js
Normal file
@ -0,0 +1,6 @@
|
||||
import { applyMiddleware, createStore } from "redux";
|
||||
import reducer from "./Reducer/reducer";
|
||||
import { thunk } from "redux-thunk";
|
||||
|
||||
const store = createStore(reducer, applyMiddleware(thunk));
|
||||
export default store;
|
||||
2
src/config/index.js
Normal file
2
src/config/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
import store from "./Redux/store";
|
||||
export { store };
|
||||
14
src/index.css
Normal file
14
src/index.css
Normal file
@ -0,0 +1,14 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
* {
|
||||
font-family: "Poppins", sans-serif;
|
||||
}
|
||||
|
||||
.swiper-slide {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
.swiper-wrapper {
|
||||
width: 25% !important;
|
||||
}
|
||||
13
src/main.jsx
Normal file
13
src/main.jsx
Normal file
@ -0,0 +1,13 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App.jsx";
|
||||
import "./index.css";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")).render(
|
||||
<React.StrictMode>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</React.StrictMode>
|
||||
);
|
||||
11
src/pages/Admin/DashboardAdmin.jsx
Normal file
11
src/pages/Admin/DashboardAdmin.jsx
Normal file
@ -0,0 +1,11 @@
|
||||
import transition from "../../transition";
|
||||
|
||||
const DashboardAdmin = () => {
|
||||
return (
|
||||
<div className="flex justify-center items-center w-full h-screen">
|
||||
<h1>Dashboard Admin</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardAdmin;
|
||||
347
src/pages/Admin/Dokter.jsx
Normal file
347
src/pages/Admin/Dokter.jsx
Normal file
@ -0,0 +1,347 @@
|
||||
import { useLocation, useOutletContext } from "react-router-dom";
|
||||
import Input from "../../components/Input";
|
||||
import { Spinner, Table } from "flowbite-react";
|
||||
import TextArea from "../../components/TextArea";
|
||||
import { useEffect, useState } from "react";
|
||||
import ReactSelect from "../../components/ReactSelect";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { getPoli, getPoliById } from "../../config/Redux/Action/poliAction";
|
||||
import {
|
||||
getAllDokter,
|
||||
getDokterById,
|
||||
registerDokter,
|
||||
updateDokter,
|
||||
} from "../../config/Redux/Action/dokterAction";
|
||||
import Modals from "../../components/Modals";
|
||||
|
||||
const Dokter = () => {
|
||||
const pathName = useLocation().pathname;
|
||||
const [role] = useOutletContext();
|
||||
const dispatch = useDispatch();
|
||||
const { poli, poliById } = useSelector((state) => state.poliReducer);
|
||||
const { allDokter, dokterById } = useSelector((state) => state.dokterReducer);
|
||||
const [poliOption, setPoliOption] = useState([]);
|
||||
const [poliSelected, setPoliSelected] = useState([]);
|
||||
const [dokterForm, setDokterForm] = useState({
|
||||
nama: "",
|
||||
alamat: "",
|
||||
no_hp: "",
|
||||
id_poli: "",
|
||||
username: "",
|
||||
password: "",
|
||||
role: "dokter",
|
||||
});
|
||||
const [editDokter, setEditDokter] = useState(false);
|
||||
const [poliEditSelected, setPoliEditSelected] = useState([]);
|
||||
const [dokterId, setDokterId] = useState("");
|
||||
const [editDokterForm, setEditDokterForm] = useState({
|
||||
nama: dokterById?.nama,
|
||||
alamat: dokterById?.alamat,
|
||||
no_hp: dokterById?.no_hp,
|
||||
id_poli: poliEditSelected?.value,
|
||||
username: dokterById?.username,
|
||||
role: "dokter",
|
||||
});
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(registerDokter(dokterForm, setLoading));
|
||||
setDokterForm({
|
||||
nama: "",
|
||||
alamat: "",
|
||||
no_hp: "",
|
||||
id_poli: "",
|
||||
username: "",
|
||||
password: "",
|
||||
role: "dokter",
|
||||
});
|
||||
setPoliSelected([]);
|
||||
};
|
||||
|
||||
const handleEdit = (id) => {
|
||||
setEditDokter(true);
|
||||
setDokterId(id);
|
||||
};
|
||||
|
||||
const handleUpdate = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(updateDokter(dokterId, editDokterForm, setLoading, setEditDokter));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setDokterForm({ ...dokterForm, id_poli: poliSelected?.value });
|
||||
}, [poliSelected]);
|
||||
|
||||
useEffect(() => {
|
||||
if (dokterById) {
|
||||
dispatch(getPoliById(dokterById.id_poli));
|
||||
}
|
||||
}, [dokterById, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getPoli());
|
||||
dispatch(getAllDokter());
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (poliById) {
|
||||
setPoliEditSelected({
|
||||
value: poliById.id,
|
||||
label: poliById.nama_poli,
|
||||
});
|
||||
}
|
||||
}, [poliById]);
|
||||
|
||||
useEffect(() => {
|
||||
if (poli) {
|
||||
setPoliOption(
|
||||
poli.map((item) => {
|
||||
return {
|
||||
value: item.id,
|
||||
label: item.nama_poli,
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
}, [poli]);
|
||||
|
||||
useEffect(() => {
|
||||
if (dokterId) {
|
||||
dispatch(getDokterById(dokterId));
|
||||
}
|
||||
}, [dokterId, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (dokterById) {
|
||||
setEditDokterForm({
|
||||
nama: dokterById.nama,
|
||||
alamat: dokterById.alamat,
|
||||
no_hp: dokterById.no_hp,
|
||||
username: dokterById.username,
|
||||
role: "dokter",
|
||||
});
|
||||
}
|
||||
}, [dokterById]);
|
||||
|
||||
useEffect(() => {
|
||||
setEditDokterForm({ ...editDokterForm, id_poli: poliEditSelected?.value });
|
||||
}, [poliEditSelected]);
|
||||
|
||||
useEffect(() => {
|
||||
if (role !== "admin") {
|
||||
window.location.href = "/";
|
||||
}
|
||||
}, [role]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="container min-h-[90vh] m-5 my-[3rem]">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Dokter</h1>
|
||||
<h1>{pathName}</h1>
|
||||
</div>
|
||||
<form className="mt-5" onSubmit={handleSubmit}>
|
||||
<Input
|
||||
label="Nama"
|
||||
type="text"
|
||||
placeholder="Nama Dokter"
|
||||
value={dokterForm.nama}
|
||||
onChange={(e) =>
|
||||
setDokterForm({ ...dokterForm, nama: e.target.value })
|
||||
}
|
||||
/>
|
||||
<TextArea
|
||||
label="Alamat"
|
||||
type="text"
|
||||
placeholder="Alamat Dokter"
|
||||
value={dokterForm.alamat}
|
||||
onChange={(e) =>
|
||||
setDokterForm({ ...dokterForm, alamat: e.target.value })
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="No HP"
|
||||
type="number"
|
||||
placeholder="No. HP Dokter"
|
||||
value={dokterForm.no_hp}
|
||||
onChange={(e) =>
|
||||
setDokterForm({ ...dokterForm, no_hp: e.target.value })
|
||||
}
|
||||
/>
|
||||
<ReactSelect
|
||||
title="Poli"
|
||||
data={poliOption}
|
||||
value={poliSelected}
|
||||
onChange={(e) => setPoliSelected(e)}
|
||||
/>
|
||||
<Input
|
||||
label="Username"
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
value={dokterForm.username}
|
||||
onChange={(e) =>
|
||||
setDokterForm({ ...dokterForm, username: e.target.value })
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="Password"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={dokterForm.password}
|
||||
onChange={(e) =>
|
||||
setDokterForm({ ...dokterForm, password: e.target.value })
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end mt-4">
|
||||
<button
|
||||
className="bg-slate-500 p-2 px-3 text-sm rounded text-white"
|
||||
type="reset"
|
||||
>
|
||||
Reset Form
|
||||
</button>
|
||||
{loading ? (
|
||||
<button
|
||||
className="bg-[#1B4242] p-2 text-sm px-3 rounded text-white mx-2 cursor-default "
|
||||
type="button"
|
||||
>
|
||||
<Spinner color="success" />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="bg-[#1B4242] p-2 text-sm px-3 rounded text-white mx-2"
|
||||
type="submit"
|
||||
>
|
||||
Tambah
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div className="card bg-white p-5 mt-[3rem]">
|
||||
<div className="card-body">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium"> Dokter</h1>
|
||||
</div>
|
||||
<div className="overflow-x-auto mt-4">
|
||||
<Table striped>
|
||||
<Table.Head>
|
||||
<Table.HeadCell>No</Table.HeadCell>
|
||||
<Table.HeadCell>Nama</Table.HeadCell>
|
||||
<Table.HeadCell>Alamat</Table.HeadCell>
|
||||
<Table.HeadCell>No. HP</Table.HeadCell>
|
||||
<Table.HeadCell>Poli</Table.HeadCell>
|
||||
<Table.HeadCell>Aksi</Table.HeadCell>
|
||||
</Table.Head>
|
||||
<Table.Body className="divide-y">
|
||||
{allDokter.map((item, index) => (
|
||||
<Table.Row key={index}>
|
||||
<Table.Cell>{index + 1}</Table.Cell>
|
||||
<Table.Cell>Dr. {item.nama}</Table.Cell>
|
||||
<Table.Cell>{item.alamat}</Table.Cell>
|
||||
<Table.Cell>{item.no_hp}</Table.Cell>
|
||||
<Table.Cell>{item?.poli?.nama_poli}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<button
|
||||
className="bg-[#5C8374] p-2 rounded text-white mx-2"
|
||||
onClick={() => handleEdit(item.id)}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
<Modals
|
||||
openModal={editDokter}
|
||||
setOpenModal={() => setEditDokter(false)}
|
||||
title="Edit Dokter"
|
||||
buttonClose={false}
|
||||
body={
|
||||
<form onSubmit={handleUpdate}>
|
||||
<Input
|
||||
label="Nama"
|
||||
type="text"
|
||||
placeholder="Nama Dokter"
|
||||
value={editDokterForm?.nama}
|
||||
onChange={(e) =>
|
||||
setEditDokterForm({
|
||||
...editDokterForm,
|
||||
nama: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<TextArea
|
||||
label="Alamat"
|
||||
type="text"
|
||||
placeholder="Alamat Dokter"
|
||||
value={editDokterForm?.alamat}
|
||||
onChange={(e) =>
|
||||
setEditDokterForm({
|
||||
...editDokterForm,
|
||||
alamat: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="No HP"
|
||||
type="number"
|
||||
placeholder="No. HP Dokter"
|
||||
value={editDokterForm?.no_hp}
|
||||
onChange={(e) =>
|
||||
setEditDokterForm({
|
||||
...editDokterForm,
|
||||
no_hp: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<ReactSelect
|
||||
title="Poli"
|
||||
data={poliOption}
|
||||
value={poliEditSelected}
|
||||
onChange={(e) => setPoliEditSelected(e)}
|
||||
/>
|
||||
<Input
|
||||
label="Username"
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
value={editDokterForm?.username}
|
||||
onChange={(e) =>
|
||||
setEditDokterForm({
|
||||
...editDokterForm,
|
||||
username: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<div className="flex justify-end">
|
||||
{loading ? (
|
||||
<button
|
||||
className="bg-[#1B4242] p-2 text-sm px-3 mt-5 rounded text-white"
|
||||
type="button"
|
||||
disabled
|
||||
>
|
||||
<Spinner color="white" />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="bg-[#1B4242] p-2 text-sm px-3 mt-5 rounded text-white"
|
||||
type="submit"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dokter;
|
||||
241
src/pages/Admin/Obat.jsx
Normal file
241
src/pages/Admin/Obat.jsx
Normal file
@ -0,0 +1,241 @@
|
||||
import Input from "../../components/Input";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { Table } from "flowbite-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import {
|
||||
deleteObat,
|
||||
getObat,
|
||||
storeObat,
|
||||
getObatById,
|
||||
updateObat,
|
||||
} from "../../config/Redux/Action/obatAction";
|
||||
import Modals from "../../components/Modals";
|
||||
|
||||
const Obat = () => {
|
||||
const pathName = useLocation().pathname;
|
||||
const dispatch = useDispatch();
|
||||
const [editObat, setEditObat] = useState(false);
|
||||
const { obat, obatById } = useSelector((state) => state.obatReducer);
|
||||
const [obatForm, setObatForm] = useState({
|
||||
nama_obat: "",
|
||||
kemasan: "",
|
||||
harga: "",
|
||||
});
|
||||
const [obatEditForm, setObatEditForm] = useState({
|
||||
nama_obat: "",
|
||||
kemasan: "",
|
||||
harga: "",
|
||||
});
|
||||
const [idObat, setIdObat] = useState("");
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(storeObat(obatForm));
|
||||
setObatForm({
|
||||
nama_obat: "",
|
||||
kemasan: "",
|
||||
harga: "",
|
||||
});
|
||||
};
|
||||
|
||||
const handleEdit = (id) => {
|
||||
setEditObat(true);
|
||||
setIdObat(id);
|
||||
};
|
||||
|
||||
const handleDelete = (id) => {
|
||||
dispatch(deleteObat(id));
|
||||
};
|
||||
|
||||
const handleSumbitEdit = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(updateObat(idObat, obatEditForm));
|
||||
setEditObat(false);
|
||||
};
|
||||
|
||||
const formatPriceInRupiah = (price) => {
|
||||
return new Intl.NumberFormat("id-ID", {
|
||||
style: "currency",
|
||||
currency: "IDR",
|
||||
}).format(price);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getObat());
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (idObat) {
|
||||
dispatch(getObatById(idObat));
|
||||
}
|
||||
}, [idObat, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
setObatEditForm({
|
||||
nama_obat: obatById.nama_obat,
|
||||
kemasan: obatById.kemasan,
|
||||
harga: obatById.harga,
|
||||
});
|
||||
}, [obatById]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="container min-h-[90vh] m-5 my-[3rem]">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Obat</h1>
|
||||
<h1>{pathName}</h1>
|
||||
</div>
|
||||
<form className="mt-5" onSubmit={handleSubmit}>
|
||||
<Input
|
||||
label="Nama Obat"
|
||||
type="text"
|
||||
placeholder="Nama Obat"
|
||||
name={"nama_obat"}
|
||||
value={obatForm.nama_obat}
|
||||
onChange={(e) =>
|
||||
setObatForm({ ...obatForm, nama_obat: e.target.value })
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="Kemasan"
|
||||
type="text"
|
||||
placeholder="Kemasan"
|
||||
name={"kemasan"}
|
||||
value={obatForm.kemasan}
|
||||
onChange={(e) =>
|
||||
setObatForm({ ...obatForm, kemasan: e.target.value })
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="Harga"
|
||||
type="number"
|
||||
placeholder="Harga"
|
||||
name={"harga"}
|
||||
value={obatForm.harga}
|
||||
onChange={(e) =>
|
||||
setObatForm({ ...obatForm, harga: e.target.value })
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end mt-4">
|
||||
<button
|
||||
className="bg-slate-500 p-2 px-3 text-sm rounded-md text-white"
|
||||
type="reset"
|
||||
>
|
||||
Reset Form
|
||||
</button>
|
||||
<button
|
||||
className="bg-[#1B4242] p-2 px-3 text-sm rounded-md text-white mx-2"
|
||||
type="submit"
|
||||
>
|
||||
Tambah
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div className="card bg-white p-5 mt-[3rem]">
|
||||
<div className="card-body">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Poli</h1>
|
||||
</div>
|
||||
<div className="overflow-auto mt-4 max-h-[40vh]">
|
||||
<Table striped>
|
||||
<Table.Head>
|
||||
<Table.HeadCell>No</Table.HeadCell>
|
||||
<Table.HeadCell width="40%">Nama Obat</Table.HeadCell>
|
||||
<Table.HeadCell>Kemasan</Table.HeadCell>
|
||||
<Table.HeadCell>Harga</Table.HeadCell>
|
||||
<Table.HeadCell>Aksi</Table.HeadCell>
|
||||
</Table.Head>
|
||||
<Table.Body className="divide-y">
|
||||
{obat.map((item, index) => (
|
||||
<Table.Row key={item.id}>
|
||||
<Table.Cell>{index + 1}</Table.Cell>
|
||||
<Table.Cell>{item.nama_obat}</Table.Cell>
|
||||
<Table.Cell>{item.kemasan}</Table.Cell>
|
||||
<Table.Cell>{formatPriceInRupiah(item.harga)}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<button
|
||||
className="bg-[#5C8374] p-2 rounded text-white mx-2"
|
||||
onClick={() => handleEdit(item.id)}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
className="bg-red-500 p-2 rounded text-white mx-2"
|
||||
onClick={() => handleDelete(item.id)}
|
||||
>
|
||||
Hapus
|
||||
</button>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
<Modals
|
||||
openModal={editObat}
|
||||
setOpenModal={() => setEditObat(false)}
|
||||
title={"Edit Obat"}
|
||||
buttonClose={false}
|
||||
body={
|
||||
<form className="flex flex-col" onSubmit={handleSumbitEdit}>
|
||||
<Input
|
||||
label="Nama Obat"
|
||||
type="text"
|
||||
placeholder="Nama Obat"
|
||||
name={"nama_obat"}
|
||||
value={obatEditForm.nama_obat}
|
||||
onChange={(e) =>
|
||||
setObatEditForm({
|
||||
...obatEditForm,
|
||||
nama_obat: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="Kemasan"
|
||||
type="text"
|
||||
placeholder="Kemasan"
|
||||
name={"kemasan"}
|
||||
value={obatEditForm.kemasan}
|
||||
onChange={(e) =>
|
||||
setObatEditForm({
|
||||
...obatEditForm,
|
||||
kemasan: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="Harga"
|
||||
type="number"
|
||||
placeholder="Harga"
|
||||
name={"harga"}
|
||||
value={obatEditForm.harga}
|
||||
onChange={(e) =>
|
||||
setObatEditForm({
|
||||
...obatEditForm,
|
||||
harga: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<div className="flex justify-end mt-4">
|
||||
<button
|
||||
className="bg-[#1B4242] p-2 px-3 rounded text-white mx-2"
|
||||
type="submit"
|
||||
>
|
||||
Update
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Obat;
|
||||
298
src/pages/Admin/Pasien.jsx
Normal file
298
src/pages/Admin/Pasien.jsx
Normal file
@ -0,0 +1,298 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import Input from "../../components/Input";
|
||||
import TextArea from "../../components/TextArea";
|
||||
import { Spinner, Table } from "flowbite-react";
|
||||
import { useLocation, useOutletContext } from "react-router-dom";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import {
|
||||
getAllPasien,
|
||||
getPasienById,
|
||||
registerPasien,
|
||||
updatePasien,
|
||||
} from "../../config/Redux/action/pasienAction";
|
||||
import Modals from "../../components/Modals";
|
||||
|
||||
const Pasien = () => {
|
||||
const pathName = useLocation().pathname;
|
||||
const [role] = useOutletContext();
|
||||
const dispatch = useDispatch();
|
||||
const { pasienAll, pasienById } = useSelector((state) => state.pasienReducer);
|
||||
const [pasienForm, setPasienForm] = useState({
|
||||
nama: "",
|
||||
alamat: "",
|
||||
no_ktp: "",
|
||||
no_hp: "",
|
||||
username: "",
|
||||
password: "",
|
||||
});
|
||||
const [editPasienForm, setEditPasienForm] = useState({
|
||||
nama: pasienById?.nama,
|
||||
alamat: pasienById?.alamat,
|
||||
no_ktp: pasienById?.no_ktp,
|
||||
no_hp: pasienById?.no_hp,
|
||||
username: pasienById?.username,
|
||||
});
|
||||
const [editPasien, setEditPasien] = useState(false);
|
||||
const [idPasien, setIdPasien] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleSumit = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(registerPasien(pasienForm));
|
||||
};
|
||||
|
||||
const handleEdit = (id) => {
|
||||
setEditPasien(true);
|
||||
setIdPasien(id);
|
||||
};
|
||||
|
||||
const handleUpdate = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(updatePasien(idPasien, editPasienForm, setLoading, setEditPasien));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (idPasien) {
|
||||
dispatch(getPasienById(idPasien));
|
||||
}
|
||||
}, [dispatch, idPasien]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getAllPasien());
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
setEditPasienForm({
|
||||
nama: pasienById?.nama,
|
||||
alamat: pasienById?.alamat,
|
||||
no_ktp: pasienById?.no_ktp,
|
||||
no_hp: pasienById?.no_hp,
|
||||
username: pasienById?.username,
|
||||
});
|
||||
}, [pasienById]);
|
||||
|
||||
useEffect(() => {
|
||||
if (role !== "admin") {
|
||||
window.location.href = "/";
|
||||
}
|
||||
}, [role]);
|
||||
return (
|
||||
<>
|
||||
<div className="container min-h-[90vh] m-5 my-[3rem]">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Pasien</h1>
|
||||
<h1>{pathName}</h1>
|
||||
</div>
|
||||
<form onSubmit={handleSumit} className="mt-5">
|
||||
<Input
|
||||
label="Nama Pasien"
|
||||
type="text"
|
||||
placeholder="Nama Pasien"
|
||||
onChange={(e) =>
|
||||
setPasienForm({ ...pasienForm, nama: e.target.value })
|
||||
}
|
||||
/>
|
||||
<TextArea
|
||||
label="Alamat"
|
||||
type="text"
|
||||
placeholder="Alamat Pasien"
|
||||
onChange={(e) =>
|
||||
setPasienForm({ ...pasienForm, alamat: e.target.value })
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="No. KTP Pasien"
|
||||
type="text"
|
||||
placeholder="No. KTP Pasien"
|
||||
onChange={(e) =>
|
||||
setPasienForm({ ...pasienForm, no_ktp: e.target.value })
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="No. HP Pasien"
|
||||
type="text"
|
||||
placeholder="No. HP Pasien"
|
||||
onChange={(e) =>
|
||||
setPasienForm({ ...pasienForm, no_hp: e.target.value })
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="Username"
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
onChange={(e) =>
|
||||
setPasienForm({ ...pasienForm, username: e.target.value })
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="Password"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
onChange={(e) =>
|
||||
setPasienForm({ ...pasienForm, password: e.target.value })
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end mt-4">
|
||||
<button
|
||||
className="bg-slate-500 p-2 rounded text-white px-3 text-sm"
|
||||
type="reset"
|
||||
>
|
||||
Reset Form
|
||||
</button>
|
||||
<button
|
||||
className="bg-[#1B4242] p-2 rounded text-white mx-2 px-3 text-sm"
|
||||
type="submit"
|
||||
>
|
||||
Tambah
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div className="card bg-white p-5 mt-[3rem]">
|
||||
<div className="card-body">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Pasien</h1>
|
||||
</div>
|
||||
<div className="overflow-x-auto mt-4">
|
||||
<Table striped>
|
||||
<Table.Head>
|
||||
<Table.HeadCell>No</Table.HeadCell>
|
||||
<Table.HeadCell>Nama</Table.HeadCell>
|
||||
<Table.HeadCell width="30%">Alamat</Table.HeadCell>
|
||||
<Table.HeadCell>No. KTP</Table.HeadCell>
|
||||
<Table.HeadCell>No. HP</Table.HeadCell>
|
||||
<Table.HeadCell>No. RM</Table.HeadCell>
|
||||
<Table.HeadCell>Aksi</Table.HeadCell>
|
||||
</Table.Head>
|
||||
<Table.Body className="divide-y">
|
||||
{pasienAll.map((pasien, index) => (
|
||||
<>
|
||||
<Table.Row key={index}>
|
||||
<Table.Cell>{index + 1}</Table.Cell>
|
||||
<Table.Cell>{pasien.nama}</Table.Cell>
|
||||
<Table.Cell>{pasien.alamat}</Table.Cell>
|
||||
<Table.Cell>{pasien.no_ktp}</Table.Cell>
|
||||
<Table.Cell>{pasien.no_hp}</Table.Cell>
|
||||
<Table.Cell>{pasien.no_rm}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<button
|
||||
className="bg-[#5C8374] p-2 rounded text-white mx-2"
|
||||
onClick={() => handleEdit(pasien.id)}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
<Modals
|
||||
openModal={editPasien}
|
||||
setOpenModal={setEditPasien}
|
||||
title="Edit Pasien"
|
||||
buttonClose={false}
|
||||
body={
|
||||
<form onSubmit={handleUpdate}>
|
||||
<Input
|
||||
label="No. RM Pasien"
|
||||
type="text"
|
||||
placeholder="No. RM Pasien"
|
||||
value={pasienById?.no_rm}
|
||||
disabled
|
||||
/>
|
||||
<Input
|
||||
label="Nama Pasien"
|
||||
type="text"
|
||||
placeholder="Nama Pasien"
|
||||
onChange={(e) =>
|
||||
setEditPasienForm({
|
||||
...editPasienForm,
|
||||
nama: e.target.value,
|
||||
})
|
||||
}
|
||||
value={editPasienForm.nama}
|
||||
/>
|
||||
<TextArea
|
||||
label="Alamat"
|
||||
type="text"
|
||||
placeholder="Alamat Pasien"
|
||||
onChange={(e) =>
|
||||
setEditPasienForm({
|
||||
...editPasienForm,
|
||||
alamat: e.target.value,
|
||||
})
|
||||
}
|
||||
value={editPasienForm.alamat}
|
||||
/>
|
||||
<Input
|
||||
label="No. KTP Pasien"
|
||||
type="text"
|
||||
placeholder="No. KTP Pasien"
|
||||
onChange={(e) =>
|
||||
setEditPasienForm({
|
||||
...editPasienForm,
|
||||
no_ktp: e.target.value,
|
||||
})
|
||||
}
|
||||
value={editPasienForm.no_ktp}
|
||||
/>
|
||||
<Input
|
||||
label="No. HP Pasien"
|
||||
type="text"
|
||||
placeholder="No. HP Pasien"
|
||||
onChange={(e) =>
|
||||
setEditPasienForm({
|
||||
...editPasienForm,
|
||||
no_hp: e.target.value,
|
||||
})
|
||||
}
|
||||
value={editPasienForm.no_hp}
|
||||
/>
|
||||
<Input
|
||||
label="Username"
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
onChange={(e) =>
|
||||
setEditPasienForm({
|
||||
...editPasienForm,
|
||||
username: e.target.value,
|
||||
})
|
||||
}
|
||||
value={editPasienForm.username}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end mt-4">
|
||||
{loading ? (
|
||||
<button
|
||||
className="bg-[#1B4242] p-2 rounded text-white px-3 text-sm"
|
||||
type="button"
|
||||
disabled
|
||||
>
|
||||
<Spinner
|
||||
color="success"
|
||||
aria-label="Success spinner example"
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="bg-[#1B4242] p-2 rounded text-white px-3 text-sm"
|
||||
type="submit"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Pasien;
|
||||
214
src/pages/Admin/Poli.jsx
Normal file
214
src/pages/Admin/Poli.jsx
Normal file
@ -0,0 +1,214 @@
|
||||
import TextArea from "../../components/TextArea";
|
||||
import { useLocation, useOutletContext } from "react-router-dom";
|
||||
import { Table } from "flowbite-react";
|
||||
import Input from "../../components/Input";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import {
|
||||
addPoli,
|
||||
deletePoli,
|
||||
getPoli,
|
||||
getPoliById,
|
||||
updatePoli,
|
||||
} from "../../config/Redux/Action/poliAction";
|
||||
import Modals from "../../components/Modals";
|
||||
|
||||
const Poli = () => {
|
||||
const pathName = useLocation().pathname;
|
||||
const [role] = useOutletContext();
|
||||
const dispatch = useDispatch();
|
||||
const { poli, poliById } = useSelector((state) => state.poliReducer);
|
||||
const [edit, setEdit] = useState(false);
|
||||
const [poliForm, setPoliForm] = useState({
|
||||
nama_poli: "",
|
||||
keterangan: "",
|
||||
});
|
||||
const [poliFormEdit, setPoliFormEdit] = useState({
|
||||
nama_poli: "",
|
||||
keterangan: "",
|
||||
});
|
||||
const [idPoli, setIdPoli] = useState("");
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(addPoli(poliForm));
|
||||
setPoliForm({
|
||||
nama_poli: "",
|
||||
keterangan: "",
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (id) => {
|
||||
dispatch(deletePoli(id));
|
||||
};
|
||||
|
||||
const handleEdit = (id) => {
|
||||
setEdit(true);
|
||||
setIdPoli(id);
|
||||
};
|
||||
|
||||
const handleSumbitEdit = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(updatePoli(idPoli, poliFormEdit));
|
||||
setEdit(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getPoli());
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (poliById) {
|
||||
setPoliFormEdit({
|
||||
nama_poli: poliById.nama_poli,
|
||||
keterangan: poliById.keterangan,
|
||||
});
|
||||
}
|
||||
}, [poliById]);
|
||||
|
||||
useEffect(() => {
|
||||
if (idPoli) {
|
||||
dispatch(getPoliById(idPoli));
|
||||
}
|
||||
}, [idPoli, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (role !== "admin") {
|
||||
window.location.href = "/";
|
||||
}
|
||||
}, [role]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="container min-h-[90vh] m-5 my-[3rem]">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Poli</h1>
|
||||
<h1>{pathName}</h1>
|
||||
</div>
|
||||
<form action="" className="mt-5" onSubmit={handleSubmit}>
|
||||
<Input
|
||||
label="Nama Poli"
|
||||
type="text"
|
||||
placeholder="Nama Poli"
|
||||
name="nama_poli"
|
||||
value={poliForm.nama_poli}
|
||||
onChange={(e) =>
|
||||
setPoliForm({ ...poliForm, nama_poli: e.target.value })
|
||||
}
|
||||
/>
|
||||
<TextArea
|
||||
label="Keterangan"
|
||||
type="text"
|
||||
placeholder="Keterangan"
|
||||
name="keterangan"
|
||||
value={poliForm.keterangan}
|
||||
onChange={(e) =>
|
||||
setPoliForm({ ...poliForm, keterangan: e.target.value })
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end mt-4">
|
||||
<button className="bg-slate-500 p-2 px-3 text-sm rounded-md text-white">
|
||||
Reset Form
|
||||
</button>
|
||||
<button
|
||||
className="bg-[#1B4242] p-2 px-3 text-sm rounded-md text-white mx-2"
|
||||
type="submit"
|
||||
>
|
||||
Tambah
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div className="card bg-white p-5 mt-[3rem]">
|
||||
<div className="card-body">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Poli</h1>
|
||||
</div>
|
||||
<div className="overflow-x-auto mt-4">
|
||||
<Table striped>
|
||||
<Table.Head>
|
||||
<Table.HeadCell>No</Table.HeadCell>
|
||||
<Table.HeadCell>Nama Poli</Table.HeadCell>
|
||||
<Table.HeadCell width="40%">Keterangan</Table.HeadCell>
|
||||
<Table.HeadCell>Aksi</Table.HeadCell>
|
||||
</Table.Head>
|
||||
<Table.Body className="divide-y">
|
||||
{poli.map((item, index) => (
|
||||
<>
|
||||
<Table.Row key={index}>
|
||||
<Table.Cell>{index + 1}</Table.Cell>
|
||||
<Table.Cell>{item.nama_poli}</Table.Cell>
|
||||
<Table.Cell>{item.keterangan}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<button
|
||||
className="bg-[#5C8374] p-2 rounded text-white mx-2"
|
||||
onClick={() => handleEdit(item.id)}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
className="bg-red-500 p-2 rounded text-white mx-2"
|
||||
onClick={() => handleDelete(item.id)}
|
||||
>
|
||||
Hapus
|
||||
</button>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
<Modals
|
||||
openModal={edit}
|
||||
setOpenModal={setEdit}
|
||||
title="Edit Poli"
|
||||
buttonClose={false}
|
||||
body={
|
||||
<form className="mt-[-1.5rem]" onSubmit={handleSumbitEdit}>
|
||||
<Input
|
||||
label="Nama Poli"
|
||||
type="text"
|
||||
placeholder="Nama Poli"
|
||||
name="nama_poli"
|
||||
value={poliFormEdit.nama_poli}
|
||||
onChange={(e) =>
|
||||
setPoliFormEdit({
|
||||
...poliFormEdit,
|
||||
nama_poli: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<TextArea
|
||||
label="Keterangan"
|
||||
type="text"
|
||||
placeholder="Keterangan"
|
||||
name="keterangan"
|
||||
value={poliFormEdit.keterangan}
|
||||
onChange={(e) =>
|
||||
setPoliFormEdit({
|
||||
...poliFormEdit,
|
||||
keterangan: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<div className="flex justify-end mt-4">
|
||||
<button
|
||||
className="bg-[#1B4242] p-2 px-3 text-sm rounded-md text-white mx-2"
|
||||
type="submit"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Poli;
|
||||
513
src/pages/Auth/LoginPortal.jsx
Normal file
513
src/pages/Auth/LoginPortal.jsx
Normal file
@ -0,0 +1,513 @@
|
||||
import { FaAddressCard, FaRegAddressCard, FaUserAlt } from "react-icons/fa";
|
||||
import { FaLocationDot } from "react-icons/fa6";
|
||||
import { MdOutlineArrowRightAlt } from "react-icons/md";
|
||||
import Modals from "../../components/Modals";
|
||||
import { useState } from "react";
|
||||
import { Checkbox, Label, Spinner, TextInput } from "flowbite-react";
|
||||
import { HiLockClosed, HiMail } from "react-icons/hi";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { loginAdmin } from "../../config/Redux/Action/adminAction";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { loginDokter } from "../../config/Redux/Action/dokterAction";
|
||||
import {
|
||||
loginPasien,
|
||||
registerPasien,
|
||||
} from "../../config/Redux/Action/pasienAction";
|
||||
import { ToastContainer } from "react-toastify";
|
||||
|
||||
const LoginPortal = () => {
|
||||
const dispatch = useDispatch();
|
||||
const nav = useNavigate();
|
||||
const [pasien, setPasien] = useState(false);
|
||||
const [dokter, setDokter] = useState(false);
|
||||
const [admin, setAdmin] = useState(false);
|
||||
const [register, setRegister] = useState(false);
|
||||
const [adminForm, setAdminForm] = useState({
|
||||
username: "",
|
||||
password: "",
|
||||
});
|
||||
const [dokterForm, setDokterForm] = useState({
|
||||
username: "",
|
||||
password: "",
|
||||
});
|
||||
const [pasienRegister, setPasienRegister] = useState({
|
||||
nama: "",
|
||||
no_ktp: "",
|
||||
no_hp: "",
|
||||
username: "",
|
||||
password: "",
|
||||
alamat: "",
|
||||
});
|
||||
const [pasienForm, setPasienForm] = useState({
|
||||
username: "",
|
||||
password: "",
|
||||
});
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleLoginAdmin = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(loginAdmin(adminForm, nav, setLoading, setAdmin));
|
||||
};
|
||||
|
||||
const handleLoginDokter = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(loginDokter(dokterForm, nav, setLoading, setDokter));
|
||||
};
|
||||
|
||||
const handleRegisterPasien = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(registerPasien(pasienRegister, setRegister, setLoading));
|
||||
setPasienRegister({
|
||||
nama: "",
|
||||
no_ktp: "",
|
||||
no_hp: "",
|
||||
username: "",
|
||||
password: "",
|
||||
alamat: "",
|
||||
});
|
||||
};
|
||||
|
||||
const handleLoginPasien = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(loginPasien(pasienForm, nav, setLoading, setPasien));
|
||||
setPasienForm({
|
||||
username: "",
|
||||
password: "",
|
||||
});
|
||||
setRegister(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ToastContainer />
|
||||
<div className=" bg-[#00008B]">
|
||||
<div className="flex items-center justify-center flex-col container mx-auto py-[10rem] ">
|
||||
<h1 className="text-4xl font-bold text-white mb-[5rem]">
|
||||
{" "}
|
||||
Login Portal{" "}
|
||||
</h1>
|
||||
<div className="w-full flex flex-col items-center">
|
||||
{/* Login Admin */}
|
||||
<div className="flex flex-col justify-center items-start bg-white text-black p-5 rounded shadow-lg w-[50rem]">
|
||||
<div className="bg-[#00008B] p-4 rounded">
|
||||
<FaUserAlt size={25} color="white" />
|
||||
</div>
|
||||
<h1 className=" text-2xl font-bold mt-2">Login Sebagai Pasien</h1>
|
||||
<p className=" text-lg">
|
||||
Apabila Anda adalah pasien, silahkan login disini.
|
||||
</p>
|
||||
<button
|
||||
className="bg-white text-[#12486B] rounded-full py-2 mt-3 flex items-center hover:underline"
|
||||
onClick={() => setPasien(true)}
|
||||
>
|
||||
Klik Disini <MdOutlineArrowRightAlt className="ml-2" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex flex-col justify-center items-start bg-white text-black p-5 rounded shadow-lg w-[50rem] my-3">
|
||||
<div className="bg-[#00008B] p-4 rounded">
|
||||
<FaUserAlt size={25} color="white" />
|
||||
</div>
|
||||
<h1 className=" text-2xl font-bold mt-2">Login Sebagai Dokter</h1>
|
||||
<p className=" text-lg">
|
||||
Apabila Anda adalah dokter, silahkan login disini.
|
||||
</p>
|
||||
<button
|
||||
className="bg-white text-[#12486B] rounded-full py-2 mt-3 flex items-center hover:underline"
|
||||
onClick={() => setDokter(true)}
|
||||
>
|
||||
Klik Disini <MdOutlineArrowRightAlt className="ml-2" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex flex-col justify-center items-start bg-white text-black p-5 rounded shadow-lg w-[50rem]">
|
||||
<div className="bg-[#00008B] p-4 rounded">
|
||||
<FaUserAlt size={25} color="white" />
|
||||
</div>
|
||||
<h1 className=" text-2xl font-bold mt-2">Login Sebagai Admin</h1>
|
||||
<p className=" text-lg">
|
||||
Apabila Anda adalah admin, silahkan login disini.
|
||||
</p>
|
||||
<button
|
||||
className="bg-white text-[#12486B] rounded-full py-2 mt-3 flex items-center hover:underline"
|
||||
onClick={() => setAdmin(true)}
|
||||
>
|
||||
Klik Disini <MdOutlineArrowRightAlt className="ml-2" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Modals
|
||||
openModal={pasien}
|
||||
setOpenModal={setPasien}
|
||||
size="lg"
|
||||
title="Login Pasien"
|
||||
buttonClose={false}
|
||||
body={
|
||||
<>
|
||||
<form className="flex flex-col" onSubmit={handleLoginPasien}>
|
||||
<div className="w-full">
|
||||
<TextInput
|
||||
id="username"
|
||||
type="text"
|
||||
icon={HiMail}
|
||||
placeholder="Username"
|
||||
name="username"
|
||||
onChange={(e) => {
|
||||
setPasienForm({
|
||||
...pasienForm,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full my-3">
|
||||
<TextInput
|
||||
id="password"
|
||||
type="password"
|
||||
icon={HiLockClosed}
|
||||
placeholder="Password"
|
||||
name="password"
|
||||
onChange={(e) => {
|
||||
setPasienForm({
|
||||
...pasienForm,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-end text-sm text-[#132043] hover:underline my-1">
|
||||
<button onClick={() => setRegister(true)}>
|
||||
Belum punya akun?
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox id="accept" />
|
||||
<Label htmlFor="accept" className="flex">
|
||||
Remember Me
|
||||
</Label>
|
||||
</div>
|
||||
{loading ? (
|
||||
<button
|
||||
className="bg-[#12486B] text-white p-2 px-4 rounded-lg w-fit"
|
||||
type="button"
|
||||
>
|
||||
<Spinner
|
||||
color="success"
|
||||
aria-label="Success spinner example"
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="bg-[#1E90FF] text-white p-2 px-4 rounded-lg w-fit"
|
||||
type="submit"
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Modals
|
||||
openModal={register}
|
||||
setOpenModal={setRegister}
|
||||
size="lg"
|
||||
title="Register Pasien"
|
||||
buttonClose={false}
|
||||
body={
|
||||
<>
|
||||
<form className="flex flex-col" onSubmit={handleRegisterPasien}>
|
||||
<div className="w-full">
|
||||
<TextInput
|
||||
id="nama"
|
||||
type="text"
|
||||
icon={HiMail}
|
||||
placeholder="Full Name"
|
||||
name="nama"
|
||||
onChange={(e) => {
|
||||
setPasienRegister({
|
||||
...pasienRegister,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full mt-3">
|
||||
<TextInput
|
||||
id="alamat"
|
||||
type="text"
|
||||
icon={FaLocationDot}
|
||||
placeholder="Alamat"
|
||||
name="alamat"
|
||||
onChange={(e) => {
|
||||
setPasienRegister({
|
||||
...pasienRegister,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full mt-3">
|
||||
<TextInput
|
||||
id="no_ktp"
|
||||
type="number"
|
||||
icon={FaAddressCard}
|
||||
placeholder="No. KTP"
|
||||
name="no_ktp"
|
||||
onChange={(e) => {
|
||||
setPasienRegister({
|
||||
...pasienRegister,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full mt-3">
|
||||
<TextInput
|
||||
id="no_hp"
|
||||
type="number"
|
||||
icon={FaRegAddressCard}
|
||||
placeholder="No. HP"
|
||||
name="no_hp"
|
||||
onChange={(e) => {
|
||||
setPasienRegister({
|
||||
...pasienRegister,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full mt-3">
|
||||
<TextInput
|
||||
id="username"
|
||||
type="text"
|
||||
icon={HiMail}
|
||||
placeholder="Username"
|
||||
name="username"
|
||||
onChange={(e) => {
|
||||
setPasienRegister({
|
||||
...pasienRegister,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full my-3">
|
||||
<TextInput
|
||||
id="password"
|
||||
type="password"
|
||||
icon={HiLockClosed}
|
||||
placeholder="Password"
|
||||
name="password"
|
||||
onChange={(e) => {
|
||||
setPasienRegister({
|
||||
...pasienRegister,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox id="accept" />
|
||||
<Label htmlFor="accept" className="flex">
|
||||
I agree with the
|
||||
<a
|
||||
href="#"
|
||||
className="text-cyan-600 hover:underline dark:text-cyan-500"
|
||||
>
|
||||
terms and conditions
|
||||
</a>
|
||||
</Label>
|
||||
</div>
|
||||
{loading ? (
|
||||
<button
|
||||
className="bg-[#12486B] text-white p-2 px-4 rounded-lg w-fit"
|
||||
type="button"
|
||||
>
|
||||
<Spinner
|
||||
color="success"
|
||||
aria-label="Success spinner example"
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="bg-[#1E90FF ] text-white p-2 px-4 rounded-lg w-fit"
|
||||
type="submit"
|
||||
>
|
||||
Register
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex justify-end text-sm text-[#132043] hover:underline my-1">
|
||||
<button
|
||||
onClick={() => setRegister(false)}
|
||||
className="text-[#132043]"
|
||||
type="button"
|
||||
>
|
||||
Sudah memiliki akun?
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Modals
|
||||
openModal={dokter}
|
||||
setOpenModal={setDokter}
|
||||
size="lg"
|
||||
title="Login Dokter"
|
||||
buttonClose={false}
|
||||
body={
|
||||
<>
|
||||
<form className="flex flex-col" onSubmit={handleLoginDokter}>
|
||||
<div className="w-full">
|
||||
<TextInput
|
||||
id="nama"
|
||||
type="text"
|
||||
icon={HiMail}
|
||||
placeholder="Username"
|
||||
name="username"
|
||||
onChange={(e) => {
|
||||
setDokterForm({
|
||||
...dokterForm,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full my-3">
|
||||
<TextInput
|
||||
id="nama"
|
||||
type="password"
|
||||
icon={HiLockClosed}
|
||||
placeholder="Password"
|
||||
name="password"
|
||||
onChange={(e) => {
|
||||
setDokterForm({
|
||||
...dokterForm,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox id="accept" />
|
||||
<Label htmlFor="accept" className="flex">
|
||||
Remember Me
|
||||
</Label>
|
||||
</div>
|
||||
{loading ? (
|
||||
<button
|
||||
className="bg-[#12486B] text-white p-2 px-4 rounded-lg w-fit"
|
||||
type="button"
|
||||
>
|
||||
<Spinner
|
||||
color="success"
|
||||
aria-label="Success spinner example"
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="bg-[#1E90FF] text-white p-2 px-4 rounded-lg w-fit"
|
||||
type="submit"
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Modals
|
||||
openModal={admin}
|
||||
setOpenModal={setAdmin}
|
||||
size="lg"
|
||||
title="Login Admin"
|
||||
buttonClose={false}
|
||||
body={
|
||||
<>
|
||||
<form className="flex flex-col" onSubmit={handleLoginAdmin}>
|
||||
<div className="w-full">
|
||||
<TextInput
|
||||
id="nama"
|
||||
type="text"
|
||||
icon={HiMail}
|
||||
placeholder="Username"
|
||||
name="username"
|
||||
onChange={(e) => {
|
||||
setAdminForm({
|
||||
...adminForm,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full my-3">
|
||||
<TextInput
|
||||
id="nama"
|
||||
type="password"
|
||||
icon={HiLockClosed}
|
||||
placeholder="Password"
|
||||
name="password"
|
||||
onChange={(e) => {
|
||||
setAdminForm({
|
||||
...adminForm,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox id="accept" />
|
||||
<Label htmlFor="accept" className="flex">
|
||||
Remember Me
|
||||
</Label>
|
||||
</div>
|
||||
{loading ? (
|
||||
<button
|
||||
className="bg-[#12486B] text-white p-2 px-4 rounded-lg w-fit"
|
||||
type="button"
|
||||
>
|
||||
<Spinner
|
||||
color="success"
|
||||
aria-label="Success spinner example"
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="bg-[#1E90FF] text-white p-2 px-4 rounded-lg w-fit"
|
||||
type="submit"
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPortal;
|
||||
11
src/pages/Dokter/DashboardDokter.jsx
Normal file
11
src/pages/Dokter/DashboardDokter.jsx
Normal file
@ -0,0 +1,11 @@
|
||||
import transition from "../../transition";
|
||||
|
||||
const DashboardDokter = () => {
|
||||
return (
|
||||
<div className="flex justify-center items-center w-full h-screen">
|
||||
<h1>Dashboard Dokter</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardDokter;
|
||||
129
src/pages/Dokter/JadwalPeriksa/JadwalPeriksa.jsx
Normal file
129
src/pages/Dokter/JadwalPeriksa/JadwalPeriksa.jsx
Normal file
@ -0,0 +1,129 @@
|
||||
import { Table } from "flowbite-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { FaPlus } from "react-icons/fa";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import {
|
||||
Link,
|
||||
useLocation,
|
||||
useOutletContext,
|
||||
useParams,
|
||||
} from "react-router-dom";
|
||||
import { getJadwalPeriksa } from "../../../config/Redux/Action/jadwalPeriksaAction";
|
||||
|
||||
const JadwalPeriksa = () => {
|
||||
const pathName = useLocation().pathname;
|
||||
const [role] = useOutletContext();
|
||||
const { id } = useParams();
|
||||
const dispatch = useDispatch();
|
||||
const { jadwalPeriksa } = useSelector((state) => state.jadwalPeriksaReducer);
|
||||
const [jadwal, setJadwal] = useState([]);
|
||||
|
||||
const timeFormat = (time) => {
|
||||
const date = new Date(`1970-01-01T${time}`);
|
||||
|
||||
if (isNaN(date)) {
|
||||
return "Invalid time";
|
||||
}
|
||||
|
||||
return date.toLocaleTimeString("id-ID", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
};
|
||||
|
||||
const dateFormat = (date) => {
|
||||
const dateObj = new Date(date);
|
||||
const month = dateObj.toLocaleString("default", { month: "long" });
|
||||
const day = dateObj.getDate();
|
||||
const year = dateObj.getFullYear();
|
||||
|
||||
return `${day} ${month} ${year}`;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const updatedJadwal = jadwalPeriksa.map((item) => {
|
||||
if (item.id_dokter === id) {
|
||||
return item;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
setJadwal(updatedJadwal);
|
||||
}, [jadwalPeriksa, id]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getJadwalPeriksa());
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (role !== "dokter") {
|
||||
window.location.href = "/";
|
||||
}
|
||||
}, [role]);
|
||||
|
||||
return (
|
||||
<div className="container min-h-[90vh] m-5 my-[3rem]">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Jadwal Periksa</h1>
|
||||
<h1>{pathName}</h1>
|
||||
</div>
|
||||
<div className="card bg-white p-5 mt-[3rem]">
|
||||
<div className="card-body">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Daftar Jadwal Periksa</h1>
|
||||
<Link
|
||||
className="bg-[#1B4242] p-2 rounded text-white mx-2 flex items-center"
|
||||
to={"/dokter/jadwal-periksa/kelola-jadwal/" + id}
|
||||
>
|
||||
<FaPlus className="mr-2" /> Tambah
|
||||
</Link>
|
||||
</div>
|
||||
<div className="overflow-x-auto mt-4">
|
||||
<Table striped>
|
||||
<Table.Head>
|
||||
<Table.HeadCell>No</Table.HeadCell>
|
||||
<Table.HeadCell>Nama Dokter</Table.HeadCell>
|
||||
<Table.HeadCell>Hari</Table.HeadCell>
|
||||
<Table.HeadCell>Jam Mulai</Table.HeadCell>
|
||||
<Table.HeadCell>Jam Selesai</Table.HeadCell>
|
||||
<Table.HeadCell>Tanggal</Table.HeadCell>
|
||||
<Table.HeadCell>Status</Table.HeadCell>
|
||||
|
||||
<Table.HeadCell>Aksi</Table.HeadCell>
|
||||
</Table.Head>
|
||||
<Table.Body className="divide-y">
|
||||
{jadwal
|
||||
.filter((item) => item.id_dokter === id)
|
||||
.map((item, index) => {
|
||||
return (
|
||||
<Table.Row key={index}>
|
||||
<Table.Cell>{index + 1}</Table.Cell>
|
||||
<Table.Cell>{item.dokter.nama}</Table.Cell>
|
||||
<Table.Cell>{item.hari}</Table.Cell>
|
||||
<Table.Cell>{timeFormat(item.jam_mulai)}</Table.Cell>
|
||||
<Table.Cell>{timeFormat(item.jam_selesai)}</Table.Cell>
|
||||
<Table.Cell>{dateFormat(item.tanggal)}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{item.status === "Y" ? "Aktif" : "Tidak Aktif"}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Link
|
||||
className={`bg-[#5C8374] p-2 rounded text-white mx-2 `}
|
||||
to={`/dokter/jadwal-periksa/kelola-jadwal/${item.id}/${id}`}
|
||||
>
|
||||
Edit
|
||||
</Link>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
);
|
||||
})}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default JadwalPeriksa;
|
||||
173
src/pages/Dokter/JadwalPeriksa/KelolaJadwalPeriksa.jsx
Normal file
173
src/pages/Dokter/JadwalPeriksa/KelolaJadwalPeriksa.jsx
Normal file
@ -0,0 +1,173 @@
|
||||
import { useLocation, useNavigate, useParams } from "react-router-dom";
|
||||
import Input from "../../../components/Input";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import Select from "react-select";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
addJadwalPeriksa,
|
||||
getJadwalPeriksaById,
|
||||
updateJadwalPeriksa,
|
||||
} from "../../../config/Redux/Action/jadwalPeriksaAction";
|
||||
import ReactSelect from "../../../components/ReactSelect";
|
||||
|
||||
const KelolaJadwalPeriksa = () => {
|
||||
const pathName = useLocation().pathname;
|
||||
const { id, idJadwal } = useParams();
|
||||
const { dokter } = useSelector((state) => state.dokterReducer);
|
||||
const { jadwalPeriksaById } = useSelector(
|
||||
(state) => state.jadwalPeriksaReducer
|
||||
);
|
||||
const dispatch = useDispatch();
|
||||
const nav = useNavigate();
|
||||
const [addForm, setAddForm] = useState({
|
||||
id_dokter: id,
|
||||
hari: "",
|
||||
jam_mulai: "",
|
||||
jam_selesai: "",
|
||||
tanggal: "",
|
||||
status: "",
|
||||
});
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(addJadwalPeriksa(addForm, nav));
|
||||
};
|
||||
|
||||
const handleSubmitEdit = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(updateJadwalPeriksa(idJadwal, addForm, nav));
|
||||
};
|
||||
|
||||
const dateFormat = (date) => {
|
||||
return date?.split(" ")[0];
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (idJadwal !== undefined && idJadwal !== null) {
|
||||
// Lakukan sesuatu ketika idJadwal memiliki nilai yang valid
|
||||
dispatch(getJadwalPeriksaById(idJadwal));
|
||||
} else {
|
||||
// Jika idJadwal adalah null atau undefined, atur kembali addForm
|
||||
setAddForm({
|
||||
id_dokter: id,
|
||||
hari: "",
|
||||
jam_mulai: "",
|
||||
jam_selesai: "",
|
||||
tanggal: "",
|
||||
status: "",
|
||||
});
|
||||
}
|
||||
}, [dispatch, id, idJadwal]);
|
||||
|
||||
useEffect(() => {
|
||||
if (jadwalPeriksaById && idJadwal !== undefined) {
|
||||
setAddForm({
|
||||
id_dokter: id,
|
||||
hari: jadwalPeriksaById.hari,
|
||||
jam_mulai: jadwalPeriksaById.jam_mulai,
|
||||
jam_selesai: jadwalPeriksaById.jam_selesai,
|
||||
tanggal: jadwalPeriksaById.tanggal,
|
||||
status: jadwalPeriksaById.status,
|
||||
});
|
||||
}
|
||||
}, [id, jadwalPeriksaById, idJadwal]);
|
||||
|
||||
return (
|
||||
<div className="container min-h-[90vh] m-5 my-[3rem] ">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Jadwal Periksa</h1>
|
||||
<h1>{pathName}</h1>
|
||||
</div>
|
||||
<div className="card bg-white mt-5 rounded-t-md">
|
||||
<div className="header p-3 bg-[#1B4242] text-white rounded-t-md">
|
||||
<h2>{id ? "Edit" : "Tambah"} Jadwal Periksa</h2>
|
||||
</div>
|
||||
<div className="p-5">
|
||||
<form
|
||||
onSubmit={idJadwal !== undefined ? handleSubmitEdit : handleSubmit}
|
||||
>
|
||||
<Input
|
||||
label="Nama"
|
||||
type="text"
|
||||
placeholder="Nama Dokter"
|
||||
value={dokter.nama}
|
||||
disabled
|
||||
/>
|
||||
<div className="my-3">
|
||||
<label className="text-black text-sm font-normal mb-5">
|
||||
Hari
|
||||
</label>
|
||||
<Select
|
||||
options={[
|
||||
{ value: "1", label: "Senin" },
|
||||
{ value: "2", label: "Selasa" },
|
||||
{ value: "3", label: "Rabu" },
|
||||
{ value: "4", label: "Kamis" },
|
||||
{ value: "5", label: "Jumat" },
|
||||
{ value: "6", label: "Sabtu" },
|
||||
{ value: "7", label: "Minggu" },
|
||||
]}
|
||||
name="hari"
|
||||
onChange={(e) => setAddForm({ ...addForm, hari: e.label })}
|
||||
value={{ label: addForm.hari }}
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
label="Jam Mulai"
|
||||
type="time"
|
||||
placeholder="Jam Mulai"
|
||||
name="jam_mulai"
|
||||
value={addForm.jam_mulai}
|
||||
onChange={(e) =>
|
||||
setAddForm({ ...addForm, jam_mulai: e.target.value })
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="Jam Berakhir"
|
||||
type="time"
|
||||
placeholder="Jam Berakhir"
|
||||
name="jam_selesai"
|
||||
value={addForm.jam_selesai}
|
||||
onChange={(e) =>
|
||||
setAddForm({ ...addForm, jam_selesai: e.target.value })
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="Tanggal"
|
||||
type="date"
|
||||
placeholder="tanggal"
|
||||
name="tanggal"
|
||||
value={dateFormat(addForm.tanggal)}
|
||||
onChange={(e) =>
|
||||
setAddForm({ ...addForm, tanggal: e.target.value })
|
||||
}
|
||||
/>
|
||||
<ReactSelect
|
||||
title="Status"
|
||||
value={{
|
||||
value: addForm.status,
|
||||
label: addForm.status === "Y" ? "Aktif" : "Tidak Aktif",
|
||||
}}
|
||||
onChange={(e) => setAddForm({ ...addForm, status: e.value })}
|
||||
data={[
|
||||
{ value: "Y", label: "Aktif" },
|
||||
{ value: "N", label: "Tidak Aktif" },
|
||||
]}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end mt-4">
|
||||
<button
|
||||
className="bg-[#5C8374] p-2 rounded text-white mx-2"
|
||||
type="submit"
|
||||
>
|
||||
{idJadwal ? "Edit" : "Tambah"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default KelolaJadwalPeriksa;
|
||||
267
src/pages/Dokter/PeriksaPasien/DaftarPeriksa.jsx
Normal file
267
src/pages/Dokter/PeriksaPasien/DaftarPeriksa.jsx
Normal file
@ -0,0 +1,267 @@
|
||||
import { Table } from "flowbite-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { FaEdit, FaStethoscope } from "react-icons/fa";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import {
|
||||
Link,
|
||||
useLocation,
|
||||
useOutletContext,
|
||||
useParams,
|
||||
} from "react-router-dom";
|
||||
import {
|
||||
getDaftarPoli,
|
||||
getDetailPriksaByPeriksaId,
|
||||
getObat,
|
||||
getPeriksa,
|
||||
getPeriksaByDafPol,
|
||||
updatePeriksa,
|
||||
} from "../../../config/Redux/Action";
|
||||
import Modals from "../../../components/Modals";
|
||||
import Input from "../../../components/Input";
|
||||
import ReactSelect from "../../../components/ReactSelect";
|
||||
|
||||
const DaftarPeriksa = () => {
|
||||
const pathName = useLocation().pathname;
|
||||
const [role] = useOutletContext();
|
||||
const { id } = useParams();
|
||||
const { daftarPoli } = useSelector((state) => state.daftarPoliReducer);
|
||||
const { periksa, periksaByDafPolId } = useSelector(
|
||||
(state) => state.periksaReducer
|
||||
);
|
||||
const { detailPriksaByPriksaId } = useSelector(
|
||||
(state) => state.detailPeriksaReducer
|
||||
);
|
||||
const { obat } = useSelector((state) => state.obatReducer);
|
||||
const dispatch = useDispatch();
|
||||
const [edit, setEdit] = useState(false);
|
||||
const [idPeriksa, setIdPeriksa] = useState("");
|
||||
const [obatValue, setObatValue] = useState([]);
|
||||
const [editForm, setEditForm] = useState({
|
||||
id_daftar_poli: periksaByDafPolId?.id_daftar_poli,
|
||||
tgl_periksa: periksaByDafPolId?.tanggal,
|
||||
catatan: periksaByDafPolId?.catatan,
|
||||
biaya_periksa: obatValue.reduce((acc, curr) => acc + curr.harga, 150000),
|
||||
});
|
||||
|
||||
const handleEdit = (id) => {
|
||||
setEdit(true);
|
||||
setIdPeriksa(id);
|
||||
};
|
||||
|
||||
const handleUpdate = (e) => {
|
||||
e.preventDefault();
|
||||
const data = {
|
||||
...editForm,
|
||||
};
|
||||
dispatch(updatePeriksa(periksaByDafPolId?.id, data, obatValue));
|
||||
setEdit(false);
|
||||
};
|
||||
|
||||
const formatPriceInRupiah = (price) => {
|
||||
return new Intl.NumberFormat("id-ID", {
|
||||
style: "currency",
|
||||
currency: "IDR",
|
||||
}).format(price);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getDaftarPoli());
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getPeriksa());
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (idPeriksa !== "") {
|
||||
dispatch(getPeriksaByDafPol(idPeriksa));
|
||||
dispatch(getObat());
|
||||
}
|
||||
}, [dispatch, idPeriksa]);
|
||||
|
||||
useEffect(() => {
|
||||
if (periksaByDafPolId?.id) {
|
||||
dispatch(getDetailPriksaByPeriksaId(periksaByDafPolId?.id));
|
||||
}
|
||||
}, [dispatch, periksaByDafPolId?.id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (periksaByDafPolId) {
|
||||
setEditForm({
|
||||
id_daftar_poli: periksaByDafPolId.id_daftar_poli,
|
||||
tgl_periksa: periksaByDafPolId.tanggal,
|
||||
catatan: periksaByDafPolId.catatan,
|
||||
biaya_periksa: obatValue.reduce(
|
||||
(acc, curr) => acc + curr.harga,
|
||||
150000
|
||||
),
|
||||
});
|
||||
}
|
||||
}, [periksaByDafPolId, obatValue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (detailPriksaByPriksaId?.length > 0) {
|
||||
setObatValue([]); // Reset array obat menjadi kosong sebelum membangun kembali
|
||||
|
||||
detailPriksaByPriksaId?.forEach((item) => {
|
||||
setObatValue((obat) => [
|
||||
...obat,
|
||||
{
|
||||
value: item.obat.id,
|
||||
label: item.obat.nama_obat,
|
||||
harga: item.obat.harga,
|
||||
},
|
||||
]);
|
||||
});
|
||||
} else {
|
||||
setObatValue([]); // Jika detailPriksaByPriksaId kosong, reset obat menjadi array kosong
|
||||
}
|
||||
}, [detailPriksaByPriksaId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (role !== "dokter") {
|
||||
window.location.href = "/";
|
||||
}
|
||||
}, [role]);
|
||||
|
||||
return (
|
||||
<div className="container min-h-[90vh] m-5 my-[3rem] ">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Periksa</h1>
|
||||
<h1>{pathName}</h1>
|
||||
</div>
|
||||
<div className="card bg-white p-5 mt-[3rem]">
|
||||
<div className="card-body">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Daftar Periksa</h1>
|
||||
</div>
|
||||
<div className="overflow-x-auto mt-4">
|
||||
<Table striped>
|
||||
<Table.Head>
|
||||
<Table.HeadCell>No</Table.HeadCell>
|
||||
<Table.HeadCell>Nama Pasien</Table.HeadCell>
|
||||
<Table.HeadCell>Keluhan</Table.HeadCell>
|
||||
<Table.HeadCell>Aksi</Table.HeadCell>
|
||||
</Table.Head>
|
||||
<Table.Body className="divide-y">
|
||||
{daftarPoli.map((item, index) => {
|
||||
if (item.jadwal_periksa.id_dokter === id) {
|
||||
index = 0;
|
||||
return (
|
||||
<>
|
||||
<Table.Row key={index}>
|
||||
<Table.Cell>{index + 1}</Table.Cell>
|
||||
<Table.Cell>{item.pasien.nama}</Table.Cell>
|
||||
<Table.Cell>{item.keluhan}</Table.Cell>
|
||||
<Table.Cell width={150}>
|
||||
{periksa?.find(
|
||||
(periksa) => periksa.id_daftar_poli === item.id
|
||||
) ? (
|
||||
<button
|
||||
className="bg-[#5C8374] p-2 rounded text-white mx-2 flex items-center"
|
||||
onClick={() => handleEdit(item.id)}
|
||||
>
|
||||
<FaEdit className="mr-2" /> Edit
|
||||
</button>
|
||||
) : (
|
||||
<Link
|
||||
to={
|
||||
"/dokter/daftar-periksa/periksa/" +
|
||||
item.id +
|
||||
"/" +
|
||||
id
|
||||
}
|
||||
className="bg-[#1B4242] p-2 rounded text-white mx-2 flex items-center w-fit"
|
||||
>
|
||||
<FaStethoscope className="mr-2" /> Periksa
|
||||
</Link>
|
||||
)}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</>
|
||||
);
|
||||
}
|
||||
})}
|
||||
<Modals
|
||||
openModal={edit}
|
||||
setOpenModal={() => setEdit(!edit)}
|
||||
title={"Edit Periksa"}
|
||||
buttonClose={false}
|
||||
body={
|
||||
<form className="flex flex-col" onSubmit={handleUpdate}>
|
||||
<Input
|
||||
label="Nama Pasien"
|
||||
disabled
|
||||
value={periksaByDafPolId?.daftar_poli?.pasien?.nama}
|
||||
onChange={(e) =>
|
||||
setEditForm({ ...editForm, nama: e.target.value })
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="Tanggal Periksa"
|
||||
type="datetime-local"
|
||||
value={editForm.tgl_periksa}
|
||||
onChange={(e) =>
|
||||
setEditForm({
|
||||
...editForm,
|
||||
tgl_periksa: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="Catatan"
|
||||
value={editForm.catatan}
|
||||
onChange={(e) =>
|
||||
setEditForm({ ...editForm, catatan: e.target.value })
|
||||
}
|
||||
/>
|
||||
<ReactSelect
|
||||
title="Obat"
|
||||
value={obatValue}
|
||||
isMulti
|
||||
onChange={(e) => {
|
||||
// Membuat objek baru dengan value, label, dan harga
|
||||
const updatedObatValue = e.map((item) => ({
|
||||
value: item.value,
|
||||
label: item.label,
|
||||
harga:
|
||||
obat.find(
|
||||
(obatItem) => obatItem.id === item.value
|
||||
)?.harga || 0, // Mendapatkan harga dari obat yang cocok dengan value
|
||||
}));
|
||||
setObatValue(updatedObatValue); // Menetapkan nilai yang telah diperbarui ke dalam state obatValue
|
||||
}}
|
||||
data={obat.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.nama_obat,
|
||||
}))}
|
||||
/>
|
||||
<div className="my-3">
|
||||
<label className="text-black text-sm font-normal mb-5">
|
||||
Biaya Periksa
|
||||
</label>
|
||||
<h2 className="text-black text-md font-normal mb-5">
|
||||
{formatPriceInRupiah(editForm.biaya_periksa)}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="flex justify-end mt-4">
|
||||
<button
|
||||
className="bg-[#1B4242] p-2 px-3 text-sm rounded-md text-white mx-2"
|
||||
type="submit"
|
||||
>
|
||||
Simpan
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
/>
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DaftarPeriksa;
|
||||
155
src/pages/Dokter/PeriksaPasien/PeriksaPasien.jsx
Normal file
155
src/pages/Dokter/PeriksaPasien/PeriksaPasien.jsx
Normal file
@ -0,0 +1,155 @@
|
||||
import {
|
||||
useLocation,
|
||||
useNavigate,
|
||||
useOutletContext,
|
||||
useParams,
|
||||
} from "react-router-dom";
|
||||
import Input from "../../../components/Input";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import {
|
||||
getDaftarPoliById,
|
||||
getObat,
|
||||
storePeriksa,
|
||||
} from "../../../config/Redux/Action";
|
||||
import ReactSelect from "../../../components/ReactSelect";
|
||||
|
||||
const PeriksaPasien = () => {
|
||||
const pathName = useLocation().pathname;
|
||||
const { id, idPasien } = useParams();
|
||||
const [role] = useOutletContext();
|
||||
const { daftarPoliById } = useSelector((state) => state.daftarPoliReducer);
|
||||
const { obat } = useSelector((state) => state.obatReducer);
|
||||
const dispatch = useDispatch();
|
||||
const [addForm, setAddForm] = useState({
|
||||
id_daftar_poli: idPasien,
|
||||
tanggal: "",
|
||||
cataan: "",
|
||||
});
|
||||
const [obatOption, setObatOption] = useState([]);
|
||||
const [obatSelected, setObatSelected] = useState([]);
|
||||
const [biayaPeriksa, setBiayaPeriksa] = useState(150000);
|
||||
const nav = useNavigate();
|
||||
|
||||
const formatPriceInRupiah = (price) => {
|
||||
return new Intl.NumberFormat("id-ID", {
|
||||
style: "currency",
|
||||
currency: "IDR",
|
||||
}).format(price);
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const data = {
|
||||
...addForm,
|
||||
biaya_periksa: biayaPeriksa,
|
||||
};
|
||||
dispatch(storePeriksa(data, nav, id, obatSelected));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (idPasien !== undefined) {
|
||||
dispatch(getDaftarPoliById(idPasien));
|
||||
}
|
||||
}, [dispatch, idPasien]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getObat());
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (obat.length > 0) {
|
||||
const newObat = obat.map((item) => {
|
||||
return {
|
||||
value: item.id,
|
||||
label: item.nama_obat,
|
||||
harga: item.harga,
|
||||
};
|
||||
});
|
||||
setObatOption(newObat);
|
||||
}
|
||||
}, [obat]);
|
||||
|
||||
useEffect(() => {
|
||||
if (obatSelected.length > 0) {
|
||||
const newBiayaPeriksa = obatSelected.reduce((acc, item) => {
|
||||
return acc + item.harga;
|
||||
}, 150000);
|
||||
setBiayaPeriksa(newBiayaPeriksa);
|
||||
} else {
|
||||
setBiayaPeriksa(150000);
|
||||
}
|
||||
}, [obatSelected]);
|
||||
|
||||
useEffect(() => {
|
||||
if (role !== "dokter") {
|
||||
window.location.href = "/";
|
||||
}
|
||||
}, [role]);
|
||||
return (
|
||||
<div className="container min-h-[90vh] m-5 my-[3rem]">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Periksa </h1>
|
||||
<h1>{pathName}</h1>
|
||||
</div>
|
||||
<div className="card bg-white mt-5 rounded-t-md">
|
||||
<div className="header p-3 bg-[#1B4242] text-white rounded-t-md">
|
||||
<h2>Periksa Pasien</h2>
|
||||
</div>
|
||||
<div className="p-5">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Input
|
||||
label="Nama Pasien"
|
||||
type="text"
|
||||
placeholder="Nama Pasien"
|
||||
value={daftarPoliById?.pasien?.nama}
|
||||
disabled
|
||||
/>
|
||||
<Input
|
||||
label="Tanggal Periksa"
|
||||
type="datetime-local"
|
||||
placeholder="Tanggal Periksa"
|
||||
value={addForm.tanggal}
|
||||
onChange={(e) =>
|
||||
setAddForm({ ...addForm, tanggal: e.target.value })
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="Catatan"
|
||||
type="text"
|
||||
placeholder="Catatan"
|
||||
value={addForm.catatan}
|
||||
onChange={(e) =>
|
||||
setAddForm({ ...addForm, catatan: e.target.value })
|
||||
}
|
||||
/>
|
||||
<ReactSelect
|
||||
title="Obat"
|
||||
isMulti={true}
|
||||
data={obatOption}
|
||||
onChange={(e) => setObatSelected(e)}
|
||||
/>
|
||||
<div className="my-3">
|
||||
<label className="text-black text-sm font-normal mb-5">
|
||||
Biaya Periksa
|
||||
</label>
|
||||
<h2 className="text-black text-md font-normal mb-5">
|
||||
{formatPriceInRupiah(biayaPeriksa)}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="flex justify-end mt-4">
|
||||
<button
|
||||
className="bg-[#5C8374] p-2 rounded text-white mx-2"
|
||||
type="submit"
|
||||
>
|
||||
Simpan
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PeriksaPasien;
|
||||
93
src/pages/Dokter/ProfileDokter.jsx
Normal file
93
src/pages/Dokter/ProfileDokter.jsx
Normal file
@ -0,0 +1,93 @@
|
||||
import { useLocation, useOutletContext } from "react-router-dom";
|
||||
import Input from "../../components/Input";
|
||||
import TextArea from "../../components/TextArea";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { updateDokter } from "../../config/Redux/Action";
|
||||
|
||||
const ProfileDokter = () => {
|
||||
const pathName = useLocation().pathname;
|
||||
const [role] = useOutletContext();
|
||||
const dispatch = useDispatch();
|
||||
const { dokter } = useSelector((state) => state.dokterReducer);
|
||||
const [dokterForm, setDokterForm] = useState({
|
||||
nama: dokter.nama,
|
||||
alamat: dokter.alamat,
|
||||
no_hp: dokter.no_hp,
|
||||
});
|
||||
|
||||
const handleUpdate = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(updateDokter(dokter.id, dokterForm));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setDokterForm({
|
||||
nama: dokter.nama,
|
||||
alamat: dokter.alamat,
|
||||
no_hp: dokter.no_hp,
|
||||
});
|
||||
}, [dokter]);
|
||||
|
||||
useEffect(() => {
|
||||
if (role !== "dokter") {
|
||||
window.location.href = "/";
|
||||
}
|
||||
}, [role]);
|
||||
return (
|
||||
<div className="container min-h-[90vh] m-5 my-[3rem]">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Dokter</h1>
|
||||
<h1>{pathName}</h1>
|
||||
</div>
|
||||
|
||||
<div className="card bg-white mt-5 rounded-t-md">
|
||||
<div className="header p-3 bg-[#1B4242] text-white rounded-t-md">
|
||||
<h2>Profile Dokter</h2>
|
||||
</div>
|
||||
<div className="p-5">
|
||||
<form onSubmit={handleUpdate}>
|
||||
<Input
|
||||
label="Nama"
|
||||
type="text"
|
||||
placeholder="Nama Dokter"
|
||||
value={dokterForm.nama}
|
||||
onChange={(e) =>
|
||||
setDokterForm({ ...dokterForm, nama: e.target.value })
|
||||
}
|
||||
/>
|
||||
<TextArea
|
||||
label="Alamat"
|
||||
type="text"
|
||||
placeholder="Alamat Dokter"
|
||||
value={dokterForm.alamat}
|
||||
onChange={(e) =>
|
||||
setDokterForm({ ...dokterForm, alamat: e.target.value })
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="No. HP"
|
||||
type="number"
|
||||
placeholder="No. HP Dokter"
|
||||
value={dokterForm.no_hp}
|
||||
onChange={(e) =>
|
||||
setDokterForm({ ...dokterForm, no_hp: e.target.value })
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end mt-4">
|
||||
<button
|
||||
className="bg-[#5C8374] p-2 rounded text-white mx-2"
|
||||
type="submit"
|
||||
>
|
||||
Simpan
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfileDokter;
|
||||
224
src/pages/Dokter/RiwayatPasien.jsx
Normal file
224
src/pages/Dokter/RiwayatPasien.jsx
Normal file
@ -0,0 +1,224 @@
|
||||
import { Table } from "flowbite-react";
|
||||
import { useLocation, useOutletContext, useParams } from "react-router-dom";
|
||||
import Modals from "../../components/Modals";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { getPeriksa } from "../../config/Redux/Action/periksaAction";
|
||||
import {
|
||||
getAllPasien,
|
||||
getDaftarPoli,
|
||||
getDaftarPoliByPasienId,
|
||||
getDetailPriksa,
|
||||
getPasienById,
|
||||
} from "../../config/Redux/Action";
|
||||
|
||||
const RiwayatPasien = () => {
|
||||
const pathName = useLocation().pathname;
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const [role] = useOutletContext();
|
||||
const dispatch = useDispatch();
|
||||
const { periksa } = useSelector((state) => state.periksaReducer);
|
||||
const { id } = useParams();
|
||||
const { daftarPoli, daftarPoliByPasienId } = useSelector(
|
||||
(state) => state.daftarPoliReducer
|
||||
);
|
||||
const { detailPriksa } = useSelector((state) => state.detailPeriksaReducer);
|
||||
const { pasienAll, pasienById } = useSelector((state) => state.pasienReducer);
|
||||
const [riwayat, setRiwayat] = useState([]);
|
||||
const [pasien, setPasien] = useState([]);
|
||||
const [idPasien, setIdPasien] = useState("");
|
||||
|
||||
const handleOpenModal = (id) => {
|
||||
setOpenModal(true);
|
||||
setIdPasien(id);
|
||||
};
|
||||
|
||||
const formatPriceInRupiah = (price) => {
|
||||
return new Intl.NumberFormat("id-ID", {
|
||||
style: "currency",
|
||||
currency: "IDR",
|
||||
}).format(price);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getPeriksa());
|
||||
dispatch(getDaftarPoli());
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getAllPasien());
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (idPasien) {
|
||||
dispatch(getDaftarPoliByPasienId(idPasien));
|
||||
}
|
||||
}, [dispatch, idPasien]);
|
||||
|
||||
useEffect(() => {
|
||||
const matchedPatients = periksa
|
||||
.map((item) => {
|
||||
return daftarPoli.find(
|
||||
(item2) =>
|
||||
item.id_daftar_poli === item2.id &&
|
||||
item2.jadwal_periksa.id_dokter === id
|
||||
);
|
||||
})
|
||||
.filter((matchedItem) => matchedItem !== undefined);
|
||||
|
||||
const matchedPatients2 = matchedPatients
|
||||
.map((item) => {
|
||||
return pasienAll.find((item2) => item2.id === item.id_pasien);
|
||||
})
|
||||
.filter((matchedItem) => matchedItem !== undefined);
|
||||
|
||||
const uniquePatients = [
|
||||
...new Map(matchedPatients2.map((item) => [item.id, item])).values(),
|
||||
];
|
||||
|
||||
setPasien(uniquePatients || []);
|
||||
|
||||
return () => {
|
||||
setPasien([]);
|
||||
};
|
||||
}, [pasienAll, daftarPoli, id, periksa]);
|
||||
|
||||
useEffect(() => {
|
||||
if (daftarPoliByPasienId.length > 0) {
|
||||
const filteredRiwayat = periksa.filter((item2) =>
|
||||
daftarPoliByPasienId.some(
|
||||
(item) =>
|
||||
item.id === item2.id_daftar_poli &&
|
||||
item2?.daftar_poli?.jadwal_periksa?.id_dokter === id &&
|
||||
item.id_pasien === idPasien
|
||||
)
|
||||
);
|
||||
setRiwayat(filteredRiwayat);
|
||||
}
|
||||
}, [daftarPoliByPasienId, periksa, id, idPasien]);
|
||||
|
||||
useEffect(() => {
|
||||
if (riwayat.length > 0) {
|
||||
dispatch(getDetailPriksa());
|
||||
}
|
||||
}, [riwayat, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (idPasien) {
|
||||
dispatch(getPasienById(idPasien));
|
||||
}
|
||||
}, [dispatch, idPasien]);
|
||||
|
||||
useEffect(() => {
|
||||
if (role !== "dokter") {
|
||||
window.location.href = "/";
|
||||
}
|
||||
}, [role]);
|
||||
return (
|
||||
<div className="container min-h-[90vh] m-5 my-[3rem]">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Riwayat Pasien</h1>
|
||||
<h1>{pathName}</h1>
|
||||
</div>
|
||||
<div className="card bg-white p-5 mt-[3rem]">
|
||||
<div className="card-body">
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-medium">Daftar Riwayat Pasien</h1>
|
||||
</div>
|
||||
<div className="overflow-x-auto mt-4">
|
||||
<Table striped>
|
||||
<Table.Head>
|
||||
<Table.HeadCell>No</Table.HeadCell>
|
||||
<Table.HeadCell>Nama</Table.HeadCell>
|
||||
<Table.HeadCell width="30%">Alamat</Table.HeadCell>
|
||||
<Table.HeadCell>No. KTP</Table.HeadCell>
|
||||
<Table.HeadCell>No. HP</Table.HeadCell>
|
||||
<Table.HeadCell>No. RM</Table.HeadCell>
|
||||
<Table.HeadCell>Aksi</Table.HeadCell>
|
||||
</Table.Head>
|
||||
<Table.Body className="divide-y">
|
||||
{pasien.map((item, index) => {
|
||||
return (
|
||||
<Table.Row key={index}>
|
||||
<Table.Cell>{index + 1}</Table.Cell>
|
||||
<Table.Cell>{item.nama}</Table.Cell>
|
||||
<Table.Cell>{item.alamat}</Table.Cell>
|
||||
<Table.Cell>{item.no_ktp}</Table.Cell>
|
||||
<Table.Cell>{item.no_hp}</Table.Cell>
|
||||
<Table.Cell>{item.no_rm}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<button
|
||||
className="bg-[#ff8d9a] p-2 rounded text-white mx-2"
|
||||
onClick={() => handleOpenModal(item.id)}
|
||||
>
|
||||
Detail Riwayat Periksa
|
||||
</button>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
);
|
||||
})}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
<Modals
|
||||
openModal={openModal}
|
||||
setOpenModal={setOpenModal}
|
||||
size="xxl"
|
||||
title={"Riwayat " + pasienById?.nama}
|
||||
body={
|
||||
<div className="overflow-x-auto">
|
||||
<Table striped>
|
||||
<Table.Head>
|
||||
<Table.HeadCell>No</Table.HeadCell>
|
||||
<Table.HeadCell>Tanggal Periksa</Table.HeadCell>
|
||||
<Table.HeadCell>Nama Pasien</Table.HeadCell>
|
||||
<Table.HeadCell>Nama Dokter</Table.HeadCell>
|
||||
<Table.HeadCell>Keluhan</Table.HeadCell>
|
||||
<Table.HeadCell>Catatan</Table.HeadCell>
|
||||
<Table.HeadCell>Obat</Table.HeadCell>
|
||||
<Table.HeadCell>Biaya Priksa</Table.HeadCell>
|
||||
<Table.HeadCell>Total Biaya</Table.HeadCell>
|
||||
</Table.Head>
|
||||
<Table.Body className="divide-y">
|
||||
{riwayat?.map((item, index) => (
|
||||
<Table.Row key={index}>
|
||||
<Table.Cell>{index + 1}</Table.Cell>
|
||||
<Table.Cell>{item.tanggal}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{item.daftar_poli?.pasien?.nama}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Dr. {item.daftar_poli?.jadwal_periksa?.dokter?.nama}
|
||||
</Table.Cell>
|
||||
<Table.Cell>{item?.daftar_poli?.keluhan}</Table.Cell>
|
||||
<Table.Cell>{item?.catatan}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{detailPriksa?.map((item2, index) => {
|
||||
if (item2.id_periksa === item.id) {
|
||||
return (
|
||||
<div key={index}>
|
||||
{item2?.obat?.nama_obat} -{" "}
|
||||
{formatPriceInRupiah(item2?.obat?.harga)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</Table.Cell>
|
||||
<Table.Cell>{formatPriceInRupiah(150000)}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{formatPriceInRupiah(item.biaya_periksa)}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RiwayatPasien;
|
||||
449
src/pages/Home.jsx
Normal file
449
src/pages/Home.jsx
Normal file
@ -0,0 +1,449 @@
|
||||
import { Dropdown, Footer, Navbar } from "flowbite-react";
|
||||
import { Link, useNavigate, useParams } from "react-router-dom";
|
||||
import udinus from "../assets/logo/udinus.png";
|
||||
import {
|
||||
BsDribbble,
|
||||
BsFacebook,
|
||||
BsFillPersonLinesFill,
|
||||
BsGithub,
|
||||
BsInstagram,
|
||||
BsTwitter,
|
||||
} from "react-icons/bs";
|
||||
import { MdOutlineArrowRightAlt } from "react-icons/md";
|
||||
import SwiperCoverflow from "../components/SwiperCoverflow";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
getAdmin,
|
||||
logoutAdmin,
|
||||
getDokter,
|
||||
logoutDokter,
|
||||
addDaftarPoli,
|
||||
getJadwalPeriksa,
|
||||
getPasien,
|
||||
getPoli,
|
||||
logoutPasien,
|
||||
} from "../config/Redux/Action";
|
||||
import Modals from "../components/Modals";
|
||||
import Input from "../components/Input";
|
||||
import ReactSelect from "../components/ReactSelect";
|
||||
import TextArea from "../components/TextArea";
|
||||
import { FaAngleDown } from "react-icons/fa6";
|
||||
import { HiLogout } from "react-icons/hi";
|
||||
import { ToastContainer } from "react-toastify";
|
||||
import transition from "../transition";
|
||||
|
||||
const Home = () => {
|
||||
const { id } = useParams();
|
||||
const dispatch = useDispatch();
|
||||
const nav = useNavigate();
|
||||
const { admin, isLogin } = useSelector((state) => state.adminReducer);
|
||||
const { dokter, isLoginDokter } = useSelector((state) => state.dokterReducer);
|
||||
const { pasien, isLoginPasien } = useSelector((state) => state.pasienReducer);
|
||||
const { poli } = useSelector((state) => state.poliReducer);
|
||||
const { jadwalPeriksa } = useSelector((state) => state.jadwalPeriksaReducer);
|
||||
const token = localStorage.getItem("token");
|
||||
const role = localStorage.getItem("role");
|
||||
const [daftarPoli, setDaftarPoli] = useState(false);
|
||||
const [poliOption, setPoliOption] = useState([]);
|
||||
const [selectedPoli, setSelectedPoli] = useState();
|
||||
const [jadwal, setJadwal] = useState([]);
|
||||
const [daftarPoliForm, setDaftarPoliForm] = useState({
|
||||
id_pasien: pasien.id,
|
||||
id_jadwal: "",
|
||||
keluhan: "",
|
||||
});
|
||||
|
||||
const handleLogoutAdmin = () => {
|
||||
dispatch(logoutAdmin(token, nav));
|
||||
};
|
||||
|
||||
const handleLogoutDokter = () => {
|
||||
dispatch(logoutDokter(token, nav));
|
||||
};
|
||||
|
||||
const handleLogoutPasien = () => {
|
||||
dispatch(logoutPasien(token, nav));
|
||||
};
|
||||
|
||||
const handleOpenDaftarPoli = () => {
|
||||
setDaftarPoli(true);
|
||||
dispatch(getPoli());
|
||||
};
|
||||
|
||||
const handleCloseDaftarPoli = () => {
|
||||
setDaftarPoli(false);
|
||||
};
|
||||
|
||||
const handleDaftarPoli = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(addDaftarPoli(daftarPoliForm));
|
||||
setDaftarPoli(false);
|
||||
};
|
||||
|
||||
const dateFormat = (date) => {
|
||||
return date?.split(" ")[0];
|
||||
};
|
||||
|
||||
const timeFormat = (time) => {
|
||||
const date = new Date(`1970-01-01T${time}`);
|
||||
|
||||
if (isNaN(date)) {
|
||||
return "Invalid time";
|
||||
}
|
||||
|
||||
return date.toLocaleTimeString("id-ID", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (role === "admin") {
|
||||
if (!token || id === undefined) {
|
||||
dispatch(getAdmin(token));
|
||||
if (isLogin && admin.id !== undefined) {
|
||||
nav("/" + admin.id);
|
||||
}
|
||||
} else {
|
||||
dispatch(getAdmin(token));
|
||||
}
|
||||
}
|
||||
}, [dispatch, id, isLogin, nav, token, admin.id, role]);
|
||||
|
||||
useEffect(() => {
|
||||
if (role === "dokter") {
|
||||
if (!token || id === undefined) {
|
||||
dispatch(getDokter(token));
|
||||
if (isLoginDokter && dokter.id !== undefined) {
|
||||
nav("/" + dokter.id);
|
||||
}
|
||||
} else {
|
||||
dispatch(getDokter(token));
|
||||
}
|
||||
}
|
||||
}, [dispatch, id, isLoginDokter, nav, token, dokter.id, role]);
|
||||
|
||||
useEffect(() => {
|
||||
if (role === "pasien") {
|
||||
if (!token || id === undefined) {
|
||||
dispatch(getPasien(token));
|
||||
if (isLoginPasien && pasien.id !== undefined) {
|
||||
nav("/" + pasien.id);
|
||||
}
|
||||
} else {
|
||||
dispatch(getPasien(token));
|
||||
}
|
||||
}
|
||||
}, [dispatch, id, isLoginPasien, nav, token, pasien.id, role]);
|
||||
|
||||
useEffect(() => {
|
||||
if (poli) {
|
||||
const data = poli.map((item) => {
|
||||
return {
|
||||
value: item.id,
|
||||
label: item.nama_poli,
|
||||
};
|
||||
});
|
||||
setPoliOption(data);
|
||||
}
|
||||
}, [poli]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedPoli) {
|
||||
dispatch(getJadwalPeriksa());
|
||||
}
|
||||
}, [dispatch, selectedPoli]);
|
||||
|
||||
useEffect(() => {
|
||||
if (jadwalPeriksa) {
|
||||
const currentDate = new Date();
|
||||
const data = jadwalPeriksa
|
||||
.map((item) => {
|
||||
const date = item.tanggal.split(" ")[0];
|
||||
const time = new Date(`${date}T${item.jam_mulai}`);
|
||||
if (
|
||||
item.dokter.poli.nama_poli === selectedPoli &&
|
||||
time >= currentDate &&
|
||||
item.status === "Y"
|
||||
) {
|
||||
return {
|
||||
value: item.id,
|
||||
label: `Dr. ${item.dokter.nama}, ${item.hari}, ${dateFormat(
|
||||
item.tanggal
|
||||
)}, jam ${timeFormat(item.jam_mulai)} sampai ${timeFormat(
|
||||
item.jam_selesai
|
||||
)}`,
|
||||
};
|
||||
}
|
||||
// Jika kondisi tidak terpenuhi, kembalikan null atau objek kosong
|
||||
return null; // atau return {};
|
||||
})
|
||||
.filter((item) => item !== null); // Filter elemen yang bernilai null
|
||||
|
||||
setJadwal(data);
|
||||
}
|
||||
}, [jadwalPeriksa, selectedPoli]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ToastContainer />
|
||||
<Navbar className="shadow-md py-5 bg-[#00008B]">
|
||||
<Navbar.Brand as={Link} href="https://flowbite-react.com">
|
||||
<img
|
||||
src={udinus}
|
||||
className="mr-3 h-6 sm:h-9"
|
||||
alt="Flowbite React Logo"
|
||||
/>
|
||||
<span className="self-center whitespace-nowrap text-xl font-semibold text-white">
|
||||
Poliklinik BK
|
||||
</span>
|
||||
</Navbar.Brand>
|
||||
<Navbar.Toggle />
|
||||
<Navbar.Collapse className="flex items-center">
|
||||
<Navbar.Link
|
||||
href="#"
|
||||
className="flex items-center h-full text-white"
|
||||
as={Link}
|
||||
>
|
||||
Home
|
||||
</Navbar.Link>
|
||||
<Navbar.Link
|
||||
as={Link}
|
||||
href="/dashboard"
|
||||
className="flex items-center h-full text-white"
|
||||
>
|
||||
About
|
||||
</Navbar.Link>
|
||||
{id && token ? (
|
||||
<>
|
||||
{role !== "pasien" && (
|
||||
<Link
|
||||
to={role == "admin" ? `/admin/${id}` : `/dokter/${id}`}
|
||||
className="flex items-center h-100 text-white"
|
||||
>
|
||||
Dashboard
|
||||
</Link>
|
||||
)}
|
||||
<div className="flex items-center">
|
||||
<Dropdown
|
||||
renderTrigger={() => (
|
||||
<div className="flex items-center cursor-pointer">
|
||||
<div className="flex flex-col justify-center items-end">
|
||||
<h3 className=" font-bold text-white">
|
||||
{admin.username
|
||||
? admin.username
|
||||
: dokter.username
|
||||
? dokter.username
|
||||
: pasien.username}
|
||||
</h3>
|
||||
<span className=" text-[12px] text-white">
|
||||
{admin.role
|
||||
? admin.role
|
||||
: dokter.role
|
||||
? dokter.role
|
||||
: pasien.role}
|
||||
</span>
|
||||
</div>
|
||||
<FaAngleDown className="text-white mr-2 ml-1" />
|
||||
</div>
|
||||
)}
|
||||
dismissOnClick={false}
|
||||
className="flex flex-col items-center py-3 px-5"
|
||||
>
|
||||
<button
|
||||
className="flex items-center bg-red-500 hover:bg-red-600 p-2 px-3 text-white rounded-md w-full"
|
||||
onClick={
|
||||
role === "admin"
|
||||
? () => handleLogoutAdmin()
|
||||
: role === "dokter"
|
||||
? () => handleLogoutDokter()
|
||||
: () => handleLogoutPasien()
|
||||
}
|
||||
>
|
||||
<HiLogout className="mr-2" />
|
||||
Logout
|
||||
</button>
|
||||
</Dropdown>
|
||||
|
||||
<img
|
||||
src="https://i.pravatar.cc/150?img=3"
|
||||
alt="user"
|
||||
className="w-10 h-10 rounded-full"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<Link to={"/login"}>
|
||||
<button className="bg-[#1E90FF] text-black p-2 px-3 rounded-lg">
|
||||
Login
|
||||
</button>
|
||||
</Link>
|
||||
)}
|
||||
</Navbar.Collapse>
|
||||
</Navbar>
|
||||
|
||||
<main className="container mx-auto mt-8 px-4 sm:px-6 lg:px-8">
|
||||
{/* Hero Section */}
|
||||
<section className="relative h-[600px] lg:h-[800px] overflow-hidden">
|
||||
<img
|
||||
src="https://mmc.tirto.id/image/2020/04/14/ilustrasi-dokter-istock--1_ratio-16x9.jpg"
|
||||
alt="hero"
|
||||
className="object-cover w-full h-full"
|
||||
/>
|
||||
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-center text-white w-full px-4 sm:px-6 lg:px-8">
|
||||
<h1 className="text-4xl lg:text-6xl font-bold leading-tight mb-4">
|
||||
The Best Poliklinik in Semarang
|
||||
</h1>
|
||||
<p className="text-lg lg:text-xl mb-6">
|
||||
There are many expert doctors here.
|
||||
</p>
|
||||
<div className="flex justify-center items-center">
|
||||
<p className="mt-4">
|
||||
{role === "pasien" ? (
|
||||
<button
|
||||
className="bg-blue-900 p-2 px-3 mt-4 text-white flex w-fit items-center rounded-md"
|
||||
onClick={() => handleOpenDaftarPoli()}
|
||||
>
|
||||
Daftar Poli
|
||||
<MdOutlineArrowRightAlt color="white" className="ml-2" />
|
||||
</button>
|
||||
) : role === "admin" || role === "dokter" ? (
|
||||
<Link
|
||||
to={`/${role}/${role === "admin" ? admin.id : dokter.id}`}
|
||||
className="bg-blue-900 p-2 px-3 mt-4 text-white flex w-fit items-center rounded-md"
|
||||
>
|
||||
Dashboard{" "}
|
||||
<MdOutlineArrowRightAlt color="white" className="ml-2" />
|
||||
</Link>
|
||||
) : (
|
||||
<Link
|
||||
to={`/login`}
|
||||
className="bg-blue-900 p-2 px-3 mt-4 text-white flex w-fit items-center rounded-md"
|
||||
>
|
||||
Register Now{" "}
|
||||
<MdOutlineArrowRightAlt color="white" className="ml-2" />
|
||||
</Link>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Modals
|
||||
openModal={daftarPoli}
|
||||
setOpenModal={handleCloseDaftarPoli}
|
||||
title="Daftar Poli"
|
||||
buttonClose={false}
|
||||
body={
|
||||
<form className="mt-[-1.5rem]" onSubmit={handleDaftarPoli}>
|
||||
<Input
|
||||
label="Nomor Rekam Medis"
|
||||
type="text"
|
||||
placeholder="Nomor Rekam Medis"
|
||||
value={pasien.no_rm}
|
||||
disabled={true}
|
||||
/>
|
||||
<ReactSelect
|
||||
data={poliOption}
|
||||
title="Poli"
|
||||
onChange={(e) => setSelectedPoli(e.label)}
|
||||
/>
|
||||
<ReactSelect
|
||||
data={jadwal}
|
||||
title="Pilih Jadwal"
|
||||
disabled={selectedPoli === "" ? true : false}
|
||||
onChange={(e) =>
|
||||
setDaftarPoliForm({ ...daftarPoliForm, id_jadwal: e.value })
|
||||
}
|
||||
/>
|
||||
<TextArea
|
||||
label="Keluhan"
|
||||
placeholder="Keluhan"
|
||||
onChange={(e) =>
|
||||
setDaftarPoliForm({
|
||||
...daftarPoliForm,
|
||||
keluhan: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-blue-900 p-2 px-3 mt-4 text-white w-fit rounded-md text-sm"
|
||||
>
|
||||
Daftar Poli
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Testimonial Section */}
|
||||
<section className="mb-[5rem] mt-8">
|
||||
<div className="text-center">
|
||||
<h1 className="text-4xl font-bold leading-tight text-gray-900">
|
||||
Patient Testimonal
|
||||
</h1>
|
||||
<p className="text-xl text-gray-700 mt-2">
|
||||
Testimoni dari pasien yang telah menggunakan layanan kami.
|
||||
</p>
|
||||
<div className="my-[8rem]">
|
||||
<SwiperCoverflow />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<Footer container className="bg-[#00008B] rounded-none">
|
||||
<div className="container mx-auto">
|
||||
<div className="grid w-full justify-between sm:flex sm:justify-between md:flex md:grid-cols-1">
|
||||
<div>
|
||||
<Footer.Brand
|
||||
src={udinus}
|
||||
alt="Udinus Logo"
|
||||
name="Udinus"
|
||||
className="h-[4rem] sm:h-[5rem] text-white"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-8 sm:mt-4 sm:grid-cols-3 sm:gap-6">
|
||||
<div>
|
||||
<Footer.Title title="about" />
|
||||
<Footer.LinkGroup col>
|
||||
<Footer.Link href="#">Flowbite</Footer.Link>
|
||||
<Footer.Link href="#">Tailwind CSS</Footer.Link>
|
||||
</Footer.LinkGroup>
|
||||
</div>
|
||||
<div>
|
||||
<Footer.Title title="Follow us" />
|
||||
<Footer.LinkGroup col>
|
||||
<Footer.Link href="#">Github</Footer.Link>
|
||||
<Footer.Link href="#">Discord</Footer.Link>
|
||||
</Footer.LinkGroup>
|
||||
</div>
|
||||
<div>
|
||||
<Footer.Title title="Legal" />
|
||||
<Footer.LinkGroup col>
|
||||
<Footer.Link href="#">Privacy Policy</Footer.Link>
|
||||
<Footer.Link href="#">Terms & Conditions</Footer.Link>
|
||||
</Footer.LinkGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Footer.Divider />
|
||||
<div className="w-full sm:flex sm:items-center sm:justify-between">
|
||||
<Footer.Copyright href="#" by="Flowbite™" year={2022} />
|
||||
<div className="mt-4 flex space-x-6 sm:mt-0 sm:justify-center">
|
||||
<Footer.Icon href="#" icon={BsFacebook} />
|
||||
<Footer.Icon href="#" icon={BsInstagram} />
|
||||
<Footer.Icon href="#" icon={BsTwitter} />
|
||||
<Footer.Icon href="#" icon={BsGithub} />
|
||||
<Footer.Icon href="#" icon={BsDribbble} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Footer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
32
src/transition.jsx
Normal file
32
src/transition.jsx
Normal file
@ -0,0 +1,32 @@
|
||||
import { motion } from "framer-motion";
|
||||
import udinus from "./assets/logo/udinus.png";
|
||||
|
||||
const transition = (OgComponent) => {
|
||||
return () => {
|
||||
return (
|
||||
<>
|
||||
<OgComponent />
|
||||
<motion.div
|
||||
className="fixed top-0 left-0 w-full h-screen origin-right bg-[#092635] z-50"
|
||||
initial={{ scaleX: 0 }}
|
||||
animate={{ scaleX: 0 }}
|
||||
exit={{ scaleX: 1 }}
|
||||
transition={{ duration: 1, ease: [0.22, 1, 0.36, 1] }}
|
||||
>
|
||||
<div className="flex justify-center items-center h-full">
|
||||
<img src={udinus} alt="udinus" className="w-40" />
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
className="fixed top-0 left-0 w-full h-screen origin-left bg-[#092635] z-50"
|
||||
initial={{ scaleX: 1 }}
|
||||
animate={{ scaleX: 0 }}
|
||||
exit={{ scaleX: 0 }}
|
||||
transition={{ duration: 0.8, ease: [0.22, 1, 0.36, 1] }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export default transition;
|
||||
17
tailwind.config.js
Normal file
17
tailwind.config.js
Normal file
@ -0,0 +1,17 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
"node_modules/flowbite-react/lib/esm/**/*.js",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
poppins: ["Poppins", "sans-serif"],
|
||||
},
|
||||
},
|
||||
},
|
||||
// eslint-disable-next-line no-undef
|
||||
plugins: [require("flowbite/plugin")],
|
||||
};
|
||||
7
vite.config.js
Normal file
7
vite.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user