@data-club/ai-hub 0.0.7 → 0.0.9
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/dist/auth/locales/cs.d.ts.map +1 -1
- package/dist/auth/locales/cs.js +1 -4
- package/dist/auth/locales/cs.js.map +1 -1
- package/dist/auth/locales/sk.d.ts.map +1 -1
- package/dist/auth/locales/sk.js +1 -4
- package/dist/auth/locales/sk.js.map +1 -1
- package/dist/auth/texts.d.ts +2 -9
- package/dist/auth/texts.d.ts.map +1 -1
- package/dist/auth/texts.js +1 -4
- package/dist/auth/texts.js.map +1 -1
- package/dist/components/ChatShell/ShellHeader.d.ts +4 -4
- package/dist/components/ChatShell/UserMenu.d.ts +4 -10
- package/dist/components/ChatShell/UserMenu.d.ts.map +1 -1
- package/dist/components/ChatShell/UserMenu.js +8 -15
- package/dist/components/ChatShell/UserMenu.js.map +1 -1
- package/dist/components/DataClubAIHubAdmin/AgentsPage.d.ts +1 -1
- package/dist/components/DataClubAIHubAdmin/AgentsPage.d.ts.map +1 -1
- package/dist/components/DataClubAIHubAdmin/AgentsPage.js +2 -11
- package/dist/components/DataClubAIHubAdmin/AgentsPage.js.map +1 -1
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.d.ts.map +1 -1
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.js +6 -3
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.js.map +1 -1
- package/dist/components/DataClubAIHubAdmin/SettingsPage.js +2 -6
- package/dist/components/DataClubAIHubAdmin/SettingsPage.js.map +1 -1
- package/dist/components/DataClubAIHubAdmin/index.d.ts +1 -1
- package/dist/components/DataClubAIHubAdmin/index.js +1 -1
- package/dist/util/relativeTime.d.ts +11 -0
- package/dist/util/relativeTime.d.ts.map +1 -1
- package/dist/util/relativeTime.js +19 -0
- package/dist/util/relativeTime.js.map +1 -1
- package/package.json +1 -1
- package/src/auth/locales/cs.ts +1 -6
- package/src/auth/locales/sk.ts +1 -6
- package/src/auth/texts.ts +3 -15
- package/src/components/ChatShell/ShellHeader.tsx +4 -4
- package/src/components/ChatShell/UserMenu.tsx +7 -27
- package/src/components/DataClubAIHubAdmin/AgentsPage.tsx +1 -21
- package/src/components/DataClubAIHubAdmin/RotatingMessagesPage.tsx +8 -1
- package/src/components/DataClubAIHubAdmin/SettingsPage.tsx +1 -21
- package/src/components/DataClubAIHubAdmin/index.tsx +1 -1
- package/src/styles/CLASSES.md +1 -1
- package/src/styles/default.css +0 -4
- package/src/util/relativeTime.ts +19 -0
|
@@ -4,22 +4,16 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Step 9 ships the minimal version: avatar circle + username, click
|
|
6
6
|
* opens a dropdown with the role label and a `Sign out` item. Step 10
|
|
7
|
-
* adds the `Theme: Light ↔ Dark` item. **Step 11 adds the `Admin`
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* `Open Letta ADE` opens the deployment's ADE in a new tab
|
|
13
|
-
* (`target="_blank" rel="noopener noreferrer"`); the URL comes from the
|
|
14
|
-
* `deployment.letta_ade_url` field on `/auth/me` (only present for
|
|
15
|
-
* `power_user` and above by design — see `AIHubDeployment` JSDoc).
|
|
7
|
+
* adds the `Theme: Light ↔ Dark` item. **Step 11 adds the `Admin`
|
|
8
|
+
* item**, gated to `role >= power_user` per §8.3 / [00-shell.md].
|
|
9
|
+
* Step 12 wires the voluntary `Change Password` item via the shell's
|
|
10
|
+
* shell-aware framing.
|
|
16
11
|
*
|
|
17
12
|
* The right side of the shell header is **library-owned** and not
|
|
18
13
|
* customizable — per-customer wrappers can theme via CSS but cannot
|
|
19
14
|
* replace this component.
|
|
20
15
|
*
|
|
21
16
|
* @see ui-sketches/00-shell.md — user_menu_button + user_menu_dropdown
|
|
22
|
-
* @see SPECIFICATIONS.md §8.3 — Open Letta ADE link
|
|
23
17
|
*/
|
|
24
18
|
|
|
25
19
|
import {
|
|
@@ -37,11 +31,11 @@ import type { AIHubRole } from "../../types.js";
|
|
|
37
31
|
import { ThemeSwitcher } from "../ThemeSwitcher/index.js";
|
|
38
32
|
|
|
39
33
|
/**
|
|
40
|
-
* Returns true when the given role can see the admin-only menu
|
|
41
|
-
*
|
|
34
|
+
* Returns true when the given role can see the admin-only `Admin` menu
|
|
35
|
+
* item. Mirrors the §8.2 matrix.
|
|
42
36
|
*
|
|
43
37
|
* @param role - the caller's role, or `null` when not authenticated.
|
|
44
|
-
* @returns whether the
|
|
38
|
+
* @returns whether the item should render.
|
|
45
39
|
*/
|
|
46
40
|
function canSeeAdminItems(role: AIHubRole | null): boolean {
|
|
47
41
|
return role === "power_user" || role === "admin" || role === "super_admin";
|
|
@@ -76,7 +70,6 @@ export function UserMenu(props: UserMenuProps): ReactNode {
|
|
|
76
70
|
const [open, setOpen] = useState(false);
|
|
77
71
|
const wrapRef = useRef<HTMLDivElement | null>(null);
|
|
78
72
|
const showAdminItems = canSeeAdminItems(auth.role);
|
|
79
|
-
const lettaAdeUrl = auth.deployment?.letta_ade_url ?? null;
|
|
80
73
|
|
|
81
74
|
// Close on outside-click + Escape so the menu can't be left open
|
|
82
75
|
// accidentally over the chat below.
|
|
@@ -168,19 +161,6 @@ export function UserMenu(props: UserMenuProps): ReactNode {
|
|
|
168
161
|
⚙ {texts.adminMenuItem}
|
|
169
162
|
</button>
|
|
170
163
|
) : null}
|
|
171
|
-
{showAdminItems && lettaAdeUrl !== null && lettaAdeUrl.length > 0 ? (
|
|
172
|
-
<a
|
|
173
|
-
role="menuitem"
|
|
174
|
-
className="data-club-ai-hub__user-menu__item data-club-ai-hub__user-menu__item--link"
|
|
175
|
-
data-testid="user-menu-open-letta-ade"
|
|
176
|
-
href={lettaAdeUrl}
|
|
177
|
-
target="_blank"
|
|
178
|
-
rel="noopener noreferrer"
|
|
179
|
-
onClick={() => setOpen(false)}
|
|
180
|
-
>
|
|
181
|
-
↗ {texts.openLettaAdeMenuItem}
|
|
182
|
-
</a>
|
|
183
|
-
) : null}
|
|
184
164
|
{themeSwitcherEnabled ? (
|
|
185
165
|
<div
|
|
186
166
|
className="data-club-ai-hub__user-menu__item"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* `<DataClubAIHubAdmin />` ([06-admin-agents.md]).
|
|
4
4
|
*
|
|
5
5
|
* Lists the ai-hub agents catalog with per-row `[Edit]` + `[Archive]` /
|
|
6
|
-
* `[Unarchive]` actions, top-right `[+ Create agent]`
|
|
6
|
+
* `[Unarchive]` actions, a top-right `[+ Create agent]` button,
|
|
7
7
|
* and a `[☐ Show archived]` toggle. **No TPM⚙ button** — TPM-recovery was
|
|
8
8
|
* removed in Step 11.5 (§6.5); the column is retained on the DB schema but
|
|
9
9
|
* the UI doesn't surface it. **No 🗑 button** — agents cannot be hard-deleted
|
|
@@ -21,7 +21,6 @@ import { useEffect, useMemo, useState, type ReactNode } from "react";
|
|
|
21
21
|
import type { AIHubAdminTexts } from "../../auth/texts.js";
|
|
22
22
|
import type { AdminAgentView } from "../../client/AIHubClient.js";
|
|
23
23
|
import { useAIHub } from "../../context/useAIHub.js";
|
|
24
|
-
import { useAIHubAuth } from "../../context/useAIHubAuth.js";
|
|
25
24
|
|
|
26
25
|
import {
|
|
27
26
|
AgentEditorModal,
|
|
@@ -35,12 +34,6 @@ type EditorState =
|
|
|
35
34
|
| { kind: "create" }
|
|
36
35
|
| { kind: "edit"; agent: AdminAgentView };
|
|
37
36
|
|
|
38
|
-
/** Resolves the deployment's ADE URL or derives one from the base URL. */
|
|
39
|
-
function deriveAdeUrl(adeUrl: string | undefined): string | null {
|
|
40
|
-
if (!adeUrl) return null;
|
|
41
|
-
return adeUrl.replace(/\/$/, "");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
37
|
/** Sort comparator — alphabetical by display_name, stable. */
|
|
45
38
|
function byDisplayName(a: AdminAgentView, b: AdminAgentView): number {
|
|
46
39
|
return a.display_name.localeCompare(b.display_name);
|
|
@@ -60,8 +53,6 @@ export interface AdminAgentsPageProps {
|
|
|
60
53
|
*/
|
|
61
54
|
export function AdminAgentsPage(props: AdminAgentsPageProps): ReactNode {
|
|
62
55
|
const { client } = useAIHub();
|
|
63
|
-
const auth = useAIHubAuth();
|
|
64
|
-
const adeUrl = deriveAdeUrl(auth.deployment?.letta_ade_url);
|
|
65
56
|
|
|
66
57
|
const [agents, setAgents] = useState<AdminAgentView[] | null>(null);
|
|
67
58
|
const [loadError, setLoadError] = useState<string | null>(null);
|
|
@@ -171,17 +162,6 @@ export function AdminAgentsPage(props: AdminAgentsPageProps): ReactNode {
|
|
|
171
162
|
{props.texts.agentsHeading}
|
|
172
163
|
</h1>
|
|
173
164
|
<div className="data-club-ai-hub-admin__page-header-actions">
|
|
174
|
-
{adeUrl !== null ? (
|
|
175
|
-
<a
|
|
176
|
-
className="data-club-ai-hub-admin__button"
|
|
177
|
-
data-testid="admin-agents-open-ade"
|
|
178
|
-
href={adeUrl}
|
|
179
|
-
target="_blank"
|
|
180
|
-
rel="noopener noreferrer"
|
|
181
|
-
>
|
|
182
|
-
{props.texts.openLettaAdeButton}
|
|
183
|
-
</a>
|
|
184
|
-
) : null}
|
|
185
165
|
<button
|
|
186
166
|
type="button"
|
|
187
167
|
className="data-club-ai-hub-admin__button data-club-ai-hub-admin__button--primary"
|
|
@@ -17,6 +17,7 @@ import { useEffect, useMemo, useState, type ReactNode } from "react";
|
|
|
17
17
|
import type { AIHubAdminTexts } from "../../auth/texts.js";
|
|
18
18
|
import type { AdminRotatingMessageView } from "../../client/AIHubClient.js";
|
|
19
19
|
import { useAIHub } from "../../context/useAIHub.js";
|
|
20
|
+
import { formatDateTime } from "../../util/relativeTime.js";
|
|
20
21
|
|
|
21
22
|
import {
|
|
22
23
|
RotatingMessageEditorModal,
|
|
@@ -80,7 +81,7 @@ export interface AdminRotatingMessagesPageProps {
|
|
|
80
81
|
export function AdminRotatingMessagesPage(
|
|
81
82
|
props: AdminRotatingMessagesPageProps,
|
|
82
83
|
): ReactNode {
|
|
83
|
-
const { client } = useAIHub();
|
|
84
|
+
const { client, language } = useAIHub();
|
|
84
85
|
|
|
85
86
|
const [rows, setRows] = useState<AdminRotatingMessageView[] | null>(null);
|
|
86
87
|
const [loadError, setLoadError] = useState<string | null>(null);
|
|
@@ -246,6 +247,7 @@ export function AdminRotatingMessagesPage(
|
|
|
246
247
|
<th>{props.texts.rotatingColumnAgent}</th>
|
|
247
248
|
<th>{props.texts.rotatingColumnSchedule}</th>
|
|
248
249
|
<th>{props.texts.rotatingColumnStatus}</th>
|
|
250
|
+
<th>{props.texts.rotatingColumnLastRotated}</th>
|
|
249
251
|
<th>{props.texts.rotatingColumnId}</th>
|
|
250
252
|
<th>{props.texts.rotatingColumnActions}</th>
|
|
251
253
|
</tr>
|
|
@@ -281,6 +283,11 @@ export function AdminRotatingMessagesPage(
|
|
|
281
283
|
{statusLabel(r, props.texts)}
|
|
282
284
|
</span>
|
|
283
285
|
</td>
|
|
286
|
+
<td>
|
|
287
|
+
{r.last_rotated_at
|
|
288
|
+
? formatDateTime(r.last_rotated_at, language)
|
|
289
|
+
: "—"}
|
|
290
|
+
</td>
|
|
284
291
|
<td>
|
|
285
292
|
<button
|
|
286
293
|
type="button"
|
|
@@ -245,7 +245,6 @@ interface LettaConnectionSectionProps extends SectionProps {
|
|
|
245
245
|
function LettaConnectionSection(props: LettaConnectionSectionProps): ReactNode {
|
|
246
246
|
const { client } = useAIHub();
|
|
247
247
|
const [baseUrl, setBaseUrl] = useState(props.settings.letta_base_url ?? "");
|
|
248
|
-
const [adeUrl, setAdeUrl] = useState(props.settings.letta_ade_url ?? "");
|
|
249
248
|
// Empty string = leave-as-is; non-empty = replace.
|
|
250
249
|
const [apiKey, setApiKey] = useState("");
|
|
251
250
|
const [saving, setSaving] = useState(false);
|
|
@@ -265,9 +264,6 @@ function LettaConnectionSection(props: LettaConnectionSectionProps): ReactNode {
|
|
|
265
264
|
if (baseUrl !== (props.settings.letta_base_url ?? "")) {
|
|
266
265
|
body.lettaBaseUrl = baseUrl === "" ? null : baseUrl;
|
|
267
266
|
}
|
|
268
|
-
if (adeUrl !== (props.settings.letta_ade_url ?? "")) {
|
|
269
|
-
body.lettaAdeUrl = adeUrl === "" ? null : adeUrl;
|
|
270
|
-
}
|
|
271
267
|
if (apiKey !== "") body.lettaApiKey = apiKey;
|
|
272
268
|
const next = await client.patchAdminSettings(body);
|
|
273
269
|
props.onSaved(next);
|
|
@@ -289,7 +285,7 @@ function LettaConnectionSection(props: LettaConnectionSectionProps): ReactNode {
|
|
|
289
285
|
// Clears the DB column (sends `letta_api_key: ""`), which makes the
|
|
290
286
|
// server fall back to env.LETTA_API_KEY at runtime per the
|
|
291
287
|
// resolveApiKey precedence in letta/client.ts. Same code path as the
|
|
292
|
-
// base
|
|
288
|
+
// base URL nulling — only it targets the API-key column.
|
|
293
289
|
async function handleClearApiKeyOverride(): Promise<void> {
|
|
294
290
|
setSaving(true);
|
|
295
291
|
setFlash(null);
|
|
@@ -332,22 +328,6 @@ function LettaConnectionSection(props: LettaConnectionSectionProps): ReactNode {
|
|
|
332
328
|
disabled={saving}
|
|
333
329
|
/>
|
|
334
330
|
</label>
|
|
335
|
-
<label className="data-club-ai-hub-admin__form-field">
|
|
336
|
-
<span className="data-club-ai-hub-admin__form-label">
|
|
337
|
-
{props.texts.settingsLettaAdeUrlLabel}
|
|
338
|
-
</span>
|
|
339
|
-
<input
|
|
340
|
-
type="url"
|
|
341
|
-
className="data-club-ai-hub-admin__form-input"
|
|
342
|
-
data-testid="admin-settings-letta-ade-url"
|
|
343
|
-
value={adeUrl}
|
|
344
|
-
onChange={(event) => setAdeUrl(event.target.value)}
|
|
345
|
-
disabled={saving}
|
|
346
|
-
/>
|
|
347
|
-
<span className="data-club-ai-hub-admin__form-helper">
|
|
348
|
-
{props.texts.settingsLettaAdeUrlHelper}
|
|
349
|
-
</span>
|
|
350
|
-
</label>
|
|
351
331
|
<label className="data-club-ai-hub-admin__form-field">
|
|
352
332
|
<span className="data-club-ai-hub-admin__form-label">
|
|
353
333
|
{props.texts.settingsLettaApiKeyLabel}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* `user`-role caller the component returns `null` outright.
|
|
9
9
|
*
|
|
10
10
|
* The shell **renders its own `<ShellHeader />`** so the user-menu (log
|
|
11
|
-
* out, theme toggle,
|
|
11
|
+
* out, theme toggle, ← Back to chat) stays available
|
|
12
12
|
* from inside the admin view too — [00-shell.md] makes the shell header
|
|
13
13
|
* part of every authenticated page. The component is identical to the
|
|
14
14
|
* one ChatShell renders.
|
package/src/styles/CLASSES.md
CHANGED
|
@@ -177,7 +177,7 @@ block and modifier.
|
|
|
177
177
|
| `data-club-ai-hub__user-menu__header` | Header row inside the dropdown (username + role). |
|
|
178
178
|
| `data-club-ai-hub__user-menu__username-large` | Larger username text in the dropdown header. |
|
|
179
179
|
| `data-club-ai-hub__user-menu__role` | Role label below the username. |
|
|
180
|
-
| `data-club-ai-hub__user-menu__item` | A menu item (Sign out today; Theme / Admin
|
|
180
|
+
| `data-club-ai-hub__user-menu__item` | A menu item (Sign out today; Theme / Admin in Steps 10–11). |
|
|
181
181
|
|
|
182
182
|
### Agents picker — Step 9 (`ui-sketches/03-chat.md`)
|
|
183
183
|
|
package/src/styles/default.css
CHANGED
|
@@ -660,10 +660,6 @@
|
|
|
660
660
|
background-color: color-mix(in srgb, var(--dc-aih-accent) 12%, transparent);
|
|
661
661
|
}
|
|
662
662
|
|
|
663
|
-
.data-club-ai-hub__user-menu__item--link {
|
|
664
|
-
text-decoration: none;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
663
|
/* ── Language switcher (left of the user menu in the shell header) ────── */
|
|
668
664
|
|
|
669
665
|
.data-club-ai-hub__language-switcher {
|
package/src/util/relativeTime.ts
CHANGED
|
@@ -69,6 +69,25 @@ export function formatAbsoluteDate(
|
|
|
69
69
|
);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Formats an ISO timestamp as a short, locale-formatted **date + time**
|
|
74
|
+
* (e.g. `08/06/2026, 14:32`). Used where the exact moment matters — e.g. the
|
|
75
|
+
* admin rotating-messages "last rotation" column, where rotations can be
|
|
76
|
+
* hourly so a date alone wouldn't distinguish runs.
|
|
77
|
+
*
|
|
78
|
+
* @param iso - ISO timestamp. Returns the raw input for an unparseable value.
|
|
79
|
+
* @param language - the active UI language.
|
|
80
|
+
* @returns the localized date-time string.
|
|
81
|
+
*/
|
|
82
|
+
export function formatDateTime(iso: string, language: AIHubLanguage): string {
|
|
83
|
+
const ts = Date.parse(iso);
|
|
84
|
+
if (Number.isNaN(ts)) return iso;
|
|
85
|
+
return new Intl.DateTimeFormat(language, {
|
|
86
|
+
dateStyle: "short",
|
|
87
|
+
timeStyle: "short",
|
|
88
|
+
}).format(new Date(ts));
|
|
89
|
+
}
|
|
90
|
+
|
|
72
91
|
/**
|
|
73
92
|
* Day-granular relative label — `"today"` / `"yesterday"` / `"N days ago"`
|
|
74
93
|
* (localized). Used for the admin settings rotation stamp, which pairs an
|