fix: UDB compact layout, mobile table view, DataSource ID attach
All checks were successful
Deploy Nyla Frontend to Production / deploy (push) Successful in 48s

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ValueOn AG 2026-06-01 00:00:38 +02:00
parent 7da7ad5041
commit 5711450606
6 changed files with 101 additions and 42 deletions

View file

@ -300,40 +300,45 @@
/* Responsive Design */ /* Responsive Design */
@media (max-width: 768px) { @media (max-width: 768px) {
.deleteControlsIntegrated { .deleteControlsIntegrated {
flex-direction: column; flex-direction: row;
align-items: stretch; flex-wrap: wrap;
gap: 10px; align-items: center;
gap: 6px;
width: 100%; width: 100%;
} }
.controls { .controls {
flex-direction: column; flex-direction: row;
align-items: stretch; flex-wrap: wrap;
gap: 15px; align-items: center;
padding: 10px; gap: 6px;
padding: 6px 8px;
} }
.filtersContainer { .filtersContainer {
flex-direction: column; flex-direction: row;
align-items: stretch; flex-wrap: wrap;
gap: 10px; align-items: center;
gap: 6px;
} }
.filterGroup { .filterGroup {
flex-direction: column; flex-direction: row;
align-items: flex-start; align-items: center;
gap: 4px; gap: 4px;
} }
.filterInput, .filterInput,
.filterSelect { .filterSelect {
width: 100%; width: auto;
min-width: auto; min-width: 100px;
max-width: 160px;
font-size: 12px;
} }
.floatingLabelInput { .floatingLabelInput {
max-width: none; max-width: 160px;
width: 100%; width: auto;
} }
.filterGroup .floatingLabelInput { .filterGroup .floatingLabelInput {

View file

@ -962,10 +962,24 @@ tbody .actionsColumn {
/* Responsive */ /* Responsive */
@media (max-width: 768px) { @media (max-width: 768px) {
.formGeneratorTable {
gap: 4px;
}
.title {
font-size: 1.1rem;
margin-bottom: 4px;
}
.tableWrapper {
border-radius: 4px;
}
.tableContainer { .tableContainer {
flex: 1; flex: 1;
min-height: 0; min-height: 0;
max-height: 100%; max-height: 100%;
-webkit-overflow-scrolling: touch;
} }
.emptyTable { .emptyTable {
@ -974,31 +988,34 @@ tbody .actionsColumn {
} }
.th { .th {
padding: 6px 8px; padding: 4px 6px;
font-size: 10px; font-size: 10px;
letter-spacing: 0;
} }
.td { .td {
padding: 6px 8px; padding: 4px 6px;
font-size: 12px; font-size: 12px;
} }
.actionButtons { .actionButtons {
flex-direction: column; flex-direction: row;
gap: 2px; gap: 2px;
} }
.actionButton { .actionButton {
padding: 4px; padding: 3px;
font-size: 10px; font-size: 10px;
min-width: 22px; min-width: 20px;
min-height: 22px; min-height: 20px;
} }
.pagination { .pagination {
flex-direction: column; flex-direction: row;
gap: 10px; flex-wrap: wrap;
padding: 10px; justify-content: center;
gap: 6px;
padding: 6px 8px;
} }
.pageSizeSelector { .pageSizeSelector {
@ -1009,7 +1026,7 @@ tbody .actionsColumn {
.paginationInfo { .paginationInfo {
text-align: center; text-align: center;
margin: 0; margin: 0;
font-size: 12px; font-size: 11px;
} }
.pageNumbers { .pageNumbers {
@ -1018,8 +1035,8 @@ tbody .actionsColumn {
} }
.pageNumber { .pageNumber {
min-width: 24px; min-width: 22px;
height: 24px; height: 22px;
font-size: 11px; font-size: 11px;
} }
} }

View file

@ -252,7 +252,7 @@
} }
.nodeRowCompact { .nodeRowCompact {
height: 32px; height: 28px;
} }
.nodeRow:hover { .nodeRow:hover {
@ -561,7 +561,7 @@
/* Compact mode */ /* Compact mode */
.compactMode .sectionHeader { .compactMode .sectionHeader {
padding: 6px 8px; padding: 4px 6px;
} }
.compactMode .sectionTitle { .compactMode .sectionTitle {
@ -574,13 +574,38 @@
} }
.compactMode .nodeRow { .compactMode .nodeRow {
padding: 0 6px; padding: 0 4px;
gap: 3px;
} }
.compactMode .nodeName { .compactMode .nodeName {
font-size: 12px; font-size: 12px;
} }
.compactMode .expandChevron,
.compactMode .expandChevronPlaceholder {
width: 14px;
height: 14px;
font-size: 9px;
}
.compactMode .nodeIcon {
width: 14px;
font-size: 13px;
}
.compactMode .emojiBtn {
width: 18px;
font-size: 11px;
padding: 0 1px;
}
.compactMode .nodeActionBtn {
width: 18px;
height: 18px;
font-size: 12px;
}
/* Dark theme */ /* Dark theme */
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
.sectionHeader { .sectionHeader {

View file

@ -19,7 +19,7 @@ import { useConfirm } from '../../../hooks/useConfirm';
import { useLanguage } from '../../../providers/language/LanguageContext'; import { useLanguage } from '../../../providers/language/LanguageContext';
import styles from './FormGeneratorTree.module.css'; import styles from './FormGeneratorTree.module.css';
const INDENT_PX = 24; const INDENT_PX = 16;
const DRAG_MIME = 'application/x-poweron-tree-items'; const DRAG_MIME = 'application/x-poweron-tree-items';
const SCOPE_ORDER: ScopeValue[] = ['personal', 'featureInstance', 'mandate', 'global']; const SCOPE_ORDER: ScopeValue[] = ['personal', 'featureInstance', 'mandate', 'global'];

View file

@ -48,7 +48,14 @@ const SourcesTab: React.FC<SourcesTabProps> = ({
setSettingsModal({ dataSourceId, title: label }); setSettingsModal({ dataSourceId, title: label });
}, []); }, []);
const _handleSendToChat = useCallback((node: TreeNode<UdbBackendNode>) => { const provider = useMemo(
() => instanceId
? createUdbSourcesProvider(instanceId, _handleOpenSettings)
: null,
[instanceId, _handleOpenSettings],
);
const _handleSendToChat = useCallback(async (node: TreeNode<UdbBackendNode>) => {
const data = node.data; const data = node.data;
if (!data) return; if (!data) return;
@ -65,16 +72,15 @@ const SourcesTab: React.FC<SourcesTabProps> = ({
}); });
} else if (data.kind === 'connection' || data.kind === 'service' } else if (data.kind === 'connection' || data.kind === 'service'
|| data.kind === 'folder' || data.kind === 'file') { || data.kind === 'folder' || data.kind === 'file') {
onAttachDataSource?.(data.connectionId || data.key); if (!provider) return;
const dsId = await provider.ensureDataSourceId(data);
if (dsId) {
onAttachDataSource?.(dsId);
} else {
console.warn('[SourcesTab] Could not resolve DataSource ID for', data.key);
}
} }
}, [onSendToChat_FeatureSource, onAttachDataSource]); }, [onSendToChat_FeatureSource, onAttachDataSource, provider]);
const provider = useMemo(
() => instanceId
? createUdbSourcesProvider(instanceId, _handleOpenSettings)
: null,
[instanceId, _handleOpenSettings],
);
if (!instanceId || !provider) { if (!instanceId || !provider) {
return ( return (

View file

@ -212,6 +212,8 @@ function _mapBackendNode(
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
export interface UdbSourcesProviderHandle extends TreeNodeProvider<UdbBackendNode> { export interface UdbSourcesProviderHandle extends TreeNodeProvider<UdbBackendNode> {
/** Resolve the DataSource UUID for a node, creating a record if needed. */
ensureDataSourceId(node: UdbBackendNode): Promise<string | null>;
/** Test/diagnostic hook only -- exposes the latest cached backend payloads /** Test/diagnostic hook only -- exposes the latest cached backend payloads
* so consumers can inspect data flow without round-tripping through the * so consumers can inspect data flow without round-tripping through the
* network. Not part of the contract used at runtime. */ * network. Not part of the contract used at runtime. */
@ -375,6 +377,10 @@ export function createUdbSourcesProvider(
} }
}, },
async ensureDataSourceId(node: UdbBackendNode): Promise<string | null> {
return _ensureRecordForSettings(node);
},
_diagnosticGetCacheSize() { _diagnosticGetCacheSize() {
return nodeCache.size; return nodeCache.size;
}, },