'use server';
export async function addPost(formData: FormData): Promise<void> {
const title = formData.get('title') as string;
const content = formData.get('content') as string;
const tag = formData.get('tag') as string;
// Simulate saving to a database
console.log(`Post added: ${title}, ${content}, Tag: ${tag}`);
}
'use server';
import { revalidatePath } from 'next/cache';
export async function addPost(formData: FormData): Promise<{
success: boolean; error?:
string
}> {
const title = formData.get('title') as string;
const content = formData.get('content') as string;
const tag = formData.get('tag') as string;
if (!title || title.length < 3) {
return { success: false, error: 'Title must be at least 3 characters' };
}
const newPost = { id: Date.now(), title, content, tag, date: new Date() };
console.log('Post added:', newPost);
revalidatePath('/posts'); // Refresh user view
return { success: true };
}
// app/admin/add-post/page.tsx
'use client';
import { useActionState } from 'react';
import { addPost } from '@/app/actions';
export default function AddPostPage() {
const [state, formAction] = useActionState(addPost, {
success: false,
error: null
});
return (
<form action={formAction}>
<input name="title" placeholder="Post Title" />
<textarea name="content" placeholder="Write your post..." />
<input name="tag" placeholder="Tag (e.g., tech)" />
{state.error && <p style={{ color: 'red' }}>{state.error}</p>}
{state.success && <p style={{ color: 'green' }}>Post added!</p>}
<button type="submit">Add Post</button>
</form>
);
}
// app/actions.ts
'use server';
export async function saveDraft(content: string): Promise<{ success: boolean }> {
console.log('Draft saved:', content);
return { success: true };
}
// app/admin/edit-post/page.tsx
'use client';
import { saveDraft } from '@/app/actions';
export default function EditPost() {
async function handleChange(e: React.ChangeEvent<HTMLTextAreaElement>) {
await saveDraft(e.target.value);
}
return (
<form>
<textarea name="content" placeholder="Draft your post..." onChange={handleChange} />
<button type="submit">Publish</button>
</form>
);
}
/ app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
export async function addPostOptimistic(formData: FormData): Promise<{
id: number;
title: string;
tag: string
}> {
const title = formData.get('title') as string;
const tag = formData.get('tag') as string;
const newPost = { id: Date.now(), title, tag };
console.log('Post added:', newPost);
revalidatePath('/posts');
return newPost;
}
// app/posts/page.tsx
'use client';
import { useOptimistic } from 'react';
import { addPostOptimistic } from '@/app/actions';
type Post = { id: number; title: string; tag: string };
export default function PostsPage({ initialPosts }: { initialPosts: Post[] }) {
const [optimisticPosts, addOptimisticPost] = useOptimistic<Post[], Post>(
initialPosts,
(state, newPost) => [...state, newPost]
);
async function handleAddPost(formData: FormData) {
const newPost = {
id: Date.now(),
title: formData.get('title') as string,
tag: formData.get('tag') as string,
};
addOptimisticPost(newPost); // Show instantly
await addPostOptimistic(formData); // Confirm with server
}
return (
<div>
<ul>
{optimisticPosts.map(post => (
<li key={post.id}>{post.title} - {post.tag}</li>
))}
</ul>
<form action={handleAddPost}>
<input name="title" placeholder="Post Title" />
<input name="tag" placeholder="Tag (e.g., tech)" />
<button type="submit">Add Post</button>
</form>
</div>
);
}