изменил архитектуру работы с api
This commit is contained in:
parent
3fc30f8e59
commit
2372083afe
@ -20,21 +20,21 @@ def start_app():
|
||||
|
||||
api_app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=['*'],
|
||||
allow_origins=['http://localhost:5173'],
|
||||
allow_credentials=True,
|
||||
allow_methods=['GET', 'POST', 'PUT', 'DELETE'],
|
||||
allow_methods=['*'],
|
||||
allow_headers=['*'],
|
||||
)
|
||||
|
||||
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(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(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(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(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/palette-antd": "^5.3.0",
|
||||
"antd": "^5.23.1",
|
||||
"antd-dayjs-webpack-plugin": "^1.0.6",
|
||||
"antd-mask-input": "^2.0.7",
|
||||
"axios": "^1.7.9",
|
||||
"dayjs": "^1.11.13",
|
||||
|
||||
@ -4,11 +4,11 @@ import {AuthProvider} from "./AuthContext.jsx";
|
||||
import "/src/styles/app.css";
|
||||
|
||||
const App = () => (
|
||||
<AuthProvider>
|
||||
<Router>
|
||||
<Router>
|
||||
<AuthProvider>
|
||||
<AppRouter/>
|
||||
</Router>
|
||||
</AuthProvider>
|
||||
</AuthProvider>
|
||||
</Router>
|
||||
);
|
||||
|
||||
export default App
|
||||
export default App;
|
||||
|
||||
@ -24,6 +24,6 @@ const AppRouter = () => (
|
||||
</Route>
|
||||
<Route path={"*"} element={<Navigate to={"/"}/>}/>
|
||||
</Routes>
|
||||
)
|
||||
);
|
||||
|
||||
export default AppRouter;
|
||||
@ -1,13 +1,16 @@
|
||||
import {createContext, useState, useContext, useEffect} from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import loginUser from "./api/auth/LoginRequest.jsx";
|
||||
import loginUser from "./api/auth/loginRequest.jsx";
|
||||
import {Spin} from "antd";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
import createApi from "./core/axiosConfig.jsx";
|
||||
|
||||
const AuthContext = createContext(undefined);
|
||||
|
||||
export const AuthProvider = ({children}) => {
|
||||
const AuthProvider = ({children}) => {
|
||||
const [user, setUser] = useState(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
const token = localStorage.getItem("access_token");
|
||||
@ -17,9 +20,10 @@ export const AuthProvider = ({children}) => {
|
||||
setIsLoading(false);
|
||||
}, []);
|
||||
|
||||
|
||||
const login = async (loginData) => {
|
||||
try {
|
||||
const token = await loginUser(loginData);
|
||||
const token = await loginUser(loginData, api);
|
||||
localStorage.setItem("access_token", token);
|
||||
setUser({token});
|
||||
} catch (error) {
|
||||
@ -28,6 +32,12 @@ export const AuthProvider = ({children}) => {
|
||||
}
|
||||
};
|
||||
|
||||
const logoutAndRedirect = () => {
|
||||
localStorage.removeItem("access_token");
|
||||
setUser(null);
|
||||
navigate("/login");
|
||||
};
|
||||
|
||||
const logout = () => {
|
||||
localStorage.removeItem("access_token");
|
||||
setUser(null);
|
||||
@ -37,8 +47,10 @@ export const AuthProvider = ({children}) => {
|
||||
return <Spin/>;
|
||||
}
|
||||
|
||||
const api = createApi(logoutAndRedirect);
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{user, login, logout}}>
|
||||
<AuthContext.Provider value={{user, login, logout, logoutAndRedirect, api}}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
@ -48,6 +60,8 @@ AuthProvider.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export const useAuth = () => {
|
||||
const useAuth = () => {
|
||||
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/>;
|
||||
};
|
||||
|
||||
|
||||
export default PrivateRoute;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import {BuildOutlined, TableOutlined} from "@ant-design/icons";
|
||||
import {Select, Tooltip} from "antd";
|
||||
import PropTypes from "prop-types";
|
||||
import {cacheInfo} from "../utils/cachedInfoUtils.jsx";
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
@ -13,7 +14,7 @@ const SelectViewMode = ({viewMode, setViewMode, localStorageKey, toolTipText, vi
|
||||
value={viewMode}
|
||||
onChange={value => {
|
||||
setViewMode(value);
|
||||
localStorage.setItem(localStorageKey, value);
|
||||
cacheInfo(localStorageKey, value);
|
||||
}}
|
||||
style={{width: "100%"}}
|
||||
>
|
||||
|
||||
@ -3,15 +3,15 @@ import {
|
||||
Modal, Input, Button, notification, Typography, Collapse, Steps, Row, Alert, Col, DatePicker, Spin, Grid
|
||||
} from "antd";
|
||||
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 dayjs from "dayjs";
|
||||
import getNotIssuedLenses from "../../api/lenses/GetNotIssuedLenses.jsx";
|
||||
import getNotIssuedLenses from "../../api/lenses/getNotIssuedLenses.jsx";
|
||||
|
||||
const {useBreakpoint} = Grid;
|
||||
|
||||
const LensIssueFormModal = ({visible, onCancel, onSubmit}) => {
|
||||
const {user} = useAuth();
|
||||
const {api} = useAuth();
|
||||
const screens = useBreakpoint();
|
||||
|
||||
const [patients, setPatients] = useState([]);
|
||||
@ -37,7 +37,7 @@ const LensIssueFormModal = ({visible, onCancel, onSubmit}) => {
|
||||
|
||||
const fetchPatients = async () => {
|
||||
try {
|
||||
const data = await getAllPatients(user.token);
|
||||
const data = await getAllPatients(api);
|
||||
setPatients(data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@ -49,7 +49,7 @@ const LensIssueFormModal = ({visible, onCancel, onSubmit}) => {
|
||||
|
||||
const fetchLenses = async () => {
|
||||
try {
|
||||
const data = await getNotIssuedLenses(user.token);
|
||||
const data = await getNotIssuedLenses(api);
|
||||
setLenses(data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@ -83,17 +83,17 @@ const LensIssueFormModal = ({visible, onCancel, onSubmit}) => {
|
||||
});
|
||||
|
||||
const patientsItems = filteredPatients.map((patient) => ({
|
||||
key: patient.id,
|
||||
label: `${patient.last_name} ${patient.first_name} (${new Date(patient.birthday).toLocaleDateString("ru-RU")})`,
|
||||
children: <div>
|
||||
<p><b>Пациент:</b> {patient.last_name} {patient.first_name}</p>
|
||||
<p><b>Дата рождения:</b> {new Date(patient.birthday).toLocaleDateString("ru-RU")}</p>
|
||||
<p><b>Диагноз:</b> {patient.diagnosis}</p>
|
||||
<p><b>Email:</b> {patient.email}</p>
|
||||
<p><b>Телефон:</b> {patient.phone}</p>
|
||||
<Button type="primary" onClick={() => setSelectedPatient(patient)}>Выбрать</Button>
|
||||
</div>,
|
||||
}));
|
||||
key: patient.id,
|
||||
label: `${patient.last_name} ${patient.first_name} (${new Date(patient.birthday).toLocaleDateString("ru-RU")})`,
|
||||
children: <div>
|
||||
<p><b>Пациент:</b> {patient.last_name} {patient.first_name}</p>
|
||||
<p><b>Дата рождения:</b> {new Date(patient.birthday).toLocaleDateString("ru-RU")}</p>
|
||||
<p><b>Диагноз:</b> {patient.diagnosis}</p>
|
||||
<p><b>Email:</b> {patient.email}</p>
|
||||
<p><b>Телефон:</b> {patient.phone}</p>
|
||||
<Button type="primary" onClick={() => setSelectedPatient(patient)}>Выбрать</Button>
|
||||
</div>,
|
||||
}));
|
||||
|
||||
const filteredLenses = lenses.filter((lens) => {
|
||||
const searchLower = searchLensString.toLowerCase();
|
||||
@ -104,19 +104,19 @@ const LensIssueFormModal = ({visible, onCancel, onSubmit}) => {
|
||||
})
|
||||
|
||||
const lensesItems = filteredLenses.map((lens) => ({
|
||||
key: lens.id, label: `Линза ${lens.side} ${lens.diameter} мм`, children: <div>
|
||||
<p><b>Диаметр:</b> {lens.diameter}</p>
|
||||
<p><b>Тор:</b> {lens.tor}</p>
|
||||
<p><b>Пресетная рефракция:</b> {lens.preset_refraction}</p>
|
||||
<p><b>Диаметр:</b> {lens.diameter}</p>
|
||||
<p><b>FVC:</b> {lens.fvc}</p>
|
||||
<p><b>Острота зрения (Trial):</b> {lens.trial}</p>
|
||||
<p><b>Периферийная торичность:</b> {lens.periphery_toricity}</p>
|
||||
<p><b>Сторона:</b> {lens.side}</p>
|
||||
<p><b>Esa:</b> {lens.esa}</p>
|
||||
<Button type="primary" onClick={() => setSelectedLens(lens)}>Выбрать</Button>
|
||||
</div>,
|
||||
}));
|
||||
key: lens.id, label: `Линза ${lens.side} ${lens.diameter} мм`, children: <div>
|
||||
<p><b>Диаметр:</b> {lens.diameter}</p>
|
||||
<p><b>Тор:</b> {lens.tor}</p>
|
||||
<p><b>Пресетная рефракция:</b> {lens.preset_refraction}</p>
|
||||
<p><b>Диаметр:</b> {lens.diameter}</p>
|
||||
<p><b>FVC:</b> {lens.fvc}</p>
|
||||
<p><b>Острота зрения (Trial):</b> {lens.trial}</p>
|
||||
<p><b>Периферийная торичность:</b> {lens.periphery_toricity}</p>
|
||||
<p><b>Сторона:</b> {lens.side}</p>
|
||||
<p><b>Esa:</b> {lens.esa}</p>
|
||||
<Button type="primary" onClick={() => setSelectedLens(lens)}>Выбрать</Button>
|
||||
</div>,
|
||||
}));
|
||||
|
||||
const SelectPatientStep = () => {
|
||||
return selectedPatient ? (
|
||||
@ -135,24 +135,96 @@ const LensIssueFormModal = ({visible, onCancel, onSubmit}) => {
|
||||
Выбрать другого пациента
|
||||
</Button>
|
||||
</div>) : (<>
|
||||
<Input
|
||||
placeholder="Поиск пациента"
|
||||
value={searchPatientString}
|
||||
onChange={(e) => setSearchPatientString(e.target.value)}
|
||||
style={{marginBottom: 16}}
|
||||
allowClear
|
||||
/>
|
||||
<Input
|
||||
placeholder="Поиск пациента"
|
||||
value={searchPatientString}
|
||||
onChange={(e) => setSearchPatientString(e.target.value)}
|
||||
style={{marginBottom: 16}}
|
||||
allowClear
|
||||
/>
|
||||
|
||||
<div style={{maxHeight: 300, overflowY: "auto", border: "1px solid #d9d9d9", padding: 8}}>
|
||||
<Collapse
|
||||
items={patientsItems}
|
||||
/>
|
||||
</div>
|
||||
</>)
|
||||
<div style={{maxHeight: 300, overflowY: "auto", border: "1px solid #d9d9d9", padding: 8}}>
|
||||
<Collapse
|
||||
items={patientsItems}
|
||||
/>
|
||||
</div>
|
||||
</>)
|
||||
};
|
||||
|
||||
const SelectLensStep = () => {
|
||||
return selectedLens ? (<div style={{padding: "10px", background: "#f5f5f5", borderRadius: 5, marginBottom: 15}}>
|
||||
<Typography.Text strong>
|
||||
{selectedLens.diameter} {selectedLens.tor} {selectedLens.preset_refraction}
|
||||
</Typography.Text>
|
||||
<p><b>Диаметр:</b> {selectedLens.diameter}</p>
|
||||
<p><b>Тор:</b> {selectedLens.tor}</p>
|
||||
<p><b>Пресетная рефракция:</b> {selectedLens.preset_refraction}</p>
|
||||
<p><b>Диаметр:</b> {selectedLens.diameter}</p>
|
||||
<p><b>FVC:</b> {selectedLens.fvc}</p>
|
||||
<p><b>Острота зрения (Trial):</b> {selectedLens.trial}</p>
|
||||
<p><b>Периферийная торичность:</b> {selectedLens.periphery_toricity}</p>
|
||||
<p><b>Сторона:</b> {selectedLens.side}</p>
|
||||
<p><b>Esa:</b> {selectedLens.esa}</p>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => setSelectedLens(null)}
|
||||
danger
|
||||
>
|
||||
Выбрать другую линзу
|
||||
</Button>
|
||||
</div>) : (<>
|
||||
<Input
|
||||
placeholder="Поиск линз"
|
||||
value={searchLensString}
|
||||
onChange={(e) => setSearchLensString(e.target.value)}
|
||||
style={{marginBottom: 16}}
|
||||
allowClear
|
||||
/>
|
||||
|
||||
<div style={{maxHeight: 300, overflowY: "auto", border: "1px solid #d9d9d9", padding: 8}}>
|
||||
<Collapse
|
||||
items={lensesItems}
|
||||
/>
|
||||
</div>
|
||||
</>)
|
||||
};
|
||||
|
||||
const ConfirmStep = () => {
|
||||
return (<>
|
||||
<Alert
|
||||
type="warning"
|
||||
message="Внимание! После подтверждения линза будет считаться выданной, данное действие нельзя будет отменить."
|
||||
style={{marginBottom: 15}}
|
||||
/>
|
||||
|
||||
<Row style={{padding: "10px", background: "#f5f5f5", borderRadius: 5, marginBottom: 15}}
|
||||
gutter={[16, 16]}>
|
||||
<Col xs={24} md={16} style={{display: "flex", alignItems: "center"}}>
|
||||
<Typography.Text strong>
|
||||
Дата выдачи: {issueDate?.toDate().toLocaleDateString("ru-RU")}
|
||||
</Typography.Text>
|
||||
</Col>
|
||||
<Col xs={24} md={8}>
|
||||
<DatePicker
|
||||
value={issueDate}
|
||||
onChange={(date) => setIssueDate(date)}
|
||||
format="DD.MM.YYYY"
|
||||
style={{width: "100%"}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<div style={{padding: "10px", background: "#f5f5f5", borderRadius: 5, marginBottom: 15}}>
|
||||
<Typography.Text strong>
|
||||
{selectedPatient.last_name} {selectedPatient.first_name}
|
||||
</Typography.Text>
|
||||
<p><b>Дата рождения:</b> {new Date(selectedPatient.birthday).toLocaleDateString("ru-RU")}</p>
|
||||
<p><b>Email:</b> {selectedPatient.email}</p>
|
||||
<p><b>Телефон:</b> {selectedPatient.phone}</p>
|
||||
<p><b>Линза:</b> {selectedLens.diameter} {selectedLens.tor} {selectedLens.preset_refraction}</p>
|
||||
</div>
|
||||
|
||||
<div style={{padding: "10px", background: "#f5f5f5", borderRadius: 5, marginBottom: 15}}>
|
||||
<Typography.Text strong>
|
||||
{selectedLens.diameter} {selectedLens.tor} {selectedLens.preset_refraction}
|
||||
</Typography.Text>
|
||||
@ -165,80 +237,8 @@ const LensIssueFormModal = ({visible, onCancel, onSubmit}) => {
|
||||
<p><b>Периферийная торичность:</b> {selectedLens.periphery_toricity}</p>
|
||||
<p><b>Сторона:</b> {selectedLens.side}</p>
|
||||
<p><b>Esa:</b> {selectedLens.esa}</p>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => setSelectedLens(null)}
|
||||
danger
|
||||
>
|
||||
Выбрать другую линзу
|
||||
</Button>
|
||||
</div>) : (<>
|
||||
<Input
|
||||
placeholder="Поиск линз"
|
||||
value={searchLensString}
|
||||
onChange={(e) => setSearchLensString(e.target.value)}
|
||||
style={{marginBottom: 16}}
|
||||
allowClear
|
||||
/>
|
||||
|
||||
<div style={{maxHeight: 300, overflowY: "auto", border: "1px solid #d9d9d9", padding: 8}}>
|
||||
<Collapse
|
||||
items={lensesItems}
|
||||
/>
|
||||
</div>
|
||||
</>)
|
||||
};
|
||||
|
||||
const ConfirmStep = () => {
|
||||
return (<>
|
||||
<Alert
|
||||
type="warning"
|
||||
message="Внимание! После подтверждения линза будет считаться выданной, данное действие нельзя будет отменить."
|
||||
style={{marginBottom: 15}}
|
||||
/>
|
||||
|
||||
<Row style={{padding: "10px", background: "#f5f5f5", borderRadius: 5, marginBottom: 15}}
|
||||
gutter={[16, 16]}>
|
||||
<Col xs={24} md={16} style={{display: "flex", alignItems: "center"}}>
|
||||
<Typography.Text strong>
|
||||
Дата выдачи: {issueDate?.toDate().toLocaleDateString("ru-RU")}
|
||||
</Typography.Text>
|
||||
</Col>
|
||||
<Col xs={24} md={8}>
|
||||
<DatePicker
|
||||
value={issueDate}
|
||||
onChange={(date) => setIssueDate(date)}
|
||||
format="DD.MM.YYYY"
|
||||
style={{width: "100%"}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<div style={{padding: "10px", background: "#f5f5f5", borderRadius: 5, marginBottom: 15}}>
|
||||
<Typography.Text strong>
|
||||
{selectedPatient.last_name} {selectedPatient.first_name}
|
||||
</Typography.Text>
|
||||
<p><b>Дата рождения:</b> {new Date(selectedPatient.birthday).toLocaleDateString("ru-RU")}</p>
|
||||
<p><b>Email:</b> {selectedPatient.email}</p>
|
||||
<p><b>Телефон:</b> {selectedPatient.phone}</p>
|
||||
<p><b>Линза:</b> {selectedLens.diameter} {selectedLens.tor} {selectedLens.preset_refraction}</p>
|
||||
</div>
|
||||
|
||||
<div style={{padding: "10px", background: "#f5f5f5", borderRadius: 5, marginBottom: 15}}>
|
||||
<Typography.Text strong>
|
||||
{selectedLens.diameter} {selectedLens.tor} {selectedLens.preset_refraction}
|
||||
</Typography.Text>
|
||||
<p><b>Диаметр:</b> {selectedLens.diameter}</p>
|
||||
<p><b>Тор:</b> {selectedLens.tor}</p>
|
||||
<p><b>Пресетная рефракция:</b> {selectedLens.preset_refraction}</p>
|
||||
<p><b>Диаметр:</b> {selectedLens.diameter}</p>
|
||||
<p><b>FVC:</b> {selectedLens.fvc}</p>
|
||||
<p><b>Острота зрения (Trial):</b> {selectedLens.trial}</p>
|
||||
<p><b>Периферийная торичность:</b> {selectedLens.periphery_toricity}</p>
|
||||
<p><b>Сторона:</b> {selectedLens.side}</p>
|
||||
<p><b>Esa:</b> {selectedLens.esa}</p>
|
||||
</div>
|
||||
</>);
|
||||
</div>
|
||||
</>);
|
||||
};
|
||||
|
||||
const steps = [{
|
||||
@ -266,61 +266,61 @@ const LensIssueFormModal = ({visible, onCancel, onSubmit}) => {
|
||||
};
|
||||
|
||||
return (<Modal
|
||||
title="Выдача линзы пациенту"
|
||||
open={visible}
|
||||
onCancel={() => {
|
||||
setSelectedPatient(null);
|
||||
setSelectedLens(null);
|
||||
setCurrentStep(0);
|
||||
setIssueDate(dayjs(new Date()));
|
||||
onCancel();
|
||||
}}
|
||||
footer={null}
|
||||
maskClosable={false}
|
||||
width={!screens.xs ? 700 : "90%"}
|
||||
centered
|
||||
title="Выдача линзы пациенту"
|
||||
open={visible}
|
||||
onCancel={() => {
|
||||
setSelectedPatient(null);
|
||||
setSelectedLens(null);
|
||||
setCurrentStep(0);
|
||||
setIssueDate(dayjs(new Date()));
|
||||
onCancel();
|
||||
}}
|
||||
footer={null}
|
||||
maskClosable={false}
|
||||
width={!screens.xs ? 700 : "90%"}
|
||||
centered
|
||||
>
|
||||
{loading ? (<div style={{display: "flex", justifyContent: "center", alignItems: "center", height: "70vh"}}>
|
||||
<Spin size="large"/>
|
||||
</div>) : (<div style={{maxHeight: "70vh", overflowY: "auto", padding: "10px"}}>
|
||||
{steps[currentStep].content}
|
||||
</div>)}
|
||||
|
||||
|
||||
{!screens.xs && (<Steps
|
||||
current={currentStep}
|
||||
items={steps}
|
||||
style={{marginTop: 16}}
|
||||
direction={!screens.xs ? "horizontal" : "vertical"}
|
||||
/>)}
|
||||
|
||||
<Row
|
||||
justify="end"
|
||||
style={{marginTop: 20}}
|
||||
gutter={[8, 8]}
|
||||
>
|
||||
{loading ? (<div style={{display: "flex", justifyContent: "center", alignItems: "center", height: "70vh"}}>
|
||||
<Spin size="large"/>
|
||||
</div>) : (<div style={{maxHeight: "70vh", overflowY: "auto", padding: "10px"}}>
|
||||
{steps[currentStep].content}
|
||||
</div>)}
|
||||
|
||||
|
||||
{!screens.xs && (<Steps
|
||||
current={currentStep}
|
||||
items={steps}
|
||||
style={{marginTop: 16}}
|
||||
direction={!screens.xs ? "horizontal" : "vertical"}
|
||||
/>)}
|
||||
|
||||
<Row
|
||||
justify="end"
|
||||
style={{marginTop: 20}}
|
||||
gutter={[8, 8]}
|
||||
<Button
|
||||
style={{marginRight: 8}}
|
||||
onClick={() => setCurrentStep(currentStep - 1)}
|
||||
disabled={!isActivePrevButton()}
|
||||
>
|
||||
<Button
|
||||
style={{marginRight: 8}}
|
||||
onClick={() => setCurrentStep(currentStep - 1)}
|
||||
disabled={!isActivePrevButton()}
|
||||
>
|
||||
Назад
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
if (isActiveFinishButton()) {
|
||||
await handleOk();
|
||||
} else {
|
||||
setCurrentStep(currentStep + 1);
|
||||
}
|
||||
}}
|
||||
disabled={!isActiveNextButton() && !isActiveFinishButton()}
|
||||
>
|
||||
{isActiveFinishButton() ? "Завершить" : "Далее"}
|
||||
</Button>
|
||||
</Row>
|
||||
</Modal>);
|
||||
Назад
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
if (isActiveFinishButton()) {
|
||||
await handleOk();
|
||||
} else {
|
||||
setCurrentStep(currentStep + 1);
|
||||
}
|
||||
}}
|
||||
disabled={!isActiveNextButton() && !isActiveFinishButton()}
|
||||
>
|
||||
{isActiveFinishButton() ? "Завершить" : "Далее"}
|
||||
</Button>
|
||||
</Row>
|
||||
</Modal>);
|
||||
};
|
||||
|
||||
LensIssueFormModal.propTypes = {
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import {Col, Form, InputNumber, Modal, notification, Row, Select} from "antd";
|
||||
import {useEffect, useState} from "react";
|
||||
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";
|
||||
|
||||
|
||||
const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
||||
const {user} = useAuth();
|
||||
const {api} = useAuth();
|
||||
const [form] = Form.useForm();
|
||||
const [lensTypes, setLensTypes] = useState([]);
|
||||
|
||||
@ -27,7 +27,7 @@ const LensFormModal = ({visible, onCancel, onSubmit, lens}) => {
|
||||
|
||||
const fetchLensTypes = async () => {
|
||||
try {
|
||||
const data = await getAllLensTypes(user.token);
|
||||
const data = await getAllLensTypes(api);
|
||||
setLensTypes(data);
|
||||
} catch (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 {PlusOutlined, DeleteOutlined} from "@ant-design/icons";
|
||||
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 PropTypes from "prop-types";
|
||||
import getSetContentBySetId from "../../api/set_content/GetSetContentBySetId.jsx";
|
||||
import getSetContentBySetId from "../../api/set_content/getSetContentBySetId.jsx";
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
const SetFormModal = ({visible, onCancel, setData, onSubmit}) => {
|
||||
const {user} = useAuth();
|
||||
const {api} = useAuth();
|
||||
|
||||
const [form] = Form.useForm();
|
||||
const [content, setContent] = useState([]);
|
||||
@ -33,7 +33,7 @@ const SetFormModal = ({visible, onCancel, setData, onSubmit}) => {
|
||||
if (!setData) return;
|
||||
|
||||
try {
|
||||
const data = await getSetContentBySetId(user.token, setData.id);
|
||||
const data = await getSetContentBySetId(api, setData.id);
|
||||
setContent(data);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
@ -47,7 +47,7 @@ const SetFormModal = ({visible, onCancel, setData, onSubmit}) => {
|
||||
|
||||
const fetchLensTypes = async () => {
|
||||
try {
|
||||
const data = await getAllLensTypes(user.token);
|
||||
const data = await getAllLensTypes(api);
|
||||
setLensTypes(data);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
@ -278,5 +278,4 @@ SetFormModal.propTypes = {
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
export default SetFormModal;
|
||||
|
||||
@ -7,7 +7,6 @@ const SetListCard = ({set, handleEditSet, handleDeleteSet, handleAppendSet}) =>
|
||||
handleDeleteSet(set.id);
|
||||
};
|
||||
|
||||
|
||||
const appendSet = () => {
|
||||
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 getAllAppointments from "../api/appointments/GetAllAppointments.jsx";
|
||||
import {notification, Tabs} from "antd";
|
||||
import {Button, Grid, notification, Tabs, Typography} from "antd";
|
||||
import {Splitter} from "antd";
|
||||
import {
|
||||
CalendarOutlined, TableOutlined, MenuFoldOutlined, MenuUnfoldOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import AppointmentsCalendarPage from "../pages/appointments_layout/AppointmentsCalendarPage.jsx";
|
||||
import {CalendarOutlined, TableOutlined} from "@ant-design/icons";
|
||||
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 items = [
|
||||
{
|
||||
key: '1',
|
||||
label: 'Календарь приемов',
|
||||
children: <AppointmentsCalendarPage/>,
|
||||
icon: <CalendarOutlined/>,
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: 'Таблица приемов',
|
||||
children: <AppointmentsTablePage/>,
|
||||
icon: <TableOutlined/>,
|
||||
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 () => {
|
||||
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/>,
|
||||
}, {
|
||||
key: "2",
|
||||
label: "Таблица приемов",
|
||||
children: <AppointmentsTablePage/>,
|
||||
icon: <TableOutlined/>,
|
||||
},];
|
||||
|
||||
if (loading) {
|
||||
return (<LoadingIndicator/>)
|
||||
}
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
defaultActiveKey="1"
|
||||
items={items}
|
||||
/>
|
||||
<>
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import {
|
||||
notification,
|
||||
Spin,
|
||||
Table,
|
||||
Input,
|
||||
Row,
|
||||
@ -12,21 +11,23 @@ import {
|
||||
Typography,
|
||||
Timeline, Grid, Pagination
|
||||
} from "antd";
|
||||
import getAllLensIssues from "../api/lens_issues/GetAllLensIssues.jsx";
|
||||
import getAllLensIssues from "../api/lens_issues/getAllLensIssues.jsx";
|
||||
import {useEffect, useState} from "react";
|
||||
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 dayjs from "dayjs";
|
||||
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 LoadingIndicator from "../components/LoadingIndicator.jsx";
|
||||
import {getCachedInfo, getCacheTimestamp} from "../utils/cachedInfoUtils.jsx";
|
||||
|
||||
const {Title} = Typography;
|
||||
const {useBreakpoint} = Grid;
|
||||
|
||||
const IssuesPage = () => {
|
||||
const {user} = useAuth();
|
||||
const {api} = useAuth();
|
||||
const screens = useBreakpoint();
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
@ -56,13 +57,13 @@ const IssuesPage = () => {
|
||||
const intervalId = setInterval(fetchLensIssues, 5000);
|
||||
return () => clearInterval(intervalId);
|
||||
}
|
||||
}, [user, isModalVisible]);
|
||||
}, [isModalVisible]);
|
||||
|
||||
const fetchLensIssuesWithCache = async () => {
|
||||
const cachedData = localStorage.getItem("lensIssuesData");
|
||||
const cacheTimestamp = localStorage.getItem("lensIssuesTimestamp");
|
||||
const cachedData = getCachedInfo("lensIssuesData");
|
||||
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));
|
||||
setLoading(false);
|
||||
} else {
|
||||
@ -71,7 +72,7 @@ const IssuesPage = () => {
|
||||
};
|
||||
|
||||
const fetchViewModeFromCache = () => {
|
||||
const cachedViewMode = localStorage.getItem("viewModeIssues");
|
||||
const cachedViewMode = getCachedInfo("viewModeIssues");
|
||||
if (cachedViewMode) {
|
||||
setViewMode(cachedViewMode);
|
||||
}
|
||||
@ -92,7 +93,7 @@ const IssuesPage = () => {
|
||||
|
||||
const handleSubmitFormModal = async (issue_date, patient_id, lens_id) => {
|
||||
try {
|
||||
await addLensIssue(user.token, {issue_date, patient_id, lens_id});
|
||||
await addLensIssue(api, {issue_date, patient_id, lens_id});
|
||||
setIsModalVisible(false);
|
||||
notification.success({
|
||||
message: "Линза выдана",
|
||||
@ -112,7 +113,7 @@ const IssuesPage = () => {
|
||||
|
||||
const fetchLensIssues = async () => {
|
||||
try {
|
||||
const data = await getAllLensIssues(user.token);
|
||||
const data = await getAllLensIssues(api);
|
||||
setLensIssues(data);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
@ -345,9 +346,7 @@ const IssuesPage = () => {
|
||||
</Col>
|
||||
</Row>
|
||||
{loading ? (
|
||||
<div style={{display: "flex", justifyContent: "center", alignItems: "center", height: "100vh"}}>
|
||||
<Spin indicator={<LoadingOutlined style={{fontSize: 64, color: "#1890ff"}} spin/>}/>
|
||||
</div>
|
||||
<LoadingIndicator/>
|
||||
) : viewMode === "table" ? (
|
||||
<TableView/>
|
||||
) : (
|
||||
|
||||
@ -6,7 +6,6 @@ import {
|
||||
FloatButton,
|
||||
Row,
|
||||
Col,
|
||||
Spin,
|
||||
notification,
|
||||
Tooltip,
|
||||
Table,
|
||||
@ -16,26 +15,27 @@ import {
|
||||
} from "antd";
|
||||
import {
|
||||
BuildOutlined,
|
||||
LoadingOutlined,
|
||||
PlusOutlined,
|
||||
SortAscendingOutlined,
|
||||
SortDescendingOutlined, TableOutlined,
|
||||
TeamOutlined
|
||||
} from "@ant-design/icons";
|
||||
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 PatientFormModal from "../components/patients/PatientFormModal.jsx";
|
||||
import updatePatient from "../api/patients/UpdatePatient.jsx";
|
||||
import addPatient from "../api/patients/AddPatient.jsx";
|
||||
import deletePatient from "../api/patients/DeletePatient.jsx";
|
||||
import updatePatient from "../api/patients/updatePatient.jsx";
|
||||
import addPatient from "../api/patients/addPatient.jsx";
|
||||
import deletePatient from "../api/patients/deletePatient.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 {Title} = Typography
|
||||
|
||||
const PatientsPage = () => {
|
||||
const {user} = useAuth();
|
||||
const {api} = useAuth();
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const [sortOrder, setSortOrder] = useState("asc");
|
||||
const [viewMode, setViewMode] = useState("tile");
|
||||
@ -59,14 +59,14 @@ const PatientsPage = () => {
|
||||
const intervalId = setInterval(fetchPatients, 5000);
|
||||
return () => clearInterval(intervalId);
|
||||
}
|
||||
}, [user, isModalVisible, selectedPatient]);
|
||||
}, [isModalVisible, selectedPatient]);
|
||||
|
||||
const fetchPatientsWithCache = async () => {
|
||||
const cachedData = localStorage.getItem("patientsData");
|
||||
const cacheTimestamp = localStorage.getItem("patientsTimestamp");
|
||||
const cachedData = getCachedInfo("patientsData");
|
||||
const cacheTimestamp = getCacheTimestamp("patientsData");
|
||||
|
||||
if (cachedData && cacheTimestamp && (Date.now() - parseInt(cacheTimestamp)) < 60 * 1000) {
|
||||
setPatients(JSON.parse(cachedData));
|
||||
if (cachedData && cacheTimestamp && (Date.now() - cacheTimestamp) < 60 * 1000) {
|
||||
setPatients(cachedData);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
@ -76,11 +76,10 @@ const PatientsPage = () => {
|
||||
|
||||
const fetchPatients = async () => {
|
||||
try {
|
||||
const data = await getAllPatients(user.token);
|
||||
const data = await getAllPatients(api);
|
||||
setPatients(data);
|
||||
|
||||
localStorage.setItem("patientsData", JSON.stringify(data));
|
||||
localStorage.setItem("patientsTimestamp", Date.now().toString());
|
||||
cacheInfo("patientsData", data);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
notification.error({
|
||||
@ -90,13 +89,11 @@ const PatientsPage = () => {
|
||||
})
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
setLoading(false);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const fetchViewModeFromCache = () => {
|
||||
const cachedViewMode = localStorage.getItem("viewModePatients");
|
||||
const cachedViewMode = getCachedInfo("viewModePatients");
|
||||
if (cachedViewMode) {
|
||||
setViewMode(cachedViewMode);
|
||||
}
|
||||
@ -128,7 +125,7 @@ const PatientsPage = () => {
|
||||
|
||||
const handleDeletePatient = async (patient_id) => {
|
||||
try {
|
||||
await deletePatient(user.token, patient_id);
|
||||
await deletePatient(api, patient_id);
|
||||
await fetchPatients();
|
||||
notification.success({
|
||||
message: "Пациент удалён",
|
||||
@ -171,7 +168,7 @@ const PatientsPage = () => {
|
||||
};
|
||||
|
||||
const editPatient = async (patient) => {
|
||||
await updatePatient(user.token, selectedPatient.id, patient);
|
||||
await updatePatient(api, selectedPatient.id, patient);
|
||||
notification.success({
|
||||
message: "Пациент обновлён",
|
||||
description: `Данные пациента ${patient.first_name} ${patient.last_name} успешно обновлены.`,
|
||||
@ -180,7 +177,7 @@ const PatientsPage = () => {
|
||||
};
|
||||
|
||||
const addNewPatient = async (patient) => {
|
||||
await addPatient(user.token, patient);
|
||||
await addPatient(api, patient);
|
||||
notification.success({
|
||||
message: "Пациент добавлен",
|
||||
description: `Пациент ${patient.first_name} ${patient.last_name} успешно добавлен.`,
|
||||
@ -370,14 +367,7 @@ const PatientsPage = () => {
|
||||
</Row>
|
||||
|
||||
{loading ? (
|
||||
<div style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "100vh",
|
||||
}}>
|
||||
<Spin indicator={<LoadingOutlined style={{fontSize: 64, color: "#1890ff"}} spin/>}/>
|
||||
</div>
|
||||
<LoadingIndicator/>
|
||||
) : viewMode === "tile" ? (
|
||||
<TileView/>
|
||||
) : (
|
||||
|
||||
@ -1,25 +1,149 @@
|
||||
import {Calendar, Grid} from "antd";
|
||||
import {Calendar, Grid, ConfigProvider, Badge, Modal} 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 from "prop-types";
|
||||
|
||||
const {useBreakpoint} = Grid;
|
||||
|
||||
const AppointmentsCalendarPage = () => {
|
||||
const screens = useBreakpoint();
|
||||
dayjs.extend(updateLocale);
|
||||
dayjs.updateLocale('ru', {
|
||||
weekStart: 1
|
||||
});
|
||||
|
||||
const [selectedDate, setSelectedDate] = useState(dayjs(
|
||||
new Date()
|
||||
));
|
||||
const AppointmentsCalendarPage = ({appointments, scheduledAppointments}) => {
|
||||
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 (
|
||||
<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 (
|
||||
<div style={{padding: 20}}>
|
||||
<Calendar
|
||||
fullscreen={!screens.xs}
|
||||
value={selectedDate}
|
||||
onSelect={setSelectedDate}
|
||||
/>
|
||||
</div>
|
||||
<ConfigProvider locale={locale}>
|
||||
<div style={{padding: 20}}>
|
||||
<Calendar
|
||||
fullscreen={!screens.xs}
|
||||
value={selectedDate}
|
||||
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>
|
||||
))}
|
||||
</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;
|
||||
@ -1,11 +1,11 @@
|
||||
import {useAuth} from "../../AuthContext.jsx";
|
||||
import {useEffect, useState} from "react";
|
||||
import getAllAppointments from "../../api/appointments/GetAllAppointments.jsx";
|
||||
import getAllAppointments from "../../api/appointments/getAllAppointments.jsx";
|
||||
import {notification} from "antd";
|
||||
|
||||
|
||||
const AppointmentsTablePage = () => {
|
||||
const {user} = useAuth();
|
||||
const {api} = useAuth();
|
||||
|
||||
const [appointments, setAppointments] = useState([]);
|
||||
|
||||
@ -16,52 +16,6 @@ const AppointmentsTablePage = () => {
|
||||
const [selectedAppointment, setSelectedAppointment] = useState(null);
|
||||
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 (
|
||||
<div style={{padding: 20}}>
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@ import {
|
||||
FloatButton,
|
||||
Row,
|
||||
Col,
|
||||
Spin,
|
||||
Button,
|
||||
Form,
|
||||
InputNumber,
|
||||
@ -18,7 +17,6 @@ import {
|
||||
Typography
|
||||
} from "antd";
|
||||
import {
|
||||
LoadingOutlined,
|
||||
PlusOutlined,
|
||||
DownOutlined,
|
||||
UpOutlined,
|
||||
@ -27,20 +25,22 @@ import {
|
||||
BuildOutlined
|
||||
} from "@ant-design/icons";
|
||||
import LensCard from "../../components/lenses/LensListCard.jsx";
|
||||
import getAllLenses from "../../api/lenses/GetAllLenses.jsx";
|
||||
import addLens from "../../api/lenses/AddLens.jsx";
|
||||
import updateLens from "../../api/lenses/UpdateLens.jsx";
|
||||
import deleteLens from "../../api/lenses/DeleteLens.jsx";
|
||||
import getAllLenses from "../../api/lenses/getAllLenses.jsx";
|
||||
import addLens from "../../api/lenses/addLens.jsx";
|
||||
import updateLens from "../../api/lenses/updateLens.jsx";
|
||||
import deleteLens from "../../api/lenses/deleteLens.jsx";
|
||||
import {useAuth} from "../../AuthContext.jsx";
|
||||
import LensFormModal from "../../components/lenses/LensFormModal.jsx";
|
||||
import SelectViewMode from "../../components/SelectViewMode.jsx";
|
||||
import LoadingIndicator from "../../components/LoadingIndicator.jsx";
|
||||
import {getCachedInfo, getCacheTimestamp} from "../../utils/cachedInfoUtils.jsx";
|
||||
|
||||
const {Option} = Select;
|
||||
const {useBreakpoint} = Grid;
|
||||
const {Title} = Typography;
|
||||
|
||||
const LensesPage = () => {
|
||||
const {user} = useAuth();
|
||||
const {api} = useAuth();
|
||||
const screens = useBreakpoint();
|
||||
|
||||
const [current, setCurrent] = useState(1);
|
||||
@ -76,11 +76,11 @@ const LensesPage = () => {
|
||||
const intervalId = setInterval(fetchLenses, 5000);
|
||||
return () => clearInterval(intervalId);
|
||||
}
|
||||
}, [user, isModalVisible, selectedLens]);
|
||||
}, [isModalVisible, selectedLens]);
|
||||
|
||||
const fetchLensWithCache = async () => {
|
||||
const cachedData = localStorage.getItem("lensData");
|
||||
const cacheTimestamp = localStorage.getItem("lensTimestamp");
|
||||
const cachedData = getCachedInfo("lensData");
|
||||
const cacheTimestamp = getCacheTimestamp("lensData");
|
||||
|
||||
if (cachedData && cacheTimestamp && (Date.now() - parseInt(cacheTimestamp)) < 60 * 1000) {
|
||||
setLenses(JSON.parse(cachedData));
|
||||
@ -93,7 +93,7 @@ const LensesPage = () => {
|
||||
|
||||
const fetchLenses = async () => {
|
||||
try {
|
||||
const data = await getAllLenses(user.token);
|
||||
const data = await getAllLenses(api);
|
||||
setLenses(data);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
@ -108,7 +108,7 @@ const LensesPage = () => {
|
||||
};
|
||||
|
||||
const fetchViewModeFromCache = () => {
|
||||
const cachedViewMode = localStorage.getItem("viewModeLenses");
|
||||
const cachedViewMode = getCachedInfo("viewModeLenses");
|
||||
if (cachedViewMode) {
|
||||
setViewMode(cachedViewMode);
|
||||
}
|
||||
@ -148,8 +148,8 @@ const LensesPage = () => {
|
||||
|
||||
const handleDeleteLens = async (lensId) => {
|
||||
try {
|
||||
await deleteLens(user.token, lensId);
|
||||
await fetchLenses(user.token);
|
||||
await deleteLens(api, lensId);
|
||||
await fetchLenses(api);
|
||||
notification.success({
|
||||
message: "Линза удалена",
|
||||
description: "Линза успешно удалена.",
|
||||
@ -168,14 +168,14 @@ const LensesPage = () => {
|
||||
const handleModalSubmit = async (lensData) => {
|
||||
try {
|
||||
if (selectedLens) {
|
||||
await updateLens(user.token, selectedLens.id, lensData);
|
||||
await updateLens(api, selectedLens.id, lensData);
|
||||
notification.success({
|
||||
message: "Линза обновлена",
|
||||
description: "Линза успешно обновлена.",
|
||||
placement: "topRight",
|
||||
});
|
||||
} else {
|
||||
await addLens(user.token, lensData);
|
||||
await addLens(api, lensData);
|
||||
notification.success({
|
||||
message: "Линза добавлена",
|
||||
description: "Линза успешно добавлена.",
|
||||
@ -345,7 +345,6 @@ const LensesPage = () => {
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<div style={{padding: 20}}>
|
||||
<Title level={1}><FolderViewOutlined/> Линзы</Title>
|
||||
@ -497,9 +496,7 @@ const LensesPage = () => {
|
||||
)}
|
||||
|
||||
{loading ? (
|
||||
<div style={{display: "flex", justifyContent: "center", alignItems: "center", height: "100vh"}}>
|
||||
<Spin indicator={<LoadingOutlined style={{fontSize: 64, color: "#1890ff"}} spin/>}/>
|
||||
</div>
|
||||
<LoadingIndicator/>
|
||||
) : viewMode === "tile" ? (
|
||||
<TileView/>
|
||||
) : (
|
||||
|
||||
@ -1,22 +1,24 @@
|
||||
import {useAuth} from "../../AuthContext.jsx";
|
||||
import {useEffect, useState} from "react";
|
||||
import {FloatButton, Input, List, notification, Row, Spin, Typography} from "antd";
|
||||
import getAllSets from "../../api/sets/GetAllSets.jsx";
|
||||
import {LoadingOutlined, PlusOutlined, SwitcherOutlined} from "@ant-design/icons";
|
||||
import {FloatButton, Input, List, notification, Row, Typography} from "antd";
|
||||
import getAllSets from "../../api/sets/getAllSets.jsx";
|
||||
import {PlusOutlined, SwitcherOutlined} from "@ant-design/icons";
|
||||
import SetListCard from "../../components/sets/SetListCard.jsx";
|
||||
import SetFormModal from "../../components/sets/SetFormModal.jsx";
|
||||
import updateSet from "../../api/sets/UpdateSet.jsx";
|
||||
import addSet from "../../api/sets/AddSet.jsx";
|
||||
import deleteSet from "../../api/sets/DeleteSet.jsx";
|
||||
import addSetContent from "../../api/set_content/AddSetContent.jsx";
|
||||
import updateSetContent from "../../api/set_content/UpdateSetContent.jsx";
|
||||
import appendLensesFromSet from "../../api/sets/AppendLensesFromSet.jsx";
|
||||
import updateSet from "../../api/sets/updateSet.jsx";
|
||||
import addSet from "../../api/sets/addSet.jsx";
|
||||
import deleteSet from "../../api/sets/deleteSet.jsx";
|
||||
import addSetContent from "../../api/set_content/addSetContent.jsx";
|
||||
import updateSetContent from "../../api/set_content/updateSetContent.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 SetLensesPage = () => {
|
||||
const {user} = useAuth();
|
||||
const {api} = useAuth();
|
||||
|
||||
const [current, setCurrent] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
@ -36,14 +38,14 @@ const SetLensesPage = () => {
|
||||
const intervalId = setInterval(fetchSets, 5000);
|
||||
return () => clearInterval(intervalId);
|
||||
}
|
||||
}, [user, isModalVisible]);
|
||||
}, [isModalVisible]);
|
||||
|
||||
const fetchSetsWithCache = async () => {
|
||||
const cachedData = localStorage.getItem("setsData");
|
||||
const cacheTimestamp = localStorage.getItem("setsTimestamp");
|
||||
const cachedData = getCachedInfo("setsData");
|
||||
const cacheTimestamp = getCacheTimestamp("setsData");
|
||||
|
||||
if (cachedData && cacheTimestamp && (Date.now() - parseInt(cacheTimestamp)) < 60 * 1000) {
|
||||
setSets(JSON.parse(cachedData));
|
||||
if (cachedData && cacheTimestamp && (Date.now() - cacheTimestamp) < 60 * 1000) {
|
||||
setSets(cachedData);
|
||||
setLoading(false);
|
||||
} else {
|
||||
await fetchSets();
|
||||
@ -51,14 +53,11 @@ const SetLensesPage = () => {
|
||||
};
|
||||
|
||||
const fetchSets = async () => {
|
||||
if (!user || !user.token) return;
|
||||
|
||||
try {
|
||||
const data = await getAllSets(user.token);
|
||||
const data = await getAllSets(api);
|
||||
setSets(data);
|
||||
|
||||
localStorage.setItem("setsData", JSON.stringify(data));
|
||||
localStorage.setItem("setsTimestamp", Date.now().toString());
|
||||
cacheInfo("setsData", data);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
notification.error({
|
||||
@ -87,7 +86,7 @@ const SetLensesPage = () => {
|
||||
|
||||
const handleDeleteSet = async (set_id) => {
|
||||
try {
|
||||
await deleteSet(user.token, set_id);
|
||||
await deleteSet(api, set_id);
|
||||
notification.success({
|
||||
message: "Набор удален",
|
||||
description: "Набор успешно удален.",
|
||||
@ -110,7 +109,7 @@ const SetLensesPage = () => {
|
||||
|
||||
const handleAppendSet = async (set) => {
|
||||
try {
|
||||
await appendLensesFromSet(user.token, set.id);
|
||||
await appendLensesFromSet(api, set.id);
|
||||
notification.success({
|
||||
message: "Линзы добавлены",
|
||||
description: "Линзы успешно добавлены.",
|
||||
@ -157,7 +156,7 @@ const SetLensesPage = () => {
|
||||
const setContent = async (content, set_id) => {
|
||||
try {
|
||||
console.log(content);
|
||||
await addSetContent(user.token, content, set_id);
|
||||
await addSetContent(api, content, set_id);
|
||||
} catch (error) {
|
||||
console.error("Ошибка сохранения набора:", error);
|
||||
notification.error({
|
||||
@ -170,7 +169,7 @@ const SetLensesPage = () => {
|
||||
|
||||
const updateContent = async (content, set_id) => {
|
||||
try {
|
||||
await updateSetContent(user.token, content, set_id);
|
||||
await updateSetContent(api, content, set_id);
|
||||
} catch (error) {
|
||||
console.error("Ошибка сохранения набора:", error);
|
||||
notification.error({
|
||||
@ -182,7 +181,7 @@ const SetLensesPage = () => {
|
||||
};
|
||||
|
||||
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({
|
||||
message: "Набор обновлен",
|
||||
description: "Набор успешно обновлен.",
|
||||
@ -192,7 +191,7 @@ const SetLensesPage = () => {
|
||||
};
|
||||
|
||||
const addNewSet = async (set) => {
|
||||
const refreshed_set = await addSet(user.token, set);
|
||||
const refreshed_set = await addSet(api, set);
|
||||
notification.success({
|
||||
message: "Набор добавлен",
|
||||
description: "Набор успешно добавлен.",
|
||||
@ -214,14 +213,7 @@ const SetLensesPage = () => {
|
||||
</Row>
|
||||
|
||||
{loading ? (
|
||||
<div style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "100vh",
|
||||
}}>
|
||||
<Spin indicator={<LoadingOutlined style={{fontSize: 64, color: "#1890ff"}} spin/>}/>
|
||||
</div>
|
||||
<LoadingIndicator/>
|
||||
) : (
|
||||
<List
|
||||
grid={{
|
||||
|
||||
@ -4,6 +4,7 @@ body {
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
*::-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