сделал заготовку страницы для выдачи линз
This commit is contained in:
parent
321dfd5628
commit
a2fdc3f115
@ -5,6 +5,7 @@ import MainLayout from "./layouts/MainLayout.jsx";
|
||||
import PatientsPage from "./pages/PatientsPage.jsx";
|
||||
import HomePage from "./pages/HomePage.jsx";
|
||||
import LensesLayout from "./layouts/LensesLayout.jsx";
|
||||
import IssuesPage from "./pages/IssuesPage.jsx";
|
||||
|
||||
|
||||
const AppRouter = () => (
|
||||
@ -15,6 +16,7 @@ const AppRouter = () => (
|
||||
<Route element={<MainLayout/>}>
|
||||
<Route path={"/patients"} element={<PatientsPage/>}/>
|
||||
<Route path={"/lenses"} element={<LensesLayout/>}/>
|
||||
<Route path={"/issues"} element={<IssuesPage/>}/>
|
||||
<Route path={"/"} element={<HomePage/>}/>
|
||||
</Route>
|
||||
</Route>
|
||||
|
||||
21
web-app/src/api/lens_issues/AddLensIssue.jsx
Normal file
21
web-app/src/api/lens_issues/AddLensIssue.jsx
Normal 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;
|
||||
21
web-app/src/api/lens_issues/GetAllLensIssues.jsx
Normal file
21
web-app/src/api/lens_issues/GetAllLensIssues.jsx
Normal 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;
|
||||
@ -26,7 +26,7 @@ const MainLayout = () => {
|
||||
const menuItems = [
|
||||
getItem("Главная", "/", <HomeOutlined/>),
|
||||
getItem("Приёмы", "/appointments", <CalendarOutlined/>),
|
||||
getItem("Выдачи линз", "/dispensing", <DatabaseOutlined/>),
|
||||
getItem("Выдачи линз", "/issues", <DatabaseOutlined/>),
|
||||
getItem("Линзы и наборы", "/lenses", <FolderViewOutlined/>),
|
||||
getItem("Пациенты", "/patients", <TeamOutlined/>),
|
||||
getItem("Рассылки", "/mailing", <MessageOutlined/>),
|
||||
|
||||
126
web-app/src/pages/IssuesPage.jsx
Normal file
126
web-app/src/pages/IssuesPage.jsx
Normal 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;
|
||||
@ -10,7 +10,7 @@ import {
|
||||
Button,
|
||||
Form,
|
||||
InputNumber,
|
||||
Card, Grid, notification, Table, Popconfirm
|
||||
Card, Grid, notification, Table, Popconfirm, Tooltip
|
||||
} from "antd";
|
||||
import {LoadingOutlined, PlusOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
|
||||
import LensCard from "../components/lenses/LensListCard.jsx";
|
||||
@ -75,8 +75,6 @@ const LensesPage = () => {
|
||||
};
|
||||
|
||||
const fetchLenses = async () => {
|
||||
if (!user || !user.token) return;
|
||||
|
||||
try {
|
||||
const data = await getAllLenses(user.token);
|
||||
setLenses(data);
|
||||
@ -334,6 +332,9 @@ const LensesPage = () => {
|
||||
</Button>
|
||||
</Col>
|
||||
<Col xs={24} md={3} sm={4} xl={2}>
|
||||
<Tooltip
|
||||
title={"Отображение линз"}
|
||||
>
|
||||
<Select
|
||||
value={viewMode}
|
||||
onChange={(value) => setViewMode(value)}
|
||||
@ -342,6 +343,7 @@ const LensesPage = () => {
|
||||
<Option value={"tile"}>Плиткой</Option>
|
||||
<Option value={"table"}>Таблицей</Option>
|
||||
</Select>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
@ -304,6 +304,9 @@ const PatientsPage = () => {
|
||||
</Tooltip>
|
||||
</Col>
|
||||
<Col xs={24} md={5} sm={8} xl={6}>
|
||||
<Tooltip
|
||||
title={"Отображение пациентов"}
|
||||
>
|
||||
<Select
|
||||
value={viewMode}
|
||||
onChange={(value) => setViewMode(value)}
|
||||
@ -312,6 +315,7 @@ const PatientsPage = () => {
|
||||
<Option value={"tile"}>Плиткой</Option>
|
||||
<Option value={"table"}>Таблицей</Option>
|
||||
</Select>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user