сделал заготовку страницы для выдачи линз

This commit is contained in:
Андрей Дувакин 2025-03-02 20:16:03 +05:00
parent 321dfd5628
commit a2fdc3f115
7 changed files with 194 additions and 18 deletions

View File

@ -5,6 +5,7 @@ import MainLayout from "./layouts/MainLayout.jsx";
import PatientsPage from "./pages/PatientsPage.jsx"; import PatientsPage from "./pages/PatientsPage.jsx";
import HomePage from "./pages/HomePage.jsx"; import HomePage from "./pages/HomePage.jsx";
import LensesLayout from "./layouts/LensesLayout.jsx"; import LensesLayout from "./layouts/LensesLayout.jsx";
import IssuesPage from "./pages/IssuesPage.jsx";
const AppRouter = () => ( const AppRouter = () => (
@ -15,6 +16,7 @@ const AppRouter = () => (
<Route element={<MainLayout/>}> <Route element={<MainLayout/>}>
<Route path={"/patients"} element={<PatientsPage/>}/> <Route path={"/patients"} element={<PatientsPage/>}/>
<Route path={"/lenses"} element={<LensesLayout/>}/> <Route path={"/lenses"} element={<LensesLayout/>}/>
<Route path={"/issues"} element={<IssuesPage/>}/>
<Route path={"/"} element={<HomePage/>}/> <Route path={"/"} element={<HomePage/>}/>
</Route> </Route>
</Route> </Route>

View File

@ -0,0 +1,21 @@
import axios from "axios";
import CONFIG from "../../core/Config.jsx";
const AddLensIssue = async (token, lens_issue) => {
try {
const response = await axios.post(`${CONFIG.BASE_URL}/lens_issues/`, lens_issue, {
headers: {
Authorization: `Bearer ${token}`,
},
});
return response.data;
} catch (error) {
if (error.response?.status === 403) {
throw new Error("Ошибка авторизации: пользователь неайден или токен недействителен");
}
throw new Error(error.message);
}
};
export default AddLensIssue;

View File

@ -0,0 +1,21 @@
import axios from "axios";
import CONFIG from "../../core/Config.jsx";
const GetAllLensIssues = async (token) => {
try {
const response = await axios.get(`${CONFIG.BASE_URL}/lens_issues/`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
return response.data;
} catch (error) {
if (error.response?.status === 403) {
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
}
throw new Error(error.message);
}
};
export default GetAllLensIssues;

View File

@ -26,7 +26,7 @@ const MainLayout = () => {
const menuItems = [ const menuItems = [
getItem("Главная", "/", <HomeOutlined/>), getItem("Главная", "/", <HomeOutlined/>),
getItem("Приёмы", "/appointments", <CalendarOutlined/>), getItem("Приёмы", "/appointments", <CalendarOutlined/>),
getItem("Выдачи линз", "/dispensing", <DatabaseOutlined/>), getItem("Выдачи линз", "/issues", <DatabaseOutlined/>),
getItem("Линзы и наборы", "/lenses", <FolderViewOutlined/>), getItem("Линзы и наборы", "/lenses", <FolderViewOutlined/>),
getItem("Пациенты", "/patients", <TeamOutlined/>), getItem("Пациенты", "/patients", <TeamOutlined/>),
getItem("Рассылки", "/mailing", <MessageOutlined/>), getItem("Рассылки", "/mailing", <MessageOutlined/>),

View File

@ -0,0 +1,126 @@
import {notification, Spin, Timeline, Input, Modal, Button} from "antd";
import getAllLensIssues from "../api/lens_issues/GetAllLensIssues.jsx";
import {useEffect, useState} from "react";
import {useAuth} from "../AuthContext.jsx";
import {LoadingOutlined} from "@ant-design/icons";
const IssuesPage = () => {
const {user} = useAuth();
const [loading, setLoading] = useState(true);
const [lensIssues, setLensIssues] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
const [selectedIssue, setSelectedIssue] = useState(null);
const [current, setCurrent] = useState(1);
const [pageSize, setPageSize] = useState(10);
useEffect(() => {
fetchLensIssuesWithCache();
}, []);
useEffect(() => {
const interval = setInterval(fetchLensIssues, 5000);
return () => clearInterval(interval);
}, [user]);
const fetchLensIssuesWithCache = async () => {
const cachedData = localStorage.getItem("lensIssuesData");
const cacheTimestamp = localStorage.getItem("lensIssuesTimestamp");
if (cachedData && cacheTimestamp && (Date.now() - parseInt(cacheTimestamp)) < 60 * 1000) {
setLensIssues(JSON.parse(cachedData));
setLoading(false);
} else {
await fetchLensIssues();
}
};
const fetchLensIssues = async () => {
try {
const data = await getAllLensIssues(user.token);
setLensIssues(data);
setLoading(false);
} catch (error) {
console.log(error);
notification.error({
message: "Ошибка загрузки данных",
description: "Проверьте подключение к сети.",
placement: "topRight",
});
setLoading(false);
}
};
const handleSearch = (e) => {
setSearchTerm(e.target.value.toLowerCase());
};
const filteredIssues = lensIssues.filter(issue =>
issue.patient.first_name.toLowerCase().includes(searchTerm) ||
issue.patient.last_name.toLowerCase().includes(searchTerm) ||
new Date(issue.issue_date).toLocaleDateString().includes(searchTerm)
);
return (
<div style={{padding: 20}}>
<Input.Search
placeholder="Поиск по пациенту или дате"
onChange={handleSearch}
style={{marginBottom: 20, width: 300}}
/>
{loading ? (
<div style={{display: "flex", justifyContent: "center", alignItems: "center", height: "100vh"}}>
<Spin indicator={<LoadingOutlined style={{fontSize: 64, color: "#1890ff"}} spin/>}/>
</div>
) : (
<Timeline
items={filteredIssues.map((issue) => (
{
key: issue.id,
label: new Date(issue.issue_date).toLocaleDateString(),
children: (
<div>
<p><b>Дата выдачи:</b> {new Date(issue.issue_date).toLocaleDateString()}</p>
<p><b>Пациент:</b> {issue.patient.last_name} {issue.patient.first_name}</p>
<p><b>Врач:</b> {issue.doctor.last_name} {issue.doctor.first_name}</p>
<p><b>Линза:</b> {issue.lens.side}, Диаметр: {issue.lens.diameter}</p>
<Button type="link" onClick={() => setSelectedIssue(issue)}>Подробнее</Button>
</div>
),
}
))}
pagination={{
current,
pageSize,
showSizeChanger: true,
pageSizeOptions: ["5", "10", "20", "50"],
onChange: (page, newPageSize) => {
setCurrent(page);
setPageSize(newPageSize);
},
}}
/>
)}
<Modal
visible={!!selectedIssue}
title="Детали выдачи линзы"
onCancel={() => setSelectedIssue(null)}
footer={null}
>
{selectedIssue && (
<div>
<p><b>Дата выдачи:</b> {new Date(selectedIssue.issue_date).toLocaleDateString()}</p>
<p><b>Пациент:</b> {selectedIssue.patient.last_name} {selectedIssue.patient.first_name}</p>
<p><b>Врач:</b> {selectedIssue.doctor.last_name} {selectedIssue.doctor.first_name}</p>
<p><b>Линза:</b> {selectedIssue.lens.side}, Диаметр: {selectedIssue.lens.diameter}</p>
</div>
)}
</Modal>
</div>
);
};
export default IssuesPage;

View File

@ -10,7 +10,7 @@ import {
Button, Button,
Form, Form,
InputNumber, InputNumber,
Card, Grid, notification, Table, Popconfirm Card, Grid, notification, Table, Popconfirm, Tooltip
} from "antd"; } from "antd";
import {LoadingOutlined, PlusOutlined, DownOutlined, UpOutlined} from "@ant-design/icons"; import {LoadingOutlined, PlusOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
import LensCard from "../components/lenses/LensListCard.jsx"; import LensCard from "../components/lenses/LensListCard.jsx";
@ -75,8 +75,6 @@ const LensesPage = () => {
}; };
const fetchLenses = async () => { const fetchLenses = async () => {
if (!user || !user.token) return;
try { try {
const data = await getAllLenses(user.token); const data = await getAllLenses(user.token);
setLenses(data); setLenses(data);
@ -334,14 +332,18 @@ const LensesPage = () => {
</Button> </Button>
</Col> </Col>
<Col xs={24} md={3} sm={4} xl={2}> <Col xs={24} md={3} sm={4} xl={2}>
<Select <Tooltip
value={viewMode} title={"Отображение линз"}
onChange={(value) => setViewMode(value)}
style={{width: "100%"}}
> >
<Option value={"tile"}>Плиткой</Option> <Select
<Option value={"table"}>Таблицей</Option> value={viewMode}
</Select> onChange={(value) => setViewMode(value)}
style={{width: "100%"}}
>
<Option value={"tile"}>Плиткой</Option>
<Option value={"table"}>Таблицей</Option>
</Select>
</Tooltip>
</Col> </Col>
</Row> </Row>

View File

@ -304,14 +304,18 @@ const PatientsPage = () => {
</Tooltip> </Tooltip>
</Col> </Col>
<Col xs={24} md={5} sm={8} xl={6}> <Col xs={24} md={5} sm={8} xl={6}>
<Select <Tooltip
value={viewMode} title={"Отображение пациентов"}
onChange={(value) => setViewMode(value)}
style={{width: "100%"}}
> >
<Option value={"tile"}>Плиткой</Option> <Select
<Option value={"table"}>Таблицей</Option> value={viewMode}
</Select> onChange={(value) => setViewMode(value)}
style={{width: "100%"}}
>
<Option value={"tile"}>Плиткой</Option>
<Option value={"table"}>Таблицей</Option>
</Select>
</Tooltip>
</Col> </Col>
</Row> </Row>