88import { type EmailDocument , MailEditor , type MailEditorHandle , parseEmailDocument } from "@mu-software/mail-editor" ;
99import { Add , Close , Save , UploadFile , Visibility } from "@mui/icons-material" ;
1010import {
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