@multiplatform.one/backoffice 7.23.2 → 7.24.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/dist/cjs/chrome/awesomebar-data.cjs +4 -2
- package/dist/cjs/chrome/awesomebar-data.native.js +7 -4
- package/dist/cjs/chrome/awesomebar-data.native.js.map +1 -1
- package/dist/cjs/chrome/help-about-dialog.cjs +2 -2
- package/dist/cjs/chrome/help-about-dialog.native.js +2 -2
- package/dist/cjs/chrome/help-shortcuts-dialog.cjs +2 -2
- package/dist/cjs/chrome/help-shortcuts-dialog.native.js +2 -2
- package/dist/cjs/chrome/notifications-panel.cjs +8 -6
- package/dist/cjs/chrome/notifications-panel.native.js +8 -6
- package/dist/cjs/chrome/notifications-panel.native.js.map +1 -1
- package/dist/cjs/form/DocFormScreen.cjs +43 -43
- package/dist/cjs/form/DocFormScreen.native.js +43 -43
- package/dist/cjs/form/DocFormScreen.native.js.map +1 -1
- package/dist/cjs/form/sidebar-people.cjs +5 -2
- package/dist/cjs/form/sidebar-people.native.js +5 -2
- package/dist/cjs/form/sidebar-people.native.js.map +1 -1
- package/dist/cjs/i18n/en.cjs +1 -1
- package/dist/cjs/i18n/en.native.js +1 -1
- package/dist/cjs/i18n/en.native.js.map +1 -1
- package/dist/cjs/list/ListBulkActionsBar.cjs +1 -1
- package/dist/cjs/list/ListBulkActionsBar.native.js +1 -2
- package/dist/cjs/list/ListBulkActionsBar.native.js.map +1 -1
- package/dist/cjs/shell/WorkspaceSidebar.cjs +9 -7
- package/dist/cjs/shell/WorkspaceSidebar.native.js +9 -7
- package/dist/cjs/shell/WorkspaceSidebar.native.js.map +1 -1
- package/dist/cjs/views/CalendarView.cjs +5 -1
- package/dist/cjs/views/CalendarView.native.js +7 -2
- package/dist/cjs/views/CalendarView.native.js.map +1 -1
- package/dist/cjs/views/DashboardView.cjs +9 -3
- package/dist/cjs/views/DashboardView.native.js +9 -3
- package/dist/cjs/views/DashboardView.native.js.map +1 -1
- package/dist/cjs/views/KanbanView.cjs +5 -2
- package/dist/cjs/views/KanbanView.native.js +7 -3
- package/dist/cjs/views/KanbanView.native.js.map +1 -1
- package/dist/cjs/views/ReportView.cjs +8 -5
- package/dist/cjs/views/ReportView.native.js +11 -6
- package/dist/cjs/views/ReportView.native.js.map +1 -1
- package/dist/cjs/views/ViewHeader.cjs +8 -1
- package/dist/cjs/views/ViewHeader.native.js +8 -1
- package/dist/cjs/views/ViewHeader.native.js.map +1 -1
- package/dist/cjs/views/dashboard-data.cjs +17 -11
- package/dist/cjs/views/dashboard-data.native.js +20 -13
- package/dist/cjs/views/dashboard-data.native.js.map +1 -1
- package/dist/esm/chrome/awesomebar-data.mjs +4 -2
- package/dist/esm/chrome/awesomebar-data.mjs.map +1 -1
- package/dist/esm/chrome/awesomebar-data.native.js +7 -4
- package/dist/esm/chrome/awesomebar-data.native.js.map +1 -1
- package/dist/esm/chrome/help-about-dialog.mjs +2 -2
- package/dist/esm/chrome/help-about-dialog.mjs.map +1 -1
- package/dist/esm/chrome/help-about-dialog.native.js +2 -2
- package/dist/esm/chrome/help-about-dialog.native.js.map +1 -1
- package/dist/esm/chrome/help-shortcuts-dialog.mjs +2 -2
- package/dist/esm/chrome/help-shortcuts-dialog.mjs.map +1 -1
- package/dist/esm/chrome/help-shortcuts-dialog.native.js +2 -2
- package/dist/esm/chrome/notifications-panel.mjs +8 -6
- package/dist/esm/chrome/notifications-panel.mjs.map +1 -1
- package/dist/esm/chrome/notifications-panel.native.js +8 -6
- package/dist/esm/chrome/notifications-panel.native.js.map +1 -1
- package/dist/esm/form/DocFormScreen.mjs +43 -43
- package/dist/esm/form/DocFormScreen.mjs.map +1 -1
- package/dist/esm/form/DocFormScreen.native.js +43 -43
- package/dist/esm/form/DocFormScreen.native.js.map +1 -1
- package/dist/esm/form/sidebar-people.mjs +6 -3
- package/dist/esm/form/sidebar-people.mjs.map +1 -1
- package/dist/esm/form/sidebar-people.native.js +6 -3
- package/dist/esm/form/sidebar-people.native.js.map +1 -1
- package/dist/esm/i18n/en.mjs +1 -1
- package/dist/esm/i18n/en.native.js +1 -1
- package/dist/esm/list/ListBulkActionsBar.mjs +2 -2
- package/dist/esm/list/ListBulkActionsBar.mjs.map +1 -1
- package/dist/esm/list/ListBulkActionsBar.native.js +2 -3
- package/dist/esm/list/ListBulkActionsBar.native.js.map +1 -1
- package/dist/esm/shell/WorkspaceSidebar.mjs +9 -7
- package/dist/esm/shell/WorkspaceSidebar.mjs.map +1 -1
- package/dist/esm/shell/WorkspaceSidebar.native.js +9 -7
- package/dist/esm/shell/WorkspaceSidebar.native.js.map +1 -1
- package/dist/esm/views/CalendarView.mjs +6 -2
- package/dist/esm/views/CalendarView.mjs.map +1 -1
- package/dist/esm/views/CalendarView.native.js +8 -3
- package/dist/esm/views/CalendarView.native.js.map +1 -1
- package/dist/esm/views/DashboardView.mjs +9 -3
- package/dist/esm/views/DashboardView.mjs.map +1 -1
- package/dist/esm/views/DashboardView.native.js +9 -3
- package/dist/esm/views/DashboardView.native.js.map +1 -1
- package/dist/esm/views/KanbanView.mjs +6 -3
- package/dist/esm/views/KanbanView.mjs.map +1 -1
- package/dist/esm/views/KanbanView.native.js +8 -4
- package/dist/esm/views/KanbanView.native.js.map +1 -1
- package/dist/esm/views/ReportView.mjs +8 -5
- package/dist/esm/views/ReportView.mjs.map +1 -1
- package/dist/esm/views/ReportView.native.js +11 -6
- package/dist/esm/views/ReportView.native.js.map +1 -1
- package/dist/esm/views/ViewHeader.mjs +8 -1
- package/dist/esm/views/ViewHeader.mjs.map +1 -1
- package/dist/esm/views/ViewHeader.native.js +8 -1
- package/dist/esm/views/ViewHeader.native.js.map +1 -1
- package/dist/esm/views/dashboard-data.mjs +17 -11
- package/dist/esm/views/dashboard-data.mjs.map +1 -1
- package/dist/esm/views/dashboard-data.native.js +20 -13
- package/dist/esm/views/dashboard-data.native.js.map +1 -1
- package/dist/jsx/chrome/awesomebar-data.mjs +4 -2
- package/dist/jsx/chrome/awesomebar-data.mjs.map +1 -1
- package/dist/jsx/chrome/awesomebar-data.native.js +7 -4
- package/dist/jsx/chrome/awesomebar-data.native.js.map +1 -1
- package/dist/jsx/chrome/help-about-dialog.mjs +2 -2
- package/dist/jsx/chrome/help-about-dialog.mjs.map +1 -1
- package/dist/jsx/chrome/help-about-dialog.native.js +2 -2
- package/dist/jsx/chrome/help-shortcuts-dialog.mjs +2 -2
- package/dist/jsx/chrome/help-shortcuts-dialog.mjs.map +1 -1
- package/dist/jsx/chrome/help-shortcuts-dialog.native.js +2 -2
- package/dist/jsx/chrome/help-shortcuts-dialog.native.js.map +1 -1
- package/dist/jsx/chrome/notifications-panel.mjs +8 -6
- package/dist/jsx/chrome/notifications-panel.mjs.map +1 -1
- package/dist/jsx/chrome/notifications-panel.native.js +8 -6
- package/dist/jsx/chrome/notifications-panel.native.js.map +1 -1
- package/dist/jsx/form/DocFormScreen.mjs +43 -43
- package/dist/jsx/form/DocFormScreen.mjs.map +1 -1
- package/dist/jsx/form/DocFormScreen.native.js +43 -43
- package/dist/jsx/form/DocFormScreen.native.js.map +1 -1
- package/dist/jsx/form/sidebar-people.mjs +6 -3
- package/dist/jsx/form/sidebar-people.mjs.map +1 -1
- package/dist/jsx/form/sidebar-people.native.js +5 -2
- package/dist/jsx/form/sidebar-people.native.js.map +1 -1
- package/dist/jsx/i18n/en.mjs +1 -1
- package/dist/jsx/i18n/en.native.js +1 -1
- package/dist/jsx/i18n/en.native.js.map +1 -1
- package/dist/jsx/list/ListBulkActionsBar.mjs +2 -2
- package/dist/jsx/list/ListBulkActionsBar.mjs.map +1 -1
- package/dist/jsx/list/ListBulkActionsBar.native.js +1 -2
- package/dist/jsx/list/ListBulkActionsBar.native.js.map +1 -1
- package/dist/jsx/shell/WorkspaceSidebar.mjs +9 -7
- package/dist/jsx/shell/WorkspaceSidebar.mjs.map +1 -1
- package/dist/jsx/shell/WorkspaceSidebar.native.js +9 -7
- package/dist/jsx/shell/WorkspaceSidebar.native.js.map +1 -1
- package/dist/jsx/views/CalendarView.mjs +6 -2
- package/dist/jsx/views/CalendarView.mjs.map +1 -1
- package/dist/jsx/views/CalendarView.native.js +7 -2
- package/dist/jsx/views/CalendarView.native.js.map +1 -1
- package/dist/jsx/views/DashboardView.mjs +9 -3
- package/dist/jsx/views/DashboardView.mjs.map +1 -1
- package/dist/jsx/views/DashboardView.native.js +9 -3
- package/dist/jsx/views/DashboardView.native.js.map +1 -1
- package/dist/jsx/views/KanbanView.mjs +6 -3
- package/dist/jsx/views/KanbanView.mjs.map +1 -1
- package/dist/jsx/views/KanbanView.native.js +7 -3
- package/dist/jsx/views/KanbanView.native.js.map +1 -1
- package/dist/jsx/views/ReportView.mjs +8 -5
- package/dist/jsx/views/ReportView.mjs.map +1 -1
- package/dist/jsx/views/ReportView.native.js +11 -6
- package/dist/jsx/views/ReportView.native.js.map +1 -1
- package/dist/jsx/views/ViewHeader.mjs +8 -1
- package/dist/jsx/views/ViewHeader.mjs.map +1 -1
- package/dist/jsx/views/ViewHeader.native.js +8 -1
- package/dist/jsx/views/ViewHeader.native.js.map +1 -1
- package/dist/jsx/views/dashboard-data.mjs +17 -11
- package/dist/jsx/views/dashboard-data.mjs.map +1 -1
- package/dist/jsx/views/dashboard-data.native.js +20 -13
- package/dist/jsx/views/dashboard-data.native.js.map +1 -1
- package/package.json +12 -12
- package/src/chrome/NotificationsButton.screen.spec.tsx +162 -0
- package/src/chrome/UserMenu.screen.spec.tsx +107 -0
- package/src/chrome/awesomebar-data.auth.spec.ts +56 -0
- package/src/chrome/awesomebar-data.tsx +4 -2
- package/src/chrome/help-about-dialog.tsx +2 -2
- package/src/chrome/help-shortcuts-dialog.tsx +2 -2
- package/src/chrome/notifications-panel.tsx +8 -9
- package/src/form/DocFormScreen.tsx +54 -45
- package/src/form/FormConnections.screen.spec.tsx +153 -0
- package/src/form/FormDialogs.screen.spec.tsx +119 -0
- package/src/form/FormToolbar.screen.spec.tsx +147 -0
- package/src/form/sidebar-people.tsx +7 -5
- package/src/i18n/en.ts +1 -1
- package/src/list/ListBulkActionsBar.screen.spec.tsx +181 -0
- package/src/list/ListBulkActionsBar.tsx +6 -2
- package/src/list/ListFilterBar.screen.spec.tsx +176 -0
- package/src/list/ListSidebar.screen.spec.tsx +202 -0
- package/src/quickEntry/QuickEntryDialog.screen.spec.tsx +143 -0
- package/src/report/DoctypeReportView.screen.spec.tsx +154 -0
- package/src/shell/WorkspaceSidebar.tsx +10 -9
- package/src/views/CalendarView.screen.spec.tsx +241 -0
- package/src/views/CalendarView.tsx +11 -3
- package/src/views/DashboardView.screen.spec.tsx +144 -0
- package/src/views/DashboardView.tsx +7 -3
- package/src/views/ImageView.screen.spec.tsx +108 -0
- package/src/views/KanbanView.screen.spec.tsx +282 -0
- package/src/views/KanbanView.tsx +7 -3
- package/src/views/ReportView.screen.spec.tsx +108 -0
- package/src/views/ReportView.tsx +10 -5
- package/src/views/ViewHeader.screen.spec.tsx +185 -0
- package/src/views/ViewHeader.tsx +4 -1
- package/src/views/dashboard-data.tsx +21 -12
- package/types/chrome/awesomebar-data.d.ts.map +1 -1
- package/types/chrome/notifications-panel.d.ts.map +1 -1
- package/types/form/DocFormScreen.d.ts.map +1 -1
- package/types/form/sidebar-people.d.ts.map +1 -1
- package/types/i18n/en.d.ts +1 -1
- package/types/list/ListBulkActionsBar.d.ts.map +1 -1
- package/types/shell/WorkspaceSidebar.d.ts.map +1 -1
- package/types/views/CalendarView.d.ts.map +1 -1
- package/types/views/DashboardView.d.ts.map +1 -1
- package/types/views/KanbanView.d.ts.map +1 -1
- package/types/views/ReportView.d.ts.map +1 -1
- package/types/views/ViewHeader.d.ts.map +1 -1
- package/types/views/dashboard-data.d.ts +4 -2
- package/types/views/dashboard-data.d.ts.map +1 -1
|
@@ -141,12 +141,12 @@ function bucketRowsByDay(rows) {
|
|
|
141
141
|
}
|
|
142
142
|
return counts;
|
|
143
143
|
}
|
|
144
|
-
async function fetchLiveDayCounts(connection, doctype, cutoff) {
|
|
144
|
+
async function fetchLiveDayCounts(connection, doctype, cutoff, scope) {
|
|
145
145
|
var db = new import_frappe.DbModule(new import_frappe.HttpClient(connection.baseURL, toAuthConfig(connection.auth)));
|
|
146
146
|
try {
|
|
147
147
|
var rows = await db.getDocList(doctype, {
|
|
148
148
|
fields: ["date(creation) as _day", "count(name) as _aggregate_value"],
|
|
149
|
-
filters: [["creation", ">=", cutoff]],
|
|
149
|
+
filters: [...scope, ["creation", ">=", cutoff]],
|
|
150
150
|
groupBy: "date(creation)",
|
|
151
151
|
limit: 0
|
|
152
152
|
});
|
|
@@ -181,21 +181,24 @@ async function fetchLiveDayCounts(connection, doctype, cutoff) {
|
|
|
181
181
|
} catch (unused) {
|
|
182
182
|
var rows1 = await db.getDocList(doctype, {
|
|
183
183
|
fields: ["creation"],
|
|
184
|
-
filters: [["creation", ">=", cutoff]],
|
|
184
|
+
filters: [...scope, ["creation", ">=", cutoff]],
|
|
185
185
|
limit: 0
|
|
186
186
|
});
|
|
187
187
|
return bucketRowsByDay(rows1);
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
|
-
async function fetchFixtureDayCounts(fixtures, doctype) {
|
|
190
|
+
async function fetchFixtureDayCounts(fixtures, doctype, scope) {
|
|
191
191
|
var rows = await fixtures.getList(doctype, {
|
|
192
|
-
fields: ["creation"]
|
|
192
|
+
fields: ["creation"],
|
|
193
|
+
filters: scope
|
|
193
194
|
});
|
|
194
195
|
return bucketRowsByDay(rows);
|
|
195
196
|
}
|
|
196
197
|
var REFETCH_DEBOUNCE_MS = 300;
|
|
198
|
+
var NO_SCOPE = [];
|
|
197
199
|
function useWeeklyCreated(connection, doctype) {
|
|
198
|
-
var weeks = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : WEEKS_WINDOW
|
|
200
|
+
var weeks = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : WEEKS_WINDOW,
|
|
201
|
+
scope = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : NO_SCOPE;
|
|
199
202
|
var [data, setData] = (0, import_react2.useState)(void 0);
|
|
200
203
|
var [isLoading, setIsLoading] = (0, import_react2.useState)(true);
|
|
201
204
|
var [isStale, setIsStale] = (0, import_react2.useState)(false);
|
|
@@ -205,24 +208,27 @@ function useWeeklyCreated(connection, doctype) {
|
|
|
205
208
|
connectionRef.current = connection;
|
|
206
209
|
var specRef = (0, import_react2.useRef)({
|
|
207
210
|
doctype,
|
|
208
|
-
weeks
|
|
211
|
+
weeks,
|
|
212
|
+
scope
|
|
209
213
|
});
|
|
210
214
|
specRef.current = {
|
|
211
215
|
doctype,
|
|
212
|
-
weeks
|
|
216
|
+
weeks,
|
|
217
|
+
scope
|
|
213
218
|
};
|
|
214
219
|
var runFetch = (0, import_react2.useCallback)(async function () {
|
|
215
220
|
var conn = connectionRef.current;
|
|
216
221
|
var {
|
|
217
222
|
doctype: currentDoctype,
|
|
218
|
-
weeks: currentWeeks
|
|
223
|
+
weeks: currentWeeks,
|
|
224
|
+
scope: currentScope
|
|
219
225
|
} = specRef.current;
|
|
220
226
|
if (!conn || !currentDoctype) return;
|
|
221
227
|
var seq = ++fetchSeq.current;
|
|
222
228
|
try {
|
|
223
229
|
var firstWeek = weekStart(/* @__PURE__ */new Date());
|
|
224
230
|
firstWeek.setDate(firstWeek.getDate() - (currentWeeks - 1) * 7);
|
|
225
|
-
var dayCounts = conn.fixtures ? await fetchFixtureDayCounts(conn.fixtures, currentDoctype) : await fetchLiveDayCounts(conn, currentDoctype, isoDate(firstWeek));
|
|
231
|
+
var dayCounts = conn.fixtures ? await fetchFixtureDayCounts(conn.fixtures, currentDoctype, currentScope) : await fetchLiveDayCounts(conn, currentDoctype, isoDate(firstWeek), currentScope);
|
|
226
232
|
var weekCounts = /* @__PURE__ */new Map();
|
|
227
233
|
var _iteratorNormalCompletion = true,
|
|
228
234
|
_didIteratorError = false,
|
|
@@ -295,15 +301,16 @@ function useWeeklyCreated(connection, doctype) {
|
|
|
295
301
|
}, []);
|
|
296
302
|
var baseURL = connection === null || connection === void 0 ? void 0 : connection.baseURL;
|
|
297
303
|
var hasFixtures = !!(connection === null || connection === void 0 ? void 0 : connection.fixtures);
|
|
304
|
+
var scopeKey = JSON.stringify(scope);
|
|
298
305
|
(0, import_react2.useEffect)(function () {
|
|
299
306
|
if (!baseURL || !doctype) return;
|
|
300
307
|
setIsLoading(true);
|
|
301
308
|
setError(void 0);
|
|
302
309
|
void runFetch();
|
|
303
310
|
},
|
|
304
|
-
// Refetch on connection target / doctype change only — the
|
|
305
|
-
// object identity churns with provider re-renders.
|
|
306
|
-
[baseURL, hasFixtures, doctype, weeks, runFetch]);
|
|
311
|
+
// Refetch on connection target / doctype / scope change only — the
|
|
312
|
+
// connection object identity churns with provider re-renders.
|
|
313
|
+
[baseURL, hasFixtures, doctype, weeks, scopeKey, runFetch]);
|
|
307
314
|
(0, import_frappe.useFrappeDoctypeEvents)(connection, doctype, function () {
|
|
308
315
|
var _debouncerRef_current;
|
|
309
316
|
setIsStale(true);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["__create","Object","create","__defProp","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__getProtoOf","getPrototypeOf","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toESM","mod","isNodeMode","__esModule","value","__toCommonJS","dashboard_data_exports","MAX_STATUS_KPI_CARDS","WEEKS_WINDOW","daysAgoIso","detectStatusField","filteredListHref","isoDate","useWeeklyCreated","weekStart","module","exports","import_react","require","import_frappe","import_theme","import_react2","import_paths","_instanceof","left","right","Symbol","hasInstance","isSelectCandidate","field","_field_options","fieldtype","hidden","options","trim","length","meta","_candidates_find","candidates","fields","filter","find","fieldname","defaultPaths","createBackofficePaths","doctype","paths","arguments","filters","list","encodeURIComponent","JSON","stringify","date","formatCalendarDate","days","Date","setDate","getDate","copy","getFullYear","getMonth","mondayOffset","getDay","parseIsoDateLocal","y","m","d","slice","split","map","Number","isNaN","getTime","parseFrappeDatetime","replace","toAuthConfig","auth","useToken","token","type","bucketRowsByDay","rows","counts","Map","_iteratorNormalCompletion","_didIteratorError","_iteratorError","_iterator","iterator","_step","next","done","row","_counts_get","created","creation","day","set","err","return","fetchLiveDayCounts","connection","cutoff","db","DbModule","HttpClient","baseURL","getDocList","groupBy","limit","_day","_aggregate_value","isFinite"],"sources":["../../../src/views/dashboard-data.tsx"],"sourcesContent":[null],"mappings":"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
|
|
1
|
+
{"version":3,"names":["__create","Object","create","__defProp","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__getProtoOf","getPrototypeOf","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toESM","mod","isNodeMode","__esModule","value","__toCommonJS","dashboard_data_exports","MAX_STATUS_KPI_CARDS","WEEKS_WINDOW","daysAgoIso","detectStatusField","filteredListHref","isoDate","useWeeklyCreated","weekStart","module","exports","import_react","require","import_frappe","import_theme","import_react2","import_paths","_instanceof","left","right","Symbol","hasInstance","isSelectCandidate","field","_field_options","fieldtype","hidden","options","trim","length","meta","_candidates_find","candidates","fields","filter","find","fieldname","defaultPaths","createBackofficePaths","doctype","paths","arguments","filters","list","encodeURIComponent","JSON","stringify","date","formatCalendarDate","days","Date","setDate","getDate","copy","getFullYear","getMonth","mondayOffset","getDay","parseIsoDateLocal","y","m","d","slice","split","map","Number","isNaN","getTime","parseFrappeDatetime","replace","toAuthConfig","auth","useToken","token","type","bucketRowsByDay","rows","counts","Map","_iteratorNormalCompletion","_didIteratorError","_iteratorError","_iterator","iterator","_step","next","done","row","_counts_get","created","creation","day","set","err","return","fetchLiveDayCounts","connection","cutoff","scope","db","DbModule","HttpClient","baseURL","getDocList","groupBy","limit","_day","_aggregate_value","isFinite","unused","rows1"],"sources":["../../../src/views/dashboard-data.tsx"],"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.
|
|
3
|
+
"version": "7.24.0",
|
|
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.
|
|
75
|
-
"@multiplatform.one/forms": "7.
|
|
76
|
-
"@multiplatform.one/frappe": "7.
|
|
77
|
-
"@multiplatform.one/frappe-ui": "7.
|
|
78
|
-
"@multiplatform.one/i18n": "7.
|
|
79
|
-
"@multiplatform.one/platform": "7.
|
|
80
|
-
"@multiplatform.one/store": "7.
|
|
81
|
-
"@multiplatform.one/table": "7.
|
|
82
|
-
"@multiplatform.one/theme": "7.
|
|
74
|
+
"@multiplatform.one/components": "7.24.0",
|
|
75
|
+
"@multiplatform.one/forms": "7.24.0",
|
|
76
|
+
"@multiplatform.one/frappe": "7.24.0",
|
|
77
|
+
"@multiplatform.one/frappe-ui": "7.24.0",
|
|
78
|
+
"@multiplatform.one/i18n": "7.24.0",
|
|
79
|
+
"@multiplatform.one/platform": "7.24.0",
|
|
80
|
+
"@multiplatform.one/store": "7.24.0",
|
|
81
|
+
"@multiplatform.one/table": "7.24.0",
|
|
82
|
+
"@multiplatform.one/theme": "7.24.0",
|
|
83
83
|
"@phosphor-icons/react": "^2.1.10",
|
|
84
84
|
"tamagui": "2.7.6"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
|
-
"@multiplatform.one/config": "7.
|
|
88
|
-
"@multiplatform.one/test-utils": "7.
|
|
87
|
+
"@multiplatform.one/config": "7.24.0",
|
|
88
|
+
"@multiplatform.one/test-utils": "7.24.0",
|
|
89
89
|
"@tamagui/build": "2.7.6",
|
|
90
90
|
"@testing-library/react": "^16.3.2",
|
|
91
91
|
"@types/react-dom": "^19.2.3",
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// The navbar bell over the Notification Log (MPO-414): unread badge, panel
|
|
2
|
+
// rows newest first, a row press routes to its document and marks it read.
|
|
3
|
+
import { InMemoryFixtureProvider } from "@multiplatform.one/frappe";
|
|
4
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
5
|
+
import { fireEvent, screen, waitFor } from "@testing-library/react";
|
|
6
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
7
|
+
import { BackofficeProvider } from "../config/BackofficeProvider";
|
|
8
|
+
import { createMemoryNavigator } from "../navigation/navigator";
|
|
9
|
+
import { NotificationsButton } from "./NotificationsButton";
|
|
10
|
+
|
|
11
|
+
const notifications = [
|
|
12
|
+
{
|
|
13
|
+
name: "notif-0001",
|
|
14
|
+
doctype: "Notification Log",
|
|
15
|
+
subject: "<b>Jane Doe</b> mentioned you in a comment on ToDo",
|
|
16
|
+
type: "Mention",
|
|
17
|
+
document_type: "ToDo",
|
|
18
|
+
document_name: "TODO-0001",
|
|
19
|
+
read: 0,
|
|
20
|
+
from_user: "jane@example.com",
|
|
21
|
+
creation: "2026-09-27 10:00:00",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "notif-0002",
|
|
25
|
+
doctype: "Notification Log",
|
|
26
|
+
subject: "<b>Joe Smith</b> assigned a new task to you",
|
|
27
|
+
type: "Assignment",
|
|
28
|
+
document_type: "ToDo",
|
|
29
|
+
document_name: "TODO-0002",
|
|
30
|
+
read: 0,
|
|
31
|
+
from_user: "joe@example.com",
|
|
32
|
+
creation: "2026-09-27 09:00:00",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "notif-0003",
|
|
36
|
+
doctype: "Notification Log",
|
|
37
|
+
subject: "<b>Administrator</b> shared a document with you",
|
|
38
|
+
type: "Share",
|
|
39
|
+
document_type: "Note",
|
|
40
|
+
document_name: "NOTE-0001",
|
|
41
|
+
read: 1,
|
|
42
|
+
from_user: "Administrator",
|
|
43
|
+
creation: "2026-09-26 08:00:00",
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
let host = 0;
|
|
48
|
+
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
vi.restoreAllMocks();
|
|
51
|
+
vi.unstubAllGlobals();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
function renderBell(rows: typeof notifications) {
|
|
55
|
+
host += 1;
|
|
56
|
+
const navigation = createMemoryNavigator("/backoffice");
|
|
57
|
+
renderWithProviders(
|
|
58
|
+
<BackofficeProvider
|
|
59
|
+
frappe={{
|
|
60
|
+
baseURL: `https://notifications-${host}.screen.spec.test`,
|
|
61
|
+
fixtures: new InMemoryFixtureProvider({ "Notification Log": rows }),
|
|
62
|
+
}}
|
|
63
|
+
navigation={navigation}
|
|
64
|
+
>
|
|
65
|
+
<NotificationsButton />
|
|
66
|
+
</BackofficeProvider>,
|
|
67
|
+
);
|
|
68
|
+
return navigation;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function json(body: unknown, status = 200) {
|
|
72
|
+
return new Response(JSON.stringify(body), {
|
|
73
|
+
status,
|
|
74
|
+
headers: { "content-type": "application/json" },
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function renderLiveBell() {
|
|
79
|
+
host += 1;
|
|
80
|
+
const navigation = createMemoryNavigator("/backoffice");
|
|
81
|
+
renderWithProviders(
|
|
82
|
+
<BackofficeProvider
|
|
83
|
+
frappe={{ baseURL: `https://notifications-live-${host}.screen.spec.test` }}
|
|
84
|
+
navigation={navigation}
|
|
85
|
+
>
|
|
86
|
+
<NotificationsButton />
|
|
87
|
+
</BackofficeProvider>,
|
|
88
|
+
);
|
|
89
|
+
return navigation;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function badgeCount(): string | undefined {
|
|
93
|
+
const badge = document.querySelector('[data-badge="count"]');
|
|
94
|
+
return badge?.textContent?.replace(/\D/g, "");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function rows(): HTMLElement[] {
|
|
98
|
+
return screen.queryAllByTestId("backoffice-notification-row");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
describe("NotificationsButton", () => {
|
|
102
|
+
it("badges the unread count and lists every row newest first", async () => {
|
|
103
|
+
renderBell(notifications);
|
|
104
|
+
await waitFor(() => expect(badgeCount()).toBe("2"));
|
|
105
|
+
fireEvent.click(screen.getByTestId("backoffice-notifications-button"));
|
|
106
|
+
await waitFor(() => expect(rows()).toHaveLength(3));
|
|
107
|
+
expect(rows().map((row) => row.getAttribute("aria-label"))).toEqual([
|
|
108
|
+
"Jane Doe mentioned you in a comment on ToDo",
|
|
109
|
+
"Joe Smith assigned a new task to you",
|
|
110
|
+
"Administrator shared a document with you",
|
|
111
|
+
]);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("routes a row press to its document and marks it read", async () => {
|
|
115
|
+
const navigation = renderBell(notifications);
|
|
116
|
+
await waitFor(() => expect(badgeCount()).toBe("2"));
|
|
117
|
+
fireEvent.click(screen.getByTestId("backoffice-notifications-button"));
|
|
118
|
+
await waitFor(() => expect(rows()).toHaveLength(3));
|
|
119
|
+
fireEvent.click(rows()[1] as HTMLElement);
|
|
120
|
+
expect(navigation.getLocation().path).toBe("/backoffice/todo/TODO-0002");
|
|
121
|
+
await waitFor(() => expect(badgeCount()).toBe("1"));
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("marks every row read from the panel header", async () => {
|
|
125
|
+
renderBell(notifications);
|
|
126
|
+
await waitFor(() => expect(badgeCount()).toBe("2"));
|
|
127
|
+
fireEvent.click(screen.getByTestId("backoffice-notifications-button"));
|
|
128
|
+
const markAll = await screen.findByTestId("backoffice-notifications-mark-all");
|
|
129
|
+
fireEvent.click(markAll);
|
|
130
|
+
await waitFor(() => expect(badgeCount()).toBeUndefined());
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("tells the bench a live row was read", async () => {
|
|
134
|
+
const markedRead: string[] = [];
|
|
135
|
+
vi.stubGlobal("fetch", async (input: string | URL | Request, init?: RequestInit) => {
|
|
136
|
+
const url = new URL(String(input instanceof Request ? input.url : input));
|
|
137
|
+
const method = url.pathname.replace(/^\/api\/method\//, "");
|
|
138
|
+
if (method === "frappe.auth.get_logged_user") return json({ message: "jane@example.com" });
|
|
139
|
+
if (method === "frappe.desk.doctype.notification_log.notification_log.mark_as_read") {
|
|
140
|
+
markedRead.push(String(init?.body ?? ""));
|
|
141
|
+
return json({ message: null });
|
|
142
|
+
}
|
|
143
|
+
if (url.pathname === "/api/resource/Notification%20Log") return json({ data: notifications });
|
|
144
|
+
return json({ data: [] });
|
|
145
|
+
});
|
|
146
|
+
renderLiveBell();
|
|
147
|
+
await waitFor(() => expect(badgeCount()).toBe("2"), { timeout: 5000 });
|
|
148
|
+
fireEvent.click(screen.getByTestId("backoffice-notifications-button"));
|
|
149
|
+
await waitFor(() => expect(rows()).toHaveLength(3));
|
|
150
|
+
fireEvent.click(rows()[0] as HTMLElement);
|
|
151
|
+
await waitFor(() => expect(markedRead).toHaveLength(1));
|
|
152
|
+
expect(markedRead[0]).toContain("notif-0001");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("shows the empty state when the log has no rows", async () => {
|
|
156
|
+
renderBell([]);
|
|
157
|
+
fireEvent.click(screen.getByTestId("backoffice-notifications-button"));
|
|
158
|
+
expect(await screen.findByText("No new notifications")).toBeTruthy();
|
|
159
|
+
expect(rows()).toHaveLength(0);
|
|
160
|
+
expect(badgeCount()).toBeUndefined();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// The navbar user menu (MPO-414): the session header, profile routing, the
|
|
2
|
+
// theme toggle through the host appearance adapter, and logout.
|
|
3
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
4
|
+
import { fireEvent, screen, waitFor } from "@testing-library/react";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { BackofficeProvider, type BackofficeProviderProps } from "../config/BackofficeProvider";
|
|
7
|
+
import { createMemoryNavigator } from "../navigation/navigator";
|
|
8
|
+
import { UserMenu } from "./UserMenu";
|
|
9
|
+
|
|
10
|
+
let host = 0;
|
|
11
|
+
let loggedUser: string | null = "jane@example.com";
|
|
12
|
+
let calls: string[] = [];
|
|
13
|
+
|
|
14
|
+
function json(body: unknown, status = 200) {
|
|
15
|
+
return new Response(JSON.stringify(body), {
|
|
16
|
+
status,
|
|
17
|
+
headers: { "content-type": "application/json" },
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
host += 1;
|
|
23
|
+
calls = [];
|
|
24
|
+
loggedUser = "jane@example.com";
|
|
25
|
+
vi.stubGlobal("fetch", async (input: string | URL | Request) => {
|
|
26
|
+
const url = new URL(String(input instanceof Request ? input.url : input));
|
|
27
|
+
calls.push(url.pathname);
|
|
28
|
+
if (url.pathname === "/api/method/frappe.auth.get_logged_user") {
|
|
29
|
+
return loggedUser ? json({ message: loggedUser }) : json({ exc_type: "Forbidden" }, 403);
|
|
30
|
+
}
|
|
31
|
+
if (url.pathname === "/api/method/frappe.client.get_value") {
|
|
32
|
+
return json({
|
|
33
|
+
message: { full_name: "Jane Cooper", email: "jane@example.com", user_image: null },
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (url.pathname === "/api/method/logout") return json({ message: "" });
|
|
37
|
+
return json({ data: [] });
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
afterEach(() => {
|
|
42
|
+
vi.restoreAllMocks();
|
|
43
|
+
vi.unstubAllGlobals();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
function renderMenu(props: Partial<BackofficeProviderProps> = {}) {
|
|
47
|
+
const navigation = createMemoryNavigator("/backoffice/todo");
|
|
48
|
+
renderWithProviders(
|
|
49
|
+
<BackofficeProvider
|
|
50
|
+
frappe={{ baseURL: `https://user-menu-${host}.screen.spec.test` }}
|
|
51
|
+
navigation={navigation}
|
|
52
|
+
{...props}
|
|
53
|
+
>
|
|
54
|
+
<UserMenu defaultOpen />
|
|
55
|
+
</BackofficeProvider>,
|
|
56
|
+
);
|
|
57
|
+
return navigation;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function item(label: string): HTMLElement {
|
|
61
|
+
const found = Array.from(document.querySelectorAll('[role="menuitem"]')).find((node) =>
|
|
62
|
+
node.textContent?.startsWith(label),
|
|
63
|
+
);
|
|
64
|
+
if (!found) throw new Error(`no menu item ${label}`);
|
|
65
|
+
return found as HTMLElement;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
describe("UserMenu", () => {
|
|
69
|
+
it("heads the menu with the session user and routes My Profile to their User form", async () => {
|
|
70
|
+
const navigation = renderMenu();
|
|
71
|
+
expect(await screen.findByText("Jane Cooper")).toBeTruthy();
|
|
72
|
+
expect(screen.getByText("jane@example.com")).toBeTruthy();
|
|
73
|
+
fireEvent.click(item("My Profile"));
|
|
74
|
+
expect(navigation.getLocation().path).toBe("/backoffice/user/jane%40example.com");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("disables the profile items while no session user resolves", async () => {
|
|
78
|
+
loggedUser = null;
|
|
79
|
+
renderMenu();
|
|
80
|
+
await waitFor(() => expect(calls).toContain("/api/method/frappe.auth.get_logged_user"));
|
|
81
|
+
await waitFor(() => expect(item("My Profile").getAttribute("aria-disabled")).toBe("true"));
|
|
82
|
+
expect(item("My Settings").getAttribute("aria-disabled")).toBe("true");
|
|
83
|
+
expect(screen.queryByText("Jane Cooper")).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("toggles the scheme through the host appearance adapter", async () => {
|
|
87
|
+
const setScheme = vi.fn();
|
|
88
|
+
renderMenu({ appearance: { scheme: "dark", setScheme } });
|
|
89
|
+
await screen.findByText("Jane Cooper");
|
|
90
|
+
fireEvent.click(item("Toggle Theme"));
|
|
91
|
+
expect(setScheme).toHaveBeenCalledWith("light");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("offers no theme toggle a host cannot honour", async () => {
|
|
95
|
+
renderMenu();
|
|
96
|
+
await screen.findByText("Jane Cooper");
|
|
97
|
+
expect(item("Toggle Theme").getAttribute("aria-disabled")).toBe("true");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("logs out on the bench and leaves for the desk home", async () => {
|
|
101
|
+
const navigation = renderMenu();
|
|
102
|
+
await screen.findByText("Jane Cooper");
|
|
103
|
+
fireEvent.click(item("Log Out"));
|
|
104
|
+
await waitFor(() => expect(navigation.getLocation().path).toBe("/backoffice"));
|
|
105
|
+
expect(calls).toContain("/api/method/logout");
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// The awesomebar's document search holds one client per connection. When the
|
|
2
|
+
// provider's connection starts sending its token, or sends it under another
|
|
3
|
+
// scheme, the next search must carry the new credentials instead of going out
|
|
4
|
+
// on a client built for the old ones (MPO-410).
|
|
5
|
+
import { renderHook, waitFor } from "@testing-library/react";
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
7
|
+
import { useGlobalSearch } from "./awesomebar-data";
|
|
8
|
+
|
|
9
|
+
const provider = vi.hoisted(() => ({ config: undefined as unknown }));
|
|
10
|
+
vi.mock("@multiplatform.one/frappe-ui", () => ({ useFrappeConfig: () => provider.config }));
|
|
11
|
+
|
|
12
|
+
type Auth = { useToken: boolean; token: string; type: string };
|
|
13
|
+
|
|
14
|
+
const auth = (useToken: boolean, type = "token"): Auth => ({ useToken, token: "key:secret", type });
|
|
15
|
+
|
|
16
|
+
const changes: [string, Auth, Auth, (string | null)[]][] = [
|
|
17
|
+
["starts sending its token", auth(false), auth(true), [null, "token key:secret"]],
|
|
18
|
+
[
|
|
19
|
+
"switches its token scheme",
|
|
20
|
+
auth(true, "token"),
|
|
21
|
+
auth(true, "Bearer"),
|
|
22
|
+
["token key:secret", "Bearer key:secret"],
|
|
23
|
+
],
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
let run = 0;
|
|
27
|
+
let sent: (string | null)[] = [];
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
run += 1;
|
|
31
|
+
sent = [];
|
|
32
|
+
vi.stubGlobal("fetch", async (_input: string | URL | Request, init?: RequestInit) => {
|
|
33
|
+
sent.push(new Headers(init?.headers).get("Authorization"));
|
|
34
|
+
return Response.json({ message: [] });
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
afterEach(() => vi.unstubAllGlobals());
|
|
39
|
+
|
|
40
|
+
describe("useGlobalSearch", () => {
|
|
41
|
+
it.each(changes)(
|
|
42
|
+
"searches with the new credentials when the connection %s",
|
|
43
|
+
async (_, before, after, expected) => {
|
|
44
|
+
const baseURL = `http://global-search-${run}.test`;
|
|
45
|
+
provider.config = { baseURL, realtime: false, auth: before };
|
|
46
|
+
const { rerender } = renderHook(({ query }: { query: string }) => useGlobalSearch(query), {
|
|
47
|
+
initialProps: { query: "pikachu" },
|
|
48
|
+
});
|
|
49
|
+
await waitFor(() => expect(sent).toHaveLength(1));
|
|
50
|
+
provider.config = { baseURL, realtime: false, auth: after };
|
|
51
|
+
rerender({ query: "raichu" });
|
|
52
|
+
await waitFor(() => expect(sent).toHaveLength(2));
|
|
53
|
+
expect(sent).toEqual(expected);
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
});
|
|
@@ -148,12 +148,14 @@ export function useGlobalSearch(query: string, limit = 10): GlobalSearchState {
|
|
|
148
148
|
const config = useFrappeConfig();
|
|
149
149
|
const baseURL = config?.baseURL;
|
|
150
150
|
const auth = config?.auth;
|
|
151
|
+
const authKey = `${auth?.useToken ? "t" : ""}:${auth?.token ?? ""}:${auth?.type ?? ""}`;
|
|
151
152
|
|
|
152
153
|
const client = useMemo(
|
|
153
154
|
() => (baseURL ? new HttpClient(baseURL, auth as AuthConfig | undefined) : undefined),
|
|
154
|
-
//
|
|
155
|
+
// Keyed by value: a host may rebuild the auth object and its token
|
|
156
|
+
// getter on every render.
|
|
155
157
|
// oxlint-disable-next-line exhaustive-deps
|
|
156
|
-
[baseURL],
|
|
158
|
+
[baseURL, authKey],
|
|
157
159
|
);
|
|
158
160
|
|
|
159
161
|
const [hits, setHits] = useState<GlobalSearchHit[]>([]);
|
|
@@ -94,7 +94,7 @@ export function HelpAboutDialog({ open, onOpenChange }: HelpAboutDialogProps) {
|
|
|
94
94
|
)}
|
|
95
95
|
</Paragraph>
|
|
96
96
|
</Dialog.Description>
|
|
97
|
-
<YStack gap="$2"
|
|
97
|
+
<YStack gap="$2" paddingTop="$2">
|
|
98
98
|
<SizableText size="$2" fontWeight="400" color="$color10" textTransform="uppercase">
|
|
99
99
|
{t("help.installedApps", "Installed Apps")}
|
|
100
100
|
</SizableText>
|
|
@@ -122,7 +122,7 @@ export function HelpAboutDialog({ open, onOpenChange }: HelpAboutDialogProps) {
|
|
|
122
122
|
</XStack>
|
|
123
123
|
))}
|
|
124
124
|
</YStack>
|
|
125
|
-
<XStack justifyContent="flex-end"
|
|
125
|
+
<XStack justifyContent="flex-end" paddingTop="$4">
|
|
126
126
|
<Dialog.Close asChild>
|
|
127
127
|
<Button size="$3">{t("help.close", "Close")}</Button>
|
|
128
128
|
</Dialog.Close>
|
|
@@ -140,12 +140,12 @@ export function HelpShortcutsDialog({ open, onOpenChange }: HelpShortcutsDialogP
|
|
|
140
140
|
{t("help.shortcutsIntro", "Shortcuts available across the backoffice (web).")}
|
|
141
141
|
</Paragraph>
|
|
142
142
|
</Dialog.Description>
|
|
143
|
-
<YStack gap="$3"
|
|
143
|
+
<YStack gap="$3" paddingTop="$2">
|
|
144
144
|
{rows.map((row) => (
|
|
145
145
|
<ShortcutRow key={row.description} {...row} />
|
|
146
146
|
))}
|
|
147
147
|
</YStack>
|
|
148
|
-
<XStack justifyContent="flex-end"
|
|
148
|
+
<XStack justifyContent="flex-end" paddingTop="$4">
|
|
149
149
|
<Dialog.Close asChild>
|
|
150
150
|
<Button size="$3">{t("help.close", "Close")}</Button>
|
|
151
151
|
</Dialog.Close>
|
|
@@ -86,15 +86,14 @@ function NotificationRow({ item, onPress }: { item: NotificationItem; onPress: (
|
|
|
86
86
|
) : null}
|
|
87
87
|
</YStack>
|
|
88
88
|
{item.unread && (
|
|
89
|
-
<View
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
/>
|
|
89
|
+
<View paddingTop="$1.5" flexShrink={0} aria-hidden>
|
|
90
|
+
<View
|
|
91
|
+
width="$0.75"
|
|
92
|
+
height="$0.75"
|
|
93
|
+
borderRadius={1000}
|
|
94
|
+
backgroundColor="$accentBackground"
|
|
95
|
+
/>
|
|
96
|
+
</View>
|
|
98
97
|
)}
|
|
99
98
|
</XStack>
|
|
100
99
|
);
|