Skip to content

Commit 4f7622d

Browse files
committed
fix: json이 없는 email template에 대해 잘못된 내용으로 덮어씌워지지 않도록 수정
1 parent e141d7c commit 4f7622d

1 file changed

Lines changed: 46 additions & 1 deletion

File tree

apps/pyconkr-admin/src/components/pages/notification/email_template_editor.tsx

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
import { type EmailDocument, MailEditor, type MailEditorHandle, parseEmailDocument } from "@mu-software/mail-editor";
99
import { Add, Close, Save, UploadFile, Visibility } from "@mui/icons-material";
1010
import {
11+
Alert,
1112
Box,
1213
Button,
1314
Chip,
@@ -95,6 +96,7 @@ const InnerAdminEmailTemplateEditor: FC = ErrorBoundary.with(
9596
const [contextJson, setContextJson] = useState("{}");
9697
const [importDialogOpen, setImportDialogOpen] = useState(false);
9798
const [importJson, setImportJson] = useState("");
99+
const [overwriteDialogOpen, setOverwriteDialogOpen] = useState(false);
98100
// MailEditor는 initialDocument가 바뀌거나 서스펜스에서 재개될 때 이 문서로 되돌아가므로,
99101
// 불러오기/저장 때마다 최신 문서로 갱신해야 편집 내용이 유실되지 않습니다.
100102
const [initialDocument, setInitialDocument] = useState<EmailDocument>(() => toInitialDocument(retrievedData?.editor_source));
@@ -111,6 +113,9 @@ const InnerAdminEmailTemplateEditor: FC = ErrorBoundary.with(
111113

112114
const isPending = createMutation.isPending || updateMutation.isPending;
113115
const jsonValid = isValidJson(contextJson);
116+
// editor_source 없이 data만 있는 템플릿(마이그레이션 seed 등)은 에디터가 기본 문서를 띄우므로,
117+
// 그대로 저장하면 기존 본문 HTML이 기본 문서로 덮어씌워집니다.
118+
const isEditorSourceMissing = !!id && !retrievedData?.editor_source?.trim() && !!retrievedData?.data?.trim();
114119

115120
const handleSubmit = async () => {
116121
if (isPending) return;
@@ -159,6 +164,15 @@ const InnerAdminEmailTemplateEditor: FC = ErrorBoundary.with(
159164
}
160165
};
161166

167+
const handleSubmitClick = () => {
168+
// 제목이 비어 있으면 어차피 handleSubmit이 저장 전에 막으므로, 확인창부터 띄우지 않습니다.
169+
if (isEditorSourceMissing && editorRef.current?.exportEmailDocument().meta.subject?.trim()) {
170+
setOverwriteDialogOpen(true);
171+
return;
172+
}
173+
void handleSubmit();
174+
};
175+
162176
const handleImport = () => {
163177
try {
164178
// parseEmailDocument는 검증 실패 사유를 메시지에 담아 throw합니다.
@@ -204,6 +218,14 @@ const InnerAdminEmailTemplateEditor: FC = ErrorBoundary.with(
204218
fullWidth
205219
/>
206220

221+
{isEditorSourceMissing && (
222+
<Alert severity="warning">
223+
이 템플릿에는 에디터 원본(editor_source)이 없어, 아래 에디터에는 저장된 본문이 아니라 기본 문서가 표시되고 있습니다. 이대로 저장하면
224+
기존 본문(data)이 에디터 내용으로 덮어씌워집니다. 기존 본문을 유지하려면 저장하지 마시고, 새로 만드시려면 저장 시 확인창에서
225+
진행해주세요.
226+
</Alert>
227+
)}
228+
207229
<Box>
208230
<Stack direction="row" sx={{ justifyContent: "space-between", alignItems: "center", mb: 1 }}>
209231
<Typography variant="subtitle1">본문 에디터</Typography>
@@ -272,11 +294,34 @@ const InnerAdminEmailTemplateEditor: FC = ErrorBoundary.with(
272294
)}
273295
</Stack>
274296
<Stack direction="row" spacing={2} sx={{ justifyContent: "flex-end" }}>
275-
<Button variant="contained" color="primary" onClick={handleSubmit} disabled={isPending} startIcon={id ? <Save /> : <Add />}>
297+
<Button variant="contained" color="primary" onClick={handleSubmitClick} disabled={isPending} startIcon={id ? <Save /> : <Add />}>
276298
{id ? "수정" : "새 객체 추가"}
277299
</Button>
278300
</Stack>
279301

302+
<Dialog open={overwriteDialogOpen} onClose={() => setOverwriteDialogOpen(false)} maxWidth="sm">
303+
<DialogTitle>기존 본문을 덮어쓸까요?</DialogTitle>
304+
<DialogContent>
305+
<Typography variant="body2">
306+
이 템플릿에는 에디터 원본(editor_source)이 없어 에디터에 기본 문서가 표시되고 있습니다. 저장하면 기존 본문(data)이 지금 에디터에 있는
307+
내용으로 대체되며, 되돌릴 수 없습니다.
308+
</Typography>
309+
</DialogContent>
310+
<DialogActions>
311+
<Button onClick={() => setOverwriteDialogOpen(false)}>취소</Button>
312+
<Button
313+
variant="contained"
314+
color="warning"
315+
onClick={() => {
316+
setOverwriteDialogOpen(false);
317+
void handleSubmit();
318+
}}
319+
>
320+
덮어쓰고 저장
321+
</Button>
322+
</DialogActions>
323+
</Dialog>
324+
280325
<Dialog open={importDialogOpen} onClose={() => setImportDialogOpen(false)} fullWidth maxWidth="md">
281326
<DialogTitle>JSON으로 불러오기</DialogTitle>
282327
<DialogContent>

0 commit comments

Comments
 (0)