@multiplatform.one/backoffice 7.28.1 → 7.29.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/README.md +1 -1
- package/dist/cjs/desk/DeskSurface.native.js.map +1 -1
- package/dist/cjs/form/DocFormScreen.cjs +167 -224
- package/dist/cjs/form/DocFormScreen.native.js +182 -259
- package/dist/cjs/form/DocFormScreen.native.js.map +1 -1
- package/dist/cjs/i18n/en.cjs +1 -0
- package/dist/cjs/i18n/en.native.js +1 -0
- package/dist/cjs/i18n/en.native.js.map +1 -1
- package/dist/cjs/list/ListBulkActionsBar.cjs +30 -18
- package/dist/cjs/list/ListBulkActionsBar.native.js +43 -31
- package/dist/cjs/list/ListBulkActionsBar.native.js.map +1 -1
- package/dist/cjs/list/sidebar-section.cjs +15 -7
- package/dist/cjs/list/sidebar-section.native.js +16 -7
- package/dist/cjs/list/sidebar-section.native.js.map +1 -1
- package/dist/cjs/report/DoctypeReportView.cjs +20 -374
- package/dist/cjs/report/DoctypeReportView.native.js +23 -455
- package/dist/cjs/report/DoctypeReportView.native.js.map +1 -1
- package/dist/cjs/report/QueryReportScreen.cjs +33 -23
- package/dist/cjs/report/QueryReportScreen.native.js +33 -23
- package/dist/cjs/report/QueryReportScreen.native.js.map +1 -1
- package/dist/cjs/report/queryReportColumns.cjs +3 -105
- package/dist/cjs/report/queryReportColumns.native.js +2 -129
- package/dist/cjs/report/queryReportColumns.native.js.map +1 -1
- package/dist/cjs/report/queryReportFilterBar.cjs +3 -4
- package/dist/cjs/report/queryReportFilterBar.native.js +3 -4
- package/dist/cjs/report/queryReportFilterBar.native.js.map +1 -1
- package/dist/cjs/report/queryReportUrlState.cjs +2 -2
- package/dist/cjs/report/queryReportUrlState.native.js +2 -2
- package/dist/cjs/report/queryReportUrlState.native.js.map +1 -1
- package/dist/cjs/report/report-grid.cjs +2 -426
- package/dist/cjs/report/report-grid.native.js +1 -479
- package/dist/cjs/report/report-grid.native.js.map +1 -1
- package/dist/cjs/report/report-toolbar.cjs +2 -228
- package/dist/cjs/report/report-toolbar.native.js +1 -274
- package/dist/cjs/report/report-toolbar.native.js.map +1 -1
- package/dist/cjs/report/reportShapes.cjs +12 -383
- package/dist/cjs/report/reportShapes.native.js +12 -501
- package/dist/cjs/report/reportShapes.native.js.map +1 -1
- package/dist/cjs/report/reportViewModel.cjs +20 -360
- package/dist/cjs/report/reportViewModel.native.js +19 -627
- package/dist/cjs/report/reportViewModel.native.js.map +1 -1
- package/dist/cjs/report/savedReports.cjs +2 -106
- package/dist/cjs/report/savedReports.native.js +1 -132
- package/dist/cjs/report/savedReports.native.js.map +1 -1
- package/dist/cjs/report/useQueryReport.cjs +11 -219
- package/dist/cjs/report/useQueryReport.native.js +11 -244
- package/dist/cjs/report/useQueryReport.native.js.map +1 -1
- package/dist/cjs/shared/measures.cjs +3 -5
- package/dist/cjs/shared/measures.native.js +3 -5
- package/dist/cjs/shared/measures.native.js.map +1 -1
- package/dist/cjs/views/ReportView.cjs +3 -256
- package/dist/cjs/views/ReportView.native.js +3 -382
- package/dist/cjs/views/ReportView.native.js.map +1 -1
- package/dist/cjs/views/report-controls.cjs +6 -6
- package/dist/cjs/views/report-controls.native.js +6 -6
- package/dist/cjs/views/report-controls.native.js.map +1 -1
- package/dist/esm/desk/DeskSurface.mjs.map +1 -1
- package/dist/esm/desk/DeskSurface.native.js.map +1 -1
- package/dist/esm/form/DocFormScreen.mjs +169 -226
- package/dist/esm/form/DocFormScreen.mjs.map +1 -1
- package/dist/esm/form/DocFormScreen.native.js +184 -261
- package/dist/esm/form/DocFormScreen.native.js.map +1 -1
- package/dist/esm/i18n/en.mjs +1 -0
- package/dist/esm/i18n/en.mjs.map +1 -1
- package/dist/esm/i18n/en.native.js +1 -0
- package/dist/esm/i18n/en.native.js.map +1 -1
- package/dist/esm/list/ListBulkActionsBar.mjs +31 -19
- package/dist/esm/list/ListBulkActionsBar.mjs.map +1 -1
- package/dist/esm/list/ListBulkActionsBar.native.js +37 -25
- package/dist/esm/list/ListBulkActionsBar.native.js.map +1 -1
- package/dist/esm/list/sidebar-section.mjs +16 -8
- package/dist/esm/list/sidebar-section.mjs.map +1 -1
- package/dist/esm/list/sidebar-section.native.js +17 -8
- package/dist/esm/list/sidebar-section.native.js.map +1 -1
- package/dist/esm/report/DoctypeReportView.mjs +21 -375
- package/dist/esm/report/DoctypeReportView.mjs.map +1 -1
- package/dist/esm/report/DoctypeReportView.native.js +24 -456
- package/dist/esm/report/DoctypeReportView.native.js.map +1 -1
- package/dist/esm/report/QueryReportScreen.mjs +26 -16
- package/dist/esm/report/QueryReportScreen.mjs.map +1 -1
- package/dist/esm/report/QueryReportScreen.native.js +26 -16
- package/dist/esm/report/QueryReportScreen.native.js.map +1 -1
- package/dist/esm/report/queryReportColumns.mjs +1 -103
- package/dist/esm/report/queryReportColumns.mjs.map +1 -1
- package/dist/esm/report/queryReportColumns.native.js +1 -117
- package/dist/esm/report/queryReportColumns.native.js.map +1 -1
- package/dist/esm/report/queryReportFilterBar.mjs +1 -2
- package/dist/esm/report/queryReportFilterBar.mjs.map +1 -1
- package/dist/esm/report/queryReportFilterBar.native.js +1 -2
- package/dist/esm/report/queryReportFilterBar.native.js.map +1 -1
- package/dist/esm/report/queryReportUrlState.mjs +1 -1
- package/dist/esm/report/queryReportUrlState.native.js +1 -1
- package/dist/esm/report/report-grid.mjs +1 -425
- package/dist/esm/report/report-grid.mjs.map +1 -1
- package/dist/esm/report/report-grid.native.js +1 -468
- package/dist/esm/report/report-grid.native.js.map +1 -1
- package/dist/esm/report/report-toolbar.mjs +2 -228
- package/dist/esm/report/report-toolbar.mjs.map +1 -1
- package/dist/esm/report/report-toolbar.native.js +2 -264
- package/dist/esm/report/report-toolbar.native.js.map +1 -1
- package/dist/esm/report/reportShapes.mjs +2 -373
- package/dist/esm/report/reportShapes.mjs.map +1 -1
- package/dist/esm/report/reportShapes.native.js +2 -491
- package/dist/esm/report/reportShapes.native.js.map +1 -1
- package/dist/esm/report/reportViewModel.mjs +2 -342
- package/dist/esm/report/reportViewModel.mjs.map +1 -1
- package/dist/esm/report/reportViewModel.native.js +2 -610
- package/dist/esm/report/reportViewModel.native.js.map +1 -1
- package/dist/esm/report/savedReports.mjs +1 -105
- package/dist/esm/report/savedReports.mjs.map +1 -1
- package/dist/esm/report/savedReports.native.js +1 -132
- package/dist/esm/report/savedReports.native.js.map +1 -1
- package/dist/esm/report/useQueryReport.mjs +2 -210
- package/dist/esm/report/useQueryReport.mjs.map +1 -1
- package/dist/esm/report/useQueryReport.native.js +2 -235
- package/dist/esm/report/useQueryReport.native.js.map +1 -1
- package/dist/esm/shared/measures.mjs +1 -3
- package/dist/esm/shared/measures.mjs.map +1 -1
- package/dist/esm/shared/measures.native.js +1 -3
- package/dist/esm/shared/measures.native.js.map +1 -1
- package/dist/esm/views/ReportView.mjs +3 -256
- package/dist/esm/views/ReportView.mjs.map +1 -1
- package/dist/esm/views/ReportView.native.js +3 -382
- package/dist/esm/views/ReportView.native.js.map +1 -1
- package/dist/esm/views/report-controls.mjs +3 -3
- package/dist/esm/views/report-controls.mjs.map +1 -1
- package/dist/esm/views/report-controls.native.js +3 -3
- package/dist/esm/views/report-controls.native.js.map +1 -1
- package/dist/jsx/desk/DeskSurface.mjs.map +1 -1
- package/dist/jsx/desk/DeskSurface.native.js.map +1 -1
- package/dist/jsx/form/DocFormScreen.mjs +169 -226
- package/dist/jsx/form/DocFormScreen.mjs.map +1 -1
- package/dist/jsx/form/DocFormScreen.native.js +182 -259
- package/dist/jsx/form/DocFormScreen.native.js.map +1 -1
- package/dist/jsx/i18n/en.mjs +1 -0
- package/dist/jsx/i18n/en.mjs.map +1 -1
- package/dist/jsx/i18n/en.native.js +1 -0
- package/dist/jsx/i18n/en.native.js.map +1 -1
- package/dist/jsx/list/ListBulkActionsBar.mjs +31 -19
- package/dist/jsx/list/ListBulkActionsBar.mjs.map +1 -1
- package/dist/jsx/list/ListBulkActionsBar.native.js +43 -31
- package/dist/jsx/list/ListBulkActionsBar.native.js.map +1 -1
- package/dist/jsx/list/sidebar-section.mjs +16 -8
- package/dist/jsx/list/sidebar-section.mjs.map +1 -1
- package/dist/jsx/list/sidebar-section.native.js +16 -7
- package/dist/jsx/list/sidebar-section.native.js.map +1 -1
- package/dist/jsx/report/DoctypeReportView.mjs +21 -375
- package/dist/jsx/report/DoctypeReportView.mjs.map +1 -1
- package/dist/jsx/report/DoctypeReportView.native.js +23 -455
- package/dist/jsx/report/DoctypeReportView.native.js.map +1 -1
- package/dist/jsx/report/QueryReportScreen.mjs +26 -16
- package/dist/jsx/report/QueryReportScreen.mjs.map +1 -1
- package/dist/jsx/report/QueryReportScreen.native.js +33 -23
- package/dist/jsx/report/QueryReportScreen.native.js.map +1 -1
- package/dist/jsx/report/queryReportColumns.mjs +1 -103
- package/dist/jsx/report/queryReportColumns.mjs.map +1 -1
- package/dist/jsx/report/queryReportColumns.native.js +2 -129
- package/dist/jsx/report/queryReportColumns.native.js.map +1 -1
- package/dist/jsx/report/queryReportFilterBar.mjs +1 -2
- package/dist/jsx/report/queryReportFilterBar.mjs.map +1 -1
- package/dist/jsx/report/queryReportFilterBar.native.js +3 -4
- package/dist/jsx/report/queryReportFilterBar.native.js.map +1 -1
- package/dist/jsx/report/queryReportUrlState.mjs +1 -1
- package/dist/jsx/report/queryReportUrlState.native.js +2 -2
- package/dist/jsx/report/queryReportUrlState.native.js.map +1 -1
- package/dist/jsx/report/report-grid.mjs +1 -425
- package/dist/jsx/report/report-grid.mjs.map +1 -1
- package/dist/jsx/report/report-grid.native.js +1 -479
- package/dist/jsx/report/report-grid.native.js.map +1 -1
- package/dist/jsx/report/report-toolbar.mjs +2 -228
- package/dist/jsx/report/report-toolbar.mjs.map +1 -1
- package/dist/jsx/report/report-toolbar.native.js +1 -274
- package/dist/jsx/report/report-toolbar.native.js.map +1 -1
- package/dist/jsx/report/reportShapes.mjs +2 -373
- package/dist/jsx/report/reportShapes.mjs.map +1 -1
- package/dist/jsx/report/reportShapes.native.js +12 -501
- package/dist/jsx/report/reportShapes.native.js.map +1 -1
- package/dist/jsx/report/reportViewModel.mjs +2 -342
- package/dist/jsx/report/reportViewModel.mjs.map +1 -1
- package/dist/jsx/report/reportViewModel.native.js +19 -627
- package/dist/jsx/report/reportViewModel.native.js.map +1 -1
- package/dist/jsx/report/savedReports.mjs +1 -105
- package/dist/jsx/report/savedReports.mjs.map +1 -1
- package/dist/jsx/report/savedReports.native.js +1 -132
- package/dist/jsx/report/savedReports.native.js.map +1 -1
- package/dist/jsx/report/useQueryReport.mjs +2 -210
- package/dist/jsx/report/useQueryReport.mjs.map +1 -1
- package/dist/jsx/report/useQueryReport.native.js +11 -244
- package/dist/jsx/report/useQueryReport.native.js.map +1 -1
- package/dist/jsx/shared/measures.mjs +1 -3
- package/dist/jsx/shared/measures.mjs.map +1 -1
- package/dist/jsx/shared/measures.native.js +3 -5
- package/dist/jsx/shared/measures.native.js.map +1 -1
- package/dist/jsx/views/ReportView.mjs +3 -256
- package/dist/jsx/views/ReportView.mjs.map +1 -1
- package/dist/jsx/views/ReportView.native.js +3 -382
- package/dist/jsx/views/ReportView.native.js.map +1 -1
- package/dist/jsx/views/report-controls.mjs +3 -3
- package/dist/jsx/views/report-controls.mjs.map +1 -1
- package/dist/jsx/views/report-controls.native.js +6 -6
- package/dist/jsx/views/report-controls.native.js.map +1 -1
- package/package.json +12 -12
- package/src/backofficeScreens.designLaw.spec.ts +0 -5
- package/src/desk/DeskSurface.tsx +4 -7
- package/src/form/DocFormScreen.chrome.spec.ts +77 -0
- package/src/form/DocFormScreen.guest.screen.spec.tsx +175 -0
- package/src/form/DocFormScreen.rights.spec.tsx +7 -3
- package/src/form/DocFormScreen.tsx +214 -315
- package/src/i18n/en.ts +1 -0
- package/src/list/ListBulkActionsBar.rights.spec.tsx +134 -0
- package/src/list/ListBulkActionsBar.screen.spec.tsx +7 -1
- package/src/list/ListBulkActionsBar.tsx +76 -57
- package/src/list/ListFilterBar.screen.spec.tsx +14 -13
- package/src/list/sidebar-section.tsx +11 -3
- package/src/report/DoctypeReportView.tsx +28 -550
- package/src/report/QueryReportScreen.spec.tsx +75 -1
- package/src/report/QueryReportScreen.tsx +38 -22
- package/src/report/queryReportColumns.tsx +4 -123
- package/src/report/queryReportFilterBar.tsx +3 -5
- package/src/report/queryReportUrlState.ts +1 -1
- package/src/report/report-grid.tsx +4 -529
- package/src/report/report-toolbar.tsx +4 -308
- package/src/report/reportShapes.ts +25 -613
- package/src/report/reportShim.spec.ts +162 -0
- package/src/report/reportViewModel.ts +32 -605
- package/src/report/savedReports.ts +4 -161
- package/src/report/useQueryReport.ts +9 -347
- package/src/shared/measures.ts +3 -11
- package/src/views/ReportView.screen.spec.tsx +25 -20
- package/src/views/ReportView.tsx +7 -439
- package/src/views/report-controls.tsx +12 -20
- package/types/desk/DeskSurface.d.ts.map +1 -1
- package/types/form/DocFormScreen.d.ts +2 -2
- package/types/form/DocFormScreen.d.ts.map +1 -1
- package/types/i18n/en.d.ts +1 -0
- package/types/i18n/en.d.ts.map +1 -1
- package/types/list/ListBulkActionsBar.d.ts.map +1 -1
- package/types/list/sidebar-section.d.ts.map +1 -1
- package/types/report/DoctypeReportView.d.ts.map +1 -1
- package/types/report/QueryReportScreen.d.ts +1 -1
- package/types/report/QueryReportScreen.d.ts.map +1 -1
- package/types/report/queryReportColumns.d.ts +2 -11
- package/types/report/queryReportColumns.d.ts.map +1 -1
- package/types/report/queryReportFilterBar.d.ts +1 -1
- package/types/report/queryReportFilterBar.d.ts.map +1 -1
- package/types/report/queryReportUrlState.d.ts +1 -1
- package/types/report/queryReportUrlState.d.ts.map +1 -1
- package/types/report/report-grid.d.ts +2 -19
- package/types/report/report-grid.d.ts.map +1 -1
- package/types/report/report-toolbar.d.ts +2 -29
- package/types/report/report-toolbar.d.ts.map +1 -1
- package/types/report/reportShapes.d.ts +2 -115
- package/types/report/reportShapes.d.ts.map +1 -1
- package/types/report/reportViewModel.d.ts +2 -135
- package/types/report/reportViewModel.d.ts.map +1 -1
- package/types/report/savedReports.d.ts +2 -18
- package/types/report/savedReports.d.ts.map +1 -1
- package/types/report/useQueryReport.d.ts +2 -35
- package/types/report/useQueryReport.d.ts.map +1 -1
- package/types/shared/measures.d.ts +1 -2
- package/types/shared/measures.d.ts.map +1 -1
- package/types/views/ReportView.d.ts +3 -22
- package/types/views/ReportView.d.ts.map +1 -1
- package/types/views/report-controls.d.ts +2 -5
- package/types/views/report-controls.d.ts.map +1 -1
- package/src/report/report-grid.spec.tsx +0 -175
- package/src/report/report-toolbar.spec.tsx +0 -90
- package/src/report/reportShapes.spec.ts +0 -391
- package/src/report/reportViewModel.spec.ts +0 -393
|
@@ -1,62 +1,18 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// device (@multiplatform.one/store) while no saved report is selected
|
|
9
|
-
// - a per-column FILTER ROW driving the standard frappe filter pipeline
|
|
10
|
-
// (kind→operator mapping shared with the list filter vocabulary)
|
|
11
|
-
// - ONE toolbar row (picker / group-by / saved reports / chips / export);
|
|
12
|
-
// page size rides the footer pager like the list screen
|
|
13
|
-
// - saved reports on the `Report` doctype (desk round-trip) selected via
|
|
14
|
-
// the ?report= search param; fixture mode keeps them in memory
|
|
15
|
-
// - live rows through the house collection channel: the FULL filtered
|
|
16
|
-
// result stays client-side (totals + export need it), ordered
|
|
17
|
-
// deterministically and windowed into pages client-side
|
|
18
|
-
import { EmptyState, ScrollView, YStack, isWeb, notify } from "@multiplatform.one/components";
|
|
19
|
-
import { Button } from "@multiplatform.one/forms";
|
|
20
|
-
import {
|
|
21
|
-
Order,
|
|
22
|
-
useFrappeCollection,
|
|
23
|
-
useLiveQuery,
|
|
24
|
-
type UseFrappeCollectionConfig,
|
|
25
|
-
} from "@multiplatform.one/frappe";
|
|
26
|
-
import { downloadBlob } from "@multiplatform.one/platform";
|
|
27
|
-
import { storage } from "@multiplatform.one/store";
|
|
28
|
-
import { DataTablePagination } from "@multiplatform.one/table";
|
|
29
|
-
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
|
30
|
-
import { useTranslation } from "react-i18next";
|
|
1
|
+
// The desk's report view (view=report): FrappeReportBuilder, the doctype
|
|
2
|
+
// report view frappe-ui exports, under the desk's navigation (MPO-15,
|
|
3
|
+
// GL-28). This file only wires the desk in: the `?f=` base filters, the
|
|
4
|
+
// `?report=` saved-report selection, the desk scope, and row and Link
|
|
5
|
+
// presses to the form route.
|
|
6
|
+
import { FrappeReportBuilder, parseReportFiltersParam } from "@multiplatform.one/frappe-ui";
|
|
7
|
+
import React, { useCallback, useMemo } from "react";
|
|
31
8
|
import {
|
|
32
9
|
useBackofficeLocation,
|
|
33
10
|
useBackofficeNavigator,
|
|
34
11
|
useBackofficePaths,
|
|
35
12
|
} from "../config/BackofficeProvider";
|
|
36
|
-
import { BACKOFFICE_I18N_NAMESPACE } from "../i18n";
|
|
37
13
|
import { useDeskScope } from "../desk/scope";
|
|
38
14
|
import type { FrappeFilters } from "../shared/types";
|
|
39
|
-
import { useDoctypeMeta } from "../shared/useDoctypeMeta";
|
|
40
|
-
import { useViewConnection } from "../views/connection";
|
|
41
15
|
import { ViewError, ViewLoading } from "../views/ViewStates";
|
|
42
|
-
import { ReportGrid } from "./report-grid";
|
|
43
|
-
import { ReportToolbar } from "./report-toolbar";
|
|
44
|
-
import { useSavedReports } from "./savedReports";
|
|
45
|
-
import {
|
|
46
|
-
DEFAULT_REPORT_PAGE_SIZE,
|
|
47
|
-
REPORT_PAGE_SIZES,
|
|
48
|
-
buildGridRows,
|
|
49
|
-
buildReportCsv,
|
|
50
|
-
columnFilterTuples,
|
|
51
|
-
computeTotals,
|
|
52
|
-
defaultReportFieldnames,
|
|
53
|
-
orderReportRows,
|
|
54
|
-
parseReportFiltersParam,
|
|
55
|
-
reportColumnsFromMeta,
|
|
56
|
-
resolveSelectedColumns,
|
|
57
|
-
type ReportRow,
|
|
58
|
-
type ReportSortState,
|
|
59
|
-
} from "./reportViewModel";
|
|
60
16
|
|
|
61
17
|
export interface DoctypeReportViewProps {
|
|
62
18
|
/** Proper-cased doctype name (e.g. "ToDo", "Sales Order") */
|
|
@@ -71,54 +27,18 @@ export interface DoctypeReportViewProps {
|
|
|
71
27
|
initialFilters?: FrappeFilters;
|
|
72
28
|
}
|
|
73
29
|
|
|
74
|
-
const EMPTY_FILTERS: FrappeFilters = [];
|
|
75
|
-
const COLUMN_FILTER_DEBOUNCE_MS = 350;
|
|
76
|
-
|
|
77
|
-
function columnsStorageKey(doctype: string): string {
|
|
78
|
-
return `backoffice.report.columns.${doctype}`;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function readStoredColumns(doctype: string): Promise<string[] | undefined> {
|
|
82
|
-
try {
|
|
83
|
-
const raw = await Promise.resolve(storage.getItem(columnsStorageKey(doctype)));
|
|
84
|
-
if (!raw) return undefined;
|
|
85
|
-
const parsed: unknown = JSON.parse(raw);
|
|
86
|
-
if (Array.isArray(parsed) && parsed.every((entry) => typeof entry === "string")) {
|
|
87
|
-
return parsed as string[];
|
|
88
|
-
}
|
|
89
|
-
} catch {
|
|
90
|
-
// storage unavailable or corrupted — defaults win
|
|
91
|
-
}
|
|
92
|
-
return undefined;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function persistStoredColumns(doctype: string, fieldnames: readonly string[]): void {
|
|
96
|
-
try {
|
|
97
|
-
void Promise.resolve(storage.setItem(columnsStorageKey(doctype), JSON.stringify(fieldnames)));
|
|
98
|
-
} catch {
|
|
99
|
-
// storage unavailable — selection stays session-only
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
30
|
export function DoctypeReportView({
|
|
104
31
|
doctype,
|
|
105
32
|
filtersParam,
|
|
106
33
|
initialFilters,
|
|
107
34
|
}: DoctypeReportViewProps) {
|
|
108
|
-
const { t } = useTranslation(BACKOFFICE_I18N_NAMESPACE);
|
|
109
35
|
const deskScope = useDeskScope();
|
|
110
36
|
const navigator = useBackofficeNavigator();
|
|
111
37
|
const paths = useBackofficePaths();
|
|
112
38
|
const { params } = useBackofficeLocation();
|
|
113
|
-
const connection = useViewConnection();
|
|
114
|
-
const fixtureMode = Boolean(connection?.fixtures);
|
|
115
|
-
const { meta, loading: metaLoading, error: metaError } = useDoctypeMeta(doctype);
|
|
116
|
-
const savedReports = useSavedReports(doctype);
|
|
117
39
|
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
// contract as the `?f=` parser: percent-decoding a decoded value is a
|
|
121
|
-
// no-op for names without a literal %).
|
|
40
|
+
// The router may hand `?report=` through raw — decode defensively (same
|
|
41
|
+
// contract as the `?f=` parser).
|
|
122
42
|
const rawReportParam = typeof params.report === "string" ? params.report : params.report?.[0];
|
|
123
43
|
const currentReport = useMemo(() => {
|
|
124
44
|
if (!rawReportParam) return null;
|
|
@@ -128,477 +48,35 @@ export function DoctypeReportView({
|
|
|
128
48
|
return rawReportParam;
|
|
129
49
|
}
|
|
130
50
|
}, [rawReportParam]);
|
|
131
|
-
|
|
132
|
-
// ── Columns: defaults → device-stored → explicit override ───
|
|
133
|
-
const allColumns = useMemo(() => reportColumnsFromMeta(meta), [meta]);
|
|
134
|
-
const defaultFieldnames = useMemo(() => defaultReportFieldnames(meta), [meta]);
|
|
135
|
-
const [storedColumns, setStoredColumns] = useState<string[] | undefined>(undefined);
|
|
136
|
-
const [selectedOverride, setSelectedOverride] = useState<string[] | null>(null);
|
|
137
|
-
|
|
138
|
-
useEffect(() => {
|
|
139
|
-
if (fixtureMode) return;
|
|
140
|
-
let cancelled = false;
|
|
141
|
-
void readStoredColumns(doctype).then((stored) => {
|
|
142
|
-
if (!cancelled) setStoredColumns(stored);
|
|
143
|
-
});
|
|
144
|
-
return () => {
|
|
145
|
-
cancelled = true;
|
|
146
|
-
};
|
|
147
|
-
}, [doctype, fixtureMode]);
|
|
148
|
-
|
|
149
|
-
const selectedFieldnames = selectedOverride ?? storedColumns ?? defaultFieldnames;
|
|
150
|
-
const selectedColumns = useMemo(
|
|
151
|
-
() => resolveSelectedColumns(allColumns, selectedFieldnames),
|
|
152
|
-
[allColumns, selectedFieldnames],
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
// ── Filters: ?f= base channel + the per-column filter row ───
|
|
156
51
|
const urlFilters = useMemo(() => parseReportFiltersParam(filtersParam), [filtersParam]);
|
|
157
|
-
const [filtersCleared, setFiltersCleared] = useState(false);
|
|
158
|
-
const [reportFilters, setReportFilters] = useState<FrappeFilters | null>(null);
|
|
159
|
-
const baseFilters = filtersCleared
|
|
160
|
-
? EMPTY_FILTERS
|
|
161
|
-
: (reportFilters ?? urlFilters ?? initialFilters ?? EMPTY_FILTERS);
|
|
162
|
-
|
|
163
|
-
const [filterDrafts, setFilterDrafts] = useState<Record<string, string>>({});
|
|
164
|
-
const [committedFilters, setCommittedFilters] = useState<Record<string, string>>({});
|
|
165
|
-
useEffect(() => {
|
|
166
|
-
const handle = setTimeout(() => {
|
|
167
|
-
setCommittedFilters((previous) =>
|
|
168
|
-
JSON.stringify(previous) === JSON.stringify(filterDrafts) ? previous : filterDrafts,
|
|
169
|
-
);
|
|
170
|
-
}, COLUMN_FILTER_DEBOUNCE_MS);
|
|
171
|
-
return () => clearTimeout(handle);
|
|
172
|
-
}, [filterDrafts]);
|
|
173
|
-
|
|
174
|
-
const columnTuples = useMemo(
|
|
175
|
-
() => columnFilterTuples(selectedColumns, committedFilters),
|
|
176
|
-
[selectedColumns, committedFilters],
|
|
177
|
-
);
|
|
178
|
-
// Desk scope (GL-5) ANDs into every report query, invisible to the
|
|
179
|
-
// filter chrome and not clearable by the user.
|
|
180
|
-
const effectiveFilters = useMemo(
|
|
181
|
-
() => [...deskScope, ...baseFilters, ...columnTuples],
|
|
182
|
-
[deskScope, baseFilters, columnTuples],
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
// ── Sort / group / paging ───────────────────────────────────
|
|
186
|
-
const metaSort = useMemo<ReportSortState>(
|
|
187
|
-
() => ({
|
|
188
|
-
field: (meta?.sort_field || "modified").split(" ")[0],
|
|
189
|
-
order: (meta?.sort_order || "DESC").toLowerCase() === "asc" ? "asc" : "desc",
|
|
190
|
-
}),
|
|
191
|
-
[meta?.sort_field, meta?.sort_order],
|
|
192
|
-
);
|
|
193
|
-
const [sortOverride, setSortOverride] = useState<ReportSortState | null>(null);
|
|
194
|
-
const sort = sortOverride ?? metaSort;
|
|
195
|
-
const [groupBy, setGroupBy] = useState<string | null>(null);
|
|
196
|
-
const [pageSize, setPageSize] = useState<number>(DEFAULT_REPORT_PAGE_SIZE);
|
|
197
|
-
const [page, setPage] = useState(0);
|
|
198
|
-
|
|
199
|
-
// Doctype swap resets every view state (route component is reused).
|
|
200
|
-
useEffect(() => {
|
|
201
|
-
setSelectedOverride(null);
|
|
202
|
-
setStoredColumns(undefined);
|
|
203
|
-
setSortOverride(null);
|
|
204
|
-
setGroupBy(null);
|
|
205
|
-
setPage(0);
|
|
206
|
-
setPageSize(DEFAULT_REPORT_PAGE_SIZE);
|
|
207
|
-
setFilterDrafts({});
|
|
208
|
-
setCommittedFilters({});
|
|
209
|
-
setFiltersCleared(false);
|
|
210
|
-
setReportFilters(null);
|
|
211
|
-
}, [doctype]);
|
|
212
|
-
|
|
213
|
-
useEffect(() => setFiltersCleared(false), [filtersParam]);
|
|
214
|
-
|
|
215
|
-
const filtersKey = useMemo(() => JSON.stringify(effectiveFilters), [effectiveFilters]);
|
|
216
|
-
useEffect(() => {
|
|
217
|
-
setPage(0);
|
|
218
|
-
}, [filtersKey, pageSize, groupBy]);
|
|
219
|
-
|
|
220
|
-
// ── Apply a saved report's config when ?report= changes ─────
|
|
221
|
-
useEffect(() => {
|
|
222
|
-
if (!currentReport) {
|
|
223
|
-
setReportFilters(null);
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
let cancelled = false;
|
|
227
|
-
void savedReports.load(currentReport).then((config) => {
|
|
228
|
-
if (cancelled || !config) return;
|
|
229
|
-
setSelectedOverride(config.fieldnames.length > 0 ? config.fieldnames : null);
|
|
230
|
-
setReportFilters(config.filters);
|
|
231
|
-
setFilterDrafts({});
|
|
232
|
-
setCommittedFilters({});
|
|
233
|
-
setFiltersCleared(false);
|
|
234
|
-
if (config.sort) setSortOverride(config.sort);
|
|
235
|
-
if (config.pageSize && REPORT_PAGE_SIZES.some((size) => size === config.pageSize)) {
|
|
236
|
-
setPageSize(config.pageSize);
|
|
237
|
-
}
|
|
238
|
-
setGroupBy(config.groupBy ?? null);
|
|
239
|
-
});
|
|
240
|
-
return () => {
|
|
241
|
-
cancelled = true;
|
|
242
|
-
};
|
|
243
|
-
// savedReports.load changes identity with its report list; reloading the
|
|
244
|
-
// config on list refreshes would clobber in-progress edits — key on the
|
|
245
|
-
// selected report (and doctype via load's closure) only.
|
|
246
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
247
|
-
}, [currentReport, doctype]);
|
|
248
|
-
|
|
249
|
-
// ── Column selection changes ────────────────────────────────
|
|
250
|
-
const handleToggleColumn = useCallback(
|
|
251
|
-
(fieldname: string) => {
|
|
252
|
-
setSelectedOverride((previous) => {
|
|
253
|
-
const current = previous ?? storedColumns ?? defaultFieldnames;
|
|
254
|
-
const next = current.includes(fieldname)
|
|
255
|
-
? current.length === 1
|
|
256
|
-
? current
|
|
257
|
-
: current.filter((entry) => entry !== fieldname)
|
|
258
|
-
: [...current, fieldname];
|
|
259
|
-
if (!fixtureMode && !currentReport) persistStoredColumns(doctype, next);
|
|
260
|
-
return next;
|
|
261
|
-
});
|
|
262
|
-
},
|
|
263
|
-
[storedColumns, defaultFieldnames, fixtureMode, currentReport, doctype],
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
const handleResetColumns = useCallback(() => {
|
|
267
|
-
setSelectedOverride(defaultFieldnames);
|
|
268
|
-
setStoredColumns(undefined);
|
|
269
|
-
if (!fixtureMode) persistStoredColumns(doctype, defaultFieldnames);
|
|
270
|
-
}, [defaultFieldnames, fixtureMode, doctype]);
|
|
271
|
-
|
|
272
|
-
// ── Data: live collection over the full filtered result ─────
|
|
273
|
-
const fetchFields = useMemo(() => {
|
|
274
|
-
const fields = new Set<string>(["name", sort.field]);
|
|
275
|
-
for (const column of selectedColumns) fields.add(column.fieldname);
|
|
276
|
-
if (groupBy) fields.add(groupBy);
|
|
277
|
-
return [...fields];
|
|
278
|
-
}, [selectedColumns, sort.field, groupBy]);
|
|
279
52
|
|
|
280
|
-
const
|
|
281
|
-
const handleSyncError = useCallback((error: unknown) => {
|
|
282
|
-
setSyncError(error instanceof Error ? error : new Error(String(error)));
|
|
283
|
-
}, []);
|
|
284
|
-
const [retryGen, setRetryGen] = useState(0);
|
|
285
|
-
|
|
286
|
-
// ViewConnection carries syncOptions as `unknown`; the shapes match the
|
|
287
|
-
// collection config (same FrappeProvider source) — assert once here.
|
|
288
|
-
const collectionConfig = useMemo(
|
|
289
|
-
() =>
|
|
290
|
-
connection && meta && retryGen >= 0
|
|
291
|
-
? (connection as unknown as UseFrappeCollectionConfig)
|
|
292
|
-
: undefined,
|
|
293
|
-
[connection, meta, retryGen],
|
|
294
|
-
);
|
|
295
|
-
const collection = useFrappeCollection<ReportRow & { doctype: string }>(
|
|
296
|
-
collectionConfig,
|
|
297
|
-
doctype,
|
|
298
|
-
{
|
|
299
|
-
fields: fetchFields,
|
|
300
|
-
filters: effectiveFilters,
|
|
301
|
-
orderBy: { field: sort.field, order: sort.order === "asc" ? Order.Asc : Order.Desc },
|
|
302
|
-
onSyncError: handleSyncError,
|
|
303
|
-
},
|
|
304
|
-
);
|
|
305
|
-
useEffect(() => setSyncError(null), [collection]);
|
|
306
|
-
const { data: rowsData, isLoading: rowsLoading } = useLiveQuery(
|
|
307
|
-
(q) => (collection ? q.from({ _report: collection }) : null),
|
|
308
|
-
[collection],
|
|
309
|
-
);
|
|
310
|
-
|
|
311
|
-
const orderedRows = useMemo(
|
|
312
|
-
() => orderReportRows((rowsData as ReportRow[] | undefined) ?? [], sort, groupBy),
|
|
313
|
-
[rowsData, sort, groupBy],
|
|
314
|
-
);
|
|
315
|
-
|
|
316
|
-
const total = orderedRows.length;
|
|
317
|
-
const pageCount = Math.max(1, Math.ceil(total / pageSize));
|
|
318
|
-
const effectivePage = Math.min(page, pageCount - 1);
|
|
319
|
-
const gridRows = useMemo(
|
|
320
|
-
() => buildGridRows(orderedRows, effectivePage * pageSize, pageSize, groupBy),
|
|
321
|
-
[orderedRows, effectivePage, pageSize, groupBy],
|
|
322
|
-
);
|
|
323
|
-
const totals = useMemo(
|
|
324
|
-
() => computeTotals(selectedColumns, orderedRows),
|
|
325
|
-
[selectedColumns, orderedRows],
|
|
326
|
-
);
|
|
327
|
-
const groupByColumn = groupBy
|
|
328
|
-
? allColumns.find((column) => column.fieldname === groupBy)
|
|
329
|
-
: undefined;
|
|
330
|
-
|
|
331
|
-
// ── Navigation ──────────────────────────────────────────────
|
|
332
|
-
const handleOpenRow = useCallback(
|
|
333
|
-
(name: string) => navigator.navigate(paths.form(doctype, name)),
|
|
334
|
-
[navigator, paths, doctype],
|
|
335
|
-
);
|
|
336
|
-
const handleOpenLink = useCallback(
|
|
53
|
+
const openForm = useCallback(
|
|
337
54
|
(targetDoctype: string, name: string) => navigator.navigate(paths.form(targetDoctype, name)),
|
|
338
55
|
[navigator, paths],
|
|
339
56
|
);
|
|
340
|
-
|
|
341
|
-
const handleSelectReport = useCallback(
|
|
57
|
+
const selectReport = useCallback(
|
|
342
58
|
(name: string | null) => {
|
|
343
59
|
const base = paths.view(doctype, "report");
|
|
344
60
|
navigator.replace(name ? `${base}?report=${encodeURIComponent(name)}` : base);
|
|
345
|
-
if (!name) {
|
|
346
|
-
setReportFilters(null);
|
|
347
|
-
setSelectedOverride(null);
|
|
348
|
-
setSortOverride(null);
|
|
349
|
-
setGroupBy(null);
|
|
350
|
-
}
|
|
351
61
|
},
|
|
352
62
|
[navigator, paths, doctype],
|
|
353
63
|
);
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
})
|
|
372
|
-
.then((savedName) => {
|
|
373
|
-
notify({
|
|
374
|
-
severity: "success",
|
|
375
|
-
scope: "page",
|
|
376
|
-
body: t("report.saveDone", {
|
|
377
|
-
defaultValue: "Saved report “{{name}}”",
|
|
378
|
-
name: savedName,
|
|
379
|
-
}),
|
|
380
|
-
});
|
|
381
|
-
if (savedName !== currentReport) handleSelectReport(savedName);
|
|
382
|
-
})
|
|
383
|
-
.catch((error: unknown) => {
|
|
384
|
-
notify({
|
|
385
|
-
severity: "error",
|
|
386
|
-
scope: "page",
|
|
387
|
-
body:
|
|
388
|
-
error instanceof Error
|
|
389
|
-
? error.message
|
|
390
|
-
: t("report.saveFailed", "Could not save the report"),
|
|
391
|
-
});
|
|
392
|
-
});
|
|
393
|
-
},
|
|
394
|
-
[
|
|
395
|
-
savedReports,
|
|
396
|
-
selectedFieldnames,
|
|
397
|
-
effectiveFilters,
|
|
398
|
-
sort,
|
|
399
|
-
pageSize,
|
|
400
|
-
groupBy,
|
|
401
|
-
totals.length,
|
|
402
|
-
currentReport,
|
|
403
|
-
handleSelectReport,
|
|
404
|
-
t,
|
|
405
|
-
],
|
|
406
|
-
);
|
|
407
|
-
|
|
408
|
-
const handleDeleteReport = useCallback(
|
|
409
|
-
(name: string) => {
|
|
410
|
-
void savedReports
|
|
411
|
-
.remove(name)
|
|
412
|
-
.then(() => {
|
|
413
|
-
notify({
|
|
414
|
-
severity: "success",
|
|
415
|
-
scope: "page",
|
|
416
|
-
body: t("report.deleteDone", {
|
|
417
|
-
defaultValue: "Deleted report “{{name}}”",
|
|
418
|
-
name,
|
|
419
|
-
}),
|
|
420
|
-
});
|
|
421
|
-
if (name === currentReport) handleSelectReport(null);
|
|
422
|
-
})
|
|
423
|
-
.catch((error: unknown) => {
|
|
424
|
-
notify({
|
|
425
|
-
severity: "error",
|
|
426
|
-
scope: "page",
|
|
427
|
-
body:
|
|
428
|
-
error instanceof Error
|
|
429
|
-
? error.message
|
|
430
|
-
: t("report.deleteFailed", "Could not delete the report"),
|
|
431
|
-
});
|
|
432
|
-
});
|
|
433
|
-
},
|
|
434
|
-
[savedReports, currentReport, handleSelectReport, t],
|
|
64
|
+
const clearUrlFilters = useCallback(() => {
|
|
65
|
+
if (urlFilters) navigator.replace(paths.view(doctype, "report"));
|
|
66
|
+
}, [urlFilters, navigator, paths, doctype]);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<FrappeReportBuilder
|
|
70
|
+
doctype={doctype}
|
|
71
|
+
filters={urlFilters ?? initialFilters}
|
|
72
|
+
scopeFilters={deskScope}
|
|
73
|
+
onClearFilters={clearUrlFilters}
|
|
74
|
+
report={currentReport}
|
|
75
|
+
onReportChange={selectReport}
|
|
76
|
+
onOpenRow={(name) => openForm(doctype, name)}
|
|
77
|
+
onOpenLink={openForm}
|
|
78
|
+
loading={<ViewLoading />}
|
|
79
|
+
error={(error) => <ViewError doctype={doctype} error={error} />}
|
|
80
|
+
/>
|
|
435
81
|
);
|
|
436
|
-
|
|
437
|
-
// ── Export: full filtered + ordered result as CSV ───────────
|
|
438
|
-
const handleExport = useCallback(() => {
|
|
439
|
-
if (!isWeb) {
|
|
440
|
-
notify({
|
|
441
|
-
severity: "warning",
|
|
442
|
-
scope: "page",
|
|
443
|
-
body: t(
|
|
444
|
-
"report.exportUnsupported",
|
|
445
|
-
"CSV export needs a browser download — not available on this platform yet",
|
|
446
|
-
),
|
|
447
|
-
});
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
downloadBlob(
|
|
451
|
-
new Blob([buildReportCsv(selectedColumns, orderedRows)], {
|
|
452
|
-
type: "text/csv;charset=utf-8;",
|
|
453
|
-
}),
|
|
454
|
-
`${doctype}_report.csv`,
|
|
455
|
-
);
|
|
456
|
-
notify({
|
|
457
|
-
severity: "success",
|
|
458
|
-
scope: "page",
|
|
459
|
-
body: t("report.exportDone", {
|
|
460
|
-
count: orderedRows.length,
|
|
461
|
-
defaultValue_one: "Exported {{count}} row to CSV",
|
|
462
|
-
defaultValue_other: "Exported {{count}} rows to CSV",
|
|
463
|
-
}),
|
|
464
|
-
});
|
|
465
|
-
}, [selectedColumns, orderedRows, doctype, t]);
|
|
466
|
-
|
|
467
|
-
const handleColumnFilterChange = useCallback((fieldname: string, value: string) => {
|
|
468
|
-
setFilterDrafts((previous) => {
|
|
469
|
-
if (value === "") {
|
|
470
|
-
if (!(fieldname in previous)) return previous;
|
|
471
|
-
const { [fieldname]: _dropped, ...rest } = previous;
|
|
472
|
-
return rest;
|
|
473
|
-
}
|
|
474
|
-
return { ...previous, [fieldname]: value };
|
|
475
|
-
});
|
|
476
|
-
}, []);
|
|
477
|
-
|
|
478
|
-
const handleClearColumnFilters = useCallback(() => {
|
|
479
|
-
setFilterDrafts({});
|
|
480
|
-
setCommittedFilters({});
|
|
481
|
-
}, []);
|
|
482
|
-
|
|
483
|
-
const activeColumnFilterCount = columnTuples.length;
|
|
484
|
-
|
|
485
|
-
// ── Render ──────────────────────────────────────────────────
|
|
486
|
-
if (metaLoading && !meta) return <ViewLoading />;
|
|
487
|
-
if (metaError && !meta) return <ViewError doctype={doctype} error={metaError} />;
|
|
488
|
-
|
|
489
|
-
let body: React.ReactNode;
|
|
490
|
-
if (syncError) {
|
|
491
|
-
body = (
|
|
492
|
-
<EmptyState
|
|
493
|
-
intent="error"
|
|
494
|
-
title={t("report.loadFailed", "Couldn't load report rows")}
|
|
495
|
-
description={syncError.message}
|
|
496
|
-
action={
|
|
497
|
-
<Button
|
|
498
|
-
size="$3"
|
|
499
|
-
onPress={() => {
|
|
500
|
-
setSyncError(null);
|
|
501
|
-
setRetryGen((generation) => generation + 1);
|
|
502
|
-
}}
|
|
503
|
-
>
|
|
504
|
-
{t("report.retry", "Retry")}
|
|
505
|
-
</Button>
|
|
506
|
-
}
|
|
507
|
-
/>
|
|
508
|
-
);
|
|
509
|
-
} else if (rowsLoading && total === 0) {
|
|
510
|
-
body = <ViewLoading />;
|
|
511
|
-
} else if (total === 0) {
|
|
512
|
-
const hasFilters = effectiveFilters.length > 0;
|
|
513
|
-
body = (
|
|
514
|
-
<EmptyState
|
|
515
|
-
title={t("report.emptyTitle", "No results")}
|
|
516
|
-
description={
|
|
517
|
-
hasFilters
|
|
518
|
-
? t("report.emptyFiltered", "No rows match the current filters")
|
|
519
|
-
: t("report.emptyDescription", {
|
|
520
|
-
defaultValue: "There are no {{doctype}} records yet",
|
|
521
|
-
doctype,
|
|
522
|
-
})
|
|
523
|
-
}
|
|
524
|
-
action={
|
|
525
|
-
hasFilters ? (
|
|
526
|
-
<Button
|
|
527
|
-
size="$3"
|
|
528
|
-
onPress={() => {
|
|
529
|
-
handleClearColumnFilters();
|
|
530
|
-
handleClearBaseFilters();
|
|
531
|
-
}}
|
|
532
|
-
>
|
|
533
|
-
{t("report.clearAllFilters", "Clear all filters")}
|
|
534
|
-
</Button>
|
|
535
|
-
) : undefined
|
|
536
|
-
}
|
|
537
|
-
/>
|
|
538
|
-
);
|
|
539
|
-
} else {
|
|
540
|
-
body = (
|
|
541
|
-
<ReportGrid
|
|
542
|
-
columns={selectedColumns}
|
|
543
|
-
rows={gridRows}
|
|
544
|
-
sort={sort}
|
|
545
|
-
onSortChange={setSortOverride}
|
|
546
|
-
columnFilters={filterDrafts}
|
|
547
|
-
onColumnFilterChange={handleColumnFilterChange}
|
|
548
|
-
onOpenRow={handleOpenRow}
|
|
549
|
-
onOpenLink={handleOpenLink}
|
|
550
|
-
totals={totals}
|
|
551
|
-
groupByColumn={groupByColumn}
|
|
552
|
-
/>
|
|
553
|
-
);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
const content = (
|
|
557
|
-
<YStack flex={1} minWidth={0}>
|
|
558
|
-
<ReportToolbar
|
|
559
|
-
columns={allColumns}
|
|
560
|
-
selected={selectedFieldnames}
|
|
561
|
-
onToggleColumn={handleToggleColumn}
|
|
562
|
-
onResetColumns={handleResetColumns}
|
|
563
|
-
groupBy={groupBy}
|
|
564
|
-
onGroupByChange={setGroupBy}
|
|
565
|
-
baseFilters={baseFilters}
|
|
566
|
-
onClearBaseFilters={baseFilters.length > 0 ? handleClearBaseFilters : undefined}
|
|
567
|
-
columnFilterCount={activeColumnFilterCount}
|
|
568
|
-
onClearColumnFilters={handleClearColumnFilters}
|
|
569
|
-
savedReports={savedReports.reports}
|
|
570
|
-
currentReport={currentReport}
|
|
571
|
-
onSelectReport={handleSelectReport}
|
|
572
|
-
onSaveReport={handleSaveReport}
|
|
573
|
-
onDeleteReport={handleDeleteReport}
|
|
574
|
-
onExport={handleExport}
|
|
575
|
-
exportDisabled={total === 0}
|
|
576
|
-
/>
|
|
577
|
-
<YStack flex={1} minWidth={0} paddingHorizontal="$3">
|
|
578
|
-
{body}
|
|
579
|
-
</YStack>
|
|
580
|
-
{total > 0 && !syncError && (
|
|
581
|
-
<YStack paddingHorizontal="$3" paddingVertical="$2">
|
|
582
|
-
<DataTablePagination
|
|
583
|
-
pagination={{
|
|
584
|
-
pageIndex: effectivePage,
|
|
585
|
-
pageSize,
|
|
586
|
-
totalCount: total,
|
|
587
|
-
totalPages: pageCount,
|
|
588
|
-
}}
|
|
589
|
-
onPaginationChange={(next) => {
|
|
590
|
-
setPage(next.pageIndex);
|
|
591
|
-
setPageSize(next.pageSize);
|
|
592
|
-
}}
|
|
593
|
-
pageSizeOptions={[...REPORT_PAGE_SIZES]}
|
|
594
|
-
compact
|
|
595
|
-
/>
|
|
596
|
-
</YStack>
|
|
597
|
-
)}
|
|
598
|
-
</YStack>
|
|
599
|
-
);
|
|
600
|
-
|
|
601
|
-
// Native has no page-level scroll (DoctypeListScreen precedent) — wrap so
|
|
602
|
-
// rows below the fold stay reachable. Web keeps the flex layout.
|
|
603
|
-
return isWeb ? content : <ScrollView flex={1}>{content}</ScrollView>;
|
|
604
82
|
}
|