| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- 'use client';
- import { useState, useEffect, useCallback } from 'react';
- import { Swiper, SwiperSlide } from 'swiper/react';
- import { Navigation, Pagination } from 'swiper/modules';
- import { PopupItem, PopupResponse } from '@/types/response/page/popup';
- import { fetchApi } from '@/lib/utils/client';
- import 'swiper/css';
- import 'swiper/css/navigation';
- import 'swiper/css/pagination';
- import '../styles/popup.scss';
- interface PopupModalProps {
- position: string;
- }
- // localStorage 키 생성
- function getDismissKey(position: string): string {
- return `popup_dismiss_${position}`;
- }
- // 24시간 이내에 dismiss 했는지 확인
- function isDismissed(position: string): boolean {
- if (typeof window === 'undefined') {
- return false;
- }
- const dismissed = localStorage.getItem(getDismissKey(position));
- if (!dismissed) {
- return false;
- }
- const dismissedAt = parseInt(dismissed, 10);
- const now = Date.now();
- const hours24 = 24 * 60 * 60 * 1000;
- return (now - dismissedAt) < hours24;
- }
- // 유효한 팝업인지 확인 (isActive + 날짜 범위)
- function isValidPopup(item: PopupItem): boolean {
- if (!item.isActive) {
- return false;
- }
- const now = new Date();
- if (item.startAt && new Date(item.startAt) > now) {
- return false;
- }
- if (item.endAt && new Date(item.endAt) < now) {
- return false;
- }
- return true;
- }
- export default function PopupModal({ position }: PopupModalProps) {
- const [open, setOpen] = useState<boolean>(false);
- const [items, setItems] = useState<PopupItem[]>([]);
- const [dismissToday, setDismissToday] = useState<boolean>(false);
- useEffect(() => {
- // 이미 dismiss 되었으면 fetch하지 않음
- if (isDismissed(position)) {
- return;
- }
- fetchApi<PopupResponse>('/api/popup/popups', {
- method: 'POST',
- body: { Code: position }
- }).then((res) => {
- if (res.success && res.data) {
- const validItems = (res.data.list ?? []).filter(isValidPopup);
- if (validItems.length > 0) {
- setItems(validItems);
- setOpen(true);
- }
- }
- });
- }, [position]);
- const handleClose = useCallback(() => {
- if (dismissToday) {
- localStorage.setItem(getDismissKey(position), Date.now().toString());
- }
- setOpen(false);
- }, [dismissToday, position]);
- // 팝업이 없거나 닫혔으면 렌더링하지 않음
- if (!open || items.length === 0) {
- return null;
- }
- return (
- <div className='popup-modal__overlay' onClick={handleClose}>
- <div className='popup-modal__container' onClick={(e) => e.stopPropagation()}>
- {/* 팝업 본문 */}
- <div className='popup-modal__body'>
- {items.length === 1 ? (
- // 단일 팝업
- <PopupSlide item={items[0]} />
- ) : (
- // 다중 팝업 - Swiper 슬라이드
- <Swiper
- modules={[Navigation, Pagination]}
- navigation
- pagination={{ clickable: true }}
- loop={items.length > 1}
- spaceBetween={0}
- slidesPerView={1}
- >
- {items.map((item) => (
- <SwiperSlide key={item.id}>
- <PopupSlide item={item} />
- </SwiperSlide>
- ))}
- </Swiper>
- )}
- </div>
- {/* 하단 - 하루 동안 보지 않기 + 닫기 */}
- <div className='popup-modal__footer'>
- <label className='popup-modal__dismiss'>
- <input
- type='checkbox'
- checked={dismissToday}
- onChange={(e) => setDismissToday(e.target.checked)}
- />
- <span>하루 동안 보지 않기</span>
- </label>
- <button type='button' className='popup-modal__close' onClick={handleClose}>
- 닫기
- </button>
- </div>
- </div>
- </div>
- );
- }
- // 개별 팝업 슬라이드
- function PopupSlide({ item }: { item: PopupItem }) {
- const content = (
- <div className='popup-modal__slide'>
- {item.subject && (
- <h3 className='popup-modal__slide-subject'>{item.subject}</h3>
- )}
- {item.content && (
- <div
- className='popup-modal__slide-content'
- dangerouslySetInnerHTML={{ __html: item.content }}
- />
- )}
- </div>
- );
- if (item.link) {
- return (
- <a href={item.link} target='_blank' rel='noopener noreferrer' className='popup-modal__slide-link'>
- {content}
- </a>
- );
- }
- return content;
- }
|