@protobase/ui 0.2.0 → 0.3.1
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/app/app.js
CHANGED
|
@@ -28,7 +28,7 @@ const Routes = ({ workspace, sidebarMode, assistant }) => {
|
|
|
28
28
|
const { state, signOut } = useAuth();
|
|
29
29
|
if (state.kind !== "signed-in") throw new Error("The shell is only rendered for a signed-in user");
|
|
30
30
|
const { user } = state;
|
|
31
|
-
const { path, basePath, navigate } = useRouter();
|
|
31
|
+
const { path, params, basePath, navigate } = useRouter();
|
|
32
32
|
const route = matchRoute(path);
|
|
33
33
|
const search = useGlobalSearch();
|
|
34
34
|
const recent = useNavRecent(meta, basePath, route);
|
|
@@ -60,6 +60,7 @@ const Routes = ({ workspace, sidebarMode, assistant }) => {
|
|
|
60
60
|
const assistantPanel = assistantUrl && assistantOpen && /* @__PURE__ */ jsx(AssistantPanel, {
|
|
61
61
|
url: assistantUrl,
|
|
62
62
|
client: assistant,
|
|
63
|
+
page: params.size > 0 ? `${path}?${params}` : path,
|
|
63
64
|
onClose: () => setAssistantOpen(false)
|
|
64
65
|
});
|
|
65
66
|
const breadcrumb = breadcrumbFor({
|
|
@@ -12,28 +12,31 @@ const AssistantButton = ({ open, onToggle }) => /* @__PURE__ */ jsxs(Button, {
|
|
|
12
12
|
onClick: onToggle,
|
|
13
13
|
children: [/* @__PURE__ */ jsx(Sparkles, { className: "size-3.5" }), "Assistant"]
|
|
14
14
|
});
|
|
15
|
-
const ConnectedDock = ({ client, onClose }) => {
|
|
15
|
+
const ConnectedDock = ({ client, page, onClose }) => {
|
|
16
16
|
const { state, offline, error, send, act } = useAssistant(client);
|
|
17
17
|
return /* @__PURE__ */ jsx(AssistantDock, {
|
|
18
18
|
state,
|
|
19
19
|
offline,
|
|
20
20
|
error,
|
|
21
|
-
onSend: send,
|
|
21
|
+
onSend: (text) => send(text, page),
|
|
22
22
|
onAction: act,
|
|
23
23
|
onClose
|
|
24
24
|
});
|
|
25
25
|
};
|
|
26
|
-
const OwnDock = ({ url,
|
|
26
|
+
const OwnDock = ({ url, ...props }) => /* @__PURE__ */ jsx(ConnectedDock, {
|
|
27
27
|
client: useAssistantClient(url),
|
|
28
|
-
|
|
28
|
+
...props
|
|
29
29
|
});
|
|
30
|
-
/**
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* The dock for the backend `/meta` names, signed in as the user, sending each message with the `page` the user is on;
|
|
32
|
+
* `client` replaces it in stories and tests.
|
|
33
|
+
*/
|
|
34
|
+
const AssistantPanel = ({ url, client, ...props }) => client ? /* @__PURE__ */ jsx(ConnectedDock, {
|
|
32
35
|
client,
|
|
33
|
-
|
|
36
|
+
...props
|
|
34
37
|
}) : /* @__PURE__ */ jsx(OwnDock, {
|
|
35
38
|
url,
|
|
36
|
-
|
|
39
|
+
...props
|
|
37
40
|
});
|
|
38
41
|
//#endregion
|
|
39
42
|
export { AssistantButton, AssistantPanel };
|
package/dist/app/nav-recent.js
CHANGED
|
@@ -29,5 +29,10 @@ const recentRecords = (model, view, recent, rows, basePath, activeKey) => {
|
|
|
29
29
|
};
|
|
30
30
|
});
|
|
31
31
|
};
|
|
32
|
+
/** Whether a record read anew differs from its row in a sidebar group on the fields the group shows: the group is then out of date. */
|
|
33
|
+
const recentRowDiffers = (model, fields, rows, key, record) => {
|
|
34
|
+
const row = rows.find((entry) => encodeKey(recordKey(model, entry)) === key);
|
|
35
|
+
return row !== void 0 && fields.some((field) => !Object.is(row[field], record[field]));
|
|
36
|
+
};
|
|
32
37
|
//#endregion
|
|
33
|
-
export { recentRecords, recentRequest };
|
|
38
|
+
export { recentRecords, recentRequest, recentRowDiffers };
|
|
@@ -1,29 +1,56 @@
|
|
|
1
1
|
import { keys } from "../data/query-keys.js";
|
|
2
2
|
import { useClient } from "../data/api-provider.js";
|
|
3
3
|
import { userMenuResources } from "./user-menu-from-meta.js";
|
|
4
|
-
import { recentRecords, recentRequest } from "./nav-recent.js";
|
|
5
|
-
import {
|
|
4
|
+
import { recentRecords, recentRequest, recentRowDiffers } from "./nav-recent.js";
|
|
5
|
+
import { useEffect, useMemo } from "react";
|
|
6
|
+
import { useQueries, useQueryClient } from "@tanstack/react-query";
|
|
6
7
|
//#region src/app/use-nav-recent.ts
|
|
8
|
+
const groupKey = ({ model, request }) => keys.navRecent(model.name, request.filter, request.orderBy, request.pageSize);
|
|
9
|
+
/**
|
|
10
|
+
* Refetches a group when a record of its resource is read anew (a record page, or an app following one) and shows
|
|
11
|
+
* something else than the group's row of it, so the sidebar follows the record without waiting for a write or focus.
|
|
12
|
+
*/
|
|
13
|
+
const useFollowRecords = (groups) => {
|
|
14
|
+
const queryClient = useQueryClient();
|
|
15
|
+
useEffect(() => queryClient.getQueryCache().subscribe((event) => {
|
|
16
|
+
if (event.type !== "updated" || event.action.type !== "success") return;
|
|
17
|
+
const [scope, resource, key] = event.query.queryKey;
|
|
18
|
+
const record = event.action.data?.record;
|
|
19
|
+
if (scope !== "record" || typeof key !== "string" || !record) return;
|
|
20
|
+
for (const group of groups) {
|
|
21
|
+
if (group.model.name !== resource) continue;
|
|
22
|
+
const page = queryClient.getQueryData(groupKey(group));
|
|
23
|
+
if (page && recentRowDiffers(group.model, group.request.fields, page.items, key, record)) queryClient.invalidateQueries({
|
|
24
|
+
queryKey: groupKey(group),
|
|
25
|
+
exact: true
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}), [queryClient, groups]);
|
|
29
|
+
};
|
|
7
30
|
/**
|
|
8
31
|
* The records of every sidebar group (`nav.recent`), one list request each, under the `list` query key so that any
|
|
9
|
-
* write to the resource refreshes them. The server applies the caller's
|
|
32
|
+
* write to the resource refreshes them, as does reading one of their records anew. The server applies the caller's
|
|
33
|
+
* access, as on the list page.
|
|
10
34
|
*/
|
|
11
35
|
const useNavRecent = (meta, basePath, active) => {
|
|
12
36
|
const client = useClient();
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
model
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
37
|
+
const groups = useMemo(() => {
|
|
38
|
+
const inMenu = userMenuResources(meta);
|
|
39
|
+
return Object.values(meta.views).flatMap((view) => {
|
|
40
|
+
const model = meta.resources[view.resource];
|
|
41
|
+
const recent = view.nav?.recent;
|
|
42
|
+
return model && recent && !view.nav?.hidden && !inMenu.has(model.name) ? [{
|
|
43
|
+
model,
|
|
44
|
+
view,
|
|
45
|
+
recent,
|
|
46
|
+
request: recentRequest(model, view, recent)
|
|
47
|
+
}] : [];
|
|
48
|
+
});
|
|
49
|
+
}, [meta]);
|
|
50
|
+
useFollowRecords(groups);
|
|
51
|
+
const results = useQueries({ queries: groups.map((group) => ({
|
|
52
|
+
queryKey: groupKey(group),
|
|
53
|
+
queryFn: () => client.list(group.model.name, group.request),
|
|
27
54
|
refetchOnWindowFocus: true
|
|
28
55
|
})) });
|
|
29
56
|
return Object.fromEntries(groups.map(({ model, view, recent }, index) => {
|
|
@@ -5,7 +5,7 @@ export declare const useAssistant: (client: AssistantClient | undefined) => {
|
|
|
5
5
|
state: import("@protobase/schema").AssistantState;
|
|
6
6
|
offline: boolean;
|
|
7
7
|
error: string | undefined;
|
|
8
|
-
send: (text: string) => void | undefined;
|
|
8
|
+
send: (text: string, page?: string) => void | undefined;
|
|
9
9
|
act: (partId: string, actionId: string) => void | undefined;
|
|
10
10
|
};
|
|
11
11
|
//#endregion
|
|
@@ -19,7 +19,7 @@ const useAssistant = (client) => {
|
|
|
19
19
|
state,
|
|
20
20
|
offline,
|
|
21
21
|
error,
|
|
22
|
-
send: (text) => client && deliver(client.send(text)),
|
|
22
|
+
send: (text, page) => client && deliver(client.send(text, page)),
|
|
23
23
|
act: (partId, actionId) => client && deliver(client.act(partId, actionId))
|
|
24
24
|
};
|
|
25
25
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "The Protobase admin app and the React hooks and components for custom UI.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"react-dom": "^19.3.0",
|
|
39
39
|
"recharts": "^3.10.1",
|
|
40
40
|
"tailwindcss": "^4.3.3",
|
|
41
|
-
"@protobase/client": "0.
|
|
42
|
-
"@protobase/layout": "0.
|
|
43
|
-
"@protobase/schema": "0.
|
|
41
|
+
"@protobase/client": "0.3.1",
|
|
42
|
+
"@protobase/layout": "0.3.1",
|
|
43
|
+
"@protobase/schema": "0.3.1"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "tsdown"
|