teamsbot auth fixes
This commit is contained in:
parent
2ee08c314b
commit
0d8e6501d3
4 changed files with 95 additions and 57 deletions
|
|
@ -58,6 +58,13 @@ function _buildChildMap<T>(nodes: TreeNode<T>[]): Map<string | '__root__', TreeN
|
|||
if (list) list.push(n);
|
||||
else map.set(key, [n]);
|
||||
}
|
||||
for (const [, children] of map) {
|
||||
children.sort((a, b) => {
|
||||
if (a.type === 'folder' && b.type !== 'folder') return -1;
|
||||
if (a.type !== 'folder' && b.type === 'folder') return 1;
|
||||
return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });
|
||||
});
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
|
@ -651,10 +658,14 @@ export function FormGeneratorTree<T = any>({
|
|||
const _handleCycleScope = useCallback(
|
||||
async (node: TreeNode<T>) => {
|
||||
const newScope = _nextScope(node.scope);
|
||||
await provider.patchScope?.([node.id], newScope);
|
||||
setNodes((prev) =>
|
||||
prev.map((n) => (n.id === node.id ? { ...n, scope: newScope } : n)),
|
||||
);
|
||||
const isFolder = node.type === 'folder';
|
||||
await provider.patchScope?.([node.id], newScope, isFolder);
|
||||
setNodes((prev) => {
|
||||
if (!isFolder) return prev.map((n) => (n.id === node.id ? { ...n, scope: newScope } : n));
|
||||
const descendantIds = new Set(_collectDescendantIds(node.id, prev));
|
||||
descendantIds.add(node.id);
|
||||
return prev.map((n) => descendantIds.has(n.id) ? { ...n, scope: newScope } : n);
|
||||
});
|
||||
},
|
||||
[provider],
|
||||
);
|
||||
|
|
@ -663,9 +674,12 @@ export function FormGeneratorTree<T = any>({
|
|||
async (node: TreeNode<T>) => {
|
||||
const newValue = !node.neutralize;
|
||||
await provider.patchNeutralize?.([node.id], newValue);
|
||||
setNodes((prev) =>
|
||||
prev.map((n) => (n.id === node.id ? { ...n, neutralize: newValue } : n)),
|
||||
);
|
||||
setNodes((prev) => {
|
||||
if (node.type !== 'folder') return prev.map((n) => (n.id === node.id ? { ...n, neutralize: newValue } : n));
|
||||
const descendantIds = new Set(_collectDescendantIds(node.id, prev));
|
||||
descendantIds.add(node.id);
|
||||
return prev.map((n) => descendantIds.has(n.id) ? { ...n, neutralize: newValue } : n);
|
||||
});
|
||||
},
|
||||
[provider],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -343,7 +343,7 @@ async function _loadServices(instanceId: string, connectionId: string): Promise<
|
|||
service: s.service,
|
||||
path: '/',
|
||||
displayPath: s.label || s.service,
|
||||
}));
|
||||
})).sort((a: TreeNode, b: TreeNode) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }));
|
||||
}
|
||||
|
||||
async function _browseService(
|
||||
|
|
@ -375,6 +375,10 @@ async function _browseService(
|
|||
path: entry.path,
|
||||
displayPath,
|
||||
};
|
||||
}).sort((a: TreeNode, b: TreeNode) => {
|
||||
if (a.type === 'folder' && b.type !== 'folder') return -1;
|
||||
if (a.type !== 'folder' && b.type === 'folder') return 1;
|
||||
return a.label.localeCompare(b.label, undefined, { sensitivity: 'base' });
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -496,6 +500,7 @@ const SourcesTab: React.FC<SourcesTabProps> = ({ context, onSourcesChanged, onSe
|
|||
scope: d.scope || 'personal',
|
||||
neutralize: d.neutralize ?? false,
|
||||
}));
|
||||
list.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }));
|
||||
setDataSources(list);
|
||||
})
|
||||
.catch(() => { if (mountedRef.current) setDataSources([]); });
|
||||
|
|
@ -519,6 +524,7 @@ const SourcesTab: React.FC<SourcesTabProps> = ({ context, onSourcesChanged, onSe
|
|||
neutralizeFields: d.neutralizeFields || undefined,
|
||||
recordFilter: d.recordFilter || undefined,
|
||||
}));
|
||||
list.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }));
|
||||
setFeatureDataSources(list);
|
||||
})
|
||||
.catch(() => { if (mountedRef.current) setFeatureDataSources([]); });
|
||||
|
|
@ -547,7 +553,8 @@ const SourcesTab: React.FC<SourcesTabProps> = ({ context, onSourcesChanged, onSe
|
|||
children: null,
|
||||
connectionId: c.id,
|
||||
authority: c.authority,
|
||||
}));
|
||||
}))
|
||||
.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }));
|
||||
setTree(nodes);
|
||||
})
|
||||
.catch(() => { if (mountedRef.current) setTree([]); })
|
||||
|
|
@ -760,7 +767,7 @@ const SourcesTab: React.FC<SourcesTabProps> = ({ context, onSourcesChanged, onSe
|
|||
expanded: false,
|
||||
loading: false,
|
||||
tables: null,
|
||||
})),
|
||||
})).sort((a: FeatureConnectionNode, b: FeatureConnectionNode) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' })),
|
||||
})));
|
||||
})
|
||||
.catch(() => { if (mountedRef.current) setFeatureTree([]); })
|
||||
|
|
@ -798,14 +805,14 @@ const SourcesTab: React.FC<SourcesTabProps> = ({ context, onSourcesChanged, onSe
|
|||
objectKey: t.objectKey ?? '',
|
||||
tableName: t.tableName ?? '',
|
||||
label: t.label ?? '',
|
||||
fields: t.fields ?? [],
|
||||
fields: (t.fields ?? []).slice().sort((a: string, b: string) => a.localeCompare(b, undefined, { sensitivity: 'base' })),
|
||||
isParent: Boolean(t.isParent),
|
||||
parentTable: t.parentTable ?? null,
|
||||
parentKey: t.parentKey ?? null,
|
||||
displayFields: t.displayFields ?? [],
|
||||
isGroup: Boolean(t.isGroup),
|
||||
group: t.group ?? null,
|
||||
}));
|
||||
})).sort((a: FeatureTableNode, b: FeatureTableNode) => (a.label || a.tableName).localeCompare(b.label || b.tableName, undefined, { sensitivity: 'base' }));
|
||||
|
||||
// Default-expand all categorical groups so users immediately see their content.
|
||||
const defaultExpansions: string[] = tables
|
||||
|
|
@ -912,7 +919,7 @@ const SourcesTab: React.FC<SourcesTabProps> = ({ context, onSourcesChanged, onSe
|
|||
displayLabel: r.displayLabel || r.id,
|
||||
fields: r.fields || {},
|
||||
tableName: table.tableName,
|
||||
}));
|
||||
})).sort((a: ParentRecordNode, b: ParentRecordNode) => a.displayLabel.localeCompare(b.displayLabel, undefined, { sensitivity: 'base' }));
|
||||
if (mountedRef.current) {
|
||||
setFeatureRecordsByPath(prev => ({ ...prev, [pathKey]: records }));
|
||||
}
|
||||
|
|
@ -1229,11 +1236,24 @@ const _TreeNodeView: React.FC<_TreeNodeViewProps> = ({
|
|||
{node.label}
|
||||
</span>
|
||||
|
||||
{/* ── 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={async (e) => {
|
||||
e.stopPropagation();
|
||||
if (ds) { onToggleRagIndex(ds); return; }
|
||||
const newId = await onEnsureDs(node);
|
||||
if (newId) {
|
||||
try { await api.patch(`/api/datasources/${newId}/rag-index`, { ragIndexEnabled: !effectiveRagIndex }); } catch {}
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
background: 'none', border: 'none', cursor: 'pointer',
|
||||
fontSize: 12, padding: '0 2px', lineHeight: 1, flexShrink: 0, width: 22, textAlign: 'center',
|
||||
opacity: (ds?.ragIndexEnabled ?? effectiveRagIndex) ? 1 : 0.35,
|
||||
}}
|
||||
title={(ds?.ragIndexEnabled ?? effectiveRagIndex) ? t('RAG-Indexierung an') : t('RAG-Indexierung aus')}
|
||||
>
|
||||
{'\uD83E\uDDE0'}
|
||||
</button>
|
||||
<button
|
||||
onClick={e => { e.stopPropagation(); onSendToChat?.(_chatPayload); }}
|
||||
style={{
|
||||
|
|
@ -1278,24 +1298,6 @@ const _TreeNodeView: React.FC<_TreeNodeViewProps> = ({
|
|||
>
|
||||
{'\uD83D\uDD12'}
|
||||
</button>
|
||||
<button
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
if (ds) { onToggleRagIndex(ds); return; }
|
||||
const newId = await onEnsureDs(node);
|
||||
if (newId) {
|
||||
try { await api.patch(`/api/datasources/${newId}/rag-index`, { ragIndexEnabled: !effectiveRagIndex }); } catch {}
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
background: 'none', border: 'none', cursor: 'pointer',
|
||||
fontSize: 12, padding: '0 2px', lineHeight: 1, flexShrink: 0, width: 22, textAlign: 'center',
|
||||
opacity: (ds?.ragIndexEnabled ?? effectiveRagIndex) ? 1 : 0.35,
|
||||
}}
|
||||
title={(ds?.ragIndexEnabled ?? effectiveRagIndex) ? t('RAG-Indexierung an') : t('RAG-Indexierung aus')}
|
||||
>
|
||||
{'\uD83E\uDDE0'}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -415,6 +415,17 @@
|
|||
padding: 1rem;
|
||||
}
|
||||
|
||||
.sessionSwitcherSelect {
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 0.85rem;
|
||||
border: 1px solid var(--border-color, #ccc);
|
||||
border-radius: 8px;
|
||||
background: var(--bg-primary, #fff);
|
||||
color: var(--text-primary, #333);
|
||||
}
|
||||
|
||||
/* ----- Session Layout (UDB Sidebar + Main) ------------------------------- */
|
||||
|
||||
.sessionLayout {
|
||||
|
|
|
|||
|
|
@ -771,28 +771,39 @@ export const TeamsbotSessionView: React.FC = () => {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Session Switcher (if multiple sessions exist) */}
|
||||
{/* Session Switcher */}
|
||||
{allSessions.length > 1 && (
|
||||
<div style={{ display: 'flex', gap: '8px', marginBottom: '12px', flexWrap: 'wrap' }}>
|
||||
{allSessions.map((s) => (
|
||||
<button
|
||||
key={s.id}
|
||||
onClick={() => _switchSession(s.id)}
|
||||
style={{
|
||||
padding: '6px 12px',
|
||||
borderRadius: '6px',
|
||||
border: s.id === sessionId ? '2px solid #4A90D9' : '1px solid #ddd',
|
||||
background: s.id === sessionId ? '#EBF3FC' : '#fff',
|
||||
cursor: 'pointer',
|
||||
fontSize: '13px',
|
||||
fontWeight: s.id === sessionId ? 600 : 400,
|
||||
}}
|
||||
>
|
||||
{s.botName}
|
||||
{['active', 'joining', 'pending'].includes(s.status) && ' (aktiv)'}
|
||||
{s.status === 'ended' && ' (beendet)'}
|
||||
</button>
|
||||
))}
|
||||
<div style={{ marginBottom: '12px' }}>
|
||||
<select
|
||||
value={sessionId}
|
||||
onChange={(e) => _switchSession(e.target.value)}
|
||||
className={styles.sessionSwitcherSelect}
|
||||
>
|
||||
{[...allSessions]
|
||||
.sort((a, b) => {
|
||||
const ta = a.startedAt ? new Date(a.startedAt).getTime() : 0;
|
||||
const tb = b.startedAt ? new Date(b.startedAt).getTime() : 0;
|
||||
return tb - ta;
|
||||
})
|
||||
.map((s) => {
|
||||
const dt = s.startedAt ? new Date(s.startedAt) : null;
|
||||
const ts = dt && !Number.isNaN(dt.getTime())
|
||||
? `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, '0')}-${String(dt.getDate()).padStart(2, '0')} ${String(dt.getHours()).padStart(2, '0')}:${String(dt.getMinutes()).padStart(2, '0')}`
|
||||
: '';
|
||||
const statusTag = ['active', 'joining', 'pending'].includes(s.status)
|
||||
? ` (${t('aktiv')})`
|
||||
: s.status === 'ended'
|
||||
? ` (${t('beendet')})`
|
||||
: s.status === 'error'
|
||||
? ` (${t('Fehler')})`
|
||||
: '';
|
||||
return (
|
||||
<option key={s.id} value={s.id}>
|
||||
{ts ? `${ts} — ` : ''}{s.botName}{statusTag}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue