Merge pull request #53 from valueonag/feat/demo-system-readieness

udb fix
This commit is contained in:
Patrick Motsch 2026-04-21 08:58:15 +02:00 committed by GitHub
commit 9093827e7c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 41 additions and 99 deletions

View file

@ -606,17 +606,6 @@ const SourcesTab: React.FC<SourcesTabProps> = ({ context, onSourcesChanged, onSe
}
}, [instanceId, _fetchDataSources, onSourcesChanged]);
/* ── Remove DataSource ── */
const _removeDatasource = useCallback(async (dsId: string) => {
try {
await api.delete(`/api/workspace/${instanceId}/datasources/${dsId}`);
_fetchDataSources();
onSourcesChanged?.();
} catch (err) {
console.error('Failed to remove data source:', err);
}
}, [instanceId, _fetchDataSources, onSourcesChanged]);
/* ── Check if a path is already added ── */
const _isAdded = useCallback((connectionId: string, service: string | undefined, path: string | undefined): boolean => {
const expectedSourceType = service ? (_SERVICE_TO_SOURCE_TYPE[service] || service) : undefined;
@ -841,17 +830,6 @@ const SourcesTab: React.FC<SourcesTabProps> = ({ context, onSourcesChanged, onSe
}
}, [instanceId, _fetchFeatureDataSources, onSourcesChanged]);
/* ── Feature: Remove FeatureDataSource ── */
const _removeFeatureDataSource = useCallback(async (fdsId: string) => {
try {
await api.delete(`/api/workspace/${instanceId}/feature-datasources/${fdsId}`);
_fetchFeatureDataSources();
onSourcesChanged?.();
} catch (err) {
console.error('Failed to remove feature data source:', err);
}
}, [instanceId, _fetchFeatureDataSources, onSourcesChanged]);
/* ── Feature: check if table already added (no record filter) ── */
const _isFeatureTableAdded = useCallback((featureInstanceId: string, tableName: string): boolean => {
return featureDataSources.some(fds =>
@ -1021,7 +999,6 @@ const SourcesTab: React.FC<SourcesTabProps> = ({ context, onSourcesChanged, onSe
dataSources={dataSources}
onCycleScope={_cyclePersonalScope}
onToggleNeutralize={_togglePersonalNeutralize}
onRemoveDs={_removeDatasource}
onSendToChat={_sendNodeToChat}
scopeCycleTitle={_scopeCycleTitle}
selectedKeys={selectedKeys}
@ -1081,7 +1058,6 @@ const SourcesTab: React.FC<SourcesTabProps> = ({ context, onSourcesChanged, onSe
onCycleScope={_cycleFeatureScope}
onToggleNeutralize={_toggleFeatureNeutralize}
onToggleNeutralizeField={_toggleNeutralizeField}
onRemoveFds={_removeFeatureDataSource}
featureTree={featureTree}
/>
))}
@ -1110,7 +1086,6 @@ interface _TreeNodeViewProps {
dataSources: UdbDataSource[];
onCycleScope: (ds: UdbDataSource) => void;
onToggleNeutralize: (ds: UdbDataSource) => void;
onRemoveDs: (dsId: string) => void;
onSendToChat?: (params: { connectionId: string; sourceType: string; path: string; label: string; displayPath?: string }) => void;
scopeCycleTitle: (scope: string) => string;
selectedKeys: Set<string>;
@ -1121,7 +1096,7 @@ interface _TreeNodeViewProps {
const _TreeNodeView: React.FC<_TreeNodeViewProps> = ({
node, depth, onToggle, onEnsureDs, isAdded, addingPath,
dataSources, onCycleScope, onToggleNeutralize, onRemoveDs, onSendToChat, scopeCycleTitle,
dataSources, onCycleScope, onToggleNeutralize, onSendToChat, scopeCycleTitle,
selectedKeys, onSelect, inheritedScope, inheritedNeutralize,
}) => {
const { t } = useLanguage();
@ -1216,19 +1191,11 @@ const _TreeNodeView: React.FC<_TreeNodeViewProps> = ({
{node.label}
</span>
{/* Dynamic action: Remove (only when DS exists) placed LEFT of the
* stable trio so the trio always anchors at the right edge. */}
{ds && (
<button
onClick={e => { e.stopPropagation(); onRemoveDs(ds.id); }}
style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 10, color: '#999', padding: '0 2px', flexShrink: 0 }}
title={t('Entfernen')}
>
{'\u2715'}
</button>
)}
{/* ── Stable trio: chat | scope | neutralize (always in this order) ── */}
{/* Stable trio: chat | scope | neutralize (always in this order).
* No "remove from workspace" button here by design: the UDB row only
* exposes the catalog state. Detach from the *current chat* happens
* via the chip "x" in WorkspaceInput; that chip is the single source
* of truth for chat-scoped attachment lifecycle. */}
<button
onClick={e => { e.stopPropagation(); onSendToChat?.(_chatPayload); }}
style={{
@ -1290,7 +1257,6 @@ const _TreeNodeView: React.FC<_TreeNodeViewProps> = ({
dataSources={dataSources}
onCycleScope={onCycleScope}
onToggleNeutralize={onToggleNeutralize}
onRemoveDs={onRemoveDs}
onSendToChat={onSendToChat}
scopeCycleTitle={scopeCycleTitle}
selectedKeys={selectedKeys}
@ -1343,7 +1309,6 @@ interface _FeatureActionContext {
onCycleScope: (fds: UdbFeatureDataSource) => void;
onToggleNeutralize: (fds: UdbFeatureDataSource) => void;
onToggleNeutralizeField: (fds: UdbFeatureDataSource, fieldName: string) => void;
onRemoveFds: (fdsId: string) => void;
featureTree: MandateGroupNode[];
}
@ -1463,16 +1428,6 @@ const _FeatureNodeView: React.FC<_FeatureNodeViewProps> = (props) => {
{node.tableCount} {t('Tabellen')}
</span>
{wildcardFds && (
<button
onClick={e => { e.stopPropagation(); ctx.onRemoveFds(wildcardFds.id); }}
style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 10, color: '#999', padding: '0 2px', flexShrink: 0 }}
title={t('Entfernen')}
>
{'\u2715'}
</button>
)}
<button
onClick={(e) => {
e.stopPropagation();
@ -1608,7 +1563,6 @@ const _FeatureItemView: React.FC<_FeatureItemViewProps> = (props) => {
onCycleScope={ctx.onCycleScope}
onToggleNeutralize={ctx.onToggleNeutralize}
onToggleNeutralizeField={ctx.onToggleNeutralizeField}
onRemoveFds={ctx.onRemoveFds}
featureTree={ctx.featureTree}
inheritedScope={inheritedScope}
inheritedNeutralize={inheritedNeutralize}
@ -1884,16 +1838,6 @@ const _RecordRowView: React.FC<_RecordRowViewProps> = (props) => {
</button>
)}
{fds && (
<button
onClick={(e) => { e.stopPropagation(); ctx.onRemoveFds(fds.id); }}
style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 10, color: '#999', padding: '0 2px', flexShrink: 0 }}
title={t('Entfernen')}
>
{'\u2715'}
</button>
)}
<button
onClick={(e) => { e.stopPropagation(); ctx.onSendToChat?.(_chatPayload); }}
style={{
@ -1975,7 +1919,6 @@ interface _FeatureTableRowProps {
onCycleScope: (fds: UdbFeatureDataSource) => void;
onToggleNeutralize: (fds: UdbFeatureDataSource) => void;
onToggleNeutralizeField: (fds: UdbFeatureDataSource, fieldName: string) => void;
onRemoveFds: (fdsId: string) => void;
featureTree: MandateGroupNode[];
inheritedScope?: string;
inheritedNeutralize?: boolean;
@ -1984,7 +1927,7 @@ interface _FeatureTableRowProps {
const _FeatureTableRow: React.FC<_FeatureTableRowProps> = ({
featureNode, table, depth, onAddFeatureTable, onSendToChat,
featureDataSources, onCycleScope, onToggleNeutralize, onToggleNeutralizeField,
onRemoveFds, featureTree, inheritedScope, inheritedNeutralize,
featureTree, inheritedScope, inheritedNeutralize,
}) => {
const { t } = useLanguage();
const [hovered, setHovered] = useState(false);
@ -2052,16 +1995,6 @@ const _FeatureTableRow: React.FC<_FeatureTableRowProps> = ({
)}
</span>
{fds && (
<button
onClick={e => { e.stopPropagation(); onRemoveFds(fds.id); }}
style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 10, color: '#999', padding: '0 2px', flexShrink: 0 }}
title={t('Entfernen')}
>
{'\u2715'}
</button>
)}
<button
onClick={e => { e.stopPropagation(); onSendToChat?.(_chatPayload); }}
style={{

View file

@ -118,23 +118,43 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({ instanceId,
}
}, [draftAppend, onDraftAppendConsumed]);
// Persist a changed attachment list to the backend so the next chat
// reload reflects the current state. Defined early so the
// pendingAttachDsId / pendingAttachFdsId effects below can also persist
// immediately after a 💬-click or drag-drop attach.
const _persistAttachments = useCallback((dsIds: string[], fdsIds: string[]) => {
if (!instanceId || !workflowId) return;
api.patch(`/api/workspace/${instanceId}/workflows/${workflowId}/attachments`, {
dataSourceIds: dsIds,
featureDataSourceIds: fdsIds,
}).catch(err => console.warn('Failed to persist chat attachments:', err));
}, [instanceId, workflowId]);
// 💬-click or drag-drop attach: parent sets pendingAttachDsId after
// creating/finding the DataSource. Add to the chip bar AND persist
// immediately so a chat reload before the user sends a message still
// shows the chip.
useEffect(() => {
if (pendingAttachDsId) {
setAttachedDataSourceIds(prev =>
prev.includes(pendingAttachDsId) ? prev : [...prev, pendingAttachDsId],
);
onPendingAttachDsConsumed?.();
}
}, [pendingAttachDsId, onPendingAttachDsConsumed]);
if (!pendingAttachDsId) return;
setAttachedDataSourceIds(prev => {
if (prev.includes(pendingAttachDsId)) return prev;
const next = [...prev, pendingAttachDsId];
_persistAttachments(next, attachedFeatureDataSourceIds);
return next;
});
onPendingAttachDsConsumed?.();
}, [pendingAttachDsId, onPendingAttachDsConsumed, _persistAttachments, attachedFeatureDataSourceIds]);
useEffect(() => {
if (pendingAttachFdsId) {
setAttachedFeatureDataSourceIds(prev =>
prev.includes(pendingAttachFdsId) ? prev : [...prev, pendingAttachFdsId],
);
onPendingAttachFdsConsumed?.();
}
}, [pendingAttachFdsId, onPendingAttachFdsConsumed]);
if (!pendingAttachFdsId) return;
setAttachedFeatureDataSourceIds(prev => {
if (prev.includes(pendingAttachFdsId)) return prev;
const next = [...prev, pendingAttachFdsId];
_persistAttachments(attachedDataSourceIds, next);
return next;
});
onPendingAttachFdsConsumed?.();
}, [pendingAttachFdsId, onPendingAttachFdsConsumed, _persistAttachments, attachedDataSourceIds]);
// Rehydrate the chip-bar whenever the parent re-loads a chat (loadedNonce
// bumps on every loadWorkflow call). We trust the loaded IDs initially;
@ -184,17 +204,6 @@ export const WorkspaceInput: React.FC<WorkspaceInputProps> = ({ instanceId,
});
}, [loadedNonce, featureDataSources]);
// Persist a changed attachment list to the backend so the next chat
// reload reflects the current state. We debounce slightly by sending on
// the next animation frame to coalesce rapid clicks.
const _persistAttachments = useCallback((dsIds: string[], fdsIds: string[]) => {
if (!instanceId || !workflowId) return;
api.patch(`/api/workspace/${instanceId}/workflows/${workflowId}/attachments`, {
dataSourceIds: dsIds,
featureDataSourceIds: fdsIds,
}).catch(err => console.warn('Failed to persist chat attachments:', err));
}, [instanceId, workflowId]);
const promptBeforeVoiceRef = useRef('');
const finalizedTextRef = useRef('');
const currentInterimRef = useRef('');