сменил расширение с .jsx на .js у файлов не содержащих компоненты. начал внедрять redux

This commit is contained in:
Андрей Дувакин 2025-03-24 12:46:39 +05:00
parent 396aee3ab7
commit 3dec01804a
66 changed files with 485 additions and 236 deletions

View File

@ -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",

View File

@ -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"
},

View File

@ -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 = () => (
<Router>
<AuthProvider>
<AppRouter/>
</AuthProvider>
</Router>
<Provider store={store}>
<Router>
<AuthProvider>
<AppRouter/>
</AuthProvider>
</Router>
</Provider>
);
export default App;

View File

@ -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);

View File

@ -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/`);

View File

@ -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, {

View File

@ -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);

View File

@ -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/`);

View File

@ -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/`);

View File

@ -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);

View File

@ -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}/`);

View File

@ -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/`);

View File

@ -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/`);

View File

@ -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);

View File

@ -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);

View File

@ -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}/`);

View File

@ -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/`);

View File

@ -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);

View File

@ -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/`);

View File

@ -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);

View File

@ -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}/`);

View File

@ -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);

View File

@ -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);

View File

@ -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}/`);

View File

@ -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}/`);

View File

@ -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/`);

View File

@ -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);

View File

@ -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;

View File

@ -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 (
<Modal
open={visible}
title={``}
>
const AppointmentCellViewModal = ({appointment}) => {
</Modal>
)
};
AppointmentCellViewModal.propTypes = {
appointment: PropTypes.oneOfType([ScheduledAppointmentPropType, AppointmentPropType]).isRequired,
};
export default AppointmentCellViewModal;

View File

@ -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}) => {

View File

@ -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;

View File

@ -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}) => {

View File

@ -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}) => {

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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 = () => {

View File

@ -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) => {

View File

@ -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;

View File

@ -5,5 +5,5 @@ import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App/>
</StrictMode>,
</StrictMode>
)

View File

@ -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;

View File

@ -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 = () => (
<List
grid={{
gutter: 16,
xs: 1,
sm: 1,
md: 2,
lg: 2,
xl: 3,
xxl: 3,
}}
dataSource={filteredPatients}
renderItem={(patient) => (
<List.Item>
<PatientListCard
patient={patient}
handleEditPatient={handleEditPatient}
handleDeletePatient={handleDeletePatient}
/>
</List.Item>
)}
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 = () => (
<List
grid={{
gutter: 16,
xs: 1,
sm: 1,
md: 2,
lg: 2,
xl: 3,
xxl: 3,
}}
dataSource={filteredPatients}
renderItem={(patient) => (
<List.Item>
<PatientListCard
patient={patient}
handleEditPatient={handleEditPatient}
handleDeletePatient={handleDeletePatient}
/>
</List.Item>
)}
pagination={{
current: 1,
pageSize: 10,
showSizeChanger: true,
pageSizeOptions: ["5", "10", "20", "50"],
onChange: (page, newPageSize) => {
dispatch(setCurrentPage(page));
dispatch(setPageSize(newPageSize));
},
}}
/>
);
if (isError) {
return (
<Result
status="error"
title="Ошибка"
subTitle="Произошла ошибка в работе страницы"
/>
);
}
return (
<div style={{padding: 20}}>
@ -296,7 +285,8 @@ const PatientsPage = () => {
<Col xs={24} md={14} sm={10} xl={18} xxl={19}>
<Input
placeholder="Поиск пациента"
onChange={(e) => setSearchText(e.target.value)}
value={searchText}
onChange={(e) => dispatch(setSearchText(e.target.value))}
style={{width: "100%"}}
allowClear
/>
@ -309,7 +299,7 @@ const PatientsPage = () => {
>
<Select
value={sortOrder}
onChange={(value) => setSortOrder(value)}
onChange={(value) => dispatch(setSortOrder(value))}
style={{width: "100%"}}
>
<Option value="asc"><SortAscendingOutlined/> А-Я</Option>
@ -330,7 +320,7 @@ const PatientsPage = () => {
}>
<SelectViewMode
viewMode={viewMode}
setViewMode={setViewMode}
setViewMode={(value) => dispatch(setViewMode(value))}
localStorageKey={"viewModePatients"}
toolTipText={"Формат отображения пациентов"}
viewModes={viewModes}
@ -338,7 +328,7 @@ const PatientsPage = () => {
</Col>
</Row>
{loading ? (
{isLoading ? (
<LoadingIndicator/>
) : viewMode === "tile" ? (
<TileView/>
@ -356,7 +346,7 @@ const PatientsPage = () => {
<PatientFormModal
visible={isModalVisible}
onCancel={handleCancel}
onCancel={() => dispatch(closeModal())}
onSubmit={handleModalPatientSubmit}
patient={selectedPatient}
/>

View File

@ -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;

View File

@ -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";

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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,