From 793a47a58d041c9980c7ea410d41d4bfd48c60f9 Mon Sep 17 00:00:00 2001 From: andrei Date: Tue, 25 Mar 2025 11:44:05 +0500 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D1=81=D1=82=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=86=D1=8B=20=D0=BF=D0=B0=D1=86=D0=B8=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=BE=D0=B2=20=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=20=D1=84?= =?UTF-8?q?=D0=BD=D1=83=D0=BA=D1=86=D0=B8=D0=B8=20=D1=81=D0=B2=D1=8F=D0=B7?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D1=8B=D0=B5=20=D1=81=20API=20=D0=B2=20=D0=BE?= =?UTF-8?q?=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D1=85=D1=83?= =?UTF-8?q?=D0=BA,=20=D1=82=D0=B0=D0=BA=D0=B6=D0=B5=20=D0=B2=20UI=20=D1=85?= =?UTF-8?q?=D1=83=D0=BA=20=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=20=D1=84=D1=83?= =?UTF-8?q?=D0=BD=D0=BA=D1=86=D0=B8=D0=B8=20=D0=B8=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=BC=D0=B5=D0=BD=D0=BD=D1=8B=D0=B5=20=D1=81=D0=B2=D1=8F?= =?UTF-8?q?=D0=B7=D0=B0=D0=BD=D0=BD=D1=8B=D0=B5=20=D1=81=20UI=20=D0=B8=20?= =?UTF-8?q?=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8=D1=8F=D0=BC?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/patients/PatientListCard.jsx | 1 - web-app/src/hooks/useLenses.js | 90 ++++++++++ web-app/src/hooks/usePatients.js | 45 +---- web-app/src/hooks/usePatientsUI.js | 101 +++++++++++ web-app/src/pages/PatientsPage.jsx | 160 +++++++----------- .../src/pages/lenses_layout/LensesPage.jsx | 3 + web-app/src/redux/services/lensesApi.js | 53 ++++++ web-app/src/redux/services/patientsApi.js | 14 +- web-app/src/redux/slices/appointmentsSlice.js | 19 --- web-app/src/redux/slices/lensesSlice.js | 72 ++++++++ web-app/src/redux/slices/patientsSlice.js | 6 +- 11 files changed, 392 insertions(+), 172 deletions(-) create mode 100644 web-app/src/hooks/useLenses.js create mode 100644 web-app/src/hooks/usePatientsUI.js create mode 100644 web-app/src/redux/services/lensesApi.js delete mode 100644 web-app/src/redux/slices/appointmentsSlice.js create mode 100644 web-app/src/redux/slices/lensesSlice.js diff --git a/web-app/src/components/patients/PatientListCard.jsx b/web-app/src/components/patients/PatientListCard.jsx index e1187c0..230754b 100644 --- a/web-app/src/components/patients/PatientListCard.jsx +++ b/web-app/src/components/patients/PatientListCard.jsx @@ -10,7 +10,6 @@ const PatientListCard = ({patient, handleEditPatient, handleDeletePatient}) => { const birthday = new Date(patient.birthday) - const deletePatient = () => { handleDeletePatient(patient.id); }; diff --git a/web-app/src/hooks/useLenses.js b/web-app/src/hooks/useLenses.js new file mode 100644 index 0000000..cba0558 --- /dev/null +++ b/web-app/src/hooks/useLenses.js @@ -0,0 +1,90 @@ +import {useDispatch, useSelector} from "react-redux"; +import { + useAddLensMutation, + useDeleteLensMutation, + useGetLensesQuery, + useUpdateLensMutation +} from "../redux/services/lensesApi.js"; +import {useEffect} from "react"; +import { + closeModal, + setViewMode, +} from "../redux/slices/lensesSlice.js"; +import {getCachedInfo} from "../utils/cachedInfoUtils.js"; +import {notification} from "antd"; + + +const useLenses = () => { + const dispatch = useDispatch(); + + const { + selectedLens, + } = useSelector(state => state.lensesUI); + + const {data: lenses = [], isLoading, isError} = useGetLensesQuery(undefined, { + pollingInterval: 20000, + }); + const [addLens] = useAddLensMutation(); + const [updateLens] = useUpdateLensMutation(); + const [deleteLens] = useDeleteLensMutation(); + + useEffect(() => { + document.title = "Линзы"; + const cachedViewMode = getCachedInfo("viewModeLenses"); + if (cachedViewMode) dispatch(setViewMode(cachedViewMode)); + }, [dispatch]); + + const handleDeleteLens = async (lensId) => { + try { + await deleteLens(lensId).unwrap(); + notification.success({ + message: "Линза удалена", + description: "Линза успешно удалена.", + placement: "topRight", + }); + } catch (error) { + notification.error({ + message: "Ошибка удаления", + description: error.data?.message || "Не удалось удалить линзу", + placement: "topRight", + }); + } + }; + + const handleModalSubmit = async (lensData) => { + try { + if (selectedLens) { + await updateLens({id: selectedLens.id, ...lensData}).unwrap(); + notification.success({ + message: "Линза обновлена", + description: "Линза успешно обновлена.", + placement: "topRight", + }); + } else { + await addLens(lensData).unwrap(); + notification.success({ + message: "Линза добавлена", + description: "Линза успешно добавлена.", + placement: "topRight", + }); + } + dispatch(closeModal()); + } catch (error) { + notification.error({ + message: "Ошибка", + description: error.data?.message || "Произошла ошибка при сохранении", + placement: "topRight", + }); + } + }; + + return { + lenses, + isLoading, + isError, + handleDeleteLens, + handleModalSubmit, + } +}; + +export default useLenses; \ No newline at end of file diff --git a/web-app/src/hooks/usePatients.js b/web-app/src/hooks/usePatients.js index c2388a9..4241df0 100644 --- a/web-app/src/hooks/usePatients.js +++ b/web-app/src/hooks/usePatients.js @@ -1,4 +1,3 @@ -import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { notification } from "antd"; import { @@ -8,25 +7,13 @@ import { useUpdatePatientMutation } from "../redux/services/patientsApi"; import { - openModal, closeModal, - selectPatient, - setSearchText, - setSortOrder, - setViewMode, - setCurrentPage, - setPageSize } from "../redux/slices/patientsSlice"; -import { getCachedInfo } from "../utils/cachedInfoUtils"; const usePatients = () => { const dispatch = useDispatch(); const { - searchText, - sortOrder, - viewMode, selectedPatient, - isModalVisible } = useSelector(state => state.patientsUI); const { data: patients = [], isLoading, isError } = useGetPatientsQuery(undefined, { @@ -36,22 +23,6 @@ const usePatients = () => { const [updatePatient] = useUpdatePatientMutation(); const [deletePatient] = useDeletePatientMutation(); - useEffect(() => { - document.title = "Пациенты"; - const cachedViewMode = getCachedInfo("viewModePatients"); - if (cachedViewMode) dispatch(setViewMode(cachedViewMode)); - }, [dispatch]); - - const handleAddPatient = () => { - dispatch(selectPatient(null)); - dispatch(openModal()); - }; - - const handleEditPatient = (patient) => { - dispatch(selectPatient(patient)); - dispatch(openModal()); - }; - const handleDeletePatient = async (patientId) => { try { await deletePatient(patientId).unwrap(); @@ -69,7 +40,7 @@ const usePatients = () => { } }; - const handleModalPatientSubmit = async (patientData) => { + const handleModalSubmit = async (patientData) => { try { if (selectedPatient) { await updatePatient({ id: selectedPatient.id, ...patientData }).unwrap(); @@ -100,20 +71,8 @@ const usePatients = () => { patients, isLoading, isError, - searchText, - sortOrder, - viewMode, - isModalVisible, - selectedPatient, - handleAddPatient, - handleEditPatient, handleDeletePatient, - handleModalPatientSubmit, - setSearchText: (text) => dispatch(setSearchText(text)), - setSortOrder: (order) => dispatch(setSortOrder(order)), - setViewMode: (mode) => dispatch(setViewMode(mode)), - setCurrentPage: (page) => dispatch(setCurrentPage(page)), - setPageSize: (size) => dispatch(setPageSize(size)), + handleModalSubmit, }; }; diff --git a/web-app/src/hooks/usePatientsUI.js b/web-app/src/hooks/usePatientsUI.js new file mode 100644 index 0000000..6ede3ee --- /dev/null +++ b/web-app/src/hooks/usePatientsUI.js @@ -0,0 +1,101 @@ +import { useEffect, useMemo } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { + openModal, + selectPatient, + setCurrentPage, + setPageSize, + setSearchText, + setSortOrder, + setViewMode +} from "../redux/slices/patientsSlice"; +import { closeModal } from "../redux/slices/lensesSlice"; +import { getCachedInfo } from "../utils/cachedInfoUtils"; +import {BuildOutlined, TableOutlined} from "@ant-design/icons"; + +const usePatientsUI = (patients) => { + const dispatch = useDispatch(); + const { + searchText, + sortOrder, + viewMode, + selectedPatient, + isModalVisible, + currentPage, + pageSize, + } = useSelector(state => state.patientsUI); + + useEffect(() => { + document.title = "Пациенты"; + const cachedViewMode = getCachedInfo("viewModePatients"); + if (cachedViewMode) dispatch(setViewMode(cachedViewMode)); + }, [dispatch]); + + const containerStyle = { padding: 20 }; + const filterBarStyle = { marginBottom: 20 }; + const formItemStyle = { width: "100%" }; + + const handleSetSearchText = (value) => dispatch(setSearchText(value)); + const handleSetSortOrder = (value) => dispatch(setSortOrder(value)); + const handleSetCurrentPage = (page) => dispatch(setCurrentPage(page)); + const handleSetPageSize = (size) => dispatch(setPageSize(size)); + const handleSetViewMode = (mode) => dispatch(setViewMode(mode)); + const handleCloseModal = () => dispatch(closeModal()); + + const handlePaginationChange = (page, pageSize) => { + handleSetCurrentPage(page); + handleSetPageSize(pageSize); + }; + + const handleAddPatient = () => { + dispatch(selectPatient(null)); + dispatch(openModal()); + }; + + const handleEditPatient = (patient) => { + dispatch(selectPatient(patient)); + dispatch(openModal()); + }; + + const filteredPatients = useMemo(() => { + return patients + .filter(patient => + Object.values(patient) + .filter(value => typeof value === "string") + .some(value => value.toLowerCase().includes(searchText.toLowerCase())) + ) + .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); + }); + }, [patients, searchText, sortOrder]); + + const formatDate = (date) => new Date(date).toLocaleDateString(); + + + + return { + searchText, + sortOrder, + viewMode, + selectedPatient, + isModalVisible, + currentPage, + pageSize, + containerStyle, + filterBarStyle, + formItemStyle, + filteredPatients: patients.map(p => ({ ...p, key: p.id })), + handleSetSearchText, + handleSetSortOrder, + handleSetViewMode, + handleCloseModal, + handleAddPatient, + handleEditPatient, + formatDate, + handlePaginationChange, + }; +}; + +export default usePatientsUI; diff --git a/web-app/src/pages/PatientsPage.jsx b/web-app/src/pages/PatientsPage.jsx index ddceac4..512836a 100644 --- a/web-app/src/pages/PatientsPage.jsx +++ b/web-app/src/pages/PatientsPage.jsx @@ -1,4 +1,3 @@ -import {useEffect, useMemo} from 'react'; import { Input, Select, @@ -16,67 +15,23 @@ import { BuildOutlined, PlusOutlined, SortAscendingOutlined, - SortDescendingOutlined, - TableOutlined, + SortDescendingOutlined, TableOutlined, TeamOutlined } from "@ant-design/icons"; -import {useDispatch, useSelector} from "react-redux"; -import { - 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 SelectViewMode from "../components/SelectViewMode.jsx"; import LoadingIndicator from "../components/LoadingIndicator.jsx"; import usePatients from "../hooks/usePatients.js"; +import usePatientsUI from "../hooks/usePatientsUI.js"; const {Option} = Select; const {Title} = Typography; const PatientsPage = () => { - const dispatch = useDispatch(); - const { - patients, - isLoading, - isError, - handleAddPatient, - handleEditPatient, - handleDeletePatient, - handleModalPatientSubmit - } = usePatients(); + const patientsData = usePatients(); - const { - searchText, - sortOrder, - viewMode, - selectedPatient, - isModalVisible, - currentPage, - pageSize - } = useSelector(state => state.patientsUI); - - useEffect(() => { - document.title = "Пациенты"; - }, []); - - const filteredPatients = useMemo(() => { - return patients - .filter(patient => - Object.values(patient) - .filter(value => typeof value === "string") - .some(value => value.toLowerCase().includes(searchText.toLowerCase())) - ) - .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); - }); - }, [patients, searchText, sortOrder]); + const patientsUI = usePatientsUI(patientsData.patients); const columns = [ { @@ -97,41 +52,36 @@ const PatientsPage = () => { title: "Отчество", dataIndex: "patronymic", key: "patronymic", - sorter: (a, b) => a.patronymic.localeCompare(b.patronymic), sortDirections: ["ascend", "descend"], }, { title: "Дата рождения", dataIndex: "birthday", - key: "birthday", sorter: (a, b) => new Date(a.birthday).getTime() - new Date(b.birthday).getTime(), sortDirections: ["ascend", "descend"], - render: (date) => new Date(date).toLocaleDateString() + render: patientsUI.formatDate, }, { title: "Телефон", dataIndex: "phone", - key: "phone", }, { title: "Email", dataIndex: "email", - key: "email", }, { title: "Действия", - key: "actions", fixed: 'right', render: (_, record) => ( - + handleDeletePatient(record.id)} + onConfirm={() => patientsData.handleDeletePatient(record.id)} okText="Да, удалить" cancelText="Отмена" > @@ -156,31 +106,35 @@ const PatientsPage = () => { } ]; - if (isError) return ; + if (patientsData.isError) return ( + + ); return ( -
+
<TeamOutlined/> Пациенты - + dispatch(setSearchText(e.target.value))} - style={{width: "100%"}} + value={patientsUI.searchText} + onChange={(e) => patientsUI.handleSetSearchText(e.target.value)} + style={patientsUI.formItemStyle} allowClear /> - {viewMode === "tile" && ( + {patientsUI.viewMode === "tile" && ( - +