From 94ef12b336ee432e2b0782f4d78b9d979edb64d4 Mon Sep 17 00:00:00 2001 From: andrei Date: Tue, 25 Mar 2025 13:03:21 +0500 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B0=D0=BB=20=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=83?= =?UTF-8?q?=20=D1=81=20=D0=BB=D0=B8=D0=BD=D0=B7=D0=B0=D0=BC=D0=B8=20=D1=81?= =?UTF-8?q?=20=D0=B8=D0=BC=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=D0=BC=20redux?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web-app/src/hooks/useLenses.js | 12 +- web-app/src/hooks/useLensesUI.js | 115 +++++++ web-app/src/hooks/usePatientsUI.js | 14 +- web-app/src/pages/PatientsPage.jsx | 21 +- .../src/pages/lenses_layout/LensesPage.jsx | 287 ++++-------------- web-app/src/redux/slices/lensesSlice.js | 4 +- web-app/src/redux/store.js | 21 +- 7 files changed, 209 insertions(+), 265 deletions(-) create mode 100644 web-app/src/hooks/useLensesUI.js diff --git a/web-app/src/hooks/useLenses.js b/web-app/src/hooks/useLenses.js index cba0558..6737292 100644 --- a/web-app/src/hooks/useLenses.js +++ b/web-app/src/hooks/useLenses.js @@ -5,12 +5,9 @@ import { useGetLensesQuery, useUpdateLensMutation } from "../redux/services/lensesApi.js"; -import {useEffect} from "react"; import { - closeModal, - setViewMode, + closeModal } from "../redux/slices/lensesSlice.js"; -import {getCachedInfo} from "../utils/cachedInfoUtils.js"; import {notification} from "antd"; @@ -24,16 +21,11 @@ const useLenses = () => { 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(); diff --git a/web-app/src/hooks/useLensesUI.js b/web-app/src/hooks/useLensesUI.js new file mode 100644 index 0000000..95c0a2a --- /dev/null +++ b/web-app/src/hooks/useLensesUI.js @@ -0,0 +1,115 @@ +import {useEffect} from "react"; +import {getCachedInfo} from "../utils/cachedInfoUtils.js"; +import { + closeModal, + openModal, + selectLens, setCurrentPage, setPageSize, + setSearchParams, + setSearchText, setShowAdvancedSearch, + setViewMode +} from "../redux/slices/lensesSlice.js"; +import {useDispatch, useSelector} from "react-redux"; + + +const useLensesUI = (lenses) => { + const dispatch = useDispatch(); + const { + searchText, + viewMode, + currentPage, + pageSize, + selectedLens, + isModalVisible, + showAdvancedSearch, + searchParams, + } = useSelector(state => state.lensesUI); + + useEffect(() => { + document.title = "Линзы"; + const cachedViewMode = getCachedInfo("viewModeLenses"); + if (cachedViewMode) dispatch(setViewMode(cachedViewMode)); + }, [dispatch]); + + const handleSetSearchText = (value) => dispatch(setSearchText(value)); + const handleCloseModal = () => dispatch(closeModal()); + const handleSetCurrentPage = (page) => dispatch(setCurrentPage(page)); + const handleSetPageSize = (size) => dispatch(setPageSize(size)); + const handleSetViewMode = (mode) => dispatch(setViewMode(mode)); + + const handleAddLens = () => { + dispatch(selectLens(null)); + dispatch(openModal()); + }; + + const handleEditLens = (lens) => { + dispatch(selectLens(lens)); + dispatch(openModal()); + }; + + const handleParamChange = (param, value) => { + dispatch(setSearchParams({...searchParams, [param]: value})); + }; + + const toggleAdvancedSearch = () => { + dispatch(setShowAdvancedSearch(!showAdvancedSearch)); + }; + + const handlePaginationChange = (page, pageSize) => { + handleSetCurrentPage(page); + handleSetPageSize(pageSize); + }; + + const filteredLenses = lenses.filter((lens) => { + const textMatch = Object.values(lens).some((value) => + value?.toString().toLowerCase().includes(searchText.toLowerCase()) + ); + + const advancedMatch = Object.entries(searchParams).every(([key, value]) => { + if (value === null || value === '') return true; + if (key === 'side') { + if (value === 'all') return true; + return lens.side === value; + } + if (key === 'issued') { + return lens.issued === value || value === "all"; + } + return lens[key] === value; + }); + + return textMatch && advancedMatch && (searchParams.issued || lens.issued === false); + }).sort((a, b) => { + return a.preset_refraction - b.preset_refraction; + }); + + const pagination = { + currentPage: currentPage, + pageSize: pageSize, + showSizeChanger: true, + pageSizeOptions: ["5", "10", "20", "50"], + onChange: (page, newPageSize) => { + handlePaginationChange(page, newPageSize); + }, + }; + + return { + searchText, + viewMode, + currentPage, + pageSize, + selectedLens, + isModalVisible, + showAdvancedSearch, + searchParams, + pagination, + filteredLenses: filteredLenses.map(lens => ({...lens, key: lens.id})), + handleSetSearchText, + handleAddLens, + handleEditLens, + handleCloseModal, + handleParamChange, + toggleAdvancedSearch, + handleSetViewMode, + } +}; + +export default useLensesUI; \ No newline at end of file diff --git a/web-app/src/hooks/usePatientsUI.js b/web-app/src/hooks/usePatientsUI.js index 6ede3ee..6421c31 100644 --- a/web-app/src/hooks/usePatientsUI.js +++ b/web-app/src/hooks/usePatientsUI.js @@ -11,7 +11,6 @@ import { } 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(); @@ -73,7 +72,15 @@ const usePatientsUI = (patients) => { const formatDate = (date) => new Date(date).toLocaleDateString(); - + const pagination = { + currentPage: currentPage, + pageSize: pageSize, + showSizeChanger: true, + pageSizeOptions: ["5", "10", "20", "50"], + onChange: (page, newPageSize) => { + handlePaginationChange(page, newPageSize); + }, + }; return { searchText, @@ -86,7 +93,8 @@ const usePatientsUI = (patients) => { containerStyle, filterBarStyle, formItemStyle, - filteredPatients: patients.map(p => ({ ...p, key: p.id })), + pagination, + filteredPatients: filteredPatients.map(p => ({ ...p, key: p.id })), handleSetSearchText, handleSetSortOrder, handleSetViewMode, diff --git a/web-app/src/pages/PatientsPage.jsx b/web-app/src/pages/PatientsPage.jsx index 512836a..0b31867 100644 --- a/web-app/src/pages/PatientsPage.jsx +++ b/web-app/src/pages/PatientsPage.jsx @@ -30,7 +30,6 @@ const {Title} = Typography; const PatientsPage = () => { const patientsData = usePatients(); - const patientsUI = usePatientsUI(patientsData.patients); const columns = [ @@ -170,29 +169,13 @@ const PatientsPage = () => { /> )} - pagination={{ - currentPage: patientsUI.currentPage, - pageSize: patientsUI.pageSize, - showSizeChanger: true, - pageSizeOptions: ["5", "10", "20", "50"], - onChange: (page, newPageSize) => { - patientsUI.handlePaginationChange(page, newPageSize); - }, - }} + pagination={patientsUI.pagination} /> ) : ( { - patientsUI.handlePaginationChange(page, newPageSize); - }, - }} + pagination={patientsUI.pagination} /> )} diff --git a/web-app/src/pages/lenses_layout/LensesPage.jsx b/web-app/src/pages/lenses_layout/LensesPage.jsx index 46b703f..7f89929 100644 --- a/web-app/src/pages/lenses_layout/LensesPage.jsx +++ b/web-app/src/pages/lenses_layout/LensesPage.jsx @@ -1,4 +1,3 @@ -import {useState, useEffect} from "react"; import { Input, Select, @@ -11,10 +10,9 @@ import { InputNumber, Card, Grid, - notification, Table, Popconfirm, - Typography + Typography, Result } from "antd"; import { PlusOutlined, @@ -25,188 +23,22 @@ import { BuildOutlined } from "@ant-design/icons"; import LensCard from "../../components/lenses/LensListCard.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.js"; -import {useDispatch} from "react-redux"; import useLenses from "../../hooks/useLenses.js"; -import {openModal, selectLens} from "../../redux/slices/lensesSlice.js"; +import useLensesUI from "../../hooks/useLensesUI.js"; const {Option} = Select; const {useBreakpoint} = Grid; const {Title} = Typography; const LensesPage = () => { - const {api} = useAuth(); + const lensesData = useLenses(); + const lensesUI = useLensesUI(lensesData.lenses); + const screens = useBreakpoint(); - const [current, setCurrent] = useState(1); - const [pageSize, setPageSize] = useState(10); - - const [searchText, setSearchText] = useState(""); - const [viewMode, setViewMode] = useState("tile"); - const [loading, setLoading] = useState(true); - const [isModalVisible, setIsModalVisible] = useState(false); - const [showAdvancedSearch, setShowAdvancedSearch] = useState(false); - - const [selectedLens, setSelectedLens] = useState(null); - const [lenses, setLenses] = useState([]); - - const [searchParams, setSearchParams] = useState({ - tor: null, - diameter: null, - preset_refraction: null, - periphery_toricity: null, - side: 'all', - issued: false, - trial: null - }); - - useEffect(() => { - fetchLensWithCache(); - fetchViewModeFromCache(); - document.title = "Линзы"; - }, []); - - useEffect(() => { - if (!isModalVisible && !selectedLens) { - const intervalId = setInterval(fetchLenses, 5000); - return () => clearInterval(intervalId); - } - }, [isModalVisible, selectedLens]); - - const fetchLensWithCache = async () => { - const cachedData = getCachedInfo("lensData"); - const cacheTimestamp = getCacheTimestamp("lensData"); - - if (cachedData && cacheTimestamp && (Date.now() - parseInt(cacheTimestamp)) < 60 * 1000) { - setLenses(JSON.parse(cachedData)); - setLoading(false); - return; - } - - await fetchLenses(); - }; - - const fetchLenses = async () => { - const data = await getAllLenses(api); - setLenses(data); - setLoading(false); - }; - - const fetchViewModeFromCache = () => { - const cachedViewMode = getCachedInfo("viewModeLenses"); - if (cachedViewMode) { - setViewMode(cachedViewMode); - } - }; - - const filteredLenses = lenses.filter((lens) => { - const textMatch = Object.values(lens).some((value) => - value?.toString().toLowerCase().includes(searchText.toLowerCase()) - ); - - const advancedMatch = Object.entries(searchParams).every(([key, value]) => { - if (value === null || value === '') return true; - if (key === 'side') { - if (value === 'all') return true; - return lens.side === value; - } - if (key === 'issued') { - return lens.issued === value || value === "all"; - } - return lens[key] === value; - }); - - return textMatch && advancedMatch && (searchParams.issued || lens.issued === false); - }).sort((a, b) => { - return a.preset_refraction - b.preset_refraction; - }); - - const handleAddLens = () => { - setSelectedLens(null); - setIsModalVisible(true); - }; - - const handleEditLens = (lens) => { - setSelectedLens(lens); - setIsModalVisible(true); - }; - - const handleDeleteLens = async (lensId) => { - await deleteLens(api, lensId); - await fetchLenses(api); - notification.success({ - message: "Линза удалена", - description: "Линза успешно удалена.", - placement: "topRight", - }) - }; - - const handleModalSubmit = async (lensData) => { - setIsModalVisible(false); - if (selectedLens) { - await updateLens(api, selectedLens.id, lensData); - notification.success({ - message: "Линза обновлена", - description: "Линза успешно обновлена.", - placement: "topRight", - }); - } else { - await addLens(api, lensData); - notification.success({ - message: "Линза добавлена", - description: "Линза успешно добавлена.", - placement: "topRight", - }); - } - await fetchLenses(); - }; - - const toggleAdvancedSearch = () => { - setShowAdvancedSearch(!showAdvancedSearch); - }; - - const handleParamChange = (param, value) => { - setSearchParams({...searchParams, [param]: value}); - }; - - const handleCancel = () => { - setIsModalVisible(false); - }; - - const TileView = () => ( - ( - - handleDeleteLens(lens.id)} - handleEditLens={() => handleEditLens(lens)} - /> - - )} - pagination={{ - current, - pageSize, - showSizeChanger: true, - pageSizeOptions: ["5", "10", "20", "50"], - onChange: (page, newPageSize) => { - setCurrent(page); - setPageSize(newPageSize); - }, - }} - /> - ); - const viewModes = [ { value: "tile", @@ -284,11 +116,11 @@ const LensesPage = () => { fixed: 'right', render: (text, record) => (
- + handleDeleteLens(record.id)} + onConfirm={() => lensesData.handleDeleteLens(record.id)} okText="Да, удалить" cancelText="Отмена" > @@ -299,24 +131,11 @@ const LensesPage = () => { }, ]; - const TableView = () => ( -
({...lens, key: lens.id}))} - scroll={{ - x: "max-content" - }} - showSorterTooltip={false} - pagination={{ - current, - pageSize, - showSizeChanger: true, - pageSizeOptions: ["5", "10", "20", "50"], - onChange: (page, newPageSize) => { - setCurrent(page); - setPageSize(newPageSize); - }, - }} + if (lensesData.isError) return ( + ); @@ -327,15 +146,16 @@ const LensesPage = () => { setSearchText(e.target.value)} + value={lensesUI.searchText} + onChange={(e) => lensesUI.handleSetSearchText(e.target.value)} style={{width: "100%"}} allowClear /> { - {showAdvancedSearch && ( + {lensesUI.showAdvancedSearch && ( {
handleParamChange("tor", value)} + value={lensesUI.searchParams.tor || 0} + onChange={(value) => lensesUI.handleParamChange("tor", value)} style={{width: "100%"}} defaultValue={0} step={0.1} @@ -376,8 +196,8 @@ const LensesPage = () => { handleParamChange("diameter", value)} + value={lensesUI.searchParams.diameter || 0} + onChange={(value) => lensesUI.handleParamChange("diameter", value)} style={{width: "100%"}} defaultValue={0} step={0.1} @@ -386,8 +206,8 @@ const LensesPage = () => { handleParamChange("preset_refraction", value)} + value={lensesUI.searchParams.preset_refraction || 0} + onChange={(value) => lensesUI.handleParamChange("preset_refraction", value)} style={{width: "100%"}} defaultValue={0} step={0.1} @@ -396,8 +216,8 @@ const LensesPage = () => { handleParamChange("periphery_toricity", value)} + value={lensesUI.searchParams.periphery_toricity || 0} + onChange={(value) => lensesUI.handleParamChange("periphery_toricity", value)} style={{width: "100%"}} defaultValue={0} step={0.1} @@ -410,8 +230,8 @@ const LensesPage = () => { handleParamChange("issued", value)} + value={lensesUI.searchParams.issued} + onChange={(value) => lensesUI.handleParamChange("issued", value)} style={{width: "100%"}} > @@ -434,8 +254,8 @@ const LensesPage = () => { handleParamChange("trial", value)} + value={lensesUI.searchParams.trial || 0} + onChange={(value) => lensesUI.handleParamChange("trial", value)} style={{width: "100%"}} defaultValue={0} step={0.1} @@ -449,7 +269,7 @@ const LensesPage = () => { type="primary" block={!screens.sm} onClick={() => { - setSearchParams({ + lensesUI.handleParamChange({ tor: null, diameter: null, preset_refraction: null, @@ -470,27 +290,48 @@ const LensesPage = () => {
)} - {loading ? ( + {lensesData.isLoading ? ( - ) : viewMode === "tile" ? ( - + ) : lensesUI.viewMode === "tile" ? ( + ( + + lensesData.handleDeleteLens(lens.id)} + handleEditLens={() => lensesUI.handleEditLens(lens)} + /> + + )} + pagination={lensesUI.pagination} + /> ) : ( - +
)} } type="primary" style={{position: "fixed", bottom: 40, right: 40}} - onClick={handleAddLens} + onClick={lensesUI.handleAddLens} tooltip="Добавить линзу" /> ); diff --git a/web-app/src/redux/slices/lensesSlice.js b/web-app/src/redux/slices/lensesSlice.js index 1468d68..f26bfc1 100644 --- a/web-app/src/redux/slices/lensesSlice.js +++ b/web-app/src/redux/slices/lensesSlice.js @@ -9,7 +9,7 @@ const initialState = { pageSize: 10, selectedLens: null, isModalVisible: false, - showAvancedSearch: false, + showAdvancedSearch: false, searchParams: { tor: null, diameter: null, @@ -52,7 +52,7 @@ const lensesSlice = createSlice({ state.searchParams = action.payload; }, setShowAdvancedSearch: (state, action) => { - state.showAvancedSearch = action.payload; + state.showAdvancedSearch = action.payload; }, } }); diff --git a/web-app/src/redux/store.js b/web-app/src/redux/store.js index d4e1c00..a42656c 100644 --- a/web-app/src/redux/store.js +++ b/web-app/src/redux/store.js @@ -1,15 +1,20 @@ -import { configureStore } from '@reduxjs/toolkit' -import {patientsApi} from "./services/patientsApi.js"; -import patientsUIReducer from './slices/patientsSlice.js' +import {configureStore} from '@reduxjs/toolkit'; +import {patientsApi} from './services/patientsApi.js'; +import patientsUIReducer from './slices/patientsSlice.js'; +import {lensesApi} from './services/lensesApi.js'; +import lensesReducer from './slices/lensesSlice.js'; export const store = configureStore({ reducer: { [patientsApi.reducerPath]: patientsApi.reducer, - patientsUI: patientsUIReducer - }, - middleware: (getDefaultMiddleware) => - getDefaultMiddleware().concat(patientsApi.middleware) -}) + patientsUI: patientsUIReducer, + [lensesApi.reducerPath]: lensesApi.reducer, + lensesUI: lensesReducer, + }, + middleware: (getDefaultMiddleware) => ( + getDefaultMiddleware().concat(patientsApi.middleware, lensesApi.middleware) + ) +}); export default store;