diff --git a/features/blog/frontend-admin/src/pages/PostEditorPage.tsx b/features/blog/frontend-admin/src/pages/PostEditorPage.tsx index 0dc4e20f4..0e8925906 100644 --- a/features/blog/frontend-admin/src/pages/PostEditorPage.tsx +++ b/features/blog/frontend-admin/src/pages/PostEditorPage.tsx @@ -1,10 +1,13 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useMemo } from 'react'; import { useParams, useNavigate } from '@lilith/ui-router'; import styled from '@lilith/ui-styled-components'; +import { TrackedChangesProvider, SuggestionSidebar, AIReviewButton } from '@lilith/ui-tracked-changes'; +import type { SuggestionAuthor } from '@lilith/tracked-changes'; import { MarkdownEditor } from '../components/MarkdownEditor'; import { PostMetaSidebar } from '../components/PostMetaSidebar'; import { SlugInput } from '../components/SlugInput'; import { usePost, useCreatePost, useUpdatePost, usePublishPost, useSchedulePost, useUnpublishPost } from '../api/posts'; +import { useSuggestions, createBlogSuggestionsAPI } from '../api/suggestions'; import type { ContentType, PostStatus } from '@features/blog'; const Container = styled.div` @@ -93,6 +96,57 @@ const ErrorState = styled.div` color: ${({ theme }) => theme.colors.error.main}; `; +const SideColumn = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing.md}; + overflow-y: auto; +`; + +const TabBar = styled.div` + display: flex; + border-bottom: 1px solid ${({ theme }) => theme.colors.border.default}; +`; + +const Tab = styled.button<{ $active?: boolean }>` + flex: 1; + padding: ${({ theme }) => `${theme.spacing.sm} ${theme.spacing.md}`}; + background: ${({ $active, theme }) => ($active ? 'white' : theme.colors.background.secondary)}; + border: none; + border-bottom: 2px solid ${({ $active, theme }) => ($active ? theme.colors.primary.main : 'transparent')}; + color: ${({ $active, theme }) => ($active ? theme.colors.primary.main : theme.colors.text.secondary)}; + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + transition: all 0.15s; + + &:hover { + color: ${({ theme }) => theme.colors.text.primary}; + } +`; + +const SuggestionBadge = styled.span` + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 18px; + height: 18px; + padding: 0 4px; + margin-left: 6px; + border-radius: 9px; + background: ${({ theme }) => theme.colors.primary.main}; + color: white; + font-size: 0.7rem; + font-weight: 700; +`; + +const SuggestionsTabContent = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing.md}; + padding: ${({ theme }) => theme.spacing.sm}; +`; + export const PostEditorPage = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); @@ -124,6 +178,23 @@ export const PostEditorPage = () => { const [scheduledFor, setScheduledFor] = useState(null); const [lastSaved, setLastSaved] = useState(null); + const [sidebarTab, setSidebarTab] = useState<'meta' | 'suggestions'>('meta'); + + const { data: suggestions } = useSuggestions(id); + const pendingSuggestionCount = suggestions?.filter((s) => s.status === 'pending').length ?? 0; + + const documentVersion = (existingPost?.metadata as Record)?.documentVersion as number ?? 1; + + const currentAuthor = useMemo(() => ({ + kind: 'human', + identifier: authorId || 'anonymous', + displayName: 'Editor', + }), [authorId]); + + const suggestionsAPI = useMemo(() => { + if (!id) return null; + return createBlogSuggestionsAPI(id); + }, [id]); useEffect(() => { if (existingPost) { @@ -256,7 +327,7 @@ export const PostEditorPage = () => { return Error loading post: {error.message}; } - return ( + const pageContent = (
navigate('/posts')}>← Back to Posts @@ -265,50 +336,93 @@ export const PostEditorPage = () => { - setTitle(e.target.value)} /> + setTitle(e.target.value)} /> - + - + + {isEditMode && ( + + setSidebarTab('meta')}> + Meta + + setSidebarTab('suggestions')}> + Suggestions + {pendingSuggestionCount > 0 && ( + {pendingSuggestionCount} + )} + + + )} + + {sidebarTab === 'meta' && ( + + )} + + {sidebarTab === 'suggestions' && ( + + + + + )} + ); + + if (!suggestionsAPI) return pageContent; + + return ( + + {pageContent} + + ); };