diff --git a/src/apis/passApi.ts b/src/apis/passApi.ts index 8f11164..610ce9f 100644 --- a/src/apis/passApi.ts +++ b/src/apis/passApi.ts @@ -4,7 +4,7 @@ import axiosWithAuthorization from "../contexts/axiosWithAuthorization"; export const fetchEntryPassLog = async (page: number) => { try { const res = await axiosWithAuthorization.get(`/pass-logs/enter?page=${page}`); - console.log("---> 출입 내역 조회:", res.data); + console.log("출입 내역 조회:", res.data); return res.data.data; } catch (error) { console.log("출입 내역 조회 오류:", error); @@ -51,4 +51,16 @@ export const reviewPass = async (passId: number, issuanceStatus: "ISSUED" | "REJ } }; +// 출입 로그 health-check +export const checkPassLogHealthCheck = async () => { + try { + const res = await axiosWithAuthorization.get(`/pass-logs/health`); + console.log("출입 로그 health-check:", res.data); + return res.data.data; + } catch (error) { + console.log("출입 로그 health-check 오류:", error); + throw error; + } +}; + diff --git a/src/components/_Admin/AdminLoginBox.tsx b/src/components/_Admin/AdminLoginBox.tsx index 0069e8d..61ef1f1 100644 --- a/src/components/_Admin/AdminLoginBox.tsx +++ b/src/components/_Admin/AdminLoginBox.tsx @@ -5,7 +5,8 @@ import ReusableButton from "../buttons/ReusableButton"; import ReusableInput from "../input/ReusableInput"; import { adminLogin } from "../../apis/loginApi"; -import { fetchEntryPassLog } from "../../apis/passApi"; +import { checkPassLogHealthCheck } from "../../apis/passApi"; +import { usePassLogContext } from "../../contexts/PassLogContext.tsx"; interface AdminLoginProps { onLogin: () => void; @@ -16,6 +17,7 @@ const AdminLogin: React.FC = ({ onLogin }) => { const [password, setPassword] = useState(""); const [error, setError] = useState(""); const navigate = useNavigate(); + const { setIsPassLogAvailable, setHasCheckedAvailability } = usePassLogContext(); useEffect(() => { const darkMode = localStorage.getItem("theme") === "dark"; @@ -35,16 +37,20 @@ const AdminLogin: React.FC = ({ onLogin }) => { const token = response.accessToken; if (token) { localStorage.setItem("accessToken", token); + localStorage.setItem("skipFirstCheck", "true"); onLogin(); try { - await fetchEntryPassLog(0); + await checkPassLogHealthCheck(); + setIsPassLogAvailable(true); + setHasCheckedAvailability(true); navigate("/dashboardstats"); } catch (error) { - console.error("<--- 출입 로그 조회 실패:", error); + console.error("<--- 출입 로그 조회 실패:", error); + setIsPassLogAvailable(false); + setHasCheckedAvailability(true); navigate("/admin/mypage"); } - } else { setError("로그인 실패: 엑세스 토큰이 없음"); } diff --git a/src/contexts/PassLogContext.tsx b/src/contexts/PassLogContext.tsx index cc6c2f2..3bddb20 100644 --- a/src/contexts/PassLogContext.tsx +++ b/src/contexts/PassLogContext.tsx @@ -1,29 +1,36 @@ import React, { createContext, useContext, useEffect, useState } from 'react'; -import { fetchEntryPassLog } from '../apis/passApi'; +import { checkPassLogHealthCheck } from '../apis/passApi'; export interface PassLogContextType { isPassLogAvailable: boolean; + hasCheckedAvailability: boolean; checkAccessLogAvailability: () => Promise; setIsPassLogAvailable: React.Dispatch>; + setHasCheckedAvailability: React.Dispatch>; } export const PassLogContext = createContext({ isPassLogAvailable: true, + hasCheckedAvailability: false, checkAccessLogAvailability: async () => false, setIsPassLogAvailable: () => {}, + setHasCheckedAvailability: () => {}, }); export const PassLogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [isPassLogAvailable, setIsPassLogAvailable] = useState(false); + const [hasCheckedAvailability, setHasCheckedAvailability] = useState(false); const checkAccessLogAvailability = async (): Promise => { try { - await fetchEntryPassLog(0); + await checkPassLogHealthCheck(); setIsPassLogAvailable(true); + setHasCheckedAvailability(true); return true; } catch (error) { console.error('<---출입 로그 사용 불가--->:', error); setIsPassLogAvailable(false); + setHasCheckedAvailability(true); return false; } }; @@ -34,12 +41,12 @@ export const PassLogProvider: React.FC<{ children: React.ReactNode }> = ({ child if (reloaded === 'true') { console.log('새로고침 후 checkAccessLogAvailability 실행'); checkAccessLogAvailability(); - // sessionStorage.removeItem('reloaded'); + sessionStorage.removeItem('reloaded'); } const intervalId = setInterval(() => { checkAccessLogAvailability(); - }, 20000); + }, 5000); const handleStorageChange = (event: StorageEvent) => { if (event.key === 'accessToken') { @@ -63,8 +70,10 @@ export const PassLogProvider: React.FC<{ children: React.ReactNode }> = ({ child {children} diff --git a/src/pages/DashboardStats.tsx b/src/pages/DashboardStats.tsx index 2c6ece0..c574ab5 100644 --- a/src/pages/DashboardStats.tsx +++ b/src/pages/DashboardStats.tsx @@ -10,11 +10,29 @@ import Layout from '../components/layout/Layout'; import Background from '../components/background/Background'; import Warning from '../components/warning/Warning'; +import Loading from "../components/loading/Loading.tsx"; import './css/DashboardStats.css'; +import '../components/loading/css/Loading.css' const DashboardStats = () => { - const { isPassLogAvailable } = usePassLogContext(); + const { isPassLogAvailable, hasCheckedAvailability } = usePassLogContext(); + + if (!hasCheckedAvailability) { + return ( + <> + + +
+
+ +
방문자 통계를 불러오는 중입니다...
+
+
+
+ + ); + } if (!isPassLogAvailable) { return ( diff --git a/src/pages/IssueDetailPage.tsx b/src/pages/IssueDetailPage.tsx index 42d61fe..2c96ad8 100644 --- a/src/pages/IssueDetailPage.tsx +++ b/src/pages/IssueDetailPage.tsx @@ -1,9 +1,11 @@ import { useLocation } from 'react-router-dom'; +import { usePassLogContext } from "../contexts/PassLogContext.tsx"; import Layout from '../components/layout/Layout.tsx'; import Background from '../components/background/Background.tsx'; import Breadcrumb from '../components/breadcrumb/Breadcrumb.tsx'; import DefaultTable from '../components/table/DefaultTable.tsx'; +import Warning from "../components/warning/Warning.tsx"; import './css/IssueDetailPage.css'; @@ -33,6 +35,19 @@ const IssueDetailPage = () => { const issue = [data]; const areasInfo = data?.areas || []; + const { isPassLogAvailable } = usePassLogContext(); + + if (!isPassLogAvailable) { + return ( + <> + + + + + + ); + } + return ( <> diff --git a/src/pages/css/DashboardStats.css b/src/pages/css/DashboardStats.css index 91d6cf1..1751778 100644 --- a/src/pages/css/DashboardStats.css +++ b/src/pages/css/DashboardStats.css @@ -6,6 +6,14 @@ justify-items: center; } +.dashboard-stats-loading-container { + position: relative; + display: flex; + align-items: center; + justify-items: center; + min-height: 400px; +} + .dashboard-stats-container > :first-child { grid-column: span 2; }