@vertesia/ui 1.5.0-dev.20260826.064249Z → 1.5.0-dev.20260901.033115Z
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/lib/core/components/FormItem.d.ts.map +1 -1
- package/lib/core/components/FormItem.js +2 -3
- package/lib/core/components/FormItem.js.map +1 -1
- package/lib/core/components/InfoTip.d.ts +20 -0
- package/lib/core/components/InfoTip.d.ts.map +1 -0
- package/lib/core/components/InfoTip.js +18 -0
- package/lib/core/components/InfoTip.js.map +1 -0
- package/lib/core/components/index.d.ts +1 -0
- package/lib/core/components/index.d.ts.map +1 -1
- package/lib/core/components/index.js +1 -0
- package/lib/core/components/index.js.map +1 -1
- package/lib/core/components/shadcn/Panel.d.ts.map +1 -1
- package/lib/core/components/shadcn/Panel.js +3 -3
- package/lib/core/components/shadcn/Panel.js.map +1 -1
- package/lib/features/store/objects/search/DocumentSearchContext.d.ts +15 -0
- package/lib/features/store/objects/search/DocumentSearchContext.d.ts.map +1 -1
- package/lib/features/store/objects/search/DocumentSearchContext.js +45 -2
- package/lib/features/store/objects/search/DocumentSearchContext.js.map +1 -1
- package/lib/features/store/objects/selection/actions/DeleteObjectsAction.d.ts.map +1 -1
- package/lib/features/store/objects/selection/actions/DeleteObjectsAction.js +5 -0
- package/lib/features/store/objects/selection/actions/DeleteObjectsAction.js.map +1 -1
- package/lib/features/store/types/IntakePolicyEditor.d.ts.map +1 -1
- package/lib/features/store/types/IntakePolicyEditor.js +1 -1
- package/lib/features/store/types/IntakePolicyEditor.js.map +1 -1
- package/lib/features/store/types/IntakePolicyForm.d.ts.map +1 -1
- package/lib/features/store/types/IntakePolicyForm.js +15 -15
- package/lib/features/store/types/IntakePolicyForm.js.map +1 -1
- package/lib/features/store/types/TypeRegistry.d.ts +10 -0
- package/lib/features/store/types/TypeRegistry.d.ts.map +1 -1
- package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts +10 -0
- package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts.map +1 -1
- package/lib/i18n/locales/ar.json +35 -0
- package/lib/i18n/locales/de.json +35 -0
- package/lib/i18n/locales/en.json +35 -0
- package/lib/i18n/locales/es.json +35 -0
- package/lib/i18n/locales/fr.json +35 -0
- package/lib/i18n/locales/it.json +35 -0
- package/lib/i18n/locales/ja.json +35 -0
- package/lib/i18n/locales/ko.json +35 -0
- package/lib/i18n/locales/pt.json +35 -0
- package/lib/i18n/locales/ru.json +35 -0
- package/lib/i18n/locales/tr.json +35 -0
- package/lib/i18n/locales/zh-TW.json +35 -0
- package/lib/i18n/locales/zh.json +35 -0
- package/lib/router/RouteComponent.d.ts +8 -0
- package/lib/router/RouteComponent.d.ts.map +1 -1
- package/lib/router/RouteComponent.js +41 -9
- package/lib/router/RouteComponent.js.map +1 -1
- package/lib/session/UserSessionProvider.d.ts.map +1 -1
- package/lib/session/UserSessionProvider.js +12 -13
- package/lib/session/UserSessionProvider.js.map +1 -1
- package/lib/session/auth/domainRouting.d.ts +11 -0
- package/lib/session/auth/domainRouting.d.ts.map +1 -1
- package/lib/session/auth/domainRouting.js +13 -0
- package/lib/session/auth/domainRouting.js.map +1 -1
- package/lib/session/useUXTracking.d.ts.map +1 -1
- package/lib/session/useUXTracking.js +6 -1
- package/lib/session/useUXTracking.js.map +1 -1
- package/lib/vertesia-ui-core.js +199 -4
- package/lib/vertesia-ui-core.js.map +1 -1
- package/lib/vertesia-ui-features.js +975 -33
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-i18n.BJBGsq-8.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.BJBGsq-8.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.BY5YG5cX.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.BY5YG5cX.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.Bd5_1BXO.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.Bd5_1BXO.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.Bvwej2HH.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.Bvwej2HH.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.CMT3f4gf.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.CMT3f4gf.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.D56DNPJW.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.D56DNPJW.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.DDzKLNx6.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.DDzKLNx6.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.D_Am1LpF.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.D_Am1LpF.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.DrZizgGV.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.DrZizgGV.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.DwHsq86z.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.DwHsq86z.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.PLpo0Bbz.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.PLpo0Bbz.chunk.js.map +1 -0
- package/lib/vertesia-ui-i18n.js +2 -38
- package/lib/vertesia-ui-i18n.js.map +1 -1
- package/lib/vertesia-ui-i18n.xVbXALqz.chunk.js +5 -0
- package/lib/vertesia-ui-i18n.xVbXALqz.chunk.js.map +1 -0
- package/lib/vertesia-ui-layout.js +80 -1
- package/lib/vertesia-ui-layout.js.map +1 -1
- package/lib/vertesia-ui-rich-text.js +278 -4
- package/lib/vertesia-ui-rich-text.js.map +1 -1
- package/lib/vertesia-ui-router.js +1 -1
- package/lib/vertesia-ui-router.js.map +1 -1
- package/lib/vertesia-ui-session.js +1 -1
- package/lib/vertesia-ui-session.js.map +1 -1
- package/lib/vertesia-ui-shell.js +118 -1
- package/lib/vertesia-ui-shell.js.map +1 -1
- package/lib/vertesia-ui-widgets.js +336 -20
- package/lib/vertesia-ui-widgets.js.map +1 -1
- package/package.json +7 -7
- package/src/core/components/FormItem.tsx +2 -5
- package/src/core/components/InfoTip.test.tsx +33 -0
- package/src/core/components/InfoTip.tsx +32 -0
- package/src/core/components/index.ts +1 -0
- package/src/core/components/shadcn/Panel.tsx +3 -7
- package/src/features/store/objects/search/DocumentSearchContext.test.ts +45 -0
- package/src/features/store/objects/search/DocumentSearchContext.ts +50 -2
- package/src/features/store/objects/selection/actions/DeleteObjectsAction.tsx +5 -0
- package/src/features/store/types/IntakePolicyEditor.tsx +3 -4
- package/src/features/store/types/IntakePolicyForm.tsx +120 -20
- package/src/i18n/locales/ar.json +35 -0
- package/src/i18n/locales/de.json +35 -0
- package/src/i18n/locales/en.json +35 -0
- package/src/i18n/locales/es.json +35 -0
- package/src/i18n/locales/fr.json +35 -0
- package/src/i18n/locales/it.json +35 -0
- package/src/i18n/locales/ja.json +35 -0
- package/src/i18n/locales/ko.json +35 -0
- package/src/i18n/locales/pt.json +35 -0
- package/src/i18n/locales/ru.json +35 -0
- package/src/i18n/locales/tr.json +35 -0
- package/src/i18n/locales/zh-TW.json +35 -0
- package/src/i18n/locales/zh.json +35 -0
- package/src/router/RouteComponent.test.tsx +139 -0
- package/src/router/RouteComponent.tsx +54 -9
- package/src/session/UserSessionProvider.tsx +12 -11
- package/src/session/auth/domainRouting.test.ts +24 -1
- package/src/session/auth/domainRouting.ts +29 -0
- package/src/session/useUXTracking.tsx +6 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertesia/ui",
|
|
3
|
-
"version": "1.5.0-dev.
|
|
3
|
+
"version": "1.5.0-dev.20260901.033115Z",
|
|
4
4
|
"description": "Vertesia UI components and and hooks",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -92,11 +92,11 @@
|
|
|
92
92
|
"vega": "^6.3.1",
|
|
93
93
|
"vega-embed": "^7.1.0",
|
|
94
94
|
"vega-lite": "^6.4.3",
|
|
95
|
-
"@vertesia/client": "1.5.0-dev.
|
|
96
|
-
"@vertesia/
|
|
97
|
-
"@vertesia/
|
|
98
|
-
"@vertesia/rich-text": "1.5.0-dev.
|
|
99
|
-
"@vertesia/
|
|
95
|
+
"@vertesia/client": "1.5.0-dev.20260901.033115Z",
|
|
96
|
+
"@vertesia/fusion-ux": "1.5.0-dev.20260901.033115Z",
|
|
97
|
+
"@vertesia/json": "1.5.0-dev.20260901.033115Z",
|
|
98
|
+
"@vertesia/rich-text": "1.5.0-dev.20260901.033115Z",
|
|
99
|
+
"@vertesia/common": "1.5.0-dev.20260901.033115Z"
|
|
100
100
|
},
|
|
101
101
|
"devDependencies": {
|
|
102
102
|
"@testing-library/react": "^16.3.2",
|
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
"url": "https://github.com/vertesia/composableai.git",
|
|
180
180
|
"directory": "packages/ui"
|
|
181
181
|
},
|
|
182
|
-
"gitHead": "
|
|
182
|
+
"gitHead": "96b1587659282d517fe75878467b5fd82bbc1ac5",
|
|
183
183
|
"scripts": {
|
|
184
184
|
"clean:lib": "rimraf ./lib ./tsconfig.tsbuildinfo",
|
|
185
185
|
"clean": "rimraf ./node_modules ./lib ./tsconfig.tsbuildinfo",
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Env } from '@vertesia/ui/env';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
|
-
import { Info } from 'lucide-react';
|
|
4
3
|
import { Children, cloneElement, Fragment, isValidElement, type ReactNode, useId, useRef } from 'react';
|
|
5
|
-
import {
|
|
4
|
+
import { InfoTip } from './InfoTip';
|
|
6
5
|
|
|
7
6
|
interface FormItemProps {
|
|
8
7
|
label: ReactNode;
|
|
@@ -126,9 +125,7 @@ export function FormItem({
|
|
|
126
125
|
</label>
|
|
127
126
|
{description && (
|
|
128
127
|
<div className="mx-2 flex w-4 items-center">
|
|
129
|
-
<
|
|
130
|
-
<Info className="size-3 text-muted" />
|
|
131
|
-
</VTooltip>
|
|
128
|
+
<InfoTip description={description} />
|
|
132
129
|
</div>
|
|
133
130
|
)}
|
|
134
131
|
</div>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { InfoTip } from './InfoTip';
|
|
4
|
+
|
|
5
|
+
describe('InfoTip', () => {
|
|
6
|
+
// The point of the null return: call sites forward an optional `description`
|
|
7
|
+
// straight through, so an unset one must leave nothing behind — not a bare icon,
|
|
8
|
+
// and not an empty flex item that opens a gap beside the label or heading.
|
|
9
|
+
it('renders nothing without a description', () => {
|
|
10
|
+
const { container } = render(<InfoTip />);
|
|
11
|
+
expect(container.innerHTML).toBe('');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('renders nothing for an empty description', () => {
|
|
15
|
+
const { container } = render(<InfoTip description="" />);
|
|
16
|
+
expect(container.innerHTML).toBe('');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('renders a focusable trigger when a description is set', () => {
|
|
20
|
+
render(<InfoTip description="What this field does" />);
|
|
21
|
+
expect(screen.getByRole('button')).toBeDefined();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('uses the larger icon for the md size', () => {
|
|
25
|
+
const { container } = render(<InfoTip description="Heading help" size="md" />);
|
|
26
|
+
expect(container.querySelector('svg')?.getAttribute('class')).toContain('size-4');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('defaults to the small icon', () => {
|
|
30
|
+
const { container } = render(<InfoTip description="Label help" />);
|
|
31
|
+
expect(container.querySelector('svg')?.getAttribute('class')).toContain('size-3');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Info } from 'lucide-react';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import { cn } from './libs/utils';
|
|
4
|
+
import { VTooltip } from './shadcn/tooltip';
|
|
5
|
+
|
|
6
|
+
interface InfoTipProps {
|
|
7
|
+
/** Tooltip text. Renders nothing when empty, so an optional value can be passed
|
|
8
|
+
* straight through without guarding it at the call site. */
|
|
9
|
+
description?: ReactNode;
|
|
10
|
+
/** `sm` (default) sits beside a form label; `md` beside a panel or section title. */
|
|
11
|
+
size?: 'sm' | 'md';
|
|
12
|
+
placement?: 'top' | 'right' | 'bottom' | 'left';
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The Info-icon tooltip that sits next to a label or a heading.
|
|
18
|
+
*
|
|
19
|
+
* Hover-only, like every `VTooltip`. It is NOT a substitute for `FormItem`'s `helpText`,
|
|
20
|
+
* which is the prop that links guidance to a control through `aria-describedby` — see the
|
|
21
|
+
* note on `FormItem.description`.
|
|
22
|
+
*/
|
|
23
|
+
export function InfoTip({ description, size = 'sm', placement = 'top', className }: InfoTipProps) {
|
|
24
|
+
if (!description) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return (
|
|
28
|
+
<VTooltip description={description} placement={placement}>
|
|
29
|
+
<Info className={cn(size === 'md' ? 'size-4' : 'size-3', 'text-muted', className)} />
|
|
30
|
+
</VTooltip>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ChevronDown, ChevronUp
|
|
1
|
+
import { ChevronDown, ChevronUp } from 'lucide-react';
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
+
import { InfoTip } from '../InfoTip';
|
|
3
4
|
import { Button } from './button';
|
|
4
|
-
import { VTooltip } from './tooltip';
|
|
5
5
|
|
|
6
6
|
interface PanelProps {
|
|
7
7
|
title: string | React.ReactNode;
|
|
@@ -47,11 +47,7 @@ export function Panel({
|
|
|
47
47
|
<>
|
|
48
48
|
<div className="flex items-center gap-2">
|
|
49
49
|
<div className={`font-semibold ${isSmall ? 'text-sm' : 'text-lg'}`}>{title}</div>
|
|
50
|
-
{description
|
|
51
|
-
<VTooltip description={description}>
|
|
52
|
-
<Info className="size-4 text-muted" />
|
|
53
|
-
</VTooltip>
|
|
54
|
-
)}
|
|
50
|
+
<InfoTip description={description} size="md" />
|
|
55
51
|
</div>
|
|
56
52
|
<div className="flex gap-2 items-center">
|
|
57
53
|
{action}
|
|
@@ -33,6 +33,51 @@ describe('DocumentSearch', () => {
|
|
|
33
33
|
});
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
+
it('keeps deleted objects out of a refetch the index has not caught up with yet', async () => {
|
|
37
|
+
// The index applies deletes out of band, so it still returns the deleted object here.
|
|
38
|
+
const stale = [
|
|
39
|
+
{ id: 'gone', name: 'Deleted object' },
|
|
40
|
+
{ id: 'kept', name: 'Surviving object' },
|
|
41
|
+
] as ContentObjectItemApiResponse[];
|
|
42
|
+
const search = new DocumentSearch(createClient(() => Promise.resolve({ results: stale })));
|
|
43
|
+
search.result.value = { objects: stale, isLoading: false, hasMore: false };
|
|
44
|
+
search.facets.value = { total: 2 };
|
|
45
|
+
|
|
46
|
+
search.removeDeletedObjects(['gone']);
|
|
47
|
+
|
|
48
|
+
// Gone from the list and the count straight away, without waiting for a refetch.
|
|
49
|
+
expect(search.objects.map((obj) => obj.id)).toEqual(['kept']);
|
|
50
|
+
expect(search.facets.value.total).toBe(1);
|
|
51
|
+
|
|
52
|
+
// The refetch still returns it, and it must not come back.
|
|
53
|
+
await expect(search.search()).resolves.toBe(true);
|
|
54
|
+
expect(search.objects.map((obj) => obj.id)).toEqual(['kept']);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('stops hiding a deleted object once the index no longer returns it', async () => {
|
|
58
|
+
const remaining = [{ id: 'kept', name: 'Surviving object' }] as ContentObjectItemApiResponse[];
|
|
59
|
+
const search = new DocumentSearch(createClient(() => Promise.resolve({ results: remaining })));
|
|
60
|
+
search.removeDeletedObjects(['gone']);
|
|
61
|
+
|
|
62
|
+
// The index has caught up: 'gone' is absent, so the entry is reconciled away.
|
|
63
|
+
await expect(search.search()).resolves.toBe(true);
|
|
64
|
+
expect(search.objects.map((obj) => obj.id)).toEqual(['kept']);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('keeps hiding an object the index never stops returning', async () => {
|
|
68
|
+
// Documents the current limitation deliberately: if the delete never reaches the index, the
|
|
69
|
+
// row stays hidden for the life of this DocumentSearch instance. A reload builds a new one,
|
|
70
|
+
// so it is not hidden permanently. Bounding this is a follow-up.
|
|
71
|
+
const stuck = [{ id: 'gone', name: 'Delete never reached the index' }] as ContentObjectItemApiResponse[];
|
|
72
|
+
const search = new DocumentSearch(createClient(() => Promise.resolve({ results: stuck })));
|
|
73
|
+
search.removeDeletedObjects(['gone']);
|
|
74
|
+
|
|
75
|
+
await search.search();
|
|
76
|
+
await search.search();
|
|
77
|
+
|
|
78
|
+
expect(search.objects).toEqual([]);
|
|
79
|
+
});
|
|
80
|
+
|
|
36
81
|
it('handles a missing index as an empty initialized search', async () => {
|
|
37
82
|
const error = Object.assign(new Error('index missing'), { status: 404 });
|
|
38
83
|
const search = new DocumentSearch(createClient(() => Promise.reject(error)));
|
|
@@ -29,6 +29,17 @@ export class DocumentSearch implements SearchInterface {
|
|
|
29
29
|
facetSpecs: FacetSpec[] = [];
|
|
30
30
|
query: ComplexSearchQuery = {};
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Ids the server confirmed deleted, hidden until the index stops returning them.
|
|
34
|
+
*
|
|
35
|
+
* Deletes reach the search index out of band, so a search issued right after one can still
|
|
36
|
+
* return the object. Without this the row reappears and stays until a manual refresh.
|
|
37
|
+
*/
|
|
38
|
+
private deletedIds = new Set<string>();
|
|
39
|
+
|
|
40
|
+
/** Objects the server has returned so far, before hiding — `loadMore` pages on this, not on the visible count. */
|
|
41
|
+
private loadedCount = 0;
|
|
42
|
+
|
|
32
43
|
constructor(
|
|
33
44
|
public client: ZenoClient,
|
|
34
45
|
public limit = 100,
|
|
@@ -98,8 +109,31 @@ export class DocumentSearch implements SearchInterface {
|
|
|
98
109
|
this.query = {};
|
|
99
110
|
}
|
|
100
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Drop objects the server confirmed deleted, and keep them out of results until the index
|
|
114
|
+
* agrees. Reconciled in `_search`: once a full search stops returning an id, it is really gone
|
|
115
|
+
* and stops being hidden.
|
|
116
|
+
*/
|
|
117
|
+
removeDeletedObjects(ids: string[]) {
|
|
118
|
+
if (ids.length === 0) return;
|
|
119
|
+
for (const id of ids) {
|
|
120
|
+
this.deletedIds.add(id);
|
|
121
|
+
}
|
|
122
|
+
const previous = this.result.value;
|
|
123
|
+
const objects = previous.objects.filter((obj) => !this.deletedIds.has(obj.id));
|
|
124
|
+
const removed = previous.objects.length - objects.length;
|
|
125
|
+
this.result.value = { ...previous, objects };
|
|
126
|
+
|
|
127
|
+
const facets = this.facets.value;
|
|
128
|
+
if (removed > 0 && typeof facets.total === 'number') {
|
|
129
|
+
this.facets.value = { ...facets, total: Math.max(0, facets.total - removed) };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
101
133
|
reset(isLoading = false) {
|
|
102
134
|
this.initialized = false;
|
|
135
|
+
this.deletedIds.clear();
|
|
136
|
+
this.loadedCount = 0;
|
|
103
137
|
this.result.value = {
|
|
104
138
|
objects: [],
|
|
105
139
|
isLoading,
|
|
@@ -161,16 +195,30 @@ export class DocumentSearch implements SearchInterface {
|
|
|
161
195
|
hasMore: loadMore ? this.result.value.hasMore : true,
|
|
162
196
|
};
|
|
163
197
|
const limit = this.limit;
|
|
164
|
-
const offset = loadMore ? this.
|
|
198
|
+
const offset = loadMore ? this.loadedCount : 0;
|
|
165
199
|
try {
|
|
166
200
|
const res = await this._searchRequest(this.query, limit, offset, !noFacets);
|
|
167
201
|
// Handle the new format with results and facets
|
|
168
202
|
const results = res.results || [];
|
|
169
203
|
const facets = res.facets || {};
|
|
170
204
|
|
|
205
|
+
this.loadedCount = loadMore ? this.loadedCount + results.length : results.length;
|
|
206
|
+
|
|
207
|
+
// Only a full search proves an id is gone. A `loadMore` page is a different slice, so an
|
|
208
|
+
// absence there says nothing and must not clear the entry.
|
|
209
|
+
if (!loadMore && this.deletedIds.size > 0) {
|
|
210
|
+
for (const id of [...this.deletedIds]) {
|
|
211
|
+
if (!results.some((obj) => obj.id === id)) {
|
|
212
|
+
this.deletedIds.delete(id);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const visible = this.deletedIds.size > 0 ? results.filter((obj) => !this.deletedIds.has(obj.id)) : results;
|
|
217
|
+
|
|
171
218
|
this.result.value = {
|
|
172
219
|
isLoading: false,
|
|
173
|
-
objects: loadMore ? this.objects.concat(
|
|
220
|
+
objects: loadMore ? this.objects.concat(visible) : visible,
|
|
221
|
+
// `results`, not `visible`: paging is the server's page size, not what we show.
|
|
174
222
|
hasMore: results.length === limit,
|
|
175
223
|
};
|
|
176
224
|
|
|
@@ -64,6 +64,11 @@ export function DeleteObjectsActionComponent({ action, objectIds, children }: Ac
|
|
|
64
64
|
if (objectIds.length === facets._value.total) {
|
|
65
65
|
search.resetFacets();
|
|
66
66
|
}
|
|
67
|
+
// Drop the deleted rows before refetching. The search index applies deletes out
|
|
68
|
+
// of band, so the refetch below can still return them; without this they come
|
|
69
|
+
// back and stay until the user refreshes by hand.
|
|
70
|
+
const failed = new Set(result.failed);
|
|
71
|
+
search.removeDeletedObjects(objectIds.filter((id) => !failed.has(id)));
|
|
67
72
|
search.search();
|
|
68
73
|
} else {
|
|
69
74
|
// we are in the object view
|
|
@@ -516,12 +516,11 @@ export function IntakePolicyEditor({
|
|
|
516
516
|
current={activeTab}
|
|
517
517
|
onTabChange={onTabChange}
|
|
518
518
|
updateHash={false}
|
|
519
|
-
responsive
|
|
520
519
|
fullHeight
|
|
521
|
-
className="px-0"
|
|
520
|
+
className="px-0 flex-row!"
|
|
522
521
|
>
|
|
523
|
-
<TabsBar
|
|
524
|
-
<TabsPanel className="min-h-0 flex-1 overflow-auto pt-1" />
|
|
522
|
+
<TabsBar className="py-2 w-48 shrink-0" direction="vertical" />
|
|
523
|
+
<TabsPanel className="min-h-0 min-w-0 flex-1 overflow-auto pt-1 ps-4" />
|
|
525
524
|
</Tabs>
|
|
526
525
|
</div>
|
|
527
526
|
</Panel>
|