изменил архитектуру работы с api
This commit is contained in:
parent
3fc30f8e59
commit
2372083afe
@ -20,21 +20,21 @@ def start_app():
|
|||||||
|
|
||||||
api_app.add_middleware(
|
api_app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=['*'],
|
allow_origins=['http://localhost:5173'],
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=['GET', 'POST', 'PUT', 'DELETE'],
|
allow_methods=['*'],
|
||||||
allow_headers=['*'],
|
allow_headers=['*'],
|
||||||
)
|
)
|
||||||
|
|
||||||
api_app.include_router(appointments_types_router, prefix=f'{settings.APP_PREFIX}/appointment_types', tags=['appointment_types'])
|
api_app.include_router(appointments_types_router, prefix=f'{settings.APP_PREFIX}/appointment_types', tags=['appointment_types'])
|
||||||
api_app.include_router(appointment_router, prefix=f'{settings.APP_PREFIX}/appointments', tags=['appointments'])
|
api_app.include_router(appointment_router, prefix=f'{settings.APP_PREFIX}/appointments', tags=['appointments'])
|
||||||
api_app.include_router(auth_router, prefix=settings.APP_PREFIX, tags=['auth'])
|
api_app.include_router(auth_router, prefix=settings.APP_PREFIX, tags=['auth'])
|
||||||
api_app.include_router(lens_issues_router, prefix=f'{settings.APP_PREFIX}/lens_issue', tags=['lens_issue'])
|
api_app.include_router(lens_issues_router, prefix=f'{settings.APP_PREFIX}/lens_issues', tags=['lens_issue'])
|
||||||
api_app.include_router(lens_types_router, prefix=f'{settings.APP_PREFIX}/lens_types', tags=['lens_types'])
|
api_app.include_router(lens_types_router, prefix=f'{settings.APP_PREFIX}/lens_types', tags=['lens_types'])
|
||||||
api_app.include_router(lenses_router, prefix=f'{settings.APP_PREFIX}/lenses', tags=['lenses'])
|
api_app.include_router(lenses_router, prefix=f'{settings.APP_PREFIX}/lenses', tags=['lenses'])
|
||||||
api_app.include_router(patients_router, prefix=f'{settings.APP_PREFIX}/patients', tags=['patients'])
|
api_app.include_router(patients_router, prefix=f'{settings.APP_PREFIX}/patients', tags=['patients'])
|
||||||
api_app.include_router(register_router, prefix=f'{settings.APP_PREFIX}/register', tags=['register'])
|
api_app.include_router(register_router, prefix=f'{settings.APP_PREFIX}/register', tags=['register'])
|
||||||
api_app.include_router(scheduled_appointments_router, prefix=f'{settings.APP_PREFIX}/scheduled_appointments_router', tags=['scheduled_appointments_router'])
|
api_app.include_router(scheduled_appointments_router, prefix=f'{settings.APP_PREFIX}/scheduled_appointments', tags=['scheduled_appointments'])
|
||||||
api_app.include_router(set_content_router, prefix=f'{settings.APP_PREFIX}/set_content', tags=['set_content'])
|
api_app.include_router(set_content_router, prefix=f'{settings.APP_PREFIX}/set_content', tags=['set_content'])
|
||||||
api_app.include_router(sets_router, prefix=f'{settings.APP_PREFIX}/sets', tags=['sets'])
|
api_app.include_router(sets_router, prefix=f'{settings.APP_PREFIX}/sets', tags=['sets'])
|
||||||
|
|
||||||
|
|||||||
1446
web-app/package-lock.json
generated
1446
web-app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,6 +14,7 @@
|
|||||||
"@react-buddy/ide-toolbox": "^2.4.0",
|
"@react-buddy/ide-toolbox": "^2.4.0",
|
||||||
"@react-buddy/palette-antd": "^5.3.0",
|
"@react-buddy/palette-antd": "^5.3.0",
|
||||||
"antd": "^5.23.1",
|
"antd": "^5.23.1",
|
||||||
|
"antd-dayjs-webpack-plugin": "^1.0.6",
|
||||||
"antd-mask-input": "^2.0.7",
|
"antd-mask-input": "^2.0.7",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
|
|||||||
@ -4,11 +4,11 @@ import {AuthProvider} from "./AuthContext.jsx";
|
|||||||
import "/src/styles/app.css";
|
import "/src/styles/app.css";
|
||||||
|
|
||||||
const App = () => (
|
const App = () => (
|
||||||
<AuthProvider>
|
|
||||||
<Router>
|
<Router>
|
||||||
|
<AuthProvider>
|
||||||
<AppRouter/>
|
<AppRouter/>
|
||||||
</Router>
|
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
|
</Router>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default App
|
export default App;
|
||||||
|
|||||||
@ -24,6 +24,6 @@ const AppRouter = () => (
|
|||||||
</Route>
|
</Route>
|
||||||
<Route path={"*"} element={<Navigate to={"/"}/>}/>
|
<Route path={"*"} element={<Navigate to={"/"}/>}/>
|
||||||
</Routes>
|
</Routes>
|
||||||
)
|
);
|
||||||
|
|
||||||
export default AppRouter;
|
export default AppRouter;
|
||||||
@ -1,13 +1,16 @@
|
|||||||
import {createContext, useState, useContext, useEffect} from "react";
|
import {createContext, useState, useContext, useEffect} from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import loginUser from "./api/auth/LoginRequest.jsx";
|
import loginUser from "./api/auth/loginRequest.jsx";
|
||||||
import {Spin} from "antd";
|
import {Spin} from "antd";
|
||||||
|
import {useNavigate} from "react-router-dom";
|
||||||
|
import createApi from "./core/axiosConfig.jsx";
|
||||||
|
|
||||||
const AuthContext = createContext(undefined);
|
const AuthContext = createContext(undefined);
|
||||||
|
|
||||||
export const AuthProvider = ({children}) => {
|
const AuthProvider = ({children}) => {
|
||||||
const [user, setUser] = useState(null);
|
const [user, setUser] = useState(null);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const token = localStorage.getItem("access_token");
|
const token = localStorage.getItem("access_token");
|
||||||
@ -17,9 +20,10 @@ export const AuthProvider = ({children}) => {
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
const login = async (loginData) => {
|
const login = async (loginData) => {
|
||||||
try {
|
try {
|
||||||
const token = await loginUser(loginData);
|
const token = await loginUser(loginData, api);
|
||||||
localStorage.setItem("access_token", token);
|
localStorage.setItem("access_token", token);
|
||||||
setUser({token});
|
setUser({token});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -28,6 +32,12 @@ export const AuthProvider = ({children}) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const logoutAndRedirect = () => {
|
||||||
|
localStorage.removeItem("access_token");
|
||||||
|
setUser(null);
|
||||||
|
navigate("/login");
|
||||||
|
};
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
localStorage.removeItem("access_token");
|
localStorage.removeItem("access_token");
|
||||||
setUser(null);
|
setUser(null);
|
||||||
@ -37,8 +47,10 @@ export const AuthProvider = ({children}) => {
|
|||||||
return <Spin/>;
|
return <Spin/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const api = createApi(logoutAndRedirect);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthContext.Provider value={{user, login, logout}}>
|
<AuthContext.Provider value={{user, login, logout, logoutAndRedirect, api}}>
|
||||||
{children}
|
{children}
|
||||||
</AuthContext.Provider>
|
</AuthContext.Provider>
|
||||||
);
|
);
|
||||||
@ -48,6 +60,8 @@ AuthProvider.propTypes = {
|
|||||||
children: PropTypes.node.isRequired,
|
children: PropTypes.node.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useAuth = () => {
|
const useAuth = () => {
|
||||||
return useContext(AuthContext);
|
return useContext(AuthContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export {useAuth, AuthProvider};
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
|
|
||||||
const getAllAppointments = async (token) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${CONFIG.BASE_URL}/appointments/`, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь неайден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getAllAppointments;
|
|
||||||
8
web-app/src/api/appointments/getAllAppointments.jsx
Normal file
8
web-app/src/api/appointments/getAllAppointments.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const getAllAppointments = async (api) => {
|
||||||
|
const response = await api.get(`${CONFIG.BASE_URL}/appointments/`);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getAllAppointments;
|
||||||
@ -1,19 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
const loginUser = async (loginData) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.post(`${CONFIG.BASE_URL}/login/`, loginData, {
|
|
||||||
withCredentials: true,
|
|
||||||
});
|
|
||||||
return response.data.access_token;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Неверное имя пользователя или пароль")
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default loginUser;
|
|
||||||
14
web-app/src/api/auth/loginRequest.jsx
Normal file
14
web-app/src/api/auth/loginRequest.jsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const loginUser = async (loginData, api) => {
|
||||||
|
const response = await api.post(`${CONFIG.BASE_URL}/login/`, loginData, {
|
||||||
|
withCredentials: true,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.data.access_token;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default loginUser;
|
||||||
@ -1,21 +0,0 @@
|
|||||||
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.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь неайден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddLensIssue;
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
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.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GetAllLensIssues;
|
|
||||||
8
web-app/src/api/lens_issues/addLensIssue.jsx
Normal file
8
web-app/src/api/lens_issues/addLensIssue.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const AddLensIssue = async (api, lens_issue) => {
|
||||||
|
const response = await api.post(`${CONFIG.BASE_URL}/lens_issues/`, lens_issue);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddLensIssue;
|
||||||
8
web-app/src/api/lens_issues/getAllLensIssues.jsx
Normal file
8
web-app/src/api/lens_issues/getAllLensIssues.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const GetAllLensIssues = async (api) => {
|
||||||
|
const response = await api.get(`${CONFIG.BASE_URL}/lens_issues/`);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GetAllLensIssues;
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
|
|
||||||
const getAllLensTypes = async (token) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${CONFIG.BASE_URL}/lens_types/`, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getAllLensTypes;
|
|
||||||
8
web-app/src/api/lens_types/getAllLensTypes.jsx
Normal file
8
web-app/src/api/lens_types/getAllLensTypes.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const getAllLensTypes = async (api) => {
|
||||||
|
const response = await api.get(`${CONFIG.BASE_URL}/lens_types/`);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getAllLensTypes;
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
|
|
||||||
const addLens = async (token, lens) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.post(`${CONFIG.BASE_URL}/lenses/`, lens, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default addLens;
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
|
|
||||||
const deleteLens = async (token, lens_id) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.delete(`${CONFIG.BASE_URL}/lenses/${lens_id}/`, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default deleteLens;
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
const getAllLenses = async (token) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${CONFIG.BASE_URL}/lenses/`, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw Error("Ошибка авторизации: пользователь неяден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getAllLenses;
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
const getNotIssuedLenses = async (token) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${CONFIG.BASE_URL}/lenses/not_issued/`, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw Error("Ошибка авторизации: пользователь неяден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getNotIssuedLenses;
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
|
|
||||||
const updateLens = async (token, lensId, lensData) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.put(`${CONFIG.BASE_URL}/lenses/${lensId}/`, lensData, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default updateLens;
|
|
||||||
8
web-app/src/api/lenses/addLens.jsx
Normal file
8
web-app/src/api/lenses/addLens.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const addLens = async (api, lens) => {
|
||||||
|
const response = await api.post(`${CONFIG.BASE_URL}/lenses/`, lens);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default addLens;
|
||||||
8
web-app/src/api/lenses/deleteLens.jsx
Normal file
8
web-app/src/api/lenses/deleteLens.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const deleteLens = async (api, lens_id) => {
|
||||||
|
const response = await api.delete(`${CONFIG.BASE_URL}/lenses/${lens_id}/`);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default deleteLens;
|
||||||
8
web-app/src/api/lenses/getAllLenses.jsx
Normal file
8
web-app/src/api/lenses/getAllLenses.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const getAllLenses = async (api) => {
|
||||||
|
const response = await api.get(`${CONFIG.BASE_URL}/lenses/`);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getAllLenses;
|
||||||
8
web-app/src/api/lenses/getNotIssuedLenses.jsx
Normal file
8
web-app/src/api/lenses/getNotIssuedLenses.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const getNotIssuedLenses = async (api) => {
|
||||||
|
const response = await api.get(`${CONFIG.BASE_URL}/lenses/not_issued/`);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getNotIssuedLenses;
|
||||||
8
web-app/src/api/lenses/updateLens.jsx
Normal file
8
web-app/src/api/lenses/updateLens.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const updateLens = async (api, lensId, lensData) => {
|
||||||
|
const response = await api.put(`${CONFIG.BASE_URL}/lenses/${lensId}/`, lensData);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default updateLens;
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
|
|
||||||
const addPatient = async (token, patient) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.post(`${CONFIG.BASE_URL}/patients/`, patient, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default addPatient;
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
|
|
||||||
const deletePatient = async (token, patient_id) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.delete(`${CONFIG.BASE_URL}/patients/${patient_id}/`, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default deletePatient;
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
|
|
||||||
const getAllPatients = async (token) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${CONFIG.BASE_URL}/patients/`, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getAllPatients;
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
|
|
||||||
const updatePatient = async (token, patientId, patientData) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.put(`${CONFIG.BASE_URL}/patients/${patientId}/`, patientData, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default updatePatient;
|
|
||||||
8
web-app/src/api/patients/addPatient.jsx
Normal file
8
web-app/src/api/patients/addPatient.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const addPatient = async (api, patient) => {
|
||||||
|
const response = await api.post(`${CONFIG.BASE_URL}/patients/`, patient);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default addPatient;
|
||||||
8
web-app/src/api/patients/deletePatient.jsx
Normal file
8
web-app/src/api/patients/deletePatient.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const deletePatient = async (api, patient_id) => {
|
||||||
|
const response = await api.delete(`${CONFIG.BASE_URL}/patients/${patient_id}/`);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default deletePatient;
|
||||||
8
web-app/src/api/patients/getAllPatients.jsx
Normal file
8
web-app/src/api/patients/getAllPatients.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const getAllPatients = async (api) => {
|
||||||
|
const response = await api.get(`${CONFIG.BASE_URL}/patients/`);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getAllPatients;
|
||||||
8
web-app/src/api/patients/updatePatient.jsx
Normal file
8
web-app/src/api/patients/updatePatient.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const updatePatient = async (api, patientId, patientData) => {
|
||||||
|
const response = await api.put(`${CONFIG.BASE_URL}/patients/${patientId}/`, patientData);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default updatePatient;
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const getAllScheduledAppointments = async (api) => {
|
||||||
|
const response = await api.get(`${CONFIG.BASE_URL}/scheduled_appointments/`);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getAllScheduledAppointments;
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
|
|
||||||
const addSetContent = async (token, set_content, set_id) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.post(`${CONFIG.BASE_URL}/set_content/${set_id}/`, set_content, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default addSetContent;
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
|
|
||||||
const getSetContentBySetId = async (token, set_id) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${CONFIG.BASE_URL}/set_content/${set_id}/`, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getSetContentBySetId;
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
|
|
||||||
const updateSetContent = async (token, set_content, set_id) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.put(`${CONFIG.BASE_URL}/set_content/${set_id}/`, set_content, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default updateSetContent;
|
|
||||||
8
web-app/src/api/set_content/addSetContent.jsx
Normal file
8
web-app/src/api/set_content/addSetContent.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const addSetContent = async (api, set_content, set_id) => {
|
||||||
|
const response = await api.post(`${CONFIG.BASE_URL}/set_content/${set_id}/`, set_content);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default addSetContent;
|
||||||
8
web-app/src/api/set_content/getSetContentBySetId.jsx
Normal file
8
web-app/src/api/set_content/getSetContentBySetId.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const getSetContentBySetId = async (api, set_id) => {
|
||||||
|
const response = await api.get(`${CONFIG.BASE_URL}/set_content/${set_id}/`);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getSetContentBySetId;
|
||||||
8
web-app/src/api/set_content/updateSetContent.jsx
Normal file
8
web-app/src/api/set_content/updateSetContent.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const updateSetContent = async (api, set_content, set_id) => {
|
||||||
|
const response = await api.put(`${CONFIG.BASE_URL}/set_content/${set_id}/`, set_content);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default updateSetContent;
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
|
|
||||||
const addSet = async (token, set) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.post(`${CONFIG.BASE_URL}/sets/`, set, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default addSet;
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
|
|
||||||
const appendLensesFromSet = async (token, set_id) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.post(`${CONFIG.BASE_URL}/sets/append_lenses/${set_id}/`, {}, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default appendLensesFromSet;
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
const deleteSet = async (token, set_id) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.delete(`${CONFIG.BASE_URL}/sets/${set_id}/`, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default deleteSet;
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
const getAllSets = async (token) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${CONFIG.BASE_URL}/sets/`, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getAllSets;
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import CONFIG from "../../core/Config.jsx";
|
|
||||||
|
|
||||||
|
|
||||||
const updateSet = async (token, set_id, set) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.put(`${CONFIG.BASE_URL}/sets/${set_id}/`, set, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === 403 || error.status === 401) {
|
|
||||||
throw new Error("Ошибка авторизации: пользователь не найден или токен недействителен");
|
|
||||||
}
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default updateSet;
|
|
||||||
8
web-app/src/api/sets/addSet.jsx
Normal file
8
web-app/src/api/sets/addSet.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const addSet = async (api, set) => {
|
||||||
|
const response = await api.post(`${CONFIG.BASE_URL}/sets/`, set);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default addSet;
|
||||||
8
web-app/src/api/sets/appendLensesFromSet.jsx
Normal file
8
web-app/src/api/sets/appendLensesFromSet.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const appendLensesFromSet = async (api, set_id) => {
|
||||||
|
const response = await api.post(`${CONFIG.BASE_URL}/sets/append_lenses/${set_id}/`);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default appendLensesFromSet;
|
||||||
8
web-app/src/api/sets/deleteSet.jsx
Normal file
8
web-app/src/api/sets/deleteSet.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const deleteSet = async (api, set_id) => {
|
||||||
|
const response = await api.delete(`${CONFIG.BASE_URL}/sets/${set_id}/`);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default deleteSet;
|
||||||
8
web-app/src/api/sets/getAllSets.jsx
Normal file
8
web-app/src/api/sets/getAllSets.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const getAllSets = async (api) => {
|
||||||
|
const response = await api.get(`${CONFIG.BASE_URL}/sets/`);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getAllSets;
|
||||||
8
web-app/src/api/sets/updateSet.jsx
Normal file
8
web-app/src/api/sets/updateSet.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import CONFIG from "../../core/сonfig.jsx";
|
||||||
|
|
||||||
|
const updateSet = async (api, set_id, set) => {
|
||||||
|
const response = await api.put(`${CONFIG.BASE_URL}/sets/${set_id}/`, set);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default updateSet;
|
||||||
18
web-app/src/components/LoadingIndicator.jsx
Normal file
18
web-app/src/components/LoadingIndicator.jsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import {Spin} from "antd";
|
||||||
|
import {LoadingOutlined} from "@ant-design/icons";
|
||||||
|
|
||||||
|
|
||||||
|
const LoadingIndicator = () => {
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "100vh",
|
||||||
|
}}>
|
||||||
|
<Spin indicator={<LoadingOutlined style={{fontSize: 64, color: "#1890ff"}} spin/>}/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LoadingIndicator;
|
||||||
@ -11,4 +11,5 @@ const PrivateRoute = () => {
|
|||||||
return <Outlet/>;
|
return <Outlet/>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default PrivateRoute;
|
export default PrivateRoute;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import {BuildOutlined, TableOutlined} from "@ant-design/icons";
|
import {BuildOutlined, TableOutlined} from "@ant-design/icons";
|
||||||
import {Select, Tooltip} from "antd";
|
import {Select, Tooltip} from "antd";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
import {cacheInfo} from "../utils/cachedInfoUtils.jsx";
|
||||||
|
|
||||||
const {Option} = Select;
|
const {Option} = Select;
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ const SelectViewMode = ({viewMode, setViewMode, localStorageKey, toolTipText, vi
|
|||||||
value={viewMode}
|
value={viewMode}
|
||||||
onChange={value => {
|
onChange={value => {
|
||||||
setViewMode(value);
|
setViewMode(value);
|
||||||
localStorage.setItem(localStorageKey, value);
|
cacheInfo(localStorageKey, value);
|
||||||
}}
|
}}
|
||||||
style={{width: "100%"}}
|
style={{width: "100%"}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -3,15 +3,15 @@ import {
|
|||||||
Modal, Input, Button, notification, Typography, Collapse, Steps, Row, Alert, Col, DatePicker, Spin, Grid
|
Modal, Input, Button, notification, Typography, Collapse, Steps, Row, Alert, Col, DatePicker, Spin, Grid
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import getAllPatients from "../../api/patients/GetAllPatients.jsx";
|
import getAllPatients from "../../api/patients/getAllPatients.jsx";
|
||||||
import {useAuth} from "../../AuthContext.jsx";
|
import {useAuth} from "../../AuthContext.jsx";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import getNotIssuedLenses from "../../api/lenses/GetNotIssuedLenses.jsx";
|
import getNotIssuedLenses from "../../api/lenses/getNotIssuedLenses.jsx";
|
||||||
|
|
||||||
const {useBreakpoint} = Grid;
|
const {useBreakpoint} = Grid;
|
||||||
|
|
||||||
const LensIssueFormModal = ({visible, onCancel, onSubmit}) => {
|
const LensIssueFormModal = ({visible, onCancel, onSubmit}) => {
|
||||||
const {user} = useAuth();
|
const {api} = useAuth();
|
||||||
const screens = useBreakpoint();
|
const screens = useBreakpoint();
|
||||||
|
|
||||||
const [patients, setPatients] = useState([]);
|
const [patients, setPatients] = useState([]);
|
||||||
@ -37,7 +37,7 @@ const LensIssueFormModal = ({visible, onCancel, onSubmit}) => {
|
|||||||
|
|
||||||
const fetchPatients = async () => {
|
const fetchPatients = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await getAllPatients(user.token);
|
const data = await getAllPatients(api);
|
||||||
setPatients(data);
|
setPatients(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -49,7 +49,7 @@ const LensIssueFormModal = ({visible, onCancel, onSubmit}) => {
|
|||||||
|
|
||||||
const fetchLenses = async () => {
|
const fetchLenses = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await getNotIssuedLenses(user.token);
|
const data = await getNotIssuedLenses(api);
|
||||||
setLenses(data);
|
setLenses(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import {Col, Form, InputNumber, Modal, notification, Row, Select} from "antd";
|
import {Col, Form, InputNumber, Modal, notification, Row, Select} from "antd";
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import getAllLensTypes from "../../api/lens_types/GetAllLensTypes.jsx";
|
import getAllLensTypes from "../../api/lens_types/getAllLensTypes.jsx";
|
||||||
import {useAuth} from "../../AuthContext.jsx";
|
import {useAuth} from "../../AuthContext.jsx";
|
||||||
|
|
||||||
|
|
||||||
const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
||||||
const {user} = useAuth();
|
const {api} = useAuth();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [lensTypes, setLensTypes] = useState([]);
|
const [lensTypes, setLensTypes] = useState([]);
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
|||||||
|
|
||||||
const fetchLensTypes = async () => {
|
const fetchLensTypes = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await getAllLensTypes(user.token);
|
const data = await getAllLensTypes(api);
|
||||||
setLensTypes(data);
|
setLensTypes(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|||||||
@ -2,15 +2,15 @@ import {useState, useEffect} from "react";
|
|||||||
import {Modal, Button, Form, Input, Table, InputNumber, Select, Space, notification} from "antd";
|
import {Modal, Button, Form, Input, Table, InputNumber, Select, Space, notification} from "antd";
|
||||||
import {PlusOutlined, DeleteOutlined} from "@ant-design/icons";
|
import {PlusOutlined, DeleteOutlined} from "@ant-design/icons";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import getAllLensTypes from "../../api/lens_types/GetAllLensTypes.jsx";
|
import getAllLensTypes from "../../api/lens_types/getAllLensTypes.jsx";
|
||||||
import {useAuth} from "../../AuthContext.jsx";
|
import {useAuth} from "../../AuthContext.jsx";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import getSetContentBySetId from "../../api/set_content/GetSetContentBySetId.jsx";
|
import getSetContentBySetId from "../../api/set_content/getSetContentBySetId.jsx";
|
||||||
|
|
||||||
const {Option} = Select;
|
const {Option} = Select;
|
||||||
|
|
||||||
const SetFormModal = ({visible, onCancel, setData, onSubmit}) => {
|
const SetFormModal = ({visible, onCancel, setData, onSubmit}) => {
|
||||||
const {user} = useAuth();
|
const {api} = useAuth();
|
||||||
|
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [content, setContent] = useState([]);
|
const [content, setContent] = useState([]);
|
||||||
@ -33,7 +33,7 @@ const SetFormModal = ({visible, onCancel, setData, onSubmit}) => {
|
|||||||
if (!setData) return;
|
if (!setData) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await getSetContentBySetId(user.token, setData.id);
|
const data = await getSetContentBySetId(api, setData.id);
|
||||||
setContent(data);
|
setContent(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
@ -47,7 +47,7 @@ const SetFormModal = ({visible, onCancel, setData, onSubmit}) => {
|
|||||||
|
|
||||||
const fetchLensTypes = async () => {
|
const fetchLensTypes = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await getAllLensTypes(user.token);
|
const data = await getAllLensTypes(api);
|
||||||
setLensTypes(data);
|
setLensTypes(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
@ -278,5 +278,4 @@ SetFormModal.propTypes = {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default SetFormModal;
|
export default SetFormModal;
|
||||||
|
|||||||
@ -7,7 +7,6 @@ const SetListCard = ({set, handleEditSet, handleDeleteSet, handleAppendSet}) =>
|
|||||||
handleDeleteSet(set.id);
|
handleDeleteSet(set.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const appendSet = () => {
|
const appendSet = () => {
|
||||||
handleAppendSet(set);
|
handleAppendSet(set);
|
||||||
};
|
};
|
||||||
|
|||||||
58
web-app/src/core/axiosConfig.jsx
Normal file
58
web-app/src/core/axiosConfig.jsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import CONFIG from "./сonfig.jsx";
|
||||||
|
import {notification} from "antd";
|
||||||
|
|
||||||
|
const createApi = (logoutAndRedirect) => {
|
||||||
|
const api = axios.create({
|
||||||
|
baseURL: CONFIG.BASE_URL,
|
||||||
|
});
|
||||||
|
|
||||||
|
api.interceptors.request.use(config => {
|
||||||
|
const token = localStorage.getItem('access_token');
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
|
api.interceptors.response.use(
|
||||||
|
(response) => response,
|
||||||
|
(error) => {
|
||||||
|
if (error.response) {
|
||||||
|
const {status} = error.response;
|
||||||
|
|
||||||
|
if (status === 401 || status === 403) {
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка авторизации",
|
||||||
|
description: "Пользователь не найден или токен недействителен",
|
||||||
|
});
|
||||||
|
|
||||||
|
logoutAndRedirect();
|
||||||
|
} else {
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка API",
|
||||||
|
description: error.response.data.message || "Что-то пошло не так",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (error.request) {
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка соединения",
|
||||||
|
description: "Сервер не отвечает. Проверьте соединение с сетью.",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
notification.error({
|
||||||
|
message: "Неизвестная ошибка",
|
||||||
|
description: "Проверьте соединение с сетью.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return api;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createApi;
|
||||||
@ -1,33 +1,181 @@
|
|||||||
import {useAuth} from "../AuthContext.jsx";
|
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import getAllAppointments from "../api/appointments/GetAllAppointments.jsx";
|
import {Button, Grid, notification, Tabs, Typography} from "antd";
|
||||||
import {notification, Tabs} from "antd";
|
import {Splitter} from "antd";
|
||||||
|
import {
|
||||||
|
CalendarOutlined, TableOutlined, MenuFoldOutlined, MenuUnfoldOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
import AppointmentsCalendarPage from "../pages/appointments_layout/AppointmentsCalendarPage.jsx";
|
import AppointmentsCalendarPage from "../pages/appointments_layout/AppointmentsCalendarPage.jsx";
|
||||||
import {CalendarOutlined, TableOutlined} from "@ant-design/icons";
|
|
||||||
import AppointmentsTablePage from "../pages/appointments_layout/AppointmentsTablePage.jsx";
|
import AppointmentsTablePage from "../pages/appointments_layout/AppointmentsTablePage.jsx";
|
||||||
|
import getAllAppointments from "../api/appointments/getAllAppointments.jsx";
|
||||||
|
import getAllScheduledAppointments from "../api/scheduled_appointments/getAllScheduledAppointments.jsx";
|
||||||
|
import {useAuth} from "../AuthContext.jsx";
|
||||||
|
import LoadingIndicator from "../components/LoadingIndicator.jsx";
|
||||||
|
import {cacheInfo, getCachedInfo, getCacheTimestamp} from "../utils/cachedInfoUtils.jsx";
|
||||||
|
|
||||||
|
const {useBreakpoint} = Grid;
|
||||||
|
|
||||||
const AppointmentsLayout = () => {
|
const AppointmentsLayout = () => {
|
||||||
const items = [
|
const [collapsed, setCollapsed] = useState(true);
|
||||||
{
|
const [siderWidth, setSiderWidth] = useState(250);
|
||||||
key: '1',
|
const [hovered, setHovered] = useState(false);
|
||||||
label: 'Календарь приемов',
|
|
||||||
children: <AppointmentsCalendarPage/>,
|
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 () => {
|
||||||
|
try {
|
||||||
|
const data = await getAllAppointments(api);
|
||||||
|
setAppointments(data);
|
||||||
|
|
||||||
|
cacheInfo("appointmentsData", data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка загрузки данных", description: "Проверьте подключение к сети.", placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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 () => {
|
||||||
|
try {
|
||||||
|
const data = await getAllScheduledAppointments(api);
|
||||||
|
setScheduledAppointments(data);
|
||||||
|
|
||||||
|
cacheInfo("scheduledAppointmentsData", data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка загрузки данных", description: "Проверьте подключение к сети.", placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const items = [{
|
||||||
|
key: "1",
|
||||||
|
label: "Календарь приемов",
|
||||||
|
children: <AppointmentsCalendarPage appointments={appointments} scheduledAppointments={scheduledAppointments}/>,
|
||||||
icon: <CalendarOutlined/>,
|
icon: <CalendarOutlined/>,
|
||||||
},
|
}, {
|
||||||
{
|
key: "2",
|
||||||
key: '2',
|
label: "Таблица приемов",
|
||||||
label: 'Таблица приемов',
|
|
||||||
children: <AppointmentsTablePage/>,
|
children: <AppointmentsTablePage/>,
|
||||||
icon: <TableOutlined/>,
|
icon: <TableOutlined/>,
|
||||||
|
},];
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (<LoadingIndicator/>)
|
||||||
}
|
}
|
||||||
]
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<>
|
||||||
defaultActiveKey="1"
|
<Splitter
|
||||||
items={items}
|
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>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
notification,
|
notification,
|
||||||
Spin,
|
|
||||||
Table,
|
Table,
|
||||||
Input,
|
Input,
|
||||||
Row,
|
Row,
|
||||||
@ -12,21 +11,23 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
Timeline, Grid, Pagination
|
Timeline, Grid, Pagination
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import getAllLensIssues from "../api/lens_issues/GetAllLensIssues.jsx";
|
import getAllLensIssues from "../api/lens_issues/getAllLensIssues.jsx";
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import {useAuth} from "../AuthContext.jsx";
|
import {useAuth} from "../AuthContext.jsx";
|
||||||
import {DatabaseOutlined, LoadingOutlined, PlusOutlined, UnorderedListOutlined} from "@ant-design/icons";
|
import {DatabaseOutlined, PlusOutlined, UnorderedListOutlined} from "@ant-design/icons";
|
||||||
import LensIssueViewModal from "../components/lens_issues/LensIssueViewModal.jsx";
|
import LensIssueViewModal from "../components/lens_issues/LensIssueViewModal.jsx";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import LensIssueFormModal from "../components/lens_issues/LensIssueFormModal.jsx";
|
import LensIssueFormModal from "../components/lens_issues/LensIssueFormModal.jsx";
|
||||||
import addLensIssue from "../api/lens_issues/AddLensIssue.jsx";
|
import addLensIssue from "../api/lens_issues/addLensIssue.jsx";
|
||||||
import SelectViewMode from "../components/SelectViewMode.jsx";
|
import SelectViewMode from "../components/SelectViewMode.jsx";
|
||||||
|
import LoadingIndicator from "../components/LoadingIndicator.jsx";
|
||||||
|
import {getCachedInfo, getCacheTimestamp} from "../utils/cachedInfoUtils.jsx";
|
||||||
|
|
||||||
const {Title} = Typography;
|
const {Title} = Typography;
|
||||||
const {useBreakpoint} = Grid;
|
const {useBreakpoint} = Grid;
|
||||||
|
|
||||||
const IssuesPage = () => {
|
const IssuesPage = () => {
|
||||||
const {user} = useAuth();
|
const {api} = useAuth();
|
||||||
const screens = useBreakpoint();
|
const screens = useBreakpoint();
|
||||||
|
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@ -56,13 +57,13 @@ const IssuesPage = () => {
|
|||||||
const intervalId = setInterval(fetchLensIssues, 5000);
|
const intervalId = setInterval(fetchLensIssues, 5000);
|
||||||
return () => clearInterval(intervalId);
|
return () => clearInterval(intervalId);
|
||||||
}
|
}
|
||||||
}, [user, isModalVisible]);
|
}, [isModalVisible]);
|
||||||
|
|
||||||
const fetchLensIssuesWithCache = async () => {
|
const fetchLensIssuesWithCache = async () => {
|
||||||
const cachedData = localStorage.getItem("lensIssuesData");
|
const cachedData = getCachedInfo("lensIssuesData");
|
||||||
const cacheTimestamp = localStorage.getItem("lensIssuesTimestamp");
|
const cacheTimestamp = getCacheTimestamp("lensIssuesData");
|
||||||
|
|
||||||
if (cachedData && cacheTimestamp && (Date.now() - parseInt(cacheTimestamp)) < 60 * 1000) {
|
if (cachedData && cacheTimestamp && (Date.now() - cacheTimestamp) < 60 * 1000) {
|
||||||
setLensIssues(JSON.parse(cachedData));
|
setLensIssues(JSON.parse(cachedData));
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
} else {
|
} else {
|
||||||
@ -71,7 +72,7 @@ const IssuesPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fetchViewModeFromCache = () => {
|
const fetchViewModeFromCache = () => {
|
||||||
const cachedViewMode = localStorage.getItem("viewModeIssues");
|
const cachedViewMode = getCachedInfo("viewModeIssues");
|
||||||
if (cachedViewMode) {
|
if (cachedViewMode) {
|
||||||
setViewMode(cachedViewMode);
|
setViewMode(cachedViewMode);
|
||||||
}
|
}
|
||||||
@ -92,7 +93,7 @@ const IssuesPage = () => {
|
|||||||
|
|
||||||
const handleSubmitFormModal = async (issue_date, patient_id, lens_id) => {
|
const handleSubmitFormModal = async (issue_date, patient_id, lens_id) => {
|
||||||
try {
|
try {
|
||||||
await addLensIssue(user.token, {issue_date, patient_id, lens_id});
|
await addLensIssue(api, {issue_date, patient_id, lens_id});
|
||||||
setIsModalVisible(false);
|
setIsModalVisible(false);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "Линза выдана",
|
message: "Линза выдана",
|
||||||
@ -112,7 +113,7 @@ const IssuesPage = () => {
|
|||||||
|
|
||||||
const fetchLensIssues = async () => {
|
const fetchLensIssues = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await getAllLensIssues(user.token);
|
const data = await getAllLensIssues(api);
|
||||||
setLensIssues(data);
|
setLensIssues(data);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -345,9 +346,7 @@ const IssuesPage = () => {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div style={{display: "flex", justifyContent: "center", alignItems: "center", height: "100vh"}}>
|
<LoadingIndicator/>
|
||||||
<Spin indicator={<LoadingOutlined style={{fontSize: 64, color: "#1890ff"}} spin/>}/>
|
|
||||||
</div>
|
|
||||||
) : viewMode === "table" ? (
|
) : viewMode === "table" ? (
|
||||||
<TableView/>
|
<TableView/>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import {
|
|||||||
FloatButton,
|
FloatButton,
|
||||||
Row,
|
Row,
|
||||||
Col,
|
Col,
|
||||||
Spin,
|
|
||||||
notification,
|
notification,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Table,
|
Table,
|
||||||
@ -16,26 +15,27 @@ import {
|
|||||||
} from "antd";
|
} from "antd";
|
||||||
import {
|
import {
|
||||||
BuildOutlined,
|
BuildOutlined,
|
||||||
LoadingOutlined,
|
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
SortAscendingOutlined,
|
SortAscendingOutlined,
|
||||||
SortDescendingOutlined, TableOutlined,
|
SortDescendingOutlined, TableOutlined,
|
||||||
TeamOutlined
|
TeamOutlined
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import {useAuth} from "../AuthContext.jsx";
|
import {useAuth} from "../AuthContext.jsx";
|
||||||
import getAllPatients from "../api/patients/GetAllPatients.jsx";
|
import getAllPatients from "../api/patients/getAllPatients.jsx";
|
||||||
import PatientListCard from "../components/patients/PatientListCard.jsx";
|
import PatientListCard from "../components/patients/PatientListCard.jsx";
|
||||||
import PatientFormModal from "../components/patients/PatientFormModal.jsx";
|
import PatientFormModal from "../components/patients/PatientFormModal.jsx";
|
||||||
import updatePatient from "../api/patients/UpdatePatient.jsx";
|
import updatePatient from "../api/patients/updatePatient.jsx";
|
||||||
import addPatient from "../api/patients/AddPatient.jsx";
|
import addPatient from "../api/patients/addPatient.jsx";
|
||||||
import deletePatient from "../api/patients/DeletePatient.jsx";
|
import deletePatient from "../api/patients/deletePatient.jsx";
|
||||||
import SelectViewMode from "../components/SelectViewMode.jsx";
|
import SelectViewMode from "../components/SelectViewMode.jsx";
|
||||||
|
import LoadingIndicator from "../components/LoadingIndicator.jsx";
|
||||||
|
import {cacheInfo, getCachedInfo, getCacheTimestamp} from "../utils/cachedInfoUtils.jsx";
|
||||||
|
|
||||||
const {Option} = Select;
|
const {Option} = Select;
|
||||||
const {Title} = Typography
|
const {Title} = Typography
|
||||||
|
|
||||||
const PatientsPage = () => {
|
const PatientsPage = () => {
|
||||||
const {user} = useAuth();
|
const {api} = useAuth();
|
||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
const [sortOrder, setSortOrder] = useState("asc");
|
const [sortOrder, setSortOrder] = useState("asc");
|
||||||
const [viewMode, setViewMode] = useState("tile");
|
const [viewMode, setViewMode] = useState("tile");
|
||||||
@ -59,14 +59,14 @@ const PatientsPage = () => {
|
|||||||
const intervalId = setInterval(fetchPatients, 5000);
|
const intervalId = setInterval(fetchPatients, 5000);
|
||||||
return () => clearInterval(intervalId);
|
return () => clearInterval(intervalId);
|
||||||
}
|
}
|
||||||
}, [user, isModalVisible, selectedPatient]);
|
}, [isModalVisible, selectedPatient]);
|
||||||
|
|
||||||
const fetchPatientsWithCache = async () => {
|
const fetchPatientsWithCache = async () => {
|
||||||
const cachedData = localStorage.getItem("patientsData");
|
const cachedData = getCachedInfo("patientsData");
|
||||||
const cacheTimestamp = localStorage.getItem("patientsTimestamp");
|
const cacheTimestamp = getCacheTimestamp("patientsData");
|
||||||
|
|
||||||
if (cachedData && cacheTimestamp && (Date.now() - parseInt(cacheTimestamp)) < 60 * 1000) {
|
if (cachedData && cacheTimestamp && (Date.now() - cacheTimestamp) < 60 * 1000) {
|
||||||
setPatients(JSON.parse(cachedData));
|
setPatients(cachedData);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -76,11 +76,10 @@ const PatientsPage = () => {
|
|||||||
|
|
||||||
const fetchPatients = async () => {
|
const fetchPatients = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await getAllPatients(user.token);
|
const data = await getAllPatients(api);
|
||||||
setPatients(data);
|
setPatients(data);
|
||||||
|
|
||||||
localStorage.setItem("patientsData", JSON.stringify(data));
|
cacheInfo("patientsData", data);
|
||||||
localStorage.setItem("patientsTimestamp", Date.now().toString());
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
notification.error({
|
notification.error({
|
||||||
@ -90,13 +89,11 @@ const PatientsPage = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchViewModeFromCache = () => {
|
const fetchViewModeFromCache = () => {
|
||||||
const cachedViewMode = localStorage.getItem("viewModePatients");
|
const cachedViewMode = getCachedInfo("viewModePatients");
|
||||||
if (cachedViewMode) {
|
if (cachedViewMode) {
|
||||||
setViewMode(cachedViewMode);
|
setViewMode(cachedViewMode);
|
||||||
}
|
}
|
||||||
@ -128,7 +125,7 @@ const PatientsPage = () => {
|
|||||||
|
|
||||||
const handleDeletePatient = async (patient_id) => {
|
const handleDeletePatient = async (patient_id) => {
|
||||||
try {
|
try {
|
||||||
await deletePatient(user.token, patient_id);
|
await deletePatient(api, patient_id);
|
||||||
await fetchPatients();
|
await fetchPatients();
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "Пациент удалён",
|
message: "Пациент удалён",
|
||||||
@ -171,7 +168,7 @@ const PatientsPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const editPatient = async (patient) => {
|
const editPatient = async (patient) => {
|
||||||
await updatePatient(user.token, selectedPatient.id, patient);
|
await updatePatient(api, selectedPatient.id, patient);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "Пациент обновлён",
|
message: "Пациент обновлён",
|
||||||
description: `Данные пациента ${patient.first_name} ${patient.last_name} успешно обновлены.`,
|
description: `Данные пациента ${patient.first_name} ${patient.last_name} успешно обновлены.`,
|
||||||
@ -180,7 +177,7 @@ const PatientsPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const addNewPatient = async (patient) => {
|
const addNewPatient = async (patient) => {
|
||||||
await addPatient(user.token, patient);
|
await addPatient(api, patient);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "Пациент добавлен",
|
message: "Пациент добавлен",
|
||||||
description: `Пациент ${patient.first_name} ${patient.last_name} успешно добавлен.`,
|
description: `Пациент ${patient.first_name} ${patient.last_name} успешно добавлен.`,
|
||||||
@ -370,14 +367,7 @@ const PatientsPage = () => {
|
|||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div style={{
|
<LoadingIndicator/>
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
height: "100vh",
|
|
||||||
}}>
|
|
||||||
<Spin indicator={<LoadingOutlined style={{fontSize: 64, color: "#1890ff"}} spin/>}/>
|
|
||||||
</div>
|
|
||||||
) : viewMode === "tile" ? (
|
) : viewMode === "tile" ? (
|
||||||
<TileView/>
|
<TileView/>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -1,25 +1,149 @@
|
|||||||
import {Calendar, Grid} from "antd";
|
import {Calendar, Grid, ConfigProvider, Badge, Modal} from "antd";
|
||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import 'dayjs/locale/ru';
|
||||||
|
import locale from 'antd/es/locale/ru_RU';
|
||||||
|
import updateLocale from 'dayjs/plugin/updateLocale';
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
const {useBreakpoint} = Grid;
|
const {useBreakpoint} = Grid;
|
||||||
|
|
||||||
const AppointmentsCalendarPage = () => {
|
dayjs.extend(updateLocale);
|
||||||
const screens = useBreakpoint();
|
dayjs.updateLocale('ru', {
|
||||||
|
weekStart: 1
|
||||||
|
});
|
||||||
|
|
||||||
const [selectedDate, setSelectedDate] = useState(dayjs(
|
const AppointmentsCalendarPage = ({appointments, scheduledAppointments}) => {
|
||||||
new Date()
|
const screens = useBreakpoint();
|
||||||
));
|
const [selectedDate, setSelectedDate] = useState(dayjs(new Date()));
|
||||||
|
const [modalVisible, setModalVisible] = useState(false);
|
||||||
|
const [selectedAppointments, setSelectedAppointments] = useState([]);
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
|
<ul style={{listStyle: 'none', padding: 0}}>
|
||||||
|
{appointmentsForDate.map(app => (
|
||||||
|
<li key={app.id}>
|
||||||
|
<Badge status="success" text={`Прием ${dayjs(app.appointment_datetime).format('HH:mm')}`}/>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
{scheduledForDate.map(app => (
|
||||||
|
<li key={app.id}>
|
||||||
|
<Badge status="processing"
|
||||||
|
text={`Запланировано ${dayjs(app.scheduled_datetime).format('HH:mm')}`}/>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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}}>
|
<div style={{padding: 20}}>
|
||||||
<Calendar
|
<Calendar
|
||||||
fullscreen={!screens.xs}
|
fullscreen={!screens.xs}
|
||||||
value={selectedDate}
|
value={selectedDate}
|
||||||
onSelect={setSelectedDate}
|
onSelect={onSelect}
|
||||||
|
cellRender={dateCellRender}
|
||||||
/>
|
/>
|
||||||
|
<Modal
|
||||||
|
title={`Приемы на ${selectedDate.format('DD.MM.YYYY')}`}
|
||||||
|
open={modalVisible}
|
||||||
|
onCancel={() => setModalVisible(false)}
|
||||||
|
footer={null}
|
||||||
|
>
|
||||||
|
{selectedAppointments.map(app => (
|
||||||
|
<div key={app.id}>
|
||||||
|
<p>{app.appointment_datetime ? 'Прием' : 'Запланировано'}: {dayjs(app.appointment_datetime || app.scheduled_datetime).format('HH:mm')}</p>
|
||||||
|
<p>Пациент: {app.patient?.name || 'Не указан'}</p>
|
||||||
|
<p>Врач: {app.doctor?.name || 'Не указан'}</p>
|
||||||
|
<p>Тип: {app.type?.name || 'Не указан'}</p>
|
||||||
|
{app.results && <p>Результаты: {app.results}</p>}
|
||||||
|
<hr/>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
</ConfigProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AppointmentsCalendarPage.propTypes = {
|
||||||
|
appointments: PropTypes.arrayOf(
|
||||||
|
PropTypes.shape({
|
||||||
|
id: PropTypes.number.isRequired,
|
||||||
|
results: PropTypes.string,
|
||||||
|
days_until_the_next_appointment: PropTypes.number,
|
||||||
|
appointment_datetime: PropTypes.string.isRequired,
|
||||||
|
patient: PropTypes.shape({
|
||||||
|
first_name: PropTypes.string,
|
||||||
|
last_name: PropTypes.string,
|
||||||
|
patronymic: PropTypes.string,
|
||||||
|
birthday: PropTypes.string,
|
||||||
|
address: PropTypes.string,
|
||||||
|
email: PropTypes.string,
|
||||||
|
phone: PropTypes.string,
|
||||||
|
diagnosis: PropTypes.string,
|
||||||
|
correction: PropTypes.string,
|
||||||
|
}),
|
||||||
|
doctor: PropTypes.shape({
|
||||||
|
last_name: PropTypes.string,
|
||||||
|
first_name: PropTypes.string,
|
||||||
|
login: PropTypes.string,
|
||||||
|
}),
|
||||||
|
type: PropTypes.shape({
|
||||||
|
title: PropTypes.string,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
),
|
||||||
|
scheduledAppointments: PropTypes.arrayOf(
|
||||||
|
PropTypes.shape(
|
||||||
|
{
|
||||||
|
id: PropTypes.number.isRequired,
|
||||||
|
scheduled_datetime: PropTypes.string.isRequired,
|
||||||
|
patient: PropTypes.shape({
|
||||||
|
first_name: PropTypes.string,
|
||||||
|
last_name: PropTypes.string,
|
||||||
|
patronymic: PropTypes.string,
|
||||||
|
birthday: PropTypes.string,
|
||||||
|
address: PropTypes.string,
|
||||||
|
email: PropTypes.string,
|
||||||
|
phone: PropTypes.string,
|
||||||
|
diagnosis: PropTypes.string,
|
||||||
|
correction: PropTypes.string,
|
||||||
|
}),
|
||||||
|
doctor: PropTypes.shape({
|
||||||
|
last_name: PropTypes.string,
|
||||||
|
first_name: PropTypes.string,
|
||||||
|
login: PropTypes.string,
|
||||||
|
}),
|
||||||
|
type: PropTypes.shape({
|
||||||
|
title: PropTypes.string,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
export default AppointmentsCalendarPage;
|
export default AppointmentsCalendarPage;
|
||||||
@ -1,11 +1,11 @@
|
|||||||
import {useAuth} from "../../AuthContext.jsx";
|
import {useAuth} from "../../AuthContext.jsx";
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import getAllAppointments from "../../api/appointments/GetAllAppointments.jsx";
|
import getAllAppointments from "../../api/appointments/getAllAppointments.jsx";
|
||||||
import {notification} from "antd";
|
import {notification} from "antd";
|
||||||
|
|
||||||
|
|
||||||
const AppointmentsTablePage = () => {
|
const AppointmentsTablePage = () => {
|
||||||
const {user} = useAuth();
|
const {api} = useAuth();
|
||||||
|
|
||||||
const [appointments, setAppointments] = useState([]);
|
const [appointments, setAppointments] = useState([]);
|
||||||
|
|
||||||
@ -16,52 +16,6 @@ const AppointmentsTablePage = () => {
|
|||||||
const [selectedAppointment, setSelectedAppointment] = useState(null);
|
const [selectedAppointment, setSelectedAppointment] = useState(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchAppointmentsWithCache();
|
|
||||||
document.title = "Приемы";
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isModalVisible && !selectedAppointment) {
|
|
||||||
const intervalId = setInterval(fetchAppointments, 5000);
|
|
||||||
return () => clearInterval(intervalId);
|
|
||||||
}
|
|
||||||
}, [user, isModalVisible, selectedAppointment]);
|
|
||||||
|
|
||||||
const fetchAppointmentsWithCache = async () => {
|
|
||||||
const cachedData = localStorage.getItem("appointmentsData");
|
|
||||||
const cacheTimestamp = localStorage.getItem("appointmentsTimestamp");
|
|
||||||
const currentTime = Date.now();
|
|
||||||
if (cachedData && currentTime - cacheTimestamp < 60000) {
|
|
||||||
setAppointments(JSON.parse(cachedData));
|
|
||||||
setLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await fetchAppointments();
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchAppointments = async () => {
|
|
||||||
try {
|
|
||||||
const data = await getAllAppointments(user.token);
|
|
||||||
setAppointments(data);
|
|
||||||
|
|
||||||
localStorage.setItem("appointmentsData", JSON.stringify(data));
|
|
||||||
localStorage.setItem("appointmentsTimestamp", Date.now().toString());
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
notification.error({
|
|
||||||
message: "Ошибка загрузки данных",
|
|
||||||
description: "Проверьте подключение к сети.",
|
|
||||||
placement: "topRight",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{padding: 20}}>
|
<div style={{padding: 20}}>
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import {
|
|||||||
FloatButton,
|
FloatButton,
|
||||||
Row,
|
Row,
|
||||||
Col,
|
Col,
|
||||||
Spin,
|
|
||||||
Button,
|
Button,
|
||||||
Form,
|
Form,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
@ -18,7 +17,6 @@ import {
|
|||||||
Typography
|
Typography
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import {
|
import {
|
||||||
LoadingOutlined,
|
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
DownOutlined,
|
DownOutlined,
|
||||||
UpOutlined,
|
UpOutlined,
|
||||||
@ -27,20 +25,22 @@ import {
|
|||||||
BuildOutlined
|
BuildOutlined
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import LensCard from "../../components/lenses/LensListCard.jsx";
|
import LensCard from "../../components/lenses/LensListCard.jsx";
|
||||||
import getAllLenses from "../../api/lenses/GetAllLenses.jsx";
|
import getAllLenses from "../../api/lenses/getAllLenses.jsx";
|
||||||
import addLens from "../../api/lenses/AddLens.jsx";
|
import addLens from "../../api/lenses/addLens.jsx";
|
||||||
import updateLens from "../../api/lenses/UpdateLens.jsx";
|
import updateLens from "../../api/lenses/updateLens.jsx";
|
||||||
import deleteLens from "../../api/lenses/DeleteLens.jsx";
|
import deleteLens from "../../api/lenses/deleteLens.jsx";
|
||||||
import {useAuth} from "../../AuthContext.jsx";
|
import {useAuth} from "../../AuthContext.jsx";
|
||||||
import LensFormModal from "../../components/lenses/LensFormModal.jsx";
|
import LensFormModal from "../../components/lenses/LensFormModal.jsx";
|
||||||
import SelectViewMode from "../../components/SelectViewMode.jsx";
|
import SelectViewMode from "../../components/SelectViewMode.jsx";
|
||||||
|
import LoadingIndicator from "../../components/LoadingIndicator.jsx";
|
||||||
|
import {getCachedInfo, getCacheTimestamp} from "../../utils/cachedInfoUtils.jsx";
|
||||||
|
|
||||||
const {Option} = Select;
|
const {Option} = Select;
|
||||||
const {useBreakpoint} = Grid;
|
const {useBreakpoint} = Grid;
|
||||||
const {Title} = Typography;
|
const {Title} = Typography;
|
||||||
|
|
||||||
const LensesPage = () => {
|
const LensesPage = () => {
|
||||||
const {user} = useAuth();
|
const {api} = useAuth();
|
||||||
const screens = useBreakpoint();
|
const screens = useBreakpoint();
|
||||||
|
|
||||||
const [current, setCurrent] = useState(1);
|
const [current, setCurrent] = useState(1);
|
||||||
@ -76,11 +76,11 @@ const LensesPage = () => {
|
|||||||
const intervalId = setInterval(fetchLenses, 5000);
|
const intervalId = setInterval(fetchLenses, 5000);
|
||||||
return () => clearInterval(intervalId);
|
return () => clearInterval(intervalId);
|
||||||
}
|
}
|
||||||
}, [user, isModalVisible, selectedLens]);
|
}, [isModalVisible, selectedLens]);
|
||||||
|
|
||||||
const fetchLensWithCache = async () => {
|
const fetchLensWithCache = async () => {
|
||||||
const cachedData = localStorage.getItem("lensData");
|
const cachedData = getCachedInfo("lensData");
|
||||||
const cacheTimestamp = localStorage.getItem("lensTimestamp");
|
const cacheTimestamp = getCacheTimestamp("lensData");
|
||||||
|
|
||||||
if (cachedData && cacheTimestamp && (Date.now() - parseInt(cacheTimestamp)) < 60 * 1000) {
|
if (cachedData && cacheTimestamp && (Date.now() - parseInt(cacheTimestamp)) < 60 * 1000) {
|
||||||
setLenses(JSON.parse(cachedData));
|
setLenses(JSON.parse(cachedData));
|
||||||
@ -93,7 +93,7 @@ const LensesPage = () => {
|
|||||||
|
|
||||||
const fetchLenses = async () => {
|
const fetchLenses = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await getAllLenses(user.token);
|
const data = await getAllLenses(api);
|
||||||
setLenses(data);
|
setLenses(data);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -108,7 +108,7 @@ const LensesPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fetchViewModeFromCache = () => {
|
const fetchViewModeFromCache = () => {
|
||||||
const cachedViewMode = localStorage.getItem("viewModeLenses");
|
const cachedViewMode = getCachedInfo("viewModeLenses");
|
||||||
if (cachedViewMode) {
|
if (cachedViewMode) {
|
||||||
setViewMode(cachedViewMode);
|
setViewMode(cachedViewMode);
|
||||||
}
|
}
|
||||||
@ -148,8 +148,8 @@ const LensesPage = () => {
|
|||||||
|
|
||||||
const handleDeleteLens = async (lensId) => {
|
const handleDeleteLens = async (lensId) => {
|
||||||
try {
|
try {
|
||||||
await deleteLens(user.token, lensId);
|
await deleteLens(api, lensId);
|
||||||
await fetchLenses(user.token);
|
await fetchLenses(api);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "Линза удалена",
|
message: "Линза удалена",
|
||||||
description: "Линза успешно удалена.",
|
description: "Линза успешно удалена.",
|
||||||
@ -168,14 +168,14 @@ const LensesPage = () => {
|
|||||||
const handleModalSubmit = async (lensData) => {
|
const handleModalSubmit = async (lensData) => {
|
||||||
try {
|
try {
|
||||||
if (selectedLens) {
|
if (selectedLens) {
|
||||||
await updateLens(user.token, selectedLens.id, lensData);
|
await updateLens(api, selectedLens.id, lensData);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "Линза обновлена",
|
message: "Линза обновлена",
|
||||||
description: "Линза успешно обновлена.",
|
description: "Линза успешно обновлена.",
|
||||||
placement: "topRight",
|
placement: "topRight",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await addLens(user.token, lensData);
|
await addLens(api, lensData);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "Линза добавлена",
|
message: "Линза добавлена",
|
||||||
description: "Линза успешно добавлена.",
|
description: "Линза успешно добавлена.",
|
||||||
@ -345,7 +345,6 @@ const LensesPage = () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{padding: 20}}>
|
<div style={{padding: 20}}>
|
||||||
<Title level={1}><FolderViewOutlined/> Линзы</Title>
|
<Title level={1}><FolderViewOutlined/> Линзы</Title>
|
||||||
@ -497,9 +496,7 @@ const LensesPage = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div style={{display: "flex", justifyContent: "center", alignItems: "center", height: "100vh"}}>
|
<LoadingIndicator/>
|
||||||
<Spin indicator={<LoadingOutlined style={{fontSize: 64, color: "#1890ff"}} spin/>}/>
|
|
||||||
</div>
|
|
||||||
) : viewMode === "tile" ? (
|
) : viewMode === "tile" ? (
|
||||||
<TileView/>
|
<TileView/>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -1,22 +1,24 @@
|
|||||||
import {useAuth} from "../../AuthContext.jsx";
|
import {useAuth} from "../../AuthContext.jsx";
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import {FloatButton, Input, List, notification, Row, Spin, Typography} from "antd";
|
import {FloatButton, Input, List, notification, Row, Typography} from "antd";
|
||||||
import getAllSets from "../../api/sets/GetAllSets.jsx";
|
import getAllSets from "../../api/sets/getAllSets.jsx";
|
||||||
import {LoadingOutlined, PlusOutlined, SwitcherOutlined} from "@ant-design/icons";
|
import {PlusOutlined, SwitcherOutlined} from "@ant-design/icons";
|
||||||
import SetListCard from "../../components/sets/SetListCard.jsx";
|
import SetListCard from "../../components/sets/SetListCard.jsx";
|
||||||
import SetFormModal from "../../components/sets/SetFormModal.jsx";
|
import SetFormModal from "../../components/sets/SetFormModal.jsx";
|
||||||
import updateSet from "../../api/sets/UpdateSet.jsx";
|
import updateSet from "../../api/sets/updateSet.jsx";
|
||||||
import addSet from "../../api/sets/AddSet.jsx";
|
import addSet from "../../api/sets/addSet.jsx";
|
||||||
import deleteSet from "../../api/sets/DeleteSet.jsx";
|
import deleteSet from "../../api/sets/deleteSet.jsx";
|
||||||
import addSetContent from "../../api/set_content/AddSetContent.jsx";
|
import addSetContent from "../../api/set_content/addSetContent.jsx";
|
||||||
import updateSetContent from "../../api/set_content/UpdateSetContent.jsx";
|
import updateSetContent from "../../api/set_content/updateSetContent.jsx";
|
||||||
import appendLensesFromSet from "../../api/sets/AppendLensesFromSet.jsx";
|
import appendLensesFromSet from "../../api/sets/appendLensesFromSet.jsx";
|
||||||
|
import LoadingIndicator from "../../components/LoadingIndicator.jsx";
|
||||||
|
import {cacheInfo, getCachedInfo, getCacheTimestamp} from "../../utils/cachedInfoUtils.jsx";
|
||||||
|
|
||||||
|
|
||||||
const {Title} = Typography;
|
const {Title} = Typography;
|
||||||
|
|
||||||
const SetLensesPage = () => {
|
const SetLensesPage = () => {
|
||||||
const {user} = useAuth();
|
const {api} = useAuth();
|
||||||
|
|
||||||
const [current, setCurrent] = useState(1);
|
const [current, setCurrent] = useState(1);
|
||||||
const [pageSize, setPageSize] = useState(10);
|
const [pageSize, setPageSize] = useState(10);
|
||||||
@ -36,14 +38,14 @@ const SetLensesPage = () => {
|
|||||||
const intervalId = setInterval(fetchSets, 5000);
|
const intervalId = setInterval(fetchSets, 5000);
|
||||||
return () => clearInterval(intervalId);
|
return () => clearInterval(intervalId);
|
||||||
}
|
}
|
||||||
}, [user, isModalVisible]);
|
}, [isModalVisible]);
|
||||||
|
|
||||||
const fetchSetsWithCache = async () => {
|
const fetchSetsWithCache = async () => {
|
||||||
const cachedData = localStorage.getItem("setsData");
|
const cachedData = getCachedInfo("setsData");
|
||||||
const cacheTimestamp = localStorage.getItem("setsTimestamp");
|
const cacheTimestamp = getCacheTimestamp("setsData");
|
||||||
|
|
||||||
if (cachedData && cacheTimestamp && (Date.now() - parseInt(cacheTimestamp)) < 60 * 1000) {
|
if (cachedData && cacheTimestamp && (Date.now() - cacheTimestamp) < 60 * 1000) {
|
||||||
setSets(JSON.parse(cachedData));
|
setSets(cachedData);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
} else {
|
} else {
|
||||||
await fetchSets();
|
await fetchSets();
|
||||||
@ -51,14 +53,11 @@ const SetLensesPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fetchSets = async () => {
|
const fetchSets = async () => {
|
||||||
if (!user || !user.token) return;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await getAllSets(user.token);
|
const data = await getAllSets(api);
|
||||||
setSets(data);
|
setSets(data);
|
||||||
|
|
||||||
localStorage.setItem("setsData", JSON.stringify(data));
|
cacheInfo("setsData", data);
|
||||||
localStorage.setItem("setsTimestamp", Date.now().toString());
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
notification.error({
|
notification.error({
|
||||||
@ -87,7 +86,7 @@ const SetLensesPage = () => {
|
|||||||
|
|
||||||
const handleDeleteSet = async (set_id) => {
|
const handleDeleteSet = async (set_id) => {
|
||||||
try {
|
try {
|
||||||
await deleteSet(user.token, set_id);
|
await deleteSet(api, set_id);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "Набор удален",
|
message: "Набор удален",
|
||||||
description: "Набор успешно удален.",
|
description: "Набор успешно удален.",
|
||||||
@ -110,7 +109,7 @@ const SetLensesPage = () => {
|
|||||||
|
|
||||||
const handleAppendSet = async (set) => {
|
const handleAppendSet = async (set) => {
|
||||||
try {
|
try {
|
||||||
await appendLensesFromSet(user.token, set.id);
|
await appendLensesFromSet(api, set.id);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "Линзы добавлены",
|
message: "Линзы добавлены",
|
||||||
description: "Линзы успешно добавлены.",
|
description: "Линзы успешно добавлены.",
|
||||||
@ -157,7 +156,7 @@ const SetLensesPage = () => {
|
|||||||
const setContent = async (content, set_id) => {
|
const setContent = async (content, set_id) => {
|
||||||
try {
|
try {
|
||||||
console.log(content);
|
console.log(content);
|
||||||
await addSetContent(user.token, content, set_id);
|
await addSetContent(api, content, set_id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Ошибка сохранения набора:", error);
|
console.error("Ошибка сохранения набора:", error);
|
||||||
notification.error({
|
notification.error({
|
||||||
@ -170,7 +169,7 @@ const SetLensesPage = () => {
|
|||||||
|
|
||||||
const updateContent = async (content, set_id) => {
|
const updateContent = async (content, set_id) => {
|
||||||
try {
|
try {
|
||||||
await updateSetContent(user.token, content, set_id);
|
await updateSetContent(api, content, set_id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Ошибка сохранения набора:", error);
|
console.error("Ошибка сохранения набора:", error);
|
||||||
notification.error({
|
notification.error({
|
||||||
@ -182,7 +181,7 @@ const SetLensesPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const editCurrentSet = async (set) => {
|
const editCurrentSet = async (set) => {
|
||||||
const refreshed_set = await updateSet(user.token, selectedSet.id, set);
|
const refreshed_set = await updateSet(api, selectedSet.id, set);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "Набор обновлен",
|
message: "Набор обновлен",
|
||||||
description: "Набор успешно обновлен.",
|
description: "Набор успешно обновлен.",
|
||||||
@ -192,7 +191,7 @@ const SetLensesPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const addNewSet = async (set) => {
|
const addNewSet = async (set) => {
|
||||||
const refreshed_set = await addSet(user.token, set);
|
const refreshed_set = await addSet(api, set);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "Набор добавлен",
|
message: "Набор добавлен",
|
||||||
description: "Набор успешно добавлен.",
|
description: "Набор успешно добавлен.",
|
||||||
@ -214,14 +213,7 @@ const SetLensesPage = () => {
|
|||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div style={{
|
<LoadingIndicator/>
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
height: "100vh",
|
|
||||||
}}>
|
|
||||||
<Spin indicator={<LoadingOutlined style={{fontSize: 64, color: "#1890ff"}} spin/>}/>
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<List
|
<List
|
||||||
grid={{
|
grid={{
|
||||||
|
|||||||
@ -4,6 +4,7 @@ body {
|
|||||||
|
|
||||||
*::-webkit-scrollbar {
|
*::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
*::-webkit-scrollbar-track {
|
*::-webkit-scrollbar-track {
|
||||||
|
|||||||
18
web-app/src/utils/cachedInfoUtils.jsx
Normal file
18
web-app/src/utils/cachedInfoUtils.jsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
const cacheInfo = (key, value) => {
|
||||||
|
localStorage.setItem(key, JSON.stringify(value));
|
||||||
|
localStorage.setItem(`${key}Timestamp`, Date.now().toString());
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCachedInfo = (key) => {
|
||||||
|
const data = localStorage.getItem(key);
|
||||||
|
if (!data) return null;
|
||||||
|
return JSON.parse(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCacheTimestamp = (key) => {
|
||||||
|
const timestamp = localStorage.getItem(`${key}Timestamp`);
|
||||||
|
if (!timestamp) return null;
|
||||||
|
return parseInt(timestamp);
|
||||||
|
};
|
||||||
|
|
||||||
|
export {cacheInfo, getCachedInfo, getCacheTimestamp};
|
||||||
Loading…
x
Reference in New Issue
Block a user