@antelopejs/dms 0.7.1 → 0.7.3
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/CHANGELOG.md +47 -0
- package/dist/hooks/tenant-instances.d.ts +1 -0
- package/dist/hooks/tenant-instances.js +13 -0
- package/dist/hooks/tenant-instances.js.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/test/unit/interfaces/dms/page-topics.test.js +67 -0
- package/dist/test/unit/interfaces/dms/page-topics.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/block-vocabulary.test.js +1 -0
- package/dist/test/unit/interfaces/dms-base/block-vocabulary.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/code-block-catalog.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms-base/code-block-catalog.test.js +46 -0
- package/dist/test/unit/interfaces/dms-base/code-block-catalog.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms-base/column-display.test.js +11 -0
- package/dist/test/unit/interfaces/dms-base/column-display.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/display-blocks-catalog.test.js +31 -0
- package/dist/test/unit/interfaces/dms-base/display-blocks-catalog.test.js.map +1 -1
- package/dist/test/unit/utils/tenant-instances.test.d.ts +1 -0
- package/dist/test/unit/utils/tenant-instances.test.js +53 -0
- package/dist/test/unit/utils/tenant-instances.test.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/tenant-instances.d.ts +2 -0
- package/dist/utils/tenant-instances.js +38 -0
- package/dist/utils/tenant-instances.js.map +1 -0
- package/dist/validation/auth.schema.d.ts +2 -2
- package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +4 -0
- package/frontend-vue/layers/dms-ui/app/build/components/activity-feed/activityFeedDays.ts +35 -23
- package/frontend-vue/layers/dms-ui/app/build/components/blocks/BlockActions.vue +34 -6
- package/frontend-vue/layers/dms-ui/app/build/components/containers/drawer/DynamicDrawer.vue +26 -8
- package/frontend-vue/layers/dms-ui/app/build/components/table/Actions.vue +3 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table/Empty.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table/MonoCell.vue +3 -2
- package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +24 -13
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/useActionTargets.ts +1 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/blocks/useBlockItems.ts +12 -5
- package/frontend-vue/layers/dms-ui/app/build/composables/blocks/useRefreshTriggers.ts +40 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/metricCells.ts +15 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +85 -22
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/clearTableFilters.ts +10 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/usePersistedPagination.ts +79 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableListData.ts +94 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +41 -5
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableQuery.ts +3 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/codeEditor.ts +2 -13
- package/frontend-vue/layers/dms-ui/app/build/utils/codeHighlight.ts +59 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/codeLanguages.ts +36 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/codePalette.ts +32 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/shellParser.ts +55 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/urlVariables.ts +77 -20
- package/frontend-vue/layers/dms-ui/app/components/activity-feed/ActivityFeed.vue +15 -4
- package/frontend-vue/layers/dms-ui/app/components/blocks/BannerBlock.vue +111 -25
- package/frontend-vue/layers/dms-ui/app/components/blocks/CodeBlock.vue +95 -0
- package/frontend-vue/layers/dms-ui/app/components/blocks/EmptyStateBlock.vue +18 -0
- package/frontend-vue/layers/dms-ui/app/components/blocks/KeyValueListBlock.vue +8 -0
- package/frontend-vue/layers/dms-ui/app/components/blocks/NavCardGridBlock.vue +21 -9
- package/frontend-vue/layers/dms-ui/app/components/blocks/StatGroupBlock.vue +8 -0
- package/frontend-vue/layers/dms-ui/app/components/code-snippet/CodeSnippet.vue +97 -0
- package/frontend-vue/layers/dms-ui/app/components/empty-state/EmptyState.vue +13 -4
- package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +14 -3
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputCode.vue +2 -8
- package/frontend-vue/layers/dms-ui/app/components/key-value-list/KeyValueList.vue +19 -0
- package/frontend-vue/layers/dms-ui/app/components/master-detail/MasterDetail.vue +54 -48
- package/frontend-vue/layers/dms-ui/app/components/meter/MeterBlock.vue +24 -9
- package/frontend-vue/layers/dms-ui/app/components/tab/Tab.vue +4 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +66 -74
- package/frontend-vue/layers/dms-ui/app/components/top-list/internal/TopListRow.vue +4 -3
- package/frontend-vue/layers/dms-ui/app/composables/chart/types.ts +5 -2
- package/frontend-vue/layers/dms-ui/app/composables/chart/useChartFetch.ts +2 -29
- package/frontend-vue/layers/dms-ui/app/composables/form/useForm.ts +2 -1
- package/frontend-vue/layers/dms-ui/app/composables/tab/types/props.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/action-target.ts +4 -0
- package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +2 -2
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +1 -0
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +1 -0
- package/frontend-vue/package.json +4 -3
- package/frontend-vue/pnpm-lock.yaml +8 -5
- package/frontend-vue/tests/activity-feed-days.test.ts +26 -12
- package/frontend-vue/tests/banner-block-fetch.test.ts +162 -0
- package/frontend-vue/tests/block-fetch-options-forwarding.test.ts +100 -0
- package/frontend-vue/tests/block-items-period-realtime.test.ts +147 -0
- package/frontend-vue/tests/block-route-params.test.ts +48 -0
- package/frontend-vue/tests/code-block.test.ts +155 -0
- package/frontend-vue/tests/code-highlight.test.ts +72 -0
- package/frontend-vue/tests/composed-text-blocks.test.ts +5 -0
- package/frontend-vue/tests/composed-text-more-blocks.test.ts +391 -0
- package/frontend-vue/tests/drawer-target-direction.test.ts +205 -0
- package/frontend-vue/tests/empty-state-late-slots.test.ts +53 -0
- package/frontend-vue/tests/flow-canvas-delete-badge.test.ts +88 -0
- package/frontend-vue/tests/form-redirect-placeholders.test.ts +11 -0
- package/frontend-vue/tests/key-value-list-copy.test.ts +114 -0
- package/frontend-vue/tests/master-detail-layout.test.ts +53 -0
- package/frontend-vue/tests/mono-cell-truncate.test.ts +50 -0
- package/frontend-vue/tests/nav-card-grid-empty-category.test.ts +1 -0
- package/frontend-vue/tests/page-fill-height.test.ts +20 -3
- package/frontend-vue/tests/route-param-occurrences.test.ts +9 -0
- package/frontend-vue/tests/table-actions-search-label.test.ts +147 -0
- package/frontend-vue/tests/table-clear-filters.test.ts +22 -0
- package/frontend-vue/tests/table-empty-body-viewport.test.ts +25 -0
- package/frontend-vue/tests/table-metric-cells.test.ts +22 -0
- package/frontend-vue/tests/table-pills-cell.test.ts +126 -0
- package/frontend-vue/tests/table-source-navigation.test.ts +123 -0
- package/frontend-vue/tests/table-view-hidden-filters-page.test.ts +137 -0
- package/frontend-vue/tests/table-view-refresh.test.ts +84 -0
- package/frontend-vue/tests/table-view-sources.test.ts +67 -0
- package/frontend-vue/tests/vue-inventory.test.ts +1 -1
- package/package.json +2 -2
- package/skills/dms/SKILL.md +14 -3
- package/skills/dms-pages/REFERENCE.md +17 -1
|
@@ -47,7 +47,6 @@ import {
|
|
|
47
47
|
} from "../../composables/table-view/kanban";
|
|
48
48
|
import { useTableRowActions } from "../../build/composables/table-view/useTableViewRowActions";
|
|
49
49
|
import {
|
|
50
|
-
buildTableDataKey,
|
|
51
50
|
buildTableQuery,
|
|
52
51
|
isFilterEffective,
|
|
53
52
|
} from "../../build/composables/table-view/utils/tableQuery";
|
|
@@ -107,7 +106,9 @@ import { useTableViews } from "../../build/composables/table-view/useTableViews"
|
|
|
107
106
|
import { useGroupedRows } from "../../build/composables/table-view/useGroupedRows";
|
|
108
107
|
import { useTableFooter } from "../../build/composables/table-view/useTableFooter";
|
|
109
108
|
import { useAccumulatedPages } from "../../build/composables/table-view/useAccumulatedPages";
|
|
109
|
+
import { usePersistedPagination } from "../../build/composables/table-view/usePersistedPagination";
|
|
110
110
|
import { useTableRows } from "../../build/composables/table-view/useTableRows";
|
|
111
|
+
import { useRefreshTriggers } from "../../build/composables/blocks/useRefreshTriggers";
|
|
111
112
|
import { useTableReorder } from "../../build/composables/table-view/useTableReorder";
|
|
112
113
|
import {
|
|
113
114
|
groupedOptionsFor,
|
|
@@ -118,7 +119,10 @@ import { readTableUrlKey } from "../../build/composables/table-view/utils/views"
|
|
|
118
119
|
import TableViews, {
|
|
119
120
|
type TableViewItem,
|
|
120
121
|
} from "../../build/components/table/Views.vue";
|
|
121
|
-
import {
|
|
122
|
+
import {
|
|
123
|
+
useTableCountsData,
|
|
124
|
+
useTableListData,
|
|
125
|
+
} from "../../build/composables/table-view/useTableListData";
|
|
122
126
|
import { useTableDataChanges } from "../../composables/table-view/useTableDataChanges";
|
|
123
127
|
|
|
124
128
|
const REALTIME_ROW_TOPIC_PREFIX = "tableview:row:";
|
|
@@ -172,6 +176,8 @@ interface TableViewProps<T extends Data> extends TableViewConfig<T> {
|
|
|
172
176
|
tableId?: string;
|
|
173
177
|
/** The page carries no other table view: `?view=` / `?tab=` are its own. */
|
|
174
178
|
isSoleTableView?: boolean;
|
|
179
|
+
/** Topics whose events make the table read its rows again. */
|
|
180
|
+
realtimeTopic?: string | string[];
|
|
175
181
|
}
|
|
176
182
|
|
|
177
183
|
const props = defineProps<TableViewProps<T>>();
|
|
@@ -347,12 +353,42 @@ const resolvedChrome = resolveTableChrome(props.layout, {
|
|
|
347
353
|
});
|
|
348
354
|
const GLOBAL_FILTER_DEBOUNCE_MS = 400;
|
|
349
355
|
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
);
|
|
356
|
+
const route = useDmsRoute();
|
|
357
|
+
|
|
358
|
+
const DEFAULT_FILTER_MODE = "is";
|
|
359
|
+
|
|
360
|
+
const queryParamHiddenFilters = computed<TableFilter[]>(() => {
|
|
361
|
+
if (!queryParamFilters) return [];
|
|
362
|
+
|
|
363
|
+
return Object.entries(queryParamFilters)
|
|
364
|
+
.filter(([param]) => route.query[param] !== undefined)
|
|
365
|
+
.map(([param, config]) => ({
|
|
366
|
+
accessorKey: config.field,
|
|
367
|
+
mode: config.mode || DEFAULT_FILTER_MODE,
|
|
368
|
+
value: route.query[param] as string,
|
|
369
|
+
}));
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const routeParamHiddenFilters = computed<TableFilter[]>(() => {
|
|
373
|
+
if (!routeParamFilters || !routeParams) return [];
|
|
374
|
+
|
|
375
|
+
return Object.entries(routeParamFilters)
|
|
376
|
+
.filter(([param]) => routeParams[param] !== undefined)
|
|
377
|
+
.map(([param, config]) => ({
|
|
378
|
+
accessorKey: config.field,
|
|
379
|
+
mode: config.mode || DEFAULT_FILTER_MODE,
|
|
380
|
+
value: routeParams[param] as string,
|
|
381
|
+
}));
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// A hidden filter's value changed with the URL, in place or through a
|
|
385
|
+
// remount: the page reached among the previous rows lists from page 1.
|
|
386
|
+
const paginationState = usePersistedPagination({
|
|
387
|
+
preferences: { getPreference, setPreference },
|
|
388
|
+
preferenceKey: getTablePreferenceKey,
|
|
389
|
+
defaults: DEFAULT_PAGINATION,
|
|
390
|
+
scope: () => [queryParamHiddenFilters.value, routeParamHiddenFilters.value],
|
|
391
|
+
});
|
|
356
392
|
const rowSelect = ref<RowSelectionState>({});
|
|
357
393
|
// A column's grid header, for the sort messages.
|
|
358
394
|
const columnLabel = (id: string): string => {
|
|
@@ -615,35 +651,11 @@ const tableRows = useTableRows<T>({
|
|
|
615
651
|
location,
|
|
616
652
|
source: props.source,
|
|
617
653
|
columns: props.columns,
|
|
654
|
+
route: () => ({
|
|
655
|
+
routeParams: props.routeParams,
|
|
656
|
+
routeQuery: route.query as Record<string, unknown>,
|
|
657
|
+
}),
|
|
618
658
|
});
|
|
619
|
-
const route = useDmsRoute();
|
|
620
|
-
|
|
621
|
-
const DEFAULT_FILTER_MODE = "is";
|
|
622
|
-
|
|
623
|
-
const queryParamHiddenFilters = computed<TableFilter[]>(() => {
|
|
624
|
-
if (!queryParamFilters) return [];
|
|
625
|
-
|
|
626
|
-
return Object.entries(queryParamFilters)
|
|
627
|
-
.filter(([param]) => route.query[param] !== undefined)
|
|
628
|
-
.map(([param, config]) => ({
|
|
629
|
-
accessorKey: config.field,
|
|
630
|
-
mode: config.mode || DEFAULT_FILTER_MODE,
|
|
631
|
-
value: route.query[param] as string,
|
|
632
|
-
}));
|
|
633
|
-
});
|
|
634
|
-
|
|
635
|
-
const routeParamHiddenFilters = computed<TableFilter[]>(() => {
|
|
636
|
-
if (!routeParamFilters || !routeParams) return [];
|
|
637
|
-
|
|
638
|
-
return Object.entries(routeParamFilters)
|
|
639
|
-
.filter(([param]) => routeParams[param] !== undefined)
|
|
640
|
-
.map(([param, config]) => ({
|
|
641
|
-
accessorKey: config.field,
|
|
642
|
-
mode: config.mode || DEFAULT_FILTER_MODE,
|
|
643
|
-
value: routeParams[param] as string,
|
|
644
|
-
}));
|
|
645
|
-
});
|
|
646
|
-
|
|
647
659
|
// Quick filters: dropdowns of the toolbar over a column's values (a select's
|
|
648
660
|
// items, a boolean, the rows a relation points to), writing that column's
|
|
649
661
|
// filter like the filters row does.
|
|
@@ -788,34 +800,15 @@ const archiveQuery = computed(() =>
|
|
|
788
800
|
archiveMode ? { showArchived: showArchived.value } : {},
|
|
789
801
|
);
|
|
790
802
|
|
|
791
|
-
const
|
|
792
|
-
results: [],
|
|
793
|
-
total: 0,
|
|
794
|
-
offset: 0,
|
|
795
|
-
limit: 0,
|
|
796
|
-
};
|
|
797
|
-
|
|
798
|
-
const tableDataKey = buildTableDataKey({
|
|
803
|
+
const { data, status, error, refresh } = await useTableListData<T>({
|
|
799
804
|
componentId,
|
|
800
805
|
pageId,
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
806
|
+
rows: tableRows,
|
|
807
|
+
query: queryRequest,
|
|
808
|
+
archiveQuery,
|
|
809
|
+
isSelfManaged: isActiveDisplaySelfManaged,
|
|
804
810
|
});
|
|
805
811
|
|
|
806
|
-
const { data, status, error, refresh } = await useServerRenderedAsyncData(
|
|
807
|
-
tableDataKey,
|
|
808
|
-
(): Promise<TableViewListResponse<T>> => {
|
|
809
|
-
// Self-managed displays (e.g. kanban) fetch their own data; skip the
|
|
810
|
-
// shared list query for them.
|
|
811
|
-
if (isActiveDisplaySelfManaged.value) {
|
|
812
|
-
return Promise.resolve(EMPTY_LIST_RESULT);
|
|
813
|
-
}
|
|
814
|
-
return tableRows.list({ ...queryRequest.value, ...archiveQuery.value });
|
|
815
|
-
},
|
|
816
|
-
{ watch: [queryRequest, archiveQuery, isActiveDisplaySelfManaged] },
|
|
817
|
-
);
|
|
818
|
-
|
|
819
812
|
// Nothing listed yet (a client navigation paints before the first page
|
|
820
813
|
// arrives): the table draws its skeleton, never the empty state.
|
|
821
814
|
const isFirstPageLoading = computed(() => data.value === null && !error.value);
|
|
@@ -949,19 +942,13 @@ const tabCountsQuery = computed(() => [
|
|
|
949
942
|
// No default: `null` until the counts arrive, so the tabs draw placeholders
|
|
950
943
|
// instead of a count of nothing.
|
|
951
944
|
const { data: tabCountsData, refresh: refreshTabCounts } =
|
|
952
|
-
await
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
query: { ...query, ...archiveQuery.value },
|
|
960
|
-
})),
|
|
961
|
-
);
|
|
962
|
-
},
|
|
963
|
-
{ watch: [tabCountsQuery, archiveQuery] },
|
|
964
|
-
);
|
|
945
|
+
await useTableCountsData<T>({
|
|
946
|
+
componentId,
|
|
947
|
+
pageId,
|
|
948
|
+
rows: tableRows,
|
|
949
|
+
queries: tabCountsQuery,
|
|
950
|
+
archiveQuery,
|
|
951
|
+
});
|
|
965
952
|
|
|
966
953
|
const viewItems = computed<TableViewItem[]>(() =>
|
|
967
954
|
tableViewItems.value.map((view) => ({
|
|
@@ -1131,6 +1118,11 @@ const refreshAll = async () => {
|
|
|
1131
1118
|
]);
|
|
1132
1119
|
};
|
|
1133
1120
|
|
|
1121
|
+
// The rows can move without the table seeing it (a batch, a job, another
|
|
1122
|
+
// block's action): a topic of the backend or a page refresh reads them again,
|
|
1123
|
+
// keeping the current rows on screen until the answer lands.
|
|
1124
|
+
useRefreshTriggers(props.realtimeTopic, () => void refreshAll());
|
|
1125
|
+
|
|
1134
1126
|
// "Preview as role": a link tab to a page the previewed role could not open
|
|
1135
1127
|
// is drawn locked, one to a page it opens partially is drawn partially
|
|
1136
1128
|
// locked. Never outside a preview (`entryState` is null then).
|
|
@@ -1847,7 +1839,6 @@ defineShortcuts(
|
|
|
1847
1839
|
|
|
1848
1840
|
const tableStatePreferences = {
|
|
1849
1841
|
sorting,
|
|
1850
|
-
pagination: paginationState,
|
|
1851
1842
|
columnFilters: columnFilters,
|
|
1852
1843
|
columnVisibility,
|
|
1853
1844
|
filtersOpen: filtersRowOpen,
|
|
@@ -1980,6 +1971,7 @@ onMounted(() => {
|
|
|
1980
1971
|
:quick-filters="resolvedQuickFilters"
|
|
1981
1972
|
:footer="resolvedFooter"
|
|
1982
1973
|
:empty-states="props.emptyStates"
|
|
1974
|
+
:hidden-filters-apply="queryParamHiddenFilters.length > 0"
|
|
1983
1975
|
:default-page-size="props.pageSize"
|
|
1984
1976
|
:archive-toggle="canToggleArchived"
|
|
1985
1977
|
:displays="availableDisplays"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from "vue";
|
|
3
3
|
import type { TopListItem } from "../../../composables/chart/types";
|
|
4
|
+
import { useComposedText } from "../../../../../dms-core/app/composables/translation/useComposedText";
|
|
4
5
|
// Imported rather than resolved from the registry: a registered component is
|
|
5
6
|
// a lazy chunk of its own, fetched only when the data first shows it, so the
|
|
6
7
|
// trend would pop in a beat after the row.
|
|
@@ -31,11 +32,11 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
31
32
|
|
|
32
33
|
const PERCENT_UNIT = "%";
|
|
33
34
|
|
|
34
|
-
const {
|
|
35
|
+
const { processText } = useComposedText();
|
|
35
36
|
|
|
36
|
-
const titleDisplay = computed(() =>
|
|
37
|
+
const titleDisplay = computed(() => processText(props.item.title));
|
|
37
38
|
const descriptionDisplay = computed(() =>
|
|
38
|
-
props.item.description ?
|
|
39
|
+
props.item.description ? processText(props.item.description) : "",
|
|
39
40
|
);
|
|
40
41
|
|
|
41
42
|
const hasSparklineData = computed(
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { BlockText } from "#dms-core/app/types/composed-text";
|
|
2
|
+
|
|
1
3
|
/** Serializable X-axis settings, matching the DMS server contract. */
|
|
2
4
|
export interface ChartXAxis {
|
|
3
5
|
/** Requested intervals; ignored by ApexCharts on datetime axes. */
|
|
@@ -100,8 +102,9 @@ export interface KpiCardResponse {
|
|
|
100
102
|
|
|
101
103
|
export interface TopListItem {
|
|
102
104
|
id: string | number;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
/** A literal, a `$`-prefixed i18n key, or a composed text. */
|
|
106
|
+
title: BlockText;
|
|
107
|
+
description?: BlockText;
|
|
105
108
|
value: number;
|
|
106
109
|
delta?: number | null;
|
|
107
110
|
sparkline?: number[];
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
computed,
|
|
3
|
-
onBeforeUnmount,
|
|
4
3
|
onMounted,
|
|
5
4
|
ref,
|
|
6
5
|
watch,
|
|
@@ -12,12 +11,11 @@ import {
|
|
|
12
11
|
usePeriodScope,
|
|
13
12
|
} from "../../../../dms-core/app/composables/period/usePeriodScope";
|
|
14
13
|
import type { PeriodState } from "../../../../dms-core/app/composables/period/types";
|
|
15
|
-
import { useRealtimeTopic } from "../../../../dms-core/app/composables/realtime/useRealtimeTopic";
|
|
16
14
|
import {
|
|
17
15
|
hasUrlVariables,
|
|
18
16
|
resolveUrlVariables,
|
|
19
17
|
} from "../../build/utils/urlVariables";
|
|
20
|
-
import {
|
|
18
|
+
import { useRefreshTriggers } from "../../build/composables/blocks/useRefreshTriggers";
|
|
21
19
|
|
|
22
20
|
export interface UseChartFetchOptions<T> {
|
|
23
21
|
/**
|
|
@@ -132,20 +130,6 @@ function readStaticData<T>(
|
|
|
132
130
|
return source ?? null;
|
|
133
131
|
}
|
|
134
132
|
|
|
135
|
-
function subscribeRealtimeTopics(
|
|
136
|
-
topic: string | string[] | undefined,
|
|
137
|
-
onEvent: () => void,
|
|
138
|
-
): void {
|
|
139
|
-
if (!topic) return;
|
|
140
|
-
const topics = Array.isArray(topic) ? topic : [topic];
|
|
141
|
-
for (const entry of topics) {
|
|
142
|
-
useRealtimeTopic(entry, (event) => {
|
|
143
|
-
if (!("type" in event)) return;
|
|
144
|
-
onEvent();
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
133
|
/**
|
|
150
134
|
* The URL to request: `fetchUrl` with its tokens filled, or `undefined` while
|
|
151
135
|
* one has no value. Only a URL naming a token reads the route.
|
|
@@ -164,16 +148,6 @@ function useResolvedFetchUrl<T>(
|
|
|
164
148
|
);
|
|
165
149
|
}
|
|
166
150
|
|
|
167
|
-
// Mounted only: the page refresh is a browser event, and a block the server
|
|
168
|
-
// renders has nothing to refetch.
|
|
169
|
-
function subscribePageRefresh(onRefresh: () => void): void {
|
|
170
|
-
let unsubscribe: (() => void) | undefined;
|
|
171
|
-
onMounted(() => {
|
|
172
|
-
unsubscribe = onPageBlocksRefresh(onRefresh);
|
|
173
|
-
});
|
|
174
|
-
onBeforeUnmount(() => unsubscribe?.());
|
|
175
|
-
}
|
|
176
|
-
|
|
177
151
|
export function useChartFetch<T>(
|
|
178
152
|
options: UseChartFetchOptions<T>,
|
|
179
153
|
): UseChartFetchReturn<T> {
|
|
@@ -290,8 +264,7 @@ export function useChartFetch<T>(
|
|
|
290
264
|
scheduleRefresh,
|
|
291
265
|
);
|
|
292
266
|
onMounted(fetchOnMount);
|
|
293
|
-
|
|
294
|
-
subscribePageRefresh(refreshSameInputs);
|
|
267
|
+
useRefreshTriggers(options.realtimeTopic, refreshSameInputs);
|
|
295
268
|
} else if (typeof options.staticData === "function") {
|
|
296
269
|
watch(
|
|
297
270
|
() => readStaticData(options.staticData),
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type ReplaceUrlVariablesContext,
|
|
14
14
|
hasUrlVariables,
|
|
15
15
|
replaceUrlVariables,
|
|
16
|
+
resolveTextVariables,
|
|
16
17
|
resolveUrlVariables,
|
|
17
18
|
} from "../../build/utils/urlVariables";
|
|
18
19
|
|
|
@@ -347,7 +348,7 @@ export function resolveSubmitDefaults(
|
|
|
347
348
|
const resolved: Record<string, unknown> = {};
|
|
348
349
|
for (const [key, value] of Object.entries(submitDefaults)) {
|
|
349
350
|
if (typeof value === "string" && hasUrlVariables(value)) {
|
|
350
|
-
const replaced =
|
|
351
|
+
const replaced = resolveTextVariables(value, context);
|
|
351
352
|
if (replaced === undefined) continue;
|
|
352
353
|
resolved[key] = replaced;
|
|
353
354
|
} else {
|
|
@@ -29,6 +29,8 @@ export interface TabProps extends TabComponentProps {
|
|
|
29
29
|
* names, read in one request and again when a watch action fires.
|
|
30
30
|
*/
|
|
31
31
|
badgesUrl?: string;
|
|
32
|
+
/** Topics whose events make the tab set read `badgesUrl` again. */
|
|
33
|
+
realtimeTopic?: string | string[];
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
/** The badges `badgesUrl` answers with, by tab slot. */
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import type { DrawerDirection } from "../../containers/types";
|
|
2
|
+
|
|
1
3
|
export type ActionTarget =
|
|
2
4
|
| {
|
|
3
5
|
type: "drawer";
|
|
4
6
|
component: ComponentInfo;
|
|
5
7
|
title?: string;
|
|
6
8
|
description?: string;
|
|
9
|
+
/** Edge the drawer slides in from (`bottom` by default). */
|
|
10
|
+
direction?: DrawerDirection;
|
|
7
11
|
}
|
|
8
12
|
| {
|
|
9
13
|
type: "modal";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The window event a data block (StatGroup, KeyValueList, NavCardGrid,
|
|
3
|
-
* ActivityFeed, KPI, top list, meter, chart, a tab set's badges)
|
|
4
|
-
* on, keeping its values on screen until the new answer lands.
|
|
3
|
+
* ActivityFeed, KPI, top list, meter, chart, a tab set's badges, table view)
|
|
4
|
+
* re-fetches on, keeping its values on screen until the new answer lands.
|
|
5
5
|
*/
|
|
6
6
|
export const BLOCK_REFRESH_EVENT = "DmsComponent.Block.Refresh";
|
|
7
7
|
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"edit_item_description": "Change its fields, then save.",
|
|
26
26
|
"edit_item_in": "Changes are saved to {caption}.",
|
|
27
27
|
"search_placeholder": "Search...",
|
|
28
|
+
"search_open": "Open search",
|
|
28
29
|
"options_title": "Table options",
|
|
29
30
|
"filters_title": "Filters",
|
|
30
31
|
"columns_title": "Columns",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"edit_item_description": "Modifiez ses champs, puis enregistrez.",
|
|
26
26
|
"edit_item_in": "Les modifications sont enregistrées dans « {caption} ».",
|
|
27
27
|
"search_placeholder": "Rechercher...",
|
|
28
|
+
"search_open": "Ouvrir la recherche",
|
|
28
29
|
"options_title": "Options de la table",
|
|
29
30
|
"filters_title": "Filtres",
|
|
30
31
|
"columns_title": "Colonnes",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"license": "Apache-2.0",
|
|
12
12
|
"packageManager": "pnpm@10.6.5",
|
|
13
13
|
"engines": {
|
|
14
|
-
"@antelopejs/dms-frontend": ">=0.5.
|
|
14
|
+
"@antelopejs/dms-frontend": ">=0.5.2 <0.6.0"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
17
|
"typecheck": "ajs dms verify-source --layer \"$PWD\" --local-package @antelopejs/dms=.. --local-package @antelopejs/interface-dms=../../interface-dms",
|
|
@@ -30,12 +30,13 @@
|
|
|
30
30
|
"@codemirror/lang-json": "^6.0.2",
|
|
31
31
|
"@codemirror/lang-sql": "^6.10.0",
|
|
32
32
|
"@codemirror/language": "^6.11.0",
|
|
33
|
-
"@codemirror/streamparser": "^6.0.0",
|
|
34
33
|
"@codemirror/state": "^6.5.2",
|
|
34
|
+
"@codemirror/streamparser": "^6.0.0",
|
|
35
35
|
"@codemirror/view": "^6.38.1",
|
|
36
36
|
"@iconify-json/lucide": "^1.2.64",
|
|
37
37
|
"@iconify-json/ph": "^1.2.2",
|
|
38
38
|
"@internationalized/date": "^3.10.0",
|
|
39
|
+
"@lezer/common": "^1.5.3",
|
|
39
40
|
"@lezer/highlight": "^1.2.5",
|
|
40
41
|
"@tailwindcss/typography": "^0.5.19",
|
|
41
42
|
"@tanstack/vue-table": "^8.21.3",
|
|
@@ -73,7 +74,7 @@
|
|
|
73
74
|
},
|
|
74
75
|
"devDependencies": {
|
|
75
76
|
"@antelopejs/core": ">=1.13.4 <2",
|
|
76
|
-
"@antelopejs/dms-frontend": ">=0.5.
|
|
77
|
+
"@antelopejs/dms-frontend": ">=0.5.2 <0.6.0",
|
|
77
78
|
"@eslint/js": "^8.57.0",
|
|
78
79
|
"@inertiajs/vue3": "^3.7.0",
|
|
79
80
|
"@nuxt/ui": "4.10.0",
|
|
@@ -47,6 +47,9 @@ importers:
|
|
|
47
47
|
'@internationalized/date':
|
|
48
48
|
specifier: ^3.10.0
|
|
49
49
|
version: 3.10.0
|
|
50
|
+
'@lezer/common':
|
|
51
|
+
specifier: ^1.5.3
|
|
52
|
+
version: 1.5.3
|
|
50
53
|
'@lezer/highlight':
|
|
51
54
|
specifier: ^1.2.5
|
|
52
55
|
version: 1.2.5
|
|
@@ -157,8 +160,8 @@ importers:
|
|
|
157
160
|
specifier: '>=1.13.4 <2'
|
|
158
161
|
version: 1.13.5
|
|
159
162
|
'@antelopejs/dms-frontend':
|
|
160
|
-
specifier: '>=0.5.
|
|
161
|
-
version: 0.5.
|
|
163
|
+
specifier: '>=0.5.2 <0.6.0'
|
|
164
|
+
version: 0.5.2(@antelopejs/core@1.13.5)
|
|
162
165
|
'@eslint/js':
|
|
163
166
|
specifier: ^8.57.0
|
|
164
167
|
version: 8.57.0
|
|
@@ -234,8 +237,8 @@ packages:
|
|
|
234
237
|
engines: {node: '>=20.19'}
|
|
235
238
|
hasBin: true
|
|
236
239
|
|
|
237
|
-
'@antelopejs/dms-frontend@0.5.
|
|
238
|
-
resolution: {integrity: sha512-
|
|
240
|
+
'@antelopejs/dms-frontend@0.5.2':
|
|
241
|
+
resolution: {integrity: sha512-0kChPedSTpKES7aN/gLj5tAe4WRaU+FrwaRKpOyvo3fvvRPfDTvlTYjjdUpabsF150d3+V/AksXstZ5eb36QwQ==}
|
|
239
242
|
engines: {node: '>=20.12.0'}
|
|
240
243
|
hasBin: true
|
|
241
244
|
peerDependencies:
|
|
@@ -4205,7 +4208,7 @@ snapshots:
|
|
|
4205
4208
|
semver: 7.8.5
|
|
4206
4209
|
typescript: 5.9.3
|
|
4207
4210
|
|
|
4208
|
-
'@antelopejs/dms-frontend@0.5.
|
|
4211
|
+
'@antelopejs/dms-frontend@0.5.2(@antelopejs/core@1.13.5)':
|
|
4209
4212
|
dependencies:
|
|
4210
4213
|
'@antelopejs/core': 1.13.5
|
|
4211
4214
|
chokidar: 4.0.3
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
formatActivityTime,
|
|
4
4
|
formatShortDate,
|
|
5
5
|
groupActivityByDay,
|
|
6
|
+
activityText,
|
|
6
7
|
resolveActivityParams,
|
|
7
8
|
relativeDayBucket,
|
|
8
9
|
} from "../layers/dms-ui/app/build/components/activity-feed/activityFeedDays";
|
|
@@ -87,25 +88,38 @@ describe("activity feed day groups", () => {
|
|
|
87
88
|
});
|
|
88
89
|
|
|
89
90
|
describe("resolveActivityParams", () => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
`${key}(${JSON.stringify(params ?? {})})`;
|
|
93
|
-
|
|
94
|
-
it("translates a $ value with the entry's literal values", () => {
|
|
91
|
+
it("makes a $ value a text of its own, with the entry's other values", () => {
|
|
92
|
+
const count = { type: "count", value: 2 } as const;
|
|
95
93
|
expect(
|
|
96
|
-
resolveActivityParams(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
resolveActivityParams({
|
|
95
|
+
device: "$activity.device",
|
|
96
|
+
browser: "Chrome",
|
|
97
|
+
count,
|
|
98
|
+
}),
|
|
100
99
|
).toEqual({
|
|
101
|
-
device:
|
|
100
|
+
device: {
|
|
101
|
+
key: "$activity.device",
|
|
102
|
+
params: { browser: "Chrome", count },
|
|
103
|
+
},
|
|
102
104
|
browser: "Chrome",
|
|
103
|
-
|
|
105
|
+
count,
|
|
104
106
|
});
|
|
105
107
|
});
|
|
106
108
|
|
|
107
109
|
it("gives nothing for an entry without values", () => {
|
|
108
|
-
expect(resolveActivityParams(undefined
|
|
110
|
+
expect(resolveActivityParams(undefined)).toBeUndefined();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe("activityText", () => {
|
|
115
|
+
it("gives a $ title the entry's values and leaves other texts alone", () => {
|
|
116
|
+
expect(activityText("$a.title", { name: "Léa" })).toEqual({
|
|
117
|
+
key: "$a.title",
|
|
118
|
+
params: { name: "Léa" },
|
|
119
|
+
});
|
|
120
|
+
expect(activityText("Plain", { name: "Léa" })).toBe("Plain");
|
|
121
|
+
const composed = { key: "a.composed" };
|
|
122
|
+
expect(activityText(composed, { name: "Léa" })).toBe(composed);
|
|
109
123
|
});
|
|
110
124
|
});
|
|
111
125
|
|