Cross-Source Enrichment Review
v0.5.0Full-page cross-source enrichment review block with tag entities, clusters, relationships, and source connection map.
Preview
Install & usage
npx shadcn@latest add @signalos/cross-source-01Pulls the page source plus its widget dependencies (@signalos/tokens, @signalos/utils, @signalos/button, @signalos/badge, @signalos/form-dialog, @signalos/select, @signalos/checkbox, @signalos/input, @signalos/label, @signalos/tooltip, @signalos/split-panel, @signalos/page-header, @signalos/textarea, @signalos/dialog) into your app - you own the copy. Requires access.
usage
// after: npx shadcn@latest add @signalos/cross-source-01
// mount the pulled page in a route, e.g. src/app/semantics-details/page.tsx
import CrossSourceReview from "@/components/blocks/cross-source-01/CrossSource"
export default function Page() {
return <CrossSourceReview />
}Props
CrossSourceReviewProps
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | undefined | - | Title shown in the page header. |
| connectorIds | string[] | undefined | - | Source connector identifiers. |
| relationships | CrossSourceRelationship[] | undefined | - | Cross-source relationships to review. |
| flatTagEntities | FlatTagEntry[] | undefined | - | Flat list of tag entities to review. |
| allClusters | ClustersWithConnector[] | undefined | - | All clusters (with connector id attached). |
| allAvailableTags | string[] | undefined | - | All available tag names for dropdowns. |
| allClusterEntities | string[] | undefined | - | All cluster entity names. |
| entityClusterMap | Record<string, string> | undefined | - | Map from entity name to its cluster tag. |
| catalogTables | Record<string, string[]> | undefined | - | Available tables per connector for relationship editing. |
| catalogColumns | Record<string, string[]> | undefined | - | Available columns per connector.table for relationship editing. |
| isConfirmed | boolean | undefined | - | Whether this stage has already been confirmed. |
| stageActions | ReactNode | - | Optional action buttons (e.g. Rerun, Reset) rendered in the header. |
| isSubmitting | boolean | undefined | - | Whether a submit is in progress. |
| errorMessage | string | undefined | - | Error message to show in the banner. |
| activeTab | ReviewTab | undefined | - | Active review tab. |
| relDecisions | Record<number, RelDecision> | undefined | - | Per-relationship decision map. |
| selectedRelIdx | number | null | undefined | - | Index of the selected relationship, or null. |
| deletedRelIndices | Set<number> | undefined | - | Set of relationship indices marked for deletion. |
| isEditingRel | boolean | undefined | - | Whether the selected relationship is being edited. |
| relEditForm | EditRelForm | undefined | - | Current relationship edit form values. |
| tagDecisions | Record<number, TagDecision> | undefined | - | Per-tag decision map. |
| selectedTagIdx | number | undefined | - | Index of the selected tag entity. |
| deletedTagIndices | Set<number> | undefined | - | Set of tag indices marked for deletion. |
| clusterEdits | Record<number, ClusterEditValue> | undefined | - | Per-cluster edit map. |
| editingClusterIdx | number | null | undefined | - | Index of the cluster currently being edited. |
| clusterDialogOpen | boolean | undefined | - | Whether the cluster edit dialog is open. |
| clusterEditState | ClusterEditState | undefined | - | Current cluster edit state. |
| displayRelationships | CrossSourceRelationship[] | undefined | - | Derived display relationships (with edits applied). |
| changeSummaries | Record<number, string[]> | undefined | - | Per-relationship change summaries. |
| sourceStats | Record<string, { tables: Set<string>; relCount: number; }> | undefined | - | Source stats for the connection map. |
| totalFlagged | number | undefined | - | Total number of flagged items. |
| editFromTables | string[] | undefined | - | Derived tables for the from-side edit selects. |
| editToTables | string[] | undefined | - | Derived tables for the to-side edit selects. |
| editFromCols | string[] | undefined | - | Derived columns for the from-side edit selects. |
| editToCols | string[] | undefined | - | Derived columns for the to-side edit selects. |
| editHasChanges | boolean | undefined | - | Whether the edit form has unsaved changes. |
| editIsFormValid | boolean | undefined | - | Whether the edit form is valid for saving. |
| onTabChange | ((tab: ReviewTab) => void) | undefined | - | Fired when the active tab changes. |
| onSubmit | ((action: "confirm") => void) | undefined | - | Fired when the user clicks submit. |
| onDismissError | (() => void) | undefined | - | Fired when the user dismisses the error banner. |
| onSelectRel | ((idx: number) => void) | undefined | - | Fired when a relationship is selected. |
| onAcceptRel | ((idx: number) => void) | undefined | - | Fired when a relationship is accepted. |
| onStartEditing | (() => void) | undefined | - | Fired when the edit mode starts. |
| onCancelEditing | (() => void) | undefined | - | Fired when the edit mode is cancelled. |
| onSaveRelEdit | (() => void) | undefined | - | Fired when the rel edit is saved. |
| onEditFromSourceChange | ((v: string) => void) | undefined | - | Fired when the edit from-source changes. |
| onEditFromTableChange | ((v: string) => void) | undefined | - | Fired when the edit from-table changes. |
| onEditFromColumnChange | ((v: string) => void) | undefined | - | Fired when the edit from-column changes. |
| onEditToSourceChange | ((v: string) => void) | undefined | - | Fired when the edit to-source changes. |
| onEditToTableChange | ((v: string) => void) | undefined | - | Fired when the edit to-table changes. |
| onEditToColumnChange | ((v: string) => void) | undefined | - | Fired when the edit to-column changes. |
| onEditRelTypeChange | ((v: RelationshipType) => void) | undefined | - | Fired when the edit rel-type changes. |
| onEditExplanationChange | ((v: string) => void) | undefined | - | Fired when the edit explanation changes. |
| onToggleDeleteRel | ((idx: number) => void) | undefined | - | Fired when a rel is toggled for deletion. |
| onSelectTag | ((idx: number) => void) | undefined | - | Fired when a tag is selected. |
| onSetTagDecision | ((gi: number, status: "accepted" | "flagged") => void) | undefined | - | Fired when a tag decision changes. |
| onUpdateTagEdit | ((gi: number, field: "tags" | "primary_tag", value: string | string[]) => void) | undefined | - | Fired when a tag edit field changes. |
| onToggleDeleteTag | ((gi: number) => void) | undefined | - | Fired when a tag is toggled for deletion. |
| onOpenEditCluster | ((idx: number) => void) | undefined | - | Fired when the cluster edit dialog is opened. |
| onSetClusterDialogOpen | ((open: boolean) => void) | undefined | - | Fired when the cluster dialog open state changes. |
| onSetClusterEditState | ((state: ClusterEditState) => void) | undefined | - | Fired when the cluster edit state changes. |
| onSaveClusterEdit | ((idx: number, state: ClusterEditState) => void) | undefined | - | Fired when a cluster edit is saved. |
| onUndoClusterEdit | ((idx: number) => void) | undefined | - | Fired when a cluster edit is undone. |
| addRelDialogOpen | boolean | undefined | - | Whether the add-relationship dialog is open. |
| onSetAddRelDialogOpen | ((open: boolean) => void) | undefined | - | Fired when the add-relationship dialog open state changes. |
| isAddRelSubmitting | boolean | undefined | - | Whether the add-relationship dialog is submitting. |
| addRelErrorMessage | string | null | undefined | - | Error message shown inside the add-relationship dialog. |
| addRelForm | CrossSourceRelAddFormState | undefined | - | Controlled form values for the add-relationship dialog. |
| addRelFormErrors | CrossSourceRelAddFormErrors | undefined | - | Validation errors for the add-relationship dialog. |
| onAddRelFormChange | (<K extends keyof CrossSourceRelAddFormState>(field: K, value: CrossSourceRelAddFormState[K]) => void) | undefined | - | Called when a field in the add-relationship form changes. |
| onAddRelFormErrorsChange | ((errors: CrossSourceRelAddFormErrors) => void) | undefined | - | Called when the add-relationship form errors change. |
| onAddRelSubmit | ((values: CrossSourceRelAddValues) => void | Promise<void>) | undefined | - | Fired when the add-relationship form is submitted. |
| addClusterDialogOpen | boolean | undefined | - | Whether the add-cluster dialog is open. |
| onSetAddClusterDialogOpen | ((open: boolean) => void) | undefined | - | Fired when the add-cluster dialog open state changes. |
| isAddClusterSubmitting | boolean | undefined | - | Whether the add-cluster dialog is submitting. |
| addClusterErrorMessage | string | null | undefined | - | Error message shown inside the add-cluster dialog. |
| addClusterForm | ClusterAddFormState | undefined | - | Controlled form values for the add-cluster dialog. |
| addClusterFormErrors | ClusterAddFormErrors | undefined | - | Validation errors for the add-cluster dialog. |
| onAddClusterFormChange | (<K extends keyof ClusterAddFormState>(field: K, value: ClusterAddFormState[K]) => void) | undefined | - | Called when a field in the add-cluster form changes. |
| onAddClusterFormErrorsChange | ((errors: ClusterAddFormErrors) => void) | undefined | - | Called when the add-cluster form errors change. |
| onAddClusterSubmit | ((values: ClusterAddValues) => void | Promise<void>) | undefined | - | Fired when the add-cluster form is submitted. |
| addTagEntityDialogOpen | boolean | undefined | - | Whether the add-tag-entity dialog is open. |
| onSetAddTagEntityDialogOpen | ((open: boolean) => void) | undefined | - | Fired when the add-tag-entity dialog open state changes. |
| isAddTagEntitySubmitting | boolean | undefined | - | Whether the add-tag-entity dialog is submitting. |
| addTagEntityErrorMessage | string | null | undefined | - | Error message shown inside the add-tag-entity dialog. |
| addTagEntityForm | TagEntityAddFormState | undefined | - | Controlled form values for the add-tag-entity dialog. |
| addTagEntityFormErrors | TagEntityAddFormErrors | undefined | - | Validation errors for the add-tag-entity dialog. |
| onAddTagEntityFormChange | (<K extends keyof TagEntityAddFormState>(field: K, value: TagEntityAddFormState[K]) => void) | undefined | - | Called when a field in the add-tag-entity form changes. |
| onAddTagEntityFormErrorsChange | ((errors: TagEntityAddFormErrors) => void) | undefined | - | Called when the add-tag-entity form errors change. |
| onAddTagEntitySubmit | ((values: TagEntityAddValues) => void | Promise<void>) | undefined | - | Fired when the add-tag-entity form is submitted. |
| className | string | undefined | - | |
| data-testid | string | undefined | - | Test identifier rendered as `data-testid` on the root element. |
CrossSourceRelationship
| Prop | Type | Default | Description |
|---|---|---|---|
| from_table* | string | - | |
| from_column* | string | - | |
| from_connector_id* | string | - | |
| to_table* | string | - | |
| to_column* | string | - | |
| to_connector_id* | string | - | |
| relationship_type* | RelationshipType | - | |
| confidence* | number | - | |
| detection_method* | string | - | |
| explanation | string | undefined | - | |
| review_origin | string | undefined | - |
CrossSourceReviewItem
| Prop | Type | Default | Description |
|---|---|---|---|
| index* | number | - | |
| status* | "accepted" | "flagged" | "deleted" | - | |
| feedback* | string | - | |
| edits* | Partial<CrossSourceRelationship> | - |
RelDecision
| Prop | Type | Default | Description |
|---|---|---|---|
| status* | "accepted" | "flagged" | - | |
| feedback* | string | - | |
| edits* | Partial<CrossSourceRelationship> | - |
TagDecision
| Prop | Type | Default | Description |
|---|---|---|---|
| status* | "accepted" | "flagged" | - | |
| feedback* | string | - | |
| edits* | { primary_tag?: string | undefined; tags?: string[] | undefined; } | - | |
| connectorId* | string | - | |
| localIndex* | number | - |
TagEntityMetadata
| Prop | Type | Default | Description |
|---|---|---|---|
| reasoning | string | undefined | - | |
| column_count | number | undefined | - | |
| business_purpose | string | undefined | - | |
| relationships_summary | string | undefined | - | |
| definition | string | undefined | - | |
| mapped_entity | string | undefined | - | |
| domain_context | string | undefined | - |
TagEntity
| Prop | Type | Default | Description |
|---|---|---|---|
| entity_id* | string | - | |
| entity_name* | string | - | |
| entity_type* | string | - | |
| tags* | string[] | - | |
| primary_tag* | string | - | |
| connector_id* | string | - | |
| metadata | TagEntityMetadata | undefined | - | |
| review_origin | string | undefined | - |
TagsArtifact
| Prop | Type | Default | Description |
|---|---|---|---|
| all_tags* | string[] | - | |
| entities* | TagEntity[] | - | |
| connector_id* | string | - | |
| total_entities* | number | - |
GraphNode
| Prop | Type | Default | Description |
|---|---|---|---|
| label* | string | - | |
| node_id | string | undefined | - | |
| tags* | string[] | - | |
| primary_tag* | string | - | |
| connector_id* | string | - | |
| metadata | TagEntityMetadata | undefined | - |
GraphCluster
| Prop | Type | Default | Description |
|---|---|---|---|
| tag* | string | - | |
| nodes* | GraphNode[] | - | |
| node_count* | number | - | |
| edge_count* | number | - | |
| review_origin | string | undefined | - |
GraphArtifact
| Prop | Type | Default | Description |
|---|---|---|---|
| clusters* | GraphCluster[] | - | |
| total_nodes* | number | - | |
| total_edges* | number | - | |
| total_clusters* | number | - | |
| connector_id* | string | - | |
| cross_cluster_edges | unknown[] | undefined | - |
TagReviewItem
| Prop | Type | Default | Description |
|---|---|---|---|
| index* | number | - | |
| status* | "accepted" | "flagged" | "deleted" | - | |
| feedback* | string | - | |
| edits* | { primary_tag?: string | undefined; tags?: string[] | undefined; } | - | |
| connector_id* | string | - | |
| local_index* | number | - |
ClusterReviewItem
| Prop | Type | Default | Description |
|---|---|---|---|
| index* | number | - | |
| status* | "accepted" | "flagged" | "deleted" | - | |
| edits* | { tag?: string | undefined; entities?: string[] | undefined; } | - | |
| connector_id* | string | - |
CrossSourceItemEdits
| Prop | Type | Default | Description |
|---|---|---|---|
| from_connector_id* | string | - | |
| from_table* | string | - | |
| from_column* | string | - | |
| to_connector_id* | string | - | |
| to_table* | string | - | |
| to_column* | string | - | |
| relationship_type* | RelationshipType | - | |
| explanation | string | undefined | - |
CrossSourceTagItemEdits
| Prop | Type | Default | Description |
|---|---|---|---|
| tag* | string | - | |
| entity_names* | string[] | - |
CrossSourceClusterItemEdits
| Prop | Type | Default | Description |
|---|---|---|---|
| cluster_tag* | string | - | |
| entity_names* | string[] | - |
AddCrossSourceItemResponse
| Prop | Type | Default | Description |
|---|---|---|---|
| status | string | undefined | - | |
| index | number | undefined | - | |
| item | CrossSourceRelationship | undefined | - | |
| tag | string | undefined | - | |
| cluster_tag | string | undefined | - | |
| affected | string[] | undefined | - | |
| soft_warning | string | undefined | - |
ItemEditPayload
| Prop | Type | Default | Description |
|---|---|---|---|
| cluster_tag | string | undefined | - | |
| entity_names | string[] | undefined | - | |
| tag | string | undefined | - |
SubmitCrossSourceEnrichedPayload
| Prop | Type | Default | Description |
|---|---|---|---|
| action* | "confirm" | "retry" | "add_item" | "add_cluster" | "add_tag" | - | |
| items | CrossSourceReviewItem[] | undefined | - | |
| tag_items | TagReviewItem[] | undefined | - | |
| cluster_items | ClusterReviewItem[] | undefined | - | |
| item_edits | ItemEditPayload | undefined | - | |
| feedback | string | undefined | - | |
| connector_id | string | undefined | - |
SubmitCrossSourceReviewPayload
| Prop | Type | Default | Description |
|---|---|---|---|
| action* | "confirm" | "revise" | - | |
| items* | CrossSourceReviewItem[] | - |
EditRelForm
| Prop | Type | Default | Description |
|---|---|---|---|
| fromSource* | string | - | |
| fromTable* | string | - | |
| fromColumn* | string | - | |
| toSource* | string | - | |
| toTable* | string | - | |
| toColumn* | string | - | |
| relType* | RelationshipType | - | |
| explanation* | string | - |
RelEditChangeSummary
| Prop | Type | Default | Description |
|---|---|---|---|
| changes* | string[] | - |
ClusterEditValue
| Prop | Type | Default | Description |
|---|---|---|---|
| status* | "accepted" | "flagged" | - | |
| tag | string | undefined | - | |
| entities | string[] | undefined | - |
ClusterEditState
| Prop | Type | Default | Description |
|---|---|---|---|
| tag* | string | - | |
| newTagInput* | string | - | |
| entities* | string[] | - |
FlatTagEntry
| Prop | Type | Default | Description |
|---|---|---|---|
| entity* | TagEntity | - | |
| connectorId* | string | - | |
| localIndex* | number | - |
ClustersWithConnector
| Prop | Type | Default | Description |
|---|---|---|---|
| _connectorId* | string | - | |
| tag* | string | - | |
| nodes* | GraphNode[] | - | |
| node_count* | number | - | |
| edge_count* | number | - | |
| review_origin | string | undefined | - |
CrossSourceFdeInput
| Prop | Type | Default | Description |
|---|---|---|---|
| items* | CrossSourceReviewItem[] | - |
ClusterAddValues
| Prop | Type | Default | Description |
|---|---|---|---|
| connectorId* | string | - | |
| cluster_tag* | string | - | |
| entity_names* | string[] | - | |
| note | string | undefined | - |
CrossSourceRelAddValues
| Prop | Type | Default | Description |
|---|---|---|---|
| from_connector_id* | string | - | |
| from_table* | string | - | |
| from_column* | string | - | |
| to_connector_id* | string | - | |
| to_table* | string | - | |
| to_column* | string | - | |
| relationship_type* | RelationshipType | - | |
| explanation | string | undefined | - |
TagEntityAddValues
| Prop | Type | Default | Description |
|---|---|---|---|
| connectorId* | string | - | |
| entity_name* | string | - | |
| tags* | string[] | - | |
| reasoning | string | undefined | - |
Supporting types
export type RelationshipType = | "references" | "has_many" | "belongs_to" | "many_to_many" | "one_to_one" | "measures" | "targets" | "sponsors" | "conducted_at" | "co_occurs" | "related_to"
export type ReviewTab = "tags" | "relationships"
export type AddCrossSourceItemPayload =
| {
action: "add_item"
item_edits: CrossSourceItemEdits
feedback?: string
connector_id?: string
}
| {
action: "add_tag"
connector_id: string
item_edits: CrossSourceTagItemEdits
feedback?: string
}
| {
action: "add_cluster"
connector_id: string
item_edits: CrossSourceClusterItemEdits
feedback?: string
}export type CreateCrossSourceRelationshipPayload = CrossSourceRelationship
More Semantics Stages variants
Changelog
0.5.0
- Decompose
CrossSource.tsxintoRelationshipsTab,TagsTab, andCrossSourcePageHeadersubcomponents; moveCrossSourceReviewPropsinterface toCrossSource.types.ts. - Add
TagEntityAddDialogstate (addTagEntityDialogOpen) andhandleAddTagEntityDialogOpenChangehandler to the preview.
0.4.9
- Replace raw scale token classes with semantic tokens (text-text-error, bg-bg-success, border-border-warning, etc.) to respect client theme overrides.
0.4.8
- Fix cross-item imports to use canonical @/components paths; add missing signalos.category to manifests.
0.4.7
CrossSource: renamedata-testidfromcross-source-add-tag-entity-buttontocross-source-add-tag-buttonon the tags tab header button.CrossSource: replace single Dismiss button in error banner with a Retry + Dismiss pair; Retry callsonSubmit?.("confirm").CrossSource: swapavailableTags={allAvailableTags}→availableTags={allClusterEntities}when passing toTagEntityAddDialog.CrossSource: reorderTagEntityAddDialogrender beforeClusterAddDialogin the dialog portal section.ClusterAddDialog: replaceFormDialogwidget with rawDialog/DialogContent/DialogHeader/DialogTitle/DialogDescription/DialogFooter.CrossSource.types.ts: removeTagEntityAddFormStateandTagEntityAddFormErrorsinterfaces (now defined inside the component file).
0.4.6
- Add TagEntityAddDialog as a fully controlled component using FormDialog; wire into CrossSource with add-tag-entity props and header button on the tags tab.
- Fix forbidden
@/schemasimport, internal state, and raw color-scale tokens in TagEntityAddDialog. - Add TagEntityAddFormState, TagEntityAddFormErrors, TagEntityAddValues to CrossSource.types.ts.
0.4.4
- Replace raw shadcn Dialog in CrossSourceAddDialog and ClusterAddDialog with the reusable FormDialog widget; swap
@signalos/dialogdependency for@signalos/form-dialog.
0.4.3
- Fix file casing: rename Relationships.types.ts to match PascalCase import paths.
0.4.2
- Lift ClusterAddDialog form state (form, errors) to controlled props; remove forbidden @/schemas and @/types imports, replace with inline validation.
- Lift CrossSourceAddDialog form state (form, errors) to controlled props; remove forbidden @/schemas and @/types imports, replace with inline validation.
- CrossSourceReview now receives addRelForm/addRelFormErrors/onAddRelFormChange/onAddRelFormErrorsChange and addClusterForm/addClusterFormErrors/onAddClusterFormChange/onAddClusterFormErrorsChange.
0.3.2
- Content updates.
0.3.1
- Replace
grid grid-cols-1 lg:grid-cols-12layout in both the tags and relationships tabs withSplitPanelwidget. - Add
@signalos/split-paneltoregistryDependencies. - Remove unused
ArrowRightLefticon import.
0.2.0
- Refactor: lift all business logic, form state, and data transformations into
CrossSourceReview(parent). RelationshipDetailPanelis now fully presentational —EditRelationshipFormno longer owns state; all form values, derived column/table lists, validation flags, and handlers are passed as props from the parent.isEditingtoggle state moved to parent; panel receives it as a prop.ClustersPanel: replaced three-setter "Edit" button logic with a singleonOpenEditCluster(idx)callback; removed unusedisNavigatingprop.RelQueueList: lifted inline change-summary IIFE to parent aschangeSummaries(pre-computedRecord<number, string[]>); component now renders only.CrossSourceTabSwitcher: removed unusedactiveTab,hasTags,hasRelationshipsprops.CrossSource.types.ts: addedEditRelForm,ClusterEditValue,ClusterEditState,FlatTagEntry,ClustersWithConnectorexports; fixed casing import fromRelationships.types.