refactor: 팝업 로직 수정
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import { ViewPopupPage as VViewPopupPage } from "@/pages/popup";
|
||||
|
||||
export default async function ViewPopupPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ popupSn: string }>;
|
||||
}) {
|
||||
const { popupSn } = await params;
|
||||
return <VViewPopupPage popupSn={popupSn} />;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export { CreatePage } from "./ui/create-page";
|
||||
export { ModifyPage } from "./ui/modify-page";
|
||||
export { PopupPage } from "./ui/popup-page";
|
||||
export { ViewPopupPage } from "./ui/view-popup-page";
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
export const previewTemplate = (
|
||||
popupSn: number,
|
||||
popupTitle: string,
|
||||
popupCn: string,
|
||||
) => {
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>팝업 미리보기</title>
|
||||
<style>
|
||||
*,*::before,*::after{box-sizing:border-box}
|
||||
*{margin:0}
|
||||
@media(prefers-reduced-motion:no-preference){html{interpolate-size:allow-keywords}}
|
||||
body{line-height:1.5;-webkit-font-smoothing:antialiased}
|
||||
img,picture,video,canvas,svg{display:block;max-width:100%}
|
||||
input,button,textarea,select{font:inherit}
|
||||
p,h1,h2,h3,h4,h5,h6{overflow-wrap:break-word}
|
||||
p{text-wrap:pretty}
|
||||
h1,h2,h3,h4,h5,h6{text-wrap:balance}
|
||||
#root,#__next{isolation:isolate}
|
||||
body{margin:0;padding:0;font-family:sans-serif;padding:1rem;background:#f1f6f9}
|
||||
hr{margin:0.5rem 0 1rem;border:1px solid #eee}
|
||||
pre{font-size:1rem;line-height:1}
|
||||
.popup-container{width:100%;height:100%;overflow:auto;background:#fff;border-radius:0.5rem;padding:1rem;position:relative}
|
||||
.popup-header h1{font-size:1.2rem;font-weight:600}
|
||||
.popup-footer{display:flex;justify-content:space-between;margin-top:1rem;padding-top:1rem;border-top:1px solid #eee}
|
||||
.popup-footer .left{display:flex;align-items:center}
|
||||
.popup-footer .right{display:flex}
|
||||
.popup-footer button{padding:0.5rem 1rem;border-radius:0.25rem;border:none;cursor:pointer;font-size:0.875rem}
|
||||
.popup-footer button.close{background:#f1f6f9;color:#333}
|
||||
.popup-footer button.close:hover{background:#e3eef8}
|
||||
.popup-footer label{display:flex;align-items:center;cursor:pointer;font-size:0.875rem}
|
||||
.popup-footer input[type="checkbox"]{margin-right:0.5rem}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="popup-container">
|
||||
<div class="popup-header">
|
||||
<h1>${popupTitle || "팝업 미리보기"}</h1>
|
||||
</div>
|
||||
<hr />
|
||||
<pre>${popupCn || ""}</pre>
|
||||
<div class="popup-footer">
|
||||
<div class="left">
|
||||
<label>
|
||||
<input type="checkbox" id="doNotShowToday" />
|
||||
오늘 하루 보지 않기
|
||||
</label>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button class="close" onclick="closePopup()">닫기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function closePopup() {
|
||||
// if (document.getElementById('doNotShowToday').checked) {
|
||||
// const popupId = '${popupSn}';
|
||||
// const expireDate = new Date();
|
||||
// expireDate.setDate(expireDate.getDate() + 1);
|
||||
// document.cookie = \`cs-popup-hide-for-day-\${popupId}=true; expires=\${expireDate.toUTCString()}; path=/\`;
|
||||
// }
|
||||
window.close();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
};
|
||||
@@ -3,7 +3,6 @@ import { type ColumnDef } from "@tanstack/react-table";
|
||||
import { Button, Checkbox } from "@common/components";
|
||||
import { PopupApi } from "../api/popup-service";
|
||||
import type { Popup } from "./popup-interface";
|
||||
import { previewTemplate } from "./preview-template";
|
||||
|
||||
export const columns: ColumnDef<Popup>[] = [
|
||||
{
|
||||
@@ -83,17 +82,11 @@ const PreviewButton = ({ popupSn }: { popupSn: number }) => {
|
||||
const height = response.popupHgt;
|
||||
const left = response.popupHrzntltyPstn + window.screenX;
|
||||
const top = response.popupVtclPstn + window.screenY;
|
||||
const popupWindow = window.open(
|
||||
window.open(
|
||||
`/view-popup/${popupSn}`,
|
||||
"",
|
||||
`popup_preview_${popupSn}`,
|
||||
`width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes,status=no,location=no,menubar=no`,
|
||||
`width=${width},height=${height},left=${left},top=${top}`,
|
||||
);
|
||||
if (!popupWindow) return;
|
||||
|
||||
popupWindow.document.write(
|
||||
previewTemplate(popupSn, response.popupTtl, response.popupCn),
|
||||
);
|
||||
popupWindow.document.close();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
"use client";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Button, Checkbox, Label } from "@common/components";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { PopupApi } from "../api/popup-service";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { setCookie, getCookie } from "@/shared/lib/cookie";
|
||||
|
||||
function ViewPopupPage({ popupSn }: { popupSn: string }) {
|
||||
const doNotShowTodayRef = useRef(false);
|
||||
|
||||
const { data: popupInfo } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.POPUPS, popupSn],
|
||||
queryFn: () => PopupApi.getPopupInfo(parseInt(popupSn)),
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
if (doNotShowTodayRef.current) {
|
||||
const expireDate = new Date();
|
||||
expireDate.setHours(23, 59, 59, 999);
|
||||
|
||||
setCookie(`cs-popup-hide-for-day-${popupSn}`, "true", {
|
||||
expires: expireDate,
|
||||
path: "/",
|
||||
});
|
||||
}
|
||||
|
||||
window.close();
|
||||
};
|
||||
|
||||
const handleCheckboxChange = (checked: boolean) => {
|
||||
doNotShowTodayRef.current = checked;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const cookieName = `cs-popup-hide-for-day-${popupSn}`;
|
||||
const hasCookie = getCookie(cookieName);
|
||||
|
||||
if (hasCookie) {
|
||||
window.close();
|
||||
}
|
||||
}, [popupSn]);
|
||||
|
||||
return (
|
||||
<div className="h-svh bg-container p-4">
|
||||
<div className="card card-border !m-0">
|
||||
<div className="card-header">
|
||||
<h1 className="text-3xl font-bold text-foreground">
|
||||
{popupInfo?.popupTtl}
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
className="whitespace-pre-wrap p-2"
|
||||
dangerouslySetInnerHTML={{ __html: popupInfo?.popupCn ?? "" }}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2 flex items-center justify-between p-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
id="doNotShowToday"
|
||||
onCheckedChange={handleCheckboxChange}
|
||||
/>
|
||||
<Label htmlFor="doNotShowToday">오늘 하루 보지 않기</Label>
|
||||
</div>
|
||||
<Button size="sm" color="white" onClick={handleClose}>
|
||||
닫기
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { ViewPopupPage };
|
||||
@@ -1,16 +1,17 @@
|
||||
import "./header.css";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useCallback } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { images } from "@common/assets";
|
||||
import { getConfig } from "@common/config";
|
||||
import { ENDPOINT } from "../../config";
|
||||
import { setCookie } from "../../lib";
|
||||
import { setCookie, getCookie } from "../../lib";
|
||||
import {
|
||||
getPopupInfo,
|
||||
getPopupList,
|
||||
getSiteInfo,
|
||||
getUserInfo,
|
||||
type PopupInfo,
|
||||
} from "../../api";
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -48,46 +49,45 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) {
|
||||
queryFn: () => getPopupList(),
|
||||
});
|
||||
|
||||
const openPopup = useCallback(async (popup: PopupInfo) => {
|
||||
try {
|
||||
const cookieName = `cs-popup-hide-for-day-${popup.popupSn}`;
|
||||
const hasCookie = getCookie(cookieName);
|
||||
if (hasCookie) return;
|
||||
|
||||
const response = await getPopupInfo(popup.popupSn);
|
||||
if (!response) return;
|
||||
|
||||
const width = response.popupWdth || 400;
|
||||
const height = response.popupHgt || 300;
|
||||
const left = (response.popupHrzntltyPstn || 0) + window.screenX;
|
||||
const top = (response.popupVtclPstn || 0) + window.screenY;
|
||||
|
||||
const popupWindow = window.open(
|
||||
`/view-popup/${popup.popupSn}`,
|
||||
"",
|
||||
`width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes,status=no,location=no,menubar=no`,
|
||||
);
|
||||
|
||||
if (popupWindow) {
|
||||
popupWindow.focus();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to open popup ${popup.popupSn}:`, error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!popupList?.content.length) return;
|
||||
|
||||
const openPopups = async () => {
|
||||
for (const popup of popupList.content) {
|
||||
try {
|
||||
const cookieName = `cs-popup-hide-for-day-${popup.popupSn}`;
|
||||
const cookies = document.cookie.split(";");
|
||||
const popupCookie = cookies.find((cookie) =>
|
||||
cookie.trim().startsWith(`${cookieName}=`),
|
||||
);
|
||||
if (popupCookie) continue;
|
||||
const timer = setTimeout(() => {
|
||||
popupList.content.forEach((popup) => {
|
||||
openPopup(popup);
|
||||
});
|
||||
}, 500);
|
||||
|
||||
const response = await getPopupInfo(popup.popupSn);
|
||||
if (!response) continue;
|
||||
|
||||
const width = response.popupWdth;
|
||||
const height = response.popupHgt;
|
||||
const left = response.popupHrzntltyPstn + window.screenX;
|
||||
const top = response.popupVtclPstn + window.screenY;
|
||||
const popupWindow = window.open(
|
||||
"",
|
||||
`popup_preview_${popup.popupSn}`,
|
||||
`width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes,status=no,location=no,menubar=no`,
|
||||
);
|
||||
if (!popupWindow) continue;
|
||||
|
||||
popupWindow.document.write(
|
||||
previewTemplate(popup.popupSn, response.popupTtl, response.popupCn),
|
||||
);
|
||||
popupWindow.document.close();
|
||||
} catch (error) {
|
||||
console.error("Failed to open popup:", error);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
openPopups();
|
||||
}, [popupList]);
|
||||
return () => clearTimeout(timer);
|
||||
}, [popupList, openPopup]);
|
||||
|
||||
return (
|
||||
<header className="sticky top-0 z-30 box-border flex h-[5rem] w-full items-center justify-between border-b border-solid border-border bg-background">
|
||||
@@ -129,76 +129,3 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) {
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
const previewTemplate = (
|
||||
popupSn: number,
|
||||
popupTitle: string,
|
||||
popupCn: string,
|
||||
) => {
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>팝업 미리보기</title>
|
||||
<style>
|
||||
*,*::before,*::after{box-sizing:border-box}
|
||||
*{margin:0}
|
||||
@media(prefers-reduced-motion:no-preference){html{interpolate-size:allow-keywords}}
|
||||
body{line-height:1.5;-webkit-font-smoothing:antialiased}
|
||||
img,picture,video,canvas,svg{display:block;max-width:100%}
|
||||
input,button,textarea,select{font:inherit}
|
||||
p,h1,h2,h3,h4,h5,h6{overflow-wrap:break-word}
|
||||
p{text-wrap:pretty}
|
||||
h1,h2,h3,h4,h5,h6{text-wrap:balance}
|
||||
#root,#__next{isolation:isolate}
|
||||
body{margin:0;padding:0;font-family:sans-serif;padding:1rem;background:#f1f6f9}
|
||||
hr{margin:0.5rem 0 1rem;border:1px solid #eee}
|
||||
pre{font-size:1rem;line-height:1}
|
||||
.popup-container{width:100%;height:100%;overflow:auto;background:#fff;border-radius:0.5rem;padding:1rem;position:relative}
|
||||
.popup-header h1{font-size:1.2rem;font-weight:600}
|
||||
.popup-footer{display:flex;justify-content:space-between;margin-top:1rem;padding-top:1rem;border-top:1px solid #eee}
|
||||
.popup-footer .left{display:flex;align-items:center}
|
||||
.popup-footer .right{display:flex}
|
||||
.popup-footer button{padding:0.5rem 1rem;border-radius:0.25rem;border:none;cursor:pointer;font-size:0.875rem}
|
||||
.popup-footer button.close{background:#f1f6f9;color:#333}
|
||||
.popup-footer button.close:hover{background:#e3eef8}
|
||||
.popup-footer label{display:flex;align-items:center;cursor:pointer;font-size:0.875rem}
|
||||
.popup-footer input[type="checkbox"]{margin-right:0.5rem}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="popup-container">
|
||||
<div class="popup-header">
|
||||
<h1>${popupTitle || "팝업 미리보기"}</h1>
|
||||
</div>
|
||||
<hr />
|
||||
<pre>${popupCn || ""}</pre>
|
||||
<div class="popup-footer">
|
||||
<div class="left">
|
||||
<label>
|
||||
<input type="checkbox" id="doNotShowToday" />
|
||||
오늘 하루 보지 않기
|
||||
</label>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button class="close" onclick="closePopup()">닫기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function closePopup() {
|
||||
if (document.getElementById('doNotShowToday').checked) {
|
||||
const popupId = '${popupSn}';
|
||||
const expireDate = new Date();
|
||||
expireDate.setDate(expireDate.getDate() + 1);
|
||||
document.cookie = \`cs-popup-hide-for-day-\${popupId}=true; expires=\${expireDate.toUTCString()}; path=/\`;
|
||||
}
|
||||
window.close();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user