feat: 팝업 미리보기 기능

This commit is contained in:
2025-05-21 16:51:09 +09:00
parent 0539b7f1f9
commit 566b75bc9f
3 changed files with 129 additions and 5 deletions
+7 -4
View File
@@ -12,26 +12,29 @@ import {
import { client } from "@/shared/api";
export class PopupApiService implements PopupService {
async getPopupList(params: PopupParams): Promise<PopupList> {
public async getPopupList(params: PopupParams): Promise<PopupList> {
const response = await client.get<PopupList>(ENDPOINT.CMS_SERVICE.POPUPS, {
params,
});
return response.data;
}
async getPopupInfo(popupSn: number): Promise<PopupInfo> {
public async getPopupInfo(popupSn: number): Promise<PopupInfo> {
const response = await client.get<PopupInfo>(
`${ENDPOINT.CMS_SERVICE.POPUPS}/${popupSn}`,
);
return response.data;
}
async createPopup(data: PopupFormData): Promise<void> {
public async createPopup(data: PopupFormData): Promise<void> {
const validatedData = popupFormSchema.parse(data);
await client.post(ENDPOINT.CMS_SERVICE.POPUPS, validatedData);
}
async updatePopup(popupSn: number, data: PopupUpdateFormData): Promise<void> {
public async updatePopup(
popupSn: number,
data: PopupUpdateFormData,
): Promise<void> {
const validatedData = popupUpdateFormSchema.parse(data);
await client.put(
`${ENDPOINT.CMS_SERVICE.POPUPS}/${popupSn}`,
@@ -0,0 +1,72 @@
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>
`;
};
@@ -1,7 +1,9 @@
import dayjs from "dayjs";
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 { Checkbox } from "@common/components";
import { previewTemplate } from "./preview-template";
export const columns: ColumnDef<Popup>[] = [
{
@@ -62,4 +64,51 @@ export const columns: ColumnDef<Popup>[] = [
accessorKey: "mdfrNm",
header: "최종수정자",
},
{
id: "preview",
header: "미리보기",
cell: ({ row }) => <PreviewButton popupSn={row.original.popupSn} />,
},
];
const PreviewButton = ({ popupSn }: { popupSn: number }) => {
const handlePreview = async (e: React.MouseEvent) => {
e.stopPropagation();
try {
const cookieName = `cs-popup-hide-for-day-${popupSn}`;
const cookies = document.cookie.split(";");
const popupCookie = cookies.find((cookie) =>
cookie.trim().startsWith(`${cookieName}=`),
);
if (popupCookie) return;
const response = await PopupApi.getPopupInfo(popupSn);
if (!response) return;
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_${popupSn}`,
`width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes,status=no,location=no,menubar=no`,
);
if (!popupWindow) return;
popupWindow.document.write(
previewTemplate(popupSn, response.popupTtl, response.popupCn),
);
popupWindow.document.close();
} catch (error) {
console.error(error);
}
};
return (
<Button size="sm" onClick={handlePreview}>
미리보기
</Button>
);
};