@stigmer/react 3.11.1-dev.20260812192248 → 3.12.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/activity/useRecentActivity.d.ts +3 -2
- package/activity/useRecentActivity.d.ts.map +1 -1
- package/activity/useRecentActivity.js +3 -2
- package/activity/useRecentActivity.js.map +1 -1
- package/agent/AgentDetailView.d.ts +2 -12
- package/agent/AgentDetailView.d.ts.map +1 -1
- package/agent/AgentDetailView.js +10 -42
- package/agent/AgentDetailView.js.map +1 -1
- package/agent/internal/agentToInput.d.ts.map +1 -1
- package/agent/internal/agentToInput.js +0 -8
- package/agent/internal/agentToInput.js.map +1 -1
- package/dependency-graph/DependencyTreeNode.d.ts.map +1 -1
- package/dependency-graph/DependencyTreeNode.js +1 -7
- package/dependency-graph/DependencyTreeNode.js.map +1 -1
- package/dependency-graph/types.d.ts +8 -20
- package/dependency-graph/types.d.ts.map +1 -1
- package/dependency-graph/useDependencyGraph.d.ts.map +1 -1
- package/dependency-graph/useDependencyGraph.js +2 -20
- package/dependency-graph/useDependencyGraph.js.map +1 -1
- package/execution/useCreateAgentExecution.d.ts +3 -2
- package/execution/useCreateAgentExecution.d.ts.map +1 -1
- package/execution/useCreateAgentExecution.js.map +1 -1
- package/index.d.ts +0 -2
- package/index.d.ts.map +1 -1
- package/index.js +0 -6
- package/index.js.map +1 -1
- package/internal/isUnimplemented.d.ts +17 -0
- package/internal/isUnimplemented.d.ts.map +1 -0
- package/internal/isUnimplemented.js +26 -0
- package/internal/isUnimplemented.js.map +1 -0
- package/library/useExportResource.d.ts +3 -4
- package/library/useExportResource.d.ts.map +1 -1
- package/library/useExportResource.js +1 -1
- package/library/useExportResource.js.map +1 -1
- package/manifest/EditResourceYamlDialog.d.ts.map +1 -1
- package/manifest/EditResourceYamlDialog.js.map +1 -1
- package/mcp-server/McpServerConnectDialog.js +7 -3
- package/mcp-server/McpServerConnectDialog.js.map +1 -1
- package/mcp-server/useMcpServerCredentials.d.ts +38 -10
- package/mcp-server/useMcpServerCredentials.d.ts.map +1 -1
- package/mcp-server/useMcpServerCredentials.js +50 -7
- package/mcp-server/useMcpServerCredentials.js.map +1 -1
- package/mcp-server/useOrgOAuthApp.d.ts +22 -0
- package/mcp-server/useOrgOAuthApp.d.ts.map +1 -1
- package/mcp-server/useOrgOAuthApp.js +39 -7
- package/mcp-server/useOrgOAuthApp.js.map +1 -1
- package/package.json +4 -4
- package/session/useSessionConversation.d.ts +4 -2
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js.map +1 -1
- package/skill/useSkillVersions.d.ts.map +1 -1
- package/skill/useSkillVersions.js +1 -9
- package/skill/useSkillVersions.js.map +1 -1
- package/src/activity/useRecentActivity.ts +3 -2
- package/src/agent/AgentDetailView.tsx +6 -130
- package/src/agent/internal/__tests__/agentToInput.test.ts +2 -11
- package/src/agent/internal/agentToInput.ts +0 -11
- package/src/dependency-graph/DependencyTreeNode.tsx +1 -7
- package/src/dependency-graph/__tests__/useDependencyGraph.test.ts +18 -34
- package/src/dependency-graph/types.ts +8 -20
- package/src/dependency-graph/useDependencyGraph.ts +2 -22
- package/src/execution/useCreateAgentExecution.ts +3 -2
- package/src/index.ts +0 -82
- package/src/internal/isUnimplemented.ts +26 -0
- package/src/library/useExportResource.ts +3 -4
- package/src/manifest/EditResourceYamlDialog.tsx +1 -2
- package/src/manifest/__tests__/ApplyManifestDialog.test.tsx +2 -2
- package/src/manifest/__tests__/EditResourceYamlDialog.test.tsx +9 -9
- package/src/mcp-server/McpServerConnectDialog.tsx +29 -11
- package/src/mcp-server/__tests__/VendorApprovalBlocked.surfaces.test.tsx +103 -5
- package/src/mcp-server/__tests__/byoaEditionDegradation.test.tsx +323 -0
- package/src/mcp-server/__tests__/useMcpServerCredentials.test.tsx +306 -0
- package/src/mcp-server/useMcpServerCredentials.ts +87 -19
- package/src/mcp-server/useOrgOAuthApp.ts +54 -8
- package/src/session/useSessionConversation.ts +4 -2
- package/src/skill/useSkillVersions.ts +1 -11
- package/src/workflow/WorkflowCanvasEditor.tsx +37 -7
- package/src/workflow/WorkflowExecutionGraph.tsx +26 -1
- package/src/workflow/WorkflowExecutionViewer.tsx +27 -13
- package/src/workflow/WorkflowNode.tsx +5 -2
- package/src/workflow/WorkflowRunDialog.tsx +28 -1
- package/src/workflow/__tests__/WorkflowExecutionViewer.reconciliation.test.tsx +48 -1
- package/src/workflow/__tests__/WorkflowNode.test.tsx +93 -0
- package/src/workflow/__tests__/WorkflowRunDialog.test.tsx +127 -0
- package/src/workflow/__tests__/graph-history.test.ts +103 -0
- package/src/workflow/__tests__/overview-graph.test.ts +17 -0
- package/src/workflow/__tests__/useCanvasKeyboardShortcuts.undo.test.ts +115 -0
- package/src/workflow/__tests__/useWorkflowCanvas.undo.test.tsx +133 -0
- package/src/workflow/instance/CreateWorkflowInstanceDialog.tsx +16 -7
- package/src/workflow/instance/useDeleteWorkflowInstance.ts +9 -4
- package/src/workflow/layout/registry-dimensions.ts +2 -5
- package/src/workflow/node-shell/NodeShell.tsx +4 -0
- package/src/workflow/task-type-visual-registry.ts +5 -3
- package/src/workflow/useCanvasKeyboardShortcuts.ts +33 -3
- package/src/workflow/useGraphHistory.ts +22 -41
- package/src/workflow/useWorkflowCanvas.ts +12 -10
- package/src/workflow/useWorkflowSave.ts +2 -15
- package/src/workflow/workflow-graph-conversions.ts +25 -3
- package/styles.css +1 -1
- package/workflow/CanvasTransitionEdge.d.ts +1 -1
- package/workflow/WorkflowCanvasEditor.d.ts.map +1 -1
- package/workflow/WorkflowCanvasEditor.js +31 -6
- package/workflow/WorkflowCanvasEditor.js.map +1 -1
- package/workflow/WorkflowExecutionGraph.d.ts.map +1 -1
- package/workflow/WorkflowExecutionGraph.js +27 -1
- package/workflow/WorkflowExecutionGraph.js.map +1 -1
- package/workflow/WorkflowExecutionViewer.d.ts.map +1 -1
- package/workflow/WorkflowExecutionViewer.js +26 -12
- package/workflow/WorkflowExecutionViewer.js.map +1 -1
- package/workflow/WorkflowNode.d.ts.map +1 -1
- package/workflow/WorkflowNode.js +2 -2
- package/workflow/WorkflowNode.js.map +1 -1
- package/workflow/WorkflowRunDialog.d.ts +11 -1
- package/workflow/WorkflowRunDialog.d.ts.map +1 -1
- package/workflow/WorkflowRunDialog.js +16 -2
- package/workflow/WorkflowRunDialog.js.map +1 -1
- package/workflow/instance/CreateWorkflowInstanceDialog.d.ts.map +1 -1
- package/workflow/instance/CreateWorkflowInstanceDialog.js +10 -2
- package/workflow/instance/CreateWorkflowInstanceDialog.js.map +1 -1
- package/workflow/instance/useDeleteWorkflowInstance.d.ts +9 -4
- package/workflow/instance/useDeleteWorkflowInstance.d.ts.map +1 -1
- package/workflow/instance/useDeleteWorkflowInstance.js +4 -2
- package/workflow/instance/useDeleteWorkflowInstance.js.map +1 -1
- package/workflow/layout/registry-dimensions.d.ts.map +1 -1
- package/workflow/layout/registry-dimensions.js +2 -5
- package/workflow/layout/registry-dimensions.js.map +1 -1
- package/workflow/node-shell/NodeShell.js +1 -1
- package/workflow/node-shell/NodeShell.js.map +1 -1
- package/workflow/task-type-visual-registry.d.ts.map +1 -1
- package/workflow/task-type-visual-registry.js +5 -3
- package/workflow/task-type-visual-registry.js.map +1 -1
- package/workflow/useCanvasKeyboardShortcuts.d.ts +15 -4
- package/workflow/useCanvasKeyboardShortcuts.d.ts.map +1 -1
- package/workflow/useCanvasKeyboardShortcuts.js +25 -4
- package/workflow/useCanvasKeyboardShortcuts.js.map +1 -1
- package/workflow/useGraphHistory.d.ts +16 -9
- package/workflow/useGraphHistory.d.ts.map +1 -1
- package/workflow/useGraphHistory.js +19 -33
- package/workflow/useGraphHistory.js.map +1 -1
- package/workflow/useWorkflowCanvas.d.ts +1 -3
- package/workflow/useWorkflowCanvas.d.ts.map +1 -1
- package/workflow/useWorkflowCanvas.js +15 -9
- package/workflow/useWorkflowCanvas.js.map +1 -1
- package/workflow/useWorkflowSave.d.ts.map +1 -1
- package/workflow/useWorkflowSave.js +2 -15
- package/workflow/useWorkflowSave.js.map +1 -1
- package/workflow/workflow-graph-conversions.d.ts +19 -1
- package/workflow/workflow-graph-conversions.d.ts.map +1 -1
- package/workflow/workflow-graph-conversions.js +17 -3
- package/workflow/workflow-graph-conversions.js.map +1 -1
- package/datastore/CollectionRecordsBrowser.d.ts +0 -34
- package/datastore/CollectionRecordsBrowser.d.ts.map +0 -1
- package/datastore/CollectionRecordsBrowser.js +0 -226
- package/datastore/CollectionRecordsBrowser.js.map +0 -1
- package/datastore/CollectionSchemaView.d.ts +0 -29
- package/datastore/CollectionSchemaView.d.ts.map +0 -1
- package/datastore/CollectionSchemaView.js +0 -93
- package/datastore/CollectionSchemaView.js.map +0 -1
- package/datastore/DatastoreDetailView.d.ts +0 -44
- package/datastore/DatastoreDetailView.d.ts.map +0 -1
- package/datastore/DatastoreDetailView.js +0 -76
- package/datastore/DatastoreDetailView.js.map +0 -1
- package/datastore/DatastoreSyncReport.d.ts +0 -19
- package/datastore/DatastoreSyncReport.d.ts.map +0 -1
- package/datastore/DatastoreSyncReport.js +0 -45
- package/datastore/DatastoreSyncReport.js.map +0 -1
- package/datastore/DeleteDatastoreDialog.d.ts +0 -35
- package/datastore/DeleteDatastoreDialog.d.ts.map +0 -1
- package/datastore/DeleteDatastoreDialog.js +0 -90
- package/datastore/DeleteDatastoreDialog.js.map +0 -1
- package/datastore/FieldValueControl.d.ts +0 -55
- package/datastore/FieldValueControl.d.ts.map +0 -1
- package/datastore/FieldValueControl.js +0 -138
- package/datastore/FieldValueControl.js.map +0 -1
- package/datastore/RecordFilterBuilder.d.ts +0 -37
- package/datastore/RecordFilterBuilder.d.ts.map +0 -1
- package/datastore/RecordFilterBuilder.js +0 -122
- package/datastore/RecordFilterBuilder.js.map +0 -1
- package/datastore/RecordFormPanel.d.ts +0 -45
- package/datastore/RecordFormPanel.d.ts.map +0 -1
- package/datastore/RecordFormPanel.js +0 -215
- package/datastore/RecordFormPanel.js.map +0 -1
- package/datastore/index.d.ts +0 -43
- package/datastore/index.d.ts.map +0 -1
- package/datastore/index.js +0 -22
- package/datastore/index.js.map +0 -1
- package/datastore/recordFilter.d.ts +0 -62
- package/datastore/recordFilter.d.ts.map +0 -1
- package/datastore/recordFilter.js +0 -104
- package/datastore/recordFilter.js.map +0 -1
- package/datastore/recordValues.d.ts +0 -85
- package/datastore/recordValues.d.ts.map +0 -1
- package/datastore/recordValues.js +0 -282
- package/datastore/recordValues.js.map +0 -1
- package/datastore/useDatastore.d.ts +0 -39
- package/datastore/useDatastore.d.ts.map +0 -1
- package/datastore/useDatastore.js +0 -44
- package/datastore/useDatastore.js.map +0 -1
- package/datastore/useDatastoreCount.d.ts +0 -50
- package/datastore/useDatastoreCount.d.ts.map +0 -1
- package/datastore/useDatastoreCount.js +0 -27
- package/datastore/useDatastoreCount.js.map +0 -1
- package/datastore/useDatastoreDescription.d.ts +0 -54
- package/datastore/useDatastoreDescription.d.ts.map +0 -1
- package/datastore/useDatastoreDescription.js +0 -50
- package/datastore/useDatastoreDescription.js.map +0 -1
- package/datastore/useDatastoreList.d.ts +0 -57
- package/datastore/useDatastoreList.d.ts.map +0 -1
- package/datastore/useDatastoreList.js +0 -29
- package/datastore/useDatastoreList.js.map +0 -1
- package/datastore/useDatastoreSearch.d.ts +0 -19
- package/datastore/useDatastoreSearch.d.ts.map +0 -1
- package/datastore/useDatastoreSearch.js +0 -22
- package/datastore/useDatastoreSearch.js.map +0 -1
- package/datastore/useDeleteDatastore.d.ts +0 -53
- package/datastore/useDeleteDatastore.d.ts.map +0 -1
- package/datastore/useDeleteDatastore.js +0 -40
- package/datastore/useDeleteDatastore.js.map +0 -1
- package/datastore/useDeleteRecord.d.ts +0 -34
- package/datastore/useDeleteRecord.d.ts.map +0 -1
- package/datastore/useDeleteRecord.js +0 -43
- package/datastore/useDeleteRecord.js.map +0 -1
- package/datastore/useInsertRecord.d.ts +0 -50
- package/datastore/useInsertRecord.d.ts.map +0 -1
- package/datastore/useInsertRecord.js +0 -55
- package/datastore/useInsertRecord.js.map +0 -1
- package/datastore/useRecordCollection.d.ts +0 -104
- package/datastore/useRecordCollection.d.ts.map +0 -1
- package/datastore/useRecordCollection.js +0 -136
- package/datastore/useRecordCollection.js.map +0 -1
- package/datastore/useRecordList.d.ts +0 -66
- package/datastore/useRecordList.d.ts.map +0 -1
- package/datastore/useRecordList.js +0 -58
- package/datastore/useRecordList.js.map +0 -1
- package/datastore/useUpdateRecord.d.ts +0 -52
- package/datastore/useUpdateRecord.d.ts.map +0 -1
- package/datastore/useUpdateRecord.js +0 -55
- package/datastore/useUpdateRecord.js.map +0 -1
- package/src/agent/__tests__/AgentDetailView.datastores.test.tsx +0 -166
- package/src/datastore/CollectionRecordsBrowser.tsx +0 -571
- package/src/datastore/CollectionSchemaView.tsx +0 -247
- package/src/datastore/DatastoreDetailView.tsx +0 -201
- package/src/datastore/DatastoreSyncReport.tsx +0 -105
- package/src/datastore/DeleteDatastoreDialog.tsx +0 -202
- package/src/datastore/FieldValueControl.tsx +0 -289
- package/src/datastore/RecordFilterBuilder.tsx +0 -422
- package/src/datastore/RecordFormPanel.tsx +0 -432
- package/src/datastore/__tests__/CollectionRecordsBrowser.test.tsx +0 -245
- package/src/datastore/__tests__/DeleteDatastoreDialog.test.tsx +0 -133
- package/src/datastore/__tests__/FieldValueControl.test.tsx +0 -160
- package/src/datastore/__tests__/RecordFilterBuilder.test.tsx +0 -155
- package/src/datastore/__tests__/RecordFormPanel.test.tsx +0 -227
- package/src/datastore/__tests__/recordFilter.test.ts +0 -169
- package/src/datastore/__tests__/recordMutations.test.tsx +0 -195
- package/src/datastore/__tests__/recordValues.test.ts +0 -359
- package/src/datastore/__tests__/schemaAndSyncViews.test.tsx +0 -142
- package/src/datastore/__tests__/useDatastoreDescription.test.tsx +0 -143
- package/src/datastore/__tests__/useRecordCollection.test.tsx +0 -250
- package/src/datastore/__tests__/useRecordList.test.tsx +0 -85
- package/src/datastore/index.ts +0 -111
- package/src/datastore/recordFilter.ts +0 -154
- package/src/datastore/recordValues.ts +0 -327
- package/src/datastore/useDatastore.ts +0 -67
- package/src/datastore/useDatastoreCount.ts +0 -69
- package/src/datastore/useDatastoreDescription.ts +0 -90
- package/src/datastore/useDatastoreList.ts +0 -79
- package/src/datastore/useDatastoreSearch.ts +0 -39
- package/src/datastore/useDeleteDatastore.ts +0 -87
- package/src/datastore/useDeleteRecord.ts +0 -77
- package/src/datastore/useInsertRecord.ts +0 -92
- package/src/datastore/useRecordCollection.ts +0 -256
- package/src/datastore/useRecordList.ts +0 -131
- package/src/datastore/useUpdateRecord.ts +0 -95
|
@@ -1,422 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useCallback, useMemo, useState } from "react";
|
|
4
|
-
import { cn } from "@stigmer/theme";
|
|
5
|
-
import { Popover } from "@base-ui/react/popover";
|
|
6
|
-
import { create, type JsonValue } from "@bufbuild/protobuf";
|
|
7
|
-
import { RecordConditionOp } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/record_io_pb";
|
|
8
|
-
import {
|
|
9
|
-
FieldDeclarationSchema,
|
|
10
|
-
type CollectionDeclaration,
|
|
11
|
-
type FieldDeclaration,
|
|
12
|
-
} from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/spec_pb";
|
|
13
|
-
import { useStigmerPortalContainer } from "../portal-container.js";
|
|
14
|
-
import { FIELD_INPUT_CLASSES, FieldValueControl } from "./FieldValueControl.js";
|
|
15
|
-
import {
|
|
16
|
-
filterableFields,
|
|
17
|
-
formatConditionChip,
|
|
18
|
-
isConditionComplete,
|
|
19
|
-
type FilterableField,
|
|
20
|
-
type RecordConditionDraft,
|
|
21
|
-
} from "./recordFilter.js";
|
|
22
|
-
import {
|
|
23
|
-
isListOperator,
|
|
24
|
-
isValuelessOperator,
|
|
25
|
-
OPERATOR_LABELS,
|
|
26
|
-
} from "./recordValues.js";
|
|
27
|
-
|
|
28
|
-
/** Props for {@link RecordFilterBuilder}. */
|
|
29
|
-
export interface RecordFilterBuilderProps {
|
|
30
|
-
/** The collection whose declared schema drives fields and operators. */
|
|
31
|
-
readonly collection: CollectionDeclaration;
|
|
32
|
-
/**
|
|
33
|
-
* The caller's readable fields from `describeDatastore` (the read
|
|
34
|
-
* verb's `readable_fields`). Empty or omitted means every field.
|
|
35
|
-
* When restricted, unreadable fields are not offered — the server
|
|
36
|
-
* refuses conditions on them.
|
|
37
|
-
*/
|
|
38
|
-
readonly readableFields?: readonly string[];
|
|
39
|
-
/** Active conditions (AND-combined, per the DD-005 grammar). */
|
|
40
|
-
readonly conditions: readonly RecordConditionDraft[];
|
|
41
|
-
/** Called with the full condition set on add/remove/clear. */
|
|
42
|
-
readonly onChange: (conditions: readonly RecordConditionDraft[]) => void;
|
|
43
|
-
/** Additional CSS classes for the root container. */
|
|
44
|
-
readonly className?: string;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Schema-aware filter builder for the records browser (DD-008 SD-3):
|
|
49
|
-
* active conditions render as removable chips; the "Add filter" popover
|
|
50
|
-
* is draft-then-apply — a condition takes effect only on Apply, so
|
|
51
|
-
* half-built drafts never fire queries.
|
|
52
|
-
*
|
|
53
|
-
* The builder can express nothing `findRecords` cannot serve (DD-008
|
|
54
|
-
* invariant 2): fields come from the declarations plus the filterable
|
|
55
|
-
* system fields, operators from the per-type matrix (an unservable
|
|
56
|
-
* operator is structurally unofferable), and values from the same typed
|
|
57
|
-
* controls the record form uses.
|
|
58
|
-
*
|
|
59
|
-
* All visual properties flow through `--stgm-*` design tokens.
|
|
60
|
-
* Zero Console dependencies — safe for platform builder embedding.
|
|
61
|
-
*/
|
|
62
|
-
export function RecordFilterBuilder({
|
|
63
|
-
collection,
|
|
64
|
-
readableFields,
|
|
65
|
-
conditions,
|
|
66
|
-
onChange,
|
|
67
|
-
className,
|
|
68
|
-
}: RecordFilterBuilderProps) {
|
|
69
|
-
const fields = useMemo(
|
|
70
|
-
() => filterableFields(collection, readableFields),
|
|
71
|
-
[collection, readableFields],
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
const removeCondition = useCallback(
|
|
75
|
-
(index: number) => onChange(conditions.filter((_, i) => i !== index)),
|
|
76
|
-
[conditions, onChange],
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
const addCondition = useCallback(
|
|
80
|
-
(draft: RecordConditionDraft) => onChange([...conditions, draft]),
|
|
81
|
-
[conditions, onChange],
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
return (
|
|
85
|
-
<div
|
|
86
|
-
role="toolbar"
|
|
87
|
-
aria-label="Record filters"
|
|
88
|
-
className={cn("stg:flex stg:flex-wrap stg:items-center stg:gap-1.5", className)}
|
|
89
|
-
>
|
|
90
|
-
{conditions.map((condition, index) => (
|
|
91
|
-
<span
|
|
92
|
-
key={`${condition.field}:${index}`}
|
|
93
|
-
className="stg:inline-flex stg:items-center stg:gap-1 stg:rounded-full stg:border stg:border-border stg:bg-muted stg:px-2 stg:py-0.5 stg:text-xs stg:text-foreground"
|
|
94
|
-
>
|
|
95
|
-
{formatConditionChip(condition)}
|
|
96
|
-
<button
|
|
97
|
-
type="button"
|
|
98
|
-
onClick={() => removeCondition(index)}
|
|
99
|
-
aria-label={`Remove filter ${formatConditionChip(condition)}`}
|
|
100
|
-
className={cn(
|
|
101
|
-
"stg:inline-flex stg:size-3.5 stg:items-center stg:justify-center stg:rounded-full stg:text-muted-foreground",
|
|
102
|
-
"stg:hover:bg-accent stg:hover:text-foreground",
|
|
103
|
-
"stg:focus-visible:outline-none stg:focus-visible:ring-1 stg:focus-visible:ring-ring",
|
|
104
|
-
)}
|
|
105
|
-
>
|
|
106
|
-
<CrossIcon className="stg:size-2.5" />
|
|
107
|
-
</button>
|
|
108
|
-
</span>
|
|
109
|
-
))}
|
|
110
|
-
|
|
111
|
-
<AddFilterPopover fields={fields} onApply={addCondition} />
|
|
112
|
-
|
|
113
|
-
{conditions.length > 1 && (
|
|
114
|
-
<button
|
|
115
|
-
type="button"
|
|
116
|
-
onClick={() => onChange([])}
|
|
117
|
-
className={cn(
|
|
118
|
-
"stg:text-xs stg:text-muted-foreground stg:underline-offset-2 stg:hover:text-foreground stg:hover:underline",
|
|
119
|
-
"stg:focus-visible:outline-none stg:focus-visible:ring-1 stg:focus-visible:ring-ring",
|
|
120
|
-
)}
|
|
121
|
-
>
|
|
122
|
-
Clear all
|
|
123
|
-
</button>
|
|
124
|
-
)}
|
|
125
|
-
</div>
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// ---------------------------------------------------------------------------
|
|
130
|
-
// The draft-then-apply popover
|
|
131
|
-
// ---------------------------------------------------------------------------
|
|
132
|
-
|
|
133
|
-
function AddFilterPopover({
|
|
134
|
-
fields,
|
|
135
|
-
onApply,
|
|
136
|
-
}: {
|
|
137
|
-
readonly fields: readonly FilterableField[];
|
|
138
|
-
readonly onApply: (draft: RecordConditionDraft) => void;
|
|
139
|
-
}) {
|
|
140
|
-
const portalContainer = useStigmerPortalContainer();
|
|
141
|
-
const [open, setOpen] = useState(false);
|
|
142
|
-
|
|
143
|
-
const [fieldName, setFieldName] = useState("");
|
|
144
|
-
const [op, setOp] = useState<RecordConditionOp>(RecordConditionOp.record_condition_op_unspecified);
|
|
145
|
-
const [value, setValue] = useState<JsonValue | undefined>(undefined);
|
|
146
|
-
const [values, setValues] = useState<readonly JsonValue[]>([]);
|
|
147
|
-
|
|
148
|
-
const selected = fields.find((f) => f.name === fieldName);
|
|
149
|
-
|
|
150
|
-
const reset = useCallback(() => {
|
|
151
|
-
setFieldName("");
|
|
152
|
-
setOp(RecordConditionOp.record_condition_op_unspecified);
|
|
153
|
-
setValue(undefined);
|
|
154
|
-
setValues([]);
|
|
155
|
-
}, []);
|
|
156
|
-
|
|
157
|
-
const handleOpenChange = useCallback(
|
|
158
|
-
(next: boolean) => {
|
|
159
|
-
setOpen(next);
|
|
160
|
-
if (!next) reset();
|
|
161
|
-
},
|
|
162
|
-
[reset],
|
|
163
|
-
);
|
|
164
|
-
|
|
165
|
-
const selectField = useCallback(
|
|
166
|
-
(name: string) => {
|
|
167
|
-
setFieldName(name);
|
|
168
|
-
setOp(RecordConditionOp.record_condition_op_unspecified);
|
|
169
|
-
setValue(undefined);
|
|
170
|
-
setValues([]);
|
|
171
|
-
},
|
|
172
|
-
[],
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
const draft: RecordConditionDraft = {
|
|
176
|
-
field: fieldName,
|
|
177
|
-
op,
|
|
178
|
-
value: isListOperator(op) || isValuelessOperator(op) ? undefined : value,
|
|
179
|
-
values: isListOperator(op) ? values : undefined,
|
|
180
|
-
};
|
|
181
|
-
const canApply = fieldName !== "" && isConditionComplete(draft);
|
|
182
|
-
|
|
183
|
-
const apply = useCallback(() => {
|
|
184
|
-
// Rebuild from state rather than closing over the render's draft.
|
|
185
|
-
const applied: RecordConditionDraft = {
|
|
186
|
-
field: fieldName,
|
|
187
|
-
op,
|
|
188
|
-
value: isListOperator(op) || isValuelessOperator(op) ? undefined : value,
|
|
189
|
-
values: isListOperator(op) ? values : undefined,
|
|
190
|
-
};
|
|
191
|
-
onApply(applied);
|
|
192
|
-
handleOpenChange(false);
|
|
193
|
-
}, [onApply, handleOpenChange, fieldName, op, value, values]);
|
|
194
|
-
|
|
195
|
-
return (
|
|
196
|
-
<Popover.Root open={open} onOpenChange={handleOpenChange}>
|
|
197
|
-
<Popover.Trigger
|
|
198
|
-
className={cn(
|
|
199
|
-
"stg:inline-flex stg:items-center stg:gap-1 stg:rounded-full stg:border stg:border-dashed stg:border-input stg:px-2 stg:py-0.5 stg:text-xs stg:text-muted-foreground",
|
|
200
|
-
"stg:hover:border-ring stg:hover:text-foreground",
|
|
201
|
-
"stg:focus-visible:outline-none stg:focus-visible:ring-1 stg:focus-visible:ring-ring",
|
|
202
|
-
)}
|
|
203
|
-
>
|
|
204
|
-
<PlusIcon className="stg:size-3" />
|
|
205
|
-
Add filter
|
|
206
|
-
</Popover.Trigger>
|
|
207
|
-
<Popover.Portal container={portalContainer}>
|
|
208
|
-
<Popover.Positioner sideOffset={8} align="start">
|
|
209
|
-
<Popover.Popup
|
|
210
|
-
className={cn(
|
|
211
|
-
"stg:z-popover stg:w-72 stg:rounded-lg stg:border stg:border-border stg:bg-popover stg:p-3 stg:text-popover-foreground stg:shadow-md",
|
|
212
|
-
"stg:flex stg:flex-col stg:gap-2",
|
|
213
|
-
)}
|
|
214
|
-
>
|
|
215
|
-
<label className="stg:flex stg:flex-col stg:gap-1 stg:text-xs stg:font-medium stg:text-muted-foreground">
|
|
216
|
-
Field
|
|
217
|
-
<select
|
|
218
|
-
className={FIELD_INPUT_CLASSES}
|
|
219
|
-
value={fieldName}
|
|
220
|
-
onChange={(e) => selectField(e.target.value)}
|
|
221
|
-
>
|
|
222
|
-
<option value="">— Select —</option>
|
|
223
|
-
{fields.map((f) => (
|
|
224
|
-
<option key={f.name} value={f.name}>
|
|
225
|
-
{f.name}
|
|
226
|
-
</option>
|
|
227
|
-
))}
|
|
228
|
-
</select>
|
|
229
|
-
</label>
|
|
230
|
-
|
|
231
|
-
{selected && (
|
|
232
|
-
<label className="stg:flex stg:flex-col stg:gap-1 stg:text-xs stg:font-medium stg:text-muted-foreground">
|
|
233
|
-
Operator
|
|
234
|
-
<select
|
|
235
|
-
className={FIELD_INPUT_CLASSES}
|
|
236
|
-
value={op === RecordConditionOp.record_condition_op_unspecified ? "" : String(op)}
|
|
237
|
-
onChange={(e) => {
|
|
238
|
-
setOp(
|
|
239
|
-
e.target.value === ""
|
|
240
|
-
? RecordConditionOp.record_condition_op_unspecified
|
|
241
|
-
: (Number(e.target.value) as RecordConditionOp),
|
|
242
|
-
);
|
|
243
|
-
setValue(undefined);
|
|
244
|
-
setValues([]);
|
|
245
|
-
}}
|
|
246
|
-
>
|
|
247
|
-
<option value="">— Select —</option>
|
|
248
|
-
{selected.operators.map((o) => (
|
|
249
|
-
<option key={o} value={o}>
|
|
250
|
-
{OPERATOR_LABELS.get(o)}
|
|
251
|
-
</option>
|
|
252
|
-
))}
|
|
253
|
-
</select>
|
|
254
|
-
</label>
|
|
255
|
-
)}
|
|
256
|
-
|
|
257
|
-
{selected &&
|
|
258
|
-
op !== RecordConditionOp.record_condition_op_unspecified &&
|
|
259
|
-
!isValuelessOperator(op) &&
|
|
260
|
-
(isListOperator(op) ? (
|
|
261
|
-
<ListValueEditor field={selected} values={values} onChange={setValues} />
|
|
262
|
-
) : (
|
|
263
|
-
<div className="stg:flex stg:flex-col stg:gap-1 stg:text-xs stg:font-medium stg:text-muted-foreground">
|
|
264
|
-
Value
|
|
265
|
-
<FieldValueControl
|
|
266
|
-
field={controlDeclaration(selected)}
|
|
267
|
-
value={value}
|
|
268
|
-
onChange={setValue}
|
|
269
|
-
aria-label={`Filter value for ${selected.name}`}
|
|
270
|
-
/>
|
|
271
|
-
</div>
|
|
272
|
-
))}
|
|
273
|
-
|
|
274
|
-
<div className="stg:mt-1 stg:flex stg:justify-end stg:gap-2">
|
|
275
|
-
<button
|
|
276
|
-
type="button"
|
|
277
|
-
onClick={() => handleOpenChange(false)}
|
|
278
|
-
className={cn(
|
|
279
|
-
"stg:rounded-md stg:border stg:border-input stg:bg-background stg:px-2.5 stg:py-1 stg:text-xs stg:font-medium stg:text-foreground",
|
|
280
|
-
"stg:hover:bg-accent stg:hover:text-accent-foreground",
|
|
281
|
-
"stg:focus-visible:outline-none stg:focus-visible:ring-1 stg:focus-visible:ring-ring",
|
|
282
|
-
)}
|
|
283
|
-
>
|
|
284
|
-
Cancel
|
|
285
|
-
</button>
|
|
286
|
-
<button
|
|
287
|
-
type="button"
|
|
288
|
-
onClick={apply}
|
|
289
|
-
disabled={!canApply}
|
|
290
|
-
className={cn(
|
|
291
|
-
"stg:rounded-md stg:bg-primary stg:px-2.5 stg:py-1 stg:text-xs stg:font-medium stg:text-primary-foreground",
|
|
292
|
-
"stg:hover:bg-primary-hover",
|
|
293
|
-
"stg:focus-visible:outline-none stg:focus-visible:ring-1 stg:focus-visible:ring-ring",
|
|
294
|
-
"stg:disabled:pointer-events-none stg:disabled:opacity-50",
|
|
295
|
-
)}
|
|
296
|
-
>
|
|
297
|
-
Apply
|
|
298
|
-
</button>
|
|
299
|
-
</div>
|
|
300
|
-
</Popover.Popup>
|
|
301
|
-
</Popover.Positioner>
|
|
302
|
-
</Popover.Portal>
|
|
303
|
-
</Popover.Root>
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* The declaration handed to the value control: the declared field
|
|
309
|
-
* itself, or a synthetic declaration for system fields (`id` as string,
|
|
310
|
-
* audit timestamps as timestamp).
|
|
311
|
-
*/
|
|
312
|
-
function controlDeclaration(field: FilterableField): FieldDeclaration {
|
|
313
|
-
return (
|
|
314
|
-
field.declaration ??
|
|
315
|
-
create(FieldDeclarationSchema, { name: field.name, type: field.type })
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// ---------------------------------------------------------------------------
|
|
320
|
-
// List-value entry for in / not_in
|
|
321
|
-
// ---------------------------------------------------------------------------
|
|
322
|
-
|
|
323
|
-
function ListValueEditor({
|
|
324
|
-
field,
|
|
325
|
-
values,
|
|
326
|
-
onChange,
|
|
327
|
-
}: {
|
|
328
|
-
readonly field: FilterableField;
|
|
329
|
-
readonly values: readonly JsonValue[];
|
|
330
|
-
readonly onChange: (values: readonly JsonValue[]) => void;
|
|
331
|
-
}) {
|
|
332
|
-
const [pending, setPending] = useState<JsonValue | undefined>(undefined);
|
|
333
|
-
|
|
334
|
-
const add = useCallback(() => {
|
|
335
|
-
if (pending === undefined) return;
|
|
336
|
-
onChange([...values, pending]);
|
|
337
|
-
setPending(undefined);
|
|
338
|
-
}, [pending, values, onChange]);
|
|
339
|
-
|
|
340
|
-
return (
|
|
341
|
-
<div className="stg:flex stg:flex-col stg:gap-1 stg:text-xs stg:font-medium stg:text-muted-foreground">
|
|
342
|
-
Values
|
|
343
|
-
{values.length > 0 && (
|
|
344
|
-
<div className="stg:flex stg:flex-wrap stg:gap-1">
|
|
345
|
-
{values.map((v, i) => (
|
|
346
|
-
<span
|
|
347
|
-
key={i}
|
|
348
|
-
className="stg:inline-flex stg:items-center stg:gap-1 stg:rounded-full stg:bg-muted stg:px-2 stg:py-0.5 stg:text-xs stg:font-normal stg:text-foreground"
|
|
349
|
-
>
|
|
350
|
-
{typeof v === "string" ? v : JSON.stringify(v)}
|
|
351
|
-
<button
|
|
352
|
-
type="button"
|
|
353
|
-
onClick={() => onChange(values.filter((_, j) => j !== i))}
|
|
354
|
-
aria-label={`Remove value ${typeof v === "string" ? v : JSON.stringify(v)}`}
|
|
355
|
-
className="stg:inline-flex stg:size-3 stg:items-center stg:justify-center stg:rounded-full stg:text-muted-foreground stg:hover:text-foreground"
|
|
356
|
-
>
|
|
357
|
-
<CrossIcon className="stg:size-2" />
|
|
358
|
-
</button>
|
|
359
|
-
</span>
|
|
360
|
-
))}
|
|
361
|
-
</div>
|
|
362
|
-
)}
|
|
363
|
-
<div className="stg:flex stg:items-center stg:gap-1.5">
|
|
364
|
-
<FieldValueControl
|
|
365
|
-
field={controlDeclaration(field)}
|
|
366
|
-
value={pending}
|
|
367
|
-
onChange={setPending}
|
|
368
|
-
aria-label={`Add filter value for ${field.name}`}
|
|
369
|
-
/>
|
|
370
|
-
<button
|
|
371
|
-
type="button"
|
|
372
|
-
onClick={add}
|
|
373
|
-
disabled={pending === undefined}
|
|
374
|
-
className={cn(
|
|
375
|
-
"stg:shrink-0 stg:rounded-md stg:border stg:border-input stg:bg-background stg:px-2 stg:py-1 stg:text-xs stg:font-medium stg:text-foreground",
|
|
376
|
-
"stg:hover:bg-accent stg:hover:text-accent-foreground",
|
|
377
|
-
"stg:focus-visible:outline-none stg:focus-visible:ring-1 stg:focus-visible:ring-ring",
|
|
378
|
-
"stg:disabled:pointer-events-none stg:disabled:opacity-50",
|
|
379
|
-
)}
|
|
380
|
-
>
|
|
381
|
-
Add
|
|
382
|
-
</button>
|
|
383
|
-
</div>
|
|
384
|
-
</div>
|
|
385
|
-
);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// ---------------------------------------------------------------------------
|
|
389
|
-
// Icons (inline SVG — no icon-library dependency)
|
|
390
|
-
// ---------------------------------------------------------------------------
|
|
391
|
-
|
|
392
|
-
function PlusIcon({ className }: { readonly className?: string }) {
|
|
393
|
-
return (
|
|
394
|
-
<svg
|
|
395
|
-
className={className}
|
|
396
|
-
viewBox="0 0 16 16"
|
|
397
|
-
fill="none"
|
|
398
|
-
stroke="currentColor"
|
|
399
|
-
strokeWidth="1.5"
|
|
400
|
-
strokeLinecap="round"
|
|
401
|
-
aria-hidden="true"
|
|
402
|
-
>
|
|
403
|
-
<path d="M8 3v10M3 8h10" />
|
|
404
|
-
</svg>
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
function CrossIcon({ className }: { readonly className?: string }) {
|
|
409
|
-
return (
|
|
410
|
-
<svg
|
|
411
|
-
className={className}
|
|
412
|
-
viewBox="0 0 16 16"
|
|
413
|
-
fill="none"
|
|
414
|
-
stroke="currentColor"
|
|
415
|
-
strokeWidth="1.5"
|
|
416
|
-
strokeLinecap="round"
|
|
417
|
-
aria-hidden="true"
|
|
418
|
-
>
|
|
419
|
-
<path d="M4 4l8 8M12 4l-8 8" />
|
|
420
|
-
</svg>
|
|
421
|
-
);
|
|
422
|
-
}
|