diff --git a/web-app/package-lock.json b/web-app/package-lock.json index ef1be5e..c0bec04 100644 --- a/web-app/package-lock.json +++ b/web-app/package-lock.json @@ -11,6 +11,7 @@ "@ant-design/icons": "^5.6.1", "@react-buddy/ide-toolbox": "^2.4.0", "@react-buddy/palette-antd": "^5.3.0", + "@reduxjs/toolkit": "^2.6.1", "antd": "^5.23.1", "antd-dayjs-webpack-plugin": "^1.0.6", "antd-mask-input": "^2.0.7", @@ -19,6 +20,7 @@ "prop-types": "^15.8.1", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-redux": "^9.2.0", "react-router-dom": "^7.1.1", "validator": "^13.12.0" }, @@ -970,6 +972,30 @@ "react": "^17.0.0 || ^18.0.0" } }, + "node_modules/@reduxjs/toolkit": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.6.1.tgz", + "integrity": "sha512-SSlIqZNYhqm/oMkXbtofwZSt9lrncblzo6YcZ9zoX+zLngRBrCOjK4lNLdkNucJF58RHOWrD9txT3bT3piH7Zw==", + "license": "MIT", + "dependencies": { + "immer": "^10.0.3", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.35.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz", @@ -1486,14 +1512,14 @@ "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.18", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -1510,6 +1536,12 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, "node_modules/@vitejs/plugin-react-swc": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.8.0.tgz", @@ -3026,6 +3058,16 @@ "npm": ">=4.0.0" } }, + "node_modules/immer": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -4609,6 +4651,29 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, "node_modules/react-router": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.3.0.tgz", @@ -4649,6 +4714,21 @@ "react-dom": ">=18" } }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -4699,6 +4779,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, "node_modules/resize-observer-polyfill": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", @@ -5309,6 +5395,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/validator": { "version": "13.12.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", diff --git a/web-app/package.json b/web-app/package.json index e6c17ef..91901a9 100644 --- a/web-app/package.json +++ b/web-app/package.json @@ -13,6 +13,7 @@ "@ant-design/icons": "^5.6.1", "@react-buddy/ide-toolbox": "^2.4.0", "@react-buddy/palette-antd": "^5.3.0", + "@reduxjs/toolkit": "^2.6.1", "antd": "^5.23.1", "antd-dayjs-webpack-plugin": "^1.0.6", "antd-mask-input": "^2.0.7", @@ -21,6 +22,7 @@ "prop-types": "^15.8.1", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-redux": "^9.2.0", "react-router-dom": "^7.1.1", "validator": "^13.12.0" }, diff --git a/web-app/src/App.jsx b/web-app/src/App.jsx index c77f999..5696c1f 100644 --- a/web-app/src/App.jsx +++ b/web-app/src/App.jsx @@ -2,13 +2,17 @@ import {BrowserRouter as Router} from "react-router-dom"; import AppRouter from "./AppRouter.jsx"; import {AuthProvider} from "./AuthContext.jsx"; import "/src/styles/app.css"; +import {Provider} from "react-redux"; +import store from "./redux/store"; const App = () => ( - - - - - + + + + + + + ); export default App; diff --git a/web-app/src/AuthContext.jsx b/web-app/src/AuthContext.jsx index b93215f..b81f861 100644 --- a/web-app/src/AuthContext.jsx +++ b/web-app/src/AuthContext.jsx @@ -1,9 +1,9 @@ import {createContext, useState, useContext, useEffect} from "react"; import PropTypes from "prop-types"; -import loginUser from "./api/auth/loginRequest.jsx"; +import loginUser from "./api/auth/loginRequest.js"; import {Spin} from "antd"; import {useNavigate} from "react-router-dom"; -import createApi from "./core/axiosConfig.jsx"; +import createApi from "./core/axiosConfig.js"; const AuthContext = createContext(undefined); diff --git a/web-app/src/api/appointments/getAllAppointments.jsx b/web-app/src/api/appointments/getAllAppointments.js similarity index 80% rename from web-app/src/api/appointments/getAllAppointments.jsx rename to web-app/src/api/appointments/getAllAppointments.js index ae93746..b82e387 100644 --- a/web-app/src/api/appointments/getAllAppointments.jsx +++ b/web-app/src/api/appointments/getAllAppointments.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const getAllAppointments = async (api) => { const response = await api.get(`${CONFIG.BASE_URL}/appointments/`); diff --git a/web-app/src/api/auth/loginRequest.jsx b/web-app/src/api/auth/loginRequest.js similarity index 87% rename from web-app/src/api/auth/loginRequest.jsx rename to web-app/src/api/auth/loginRequest.js index 84af9af..f5721f3 100644 --- a/web-app/src/api/auth/loginRequest.jsx +++ b/web-app/src/api/auth/loginRequest.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const loginUser = async (loginData, api) => { const response = await api.post(`${CONFIG.BASE_URL}/login/`, loginData, { diff --git a/web-app/src/api/lens_issues/addLensIssue.jsx b/web-app/src/api/lens_issues/addLensIssue.js similarity index 81% rename from web-app/src/api/lens_issues/addLensIssue.jsx rename to web-app/src/api/lens_issues/addLensIssue.js index 22d933c..af5b500 100644 --- a/web-app/src/api/lens_issues/addLensIssue.jsx +++ b/web-app/src/api/lens_issues/addLensIssue.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const AddLensIssue = async (api, lens_issue) => { const response = await api.post(`${CONFIG.BASE_URL}/lens_issues/`, lens_issue); diff --git a/web-app/src/api/lens_issues/getAllLensIssues.jsx b/web-app/src/api/lens_issues/getAllLensIssues.js similarity index 79% rename from web-app/src/api/lens_issues/getAllLensIssues.jsx rename to web-app/src/api/lens_issues/getAllLensIssues.js index 0745c28..c3307b9 100644 --- a/web-app/src/api/lens_issues/getAllLensIssues.jsx +++ b/web-app/src/api/lens_issues/getAllLensIssues.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const GetAllLensIssues = async (api) => { const response = await api.get(`${CONFIG.BASE_URL}/lens_issues/`); diff --git a/web-app/src/api/lens_types/getAllLensTypes.jsx b/web-app/src/api/lens_types/getAllLensTypes.js similarity index 79% rename from web-app/src/api/lens_types/getAllLensTypes.jsx rename to web-app/src/api/lens_types/getAllLensTypes.js index 54c9bc2..60e2c6f 100644 --- a/web-app/src/api/lens_types/getAllLensTypes.jsx +++ b/web-app/src/api/lens_types/getAllLensTypes.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const getAllLensTypes = async (api) => { const response = await api.get(`${CONFIG.BASE_URL}/lens_types/`); diff --git a/web-app/src/api/lenses/addLens.jsx b/web-app/src/api/lenses/addLens.js similarity index 78% rename from web-app/src/api/lenses/addLens.jsx rename to web-app/src/api/lenses/addLens.js index f2a33d8..7d3be02 100644 --- a/web-app/src/api/lenses/addLens.jsx +++ b/web-app/src/api/lenses/addLens.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const addLens = async (api, lens) => { const response = await api.post(`${CONFIG.BASE_URL}/lenses/`, lens); diff --git a/web-app/src/api/lenses/deleteLens.jsx b/web-app/src/api/lenses/deleteLens.js similarity index 80% rename from web-app/src/api/lenses/deleteLens.jsx rename to web-app/src/api/lenses/deleteLens.js index c4b4c6e..dcaa41e 100644 --- a/web-app/src/api/lenses/deleteLens.jsx +++ b/web-app/src/api/lenses/deleteLens.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const deleteLens = async (api, lens_id) => { const response = await api.delete(`${CONFIG.BASE_URL}/lenses/${lens_id}/`); diff --git a/web-app/src/api/lenses/getAllLenses.jsx b/web-app/src/api/lenses/getAllLenses.js similarity index 78% rename from web-app/src/api/lenses/getAllLenses.jsx rename to web-app/src/api/lenses/getAllLenses.js index dc5afc0..9e0e095 100644 --- a/web-app/src/api/lenses/getAllLenses.jsx +++ b/web-app/src/api/lenses/getAllLenses.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const getAllLenses = async (api) => { const response = await api.get(`${CONFIG.BASE_URL}/lenses/`); diff --git a/web-app/src/api/lenses/getNotIssuedLenses.jsx b/web-app/src/api/lenses/getNotIssuedLenses.js similarity index 80% rename from web-app/src/api/lenses/getNotIssuedLenses.jsx rename to web-app/src/api/lenses/getNotIssuedLenses.js index 251c9ac..30fb48e 100644 --- a/web-app/src/api/lenses/getNotIssuedLenses.jsx +++ b/web-app/src/api/lenses/getNotIssuedLenses.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const getNotIssuedLenses = async (api) => { const response = await api.get(`${CONFIG.BASE_URL}/lenses/not_issued/`); diff --git a/web-app/src/api/lenses/updateLens.jsx b/web-app/src/api/lenses/updateLens.js similarity index 81% rename from web-app/src/api/lenses/updateLens.jsx rename to web-app/src/api/lenses/updateLens.js index 08565a6..a0fd1af 100644 --- a/web-app/src/api/lenses/updateLens.jsx +++ b/web-app/src/api/lenses/updateLens.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const updateLens = async (api, lensId, lensData) => { const response = await api.put(`${CONFIG.BASE_URL}/lenses/${lensId}/`, lensData); diff --git a/web-app/src/api/patients/addPatient.jsx b/web-app/src/api/patients/addPatient.js similarity index 80% rename from web-app/src/api/patients/addPatient.jsx rename to web-app/src/api/patients/addPatient.js index cc98bc3..f0ed081 100644 --- a/web-app/src/api/patients/addPatient.jsx +++ b/web-app/src/api/patients/addPatient.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const addPatient = async (api, patient) => { const response = await api.post(`${CONFIG.BASE_URL}/patients/`, patient); diff --git a/web-app/src/api/patients/deletePatient.jsx b/web-app/src/api/patients/deletePatient.js similarity index 81% rename from web-app/src/api/patients/deletePatient.jsx rename to web-app/src/api/patients/deletePatient.js index 95b52a2..dea07ad 100644 --- a/web-app/src/api/patients/deletePatient.jsx +++ b/web-app/src/api/patients/deletePatient.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const deletePatient = async (api, patient_id) => { const response = await api.delete(`${CONFIG.BASE_URL}/patients/${patient_id}/`); diff --git a/web-app/src/api/patients/getAllPatients.jsx b/web-app/src/api/patients/getAllPatients.js similarity index 78% rename from web-app/src/api/patients/getAllPatients.jsx rename to web-app/src/api/patients/getAllPatients.js index a8e49f5..bcade53 100644 --- a/web-app/src/api/patients/getAllPatients.jsx +++ b/web-app/src/api/patients/getAllPatients.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const getAllPatients = async (api) => { const response = await api.get(`${CONFIG.BASE_URL}/patients/`); diff --git a/web-app/src/api/patients/updatePatient.jsx b/web-app/src/api/patients/updatePatient.js similarity index 82% rename from web-app/src/api/patients/updatePatient.jsx rename to web-app/src/api/patients/updatePatient.js index b5f34fa..6040132 100644 --- a/web-app/src/api/patients/updatePatient.jsx +++ b/web-app/src/api/patients/updatePatient.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const updatePatient = async (api, patientId, patientData) => { const response = await api.put(`${CONFIG.BASE_URL}/patients/${patientId}/`, patientData); diff --git a/web-app/src/api/scheduled_appointments/getAllScheduledAppointments.jsx b/web-app/src/api/scheduled_appointments/getAllScheduledAppointments.js similarity index 82% rename from web-app/src/api/scheduled_appointments/getAllScheduledAppointments.jsx rename to web-app/src/api/scheduled_appointments/getAllScheduledAppointments.js index 1edaf52..7bf647f 100644 --- a/web-app/src/api/scheduled_appointments/getAllScheduledAppointments.jsx +++ b/web-app/src/api/scheduled_appointments/getAllScheduledAppointments.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const getAllScheduledAppointments = async (api) => { const response = await api.get(`${CONFIG.BASE_URL}/scheduled_appointments/`); diff --git a/web-app/src/api/set_content/addSetContent.jsx b/web-app/src/api/set_content/addSetContent.js similarity index 82% rename from web-app/src/api/set_content/addSetContent.jsx rename to web-app/src/api/set_content/addSetContent.js index 9fbd5cf..311b85a 100644 --- a/web-app/src/api/set_content/addSetContent.jsx +++ b/web-app/src/api/set_content/addSetContent.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const addSetContent = async (api, set_content, set_id) => { const response = await api.post(`${CONFIG.BASE_URL}/set_content/${set_id}/`, set_content); diff --git a/web-app/src/api/set_content/getSetContentBySetId.jsx b/web-app/src/api/set_content/getSetContentBySetId.js similarity index 81% rename from web-app/src/api/set_content/getSetContentBySetId.jsx rename to web-app/src/api/set_content/getSetContentBySetId.js index 4223663..2bdadad 100644 --- a/web-app/src/api/set_content/getSetContentBySetId.jsx +++ b/web-app/src/api/set_content/getSetContentBySetId.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const getSetContentBySetId = async (api, set_id) => { const response = await api.get(`${CONFIG.BASE_URL}/set_content/${set_id}/`); diff --git a/web-app/src/api/set_content/updateSetContent.jsx b/web-app/src/api/set_content/updateSetContent.js similarity index 83% rename from web-app/src/api/set_content/updateSetContent.jsx rename to web-app/src/api/set_content/updateSetContent.js index 2b8c6dc..b2edc4c 100644 --- a/web-app/src/api/set_content/updateSetContent.jsx +++ b/web-app/src/api/set_content/updateSetContent.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const updateSetContent = async (api, set_content, set_id) => { const response = await api.put(`${CONFIG.BASE_URL}/set_content/${set_id}/`, set_content); diff --git a/web-app/src/api/sets/addSet.jsx b/web-app/src/api/sets/addSet.js similarity index 78% rename from web-app/src/api/sets/addSet.jsx rename to web-app/src/api/sets/addSet.js index 19e7f09..c199d30 100644 --- a/web-app/src/api/sets/addSet.jsx +++ b/web-app/src/api/sets/addSet.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const addSet = async (api, set) => { const response = await api.post(`${CONFIG.BASE_URL}/sets/`, set); diff --git a/web-app/src/api/sets/appendLensesFromSet.jsx b/web-app/src/api/sets/appendLensesFromSet.js similarity index 82% rename from web-app/src/api/sets/appendLensesFromSet.jsx rename to web-app/src/api/sets/appendLensesFromSet.js index 498c20b..fc549f9 100644 --- a/web-app/src/api/sets/appendLensesFromSet.jsx +++ b/web-app/src/api/sets/appendLensesFromSet.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const appendLensesFromSet = async (api, set_id) => { const response = await api.post(`${CONFIG.BASE_URL}/sets/append_lenses/${set_id}/`); diff --git a/web-app/src/api/sets/deleteSet.jsx b/web-app/src/api/sets/deleteSet.js similarity index 79% rename from web-app/src/api/sets/deleteSet.jsx rename to web-app/src/api/sets/deleteSet.js index 885f32b..b983050 100644 --- a/web-app/src/api/sets/deleteSet.jsx +++ b/web-app/src/api/sets/deleteSet.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const deleteSet = async (api, set_id) => { const response = await api.delete(`${CONFIG.BASE_URL}/sets/${set_id}/`); diff --git a/web-app/src/api/sets/getAllSets.jsx b/web-app/src/api/sets/getAllSets.js similarity index 77% rename from web-app/src/api/sets/getAllSets.jsx rename to web-app/src/api/sets/getAllSets.js index ae48dc2..2e9cb8e 100644 --- a/web-app/src/api/sets/getAllSets.jsx +++ b/web-app/src/api/sets/getAllSets.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const getAllSets = async (api) => { const response = await api.get(`${CONFIG.BASE_URL}/sets/`); diff --git a/web-app/src/api/sets/updateSet.jsx b/web-app/src/api/sets/updateSet.js similarity index 80% rename from web-app/src/api/sets/updateSet.jsx rename to web-app/src/api/sets/updateSet.js index 30fedea..9855b72 100644 --- a/web-app/src/api/sets/updateSet.jsx +++ b/web-app/src/api/sets/updateSet.js @@ -1,4 +1,4 @@ -import CONFIG from "../../core/сonfig.jsx"; +import CONFIG from "../../core/сonfig.js"; const updateSet = async (api, set_id, set) => { const response = await api.put(`${CONFIG.BASE_URL}/sets/${set_id}/`, set); diff --git a/web-app/src/components/SelectViewMode.jsx b/web-app/src/components/SelectViewMode.jsx index 7ce9659..4626fe0 100644 --- a/web-app/src/components/SelectViewMode.jsx +++ b/web-app/src/components/SelectViewMode.jsx @@ -1,8 +1,8 @@ import {BuildOutlined, TableOutlined} from "@ant-design/icons"; import {Select, Tooltip} from "antd"; import PropTypes from "prop-types"; -import {cacheInfo} from "../utils/cachedInfoUtils.jsx"; -import {ViewModPropType} from "../types/viewModPropType.jsx"; +import {cacheInfo} from "../utils/cachedInfoUtils.js"; +import {ViewModPropType} from "../types/viewModPropType.js"; const {Option} = Select; diff --git a/web-app/src/components/appointments/AppointmentCellViewModal.jsx b/web-app/src/components/appointments/AppointmentCellViewModal.jsx index 65892cb..5cb1339 100644 --- a/web-app/src/components/appointments/AppointmentCellViewModal.jsx +++ b/web-app/src/components/appointments/AppointmentCellViewModal.jsx @@ -1,7 +1,22 @@ +import PropTypes from "prop-types"; +import {AppointmentPropType} from "../../types/appointmentPropType.js"; +import {ScheduledAppointmentPropType} from "../../types/scheduledAppointmentPropType.js"; +import {Modal} from "antd"; +const AppointmentCellViewModal = ({visible, onCancel, appointment}) => { + return ( + + + ) +}; -const AppointmentCellViewModal = ({appointment}) => { +AppointmentCellViewModal.propTypes = { + appointment: PropTypes.oneOfType([ScheduledAppointmentPropType, AppointmentPropType]).isRequired, +}; -}; \ No newline at end of file +export default AppointmentCellViewModal; \ No newline at end of file diff --git a/web-app/src/components/appointments/CalendarCell.jsx b/web-app/src/components/appointments/CalendarCell.jsx index 591360c..d430230 100644 --- a/web-app/src/components/appointments/CalendarCell.jsx +++ b/web-app/src/components/appointments/CalendarCell.jsx @@ -2,8 +2,8 @@ import {useEffect, useRef, useState} from "react"; import {Badge, Col, Tag, Tooltip, Typography} from "antd"; import dayjs from "dayjs"; import PropTypes from "prop-types"; -import {AppointmentPropType} from "../../types/appointmentPropType.jsx"; -import {ScheduledAppointmentPropType} from "../../types/scheduledAppointmentPropType.jsx"; +import {AppointmentPropType} from "../../types/appointmentPropType.js"; +import {ScheduledAppointmentPropType} from "../../types/scheduledAppointmentPropType.js"; const CalendarCell = ({appointments, scheduledAppointments, onCellClick, onItemClick}) => { diff --git a/web-app/src/components/lens_issues/LensIssueFormModal.jsx b/web-app/src/components/lens_issues/LensIssueFormModal.jsx index 93a20cf..f3c58a8 100644 --- a/web-app/src/components/lens_issues/LensIssueFormModal.jsx +++ b/web-app/src/components/lens_issues/LensIssueFormModal.jsx @@ -3,10 +3,10 @@ import { Modal, Input, Button, notification, Typography, Collapse, Steps, Row, Alert, Col, DatePicker, Spin, Grid } from "antd"; import PropTypes from "prop-types"; -import getAllPatients from "../../api/patients/getAllPatients.jsx"; +import getAllPatients from "../../api/patients/getAllPatients.js"; import {useAuth} from "../../AuthContext.jsx"; import dayjs from "dayjs"; -import getNotIssuedLenses from "../../api/lenses/getNotIssuedLenses.jsx"; +import getNotIssuedLenses from "../../api/lenses/getNotIssuedLenses.js"; const {useBreakpoint} = Grid; diff --git a/web-app/src/components/lens_issues/LensIssueViewModal.jsx b/web-app/src/components/lens_issues/LensIssueViewModal.jsx index 5d9612c..e96fdbc 100644 --- a/web-app/src/components/lens_issues/LensIssueViewModal.jsx +++ b/web-app/src/components/lens_issues/LensIssueViewModal.jsx @@ -1,6 +1,6 @@ import {Collapse, Modal} from "antd"; import PropTypes from "prop-types"; -import {LensIssuePropType} from "../../types/lensIssuePropType.jsx"; +import {LensIssuePropType} from "../../types/lensIssuePropType.js"; const LensIssueViewModal = ({visible, onCancel, lensIssue}) => { diff --git a/web-app/src/components/lenses/LensFormModal.jsx b/web-app/src/components/lenses/LensFormModal.jsx index 6762d04..b8a5508 100644 --- a/web-app/src/components/lenses/LensFormModal.jsx +++ b/web-app/src/components/lenses/LensFormModal.jsx @@ -1,9 +1,9 @@ import {Col, Form, InputNumber, Modal, notification, Row, Select} from "antd"; import {useEffect, useState} from "react"; import PropTypes from "prop-types"; -import getAllLensTypes from "../../api/lens_types/getAllLensTypes.jsx"; +import getAllLensTypes from "../../api/lens_types/getAllLensTypes.js"; import {useAuth} from "../../AuthContext.jsx"; -import {LensPropType} from "../../types/lensPropType.jsx"; +import {LensPropType} from "../../types/lensPropType.js"; const LensFormModal = ({visible, onCancel, onSubmit, lens}) => { diff --git a/web-app/src/components/lenses/LensListCard.jsx b/web-app/src/components/lenses/LensListCard.jsx index cefce5c..8f47d58 100644 --- a/web-app/src/components/lenses/LensListCard.jsx +++ b/web-app/src/components/lenses/LensListCard.jsx @@ -3,7 +3,7 @@ import PropTypes from "prop-types"; import {DeleteOutlined, EditOutlined, EyeOutlined} from "@ant-design/icons"; import {useState} from "react"; import LensViewModal from "./LensViewModal.jsx"; -import {LensPropType} from "../../types/lensPropType.jsx"; +import {LensPropType} from "../../types/lensPropType.js"; const LensListCard = ({lens, handleEditLens, handleDeleteLens}) => { const [showModalInfo, setShowModalInfo] = useState(false); diff --git a/web-app/src/components/lenses/LensViewModal.jsx b/web-app/src/components/lenses/LensViewModal.jsx index 54e6ad3..60022c6 100644 --- a/web-app/src/components/lenses/LensViewModal.jsx +++ b/web-app/src/components/lenses/LensViewModal.jsx @@ -1,6 +1,6 @@ import {Button, Col, Modal, Row, Typography} from "antd"; import PropTypes from "prop-types"; -import {LensPropType} from "../../types/lensPropType.jsx"; +import {LensPropType} from "../../types/lensPropType.js"; const {Text, Title} = Typography; diff --git a/web-app/src/components/patients/PatientFormModal.jsx b/web-app/src/components/patients/PatientFormModal.jsx index ce7c0c0..130ca83 100644 --- a/web-app/src/components/patients/PatientFormModal.jsx +++ b/web-app/src/components/patients/PatientFormModal.jsx @@ -5,7 +5,7 @@ import locale from "antd/es/date-picker/locale/ru_RU"; import validator from "validator"; import {MaskedInput} from "antd-mask-input"; import dayjs from "dayjs"; -import {PatientPropType} from "../../types/patientPropType.jsx"; +import {PatientPropType} from "../../types/patientPropType.js"; const {TextArea} = Input; @@ -142,7 +142,7 @@ PatientFormModal.propTypes = { visible: PropTypes.bool.isRequired, onCancel: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired, - patient: PatientPropType.isRequired, + patient: PatientPropType, }; export default PatientFormModal; diff --git a/web-app/src/components/patients/PatientListCard.jsx b/web-app/src/components/patients/PatientListCard.jsx index cb8a3a7..e1187c0 100644 --- a/web-app/src/components/patients/PatientListCard.jsx +++ b/web-app/src/components/patients/PatientListCard.jsx @@ -3,7 +3,7 @@ import PropTypes from "prop-types"; import {DeleteOutlined, EditOutlined, EyeOutlined} from "@ant-design/icons"; import {useState} from "react"; import PatientViewModal from "./PatientViewModal.jsx"; -import {PatientPropType} from "../../types/patientPropType.jsx"; +import {PatientPropType} from "../../types/patientPropType.js"; const PatientListCard = ({patient, handleEditPatient, handleDeletePatient}) => { const [showModalInfo, setShowModalInfo] = useState(false); diff --git a/web-app/src/components/patients/PatientViewModal.jsx b/web-app/src/components/patients/PatientViewModal.jsx index 8262fcf..9f48938 100644 --- a/web-app/src/components/patients/PatientViewModal.jsx +++ b/web-app/src/components/patients/PatientViewModal.jsx @@ -1,6 +1,6 @@ import {Button, Col, Modal, Row, Typography, Divider} from "antd"; import PropTypes from "prop-types"; -import {PatientPropType} from "../../types/patientPropType.jsx"; +import {PatientPropType} from "../../types/patientPropType.js"; const { Text, Title } = Typography; diff --git a/web-app/src/components/sets/SetFormModal.jsx b/web-app/src/components/sets/SetFormModal.jsx index f1eda0e..9a38c4b 100644 --- a/web-app/src/components/sets/SetFormModal.jsx +++ b/web-app/src/components/sets/SetFormModal.jsx @@ -2,11 +2,11 @@ import {useState, useEffect} from "react"; import {Modal, Button, Form, Input, Table, InputNumber, Select, Space, notification} from "antd"; import {PlusOutlined, DeleteOutlined} from "@ant-design/icons"; import axios from "axios"; -import getAllLensTypes from "../../api/lens_types/getAllLensTypes.jsx"; +import getAllLensTypes from "../../api/lens_types/getAllLensTypes.js"; import {useAuth} from "../../AuthContext.jsx"; import PropTypes from "prop-types"; -import getSetContentBySetId from "../../api/set_content/getSetContentBySetId.jsx"; -import {SetPropType} from "../../types/setPropType.jsx"; +import getSetContentBySetId from "../../api/set_content/getSetContentBySetId.js"; +import {SetPropType} from "../../types/setPropType.js"; const {Option} = Select; diff --git a/web-app/src/components/sets/SetListCard.jsx b/web-app/src/components/sets/SetListCard.jsx index 7b16af6..4f3e6f8 100644 --- a/web-app/src/components/sets/SetListCard.jsx +++ b/web-app/src/components/sets/SetListCard.jsx @@ -1,7 +1,7 @@ import PropTypes from "prop-types"; import {Card, Modal, Popconfirm, Tooltip} from "antd"; import {DeleteOutlined, EditOutlined, PlusOutlined} from "@ant-design/icons"; -import {SetPropType} from "../../types/setPropType.jsx"; +import {SetPropType} from "../../types/setPropType.js"; const SetListCard = ({set, handleEditSet, handleDeleteSet, handleAppendSet}) => { const deleteSet = () => { diff --git a/web-app/src/core/axiosConfig.jsx b/web-app/src/core/axiosConfig.js similarity index 98% rename from web-app/src/core/axiosConfig.jsx rename to web-app/src/core/axiosConfig.js index 04b0d4c..a8d5d50 100644 --- a/web-app/src/core/axiosConfig.jsx +++ b/web-app/src/core/axiosConfig.js @@ -1,5 +1,5 @@ import axios from "axios"; -import CONFIG from "./сonfig.jsx"; +import CONFIG from "./сonfig.js"; import {notification} from "antd"; const createApi = (logoutAndRedirect) => { diff --git a/web-app/src/core/сonfig.jsx b/web-app/src/core/сonfig.js similarity index 100% rename from web-app/src/core/сonfig.jsx rename to web-app/src/core/сonfig.js diff --git a/web-app/src/layouts/AppointmentsLayout.jsx b/web-app/src/layouts/AppointmentsLayout.jsx index a8534b6..9278ad5 100644 --- a/web-app/src/layouts/AppointmentsLayout.jsx +++ b/web-app/src/layouts/AppointmentsLayout.jsx @@ -6,11 +6,11 @@ import { } from "@ant-design/icons"; import AppointmentsCalendarPage from "../pages/appointments_layout/AppointmentsCalendarPage.jsx"; import AppointmentsTablePage from "../pages/appointments_layout/AppointmentsTablePage.jsx"; -import getAllAppointments from "../api/appointments/getAllAppointments.jsx"; -import getAllScheduledAppointments from "../api/scheduled_appointments/getAllScheduledAppointments.jsx"; +import getAllAppointments from "../api/appointments/getAllAppointments.js"; +import getAllScheduledAppointments from "../api/scheduled_appointments/getAllScheduledAppointments.js"; import {useAuth} from "../AuthContext.jsx"; import LoadingIndicator from "../components/LoadingIndicator.jsx"; -import {cacheInfo, getCachedInfo, getCacheTimestamp} from "../utils/cachedInfoUtils.jsx"; +import {cacheInfo, getCachedInfo, getCacheTimestamp} from "../utils/cachedInfoUtils.js"; const {useBreakpoint} = Grid; diff --git a/web-app/src/main.jsx b/web-app/src/main.jsx index d2705ef..74ce178 100644 --- a/web-app/src/main.jsx +++ b/web-app/src/main.jsx @@ -5,5 +5,5 @@ import App from './App.jsx' createRoot(document.getElementById('root')).render( - , + ) diff --git a/web-app/src/pages/IssuesPage.jsx b/web-app/src/pages/IssuesPage.jsx index 0c965f1..13e0507 100644 --- a/web-app/src/pages/IssuesPage.jsx +++ b/web-app/src/pages/IssuesPage.jsx @@ -11,17 +11,17 @@ import { Typography, Timeline, Grid, Pagination } from "antd"; -import getAllLensIssues from "../api/lens_issues/getAllLensIssues.jsx"; +import getAllLensIssues from "../api/lens_issues/getAllLensIssues.js"; import {useEffect, useState} from "react"; import {useAuth} from "../AuthContext.jsx"; import {DatabaseOutlined, PlusOutlined, UnorderedListOutlined} from "@ant-design/icons"; import LensIssueViewModal from "../components/lens_issues/LensIssueViewModal.jsx"; import dayjs from "dayjs"; import LensIssueFormModal from "../components/lens_issues/LensIssueFormModal.jsx"; -import addLensIssue from "../api/lens_issues/addLensIssue.jsx"; +import addLensIssue from "../api/lens_issues/addLensIssue.js"; import SelectViewMode from "../components/SelectViewMode.jsx"; import LoadingIndicator from "../components/LoadingIndicator.jsx"; -import {getCachedInfo, getCacheTimestamp} from "../utils/cachedInfoUtils.jsx"; +import {getCachedInfo, getCacheTimestamp} from "../utils/cachedInfoUtils.js"; const {Title} = Typography; const {useBreakpoint} = Grid; diff --git a/web-app/src/pages/PatientsPage.jsx b/web-app/src/pages/PatientsPage.jsx index b61345b..8d70a7f 100644 --- a/web-app/src/pages/PatientsPage.jsx +++ b/web-app/src/pages/PatientsPage.jsx @@ -1,4 +1,4 @@ -import {useEffect, useState} from "react"; +import {useEffect} from 'react'; import { Input, Select, @@ -11,7 +11,7 @@ import { Table, Button, Popconfirm, - Typography + Typography, Result } from "antd"; import { BuildOutlined, @@ -20,79 +20,109 @@ import { SortDescendingOutlined, TableOutlined, TeamOutlined } from "@ant-design/icons"; -import {useAuth} from "../AuthContext.jsx"; -import getAllPatients from "../api/patients/getAllPatients.jsx"; +import {useDispatch, useSelector} from "react-redux"; +import { + useAddPatientMutation, + useDeletePatientMutation, + useGetPatientsQuery, + useUpdatePatientMutation +} from "../redux/services/patientsApi.js"; +import { + openModal, + selectPatient, + setSearchText, + setSortOrder, + setViewMode, + closeModal, setCurrentPage, setPageSize +} from "../redux/slices/patientsSlice.js"; import PatientListCard from "../components/patients/PatientListCard.jsx"; import PatientFormModal from "../components/patients/PatientFormModal.jsx"; -import updatePatient from "../api/patients/updatePatient.jsx"; -import addPatient from "../api/patients/addPatient.jsx"; -import deletePatient from "../api/patients/deletePatient.jsx"; import SelectViewMode from "../components/SelectViewMode.jsx"; import LoadingIndicator from "../components/LoadingIndicator.jsx"; -import {cacheInfo, getCachedInfo, getCacheTimestamp} from "../utils/cachedInfoUtils.jsx"; +import {getCachedInfo} from "../utils/cachedInfoUtils.js"; const {Option} = Select; const {Title} = Typography const PatientsPage = () => { - const {api} = useAuth(); - const [searchText, setSearchText] = useState(""); - const [sortOrder, setSortOrder] = useState("asc"); - const [viewMode, setViewMode] = useState("tile"); - const [patients, setPatients] = useState([]); + const dispatch = useDispatch(); - const [current, setCurrent] = useState(1); - const [pageSize, setPageSize] = useState(10); + const { + searchText, + sortOrder, + viewMode, + selectedPatient, + isModalVisible + } = useSelector(state => state.patientsUI); - const [isModalVisible, setIsModalVisible] = useState(false); - const [selectedPatient, setSelectedPatient] = useState(null); - const [loading, setLoading] = useState(true); + const {data: patients = [], isLoading, isError} = useGetPatientsQuery(); + const [addPatient] = useAddPatientMutation(); + const [updatePatient] = useUpdatePatientMutation(); + const [deletePatient] = useDeletePatientMutation(); useEffect(() => { - fetchPatientsWithCache(); - fetchViewModeFromCache(); - document.title = "Пациенты"; + document.title = "Пациенты" + const cachedViewMode = getCachedInfo("viewModePatients"); + if (cachedViewMode) dispatch(setViewMode(cachedViewMode)); }, []); - useEffect(() => { - if (!isModalVisible && !selectedPatient) { - const intervalId = setInterval(fetchPatients, 5000); - return () => clearInterval(intervalId); - } - }, [isModalVisible, selectedPatient]); - - const fetchPatientsWithCache = async () => { - const cachedData = getCachedInfo("patientsData"); - const cacheTimestamp = getCacheTimestamp("patientsData"); - - if (cachedData && cacheTimestamp && (Date.now() - cacheTimestamp) < 60 * 1000) { - setPatients(cachedData); - setLoading(false); - return; - } - - await fetchPatients(); + const handleAddPatient = () => { + dispatch(selectPatient(null)); + dispatch(openModal()); }; - const fetchPatients = async () => { - const data = await getAllPatients(api); - setPatients(data); - - cacheInfo("patientsData", data); - setLoading(false); + const handleEditPatient = (patient) => { + dispatch(selectPatient(patient)); + dispatch(openModal()); }; - const fetchViewModeFromCache = () => { - const cachedViewMode = getCachedInfo("viewModePatients"); - if (cachedViewMode) { - setViewMode(cachedViewMode); + const handleDeletePatient = async (patientId) => { + try { + await deletePatient(patientId).unwrap() + notification.success({ + message: "Пациент удалён", + description: "Пациент успешно удалён из базы.", + placement: "topRight", + }); + } catch (error) { + notification.error({ + message: "Ошибка удаления", + description: error.data?.message || "Не удалось удалить пациента", + placement: "topRight", + }); + } + }; + + const handleModalPatientSubmit = async (patientData) => { + try { + if (selectedPatient) { + await updatePatient({id: selectedPatient.id, ...patientData}).unwrap() + notification.success({ + message: "Пациент обновлён", + description: `Данные пациента ${patientData.first_name} ${patientData.last_name} успешно обновлены.`, + placement: "topRight", + }) + } else { + await addPatient(patientData).unwrap() + notification.success({ + message: "Пациент добавлен", + description: `Пациент ${patientData.first_name} ${patientData.last_name} успешно добавлен.`, + placement: "topRight", + }); + } + dispatch(closeModal()); + } catch (error) { + notification.error({ + message: "Ошибка", + description: error.data?.message || "Произошла ошибка при сохранении", + placement: "topRight", + }); } }; const filteredPatients = patients - .filter((patient) => { + .filter(patient => { const searchLower = searchText.toLowerCase(); - return Object.values(patient) .filter(value => typeof value === "string") .some(value => value.toLowerCase().includes(searchLower)); @@ -100,96 +130,11 @@ const PatientsPage = () => { .sort((a, b) => { const fullNameA = `${a.last_name} ${a.first_name}`; const fullNameB = `${b.last_name} ${b.first_name}`; - return sortOrder === "asc" ? fullNameA.localeCompare(fullNameB) : fullNameB.localeCompare(fullNameA); + return sortOrder === "asc" + ? fullNameA.localeCompare(fullNameB) + : fullNameB.localeCompare(fullNameA); }); - const handleAddPatient = () => { - setSelectedPatient(null); - setIsModalVisible(true); - }; - - const handleEditPatient = (patient) => { - setSelectedPatient(patient); - setIsModalVisible(true); - }; - - const handleDeletePatient = async (patient_id) => { - await deletePatient(api, patient_id); - await fetchPatients(); - notification.success({ - message: "Пациент удалён", - description: "Пациент успешно удалён из базы.", - placement: "topRight", - }); - }; - - const handleCancel = () => { - setIsModalVisible(false); - }; - - const handleModalPatientSubmit = async (newPatient) => { - setIsModalVisible(false); - - if (selectedPatient) { - await editPatient(newPatient); - } else { - await addNewPatient(newPatient); - } - await fetchPatients(); - }; - - const editPatient = async (patient) => { - await updatePatient(api, selectedPatient.id, patient); - notification.success({ - message: "Пациент обновлён", - description: `Данные пациента ${patient.first_name} ${patient.last_name} успешно обновлены.`, - placement: "topRight", - }); - }; - - const addNewPatient = async (patient) => { - await addPatient(api, patient); - notification.success({ - message: "Пациент добавлен", - description: `Пациент ${patient.first_name} ${patient.last_name} успешно добавлен.`, - placement: "topRight", - }); - }; - - const TileView = () => ( - ( - - - - )} - pagination={{ - current, - pageSize, - showSizeChanger: true, - pageSizeOptions: ["5", "10", "20", "50"], - onChange: (page, newPageSize) => { - setCurrent(page); - setPageSize(newPageSize); - }, - }} - /> - ); - const viewModes = [ { value: "tile", @@ -277,17 +222,61 @@ const PatientsPage = () => { }} showSorterTooltip={false} pagination={{ - current, - pageSize, + current: 1, + pageSize: 10, showSizeChanger: true, pageSizeOptions: ["5", "10", "20", "50"], onChange: (page, newPageSize) => { - setCurrent(page); - setPageSize(newPageSize); + dispatch(setCurrentPage(page)); + dispatch(setPageSize(newPageSize)); }, }} /> - ) + ); + + const TileView = () => ( + ( + + + + )} + pagination={{ + current: 1, + pageSize: 10, + showSizeChanger: true, + pageSizeOptions: ["5", "10", "20", "50"], + onChange: (page, newPageSize) => { + dispatch(setCurrentPage(page)); + dispatch(setPageSize(newPageSize)); + }, + }} + /> + ); + + if (isError) { + return ( + + ); + } return ( @@ -296,7 +285,8 @@ const PatientsPage = () => { setSearchText(e.target.value)} + value={searchText} + onChange={(e) => dispatch(setSearchText(e.target.value))} style={{width: "100%"}} allowClear /> @@ -309,7 +299,7 @@ const PatientsPage = () => { > setSortOrder(value)} + onChange={(value) => dispatch(setSortOrder(value))} style={{width: "100%"}} > А-Я @@ -330,7 +320,7 @@ const PatientsPage = () => { }> dispatch(setViewMode(value))} localStorageKey={"viewModePatients"} toolTipText={"Формат отображения пациентов"} viewModes={viewModes} @@ -338,7 +328,7 @@ const PatientsPage = () => { - {loading ? ( + {isLoading ? ( ) : viewMode === "tile" ? ( @@ -356,7 +346,7 @@ const PatientsPage = () => { dispatch(closeModal())} onSubmit={handleModalPatientSubmit} patient={selectedPatient} /> @@ -364,4 +354,4 @@ const PatientsPage = () => { ); }; -export default PatientsPage; \ No newline at end of file +export default PatientsPage; diff --git a/web-app/src/pages/appointments_layout/AppointmentsCalendarPage.jsx b/web-app/src/pages/appointments_layout/AppointmentsCalendarPage.jsx index 26ca66c..5f2fe8b 100644 --- a/web-app/src/pages/appointments_layout/AppointmentsCalendarPage.jsx +++ b/web-app/src/pages/appointments_layout/AppointmentsCalendarPage.jsx @@ -6,8 +6,8 @@ import locale from 'antd/es/locale/ru_RU'; import updateLocale from 'dayjs/plugin/updateLocale'; import PropTypes, {arrayOf} from "prop-types"; import CalendarCell from "../../components/appointments/CalendarCell.jsx"; -import {AppointmentPropType} from "../../types/appointmentPropType.jsx"; -import {ScheduledAppointmentPropType} from "../../types/scheduledAppointmentPropType.jsx"; +import {AppointmentPropType} from "../../types/appointmentPropType.js"; +import {ScheduledAppointmentPropType} from "../../types/scheduledAppointmentPropType.js"; const {useBreakpoint} = Grid; diff --git a/web-app/src/pages/appointments_layout/AppointmentsTablePage.jsx b/web-app/src/pages/appointments_layout/AppointmentsTablePage.jsx index 54840d4..f0c0cf2 100644 --- a/web-app/src/pages/appointments_layout/AppointmentsTablePage.jsx +++ b/web-app/src/pages/appointments_layout/AppointmentsTablePage.jsx @@ -1,6 +1,6 @@ import {useAuth} from "../../AuthContext.jsx"; import {useEffect, useState} from "react"; -import getAllAppointments from "../../api/appointments/getAllAppointments.jsx"; +import getAllAppointments from "../../api/appointments/getAllAppointments.js"; import {notification} from "antd"; diff --git a/web-app/src/pages/lenses_layout/LensesPage.jsx b/web-app/src/pages/lenses_layout/LensesPage.jsx index 337b1e7..c0bc6ec 100644 --- a/web-app/src/pages/lenses_layout/LensesPage.jsx +++ b/web-app/src/pages/lenses_layout/LensesPage.jsx @@ -25,15 +25,15 @@ import { BuildOutlined } from "@ant-design/icons"; import LensCard from "../../components/lenses/LensListCard.jsx"; -import getAllLenses from "../../api/lenses/getAllLenses.jsx"; -import addLens from "../../api/lenses/addLens.jsx"; -import updateLens from "../../api/lenses/updateLens.jsx"; -import deleteLens from "../../api/lenses/deleteLens.jsx"; +import getAllLenses from "../../api/lenses/getAllLenses.js"; +import addLens from "../../api/lenses/addLens.js"; +import updateLens from "../../api/lenses/updateLens.js"; +import deleteLens from "../../api/lenses/deleteLens.js"; import {useAuth} from "../../AuthContext.jsx"; import LensFormModal from "../../components/lenses/LensFormModal.jsx"; import SelectViewMode from "../../components/SelectViewMode.jsx"; import LoadingIndicator from "../../components/LoadingIndicator.jsx"; -import {getCachedInfo, getCacheTimestamp} from "../../utils/cachedInfoUtils.jsx"; +import {getCachedInfo, getCacheTimestamp} from "../../utils/cachedInfoUtils.js"; const {Option} = Select; const {useBreakpoint} = Grid; diff --git a/web-app/src/pages/lenses_layout/SetLensesPage.jsx b/web-app/src/pages/lenses_layout/SetLensesPage.jsx index fbf5692..afbaf71 100644 --- a/web-app/src/pages/lenses_layout/SetLensesPage.jsx +++ b/web-app/src/pages/lenses_layout/SetLensesPage.jsx @@ -1,18 +1,18 @@ import {useAuth} from "../../AuthContext.jsx"; import {useEffect, useState} from "react"; import {FloatButton, Input, List, notification, Row, Typography} from "antd"; -import getAllSets from "../../api/sets/getAllSets.jsx"; +import getAllSets from "../../api/sets/getAllSets.js"; import {PlusOutlined, SwitcherOutlined} from "@ant-design/icons"; import SetListCard from "../../components/sets/SetListCard.jsx"; import SetFormModal from "../../components/sets/SetFormModal.jsx"; -import updateSet from "../../api/sets/updateSet.jsx"; -import addSet from "../../api/sets/addSet.jsx"; -import deleteSet from "../../api/sets/deleteSet.jsx"; -import addSetContent from "../../api/set_content/addSetContent.jsx"; -import updateSetContent from "../../api/set_content/updateSetContent.jsx"; -import appendLensesFromSet from "../../api/sets/appendLensesFromSet.jsx"; +import updateSet from "../../api/sets/updateSet.js"; +import addSet from "../../api/sets/addSet.js"; +import deleteSet from "../../api/sets/deleteSet.js"; +import addSetContent from "../../api/set_content/addSetContent.js"; +import updateSetContent from "../../api/set_content/updateSetContent.js"; +import appendLensesFromSet from "../../api/sets/appendLensesFromSet.js"; import LoadingIndicator from "../../components/LoadingIndicator.jsx"; -import {cacheInfo, getCachedInfo, getCacheTimestamp} from "../../utils/cachedInfoUtils.jsx"; +import {cacheInfo, getCachedInfo, getCacheTimestamp} from "../../utils/cachedInfoUtils.js"; const {Title} = Typography; diff --git a/web-app/src/redux/services/patientsApi.js b/web-app/src/redux/services/patientsApi.js new file mode 100644 index 0000000..cc14178 --- /dev/null +++ b/web-app/src/redux/services/patientsApi.js @@ -0,0 +1,52 @@ +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' +import CONFIG from "../../core/сonfig.js"; + +export const patientsApi = createApi({ + reducerPath: 'patientsApi', + baseQuery: fetchBaseQuery({ + baseUrl: CONFIG.BASE_URL, + prepareHeaders: (headers, { getState }) => { + const token = localStorage.getItem('access_token') + if (token) headers.set('Authorization', `Bearer ${token}`) + return headers + } + }), + tagTypes: ['Patient'], + endpoints: (builder) => ({ + getPatients: builder.query({ + query: () => '/patients/', + providesTags: ['Patient'], + refetchOnMountOrArgChange: 60 + }), + addPatient: builder.mutation({ + query: (patient) => ({ + url: '/patients/', + method: 'POST', + body: patient + }), + invalidatesTags: ['Patient'] + }), + updatePatient: builder.mutation({ + query: ({ id, ...patient }) => ({ + url: `/patients/${id}/`, + method: 'PUT', + body: patient + }), + invalidatesTags: ['Patient'] + }), + deletePatient: builder.mutation({ + query: (id) => ({ + url: `/patients/${id}/`, + method: 'DELETE' + }), + invalidatesTags: ['Patient'] + }) + }) +}) + +export const { + useGetPatientsQuery, + useAddPatientMutation, + useUpdatePatientMutation, + useDeletePatientMutation +} = patientsApi diff --git a/web-app/src/redux/slices/appointmentsSlice.js b/web-app/src/redux/slices/appointmentsSlice.js new file mode 100644 index 0000000..d26ee8f --- /dev/null +++ b/web-app/src/redux/slices/appointmentsSlice.js @@ -0,0 +1,19 @@ +import {createSlice} from '@reduxjs/toolkit'; + +const appointmentsSlice = createSlice({ + name: 'appointments', + initialState: { + data: [], + status: 'idle', + error: null, + }, + reducers: { + setAppointments(state, action) { + state.data = action.payload; + } + } +}); + +export const {setAppointments} = appointmentsSlice.actions; + +export default appointmentsSlice.reducer; diff --git a/web-app/src/redux/slices/patientsSlice.js b/web-app/src/redux/slices/patientsSlice.js new file mode 100644 index 0000000..d8ead09 --- /dev/null +++ b/web-app/src/redux/slices/patientsSlice.js @@ -0,0 +1,57 @@ +import {createSlice} from '@reduxjs/toolkit' + +const initialState = { + searchText: '', + sortOrder: 'asc', + viewMode: 'tile', + currentPage: 1, + pageSize: 10, + selectedPatient: null, + isModalVisible: false +} + +const patientsSlice = createSlice({ + name: 'patientsUI', + initialState, + reducers: { + setSearchText: (state, action) => { + state.searchText = action.payload; + }, + setSortOrder: (state, action) => { + state.sortOrder = action.payload; + }, + setViewMode: (state, action) => { + state.viewMode = action.payload; + localStorage.setItem('viewModePatients', action.payload); + }, + setCurrentPage: (state, action) => { + state.currentPage = action.payload; + }, + setPageSize: (state, action) => { + state.pageSize = action.payload; + }, + openModal: (state) => { + state.isModalVisible = true; + }, + closeModal: (state) => { + state.isModalVisible = false; + state.selectedPatient = null; + }, + selectPatient: (state, action) => { + state.selectedPatient = action.payload; + } + } +}) + +export const { + setSearchText, + setSortOrder, + setViewMode, + setCurrentPage, + setPageSize, + openModal, + closeModal, + selectPatient +} = patientsSlice.actions + +export default patientsSlice.reducer diff --git a/web-app/src/redux/store.js b/web-app/src/redux/store.js new file mode 100644 index 0000000..d4e1c00 --- /dev/null +++ b/web-app/src/redux/store.js @@ -0,0 +1,15 @@ +import { configureStore } from '@reduxjs/toolkit' +import {patientsApi} from "./services/patientsApi.js"; +import patientsUIReducer from './slices/patientsSlice.js' + +export const store = configureStore({ + reducer: { + [patientsApi.reducerPath]: patientsApi.reducer, + patientsUI: patientsUIReducer + }, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().concat(patientsApi.middleware) +}) + + +export default store; diff --git a/web-app/src/types/appointmentPropType.jsx b/web-app/src/types/appointmentPropType.js similarity index 83% rename from web-app/src/types/appointmentPropType.jsx rename to web-app/src/types/appointmentPropType.js index 39002cf..afa13be 100644 --- a/web-app/src/types/appointmentPropType.jsx +++ b/web-app/src/types/appointmentPropType.js @@ -1,7 +1,7 @@ import PropTypes from "prop-types"; -import {PatientPropType} from "./patientPropType.jsx"; -import {UserPropType} from "./userPropType.jsx"; -import {AppointmentTypePropType} from "./appointmentTypePropType.jsx"; +import {PatientPropType} from "./patientPropType.js"; +import {UserPropType} from "./userPropType.js"; +import {AppointmentTypePropType} from "./appointmentTypePropType.js"; export const AppointmentPropType = PropTypes.shape({ id: PropTypes.number, diff --git a/web-app/src/types/appointmentTypePropType.jsx b/web-app/src/types/appointmentTypePropType.js similarity index 100% rename from web-app/src/types/appointmentTypePropType.jsx rename to web-app/src/types/appointmentTypePropType.js diff --git a/web-app/src/types/lensIssuePropType.jsx b/web-app/src/types/lensIssuePropType.js similarity index 70% rename from web-app/src/types/lensIssuePropType.jsx rename to web-app/src/types/lensIssuePropType.js index 0ee25d2..d6f9373 100644 --- a/web-app/src/types/lensIssuePropType.jsx +++ b/web-app/src/types/lensIssuePropType.js @@ -1,7 +1,7 @@ import PropTypes from "prop-types"; -import {PatientPropType} from "./patientPropType.jsx"; -import {UserPropType} from "./userPropType.jsx"; -import {LensPropType} from "./lensPropType.jsx"; +import {PatientPropType} from "./patientPropType.js"; +import {UserPropType} from "./userPropType.js"; +import {LensPropType} from "./lensPropType.js"; export const LensIssuePropType = PropTypes.shape({ id: PropTypes.number, diff --git a/web-app/src/types/lensPropType.jsx b/web-app/src/types/lensPropType.js similarity index 100% rename from web-app/src/types/lensPropType.jsx rename to web-app/src/types/lensPropType.js diff --git a/web-app/src/types/lensTypePropType.jsx b/web-app/src/types/lensTypePropType.js similarity index 100% rename from web-app/src/types/lensTypePropType.jsx rename to web-app/src/types/lensTypePropType.js diff --git a/web-app/src/types/patientPropType.jsx b/web-app/src/types/patientPropType.js similarity index 100% rename from web-app/src/types/patientPropType.jsx rename to web-app/src/types/patientPropType.js diff --git a/web-app/src/types/scheduledAppointmentPropType.jsx b/web-app/src/types/scheduledAppointmentPropType.js similarity index 80% rename from web-app/src/types/scheduledAppointmentPropType.jsx rename to web-app/src/types/scheduledAppointmentPropType.js index 8546500..b154714 100644 --- a/web-app/src/types/scheduledAppointmentPropType.jsx +++ b/web-app/src/types/scheduledAppointmentPropType.js @@ -1,7 +1,7 @@ import PropTypes from "prop-types"; -import {PatientPropType} from "./patientPropType.jsx"; -import {UserPropType} from "./userPropType.jsx"; -import {AppointmentTypePropType} from "./appointmentTypePropType.jsx"; +import {PatientPropType} from "./patientPropType.js"; +import {UserPropType} from "./userPropType.js"; +import {AppointmentTypePropType} from "./appointmentTypePropType.js"; export const ScheduledAppointmentPropType = PropTypes.shape({ id: PropTypes.number, diff --git a/web-app/src/types/setContentPropType.jsx b/web-app/src/types/setContentPropType.js similarity index 100% rename from web-app/src/types/setContentPropType.jsx rename to web-app/src/types/setContentPropType.js diff --git a/web-app/src/types/setPropType.jsx b/web-app/src/types/setPropType.js similarity index 100% rename from web-app/src/types/setPropType.jsx rename to web-app/src/types/setPropType.js diff --git a/web-app/src/types/userPropType.jsx b/web-app/src/types/userPropType.js similarity index 100% rename from web-app/src/types/userPropType.jsx rename to web-app/src/types/userPropType.js diff --git a/web-app/src/types/viewModPropType.jsx b/web-app/src/types/viewModPropType.js similarity index 100% rename from web-app/src/types/viewModPropType.jsx rename to web-app/src/types/viewModPropType.js diff --git a/web-app/src/utils/cachedInfoUtils.jsx b/web-app/src/utils/cachedInfoUtils.js similarity index 100% rename from web-app/src/utils/cachedInfoUtils.jsx rename to web-app/src/utils/cachedInfoUtils.js