@@ -15,12 +15,14 @@ export default function EditPostPage() {
1515 const [ date , setDate ] = useState ( "" ) ;
1616 const [ markdown , setMarkdown ] = useState ( "" ) ;
1717 const [ saving , setSaving ] = useState ( false ) ;
18+ const [ publishing , setPublishing ] = useState ( false ) ;
1819 const [ message , setMessage ] = useState ( "" ) ;
1920 const [ showPreview , setShowPreview ] = useState ( false ) ;
2021 const [ uploading , setUploading ] = useState ( false ) ;
2122 const [ isDragging , setIsDragging ] = useState ( false ) ;
2223 const [ editorWidth , setEditorWidth ] = useState ( 900 ) ;
2324 const [ hasUnsavedChanges , setHasUnsavedChanges ] = useState ( false ) ;
25+ const [ isDraft , setIsDraft ] = useState ( true ) ;
2426 const [ showModal , setShowModal ] = useState ( false ) ;
2527 const [ modalConfig , setModalConfig ] = useState < {
2628 title : string ;
@@ -41,6 +43,7 @@ export default function EditPostPage() {
4143 const rawContent = `---\ntitle: ${ data . title } \ntags: ${ data . tags } \ndate: ${ data . date } \n---\n\n${ data . content } ` ;
4244 setMarkdown ( rawContent ) ;
4345 setDate ( data . date ) ;
46+ setIsDraft ( data . isDraft ?? false ) ;
4447 initialContentRef . current = { markdown : rawContent } ;
4548 } )
4649 . catch ( ( err ) => console . error ( "Error loading post:" , err ) ) ;
@@ -61,20 +64,26 @@ export default function EditPostPage() {
6164 setDate ( formattedDate ) ;
6265 const initialContent = `---\ntitle: \ntags: \ndate: ${ formattedDate } \n---\n\n` ;
6366 setMarkdown ( initialContent ) ;
64- }
6567
66- const savedDraft = localStorage . getItem ( draftKey ) ;
67- if ( savedDraft ) {
68- const draft = JSON . parse ( savedDraft ) ;
69- setModalConfig ( {
70- title : "Draft Found" ,
71- message : `Found unsaved draft from ${ new Date ( draft . timestamp ) . toLocaleString ( ) } . Restore it?` ,
72- onConfirm : ( ) => {
73- setMarkdown ( draft . markdown ) ;
74- setShowModal ( false ) ;
75- } ,
76- } ) ;
77- setShowModal ( true ) ;
68+ const savedDraft = localStorage . getItem ( draftKey ) ;
69+ if ( savedDraft ) {
70+ const draft = JSON . parse ( savedDraft ) ;
71+ // Only show the draft if it's from the same date
72+ if ( draft . date === formattedDate ) {
73+ setModalConfig ( {
74+ title : "Draft Found" ,
75+ message : `Found unsaved draft from ${ new Date ( draft . timestamp ) . toLocaleString ( ) } . Restore it?` ,
76+ onConfirm : ( ) => {
77+ setMarkdown ( draft . markdown ) ;
78+ setShowModal ( false ) ;
79+ } ,
80+ } ) ;
81+ setShowModal ( true ) ;
82+ } else {
83+ // Remove outdated draft from different date
84+ localStorage . removeItem ( draftKey ) ;
85+ }
86+ }
7887 }
7988 }
8089 } , [ slug , isNew , searchParams ] ) ;
@@ -136,6 +145,7 @@ export default function EditPostPage() {
136145 setMessage ( "✓ Saved!" ) ;
137146 setTimeout ( ( ) => setMessage ( "" ) , 3000 ) ;
138147 setHasUnsavedChanges ( false ) ;
148+ setIsDraft ( data . isDraft ?? isDraft ) ;
139149
140150 const draftKey = `draft-${ slugToUse } ` ;
141151 localStorage . removeItem ( draftKey ) ;
@@ -152,7 +162,76 @@ export default function EditPostPage() {
152162 } finally {
153163 setSaving ( false ) ;
154164 }
155- } , [ slug , isNew , searchParams , date , markdown , router ] ) ;
165+ } , [ slug , isNew , searchParams , date , markdown , router , isDraft ] ) ;
166+
167+ const handlePublish = useCallback ( async ( ) => {
168+ // Save first if there are unsaved changes
169+ if ( hasUnsavedChanges ) {
170+ await handleSave ( ) ;
171+ }
172+
173+ setPublishing ( true ) ;
174+ setMessage ( "" ) ;
175+
176+ try {
177+ let slugToUse = slug ;
178+ if ( isNew ) {
179+ const dateParam = searchParams . get ( "date" ) ;
180+ if ( dateParam ) {
181+ const [ year , month , day ] = dateParam . split ( "-" ) ;
182+ const yy = year . substring ( 2 ) ;
183+ slugToUse = `${ day } ${ month } ${ yy } ` ;
184+ }
185+ }
186+
187+ const response = await fetch ( "/api/admin/publish-post" , {
188+ method : "POST" ,
189+ headers : { "Content-Type" : "application/json" } ,
190+ body : JSON . stringify ( { slug : slugToUse } ) ,
191+ } ) ;
192+
193+ const data = await response . json ( ) ;
194+
195+ if ( response . ok ) {
196+ setMessage ( "✓ Published!" ) ;
197+ setTimeout ( ( ) => setMessage ( "" ) , 3000 ) ;
198+ setIsDraft ( false ) ;
199+ } else {
200+ setMessage ( `✗ Error: ${ data . error } ` ) ;
201+ }
202+ } catch ( error ) {
203+ setMessage ( `✗ Error: ${ error } ` ) ;
204+ } finally {
205+ setPublishing ( false ) ;
206+ }
207+ } , [ slug , isNew , searchParams , hasUnsavedChanges , handleSave ] ) ;
208+
209+ const handleUnpublish = useCallback ( async ( ) => {
210+ setPublishing ( true ) ;
211+ setMessage ( "" ) ;
212+
213+ try {
214+ const response = await fetch ( "/api/admin/unpublish-post" , {
215+ method : "POST" ,
216+ headers : { "Content-Type" : "application/json" } ,
217+ body : JSON . stringify ( { slug } ) ,
218+ } ) ;
219+
220+ const data = await response . json ( ) ;
221+
222+ if ( response . ok ) {
223+ setMessage ( "✓ Moved to drafts" ) ;
224+ setTimeout ( ( ) => setMessage ( "" ) , 3000 ) ;
225+ setIsDraft ( true ) ;
226+ } else {
227+ setMessage ( `✗ Error: ${ data . error } ` ) ;
228+ }
229+ } catch ( error ) {
230+ setMessage ( `✗ Error: ${ error } ` ) ;
231+ } finally {
232+ setPublishing ( false ) ;
233+ }
234+ } , [ slug ] ) ;
156235
157236 useEffect ( ( ) => {
158237 const hasChanged = markdown !== initialContentRef . current . markdown ;
@@ -163,10 +242,11 @@ export default function EditPostPage() {
163242 const draft = {
164243 markdown,
165244 timestamp : Date . now ( ) ,
245+ date,
166246 } ;
167247 localStorage . setItem ( draftKey , JSON . stringify ( draft ) ) ;
168248 }
169- } , [ markdown , slug ] ) ;
249+ } , [ markdown , slug , date ] ) ;
170250
171251 useEffect ( ( ) => {
172252 const handleBeforeUnload = ( e : BeforeUnloadEvent ) => {
@@ -348,7 +428,11 @@ export default function EditPostPage() {
348428 < div className = "border-b border-japanese-shiraumenezu dark:border-gray-700 p-4 flex justify-between items-center" >
349429 < div className = "flex items-center gap-6" >
350430 < Link
351- href = "/admin"
431+ href = {
432+ searchParams . get ( "month" )
433+ ? `/admin?month=${ searchParams . get ( "month" ) } `
434+ : "/admin"
435+ }
352436 className = "text-sm text-japanese-sumiiro dark:text-japanese-shironezu hover:text-japanese-ginnezu transition-colors"
353437 >
354438 ← Back
@@ -361,6 +445,11 @@ export default function EditPostPage() {
361445 </ div >
362446
363447 < div className = "flex gap-4 items-center" >
448+ { isDraft && ! isNew && (
449+ < span className = "text-xs px-2 py-0.5 border border-dashed border-blue-400 text-blue-600 dark:text-blue-400" >
450+ Draft
451+ </ span >
452+ ) }
364453 { hasUnsavedChanges && ! saving && ! message && (
365454 < span className = "text-xs text-japanese-ginnezu dark:text-gray-400" >
366455 Unsaved
@@ -391,6 +480,24 @@ export default function EditPostPage() {
391480 >
392481 { saving ? "Saving..." : "Save" }
393482 </ button >
483+ { isDraft && (
484+ < button
485+ onClick = { handlePublish }
486+ disabled = { publishing || isNew }
487+ className = "px-4 py-1.5 text-sm border border-green-600 text-green-600 hover:bg-green-600 hover:text-white disabled:opacity-30 disabled:hover:bg-transparent disabled:hover:text-green-600 transition-colors"
488+ >
489+ { publishing ? "Publishing..." : "Publish" }
490+ </ button >
491+ ) }
492+ { ! isDraft && ! isNew && (
493+ < button
494+ onClick = { handleUnpublish }
495+ disabled = { publishing }
496+ className = "px-4 py-1.5 text-sm border border-orange-500 text-orange-500 hover:bg-orange-500 hover:text-white disabled:opacity-30 disabled:hover:bg-transparent disabled:hover:text-orange-500 transition-colors"
497+ >
498+ { publishing ? "Moving..." : "Unpublish" }
499+ </ button >
500+ ) }
394501 </ div >
395502 </ div >
396503
0 commit comments