@featurevisor/catalog 0.0.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/assets/index-DB7fEbI6.js +25 -0
- package/dist/assets/index-DgA0Oqrg.css +2 -0
- package/dist/favicon.png +0 -0
- package/dist/index.html +14 -0
- package/dist/logo-text.png +0 -0
- package/lib/entityTypes.d.ts +16 -0
- package/lib/entityTypes.js +96 -0
- package/lib/entityTypes.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +18 -0
- package/lib/index.js.map +1 -0
- package/lib/node/index.d.ts +74 -0
- package/lib/node/index.js +1368 -0
- package/lib/node/index.js.map +1 -0
- package/lib/types.d.ts +95 -0
- package/lib/types.js +3 -0
- package/lib/types.js.map +1 -0
- package/package.json +66 -13
- package/public/favicon.png +0 -0
- package/public/logo-text.png +0 -0
- package/src/App.tsx +62 -0
- package/src/api.spec.ts +14 -0
- package/src/api.ts +74 -0
- package/src/components/tests.spec.ts +134 -0
- package/src/components/tests.tsx +301 -0
- package/src/components/trees.tsx +202 -0
- package/src/components/ui.tsx +660 -0
- package/src/components/variables.tsx +700 -0
- package/src/components/variations.tsx +450 -0
- package/src/context/CatalogContext.tsx +30 -0
- package/src/entityTypes.spec.ts +13 -0
- package/src/entityTypes.ts +102 -0
- package/src/index.ts +1 -0
- package/src/main.tsx +26 -0
- package/src/node/index.spec.ts +380 -0
- package/src/node/index.ts +1906 -0
- package/src/pages/EntityDetailPage.tsx +1144 -0
- package/src/pages/HistoryPage.tsx +144 -0
- package/src/pages/HomePage.tsx +21 -0
- package/src/pages/ListPage.tsx +737 -0
- package/src/styles.css +59 -0
- package/src/testModel.ts +123 -0
- package/src/types.ts +112 -0
- package/src/vite-env.d.ts +1 -0
- package/index.js +0 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Link, useParams } from "react-router-dom";
|
|
3
|
+
|
|
4
|
+
import { fetchHistoryPage } from "../api";
|
|
5
|
+
import { encodeRouteSegment, entityLabels, getEntityRoute } from "../entityTypes";
|
|
6
|
+
import { Button, EmptyState, EntityKey, PageHeader } from "../components/ui";
|
|
7
|
+
import { useCatalog } from "../context/CatalogContext";
|
|
8
|
+
import type { HistoryEntry } from "../types";
|
|
9
|
+
|
|
10
|
+
const HISTORY_VISIBLE_ENTITY_LIMIT = 10;
|
|
11
|
+
|
|
12
|
+
function formatTimestamp(value: string) {
|
|
13
|
+
const date = new Date(value);
|
|
14
|
+
|
|
15
|
+
if (Number.isNaN(date.getTime())) {
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return date.toLocaleString();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function HistoryEntryCard(props: { entry: HistoryEntry; setKey?: string; commitUrl?: string }) {
|
|
23
|
+
const [expanded, setExpanded] = React.useState(false);
|
|
24
|
+
const entities = props.entry.entities.filter((entity) => entity.type !== "test");
|
|
25
|
+
const hasMore = entities.length > HISTORY_VISIBLE_ENTITY_LIMIT;
|
|
26
|
+
const visibleEntities = expanded ? entities : entities.slice(0, HISTORY_VISIBLE_ENTITY_LIMIT);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<li className="rounded-lg border border-border bg-surface p-4 shadow-sm ring-1 ring-black/5">
|
|
30
|
+
<div className="text-sm">
|
|
31
|
+
<span className="font-semibold">{props.entry.author}</span>{" "}
|
|
32
|
+
<a
|
|
33
|
+
className="text-primary hover:underline"
|
|
34
|
+
href={
|
|
35
|
+
props.commitUrl?.replace("{{hash}}", props.entry.commit) || `#${props.entry.commit}`
|
|
36
|
+
}
|
|
37
|
+
target="_blank"
|
|
38
|
+
rel="noreferrer"
|
|
39
|
+
>
|
|
40
|
+
{formatTimestamp(props.entry.timestamp)}
|
|
41
|
+
</a>
|
|
42
|
+
</div>
|
|
43
|
+
<ul className="mt-3 list-inside list-disc space-y-1 text-sm text-muted">
|
|
44
|
+
{visibleEntities.map((entity) => (
|
|
45
|
+
<li key={`${entity.type}-${entity.key}`} className="[overflow-wrap:anywhere]">
|
|
46
|
+
{entityLabels[entity.type].singular}{" "}
|
|
47
|
+
<Link
|
|
48
|
+
className="font-medium text-primary hover:underline"
|
|
49
|
+
to={getEntityRoute(entity.type, entity.key, entity.set || props.setKey)}
|
|
50
|
+
>
|
|
51
|
+
<EntityKey value={entity.key} className="font-medium" />
|
|
52
|
+
</Link>
|
|
53
|
+
</li>
|
|
54
|
+
))}
|
|
55
|
+
</ul>
|
|
56
|
+
{hasMore && (
|
|
57
|
+
<button
|
|
58
|
+
type="button"
|
|
59
|
+
onClick={() => setExpanded((current) => !current)}
|
|
60
|
+
className="mt-3 text-sm font-semibold text-primary hover:underline"
|
|
61
|
+
>
|
|
62
|
+
{expanded
|
|
63
|
+
? "See less"
|
|
64
|
+
: `See more (${props.entry.entities.length - HISTORY_VISIBLE_ENTITY_LIMIT} more)`}
|
|
65
|
+
</button>
|
|
66
|
+
)}
|
|
67
|
+
</li>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function HistoryTimeline(props: { path: string; setKey?: string; commitUrl?: string }) {
|
|
72
|
+
const [entries, setEntries] = React.useState<HistoryEntry[]>([]);
|
|
73
|
+
const [page, setPage] = React.useState(0);
|
|
74
|
+
const [totalPages, setTotalPages] = React.useState(1);
|
|
75
|
+
const [error, setError] = React.useState<string | null>(null);
|
|
76
|
+
|
|
77
|
+
async function loadPage(nextPage: number) {
|
|
78
|
+
try {
|
|
79
|
+
const response = await fetchHistoryPage(props.path, nextPage);
|
|
80
|
+
setEntries((current) => [...current, ...response.entries]);
|
|
81
|
+
setPage(response.page);
|
|
82
|
+
setTotalPages(response.totalPages);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
setError((err as Error).message);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
React.useEffect(() => {
|
|
89
|
+
setEntries([]);
|
|
90
|
+
setPage(0);
|
|
91
|
+
setTotalPages(1);
|
|
92
|
+
setError(null);
|
|
93
|
+
loadPage(1);
|
|
94
|
+
}, [props.path]);
|
|
95
|
+
|
|
96
|
+
if (error) {
|
|
97
|
+
return <EmptyState title="History unavailable" description={error} />;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (entries.length === 0 && page > 0) {
|
|
101
|
+
return <EmptyState title="No history found" />;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<div className="space-y-4">
|
|
106
|
+
<ol className="space-y-4">
|
|
107
|
+
{entries.map((entry) => (
|
|
108
|
+
<HistoryEntryCard
|
|
109
|
+
key={`${entry.commit}-${entry.timestamp}`}
|
|
110
|
+
entry={entry}
|
|
111
|
+
setKey={props.setKey}
|
|
112
|
+
commitUrl={props.commitUrl}
|
|
113
|
+
/>
|
|
114
|
+
))}
|
|
115
|
+
</ol>
|
|
116
|
+
|
|
117
|
+
{page < totalPages && (
|
|
118
|
+
<Button onClick={() => loadPage(page + 1)} className="w-full">
|
|
119
|
+
Load more
|
|
120
|
+
</Button>
|
|
121
|
+
)}
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function HistoryPage() {
|
|
127
|
+
const { manifest } = useCatalog();
|
|
128
|
+
const { setKey } = useParams();
|
|
129
|
+
const path = setKey
|
|
130
|
+
? `/data/sets/${encodeRouteSegment(setKey)}/history`
|
|
131
|
+
: "/data/project/history";
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<div>
|
|
135
|
+
<PageHeader
|
|
136
|
+
title={setKey ? `History for ${setKey}` : "Project History"}
|
|
137
|
+
description="Recent Git changes for authored definitions."
|
|
138
|
+
/>
|
|
139
|
+
<div className="px-6 pb-6">
|
|
140
|
+
<HistoryTimeline path={path} setKey={setKey} commitUrl={manifest.links?.commit} />
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Navigate } from "react-router-dom";
|
|
2
|
+
|
|
3
|
+
import { useCatalog } from "../context/CatalogContext";
|
|
4
|
+
import { encodeRouteSegment } from "../entityTypes";
|
|
5
|
+
|
|
6
|
+
export function HomePage() {
|
|
7
|
+
const { manifest } = useCatalog();
|
|
8
|
+
|
|
9
|
+
if (!manifest.sets) {
|
|
10
|
+
return <Navigate to="/features" replace />;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const firstSetKey = manifest.setKeys[0];
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Navigate
|
|
17
|
+
to={firstSetKey ? `/sets/${encodeRouteSegment(firstSetKey)}/features` : "/history"}
|
|
18
|
+
replace
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
}
|