feat: implement EditPost page with functionality to update existing blog posts
All checks were successful
CI - Build and Push / Build and Push Docker Image (push) Successful in 17s

This commit is contained in:
2025-10-26 18:21:45 +08:00
parent 085e48ff69
commit 1f6bb77691
8 changed files with 566 additions and 56 deletions

View File

@@ -1,4 +1,5 @@
import { LexicalComposer } from '@lexical/react/LexicalComposer'; import { LexicalComposer } from '@lexical/react/LexicalComposer';
import type { InitialConfigType } from '@lexical/react/LexicalComposer';
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'; import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
import { ContentEditable } from '@lexical/react/LexicalContentEditable'; import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin'; import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
@@ -15,7 +16,7 @@ import { LinkNode, AutoLinkNode } from '@lexical/link';
import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin'; import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
import { AutoLinkPlugin } from '@lexical/react/LexicalAutoLinkPlugin'; import { AutoLinkPlugin } from '@lexical/react/LexicalAutoLinkPlugin';
import { HashtagNode } from '@lexical/hashtag'; import { HashtagNode } from '@lexical/hashtag';
import { forwardRef, useImperativeHandle, useEffect } from 'react'; import { forwardRef, useImperativeHandle, useEffect, useRef } from 'react';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { ImageNode } from './nodes/ImageNode'; import { ImageNode } from './nodes/ImageNode';
@@ -66,7 +67,7 @@ const MATCHERS = [
}, },
]; ];
const editorConfig = { const editorConfig: InitialConfigType = {
namespace: 'BlogEditor', namespace: 'BlogEditor',
theme: editorTheme, theme: editorTheme,
onError(error: Error) { onError(error: Error) {
@@ -95,6 +96,10 @@ export interface BlogEditorRef {
getEditorState: () => string; getEditorState: () => string;
} }
interface BlogEditorProps {
initialContent?: string;
}
// Plugin to expose editor instance via ref // Plugin to expose editor instance via ref
function EditorRefPlugin({ editorRef }: { editorRef: React.Ref<BlogEditorRef> }) { function EditorRefPlugin({ editorRef }: { editorRef: React.Ref<BlogEditorRef> }) {
const [editor] = useLexicalComposerContext(); const [editor] = useLexicalComposerContext();
@@ -108,7 +113,30 @@ function EditorRefPlugin({ editorRef }: { editorRef: React.Ref<BlogEditorRef> })
return null; return null;
} }
const BlogEditor = forwardRef<BlogEditorRef>((_, ref) => { function InitialContentPlugin({ initialContent }: { initialContent?: string }) {
const [editor] = useLexicalComposerContext();
const appliedContent = useRef<string | undefined>(undefined);
useEffect(() => {
if (initialContent === undefined || initialContent === appliedContent.current) {
return;
}
try {
const editorState = initialContent
? editor.parseEditorState(initialContent)
: editor.parseEditorState('{"root":{"children":[],"direction":null,"format":"","indent":0,"type":"root","version":1}}');
editor.setEditorState(editorState);
appliedContent.current = initialContent;
} catch (error) {
console.error('Failed to set initial editor content:', error);
}
}, [editor, initialContent]);
return null;
}
const BlogEditor = forwardRef<BlogEditorRef, BlogEditorProps>(({ initialContent }, ref) => {
const { toasts, removeToast, success, error } = useToast(); const { toasts, removeToast, success, error } = useToast();
// Setup toast handler for drag/drop plugin // Setup toast handler for drag/drop plugin
@@ -150,6 +178,7 @@ const BlogEditor = forwardRef<BlogEditorRef>((_, ref) => {
<MentionsPlugin /> <MentionsPlugin />
<MarkdownPlugin /> <MarkdownPlugin />
<EditorRefPlugin editorRef={ref} /> <EditorRefPlugin editorRef={ref} />
<InitialContentPlugin initialContent={initialContent} />
</div> </div>
</div> </div>
</LexicalComposer> </LexicalComposer>

View File

@@ -11,6 +11,8 @@ import type {
ListPostsResponse, ListPostsResponse,
ListTagsResponse, ListTagsResponse,
GetPostResponse, GetPostResponse,
UpdatePostPayload,
UpdatePostResponse,
} from './types'; } from './types';
const API_BASE = '/api/blog'; const API_BASE = '/api/blog';
@@ -113,6 +115,19 @@ export async function createBlogPost(
return response.json(); return response.json();
} }
/**
* Update an existing blog post
*/
export async function updateBlogPost(payload: UpdatePostPayload): Promise<UpdatePostResponse> {
const response = await apiPost(`${API_BASE}/post/update`, payload);
if (!response.ok) {
throw new Error(`Failed to update post: ${response.statusText}`);
}
return response.json();
}
/** /**
* Fetch a paginated list of blog posts * Fetch a paginated list of blog posts
*/ */

View File

@@ -31,6 +31,7 @@ export interface BlogPostSummary {
postId: string; postId: string;
title: string; title: string;
coverImageUrl?: string; coverImageUrl?: string;
coverImageKey?: string;
createdAt: number; createdAt: number;
updatedAt: number; updatedAt: number;
} }
@@ -39,6 +40,17 @@ export interface BlogPost extends BlogPostSummary {
content: string; content: string;
} }
export interface UpdatePostPayload {
postId: string;
title: string;
content: string;
coverImageKey?: string;
}
export interface UpdatePostResponse {
postId: string;
}
export interface ListPostsRequest { export interface ListPostsRequest {
page: number; page: number;
pageSize: number; pageSize: number;

View File

@@ -17,6 +17,7 @@ import Forum from './pages/Forum.tsx'
import AuthCallback from './pages/AuthCallback.tsx' import AuthCallback from './pages/AuthCallback.tsx'
import EditorDemo from './pages/EditorDemo.tsx' import EditorDemo from './pages/EditorDemo.tsx'
import BlogPost from './pages/BlogPost.tsx' import BlogPost from './pages/BlogPost.tsx'
import EditPost from './pages/EditPost.tsx'
createRoot(document.getElementById('root')!).render( createRoot(document.getElementById('root')!).render(
<StrictMode> <StrictMode>
@@ -31,6 +32,7 @@ createRoot(document.getElementById('root')!).render(
<Route path="/friends" element={<Friends />} /> <Route path="/friends" element={<Friends />} />
<Route path="/blog" element={<Blog />} /> <Route path="/blog" element={<Blog />} />
<Route path="/blog/:postId" element={<BlogPost />} /> <Route path="/blog/:postId" element={<BlogPost />} />
<Route path="/blog/:postId/edit" element={<EditPost />} />
<Route path="/blog/create" element={<CreatePost />} /> <Route path="/blog/create" element={<CreatePost />} />
<Route path="/servers" element={<Servers />} /> <Route path="/servers" element={<Servers />} />
<Route path="/forum" element={<Forum />} /> <Route path="/forum" element={<Forum />} />

View File

@@ -162,31 +162,6 @@ function Blog() {
{t('blog.subtitle')} {t('blog.subtitle')}
</p> </p>
<div style={{ display: 'flex', justifyContent: 'center', gap: '1rem', flexWrap: 'wrap' }}> <div style={{ display: 'flex', justifyContent: 'center', gap: '1rem', flexWrap: 'wrap' }}>
<Link
to="/blog/create"
style={{
display: 'inline-flex',
alignItems: 'center',
gap: '0.5rem',
background: 'var(--accent-color)',
color: 'white',
padding: '0.9rem 1.8rem',
borderRadius: '999px',
fontWeight: 600,
textDecoration: 'none',
transition: 'transform 0.3s ease, box-shadow 0.3s ease',
}}
onMouseEnter={(event) => {
event.currentTarget.style.transform = 'translateY(-2px)';
event.currentTarget.style.boxShadow = '0 6px 18px var(--accent-shadow)';
}}
onMouseLeave={(event) => {
event.currentTarget.style.transform = 'translateY(0)';
event.currentTarget.style.boxShadow = 'none';
}}
>
{t('blog.readMore')}
</Link>
{selectedTags.length > 0 && ( {selectedTags.length > 0 && (
<button <button
type="button" type="button"

View File

@@ -4,6 +4,7 @@ import Layout from '../components/Layout';
import BlogContentViewer from '../blog/BlogContentViewer'; import BlogContentViewer from '../blog/BlogContentViewer';
import { getBlogPost } from '../blog/api'; import { getBlogPost } from '../blog/api';
import type { BlogPost as BlogPostType } from '../blog/types'; import type { BlogPost as BlogPostType } from '../blog/types';
import { useAuth } from '../contexts/AuthContext';
import '../App.css'; import '../App.css';
function formatDate(timestamp: number | undefined): string { function formatDate(timestamp: number | undefined): string {
@@ -41,6 +42,7 @@ function LoadingSkeleton() {
export default function BlogPost() { export default function BlogPost() {
const { postId } = useParams<{ postId: string }>(); const { postId } = useParams<{ postId: string }>();
const navigate = useNavigate(); const navigate = useNavigate();
const { isAuthenticated } = useAuth();
const [post, setPost] = useState<BlogPostType | null>(null); const [post, setPost] = useState<BlogPostType | null>(null);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
@@ -124,33 +126,66 @@ export default function BlogPost() {
}} }}
/> />
<div style={{ position: 'relative', zIndex: 2, maxWidth: '900px', margin: '0 auto' }}> <div style={{ position: 'relative', zIndex: 2, maxWidth: '900px', margin: '0 auto' }}>
<div style={{ marginBottom: '2rem', display: 'flex', justifyContent: 'center' }}> <div style={{ marginBottom: '2rem', position: 'relative', minHeight: '52px' }}>
<button <div style={{ display: 'flex', justifyContent: 'center' }}>
type="button" <button
onClick={handleBack} type="button"
style={{ onClick={handleBack}
display: 'inline-flex', style={{
alignItems: 'center', display: 'inline-flex',
gap: '0.5rem', alignItems: 'center',
padding: '0.7rem 1.4rem', gap: '0.5rem',
borderRadius: '999px', padding: '0.7rem 1.4rem',
border: '1px solid rgba(255, 255, 255, 0.35)', borderRadius: '999px',
background: 'rgba(0, 0, 0, 0.25)', border: '1px solid rgba(255, 255, 255, 0.35)',
color: '#fff', background: 'rgba(0, 0, 0, 0.25)',
fontWeight: 600, color: '#fff',
cursor: 'pointer', fontWeight: 600,
transition: 'all 0.2s ease', cursor: 'pointer',
backdropFilter: 'blur(6px)', transition: 'all 0.2s ease',
}} backdropFilter: 'blur(6px)',
onMouseEnter={(event) => { }}
event.currentTarget.style.background = 'rgba(0, 0, 0, 0.45)'; onMouseEnter={(event) => {
}} event.currentTarget.style.background = 'rgba(0, 0, 0, 0.45)';
onMouseLeave={(event) => { }}
event.currentTarget.style.background = 'rgba(0, 0, 0, 0.25)'; onMouseLeave={(event) => {
}} event.currentTarget.style.background = 'rgba(0, 0, 0, 0.25)';
> }}
{'< Back to blog'} >
</button> {'< Back to blog'}
</button>
</div>
{isAuthenticated && post && postId && (
<button
type="button"
onClick={() => navigate(`/blog/${postId}/edit`)}
style={{
position: 'absolute',
right: 0,
top: 0,
display: 'inline-flex',
alignItems: 'center',
gap: '0.4rem',
padding: '0.6rem 1.2rem',
borderRadius: '999px',
border: '1px solid rgba(255, 255, 255, 0.35)',
background: 'rgba(48, 63, 159, 0.45)',
color: '#fff',
fontWeight: 600,
cursor: 'pointer',
transition: 'all 0.2s ease',
backdropFilter: 'blur(6px)',
}}
onMouseEnter={(event) => {
event.currentTarget.style.background = 'rgba(48, 63, 159, 0.7)';
}}
onMouseLeave={(event) => {
event.currentTarget.style.background = 'rgba(48, 63, 159, 0.45)';
}}
>
Edit post
</button>
)}
</div> </div>
<h1 <h1
style={{ style={{

View File

@@ -1,16 +1,18 @@
import { useState, useRef } from 'react'; import { useState, useRef } from 'react';
import { useNavigate } from 'react-router-dom'; import { Navigate, useNavigate } from 'react-router-dom';
import Layout from '../components/Layout'; import Layout from '../components/Layout';
import BlogEditor, { type BlogEditorRef } from '../blog/BlogEditor'; import BlogEditor, { type BlogEditorRef } from '../blog/BlogEditor';
import { uploadImage, createBlogPost } from '../blog/api'; import { uploadImage, createBlogPost } from '../blog/api';
import { Toast } from '../components/Toast'; import { Toast } from '../components/Toast';
import { useToast } from '../hooks/useToast'; import { useToast } from '../hooks/useToast';
import { useAuth } from '../contexts/AuthContext';
import '../App.css'; import '../App.css';
function CreatePost() { function CreatePost() {
const navigate = useNavigate(); const navigate = useNavigate();
const editorRef = useRef<BlogEditorRef>(null); const editorRef = useRef<BlogEditorRef>(null);
const { toasts, removeToast, success, error } = useToast(); const { toasts, removeToast, success, error } = useToast();
const { isAuthenticated } = useAuth();
const [title, setTitle] = useState(''); const [title, setTitle] = useState('');
const [coverImage, setCoverImage] = useState<string | null>(null); const [coverImage, setCoverImage] = useState<string | null>(null);
const [coverImageKey, setCoverImageKey] = useState<string>(''); const [coverImageKey, setCoverImageKey] = useState<string>('');
@@ -99,6 +101,10 @@ function CreatePost() {
} }
}; };
if (!isAuthenticated) {
return <Navigate to="/blog" replace />;
}
return ( return (
<Layout currentPage="blog"> <Layout currentPage="blog">
<Toast toasts={toasts} onRemove={removeToast} /> <Toast toasts={toasts} onRemove={removeToast} />

436
src/pages/EditPost.tsx Normal file
View File

@@ -0,0 +1,436 @@
import { useEffect, useRef, useState } from 'react';
import { Navigate, useNavigate, useParams } from 'react-router-dom';
import Layout from '../components/Layout';
import BlogEditor, { type BlogEditorRef } from '../blog/BlogEditor';
import { getBlogPost, updateBlogPost, uploadImage } from '../blog/api';
import { Toast } from '../components/Toast';
import { useToast } from '../hooks/useToast';
import { useAuth } from '../contexts/AuthContext';
import '../App.css';
function EditPost() {
const { postId } = useParams<{ postId: string }>();
const navigate = useNavigate();
const editorRef = useRef<BlogEditorRef>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const { toasts, removeToast, success, error } = useToast();
const { isAuthenticated } = useAuth();
const [isLoading, setIsLoading] = useState(true);
const [loadError, setLoadError] = useState<string | null>(null);
const [title, setTitle] = useState('');
const [initialContent, setInitialContent] = useState<string | undefined>(undefined);
const [coverImage, setCoverImage] = useState<string | null>(null);
const [coverImageKey, setCoverImageKey] = useState<string>('');
const [uploadProgress, setUploadProgress] = useState(0);
const [isUploading, setIsUploading] = useState(false);
const [uploadError, setUploadError] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
useEffect(() => {
if (!postId) {
setLoadError('Post not found.');
setIsLoading(false);
return;
}
(async () => {
try {
const fetched = await getBlogPost(postId);
setTitle(fetched.title);
setInitialContent(fetched.content);
setCoverImage(fetched.coverImageUrl ?? null);
setCoverImageKey(fetched.coverImageKey ?? '');
} catch (err) {
const message = err instanceof Error ? err.message : 'Failed to load post.';
setLoadError(message);
console.error('Failed to load blog post for editing:', err);
} finally {
setIsLoading(false);
}
})();
}, [postId]);
const handleCoverImageChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) {
return;
}
if (!file.type.startsWith('image/')) {
setUploadError('Please select an image file');
return;
}
const reader = new FileReader();
reader.onload = (e) => {
setCoverImage(e.target?.result as string);
};
reader.readAsDataURL(file);
setIsUploading(true);
setUploadError('');
setUploadProgress(0);
try {
const { fileKey, url } = await uploadImage(file, (progress) => {
setUploadProgress(progress);
});
setCoverImageKey(fileKey);
setCoverImage(url);
setUploadProgress(100);
success('Cover image uploaded successfully!', 2000);
} catch (err) {
const message = err instanceof Error ? err.message : 'Upload failed';
setUploadError(message);
error(`Cover image upload failed: ${message}`);
} finally {
setIsUploading(false);
}
};
const handleSubmit = async () => {
if (!postId) {
error('Invalid post identifier.');
return;
}
if (!title.trim()) {
error('Please enter a title');
return;
}
if (!editorRef.current) {
error('Editor not initialized');
return;
}
const content = editorRef.current.getEditorState();
if (!coverImageKey) {
error('Please upload a cover image');
return;
}
setIsSubmitting(true);
try {
await updateBlogPost({
postId,
title: title.trim(),
content,
coverImageKey,
});
success('Blog post updated successfully!', 2000);
setTimeout(() => {
navigate(`/blog/${postId}`);
}, 1500);
} catch (err) {
const message = err instanceof Error ? err.message : 'Failed to update blog post';
error(`Failed to update post: ${message}`);
} finally {
setIsSubmitting(false);
}
};
if (!isAuthenticated) {
return <Navigate to="/blog" replace />;
}
return (
<Layout currentPage="blog">
<Toast toasts={toasts} onRemove={removeToast} />
<div
style={{
maxWidth: '900px',
margin: '0 auto',
padding: '120px 2rem 80px',
}}
>
{isLoading ? (
<div style={{ color: 'var(--text-secondary)' }}>Loading post...</div>
) : loadError ? (
<div
style={{
padding: '2rem',
borderRadius: '12px',
border: '1px solid rgba(244, 67, 54, 0.3)',
background: 'rgba(244, 67, 54, 0.1)',
color: '#F44336',
}}
>
{loadError}
</div>
) : (
<>
<div style={{ marginBottom: '3rem' }}>
<h1
style={{
fontSize: '2.5rem',
fontWeight: 'bold',
color: 'var(--text-primary)',
marginBottom: '1rem',
}}
>
Edit Post
</h1>
<p style={{ fontSize: '1.1rem', color: 'var(--text-secondary)' }}>
Update your article content and cover image
</p>
</div>
<div style={{ marginBottom: '2rem' }}>
<label
style={{
display: 'block',
fontSize: '1rem',
fontWeight: 600,
color: 'var(--text-primary)',
marginBottom: '0.5rem',
}}
>
Title *
</label>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Enter your blog post title..."
style={{
width: '100%',
padding: '1rem',
fontSize: '1.2rem',
border: '2px solid var(--border-color)',
borderRadius: '8px',
background: 'var(--bg-card)',
color: 'var(--text-primary)',
outline: 'none',
transition: 'border-color 0.3s ease',
}}
onFocus={(e) => {
e.currentTarget.style.borderColor = 'var(--accent-color)';
}}
onBlur={(e) => {
e.currentTarget.style.borderColor = 'var(--border-color)';
}}
/>
</div>
<div style={{ marginBottom: '2rem' }}>
<label
style={{
display: 'block',
fontSize: '1rem',
fontWeight: 600,
color: 'var(--text-primary)',
marginBottom: '0.5rem',
}}
>
Cover Image *
</label>
<div
style={{
border: '2px dashed var(--border-color)',
borderRadius: '8px',
padding: '2rem',
textAlign: 'center',
background: 'var(--bg-card)',
cursor: 'pointer',
transition: 'border-color 0.3s ease',
}}
onClick={() => fileInputRef.current?.click()}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = 'var(--accent-color)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = 'var(--border-color)';
}}
>
{coverImage ? (
<div style={{ position: 'relative' }}>
<img
src={coverImage}
alt="Cover"
style={{
maxWidth: '100%',
maxHeight: '400px',
borderRadius: '8px',
}}
/>
{isUploading && (
<div
style={{
position: 'absolute',
bottom: '10px',
left: '50%',
transform: 'translateX(-50%)',
background: 'rgba(0, 0, 0, 0.8)',
padding: '10px 20px',
borderRadius: '8px',
color: 'white',
minWidth: '200px',
}}
>
<div
style={{
background: 'rgba(255, 255, 255, 0.2)',
borderRadius: '4px',
overflow: 'hidden',
height: '6px',
marginBottom: '5px',
}}
>
<div
style={{
background: 'var(--accent-color, #4CAF50)',
height: '100%',
width: `${uploadProgress}%`,
transition: 'width 0.3s ease',
}}
/>
</div>
<div style={{ fontSize: '14px' }}>
Uploading... {Math.round(uploadProgress)}%
</div>
</div>
)}
</div>
) : (
<div>
<div style={{ fontSize: '3rem', marginBottom: '1rem' }}>📷</div>
<p style={{ color: 'var(--text-secondary)', fontSize: '1rem' }}>
Click to upload cover image
</p>
<p
style={{
color: 'var(--text-secondary)',
fontSize: '0.9rem',
marginTop: '0.5rem',
}}
>
PNG, JPG, GIF up to 10MB
</p>
</div>
)}
</div>
{uploadError && (
<div
style={{
marginTop: '1rem',
padding: '1rem',
background: 'rgba(244, 67, 54, 0.1)',
border: '1px solid rgba(244, 67, 54, 0.3)',
borderRadius: '8px',
color: '#F44336',
}}
>
{uploadError}
</div>
)}
<input
ref={fileInputRef}
type="file"
accept="image/*"
onChange={handleCoverImageChange}
style={{ display: 'none' }}
/>
</div>
<div style={{ marginBottom: '2rem' }}>
<label
style={{
display: 'block',
fontSize: '1rem',
fontWeight: 600,
color: 'var(--text-primary)',
marginBottom: '0.5rem',
}}
>
Content *
</label>
<div
style={{
border: '2px solid var(--border-color)',
borderRadius: '8px',
overflow: 'hidden',
background: 'var(--bg-card)',
}}
>
<BlogEditor ref={editorRef} initialContent={initialContent} />
</div>
</div>
<div
style={{
display: 'flex',
gap: '1rem',
justifyContent: 'flex-end',
}}
>
<button
onClick={() => navigate(`/blog/${postId}`)}
style={{
padding: '1rem 2rem',
fontSize: '1rem',
fontWeight: 600,
border: '2px solid var(--border-color)',
borderRadius: '8px',
background: 'transparent',
color: 'var(--text-primary)',
cursor: 'pointer',
transition: 'all 0.3s ease',
}}
onMouseEnter={(e) => {
e.currentTarget.style.background = 'var(--bg-secondary)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = 'transparent';
}}
>
Cancel
</button>
<button
onClick={handleSubmit}
disabled={isSubmitting || isUploading}
style={{
padding: '1rem 2rem',
fontSize: '1rem',
fontWeight: 600,
border: 'none',
borderRadius: '8px',
background: isSubmitting || isUploading ? 'var(--bg-secondary)' : 'var(--accent-color)',
color: 'white',
cursor: isSubmitting || isUploading ? 'not-allowed' : 'pointer',
transition: 'all 0.3s ease',
opacity: isSubmitting || isUploading ? 0.6 : 1,
}}
onMouseEnter={(e) => {
if (!isSubmitting && !isUploading) {
e.currentTarget.style.transform = 'translateY(-2px)';
e.currentTarget.style.boxShadow = '0 4px 12px var(--accent-shadow)';
}
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = 'none';
}}
>
{isSubmitting ? 'Saving...' : 'Save Changes'}
</button>
</div>
</>
)}
</div>
</Layout>
);
}
export default EditPost;