public
anord
read
Ledger
Why work hard when you can work easier?
Languages
Repository composition by tracked source files.
TypeScript
86%
CSS
10%
SQL
3%
Shell
1%
HTML
0%
Trace
apps/web/src/components/DraftsPage.tsx
Trace helps you understand code history line by line. See who changed each line, when it changed, and which commit introduced it.
Author
Date
Commit
Line
Code
1
import { useEffect, useState } from "react";
2
import { Link } from "react-router-dom";
3
import type { PageSummary, SessionUser } from "@ledger/shared";
4
import { api } from "../lib/api";
5
import { EmptyState } from "./EmptyState";
6
import { Icon } from "./Icon";
7
import { PageHeader } from "./PageHeader";
9
type Space = {
10
id: string;
11
name: string;
12
key: string;
13
visibility: string;
14
};
16
export function DraftsPage({
17
user,
18
spaces
19
}: {
20
user: SessionUser | null;
21
spaces: Space[];
22
}) {
23
const [drafts, setDrafts] = useState<PageSummary[]>([]);
24
const [loading, setLoading] = useState(true);
25
const [error, setError] = useState<string | null>(null);
27
useEffect(() => {
28
if (!user || user.role === "viewer" || user.role === "public") {
29
setLoading(false);
30
setDrafts([]);
31
return;
32
}
34
api
35
.get<{ pages: PageSummary[] }>("/api/pages/drafts")
36
.then((response) => setDrafts(response.pages))
37
.catch((reason) => setError(reason instanceof Error ? reason.message : "Could not load drafts."))
38
.finally(() => setLoading(false));
39
}, [user]);
41
return (
42
<div className="stack-page">
43
<PageHeader
44
eyebrow="Drafts"
45
title="Work in progress"
46
description="Draft pages are only visible to people with the right access. Use this space to review and publish content safely."
47
actions={user && user.role !== "viewer" && user.role !== "public" ? <Link to="/spaces" className="button-secondary">New draft</Link> : null}
48
/>
50
<section className="content-section">
51
{loading ? <p className="muted">Loading drafts...</p> : null}
52
{!loading && (!user || user.role === "viewer" || user.role === "public") ? (
53
<EmptyState
54
title="Drafts require editor access"
55
description="Sign in with an editor, admin, or owner account to review draft content."
56
/>
57
) : null}
58
{!loading && error ? (
59
<EmptyState title="Could not load drafts" description={error} />
60
) : null}
61
{!loading && !error && drafts.length === 0 && user && user.role !== "viewer" && user.role !== "public" ? (
62
<EmptyState
63
title="No drafts yet"
64
description="Create a new draft page from the admin area to start building documentation before publishing."
65
/>
66
) : null}
67
{!loading && drafts.length > 0 ? (
68
<div className="document-feed">
69
{drafts.map((page) => (
70
<Link key={page.id} to={`/page/${page.slug}`} className="document-feed__item">
71
<div className="document-feed__icon">
72
<Icon name="document" className="icon icon-sm" />
73
</div>
74
<div className="document-feed__body">
75
<strong className="document-feed__title">{page.title}</strong>
76
<p className="document-feed__meta">
77
{spaces.find((space) => space.id === page.spaceId)?.name ?? "Collection"} - draft
78
</p>
79
<p className="document-feed__excerpt">{page.excerpt ?? "Draft page without an excerpt yet."}</p>
80
</div>
81
</Link>
82
))}
83
</div>
84
) : null}
85
</section>
86
</div>
87
);
88
}