deploy: 2026-03-11 배포
- feat: MNG→SAM 자동 로그인 (auto-login 페이지, token-login API 프록시, auth-config) - feat: QMS 품질감사 API 연동 (actions, hooks, Day1/Day2 컴포넌트 개선) - feat: 공지 팝업 모달 (NoticePopupContainer, PopupManagement 설정 개선) - feat: CEO 대시보드 캘린더 섹션 개선 - fix: 게시판 폼, 로그인 페이지, 작업자 화면, 청구서 관리 수정 - chore: AuthenticatedLayout, logout, userStorage 정리
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { NoticePopupModal, isPopupDismissedForToday } from './NoticePopupModal';
|
||||
import { getActivePopups } from './actions';
|
||||
import type { NoticePopupData } from './NoticePopupModal';
|
||||
import type { Popup } from '@/components/settings/PopupManagement/types';
|
||||
|
||||
/**
|
||||
* 활성 팝업을 자동으로 가져와 순차적으로 표시하는 컨테이너
|
||||
* - AuthenticatedLayout에 마운트
|
||||
* - 오늘 하루 안 보기 처리된 팝업은 건너뜀
|
||||
* - 여러 개일 경우 하나 닫으면 다음 팝업 표시
|
||||
*/
|
||||
export default function NoticePopupContainer() {
|
||||
const [popups, setPopups] = useState<NoticePopupData[]>([]);
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
async function fetchPopups() {
|
||||
try {
|
||||
// localStorage에서 사용자 부서 ID 조회 (부서별 팝업 필터링용)
|
||||
const user = JSON.parse(localStorage.getItem('user') || '{}');
|
||||
const activePopups = await getActivePopups(user.department_id ?? undefined);
|
||||
|
||||
if (cancelled) return;
|
||||
|
||||
// 날짜 범위 + 오늘 하루 안 보기 필터링
|
||||
const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
|
||||
const visiblePopups = activePopups
|
||||
.filter((p) => {
|
||||
// 기간 내 팝업만 (startDate~endDate)
|
||||
if (p.startDate && today < p.startDate) return false;
|
||||
if (p.endDate && today > p.endDate) return false;
|
||||
// 오늘 하루 안 보기 처리된 팝업 제외
|
||||
if (isPopupDismissedForToday(p.id)) return false;
|
||||
return true;
|
||||
})
|
||||
.map((p) => ({
|
||||
id: p.id,
|
||||
title: p.title,
|
||||
content: p.content,
|
||||
}));
|
||||
|
||||
if (visiblePopups.length > 0) {
|
||||
setPopups(visiblePopups);
|
||||
setCurrentIndex(0);
|
||||
setOpen(true);
|
||||
}
|
||||
} catch {
|
||||
// 팝업 로드 실패 시 무시 (핵심 기능 아님)
|
||||
}
|
||||
}
|
||||
|
||||
fetchPopups();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const currentPopup = popups[currentIndex];
|
||||
|
||||
if (!currentPopup) return null;
|
||||
|
||||
const handleOpenChange = (isOpen: boolean) => {
|
||||
if (!isOpen) {
|
||||
// 다음 팝업이 있으면 표시
|
||||
const nextIndex = currentIndex + 1;
|
||||
if (nextIndex < popups.length) {
|
||||
setCurrentIndex(nextIndex);
|
||||
// 약간의 딜레이로 자연스러운 전환
|
||||
setTimeout(() => setOpen(true), 200);
|
||||
} else {
|
||||
setOpen(false);
|
||||
}
|
||||
} else {
|
||||
setOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<NoticePopupModal
|
||||
popup={currentPopup}
|
||||
open={open}
|
||||
onOpenChange={handleOpenChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
31
src/components/common/NoticePopupModal/actions.ts
Normal file
31
src/components/common/NoticePopupModal/actions.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
'use server';
|
||||
|
||||
/**
|
||||
* 공지 팝업 서버 액션
|
||||
*
|
||||
* API Endpoints:
|
||||
* - GET /api/v1/popups/active - 사용자용 활성 팝업 조회 (날짜+부서 필터 백엔드 처리)
|
||||
*/
|
||||
|
||||
import { executeServerAction } from '@/lib/api/execute-server-action';
|
||||
import { buildApiUrl } from '@/lib/api/query-params';
|
||||
import { type PopupApiData, transformApiToFrontend } from '@/components/settings/PopupManagement/utils';
|
||||
import type { Popup } from '@/components/settings/PopupManagement/types';
|
||||
|
||||
/**
|
||||
* 활성 팝업 목록 조회 (사용자용)
|
||||
* - 백엔드 scopeActive(): status=active + 날짜 범위 내
|
||||
* - 백엔드 scopeForUser(): 전사 OR 사용자 부서
|
||||
* @param departmentId - 사용자 소속 부서 ID (부서별 팝업 필터용)
|
||||
*/
|
||||
export async function getActivePopups(departmentId?: number): Promise<Popup[]> {
|
||||
const result = await executeServerAction({
|
||||
url: buildApiUrl('/api/v1/popups/active', {
|
||||
department_id: departmentId,
|
||||
}),
|
||||
transform: (data: PopupApiData[]) => data.map(transformApiToFrontend),
|
||||
errorMessage: '활성 팝업 조회에 실패했습니다.',
|
||||
});
|
||||
|
||||
return result.success ? (result.data ?? []) : [];
|
||||
}
|
||||
Reference in New Issue
Block a user