Skip to content

Commit 0ae51e3

Browse files
committed
admin apge upates
1 parent cca92f2 commit 0ae51e3

File tree

6 files changed

+282
-29
lines changed

6 files changed

+282
-29
lines changed

app/admin/edit/[slug]/page.tsx

Lines changed: 123 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -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

app/api/admin/get-post/route.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,24 @@ export async function GET(request: NextRequest) {
1212
}
1313

1414
try {
15-
const filePath = path.join(process.cwd(), "posts", `${slug}.md`);
15+
const postsDir = path.join(process.cwd(), "posts");
16+
const draftsDir = path.join(process.cwd(), "posts", "drafts");
17+
const publishedPath = path.join(postsDir, `${slug}.md`);
18+
const draftPath = path.join(draftsDir, `${slug}.md`);
19+
20+
// Check published first, then drafts
21+
let filePath: string;
22+
let isDraft = false;
23+
24+
if (fs.existsSync(publishedPath)) {
25+
filePath = publishedPath;
26+
} else if (fs.existsSync(draftPath)) {
27+
filePath = draftPath;
28+
isDraft = true;
29+
} else {
30+
return NextResponse.json({ error: "Post not found" }, { status: 404 });
31+
}
32+
1633
const fileContent = fs.readFileSync(filePath, "utf8");
1734
const { data, content } = matter(fileContent);
1835

@@ -21,6 +38,7 @@ export async function GET(request: NextRequest) {
2138
tags: data.tags || "",
2239
date: data.date || "",
2340
content,
41+
isDraft,
2442
});
2543
} catch (error) {
2644
return NextResponse.json({ error: "Post not found" }, { status: 404 });
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import fs from "fs";
2+
import path from "path";
3+
import { NextRequest, NextResponse } from "next/server";
4+
5+
export async function POST(request: NextRequest) {
6+
try {
7+
const { slug } = await request.json();
8+
9+
if (!slug) {
10+
return NextResponse.json({ error: "Slug required" }, { status: 400 });
11+
}
12+
13+
const postsDir = path.join(process.cwd(), "posts");
14+
const draftsDir = path.join(process.cwd(), "posts", "drafts");
15+
const publishedPath = path.join(postsDir, `${slug}.md`);
16+
const draftPath = path.join(draftsDir, `${slug}.md`);
17+
18+
// Check if already published
19+
if (fs.existsSync(publishedPath)) {
20+
return NextResponse.json(
21+
{ error: "Post is already published" },
22+
{ status: 400 }
23+
);
24+
}
25+
26+
// Check if draft exists
27+
if (!fs.existsSync(draftPath)) {
28+
return NextResponse.json({ error: "Draft not found" }, { status: 404 });
29+
}
30+
31+
// Move from drafts to posts
32+
fs.renameSync(draftPath, publishedPath);
33+
34+
return NextResponse.json({ success: true, slug });
35+
} catch (error) {
36+
console.error("Publish error:", error);
37+
return NextResponse.json(
38+
{ error: "Failed to publish post" },
39+
{ status: 500 }
40+
);
41+
}
42+
}

app/api/admin/save-post/route.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,34 @@ export async function POST(request: NextRequest) {
1313
);
1414
}
1515

16-
const filePath = path.join(process.cwd(), "posts", `${slug}.md`);
16+
const postsDir = path.join(process.cwd(), "posts");
17+
const draftsDir = path.join(process.cwd(), "posts", "drafts");
18+
const publishedPath = path.join(postsDir, `${slug}.md`);
19+
const draftPath = path.join(draftsDir, `${slug}.md`);
20+
21+
// Check if post already exists (in either location) when creating new
22+
if (isNew) {
23+
if (fs.existsSync(publishedPath)) {
24+
return NextResponse.json(
25+
{ error: "A post already exists for this date" },
26+
{ status: 409 }
27+
);
28+
}
29+
if (fs.existsSync(draftPath)) {
30+
return NextResponse.json(
31+
{ error: "A draft already exists for this date" },
32+
{ status: 409 }
33+
);
34+
}
35+
}
1736

18-
if (isNew && fs.existsSync(filePath)) {
19-
return NextResponse.json(
20-
{ error: "A post already exists for this date" },
21-
{ status: 409 }
22-
);
37+
// Determine where to save: if already published, keep it published; otherwise save as draft
38+
const isPublished = fs.existsSync(publishedPath);
39+
const filePath = isPublished ? publishedPath : draftPath;
40+
41+
// Ensure drafts directory exists
42+
if (!isPublished && !fs.existsSync(draftsDir)) {
43+
fs.mkdirSync(draftsDir, { recursive: true });
2344
}
2445

2546
const frontmatter = `---
@@ -32,7 +53,7 @@ ${content}`;
3253

3354
fs.writeFileSync(filePath, frontmatter, "utf8");
3455

35-
return NextResponse.json({ success: true, slug });
56+
return NextResponse.json({ success: true, slug, isDraft: !isPublished });
3657
} catch (error) {
3758
console.error("Save error:", error);
3859
return NextResponse.json({ error: "Failed to save post" }, { status: 500 });
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import fs from "fs";
2+
import path from "path";
3+
import { NextRequest, NextResponse } from "next/server";
4+
5+
export async function POST(request: NextRequest) {
6+
try {
7+
const { slug } = await request.json();
8+
9+
if (!slug) {
10+
return NextResponse.json({ error: "Slug required" }, { status: 400 });
11+
}
12+
13+
const postsDir = path.join(process.cwd(), "posts");
14+
const draftsDir = path.join(process.cwd(), "posts", "drafts");
15+
const publishedPath = path.join(postsDir, `${slug}.md`);
16+
const draftPath = path.join(draftsDir, `${slug}.md`);
17+
18+
// Check if published post exists
19+
if (!fs.existsSync(publishedPath)) {
20+
return NextResponse.json(
21+
{ error: "Published post not found" },
22+
{ status: 404 }
23+
);
24+
}
25+
26+
// Ensure drafts directory exists
27+
if (!fs.existsSync(draftsDir)) {
28+
fs.mkdirSync(draftsDir, { recursive: true });
29+
}
30+
31+
// Move from posts to drafts
32+
fs.renameSync(publishedPath, draftPath);
33+
34+
return NextResponse.json({ success: true, slug });
35+
} catch (error) {
36+
console.error("Unpublish error:", error);
37+
return NextResponse.json(
38+
{ error: "Failed to unpublish post" },
39+
{ status: 500 }
40+
);
41+
}
42+
}

0 commit comments

Comments
 (0)