feat(frontend-admin): Add React components for admin pages to manage blog content (authors, categories, posts, series) and dashboard

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-03-25 23:56:28 -07:00
parent 250bdd2287
commit 12b8970660
6 changed files with 39 additions and 19 deletions

View file

@ -16,7 +16,7 @@ const Title = styled.h1`
`;
const TableContainer = styled.div`
background: white;
background: ${({ theme }) => theme.colors.background.secondary};
border: 1px solid ${({ theme }) => theme.colors.border.default};
border-radius: ${({ theme }) => theme.borderRadius.lg};
overflow: hidden;
@ -76,7 +76,7 @@ const Button = styled.button<{ $variant?: 'primary' | 'danger' }>`
padding: ${({ theme }) => `${theme.spacing.xs} ${theme.spacing.sm}`};
border: 1px solid ${({ theme, $variant }) => ($variant === 'danger' ? theme.colors.error.main : theme.colors.border.hover)};
border-radius: ${({ theme }) => theme.borderRadius.sm};
background: ${({ theme, $variant }) => ($variant === 'primary' ? theme.colors.primary.main : $variant === 'danger' ? theme.colors.error.main : 'white')};
background: ${({ theme, $variant }) => ($variant === 'primary' ? theme.colors.primary.main : $variant === 'danger' ? theme.colors.error.main : theme.colors.background.tertiary)};
color: ${({ theme, $variant }) => ($variant === 'primary' || $variant === 'danger' ? 'white' : theme.colors.text.primary)};
font-size: 0.875rem;
cursor: pointer;
@ -106,6 +106,8 @@ const Input = styled.input`
border: 1px solid ${({ theme }) => theme.colors.border.hover};
border-radius: ${({ theme }) => theme.borderRadius.md};
font-size: 0.875rem;
background: ${({ theme }) => theme.colors.background.tertiary};
color: ${({ theme }) => theme.colors.text.primary};
&:focus {
outline: none;

View file

@ -16,7 +16,7 @@ const Title = styled.h1`
`;
const TableContainer = styled.div`
background: white;
background: ${({ theme }) => theme.colors.background.secondary};
border: 1px solid ${({ theme }) => theme.colors.border.default};
border-radius: ${({ theme }) => theme.borderRadius.lg};
overflow: hidden;
@ -67,7 +67,7 @@ const Button = styled.button<{ $variant?: 'primary' | 'danger' }>`
padding: ${({ theme }) => `${theme.spacing.xs} ${theme.spacing.sm}`};
border: 1px solid ${({ theme, $variant }) => ($variant === 'danger' ? theme.colors.error.main : theme.colors.border.hover)};
border-radius: ${({ theme }) => theme.borderRadius.sm};
background: ${({ theme, $variant }) => ($variant === 'primary' ? theme.colors.primary.main : $variant === 'danger' ? theme.colors.error.main : 'white')};
background: ${({ theme, $variant }) => ($variant === 'primary' ? theme.colors.primary.main : $variant === 'danger' ? theme.colors.error.main : theme.colors.background.tertiary)};
color: ${({ theme, $variant }) => ($variant === 'primary' || $variant === 'danger' ? 'white' : theme.colors.text.primary)};
font-size: 0.875rem;
cursor: pointer;
@ -97,6 +97,8 @@ const Input = styled.input`
border: 1px solid ${({ theme }) => theme.colors.border.hover};
border-radius: ${({ theme }) => theme.borderRadius.md};
font-size: 0.875rem;
background: ${({ theme }) => theme.colors.background.tertiary};
color: ${({ theme }) => theme.colors.text.primary};
&:focus {
outline: none;
@ -109,7 +111,8 @@ const Select = styled.select`
border: 1px solid ${({ theme }) => theme.colors.border.hover};
border-radius: ${({ theme }) => theme.borderRadius.md};
font-size: 0.875rem;
background: white;
background: ${({ theme }) => theme.colors.background.tertiary};
color: ${({ theme }) => theme.colors.text.primary};
&:focus {
outline: none;

View file

@ -51,7 +51,7 @@ const Stats = styled.div`
`;
const StatCard = styled.div`
background: white;
background: ${({ theme }) => theme.colors.background.secondary};
border: 1px solid ${({ theme }) => theme.colors.border.default};
border-radius: ${({ theme }) => theme.borderRadius.lg};
padding: ${({ theme }) => theme.spacing.lg};
@ -70,7 +70,7 @@ const StatValue = styled.div`
`;
const Section = styled.section`
background: white;
background: ${({ theme }) => theme.colors.background.secondary};
border: 1px solid ${({ theme }) => theme.colors.border.default};
border-radius: ${({ theme }) => theme.borderRadius.lg};
overflow: hidden;

View file

@ -25,7 +25,7 @@ const Header = styled.div`
const BackButton = styled.button`
padding: ${({ theme }) => `${theme.spacing.sm} ${theme.spacing.md}`};
background: white;
background: ${({ theme }) => theme.colors.background.secondary};
border: 1px solid ${({ theme }) => theme.colors.border.hover};
border-radius: ${({ theme }) => theme.borderRadius.md};
color: ${({ theme }) => theme.colors.text.primary};
@ -61,6 +61,7 @@ const TitleInput = styled.input`
font-size: 2rem;
font-weight: 700;
color: ${({ theme }) => theme.colors.text.primary};
background: ${({ theme }) => theme.colors.background.tertiary};
&:focus {
outline: none;
@ -111,7 +112,7 @@ const TabBar = styled.div`
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)};
background: ${({ $active, theme }) => ($active ? theme.colors.background.tertiary : 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)};
@ -147,6 +148,11 @@ const SuggestionsTabContent = styled.div`
padding: ${({ theme }) => theme.spacing.sm};
`;
const LastSavedLabel = styled.span`
font-size: 0.875rem;
color: ${({ theme }) => theme.colors.text.secondary};
`;
export const PostEditorPage = () => {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
@ -331,7 +337,7 @@ export const PostEditorPage = () => {
<Container>
<Header>
<BackButton onClick={() => navigate('/posts')}> Back to Posts</BackButton>
{lastSaved && <span style={{ fontSize: '0.875rem', color: '#6b7280' }}>Last saved: {lastSaved.toLocaleTimeString()}</span>}
{lastSaved && <LastSavedLabel>Last saved: {lastSaved.toLocaleTimeString()}</LastSavedLabel>}
</Header>
<EditorLayout>

View file

@ -78,6 +78,8 @@ const SearchInput = styled.input`
border-radius: ${({ theme }) => theme.borderRadius.md};
font-size: 0.875rem;
min-width: 250px;
background: ${({ theme }) => theme.colors.background.tertiary};
color: ${({ theme }) => theme.colors.text.primary};
&:focus {
outline: none;
@ -91,7 +93,8 @@ const Select = styled.select`
border: 1px solid ${({ theme }) => theme.colors.border.hover};
border-radius: ${({ theme }) => theme.borderRadius.md};
font-size: 0.875rem;
background: white;
background: ${({ theme }) => theme.colors.background.tertiary};
color: ${({ theme }) => theme.colors.text.primary};
cursor: pointer;
&:focus {
@ -102,7 +105,7 @@ const Select = styled.select`
`;
const TableContainer = styled.div`
background: white;
background: ${({ theme }) => theme.colors.background.secondary};
border: 1px solid ${({ theme }) => theme.colors.border.default};
border-radius: ${({ theme }) => theme.borderRadius.lg};
overflow: hidden;
@ -186,7 +189,7 @@ const PageButton = styled.button`
padding: ${({ theme }) => `${theme.spacing.xs} ${theme.spacing.sm}`};
border: 1px solid ${({ theme }) => theme.colors.border.hover};
border-radius: ${({ theme }) => theme.borderRadius.sm};
background: white;
background: ${({ theme }) => theme.colors.background.secondary};
color: ${({ theme }) => theme.colors.text.primary};
font-size: 0.875rem;
cursor: pointer;

View file

@ -17,7 +17,7 @@ const Title = styled.h1`
`;
const TableContainer = styled.div`
background: white;
background: ${({ theme }) => theme.colors.background.secondary};
border: 1px solid ${({ theme }) => theme.colors.border.default};
border-radius: ${({ theme }) => theme.borderRadius.lg};
overflow: hidden;
@ -65,9 +65,12 @@ const StatusBadge = styled.span<{ $status: SeriesStatus }>`
border-radius: ${({ theme }) => theme.borderRadius.sm};
font-size: 0.75rem;
font-weight: 600;
background: ${({ $status }) => ($status === 'published' ? '#10b98115' : '#6b728015')};
color: ${({ $status }) => ($status === 'published' ? '#10b981' : '#6b7280')};
border: 1px solid ${({ $status }) => ($status === 'published' ? '#10b98130' : '#6b728030')};
background: ${({ $status, theme }) =>
$status === 'published' ? theme.colors.success.background : theme.colors.background.tertiary};
color: ${({ $status, theme }) =>
$status === 'published' ? theme.colors.success.text : theme.colors.text.secondary};
border: 1px solid ${({ $status, theme }) =>
$status === 'published' ? theme.colors.success.border : theme.colors.border.default};
`;
const Actions = styled.div`
@ -79,7 +82,7 @@ const Button = styled.button<{ $variant?: 'primary' | 'danger' }>`
padding: ${({ theme }) => `${theme.spacing.xs} ${theme.spacing.sm}`};
border: 1px solid ${({ theme, $variant }) => ($variant === 'danger' ? theme.colors.error.main : theme.colors.border.hover)};
border-radius: ${({ theme }) => theme.borderRadius.sm};
background: ${({ theme, $variant }) => ($variant === 'primary' ? theme.colors.primary.main : $variant === 'danger' ? theme.colors.error.main : 'white')};
background: ${({ theme, $variant }) => ($variant === 'primary' ? theme.colors.primary.main : $variant === 'danger' ? theme.colors.error.main : theme.colors.background.tertiary)};
color: ${({ theme, $variant }) => ($variant === 'primary' || $variant === 'danger' ? 'white' : theme.colors.text.primary)};
font-size: 0.875rem;
cursor: pointer;
@ -109,6 +112,8 @@ const Input = styled.input`
border: 1px solid ${({ theme }) => theme.colors.border.hover};
border-radius: ${({ theme }) => theme.borderRadius.md};
font-size: 0.875rem;
background: ${({ theme }) => theme.colors.background.tertiary};
color: ${({ theme }) => theme.colors.text.primary};
&:focus {
outline: none;
@ -121,7 +126,8 @@ const Select = styled.select`
border: 1px solid ${({ theme }) => theme.colors.border.hover};
border-radius: ${({ theme }) => theme.borderRadius.md};
font-size: 0.875rem;
background: white;
background: ${({ theme }) => theme.colors.background.tertiary};
color: ${({ theme }) => theme.colors.text.primary};
&:focus {
outline: none;