Skip to content

Commit 886a85c

Browse files
maparentsid597
authored andcommitted
ENG-593 Change from Dev to Admin Panel (#415)
Dev panel has become admin panel (ctrl-shift-A). It shows the supabase Context object, and a table of all nodes of a certain types, starting with schema nodes.
1 parent 5ba858f commit 886a85c

File tree

3 files changed

+240
-30
lines changed

3 files changed

+240
-30
lines changed
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
import React, { useState, useEffect } from "react";
2+
import { HTMLTable, Button, MenuItem, Spinner, Label } from "@blueprintjs/core";
3+
import { Select } from "@blueprintjs/select";
4+
import {
5+
getSupabaseContext,
6+
getLoggedInClient,
7+
SupabaseContext,
8+
} from "~/utils/supabaseContext";
9+
import {
10+
getNodes,
11+
getNodeSchemas,
12+
nodeSchemaSignature,
13+
type NodeSignature,
14+
type PConcept,
15+
} from "@repo/database/lib/queries";
16+
import { DGSupabaseClient } from "@repo/database/lib/client";
17+
18+
const NodeRow = ({ node }: { node: PConcept }) => {
19+
return (
20+
<tr>
21+
<td>{node.name}</td>
22+
<td>{node.created}</td>
23+
<td>{node.last_modified}</td>
24+
<td>
25+
<pre>{JSON.stringify({ ...node, Content: null }, null, 2)}</pre>
26+
</td>
27+
<td>
28+
<pre>
29+
{JSON.stringify({ ...node.Content, Document: null }, null, 2)}
30+
</pre>
31+
<span
32+
data-link-title={node.Content?.text}
33+
data-link-uid={node.Content?.source_local_id}
34+
>
35+
<span className="rm-page-ref__brackets">[[</span>
36+
<span
37+
className="rm-page-ref rm-page-ref--link"
38+
onClick={(event) => {
39+
void (async (event) => {
40+
if (event.shiftKey) {
41+
if (node.Content?.source_local_id) {
42+
await window.roamAlphaAPI.ui.rightSidebar.addWindow({
43+
window: {
44+
// @ts-expect-error TODO: fix this
45+
"block-uid": node.Content.source_local_id,
46+
type: "outline",
47+
},
48+
});
49+
}
50+
} else if (node.Content?.Document?.source_local_id) {
51+
await window.roamAlphaAPI.ui.mainWindow.openPage({
52+
page: {
53+
uid: node.Content.Document.source_local_id,
54+
},
55+
});
56+
}
57+
})(event);
58+
}}
59+
>
60+
{node.Content?.text}
61+
</span>
62+
<span className="rm-page-ref__brackets">]]</span>
63+
</span>
64+
</td>
65+
<td>
66+
<pre>{JSON.stringify(node.Content?.Document, null, 2)}</pre>
67+
</td>
68+
</tr>
69+
);
70+
};
71+
72+
const NodeTable = ({ nodes }: { nodes: PConcept[] }) => {
73+
return (
74+
<HTMLTable>
75+
<thead>
76+
<tr>
77+
<th>Name</th>
78+
<th>Created</th>
79+
<th>Last Modified</th>
80+
<th>Concept</th>
81+
<th>Content</th>
82+
<th>Document</th>
83+
</tr>
84+
</thead>
85+
<tbody>
86+
{nodes.map((node: PConcept) => (
87+
<NodeRow node={node} key={node.id} />
88+
))}
89+
</tbody>
90+
</HTMLTable>
91+
);
92+
};
93+
94+
const AdminPanel = () => {
95+
const [context, setContext] = useState<SupabaseContext | null>(null);
96+
const [supabase, setSupabase] = useState<DGSupabaseClient | null>(null);
97+
const [schemas, setSchemas] = useState<NodeSignature[]>([]);
98+
const [showingSchema, setShowingSchema] =
99+
useState<NodeSignature>(nodeSchemaSignature);
100+
const [nodes, setNodes] = useState<PConcept[]>([]);
101+
const [loading, setLoading] = useState(true);
102+
const [loadingNodes, setLoadingNodes] = useState(true);
103+
const [error, setError] = useState<string | null>(null);
104+
105+
useEffect(() => {
106+
let ignore = false;
107+
void (async () => {
108+
try {
109+
if (!ignore) {
110+
setContext(await getSupabaseContext());
111+
}
112+
// Ask for logged-in client _after_ the context
113+
if (!ignore) {
114+
setSupabase(await getLoggedInClient());
115+
}
116+
} catch (e) {
117+
setError((e as Error).message);
118+
console.error("AdminPanel init failed", e);
119+
} finally {
120+
setLoading(false);
121+
}
122+
})();
123+
return () => {
124+
ignore = true;
125+
};
126+
}, []);
127+
128+
useEffect(() => {
129+
let ignore = false;
130+
void (async () => {
131+
if (!ignore && supabase !== null && context !== null) {
132+
try {
133+
setSchemas(await getNodeSchemas(supabase, context.spaceId));
134+
} catch (e) {
135+
setError((e as Error).message);
136+
console.error("getNodeSchemas failed", e);
137+
} finally {
138+
setLoading(false);
139+
}
140+
}
141+
})();
142+
return () => {
143+
ignore = true;
144+
};
145+
}, [supabase, context]);
146+
147+
useEffect(() => {
148+
let ignore = false;
149+
void (async () => {
150+
if (
151+
!ignore &&
152+
schemas !== null &&
153+
supabase !== null &&
154+
context !== null
155+
) {
156+
const spaceId = context.spaceId;
157+
try {
158+
setLoadingNodes(true);
159+
setNodes(
160+
await getNodes({
161+
supabase,
162+
spaceId,
163+
schemaLocalIds: showingSchema.sourceLocalId,
164+
}),
165+
);
166+
} catch (e) {
167+
setError((e as Error).message);
168+
console.error("getNodes failed", e);
169+
} finally {
170+
setLoadingNodes(false);
171+
}
172+
}
173+
})();
174+
return () => {
175+
ignore = true;
176+
};
177+
}, [schemas, showingSchema, context, supabase]);
178+
179+
if (loading) {
180+
return (
181+
<div className="p-3">
182+
<Spinner />
183+
<span className="mx-2">Loading admin data…</span>
184+
</div>
185+
);
186+
}
187+
188+
if (error) {
189+
return <p className="text-red-700">{error}</p>;
190+
}
191+
192+
return (
193+
<>
194+
<p>
195+
Context:{" "}
196+
<code>{JSON.stringify({ ...context, spacePassword: "****" })}</code>
197+
</p>
198+
{schemas.length > 0 ? (
199+
<>
200+
<Label>
201+
Display:
202+
<div className="mx-2 inline-block">
203+
<Select
204+
items={schemas}
205+
onItemSelect={(choice) => {
206+
setShowingSchema(choice);
207+
}}
208+
itemRenderer={(node, { handleClick, modifiers }) => (
209+
<MenuItem
210+
active={modifiers.active}
211+
key={node.sourceLocalId}
212+
label={node.name}
213+
onClick={handleClick}
214+
text={node.name}
215+
/>
216+
)}
217+
>
218+
<Button text={showingSchema.name} />
219+
</Select>
220+
</div>
221+
</Label>
222+
<div>{loadingNodes ? <Spinner /> : <NodeTable nodes={nodes} />}</div>
223+
</>
224+
) : (
225+
<p>No node schemas found</p>
226+
)}
227+
</>
228+
);
229+
};
230+
231+
export default AdminPanel;

apps/roam/src/components/settings/Settings.tsx

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ import { getFormattedConfigTree } from "~/utils/discourseConfigRef";
1616
import DiscourseGraphHome from "./GeneralSettings";
1717
import DiscourseGraphExport from "./ExportSettings";
1818
import QuerySettings from "./QuerySettings";
19+
import AdminPanel from "./AdminPanel";
1920
import DiscourseNodeConfigPanel from "./DiscourseNodeConfigPanel";
2021
import getDiscourseNodes, {
2122
excludeDefaultNodes,
2223
} from "~/utils/getDiscourseNodes";
2324
import NodeConfig from "./NodeConfig";
24-
import sendErrorEmail from "~/utils/sendErrorEmail";
2525
import HomePersonalSettings from "./HomePersonalSettings";
2626
import refreshConfigTree from "~/utils/refreshConfigTree";
2727
import { FeedbackWidget } from "~/components/BirdEatsBugs";
@@ -78,10 +78,10 @@ export const SettingsDialog = ({
7878

7979
useEffect(() => {
8080
const handleKeyPress = (e: KeyboardEvent) => {
81-
if (e.ctrlKey && e.shiftKey && e.key === "D") {
81+
if (e.ctrlKey && e.shiftKey && e.key === "A") {
8282
e.stopPropagation();
8383
e.preventDefault();
84-
setSelectedTabId("secret-dev-panel");
84+
setSelectedTabId("secret-admin-panel");
8585
}
8686
};
8787

@@ -224,31 +224,10 @@ export const SettingsDialog = ({
224224
{/* Secret Dev Panel */}
225225
<Tab
226226
hidden={true}
227-
id="secret-dev-panel"
228-
title="Secret Dev Panel"
227+
id="secret-admin-panel"
228+
title="Secret Admin Panel"
229229
className="overflow-y-auto"
230-
panel={
231-
<div className="flex gap-4 p-4">
232-
<Button
233-
onClick={() => {
234-
console.log("NODE_ENV:", process.env.NODE_ENV);
235-
}}
236-
>
237-
Log Node Env
238-
</Button>
239-
<Button
240-
onClick={() => {
241-
console.log("sending error email");
242-
sendErrorEmail({
243-
error: new Error("test"),
244-
type: "Test",
245-
});
246-
}}
247-
>
248-
sendErrorEmail()
249-
</Button>
250-
</div>
251-
}
230+
panel={<AdminPanel />}
252231
/>
253232
</Tabs>
254233
</div>

packages/database/src/lib/queries.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ export const getNodeSchemas = async (
9898
): Promise<NodeSignature[]> => {
9999
let result = Object.values(NODE_SCHEMA_CACHE)
100100
.filter((x) => typeof x === "object")
101-
.filter((x) => x.spaceId === spaceId);
102-
if (forceCacheReload || result.length === 0) {
101+
.filter((x) => x.spaceId === spaceId || x.spaceId === 0);
102+
if (forceCacheReload || result.length === 1) {
103103
const q = composeQuery({ supabase, spaceId });
104104
const res = (await q) as PostgrestResponse<defaultQueryShape>;
105105
if (res.error) {
@@ -122,7 +122,7 @@ export const getNodeSchemas = async (
122122
};
123123
result = Object.values(NODE_SCHEMA_CACHE)
124124
.filter((x) => typeof x === "object")
125-
.filter((x) => x.spaceId === spaceId);
125+
.filter((x) => x.spaceId === spaceId || x.spaceId === 0);
126126
}
127127
return result;
128128
};

0 commit comments

Comments
 (0)