@multiplatform.one/backoffice 7.34.2 → 7.34.4
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/cjs/chrome/BackofficeAwesomebar.cjs +7 -5
- package/dist/cjs/chrome/BackofficeAwesomebar.native.js +7 -5
- package/dist/cjs/chrome/BackofficeAwesomebar.native.js.map +1 -1
- package/dist/cjs/chrome/CommandPalette.cjs +4 -2
- package/dist/cjs/chrome/CommandPalette.native.js +4 -2
- package/dist/cjs/chrome/CommandPalette.native.js.map +1 -1
- package/dist/cjs/chrome/NotificationsButton.cjs +1 -0
- package/dist/cjs/chrome/NotificationsButton.native.js +1 -0
- package/dist/cjs/chrome/NotificationsButton.native.js.map +1 -1
- package/dist/cjs/chrome/UserMenu.cjs +1 -1
- package/dist/cjs/chrome/UserMenu.native.js +1 -1
- package/dist/cjs/chrome/UserMenu.native.js.map +1 -1
- package/dist/cjs/chrome/awesomebar-data.cjs +45 -18
- package/dist/cjs/chrome/awesomebar-data.native.js +45 -17
- package/dist/cjs/chrome/awesomebar-data.native.js.map +1 -1
- package/dist/cjs/chrome/command-palette-model.cjs +8 -7
- package/dist/cjs/chrome/command-palette-model.native.js +10 -7
- package/dist/cjs/chrome/command-palette-model.native.js.map +1 -1
- package/dist/cjs/chrome/notifications-data.cjs +2 -38
- package/dist/cjs/chrome/notifications-data.native.js +2 -39
- package/dist/cjs/chrome/notifications-data.native.js.map +1 -1
- package/dist/cjs/chrome/user-session.cjs +19 -11
- package/dist/cjs/chrome/user-session.native.js +20 -13
- package/dist/cjs/chrome/user-session.native.js.map +1 -1
- package/dist/cjs/list/DoctypeListScreen.cjs +11 -5
- package/dist/cjs/list/DoctypeListScreen.native.js +13 -5
- package/dist/cjs/list/DoctypeListScreen.native.js.map +1 -1
- package/dist/cjs/shared/sessionUser.cjs +89 -0
- package/dist/cjs/shared/sessionUser.native.js +96 -0
- package/dist/cjs/shared/sessionUser.native.js.map +1 -0
- package/dist/cjs/shared/useSessionUserId.cjs +2 -26
- package/dist/cjs/shared/useSessionUserId.native.js +2 -28
- package/dist/cjs/shared/useSessionUserId.native.js.map +1 -1
- package/dist/cjs/shell/BackofficeShell.cjs +6 -2
- package/dist/cjs/shell/BackofficeShell.native.js +6 -2
- package/dist/cjs/shell/BackofficeShell.native.js.map +1 -1
- package/dist/cjs/shell/useWorkspaces.cjs +2 -31
- package/dist/cjs/shell/useWorkspaces.native.js +2 -35
- package/dist/cjs/shell/useWorkspaces.native.js.map +1 -1
- package/dist/esm/chrome/BackofficeAwesomebar.mjs +7 -5
- package/dist/esm/chrome/BackofficeAwesomebar.mjs.map +1 -1
- package/dist/esm/chrome/BackofficeAwesomebar.native.js +7 -5
- package/dist/esm/chrome/BackofficeAwesomebar.native.js.map +1 -1
- package/dist/esm/chrome/CommandPalette.mjs +4 -2
- package/dist/esm/chrome/CommandPalette.mjs.map +1 -1
- package/dist/esm/chrome/CommandPalette.native.js +4 -2
- package/dist/esm/chrome/CommandPalette.native.js.map +1 -1
- package/dist/esm/chrome/NotificationsButton.mjs +1 -0
- package/dist/esm/chrome/NotificationsButton.mjs.map +1 -1
- package/dist/esm/chrome/NotificationsButton.native.js +1 -0
- package/dist/esm/chrome/NotificationsButton.native.js.map +1 -1
- package/dist/esm/chrome/UserMenu.mjs +1 -1
- package/dist/esm/chrome/UserMenu.mjs.map +1 -1
- package/dist/esm/chrome/UserMenu.native.js +1 -1
- package/dist/esm/chrome/UserMenu.native.js.map +1 -1
- package/dist/esm/chrome/awesomebar-data.mjs +45 -18
- package/dist/esm/chrome/awesomebar-data.mjs.map +1 -1
- package/dist/esm/chrome/awesomebar-data.native.js +45 -17
- package/dist/esm/chrome/awesomebar-data.native.js.map +1 -1
- package/dist/esm/chrome/command-palette-model.mjs +8 -7
- package/dist/esm/chrome/command-palette-model.mjs.map +1 -1
- package/dist/esm/chrome/command-palette-model.native.js +10 -7
- package/dist/esm/chrome/command-palette-model.native.js.map +1 -1
- package/dist/esm/chrome/notifications-data.mjs +3 -39
- package/dist/esm/chrome/notifications-data.mjs.map +1 -1
- package/dist/esm/chrome/notifications-data.native.js +3 -40
- package/dist/esm/chrome/notifications-data.native.js.map +1 -1
- package/dist/esm/chrome/user-session.mjs +19 -11
- package/dist/esm/chrome/user-session.mjs.map +1 -1
- package/dist/esm/chrome/user-session.native.js +20 -13
- package/dist/esm/chrome/user-session.native.js.map +1 -1
- package/dist/esm/list/DoctypeListScreen.mjs +12 -6
- package/dist/esm/list/DoctypeListScreen.mjs.map +1 -1
- package/dist/esm/list/DoctypeListScreen.native.js +14 -6
- package/dist/esm/list/DoctypeListScreen.native.js.map +1 -1
- package/dist/esm/shared/sessionUser.mjs +61 -0
- package/dist/esm/shared/sessionUser.mjs.map +1 -0
- package/dist/esm/shared/sessionUser.native.js +65 -0
- package/dist/esm/shared/sessionUser.native.js.map +1 -0
- package/dist/esm/shared/useSessionUserId.mjs +2 -26
- package/dist/esm/shared/useSessionUserId.mjs.map +1 -1
- package/dist/esm/shared/useSessionUserId.native.js +2 -28
- package/dist/esm/shared/useSessionUserId.native.js.map +1 -1
- package/dist/esm/shell/BackofficeShell.mjs +6 -2
- package/dist/esm/shell/BackofficeShell.mjs.map +1 -1
- package/dist/esm/shell/BackofficeShell.native.js +6 -2
- package/dist/esm/shell/BackofficeShell.native.js.map +1 -1
- package/dist/esm/shell/useWorkspaces.mjs +4 -33
- package/dist/esm/shell/useWorkspaces.mjs.map +1 -1
- package/dist/esm/shell/useWorkspaces.native.js +4 -37
- package/dist/esm/shell/useWorkspaces.native.js.map +1 -1
- package/dist/jsx/chrome/BackofficeAwesomebar.mjs +7 -5
- package/dist/jsx/chrome/BackofficeAwesomebar.mjs.map +1 -1
- package/dist/jsx/chrome/BackofficeAwesomebar.native.js +7 -5
- package/dist/jsx/chrome/BackofficeAwesomebar.native.js.map +1 -1
- package/dist/jsx/chrome/CommandPalette.mjs +4 -2
- package/dist/jsx/chrome/CommandPalette.mjs.map +1 -1
- package/dist/jsx/chrome/CommandPalette.native.js +4 -2
- package/dist/jsx/chrome/CommandPalette.native.js.map +1 -1
- package/dist/jsx/chrome/NotificationsButton.mjs +1 -0
- package/dist/jsx/chrome/NotificationsButton.mjs.map +1 -1
- package/dist/jsx/chrome/NotificationsButton.native.js +1 -0
- package/dist/jsx/chrome/NotificationsButton.native.js.map +1 -1
- package/dist/jsx/chrome/UserMenu.mjs +1 -1
- package/dist/jsx/chrome/UserMenu.mjs.map +1 -1
- package/dist/jsx/chrome/UserMenu.native.js +1 -1
- package/dist/jsx/chrome/UserMenu.native.js.map +1 -1
- package/dist/jsx/chrome/awesomebar-data.mjs +45 -18
- package/dist/jsx/chrome/awesomebar-data.mjs.map +1 -1
- package/dist/jsx/chrome/awesomebar-data.native.js +45 -17
- package/dist/jsx/chrome/awesomebar-data.native.js.map +1 -1
- package/dist/jsx/chrome/command-palette-model.mjs +8 -7
- package/dist/jsx/chrome/command-palette-model.mjs.map +1 -1
- package/dist/jsx/chrome/command-palette-model.native.js +10 -7
- package/dist/jsx/chrome/command-palette-model.native.js.map +1 -1
- package/dist/jsx/chrome/notifications-data.mjs +3 -39
- package/dist/jsx/chrome/notifications-data.mjs.map +1 -1
- package/dist/jsx/chrome/notifications-data.native.js +2 -39
- package/dist/jsx/chrome/notifications-data.native.js.map +1 -1
- package/dist/jsx/chrome/user-session.mjs +19 -11
- package/dist/jsx/chrome/user-session.mjs.map +1 -1
- package/dist/jsx/chrome/user-session.native.js +20 -13
- package/dist/jsx/chrome/user-session.native.js.map +1 -1
- package/dist/jsx/list/DoctypeListScreen.mjs +12 -6
- package/dist/jsx/list/DoctypeListScreen.mjs.map +1 -1
- package/dist/jsx/list/DoctypeListScreen.native.js +13 -5
- package/dist/jsx/list/DoctypeListScreen.native.js.map +1 -1
- package/dist/jsx/shared/sessionUser.mjs +61 -0
- package/dist/jsx/shared/sessionUser.mjs.map +1 -0
- package/dist/jsx/shared/sessionUser.native.js +96 -0
- package/dist/jsx/shared/sessionUser.native.js.map +1 -0
- package/dist/jsx/shared/useSessionUserId.mjs +2 -26
- package/dist/jsx/shared/useSessionUserId.mjs.map +1 -1
- package/dist/jsx/shared/useSessionUserId.native.js +2 -28
- package/dist/jsx/shared/useSessionUserId.native.js.map +1 -1
- package/dist/jsx/shell/BackofficeShell.mjs +6 -2
- package/dist/jsx/shell/BackofficeShell.mjs.map +1 -1
- package/dist/jsx/shell/BackofficeShell.native.js +6 -2
- package/dist/jsx/shell/BackofficeShell.native.js.map +1 -1
- package/dist/jsx/shell/useWorkspaces.mjs +4 -33
- package/dist/jsx/shell/useWorkspaces.mjs.map +1 -1
- package/dist/jsx/shell/useWorkspaces.native.js +2 -35
- package/dist/jsx/shell/useWorkspaces.native.js.map +1 -1
- package/package.json +12 -12
- package/src/chrome/BackofficeAwesomebar.tsx +11 -6
- package/src/chrome/CommandPalette.spec.tsx +12 -0
- package/src/chrome/CommandPalette.tsx +9 -7
- package/src/chrome/NotificationsButton.tsx +1 -0
- package/src/chrome/UserMenu.screen.spec.tsx +1 -1
- package/src/chrome/UserMenu.tsx +1 -1
- package/src/chrome/awesomebar-data.tsx +28 -19
- package/src/chrome/command-palette-model.spec.tsx +148 -0
- package/src/chrome/command-palette-model.ts +13 -5
- package/src/chrome/notifications-data.tsx +3 -50
- package/src/chrome/user-session.tsx +20 -15
- package/src/form/DocFormScreen.guest.screen.spec.tsx +3 -3
- package/src/list/DoctypeListScreen.guest.screen.spec.tsx +82 -3
- package/src/list/DoctypeListScreen.tsx +19 -13
- package/src/report/DoctypeReportView.screen.spec.tsx +12 -0
- package/src/shared/sessionUser.spec.tsx +106 -0
- package/src/shared/sessionUser.ts +59 -0
- package/src/shared/useSessionUserId.ts +2 -40
- package/src/shell/BackofficeShell.spec.tsx +31 -2
- package/src/shell/BackofficeShell.tsx +5 -3
- package/src/shell/useWorkspaces.ts +5 -56
- package/types/chrome/BackofficeAwesomebar.d.ts.map +1 -1
- package/types/chrome/NotificationsButton.d.ts.map +1 -1
- package/types/chrome/awesomebar-data.d.ts +4 -3
- package/types/chrome/awesomebar-data.d.ts.map +1 -1
- package/types/chrome/command-palette-model.d.ts +1 -0
- package/types/chrome/command-palette-model.d.ts.map +1 -1
- package/types/chrome/notifications-data.d.ts.map +1 -1
- package/types/chrome/user-session.d.ts.map +1 -1
- package/types/list/DoctypeListScreen.d.ts.map +1 -1
- package/types/shared/sessionUser.d.ts +9 -0
- package/types/shared/sessionUser.d.ts.map +1 -0
- package/types/shared/useSessionUserId.d.ts +0 -4
- package/types/shared/useSessionUserId.d.ts.map +1 -1
- package/types/shell/BackofficeShell.d.ts.map +1 -1
- package/types/shell/useWorkspaces.d.ts +0 -5
- package/types/shell/useWorkspaces.d.ts.map +1 -1
|
@@ -1,39 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useFrappeCollection, useLiveQuery } from "@multiplatform.one/frappe";
|
|
2
2
|
import { useFrappeConfig } from "@multiplatform.one/frappe-ui";
|
|
3
|
-
import {
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { useSessionUserId } from "../shared/useSessionUserId.mjs";
|
|
4
5
|
const WORKSPACE_FIELDS = ["name", "title", "icon", "parent_page", "public", "for_user", "sequence_id", "module", "is_hidden"];
|
|
5
|
-
const sessionUserCache = /* @__PURE__ */new Map();
|
|
6
|
-
async function fetchSessionUser(baseURL, auth) {
|
|
7
|
-
const client = new HttpClient(baseURL, auth);
|
|
8
|
-
const res = await client.get("/api/method/frappe.auth.get_logged_user");
|
|
9
|
-
const user = res.payload?.message;
|
|
10
|
-
return user && user !== "Guest" ? user : void 0;
|
|
11
|
-
}
|
|
12
6
|
function useSessionUser() {
|
|
13
|
-
|
|
14
|
-
const baseURL = config?.baseURL;
|
|
15
|
-
const auth = config?.auth;
|
|
16
|
-
const fixtures = config?.fixtures;
|
|
17
|
-
const [user, setUser] = useState(void 0);
|
|
18
|
-
useEffect(() => {
|
|
19
|
-
if (fixtures || !baseURL) return;
|
|
20
|
-
let cancelled = false;
|
|
21
|
-
let promise = sessionUserCache.get(baseURL);
|
|
22
|
-
if (!promise) {
|
|
23
|
-
promise = fetchSessionUser(baseURL, auth).catch(() => {
|
|
24
|
-
sessionUserCache.delete(baseURL);
|
|
25
|
-
return void 0;
|
|
26
|
-
});
|
|
27
|
-
sessionUserCache.set(baseURL, promise);
|
|
28
|
-
}
|
|
29
|
-
promise.then(value => {
|
|
30
|
-
if (!cancelled) setUser(value);
|
|
31
|
-
});
|
|
32
|
-
return () => {
|
|
33
|
-
cancelled = true;
|
|
34
|
-
};
|
|
35
|
-
}, [baseURL, fixtures]);
|
|
36
|
-
return user;
|
|
7
|
+
return useSessionUserId();
|
|
37
8
|
}
|
|
38
9
|
function bySequence(a, b) {
|
|
39
10
|
const seqA = Number(a.sequence_id ?? 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["
|
|
1
|
+
{"version":3,"names":["useFrappeCollection","useLiveQuery","useFrappeConfig","useMemo","useSessionUserId","WORKSPACE_FIELDS","useSessionUser","bySequence","a","b","seqA","Number","sequence_id","seqB","title","name","localeCompare","useWorkspaces","config","sessionUser","connectionConfig","baseURL","socketPort","siteName","socketBaseURL","auth","syncOptions","socketOptions","fixtures","collection","fields","data","rawData","isLoading","q","from","_workspace","all","filter","w","is_hidden","sort","privateWorkspaces","public","for_user","publicWorkspaces","Boolean","publicNames","Set","map","publicRoots","childrenByParent","Map","workspace","parent_page","has","siblings","get","push","set","workspaces","useDefaultWorkspace"],"sources":["../../../src/shell/useWorkspaces.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -32,43 +32,10 @@ module.exports = __toCommonJS(useWorkspaces_exports);
|
|
|
32
32
|
var import_frappe = require("@multiplatform.one/frappe");
|
|
33
33
|
var import_frappe_ui = require("@multiplatform.one/frappe-ui");
|
|
34
34
|
var import_react = require("react");
|
|
35
|
+
var import_useSessionUserId = require("../shared/useSessionUserId.native.js");
|
|
35
36
|
var WORKSPACE_FIELDS = ["name", "title", "icon", "parent_page", "public", "for_user", "sequence_id", "module", "is_hidden"];
|
|
36
|
-
var sessionUserCache = /* @__PURE__ */new Map();
|
|
37
|
-
async function fetchSessionUser(baseURL, auth) {
|
|
38
|
-
var _res_payload;
|
|
39
|
-
var client = new import_frappe.HttpClient(baseURL, auth);
|
|
40
|
-
var res = await client.get("/api/method/frappe.auth.get_logged_user");
|
|
41
|
-
var user = (_res_payload = res.payload) === null || _res_payload === void 0 ? void 0 : _res_payload.message;
|
|
42
|
-
return user && user !== "Guest" ? user : void 0;
|
|
43
|
-
}
|
|
44
37
|
function useSessionUser() {
|
|
45
|
-
|
|
46
|
-
var baseURL = config === null || config === void 0 ? void 0 : config.baseURL;
|
|
47
|
-
var auth = config === null || config === void 0 ? void 0 : config.auth;
|
|
48
|
-
var fixtures = config === null || config === void 0 ? void 0 : config.fixtures;
|
|
49
|
-
var [user, setUser] = (0, import_react.useState)(void 0);
|
|
50
|
-
(0, import_react.useEffect)(function () {
|
|
51
|
-
if (fixtures || !baseURL) return;
|
|
52
|
-
var cancelled = false;
|
|
53
|
-
var promise = sessionUserCache.get(baseURL);
|
|
54
|
-
if (!promise) {
|
|
55
|
-
promise = fetchSessionUser(baseURL, auth).catch(function () {
|
|
56
|
-
sessionUserCache.delete(baseURL);
|
|
57
|
-
return void 0;
|
|
58
|
-
});
|
|
59
|
-
sessionUserCache.set(baseURL, promise);
|
|
60
|
-
}
|
|
61
|
-
promise.then(function (value) {
|
|
62
|
-
if (!cancelled) setUser(value);
|
|
63
|
-
});
|
|
64
|
-
return function () {
|
|
65
|
-
cancelled = true;
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
// `auth` is session-stable; the cache is keyed by baseURL.
|
|
69
|
-
// oxlint-disable-next-line exhaustive-deps
|
|
70
|
-
[baseURL, fixtures]);
|
|
71
|
-
return user;
|
|
38
|
+
return (0, import_useSessionUserId.useSessionUserId)();
|
|
72
39
|
}
|
|
73
40
|
function bySequence(a, b) {
|
|
74
41
|
var _a_sequence_id, _b_sequence_id;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["__defProp","Object","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toCommonJS","mod","value","useWorkspaces_exports","useDefaultWorkspace","useSessionUser","useWorkspaces","module","exports","import_frappe","require","import_frappe_ui","import_react","
|
|
1
|
+
{"version":3,"names":["__defProp","Object","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toCommonJS","mod","value","useWorkspaces_exports","useDefaultWorkspace","useSessionUser","useWorkspaces","module","exports","import_frappe","require","import_frappe_ui","import_react","import_useSessionUserId","WORKSPACE_FIELDS","useSessionUserId","bySequence","a","b","_a_sequence_id","_b_sequence_id","seqA","Number","sequence_id","seqB","title","localeCompare","config","useFrappeConfig","sessionUser","connectionConfig","useMemo","baseURL","socketPort","siteName","socketBaseURL","auth","syncOptions","socketOptions","fixtures","collection","useFrappeCollection","fields","data","rawData","isLoading","useLiveQuery","q","_workspace","filter","w"],"sources":["../../../src/shell/useWorkspaces.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiplatform.one/backoffice",
|
|
3
|
-
"version": "7.34.
|
|
3
|
+
"version": "7.34.4",
|
|
4
4
|
"description": "Embeddable Frappe desk recreation (backoffice) for the multiplatform.one stack",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"backoffice",
|
|
@@ -71,21 +71,21 @@
|
|
|
71
71
|
"access": "public"
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
|
-
"@multiplatform.one/components": "7.34.
|
|
75
|
-
"@multiplatform.one/forms": "7.34.
|
|
76
|
-
"@multiplatform.one/frappe": "7.34.
|
|
77
|
-
"@multiplatform.one/frappe-ui": "7.34.
|
|
78
|
-
"@multiplatform.one/i18n": "7.34.
|
|
79
|
-
"@multiplatform.one/platform": "7.34.
|
|
80
|
-
"@multiplatform.one/store": "7.34.
|
|
81
|
-
"@multiplatform.one/table": "7.34.
|
|
82
|
-
"@multiplatform.one/theme": "7.34.
|
|
74
|
+
"@multiplatform.one/components": "^7.34.4",
|
|
75
|
+
"@multiplatform.one/forms": "^7.34.4",
|
|
76
|
+
"@multiplatform.one/frappe": "^7.34.4",
|
|
77
|
+
"@multiplatform.one/frappe-ui": "^7.34.4",
|
|
78
|
+
"@multiplatform.one/i18n": "^7.34.4",
|
|
79
|
+
"@multiplatform.one/platform": "^7.34.4",
|
|
80
|
+
"@multiplatform.one/store": "^7.34.4",
|
|
81
|
+
"@multiplatform.one/table": "^7.34.4",
|
|
82
|
+
"@multiplatform.one/theme": "^7.34.4",
|
|
83
83
|
"@phosphor-icons/react": "^2.1.10",
|
|
84
84
|
"tamagui": "2.7.6"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
|
-
"@multiplatform.one/config": "7.34.
|
|
88
|
-
"@multiplatform.one/test-utils": "7.34.
|
|
87
|
+
"@multiplatform.one/config": "7.34.4",
|
|
88
|
+
"@multiplatform.one/test-utils": "7.34.4",
|
|
89
89
|
"@tamagui/build": "2.7.6",
|
|
90
90
|
"@testing-library/react": "^16.3.3",
|
|
91
91
|
"@types/react-dom": "^19.2.3",
|
|
@@ -54,9 +54,13 @@ export function BackofficeAwesomebar({ focusRef }: BackofficeAwesomebarProps = {
|
|
|
54
54
|
<>
|
|
55
55
|
<Button
|
|
56
56
|
size="$3"
|
|
57
|
-
flex={1}
|
|
57
|
+
flex={isCompact ? undefined : 1}
|
|
58
|
+
flexShrink={0}
|
|
59
|
+
circular={isCompact}
|
|
60
|
+
chromeless={isCompact}
|
|
58
61
|
minWidth={0}
|
|
59
|
-
justifyContent="flex-start"
|
|
62
|
+
justifyContent={isCompact ? "center" : "flex-start"}
|
|
63
|
+
icon={MagnifyingGlassIcon}
|
|
60
64
|
aria-label={label}
|
|
61
65
|
aria-haspopup="dialog"
|
|
62
66
|
aria-expanded={open}
|
|
@@ -64,10 +68,11 @@ export function BackofficeAwesomebar({ focusRef }: BackofficeAwesomebarProps = {
|
|
|
64
68
|
onPress={() => setOpen(true)}
|
|
65
69
|
data-testid="backoffice-awesomebar"
|
|
66
70
|
>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
{!isCompact && (
|
|
72
|
+
<Text flex={1} minWidth={0} numberOfLines={1} textAlign="left" color="$color10">
|
|
73
|
+
{label}
|
|
74
|
+
</Text>
|
|
75
|
+
)}
|
|
71
76
|
{showShortcutHint && (
|
|
72
77
|
<XStack
|
|
73
78
|
borderWidth={1}
|
|
@@ -358,3 +358,15 @@ describe("CommandPalette standalone (story shape)", () => {
|
|
|
358
358
|
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
359
359
|
});
|
|
360
360
|
});
|
|
361
|
+
|
|
362
|
+
it("shows search unavailable while local navigation remains", async () => {
|
|
363
|
+
vi.stubGlobal("fetch", async () =>
|
|
364
|
+
Response.json({ exc_type: "PermissionError" }, { status: 403 }),
|
|
365
|
+
);
|
|
366
|
+
renderShell();
|
|
367
|
+
const { input } = await openPalette();
|
|
368
|
+
fireEvent.change(input, { target: { value: "todo" } });
|
|
369
|
+
await waitFor(() => expect(screen.getByText("Search unavailable")).toBeTruthy());
|
|
370
|
+
findOption("Todos");
|
|
371
|
+
expect(screen.queryByTestId("backoffice-palette-section-documents")).toBeNull();
|
|
372
|
+
});
|
|
@@ -454,7 +454,7 @@ function PaletteResults({
|
|
|
454
454
|
// (combobox focus stays on the search input). Accent + readable ink
|
|
455
455
|
// (LC-05) so selected + unselected stay one emphasis system.
|
|
456
456
|
const onAccent = useReadableTextOn("$accentBackground");
|
|
457
|
-
const { sections, searching, hasQuery, noResults, query } = model;
|
|
457
|
+
const { sections, searching, searchUnavailable, hasQuery, noResults, query } = model;
|
|
458
458
|
|
|
459
459
|
let flatIndex = -1;
|
|
460
460
|
|
|
@@ -565,13 +565,15 @@ function PaletteResults({
|
|
|
565
565
|
</XStack>
|
|
566
566
|
)}
|
|
567
567
|
|
|
568
|
-
{noResults && (
|
|
568
|
+
{(noResults || (hasQuery && searchUnavailable)) && (
|
|
569
569
|
<XStack paddingHorizontal="$3" paddingVertical="$3">
|
|
570
|
-
<Text fontSize="$2" color="$color10" {...knobProps.body}>
|
|
571
|
-
{
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
570
|
+
<Text role="status" fontSize="$2" color="$color10" {...knobProps.body}>
|
|
571
|
+
{searchUnavailable
|
|
572
|
+
? t("awesomebar.searchUnavailable", "Search unavailable")
|
|
573
|
+
: t("awesomebar.noResults", {
|
|
574
|
+
defaultValue: "No results for “{{query}}”",
|
|
575
|
+
query: model.query,
|
|
576
|
+
})}
|
|
575
577
|
</Text>
|
|
576
578
|
</XStack>
|
|
577
579
|
)}
|
|
@@ -77,7 +77,7 @@ describe("UserMenu", () => {
|
|
|
77
77
|
it("disables the profile items while no session user resolves", async () => {
|
|
78
78
|
loggedUser = null;
|
|
79
79
|
renderMenu();
|
|
80
|
-
await
|
|
80
|
+
await screen.findByText("Guest");
|
|
81
81
|
await waitFor(() => expect(item("My Profile").getAttribute("aria-disabled")).toBe("true"));
|
|
82
82
|
expect(item("My Settings").getAttribute("aria-disabled")).toBe("true");
|
|
83
83
|
expect(screen.queryByText("Jane Cooper")).toBeNull();
|
package/src/chrome/UserMenu.tsx
CHANGED
|
@@ -34,7 +34,7 @@ export function UserMenu({ defaultOpen }: UserMenuProps = {}) {
|
|
|
34
34
|
const app = useFrappeApp();
|
|
35
35
|
const frappeConfig = useFrappeConfig();
|
|
36
36
|
const { info } = useSessionUserInfo();
|
|
37
|
-
const sessionUser = info?.user;
|
|
37
|
+
const sessionUser = info?.user === "Guest" ? undefined : info?.user;
|
|
38
38
|
|
|
39
39
|
const handleLogout = useCallback(async () => {
|
|
40
40
|
try {
|
|
@@ -92,7 +92,7 @@ export interface AwesomebarCatalogState {
|
|
|
92
92
|
* honored in stories/tests), fetched once per session. SSR-safe: the fetch
|
|
93
93
|
* runs in an effect. Without a provider the catalog stays empty.
|
|
94
94
|
*/
|
|
95
|
-
export function useAwesomebarCatalog(): AwesomebarCatalogState {
|
|
95
|
+
export function useAwesomebarCatalog(enabled = true): AwesomebarCatalogState {
|
|
96
96
|
const config = useFrappeConfig();
|
|
97
97
|
const baseURL = config?.baseURL;
|
|
98
98
|
const fixtures = config?.fixtures;
|
|
@@ -102,7 +102,7 @@ export function useAwesomebarCatalog(): AwesomebarCatalogState {
|
|
|
102
102
|
const [error, setError] = useState(false);
|
|
103
103
|
|
|
104
104
|
useEffect(() => {
|
|
105
|
-
if (!baseURL) return;
|
|
105
|
+
if (!baseURL || !enabled) return;
|
|
106
106
|
let cancelled = false;
|
|
107
107
|
setError(false);
|
|
108
108
|
getCatalog({ baseURL, auth, fixtures })
|
|
@@ -118,7 +118,7 @@ export function useAwesomebarCatalog(): AwesomebarCatalogState {
|
|
|
118
118
|
// `auth` is intentionally not a dependency: the cache is keyed by
|
|
119
119
|
// baseURL/fixtures and provider auth is stable for the session.
|
|
120
120
|
// oxlint-disable-next-line exhaustive-deps
|
|
121
|
-
}, [baseURL, fixtures]);
|
|
121
|
+
}, [baseURL, fixtures, enabled]);
|
|
122
122
|
|
|
123
123
|
return { catalog, error };
|
|
124
124
|
}
|
|
@@ -135,14 +135,15 @@ export interface GlobalSearchHit {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
export interface GlobalSearchState {
|
|
138
|
+
query: string;
|
|
139
|
+
error: boolean;
|
|
138
140
|
hits: GlobalSearchHit[];
|
|
139
141
|
searching: boolean;
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
/**
|
|
143
145
|
* Document hits from the desk full-text index for an (already debounced)
|
|
144
|
-
* query. Empty query clears without fetching. Failures
|
|
145
|
-
* the nav-command and workspace sections still render.
|
|
146
|
+
* query. Empty query clears without fetching. Failures preserve local navigation.
|
|
146
147
|
*/
|
|
147
148
|
export function useGlobalSearch(query: string, limit = 10): GlobalSearchState {
|
|
148
149
|
const config = useFrappeConfig();
|
|
@@ -158,17 +159,17 @@ export function useGlobalSearch(query: string, limit = 10): GlobalSearchState {
|
|
|
158
159
|
[baseURL, authKey],
|
|
159
160
|
);
|
|
160
161
|
|
|
161
|
-
const [
|
|
162
|
-
|
|
162
|
+
const [result, setResult] = useState<GlobalSearchState & { client?: HttpClient }>({
|
|
163
|
+
query: "",
|
|
164
|
+
hits: [],
|
|
165
|
+
searching: false,
|
|
166
|
+
error: false,
|
|
167
|
+
});
|
|
163
168
|
|
|
164
169
|
useEffect(() => {
|
|
165
|
-
if (!query || !client)
|
|
166
|
-
setHits([]);
|
|
167
|
-
setSearching(false);
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
+
if (!query || !client) return;
|
|
170
171
|
let cancelled = false;
|
|
171
|
-
|
|
172
|
+
setResult({ client, query, hits: [], searching: true, error: false });
|
|
172
173
|
client
|
|
173
174
|
.get<{ message?: GlobalSearchHit[] }>("/api/method/frappe.utils.global_search.search", {
|
|
174
175
|
text: query,
|
|
@@ -176,20 +177,28 @@ export function useGlobalSearch(query: string, limit = 10): GlobalSearchState {
|
|
|
176
177
|
limit,
|
|
177
178
|
})
|
|
178
179
|
.then((response) => {
|
|
179
|
-
if (!cancelled)
|
|
180
|
+
if (!cancelled)
|
|
181
|
+
setResult({
|
|
182
|
+
client,
|
|
183
|
+
query,
|
|
184
|
+
hits: response.payload?.message ?? [],
|
|
185
|
+
searching: false,
|
|
186
|
+
error: false,
|
|
187
|
+
});
|
|
180
188
|
})
|
|
181
189
|
.catch(() => {
|
|
182
|
-
if (!cancelled)
|
|
183
|
-
})
|
|
184
|
-
.finally(() => {
|
|
185
|
-
if (!cancelled) setSearching(false);
|
|
190
|
+
if (!cancelled) setResult({ client, query, hits: [], searching: false, error: true });
|
|
186
191
|
});
|
|
187
192
|
return () => {
|
|
188
193
|
cancelled = true;
|
|
189
194
|
};
|
|
190
195
|
}, [client, query, limit]);
|
|
191
196
|
|
|
192
|
-
return { hits, searching };
|
|
197
|
+
if (!query || !client) return { query, hits: [], searching: false, error: false };
|
|
198
|
+
if (result.query !== query || result.client !== client) {
|
|
199
|
+
return { query, hits: [], searching: true, error: false };
|
|
200
|
+
}
|
|
201
|
+
return result;
|
|
193
202
|
}
|
|
194
203
|
|
|
195
204
|
/**
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { act, renderHook, waitFor } from "@testing-library/react";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { useCommandPaletteModel } from "./command-palette-model";
|
|
4
|
+
|
|
5
|
+
const provider = vi.hoisted(() => ({ baseURL: "" }));
|
|
6
|
+
vi.mock("@multiplatform.one/frappe-ui", () => ({ useFrappeConfig: () => provider }));
|
|
7
|
+
vi.mock("../config/BackofficeProvider", () => ({
|
|
8
|
+
useBackofficePaths: () => ({
|
|
9
|
+
workspace: (name: string) => `/workspace/${name}`,
|
|
10
|
+
list: (name: string) => `/list/${name}`,
|
|
11
|
+
form: (doctype: string, name: string) => `/form/${doctype}/${name}`,
|
|
12
|
+
newDoc: (name: string) => `/new/${name}`,
|
|
13
|
+
view: (name: string, view: string) => `/view/${name}/${view}`,
|
|
14
|
+
}),
|
|
15
|
+
useBackofficeCapabilities: () => ({ newDoc: { enabled: false }, views: { enabled: false } }),
|
|
16
|
+
useIsDoctypeAllowed: () => () => true,
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
let run = 0;
|
|
20
|
+
let catalogReads: string[];
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
provider.baseURL = `https://palette-model-${++run}.test`;
|
|
23
|
+
catalogReads = [];
|
|
24
|
+
vi.stubGlobal("fetch", async (input: string | URL | Request) => {
|
|
25
|
+
const url = new URL(String(input instanceof Request ? input.url : input));
|
|
26
|
+
catalogReads.push(url.pathname);
|
|
27
|
+
return Response.json(
|
|
28
|
+
url.pathname.includes("Workspace")
|
|
29
|
+
? { data: [{ name: "Sales", title: "Sales" }] }
|
|
30
|
+
: { message: [{ name: "ToDo" }] },
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
afterEach(() => vi.unstubAllGlobals());
|
|
35
|
+
|
|
36
|
+
describe("command palette catalog", () => {
|
|
37
|
+
it("waits until opened and shares the catalog on reopening", async () => {
|
|
38
|
+
const { rerender } = renderHook(
|
|
39
|
+
({ open }) => useCommandPaletteModel({ query: "", debouncedQuery: "", open }),
|
|
40
|
+
{
|
|
41
|
+
initialProps: { open: false },
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
await act(async () => {});
|
|
45
|
+
expect(catalogReads).toEqual([]);
|
|
46
|
+
rerender({ open: true });
|
|
47
|
+
await waitFor(() => expect(catalogReads).toHaveLength(2));
|
|
48
|
+
expect(catalogReads.filter((path) => path.includes("Workspace"))).toHaveLength(1);
|
|
49
|
+
expect(catalogReads.filter((path) => path.includes("get_list"))).toHaveLength(1);
|
|
50
|
+
rerender({ open: false });
|
|
51
|
+
rerender({ open: true });
|
|
52
|
+
await act(async () => {});
|
|
53
|
+
expect(catalogReads).toHaveLength(2);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe("command palette search state", () => {
|
|
58
|
+
it("hides old hits during debounce while the current query is pending", async () => {
|
|
59
|
+
const answers = new Map<string, (response: Response) => void>();
|
|
60
|
+
vi.stubGlobal("fetch", (input: string | URL | Request) => {
|
|
61
|
+
const url = new URL(String(input instanceof Request ? input.url : input));
|
|
62
|
+
if (url.pathname.includes("global_search")) {
|
|
63
|
+
return new Promise<Response>((resolve) =>
|
|
64
|
+
answers.set(url.searchParams.get("text") ?? "", resolve),
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return Promise.resolve(Response.json({ data: [], message: [] }));
|
|
68
|
+
});
|
|
69
|
+
const { result, rerender } = renderHook(
|
|
70
|
+
({ query, debouncedQuery }) => useCommandPaletteModel({ query, debouncedQuery, open: true }),
|
|
71
|
+
{
|
|
72
|
+
initialProps: { query: "A", debouncedQuery: "" },
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
expect(result.current.searching).toBe(true);
|
|
76
|
+
expect(result.current.noResults).toBe(false);
|
|
77
|
+
rerender({ query: "A", debouncedQuery: "A" });
|
|
78
|
+
await waitFor(() => expect(answers.has("A")).toBe(true));
|
|
79
|
+
rerender({ query: "B", debouncedQuery: "A" });
|
|
80
|
+
expect(result.current.searching).toBe(true);
|
|
81
|
+
await act(async () =>
|
|
82
|
+
answers.get("A")?.(
|
|
83
|
+
Response.json({ message: [{ doctype: "ToDo", name: "A document", content: "" }] }),
|
|
84
|
+
),
|
|
85
|
+
);
|
|
86
|
+
expect(result.current.items.filter((item) => item.id.startsWith("doc:"))).toEqual([]);
|
|
87
|
+
expect(result.current.searching).toBe(true);
|
|
88
|
+
rerender({ query: "B", debouncedQuery: "B" });
|
|
89
|
+
await waitFor(() => expect(answers.has("B")).toBe(true));
|
|
90
|
+
await act(async () =>
|
|
91
|
+
answers.get("B")?.(
|
|
92
|
+
Response.json({ message: [{ doctype: "ToDo", name: "B document", content: "" }] }),
|
|
93
|
+
),
|
|
94
|
+
);
|
|
95
|
+
expect(result.current.items.map((item) => item.label)).toEqual(["B document"]);
|
|
96
|
+
expect(result.current.searching).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("keeps local navigation after a refused remote search", async () => {
|
|
100
|
+
vi.stubGlobal("fetch", async (input: string | URL | Request) => {
|
|
101
|
+
const url = new URL(String(input instanceof Request ? input.url : input));
|
|
102
|
+
if (url.pathname.includes("global_search"))
|
|
103
|
+
return Response.json({ exc_type: "PermissionError" }, { status: 403 });
|
|
104
|
+
return Response.json(
|
|
105
|
+
url.pathname.includes("Workspace")
|
|
106
|
+
? { data: [{ name: "Sales", title: "Sales" }] }
|
|
107
|
+
: { message: [] },
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
const { result } = renderHook(() =>
|
|
111
|
+
useCommandPaletteModel({ query: "Sa", debouncedQuery: "Sa", open: true }),
|
|
112
|
+
);
|
|
113
|
+
await waitFor(() => expect(result.current.searchUnavailable).toBe(true));
|
|
114
|
+
expect(result.current.items.map((item) => item.label)).toEqual(["Sales"]);
|
|
115
|
+
expect(result.current.noResults).toBe(false);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("ignores A when its response arrives after B", async () => {
|
|
120
|
+
const answers = new Map<string, (response: Response) => void>();
|
|
121
|
+
vi.stubGlobal("fetch", (input: string | URL | Request) => {
|
|
122
|
+
const url = new URL(String(input instanceof Request ? input.url : input));
|
|
123
|
+
if (url.pathname.includes("global_search")) {
|
|
124
|
+
return new Promise<Response>((resolve) =>
|
|
125
|
+
answers.set(url.searchParams.get("text") ?? "", resolve),
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
return Promise.resolve(Response.json({ data: [], message: [] }));
|
|
129
|
+
});
|
|
130
|
+
const { result, rerender } = renderHook(
|
|
131
|
+
({ query }) => useCommandPaletteModel({ query, debouncedQuery: query, open: true }),
|
|
132
|
+
{ initialProps: { query: "A" } },
|
|
133
|
+
);
|
|
134
|
+
await waitFor(() => expect(answers.has("A")).toBe(true));
|
|
135
|
+
rerender({ query: "B" });
|
|
136
|
+
await waitFor(() => expect(answers.has("B")).toBe(true));
|
|
137
|
+
await act(async () =>
|
|
138
|
+
answers.get("B")?.(
|
|
139
|
+
Response.json({ message: [{ doctype: "ToDo", name: "B result", content: "" }] }),
|
|
140
|
+
),
|
|
141
|
+
);
|
|
142
|
+
await act(async () =>
|
|
143
|
+
answers.get("A")?.(
|
|
144
|
+
Response.json({ message: [{ doctype: "ToDo", name: "A result", content: "" }] }),
|
|
145
|
+
),
|
|
146
|
+
);
|
|
147
|
+
expect(result.current.items.map((item) => item.label)).toEqual(["B result"]);
|
|
148
|
+
});
|
|
@@ -76,6 +76,7 @@ export interface CommandPaletteModel {
|
|
|
76
76
|
items: CommandPaletteItem[];
|
|
77
77
|
/** Global search in flight for the debounced query. */
|
|
78
78
|
searching: boolean;
|
|
79
|
+
searchUnavailable: boolean;
|
|
79
80
|
/** The trimmed query the sections were built from. */
|
|
80
81
|
query: string;
|
|
81
82
|
/** Trimmed query is non-empty. */
|
|
@@ -135,8 +136,16 @@ export function useCommandPaletteModel({
|
|
|
135
136
|
const capabilities = useBackofficeCapabilities();
|
|
136
137
|
const isDoctypeAllowed = useIsDoctypeAllowed();
|
|
137
138
|
|
|
138
|
-
const { catalog } = useAwesomebarCatalog();
|
|
139
|
-
const
|
|
139
|
+
const { catalog } = useAwesomebarCatalog(open);
|
|
140
|
+
const remote = useGlobalSearch(open ? debouncedQuery.trim() : "");
|
|
141
|
+
const trimmed = query.trim();
|
|
142
|
+
const searching =
|
|
143
|
+
open && Boolean(trimmed) && (trimmed !== debouncedQuery.trim() || remote.searching);
|
|
144
|
+
const searchUnavailable = open && remote.query === trimmed && remote.error;
|
|
145
|
+
const hits = useMemo(
|
|
146
|
+
() => (remote.query === trimmed ? remote.hits : []),
|
|
147
|
+
[remote.query, remote.hits, trimmed],
|
|
148
|
+
);
|
|
140
149
|
const [recents, setRecents] = useState<RecentItem[]>([]);
|
|
141
150
|
|
|
142
151
|
// Fresh recents on every open (another surface may have pushed a visit
|
|
@@ -152,8 +161,6 @@ export function useCommandPaletteModel({
|
|
|
152
161
|
};
|
|
153
162
|
}, [open]);
|
|
154
163
|
|
|
155
|
-
const trimmed = query.trim();
|
|
156
|
-
|
|
157
164
|
const sections = useMemo<CommandPaletteSection[]>(() => {
|
|
158
165
|
const next: CommandPaletteSection[] = [];
|
|
159
166
|
|
|
@@ -284,8 +291,9 @@ export function useCommandPaletteModel({
|
|
|
284
291
|
sections,
|
|
285
292
|
items,
|
|
286
293
|
searching,
|
|
294
|
+
searchUnavailable,
|
|
287
295
|
query: trimmed,
|
|
288
296
|
hasQuery: Boolean(trimmed),
|
|
289
|
-
noResults: Boolean(trimmed) && !searching && items.length === 0,
|
|
297
|
+
noResults: Boolean(trimmed) && !searching && !searchUnavailable && items.length === 0,
|
|
290
298
|
};
|
|
291
299
|
}
|