проверка работы gitea
This commit is contained in:
parent
c320437b00
commit
0a14842120
@ -1,4 +1,3 @@
|
||||
|
||||
from logging.config import fileConfig
|
||||
|
||||
from alembic import context
|
||||
@ -22,7 +21,6 @@ def get_url():
|
||||
async def run_migrations_online():
|
||||
connectable = create_async_engine(get_url(), poolclass=pool.NullPool, future=True)
|
||||
|
||||
|
||||
async with connectable.connect() as connection:
|
||||
await connection.run_sync(do_run_migrations)
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ from pydantic import BaseModel
|
||||
class RegisterEntity(BaseModel):
|
||||
first_name: str
|
||||
last_name: str
|
||||
patronymic: Optional[str]
|
||||
patronymic: Optional[str] = None
|
||||
role_id: int
|
||||
login: str
|
||||
password: str
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from sqlalchemy import Column, Integer, VARCHAR
|
||||
from sqlalchemy import Column, VARCHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.domain.models.base import BaseModel
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@ -12,18 +13,18 @@ from app.domain.models import User
|
||||
|
||||
class UsersService:
|
||||
def __init__(self, db: AsyncSession):
|
||||
self.user_repository = UsersRepository(db)
|
||||
self.role_repository = RolesRepository(db)
|
||||
self.users_repository = UsersRepository(db)
|
||||
self.roles_repository = RolesRepository(db)
|
||||
|
||||
async def register_user(self, register_entity: RegisterEntity) -> UserEntity:
|
||||
role = await self.role_repository.get_by_id(register_entity.role_id)
|
||||
async def register_user(self, register_entity: RegisterEntity) -> Optional[UserEntity]:
|
||||
role = await self.roles_repository.get_by_id(register_entity.role_id)
|
||||
if not role:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail='The role with this ID was not found'
|
||||
)
|
||||
|
||||
user = await self.user_repository.get_by_login(register_entity.login)
|
||||
user = await self.users_repository.get_by_login(register_entity.login)
|
||||
if user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
@ -45,7 +46,7 @@ class UsersService:
|
||||
)
|
||||
user_model.set_password(register_entity.password)
|
||||
|
||||
created_user = await self.user_repository.create(user_model)
|
||||
created_user = await self.users_repository.create(user_model)
|
||||
|
||||
return UserEntity(
|
||||
id=created_user.id,
|
||||
|
||||
27
web-app/src/Api/appointmentsApi.js
Normal file
27
web-app/src/Api/appointmentsApi.js
Normal file
@ -0,0 +1,27 @@
|
||||
import {createApi, fetchBaseQuery} from "@reduxjs/toolkit/query/react";
|
||||
import CONFIG from "../Core/сonfig.js";
|
||||
|
||||
|
||||
export const appointmentsApi = createApi({
|
||||
reducerPath: 'appointmentsApi',
|
||||
baseQuery: fetchBaseQuery({
|
||||
baseUrl: CONFIG.BASE_URL,
|
||||
prepareHeaders: (headers) => {
|
||||
const token = localStorage.getItem('access_token');
|
||||
if (token) headers.set('Authorization', `Bearer ${token}`);
|
||||
return headers;
|
||||
}
|
||||
}),
|
||||
tagTypes: ['Appointment'],
|
||||
endpoints: (builder) => ({
|
||||
getAppointments: builder.query({
|
||||
query: () => '/appointments/',
|
||||
providesTags: ['Appointment'],
|
||||
refetchOnMountOrArgChange: 5
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetAppointmentsQuery,
|
||||
} = appointmentsApi;
|
||||
25
web-app/src/Api/scheduledAppointmentsApi.js
Normal file
25
web-app/src/Api/scheduledAppointmentsApi.js
Normal file
@ -0,0 +1,25 @@
|
||||
import {createApi, fetchBaseQuery} from "@reduxjs/toolkit/query/react";
|
||||
import CONFIG from "../Core/сonfig.js";
|
||||
|
||||
|
||||
export const scheduledAppointmentsApi = createApi({
|
||||
reducerPath: 'scheduledAppointmentsApi',
|
||||
baseQuery: fetchBaseQuery({
|
||||
baseUrl: CONFIG.BASE_URL,
|
||||
prepareHeaders: (headers) => {
|
||||
const token = localStorage.getItem('access_token');
|
||||
if (token) headers.set('Authorization', `Bearer ${token}`);
|
||||
return headers;
|
||||
}
|
||||
}),
|
||||
tagTypes: ['ScheduledAppointment'],
|
||||
endpoints: (builder) => ({
|
||||
getScheduledAppointments: builder.query({
|
||||
query: () => `/scheduledAppointments`,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetScheduledAppointmentsQuery,
|
||||
} = scheduledAppointmentsApi;
|
||||
@ -4,12 +4,19 @@ import {AuthProvider} from "../Hooks/AuthContext.jsx";
|
||||
import "/src/Styles/app.css";
|
||||
import {Provider} from "react-redux";
|
||||
import store from "../Redux/store.js";
|
||||
import dayjs from "dayjs";
|
||||
import locale from 'antd/locale/ru_RU';
|
||||
import {ConfigProvider} from "antd";
|
||||
|
||||
dayjs.locale('ru');
|
||||
|
||||
const App = () => (
|
||||
<Provider store={store}>
|
||||
<Router>
|
||||
<AuthProvider>
|
||||
<ConfigProvider locale={locale}>
|
||||
<AppRouter/>
|
||||
</ConfigProvider>
|
||||
</AuthProvider>
|
||||
</Router>
|
||||
</Provider>
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import {Routes, Route, Navigate} from "react-router-dom";
|
||||
import PrivateRoute from "../Components/PrivateRoute.jsx";
|
||||
import LoginPage from "../Components/Pages/LoginPage.jsx";
|
||||
import PrivateRoute from "./PrivateRoute.jsx";
|
||||
import LoginPage from "../Components/Pages/LoginPage/LoginPage.jsx";
|
||||
import MainLayout from "../Components/Layouts/MainLayout.jsx";
|
||||
import PatientsPage from "../Components/Pages/PatientsPage/PatientsPage.jsx";
|
||||
import HomePage from "../Components/Pages/HomePage.jsx";
|
||||
import LensesLayout from "../Components/Layouts/LensesLayout.jsx";
|
||||
import LensesSetsPage from "../Components/Pages/LensesSetsPage/LensesSetsPage.jsx";
|
||||
import IssuesPage from "../Components/Pages/IssuesPage/IssuesPage.jsx";
|
||||
import AppointmentsLayout from "../Components/Layouts/AppointmentsLayout.jsx";
|
||||
import AppointmentsPage from "../Components/Pages/AppointmentsPage/AppointmentsPage.jsx";
|
||||
|
||||
|
||||
const AppRouter = () => (
|
||||
@ -16,9 +16,9 @@ const AppRouter = () => (
|
||||
<Route element={<PrivateRoute/>}>
|
||||
<Route element={<MainLayout/>}>
|
||||
<Route path={"/Patients"} element={<PatientsPage/>}/>
|
||||
<Route path={"/Lenses"} element={<LensesLayout/>}/>
|
||||
<Route path={"/Lenses"} element={<LensesSetsPage/>}/>
|
||||
<Route path={"/issues"} element={<IssuesPage/>}/>
|
||||
<Route path={"/Appointments"} element={<AppointmentsLayout/>}/>
|
||||
<Route path={"/Appointments"} element={<AppointmentsPage/>}/>
|
||||
<Route path={"/"} element={<HomePage/>}/>
|
||||
</Route>
|
||||
</Route>
|
||||
|
||||
@ -2,7 +2,7 @@ import {Card, Popconfirm, Tooltip} from "antd";
|
||||
import PropTypes from "prop-types";
|
||||
import {DeleteOutlined, EditOutlined, EyeOutlined} from "@ant-design/icons";
|
||||
import {useState} from "react";
|
||||
import LensViewModal from "../Pages/LensesPage/Components/LensViewModal/LensViewModal.jsx";
|
||||
import LensViewModal from "../Pages/LensesSetsPage/Components/LensesTab/Components/LensViewModal/LensViewModal.jsx";
|
||||
import {LensPropType} from "../../Types/lensPropType.js";
|
||||
|
||||
const LensListCard = ({lens, handleEditLens, handleDeleteLens}) => {
|
||||
|
||||
@ -1,166 +0,0 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Button, Grid, Tabs, Typography} from "antd";
|
||||
import {Splitter} from "antd";
|
||||
import {
|
||||
CalendarOutlined, TableOutlined, MenuFoldOutlined, MenuUnfoldOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import AppointmentsCalendarPage from "../Pages/AppointmentsCalendarPage.jsx";
|
||||
import AppointmentsTablePage from "../Pages/AppointmentsTablePage.jsx";
|
||||
import getAllAppointments from "../../old_api/appointments/getAllAppointments.js";
|
||||
import getAllScheduledAppointments from "../../old_api/scheduled_appointments/getAllScheduledAppointments.js";
|
||||
import {useAuth} from "../../Hooks/AuthContext.jsx";
|
||||
import LoadingIndicator from "../Widgets/LoadingIndicator.jsx";
|
||||
import {cacheInfo, getCachedInfo, getCacheTimestamp} from "../../Utils/cachedInfoUtils.js";
|
||||
|
||||
const {useBreakpoint} = Grid;
|
||||
|
||||
const AppointmentsLayout = () => {
|
||||
const [collapsed, setCollapsed] = useState(true);
|
||||
const [siderWidth, setSiderWidth] = useState(250);
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
const screens = useBreakpoint();
|
||||
const {api} = useAuth();
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const [appointments, setAppointments] = useState([]);
|
||||
const [scheduledAppointments, setScheduledAppointments] = useState([]);
|
||||
|
||||
const toggleSider = () => setCollapsed(!collapsed);
|
||||
|
||||
useEffect(() => {
|
||||
loadDataWithCache();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(loadData, 5000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
const loadData = async () => {
|
||||
await fetchAppointments();
|
||||
await fetchScheduledAppointments();
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const loadDataWithCache = async () => {
|
||||
await fetchAppointmentsWithCache();
|
||||
await fetchScheduledAppointmentsWithCache();
|
||||
};
|
||||
|
||||
const fetchAppointmentsWithCache = async () => {
|
||||
const cachedData = getCachedInfo("appointmentsData");
|
||||
const cacheTimestamp = getCacheTimestamp("appointmentsData");
|
||||
|
||||
if (cachedData && cacheTimestamp && (Date.now() - cacheTimestamp) < 60 * 1000) {
|
||||
setAppointments(cachedData);
|
||||
return;
|
||||
}
|
||||
|
||||
await fetchAppointments();
|
||||
};
|
||||
|
||||
const fetchAppointments = async () => {
|
||||
const data = await getAllAppointments(api);
|
||||
setAppointments(data);
|
||||
cacheInfo("appointmentsData", data);
|
||||
};
|
||||
|
||||
const fetchScheduledAppointmentsWithCache = async () => {
|
||||
const cachedData = getCachedInfo("scheduledAppointmentsData");
|
||||
const cacheTimestamp = getCacheTimestamp("scheduledAppointmentsData");
|
||||
|
||||
if (cachedData && cacheTimestamp && (Date.now() - cacheTimestamp) < 60 * 1000) {
|
||||
setScheduledAppointments(cachedData);
|
||||
return;
|
||||
}
|
||||
|
||||
await fetchScheduledAppointments();
|
||||
};
|
||||
|
||||
const fetchScheduledAppointments = async () => {
|
||||
const data = await getAllScheduledAppointments(api);
|
||||
setScheduledAppointments(data);
|
||||
cacheInfo("scheduledAppointmentsData", data);
|
||||
};
|
||||
|
||||
const items = [{
|
||||
key: "1",
|
||||
label: "Календарь приемов",
|
||||
children: <AppointmentsCalendarPage appointments={appointments} scheduledAppointments={scheduledAppointments}/>,
|
||||
icon: <CalendarOutlined/>,
|
||||
}, {
|
||||
key: "2",
|
||||
label: "Таблица приемов",
|
||||
children: <AppointmentsTablePage/>,
|
||||
icon: <TableOutlined/>,
|
||||
},];
|
||||
|
||||
if (loading) {
|
||||
return (<LoadingIndicator/>)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Splitter
|
||||
style={{flex: 1}}
|
||||
min={200}
|
||||
max={400}
|
||||
initial={siderWidth}
|
||||
onChange={setSiderWidth}
|
||||
>
|
||||
<Splitter.Panel style={{padding: 16}} defaultSize="80%" min="25%" max="90%">
|
||||
<Tabs defaultActiveKey="1" items={items}/>
|
||||
</Splitter.Panel>
|
||||
|
||||
{!collapsed && !screens.xs && (
|
||||
<Splitter.Panel
|
||||
style={{
|
||||
padding: "16px", borderLeft: "1px solid #ddd", overflowY: "auto",
|
||||
}}
|
||||
defaultSize="20%"
|
||||
min="20%"
|
||||
max="75%"
|
||||
>
|
||||
<Typography.Title level={3} style={{marginBottom: 36}}>
|
||||
Предстоящие события
|
||||
</Typography.Title>
|
||||
<p>Здесь будут предстоящие приемы...</p>
|
||||
</Splitter.Panel>
|
||||
)}
|
||||
</Splitter>
|
||||
<div
|
||||
style={{
|
||||
position: "fixed",
|
||||
right: 0,
|
||||
top: "50%",
|
||||
transform: "translateY(-50%)",
|
||||
transition: "right 0.3s ease",
|
||||
zIndex: 1000,
|
||||
display: screens.xs ? "none" : "block",
|
||||
}}
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={toggleSider}
|
||||
icon={collapsed ? <MenuUnfoldOutlined/> : <MenuFoldOutlined/>}
|
||||
style={{
|
||||
width: hovered ? 250 : 50,
|
||||
padding: hovered ? "0 20px" : "0",
|
||||
overflow: "hidden",
|
||||
textAlign: "left",
|
||||
transition: "width 0.3s ease, padding 0.3s ease",
|
||||
borderRadius: "4px 0 0 4px",
|
||||
}}
|
||||
>
|
||||
{hovered ? (collapsed ? "Показать предстоящие события" : "Скрыть предстоящие события") : ""}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppointmentsLayout;
|
||||
@ -1,80 +0,0 @@
|
||||
import {Calendar, Grid, ConfigProvider, Badge, Modal, Tag, Tooltip} from "antd";
|
||||
import {useState} from "react";
|
||||
import dayjs from "dayjs";
|
||||
import 'dayjs/locale/ru';
|
||||
import locale from 'antd/es/locale/ru_RU';
|
||||
import updateLocale from 'dayjs/plugin/updateLocale';
|
||||
import PropTypes, {arrayOf} from "prop-types";
|
||||
import CalendarCell from "../Widgets/CalendarCell.jsx";
|
||||
import {AppointmentPropType} from "../../Types/appointmentPropType.js";
|
||||
import {ScheduledAppointmentPropType} from "../../Types/scheduledAppointmentPropType.js";
|
||||
|
||||
const {useBreakpoint} = Grid;
|
||||
|
||||
dayjs.extend(updateLocale);
|
||||
dayjs.updateLocale('ru', {
|
||||
weekStart: 1
|
||||
});
|
||||
|
||||
const AppointmentsCalendarPage = ({appointments, scheduledAppointments}) => {
|
||||
const screens = useBreakpoint();
|
||||
const [selectedDate, setSelectedDate] = useState(dayjs(new Date()));
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [selectedAppointments, setSelectedAppointments] = useState([]);
|
||||
const [selectedAppointment, setSelectedAppointment] = useState(null);
|
||||
|
||||
const dateCellRender = (value) => {
|
||||
const date = value.format('YYYY-MM-DD');
|
||||
const appointmentsForDate = appointments.filter(app =>
|
||||
dayjs(app.appointment_datetime).format('YYYY-MM-DD') === date
|
||||
);
|
||||
const scheduledForDate = scheduledAppointments.filter(app =>
|
||||
dayjs(app.scheduled_datetime).format('YYYY-MM-DD') === date
|
||||
);
|
||||
|
||||
return (
|
||||
<CalendarCell
|
||||
appointments={appointmentsForDate}
|
||||
scheduledAppointments={scheduledForDate}
|
||||
onCellClick={() => {
|
||||
}}
|
||||
onItemClick={() => {
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const onSelect = (date) => {
|
||||
setSelectedDate(date);
|
||||
const selectedDateStr = date.format('YYYY-MM-DD');
|
||||
const appointmentsForDate = appointments.filter(app =>
|
||||
dayjs(app.appointment_datetime).format('YYYY-MM-DD') === selectedDateStr
|
||||
);
|
||||
const scheduledForDate = scheduledAppointments.filter(app =>
|
||||
dayjs(app.scheduled_datetime).format('YYYY-MM-DD') === selectedDateStr
|
||||
);
|
||||
setSelectedAppointments([...appointmentsForDate, ...scheduledForDate]);
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<ConfigProvider locale={locale}>
|
||||
<div style={{padding: 20}}>
|
||||
<Calendar
|
||||
fullscreen={!screens.xs}
|
||||
value={selectedDate}
|
||||
onSelect={onSelect}
|
||||
cellRender={dateCellRender}
|
||||
/>
|
||||
|
||||
</div>
|
||||
</ConfigProvider>
|
||||
);
|
||||
};
|
||||
|
||||
AppointmentsCalendarPage.propTypes = {
|
||||
appointments: PropTypes.arrayOf(AppointmentPropType).isRequired,
|
||||
scheduledAppointments: PropTypes.arrayOf(ScheduledAppointmentPropType).isRequired,
|
||||
};
|
||||
|
||||
export default AppointmentsCalendarPage;
|
||||
@ -0,0 +1,76 @@
|
||||
import {Button, Tabs, Typography} from "antd";
|
||||
import {Splitter} from "antd";
|
||||
import {
|
||||
CalendarOutlined, TableOutlined, MenuFoldOutlined, MenuUnfoldOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import AppointmentsCalendarTab from "./Components/AppointmentCalendarTab/AppointmentsCalendarTab.jsx";
|
||||
import AppointmentsTableTab from "./Components/AppointmentTableTab/AppointmentsTableTab.jsx";
|
||||
import useAppointmentsUI from "./useAppointmentsUI.js";
|
||||
|
||||
|
||||
const AppointmentsPage = () => {
|
||||
const appointmentsPageUI = useAppointmentsUI();
|
||||
|
||||
const items = [{
|
||||
key: "1",
|
||||
label: "Календарь приемов",
|
||||
children: <AppointmentsCalendarTab/>,
|
||||
icon: <CalendarOutlined/>,
|
||||
}, {
|
||||
key: "2",
|
||||
label: "Таблица приемов",
|
||||
children: <AppointmentsTableTab/>,
|
||||
icon: <TableOutlined/>,
|
||||
},];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Splitter
|
||||
style={appointmentsPageUI.splitterStyle}
|
||||
min={200}
|
||||
max={400}
|
||||
initial={appointmentsPageUI.siderWidth}
|
||||
onChange={appointmentsPageUI.setSiderWidth}
|
||||
>
|
||||
<Splitter.Panel
|
||||
style={appointmentsPageUI.splitterContentPanelStyle}
|
||||
defaultSize="80%"
|
||||
min="25%"
|
||||
max="90%"
|
||||
>
|
||||
<Tabs defaultActiveKey="1" items={items}/>
|
||||
</Splitter.Panel>
|
||||
|
||||
{appointmentsPageUI.showSplitterPanel && (
|
||||
<Splitter.Panel
|
||||
style={appointmentsPageUI.splitterSiderPanelStyle}
|
||||
defaultSize="20%"
|
||||
min="20%"
|
||||
max="75%"
|
||||
>
|
||||
<Typography.Title level={3} style={appointmentsPageUI.siderTitleStyle}>
|
||||
Предстоящие события
|
||||
</Typography.Title>
|
||||
<p>Здесь будут предстоящие приемы...</p>
|
||||
</Splitter.Panel>
|
||||
)}
|
||||
</Splitter>
|
||||
<div
|
||||
style={appointmentsPageUI.siderButtonContainerStyle}
|
||||
onMouseEnter={appointmentsPageUI.handleHoverSider}
|
||||
onMouseLeave={appointmentsPageUI.handleLeaveSider}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={appointmentsPageUI.handleToggleSider}
|
||||
icon={appointmentsPageUI.collapsed ? <MenuUnfoldOutlined/> : <MenuFoldOutlined/>}
|
||||
style={appointmentsPageUI.siderButtonStyle}
|
||||
>
|
||||
{appointmentsPageUI.siderButtonText}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppointmentsPage;
|
||||
@ -0,0 +1,40 @@
|
||||
import {Calendar} from "antd";
|
||||
import 'dayjs/locale/ru';
|
||||
import CalendarCell from "../../../../Widgets/CalendarCell.jsx";
|
||||
import useAppointments from "../../useAppointments.js";
|
||||
import useAppointmentCalendarUI from "./useAppointmentCalendarUI.js";
|
||||
|
||||
|
||||
const AppointmentsCalendarTab = () => {
|
||||
const appointmentsData = useAppointments();
|
||||
const appointmentsCalendarUI = useAppointmentCalendarUI(appointmentsData.appointments, appointmentsData.scheduledAppointments);
|
||||
|
||||
const dateCellRender = (value) => {
|
||||
const appointmentsForDate = appointmentsCalendarUI.getAppointmentsByListAndDate(appointmentsData.appointments, value);
|
||||
const scheduledForDate = appointmentsCalendarUI.getAppointmentsByListAndDate(appointmentsData.scheduledAppointments, value);
|
||||
|
||||
return (
|
||||
<CalendarCell
|
||||
appointments={appointmentsForDate}
|
||||
scheduledAppointments={scheduledForDate}
|
||||
onCellClick={() => {
|
||||
}}
|
||||
onItemClick={() => {
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={appointmentsCalendarUI.calendarContainerStyle}>
|
||||
<Calendar
|
||||
fullscreen={appointmentsCalendarUI.fullScreenCalendar}
|
||||
value={appointmentsCalendarUI.selectedDate}
|
||||
onSelect={appointmentsCalendarUI.onSelect}
|
||||
cellRender={dateCellRender}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppointmentsCalendarTab;
|
||||
@ -0,0 +1,61 @@
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import dayjs from "dayjs";
|
||||
import {
|
||||
openModal,
|
||||
setSelectedAppointments,
|
||||
setSelectedDate
|
||||
} from "../../../../../Redux/Slices/appointmentsSlice.js";
|
||||
import {Grid} from "antd";
|
||||
|
||||
const {useBreakpoint} = Grid;
|
||||
|
||||
const useAppointmentCalendarUI = (appointments, scheduledAppointments) => {
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
modalVisible,
|
||||
selectedAppointments,
|
||||
selectedAppointment,
|
||||
} = useSelector(state => state.appointmentsUI);
|
||||
const selectedDate = dayjs(useSelector(state => state.appointmentsUI.selectedDate));
|
||||
|
||||
|
||||
const screens = useBreakpoint();
|
||||
const fullScreenCalendar = !screens.xs;
|
||||
|
||||
const calendarContainerStyle = {padding: 20};
|
||||
|
||||
const onSelect = (date) => {
|
||||
const selectedDateStr = date.format('YYYY-MM-DD');
|
||||
dispatch(setSelectedDate(selectedDateStr));
|
||||
|
||||
console.log(appointments)
|
||||
const appointmentsForDate = appointments.filter(app =>
|
||||
dayjs(app.appointment_datetime).format('YYYY-MM-DD') === selectedDateStr
|
||||
);
|
||||
console.log(appointmentsForDate)
|
||||
|
||||
const scheduledForDate = scheduledAppointments.filter(app =>
|
||||
dayjs(app.scheduled_datetime).format('YYYY-MM-DD') === selectedDateStr
|
||||
);
|
||||
|
||||
dispatch(setSelectedAppointments([...appointmentsForDate, ...scheduledForDate]));
|
||||
dispatch(openModal());
|
||||
};
|
||||
|
||||
const getAppointmentsByListAndDate = (list, value) => {
|
||||
const date = value.format('YYYY-MM-DD');
|
||||
return list.filter(app =>
|
||||
dayjs(app.appointment_datetime).format('YYYY-MM-DD') === date
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
selectedDate,
|
||||
fullScreenCalendar,
|
||||
calendarContainerStyle,
|
||||
onSelect,
|
||||
getAppointmentsByListAndDate,
|
||||
};
|
||||
};
|
||||
|
||||
export default useAppointmentCalendarUI;
|
||||
@ -1,10 +1,8 @@
|
||||
import {useAuth} from "../../Hooks/AuthContext.jsx";
|
||||
import {useAuth} from "../../../../../Hooks/AuthContext.jsx";
|
||||
import {useEffect, useState} from "react";
|
||||
import getAllAppointments from "../../old_api/appointments/getAllAppointments.js";
|
||||
import {notification} from "antd";
|
||||
|
||||
|
||||
const AppointmentsTablePage = () => {
|
||||
const AppointmentsTableTab = () => {
|
||||
const {api} = useAuth();
|
||||
|
||||
const [appointments, setAppointments] = useState([]);
|
||||
@ -23,4 +21,4 @@ const AppointmentsTablePage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default AppointmentsTablePage;
|
||||
export default AppointmentsTableTab;
|
||||
@ -0,0 +1,29 @@
|
||||
import {useGetAppointmentsQuery} from "../../../Api/appointmentsApi.js";
|
||||
|
||||
|
||||
const useAppointments = () => {
|
||||
const {
|
||||
data: appointments = [],
|
||||
isLoadingAppointments,
|
||||
isErrorAppointments,
|
||||
} = useGetAppointmentsQuery(undefined, {
|
||||
pollingInterval: 20000,
|
||||
});
|
||||
|
||||
const {
|
||||
data: scheduledAppointments = [],
|
||||
isLoadingScheduledAppointments,
|
||||
isErrorScheduledAppointments,
|
||||
} = useGetAppointmentsQuery(undefined, {
|
||||
pollingInterval: 20000,
|
||||
});
|
||||
|
||||
return {
|
||||
appointments,
|
||||
scheduledAppointments,
|
||||
isLoading: isLoadingAppointments || isLoadingScheduledAppointments,
|
||||
isError: isErrorAppointments || isErrorScheduledAppointments,
|
||||
};
|
||||
};
|
||||
|
||||
export default useAppointments;
|
||||
@ -0,0 +1,68 @@
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {Grid} from "antd";
|
||||
import {setHovered, toggleSider} from "../../../Redux/Slices/appointmentsSlice.js";
|
||||
import {useEffect, useMemo} from "react";
|
||||
|
||||
const {useBreakpoint} = Grid;
|
||||
|
||||
const useAppointmentsUI = () => {
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
collapsed,
|
||||
siderWidth,
|
||||
hovered,
|
||||
} = useSelector(state => state.appointmentsUI);
|
||||
const screens = useBreakpoint();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = "Приемы";
|
||||
}, []);
|
||||
|
||||
const splitterStyle = {flex: 1};
|
||||
const splitterContentPanelStyle = {padding: 16};
|
||||
const splitterSiderPanelStyle = {padding: "16px", borderLeft: "1px solid #ddd", overflowY: "auto"};
|
||||
const siderTitleStyle = {marginBottom: 36};
|
||||
const siderButtonContainerStyle = {
|
||||
position: "fixed",
|
||||
right: 0,
|
||||
top: "50%",
|
||||
transform: "translateY(-50%)",
|
||||
transition: "right 0.3s ease",
|
||||
zIndex: 1000,
|
||||
display: screens.xs ? "none" : "block",
|
||||
};
|
||||
const siderButtonStyle = {
|
||||
width: hovered ? 250 : 50,
|
||||
padding: hovered ? "0 20px" : "0",
|
||||
overflow: "hidden",
|
||||
textAlign: "left",
|
||||
transition: "width 0.3s ease, padding 0.3s ease",
|
||||
borderRadius: "4px 0 0 4px",
|
||||
};
|
||||
|
||||
const handleToggleSider = () => dispatch(toggleSider());
|
||||
const handleHoverSider = () => dispatch(setHovered(true));
|
||||
const handleLeaveSider = () => dispatch(setHovered(false));
|
||||
|
||||
const siderButtonText = useMemo(() => hovered ? (collapsed ? "Показать предстоящие события" : "Скрыть предстоящие события") : "", [collapsed, hovered]);
|
||||
const showSplitterPanel = useMemo(() => !collapsed && !screens.xs, [collapsed, screens]);
|
||||
|
||||
return {
|
||||
collapsed,
|
||||
siderWidth,
|
||||
hovered,
|
||||
showSplitterPanel,
|
||||
siderButtonText,
|
||||
splitterStyle,
|
||||
splitterContentPanelStyle,
|
||||
splitterSiderPanelStyle,
|
||||
siderTitleStyle,
|
||||
siderButtonContainerStyle,
|
||||
siderButtonStyle,
|
||||
handleToggleSider,
|
||||
handleHoverSider,
|
||||
handleLeaveSider,
|
||||
};
|
||||
};
|
||||
|
||||
export default useAppointmentsUI;
|
||||
@ -2,13 +2,13 @@ import {
|
||||
Modal, Input, Button, Typography, Collapse, Steps, Row, Alert, Col, DatePicker, Spin
|
||||
} from "antd";
|
||||
import PropTypes from "prop-types";
|
||||
import useLensIssueFormModal from "./useLensIssueFormModal.js";
|
||||
import useLensIssueFormModalUI from "./useLensIssueFormModalUI.js";
|
||||
import useLensIssueForm from "./useLensIssueForm.js";
|
||||
import useLensIssueFormUI from "./useLensIssueFormUI.js";
|
||||
import {useMemo} from "react";
|
||||
|
||||
const LensIssueFormModal = ({visible, onCancel, onSubmit}) => {
|
||||
const lensIssueFormModalData = useLensIssueFormModal();
|
||||
const lensIssueFormModalUI = useLensIssueFormModalUI(visible, onCancel, onSubmit, lensIssueFormModalData.patients, lensIssueFormModalData.lenses);
|
||||
const lensIssueFormModalData = useLensIssueForm();
|
||||
const lensIssueFormModalUI = useLensIssueFormUI(visible, onCancel, onSubmit, lensIssueFormModalData.patients, lensIssueFormModalData.lenses);
|
||||
|
||||
const patientsItems = lensIssueFormModalUI.filteredPatients.map((patient) => ({
|
||||
key: patient.id,
|
||||
@ -182,7 +182,7 @@ const LensIssueFormModal = ({visible, onCancel, onSubmit}) => {
|
||||
}, {
|
||||
title: 'Выбор линзы', content: SelectLensStep,
|
||||
}, {
|
||||
title: 'Подтверждение', content: <ConfirmStep/>,
|
||||
title: 'Подтверждение', content: ConfirmStep,
|
||||
}];
|
||||
|
||||
return (
|
||||
|
||||
@ -2,7 +2,7 @@ import {useGetPatientsQuery} from "../../../../../Api/patientsApi.js";
|
||||
import {useGetNotIssuedLensesQuery} from "../../../../../Api/lensesApi.js";
|
||||
|
||||
|
||||
const useLensIssueFormModal = () => {
|
||||
const useLensIssueForm = () => {
|
||||
const {data: patients = [], isLoading: isLoadingPatients, isError: isErrorPatients} = useGetPatientsQuery(undefined);
|
||||
const {data: lenses = [], isLoading: isLoadingLenses, isError: isErrorLenses} = useGetNotIssuedLensesQuery(undefined);
|
||||
|
||||
@ -14,4 +14,4 @@ const useLensIssueFormModal = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export default useLensIssueFormModal;
|
||||
export default useLensIssueForm;
|
||||
@ -4,7 +4,7 @@ import {Grid, notification} from "antd";
|
||||
|
||||
const {useBreakpoint} = Grid;
|
||||
|
||||
const useLensIssueFormModalUI = (visible, onCancel, onSubmit, patients, lenses) => {
|
||||
const useLensIssueFormUI = (visible, onCancel, onSubmit, patients, lenses) => {
|
||||
const screens = useBreakpoint();
|
||||
|
||||
const [searchPatientString, setSearchPatientString] = useState("");
|
||||
@ -193,4 +193,4 @@ const useLensIssueFormModalUI = (visible, onCancel, onSubmit, patients, lenses)
|
||||
};
|
||||
};
|
||||
|
||||
export default useLensIssueFormModalUI;
|
||||
export default useLensIssueFormUI;
|
||||
@ -1,13 +1,13 @@
|
||||
import {Col, Form, InputNumber, Modal, Row, Select} from "antd";
|
||||
import PropTypes from "prop-types";
|
||||
import {LensPropType} from "../../../../../Types/lensPropType.js";
|
||||
import useLensFormModal from "./useLensFormModal.js";
|
||||
import useLensFormModalUI from "./useLensFormModalUI.js";
|
||||
import {LensPropType} from "../../../../../../../Types/lensPropType.js";
|
||||
import useLensForm from "./useLensForm.js";
|
||||
import useLensFormUI from "./useLensFormUI.js";
|
||||
|
||||
|
||||
const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
||||
const lensFormData = useLensFormModal();
|
||||
const lensFormUI = useLensFormModalUI(lensFormData.lensTypes, visible, onCancel, onSubmit, lens);
|
||||
const lensFormData = useLensForm();
|
||||
const lensFormUI = useLensFormUI(lensFormData.lensTypes, visible, onCancel, onSubmit, lens);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@ -1,10 +1,10 @@
|
||||
import {useGetLensTypesQuery} from "../../../../../Api/lensTypesApi.js";
|
||||
import {useGetLensTypesQuery} from "../../../../../../../Api/lensTypesApi.js";
|
||||
|
||||
|
||||
const useLensFormModal = () => {
|
||||
const useLensForm = () => {
|
||||
const {data: lensTypes = [], isLoading, isError} = useGetLensTypesQuery(undefined);
|
||||
|
||||
return {lensTypes, isLoading, isError};
|
||||
};
|
||||
|
||||
export default useLensFormModal;
|
||||
export default useLensForm;
|
||||
@ -2,7 +2,7 @@ import {useEffect} from "react";
|
||||
import {Form} from "antd";
|
||||
|
||||
|
||||
const useLensFormModalUI = (lensTypes, visible, onCancel, onSubmit, lens) => {
|
||||
const useLensFormUI = (lensTypes, visible, onCancel, onSubmit, lens) => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
useEffect(() => {
|
||||
@ -45,4 +45,4 @@ const useLensFormModalUI = (lensTypes, visible, onCancel, onSubmit, lens) => {
|
||||
};
|
||||
};
|
||||
|
||||
export default useLensFormModalUI;
|
||||
export default useLensFormUI;
|
||||
@ -1,6 +1,6 @@
|
||||
import {Button, Col, Modal, Row, Typography} from "antd";
|
||||
import PropTypes from "prop-types";
|
||||
import {LensPropType} from "../../../../../Types/lensPropType.js";
|
||||
import {LensPropType} from "../../../../../../../Types/lensPropType.js";
|
||||
|
||||
const {Text, Title} = Typography;
|
||||
|
||||
@ -22,10 +22,10 @@ import {
|
||||
TableOutlined,
|
||||
BuildOutlined
|
||||
} from "@ant-design/icons";
|
||||
import LensCard from "../../Dummies/LensListCard.jsx";
|
||||
import LensCard from "../../../../Dummies/LensListCard.jsx";
|
||||
import LensFormModal from "./Components/LensFormModal/LensFormModal.jsx";
|
||||
import SelectViewMode from "../../Widgets/SelectViewMode.jsx";
|
||||
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
||||
import SelectViewMode from "../../../../Widgets/SelectViewMode.jsx";
|
||||
import LoadingIndicator from "../../../../Widgets/LoadingIndicator.jsx";
|
||||
import useLenses from "./useLenses.js";
|
||||
import useLensesUI from "./useLensesUI.js";
|
||||
|
||||
@ -33,7 +33,7 @@ const {Option} = Select;
|
||||
const {useBreakpoint} = Grid;
|
||||
const {Title} = Typography;
|
||||
|
||||
const LensesPage = () => {
|
||||
const LensesTab = () => {
|
||||
const lensesData = useLenses();
|
||||
const lensesUI = useLensesUI(lensesData.lenses);
|
||||
|
||||
@ -332,4 +332,4 @@ const LensesPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default LensesPage;
|
||||
export default LensesTab;
|
||||
@ -4,9 +4,9 @@ import {
|
||||
useDeleteLensMutation,
|
||||
useGetLensesQuery,
|
||||
useUpdateLensMutation
|
||||
} from "../../../Api/lensesApi.js";
|
||||
} from "../../../../../Api/lensesApi.js";
|
||||
import {notification} from "antd";
|
||||
import {closeModal} from "../../../Redux/Slices/lensesSlice.js";
|
||||
import {closeModal} from "../../../../../Redux/Slices/lensesSlice.js";
|
||||
|
||||
|
||||
const useLenses = () => {
|
||||
@ -1,5 +1,5 @@
|
||||
import {useEffect, useMemo} from "react";
|
||||
import {getCachedInfo} from "../../../Utils/cachedInfoUtils.js";
|
||||
import {getCachedInfo} from "../../../../../Utils/cachedInfoUtils.js";
|
||||
import {
|
||||
closeModal,
|
||||
openModal,
|
||||
@ -7,7 +7,7 @@ import {
|
||||
setSearchParams,
|
||||
setSearchText, setShowAdvancedSearch,
|
||||
setViewMode
|
||||
} from "../../../Redux/Slices/lensesSlice.js";
|
||||
} from "../../../../../Redux/Slices/lensesSlice.js";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import {Modal, Button, Form, Input, Table, InputNumber, Select, Space, Result} from "antd";
|
||||
import {PlusOutlined, DeleteOutlined} from "@ant-design/icons";
|
||||
import PropTypes from "prop-types";
|
||||
import {SetPropType} from "../../../../../Types/setPropType.js";
|
||||
import useSetFormModal from "./useSetFormModal.js";
|
||||
import useSetFormModalUI from "./useSetFormModalUI.js";
|
||||
import {SetPropType} from "../../../../../../../Types/setPropType.js";
|
||||
import useSetForm from "./useSetForm.js";
|
||||
import useSetFormUI from "./useSetFormUI.js";
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
const SetFormModal = ({visible, onCancel, setData, onSubmit}) => {
|
||||
const setFormModalData = useSetFormModal(setData);
|
||||
const setFormModalUI = useSetFormModalUI(visible, onCancel, setData, onSubmit, setFormModalData.setContents, setFormModalData.lensTypes);
|
||||
const setFormModalData = useSetForm(setData);
|
||||
const setFormModalUI = useSetFormUI(visible, onCancel, setData, onSubmit, setFormModalData.setContents, setFormModalData.lensTypes);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@ -1,8 +1,8 @@
|
||||
import {useGetLensTypesQuery} from "../../../../../Api/lensTypesApi.js";
|
||||
import {useGetSetContentQuery} from "../../../../../Api/setContentApi.js";
|
||||
import {useGetLensTypesQuery} from "../../../../../../../Api/lensTypesApi.js";
|
||||
import {useGetSetContentQuery} from "../../../../../../../Api/setContentApi.js";
|
||||
|
||||
|
||||
const useSetFormModal = (setData) => {
|
||||
const useSetForm = (setData) => {
|
||||
const {data: lensTypes = [], isLoading: isLoadingLensTypes, isError: isErrorLensTypes} =
|
||||
useGetLensTypesQuery(undefined);
|
||||
|
||||
@ -24,4 +24,4 @@ const useSetFormModal = (setData) => {
|
||||
};
|
||||
};
|
||||
|
||||
export default useSetFormModal;
|
||||
export default useSetForm;
|
||||
@ -2,7 +2,7 @@ import {Form, notification} from "antd";
|
||||
import {useEffect, useState} from "react";
|
||||
|
||||
|
||||
const useSetFormModalUI = (visible, onCancel, setData, onSubmit, content, lensTypes) => {
|
||||
const useSetFormUI = (visible, onCancel, setData, onSubmit, content, lensTypes) => {
|
||||
const [form] = Form.useForm();
|
||||
const [currentContent, setCurrentContent] = useState([]);
|
||||
|
||||
@ -112,4 +112,4 @@ const useSetFormModalUI = (visible, onCancel, setData, onSubmit, content, lensTy
|
||||
};
|
||||
};
|
||||
|
||||
export default useSetFormModalUI;
|
||||
export default useSetFormUI;
|
||||
@ -1,15 +1,15 @@
|
||||
import {FloatButton, Input, List, Result, Row, Typography} from "antd";
|
||||
import {PlusOutlined, SwitcherOutlined} from "@ant-design/icons";
|
||||
import SetListCard from "../../Dummies/SetListCard.jsx";
|
||||
import SetListCard from "../../../../Dummies/SetListCard.jsx";
|
||||
import SetFormModal from "./Components/SetFormModal/SetFormModal.jsx";
|
||||
import LoadingIndicator from "../../Widgets/LoadingIndicator.jsx";
|
||||
import LoadingIndicator from "../../../../Widgets/LoadingIndicator.jsx";
|
||||
import useSets from "./useSets.js";
|
||||
import useSetsUI from "./useSetsUI.js";
|
||||
|
||||
|
||||
const {Title} = Typography;
|
||||
|
||||
const SetsPage = () => {
|
||||
const SetsTab = () => {
|
||||
const setsData = useSets();
|
||||
const setsUI = useSetsUI(setsData.sets);
|
||||
|
||||
@ -79,4 +79,4 @@ const SetsPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default SetsPage;
|
||||
export default SetsTab;
|
||||
@ -5,9 +5,9 @@ import {
|
||||
useDeleteSetMutation,
|
||||
useGetSetsQuery,
|
||||
useUpdateSetMutation
|
||||
} from "../../../Api/setsApi.js";
|
||||
import {closeModal} from "../../../Redux/Slices/setsSlice.js";
|
||||
import {useAddSetContentMutation, useUpdateSetContentMutation} from "../../../Api/setContentApi.js";
|
||||
} from "../../../../../Api/setsApi.js";
|
||||
import {closeModal} from "../../../../../Redux/Slices/setsSlice.js";
|
||||
import {useAddSetContentMutation, useUpdateSetContentMutation} from "../../../../../Api/setContentApi.js";
|
||||
|
||||
|
||||
const useSets = () => {
|
||||
@ -7,7 +7,7 @@ import {
|
||||
setCurrentPage,
|
||||
setPageSize,
|
||||
setSearchText
|
||||
} from "../../../Redux/Slices/setsSlice.js";
|
||||
} from "../../../../../Redux/Slices/setsSlice.js";
|
||||
|
||||
|
||||
const useSetsUI = (sets) => {
|
||||
@ -1,27 +1,27 @@
|
||||
import {
|
||||
Tabs
|
||||
} from "antd";
|
||||
import LensesPage from "../Pages/LensesPage/LensesPage.jsx";
|
||||
import LensesTab from "./Components/LensesTab/LensesTab.jsx";
|
||||
import {FolderViewOutlined, SwitcherOutlined} from "@ant-design/icons";
|
||||
import SetsPage from "../Pages/SetsPage/SetsPage.jsx";
|
||||
import SetsTab from "./Components/SetsTab/SetsTab.jsx";
|
||||
|
||||
|
||||
const items = [
|
||||
{
|
||||
key: '1',
|
||||
label: 'Линзы',
|
||||
children: <LensesPage/>,
|
||||
children: <LensesTab/>,
|
||||
icon: <FolderViewOutlined/>
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: 'Наборы линз',
|
||||
children: <SetsPage/>,
|
||||
children: <SetsTab/>,
|
||||
icon: <SwitcherOutlined/>
|
||||
}
|
||||
]
|
||||
|
||||
const LensesLayout = () => {
|
||||
const LensesSetsPage = () => {
|
||||
return (
|
||||
<Tabs
|
||||
defaultActiveKey="1"
|
||||
@ -30,4 +30,4 @@ const LensesLayout = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default LensesLayout;
|
||||
export default LensesSetsPage;
|
||||
@ -1,6 +1,6 @@
|
||||
import {Form, Input, Button, Row, Col, Typography} from 'antd';
|
||||
import {useEffect, useState} from 'react';
|
||||
import {useAuth} from "../../Hooks/AuthContext.jsx";
|
||||
import {useAuth} from "../../../Hooks/AuthContext.jsx";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
|
||||
const {Title} = Typography;
|
||||
@ -4,12 +4,12 @@ import locale from "antd/es/date-picker/locale/ru_RU";
|
||||
import {MaskedInput} from "antd-mask-input";
|
||||
import dayjs from "dayjs";
|
||||
import {PatientPropType} from "../../../../../Types/patientPropType.js";
|
||||
import usePatientFormModalUI from "./usePatientFormModalUI.js";
|
||||
import usePatientFormUI from "./usePatientFormUI.js";
|
||||
|
||||
const {TextArea} = Input;
|
||||
|
||||
const PatientFormModal = ({visible, onCancel, onSubmit, patient}) => {
|
||||
const patientFormModalUI = usePatientFormModalUI(visible, onCancel, onSubmit, patient);
|
||||
const patientFormModalUI = usePatientFormUI(visible, onCancel, onSubmit, patient);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
||||
@ -4,7 +4,7 @@ import dayjs from "dayjs";
|
||||
import validator from "validator";
|
||||
|
||||
|
||||
const usePatientFormModalUI = (visible, onCancel, onSubmit, patient) => {
|
||||
const usePatientFormUI = (visible, onCancel, onSubmit, patient) => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
useEffect(() => {
|
||||
@ -65,4 +65,4 @@ const usePatientFormModalUI = (visible, onCancel, onSubmit, patient) => {
|
||||
}
|
||||
};
|
||||
|
||||
export default usePatientFormModalUI;
|
||||
export default usePatientFormUI;
|
||||
@ -1,5 +1,5 @@
|
||||
import {useEffect, useRef, useState} from "react";
|
||||
import {Badge, Col, Tag, Tooltip, Typography} from "antd";
|
||||
import {Badge, Col, Tag, Tooltip} from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import PropTypes from "prop-types";
|
||||
import {AppointmentPropType} from "../../Types/appointmentPropType.js";
|
||||
@ -38,6 +38,7 @@ const CalendarCell = ({appointments, scheduledAppointments, onCellClick, onItemC
|
||||
{appointments.map(app => (
|
||||
<Col
|
||||
key={app.id}
|
||||
style={{overflowX: 'hidden'}}
|
||||
>
|
||||
<Tooltip
|
||||
title={`Прошедший прием: ${dayjs(app.appointment_datetime).format('HH:mm')}`}
|
||||
@ -48,11 +49,11 @@ const CalendarCell = ({appointments, scheduledAppointments, onCellClick, onItemC
|
||||
e.stopPropagation();
|
||||
onItemClick(app);
|
||||
}}
|
||||
style={{margin: '2px 0', cursor: 'pointer'}}
|
||||
style={{margin: '2px 2px 0 0', cursor: 'pointer', width: "95%", minHeight: 30}}
|
||||
>
|
||||
<Badge
|
||||
status="success"
|
||||
text={dayjs(app.appointment_datetime).format('HH:mm')}
|
||||
text={dayjs(app.scheduled_datetime).format('HH:mm') + ` ${app.patient.last_name} ${app.patient.first_name} `}
|
||||
/>
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
@ -61,6 +62,7 @@ const CalendarCell = ({appointments, scheduledAppointments, onCellClick, onItemC
|
||||
{scheduledAppointments.map(app => (
|
||||
<Col
|
||||
key={app.id}
|
||||
style={{overflowX: 'hidden'}}
|
||||
>
|
||||
<Tooltip
|
||||
title={`Запланированный прием: ${dayjs(app.scheduled_datetime).format('HH:mm')}`}
|
||||
@ -71,11 +73,11 @@ const CalendarCell = ({appointments, scheduledAppointments, onCellClick, onItemC
|
||||
e.stopPropagation();
|
||||
onItemClick(app);
|
||||
}}
|
||||
style={{margin: '2px 0', cursor: 'pointer'}}
|
||||
style={{margin: '2px 2px 0 0', cursor: 'pointer', width: "95%", minHeight: 30}}
|
||||
>
|
||||
<Badge
|
||||
status="processing"
|
||||
text={dayjs(app.scheduled_datetime).format('HH:mm')}
|
||||
text={dayjs(app.scheduled_datetime).format('HH:mm') + ` ${app.patient.last_name} ${app.patient.first_name}`}
|
||||
/>
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
|
||||
57
web-app/src/Redux/Slices/appointmentsSlice.js
Normal file
57
web-app/src/Redux/Slices/appointmentsSlice.js
Normal file
@ -0,0 +1,57 @@
|
||||
import {createSlice} from "@reduxjs/toolkit";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
|
||||
const initialState = {
|
||||
collapsed: true,
|
||||
siderWidth: 250,
|
||||
hovered: false,
|
||||
selectedDate: dayjs().format('YYYY-MM-DD'),
|
||||
modalVisible: false,
|
||||
selectedAppointments: [],
|
||||
selectedAppointment: null,
|
||||
};
|
||||
|
||||
const appointmentsSlice = createSlice({
|
||||
name: 'appointmentsUI',
|
||||
initialState,
|
||||
reducers: {
|
||||
toggleSider: (state) => {
|
||||
state.collapsed = !state.collapsed;
|
||||
},
|
||||
setSiderWidth: (state, action) => {
|
||||
state.siderWidth = action.payload;
|
||||
},
|
||||
setHovered: (state, action) => {
|
||||
state.hovered = action.payload;
|
||||
},
|
||||
setSelectedDate: (state, action) => {
|
||||
state.selectedDate = action.payload;
|
||||
},
|
||||
openModal: (state) => {
|
||||
state.modalVisible = true;
|
||||
},
|
||||
closeModal: (state) => {
|
||||
state.modalVisible = false;
|
||||
},
|
||||
setSelectedAppointments: (state, action) => {
|
||||
state.selectedAppointments = action.payload;
|
||||
},
|
||||
setSelectedAppointment: (state, action) => {
|
||||
state.selectedAppointment = action.payload;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
export const {
|
||||
toggleSider,
|
||||
setSiderWidth,
|
||||
setHovered,
|
||||
setSelectedDate,
|
||||
openModal,
|
||||
closeModal,
|
||||
setSelectedAppointments,
|
||||
setSelectedAppointment,
|
||||
} = appointmentsSlice.actions;
|
||||
|
||||
export default appointmentsSlice.reducer;
|
||||
@ -9,6 +9,8 @@ import {setContentApi} from "../Api/setContentApi.js";
|
||||
import {lensIssuesApi} from "../Api/lensIssuesApi.js";
|
||||
import lensIssuesReducer from "./Slices/lensIssuesSlice.js";
|
||||
import {lensTypesApi} from "../Api/lensTypesApi.js";
|
||||
import {appointmentsApi} from "../Api/appointmentsApi.js";
|
||||
import appointmentsReducer from "./Slices/appointmentsSlice.js";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
@ -27,6 +29,9 @@ export const store = configureStore({
|
||||
|
||||
[lensIssuesApi.reducerPath]: lensIssuesApi.reducer,
|
||||
lensIssuesUI: lensIssuesReducer,
|
||||
|
||||
[appointmentsApi.reducerPath]: appointmentsApi.reducer,
|
||||
appointmentsUI: appointmentsReducer,
|
||||
},
|
||||
middleware: (getDefaultMiddleware) => (
|
||||
getDefaultMiddleware().concat(
|
||||
@ -36,8 +41,9 @@ export const store = configureStore({
|
||||
setContentApi.middleware,
|
||||
lensTypesApi.middleware,
|
||||
lensIssuesApi.middleware,
|
||||
appointmentsApi.middleware,
|
||||
)
|
||||
)
|
||||
),
|
||||
});
|
||||
|
||||
export default store;
|
||||
@ -1,8 +0,0 @@
|
||||
import CONFIG from "../../Core/сonfig.js";
|
||||
|
||||
const getAllAppointments = async (api) => {
|
||||
const response = await api.get(`${CONFIG.BASE_URL}/appointments/`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default getAllAppointments;
|
||||
@ -1,8 +0,0 @@
|
||||
import CONFIG from "../../Core/сonfig.js";
|
||||
|
||||
const AddLensIssue = async (api, lens_issue) => {
|
||||
const response = await api.post(`${CONFIG.BASE_URL}/lens_issues/`, lens_issue);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default AddLensIssue;
|
||||
@ -1,8 +0,0 @@
|
||||
import CONFIG from "../../Core/сonfig.js";
|
||||
|
||||
const GetAllLensIssues = async (api) => {
|
||||
const response = await api.get(`${CONFIG.BASE_URL}/lens_issues/`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default GetAllLensIssues;
|
||||
@ -1,8 +0,0 @@
|
||||
import CONFIG from "../../Core/сonfig.js";
|
||||
|
||||
const getAllLensTypes = async (api) => {
|
||||
const response = await api.get(`${CONFIG.BASE_URL}/lens_types/`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default getAllLensTypes;
|
||||
@ -1,8 +0,0 @@
|
||||
import CONFIG from "../../Core/сonfig.js";
|
||||
|
||||
const getNotIssuedLenses = async (api) => {
|
||||
const response = await api.get(`${CONFIG.BASE_URL}/lenses/not_issued/`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default getNotIssuedLenses;
|
||||
@ -1,8 +0,0 @@
|
||||
import CONFIG from "../../Core/сonfig.js";
|
||||
|
||||
const getAllPatients = async (api) => {
|
||||
const response = await api.get(`${CONFIG.BASE_URL}/patients/`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default getAllPatients;
|
||||
@ -1,8 +0,0 @@
|
||||
import CONFIG from "../../Core/сonfig.js";
|
||||
|
||||
const getAllScheduledAppointments = async (api) => {
|
||||
const response = await api.get(`${CONFIG.BASE_URL}/scheduled_appointments/`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default getAllScheduledAppointments;
|
||||
@ -1,8 +0,0 @@
|
||||
import CONFIG from "../../Core/сonfig.js";
|
||||
|
||||
const getSetContentBySetId = async (api, set_id) => {
|
||||
const response = await api.get(`${CONFIG.BASE_URL}/set_content/${set_id}/`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default getSetContentBySetId;
|
||||
Loading…
x
Reference in New Issue
Block a user