diff --git a/next.config.ts b/next.config.ts
index 68a6c64..04361fe 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -2,6 +2,18 @@ import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "standalone",
+ images: {
+ remotePatterns: [
+ {
+ protocol: "https",
+ hostname: "**",
+ },
+ {
+ protocol: "http",
+ hostname: "**",
+ },
+ ],
+ },
};
export default nextConfig;
diff --git a/src/app/admin/editor/[id]/page.tsx b/src/app/admin/editor/[id]/page.tsx
index bcaebbc..7c70818 100644
--- a/src/app/admin/editor/[id]/page.tsx
+++ b/src/app/admin/editor/[id]/page.tsx
@@ -8,6 +8,7 @@ import { Skeleton } from "@/components/ui/Skeleton";
import { MarkdownEditor } from "@/components/editor/MarkdownEditor";
import { ImageUpload } from "@/components/editor/ImageUpload";
import { PostContent } from "@/components/post/PostContent";
+import { SaveStatus } from "@/components/SaveStatus";
import { api } from "@/lib/api";
interface EditPostPageProps {
@@ -22,7 +23,7 @@ export default function EditPostEditor({ params }: EditPostPageProps) {
const [published, setPublished] = useState(false);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
- const [saved, setSaved] = useState(false);
+ const [saveStatus, setSaveStatus] = useState<"idle" | "saving" | "saved" | "error">("idle");
useEffect(() => {
async function fetchPost() {
@@ -45,15 +46,16 @@ export default function EditPostEditor({ params }: EditPostPageProps) {
if (!title.trim()) return;
setSaving(true);
+ setSaveStatus("saving");
try {
await api.updatePost(id, {
title,
content,
published,
});
- setSaved(true);
+ setSaveStatus("saved");
} catch {
- alert("Failed to save post");
+ setSaveStatus("error");
} finally {
setSaving(false);
}
@@ -65,11 +67,11 @@ export default function EditPostEditor({ params }: EditPostPageProps) {
};
useEffect(() => {
- if (saved) {
- const timer = setTimeout(() => setSaved(false), 3000);
+ if (saveStatus === "saved" || saveStatus === "error") {
+ const timer = setTimeout(() => setSaveStatus("idle"), 3000);
return () => clearTimeout(timer);
}
- }, [saved]);
+ }, [saveStatus]);
if (loading) {
return (
@@ -92,6 +94,8 @@ export default function EditPostEditor({ params }: EditPostPageProps) {
/>
+
+
@@ -112,7 +116,7 @@ export default function EditPostEditor({ params }: EditPostPageProps) {
-
+
diff --git a/src/app/admin/editor/page.tsx b/src/app/admin/editor/page.tsx
index 1959bcb..8d47057 100644
--- a/src/app/admin/editor/page.tsx
+++ b/src/app/admin/editor/page.tsx
@@ -7,6 +7,7 @@ import { Button } from "@/components/ui/Button";
import { MarkdownEditor } from "@/components/editor/MarkdownEditor";
import { ImageUpload } from "@/components/editor/ImageUpload";
import { PostContent } from "@/components/post/PostContent";
+import { SaveStatus } from "@/components/SaveStatus";
import { api } from "@/lib/api";
export default function NewPostEditor() {
@@ -15,24 +16,25 @@ export default function NewPostEditor() {
const [content, setContent] = useState("");
const [published, setPublished] = useState(false);
const [saving, setSaving] = useState(false);
- const [saved, setSaved] = useState(false);
+ const [saveStatus, setSaveStatus] = useState<"idle" | "saving" | "saved" | "error">("idle");
const handleSave = useCallback(async () => {
if (!title.trim()) return;
setSaving(true);
+ setSaveStatus("saving");
try {
const post = await api.createPost({
title,
content,
published,
});
- setSaved(true);
+ setSaveStatus("saved");
setTimeout(() => {
router.push(`/admin/editor/${post.id}`);
}, 1000);
} catch {
- alert("Failed to save post");
+ setSaveStatus("error");
} finally {
setSaving(false);
}
@@ -44,11 +46,11 @@ export default function NewPostEditor() {
};
useEffect(() => {
- if (saved) {
- const timer = setTimeout(() => setSaved(false), 3000);
+ if (saveStatus === "saved" || saveStatus === "error") {
+ const timer = setTimeout(() => setSaveStatus("idle"), 3000);
return () => clearTimeout(timer);
}
- }, [saved]);
+ }, [saveStatus]);
return (
@@ -62,6 +64,8 @@ export default function NewPostEditor() {
/>
+
+
@@ -82,7 +86,7 @@ export default function NewPostEditor() {
-
+
diff --git a/src/app/drafts/page.tsx b/src/app/drafts/page.tsx
index 6dc3061..adaa8e1 100644
--- a/src/app/drafts/page.tsx
+++ b/src/app/drafts/page.tsx
@@ -95,7 +95,6 @@ function DraftsContent() {
size="sm"
onClick={() => handlePublish(post.id)}
disabled={publishing === post.id}
- className="border-green-600 text-green-600 hover:bg-green-600 hover:text-white"
>
{publishing === post.id ? "Publishing..." : "Publish"}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 27d95dd..aa16d7f 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -31,7 +31,7 @@ export default function RootLayout({
return (
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 9e7b476..1322ad1 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -43,7 +43,7 @@ export default function HomePage() {
{error && (
- {error}
+ {error}
)}
{loading && (
diff --git a/src/app/posts/[id]/edit/page.tsx b/src/app/posts/[id]/edit/page.tsx
index 477f213..4a62b24 100644
--- a/src/app/posts/[id]/edit/page.tsx
+++ b/src/app/posts/[id]/edit/page.tsx
@@ -17,7 +17,7 @@ interface EditPostPageProps {
params: Promise<{ id: string }>;
}
-const AUTOSAVE_DELAY = 300;
+const AUTOSAVE_DELAY = 2000;
function EditPostContent({ id }: { id: string }) {
const router = useRouter();
@@ -173,7 +173,7 @@ function EditPostContent({ id }: { id: string }) {
-
+
diff --git a/src/app/posts/[id]/page.tsx b/src/app/posts/[id]/page.tsx
index d6b069d..560e6d8 100644
--- a/src/app/posts/[id]/page.tsx
+++ b/src/app/posts/[id]/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { useState, useEffect, use } from "react";
-import { useRouter } from "next/navigation";
+import { useRouter, notFound } from "next/navigation";
import Link from "next/link";
import Image from "next/image";
import { Container } from "@/components/layout/Container";
@@ -49,8 +49,7 @@ export default function PostPage({ params }: PostPageProps) {
};
if (error) {
- router.push("/not-found");
- return null;
+ notFound();
}
return (
diff --git a/src/app/register/page.tsx b/src/app/register/page.tsx
index 7adb29f..111c13a 100644
--- a/src/app/register/page.tsx
+++ b/src/app/register/page.tsx
@@ -93,14 +93,14 @@ export default function RegisterPage() {
? "border-red-500 animate-shake"
: password.length > 0
? passwordValid
- ? "border-green-500"
- : "border-yellow-500"
+ ? "border-black"
+ : "border-neutral-400"
: "border-neutral-200 focus:border-black"
}`}
/>
{password.length}/{MIN_PASSWORD_LENGTH}
diff --git a/src/components/editor/ImageUpload.tsx b/src/components/editor/ImageUpload.tsx
index 654c7f6..5c637a2 100644
--- a/src/components/editor/ImageUpload.tsx
+++ b/src/components/editor/ImageUpload.tsx
@@ -87,10 +87,10 @@ export function ImageUpload({ onUpload }: ImageUploadProps) {
onDragLeave={handleDragLeave}
onDrop={handleDrop}
className={clsx(
- "border-2 border-dashed p-8 text-center cursor-pointer transition-colors",
+ "border p-8 text-center cursor-pointer transition-colors",
isDragging && "border-black bg-neutral-50",
error && "border-red-500 animate-shake",
- !isDragging && !error && "border-neutral-300 hover:border-neutral-400"
+ !isDragging && !error && "border-neutral-200 hover:border-neutral-400"
)}
>
+
{post.cover_image && (
-
+
@@ -37,7 +37,7 @@ export function PostCard({ post, isOwner = false }: PostCardProps) {
-
+
{post.title}
{isOwner && !post.published && (
(Draft)
@@ -45,16 +45,16 @@ export function PostCard({ post, isOwner = false }: PostCardProps) {
-
+
{post.content.slice(0, 200).replace(/[#*`]/g, "")}
{post.content.length > 200 ? "..." : ""}
- Read More
+ Read More
→
diff --git a/src/components/post/PostContent.tsx b/src/components/post/PostContent.tsx
index 54fdd58..8bd8a9e 100644
--- a/src/components/post/PostContent.tsx
+++ b/src/components/post/PostContent.tsx
@@ -10,7 +10,7 @@ interface PostContentProps {
export function PostContent({ content }: PostContentProps) {
return (
-
+
),
pre: ({ children }) => (
-
+
{children}
),
diff --git a/src/components/post/PostMeta.tsx b/src/components/post/PostMeta.tsx
index 276f1ca..bd69388 100644
--- a/src/components/post/PostMeta.tsx
+++ b/src/components/post/PostMeta.tsx
@@ -14,7 +14,7 @@ export function PostMeta({ date }: PostMetaProps) {
};
return (
-