mirror of
https://github.com/chartdb/chartdb.git
synced 2026-02-09 21:19:45 -06:00
fix(filter): show visible/total count badge for schema and area groups (#1045)
This commit is contained in:
@@ -354,13 +354,27 @@ function TreeNode<Type extends string, Context extends Record<Type, unknown>>({
|
||||
<span
|
||||
{...node.labelProps}
|
||||
className={cn(
|
||||
'text-xs truncate min-w-0 flex-1 w-0',
|
||||
'text-xs truncate min-w-0 flex-1 w-0 flex items-center gap-1.5',
|
||||
isSelected && 'font-medium text-primary text-white',
|
||||
node.labelProps?.className
|
||||
)}
|
||||
{...(isSelected ? { 'data-selected': true } : {})}
|
||||
>
|
||||
{node.empty ? '' : node.name}
|
||||
<span className="truncate">
|
||||
{node.empty ? '' : node.name}
|
||||
</span>
|
||||
{node.suffix && (
|
||||
<span
|
||||
className={cn(
|
||||
'flex-shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium leading-none',
|
||||
isSelected
|
||||
? 'bg-sky-400/50 text-white'
|
||||
: 'bg-gray-200/50 text-muted-foreground dark:bg-gray-700/50'
|
||||
)}
|
||||
>
|
||||
{node.suffix}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
{renderActionsComponent && renderActionsComponent(node)}
|
||||
{isHovered && renderHoverComponent
|
||||
|
||||
@@ -12,6 +12,7 @@ export interface TreeNode<
|
||||
icon?: LucideIcon;
|
||||
iconProps?: React.ComponentProps<LucideIcon>;
|
||||
labelProps?: React.ComponentProps<'span'>;
|
||||
suffix?: string;
|
||||
type: Type;
|
||||
unselectable?: boolean;
|
||||
tooltip?: string;
|
||||
|
||||
@@ -13,6 +13,43 @@ import { Box, Database, Layers, Table } from 'lucide-react';
|
||||
import { filterTable } from '@/lib/domain/diagram-filter/filter';
|
||||
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||
|
||||
type TableWithVisibility = RelevantTableData & { visible: boolean };
|
||||
|
||||
const computeTableVisibility = (
|
||||
tables: RelevantTableData[],
|
||||
filter: DiagramFilter | undefined,
|
||||
databaseType: DatabaseType
|
||||
): TableWithVisibility[] =>
|
||||
tables.map((table) => ({
|
||||
...table,
|
||||
visible: filterTable({
|
||||
table: {
|
||||
id: table.id,
|
||||
schema: table.schema,
|
||||
},
|
||||
filter,
|
||||
options: {
|
||||
defaultSchema: defaultSchemas[databaseType],
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
const createTableChildren = (
|
||||
tablesWithVisibility: TableWithVisibility[]
|
||||
): TreeNode<NodeType, NodeContext>[] =>
|
||||
tablesWithVisibility.map((table) => ({
|
||||
id: table.id,
|
||||
name: table.name,
|
||||
type: 'table' as const,
|
||||
isFolder: false,
|
||||
icon: Table,
|
||||
context: {
|
||||
tableSchema: table.schema,
|
||||
visible: table.visible,
|
||||
} satisfies TableContext,
|
||||
className: !table.visible ? 'opacity-50' : '',
|
||||
}));
|
||||
|
||||
export const generateTreeDataByAreas = ({
|
||||
areas,
|
||||
databaseType,
|
||||
@@ -50,27 +87,23 @@ export const generateTreeDataByAreas = ({
|
||||
// Create nodes for areas
|
||||
areas.forEach((area) => {
|
||||
const areaTables = tablesByArea.get(area.id) || [];
|
||||
if (areaTables.length === 0) return;
|
||||
|
||||
// Check if at least one table in the area is visible
|
||||
const areaVisible =
|
||||
// areaTables.length === 0 ||
|
||||
!areaTables.some(
|
||||
(table) =>
|
||||
filterTable({
|
||||
table: {
|
||||
id: table.id,
|
||||
schema: table.schema,
|
||||
},
|
||||
filter,
|
||||
options: {
|
||||
defaultSchema: defaultSchemas[databaseType],
|
||||
},
|
||||
}) === false
|
||||
);
|
||||
// Pre-compute visibility for all tables in this area (single pass)
|
||||
const tablesWithVisibility = computeTableVisibility(
|
||||
areaTables,
|
||||
filter,
|
||||
databaseType
|
||||
);
|
||||
const visibleCount = tablesWithVisibility.filter(
|
||||
(t) => t.visible
|
||||
).length;
|
||||
const areaVisible = visibleCount === areaTables.length;
|
||||
|
||||
const areaNode: TreeNode<NodeType, NodeContext> = {
|
||||
id: `area-${area.id}`,
|
||||
name: `${area.name} (${areaTables.length})`,
|
||||
name: area.name,
|
||||
suffix: `${visibleCount}/${areaTables.length}`,
|
||||
type: 'area',
|
||||
isFolder: true,
|
||||
icon: Box,
|
||||
@@ -81,59 +114,28 @@ export const generateTreeDataByAreas = ({
|
||||
isUngrouped: false,
|
||||
} satisfies AreaContext,
|
||||
className: !areaVisible ? 'opacity-50' : '',
|
||||
children: areaTables.map(
|
||||
(table): TreeNode<NodeType, NodeContext> => {
|
||||
const tableVisible = filterTable({
|
||||
table: {
|
||||
id: table.id,
|
||||
schema: table.schema,
|
||||
},
|
||||
filter,
|
||||
options: {
|
||||
defaultSchema: defaultSchemas[databaseType],
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
id: table.id,
|
||||
name: table.name,
|
||||
type: 'table',
|
||||
isFolder: false,
|
||||
icon: Table,
|
||||
context: {
|
||||
tableSchema: table.schema,
|
||||
visible: tableVisible,
|
||||
} satisfies TableContext,
|
||||
className: !tableVisible ? 'opacity-50' : '',
|
||||
};
|
||||
}
|
||||
),
|
||||
children: createTableChildren(tablesWithVisibility),
|
||||
};
|
||||
|
||||
if (areaTables.length > 0) {
|
||||
nodes.push(areaNode);
|
||||
}
|
||||
nodes.push(areaNode);
|
||||
});
|
||||
|
||||
// Add ungrouped tables
|
||||
if (tablesWithoutArea.length > 0) {
|
||||
const ungroupedVisible = !tablesWithoutArea.some(
|
||||
(table) =>
|
||||
filterTable({
|
||||
table: {
|
||||
id: table.id,
|
||||
schema: table.schema,
|
||||
},
|
||||
filter,
|
||||
options: {
|
||||
defaultSchema: defaultSchemas[databaseType],
|
||||
},
|
||||
}) == false
|
||||
const tablesWithVisibility = computeTableVisibility(
|
||||
tablesWithoutArea,
|
||||
filter,
|
||||
databaseType
|
||||
);
|
||||
const visibleCount = tablesWithVisibility.filter(
|
||||
(t) => t.visible
|
||||
).length;
|
||||
const ungroupedVisible = visibleCount === tablesWithoutArea.length;
|
||||
|
||||
const ungroupedNode: TreeNode<NodeType, NodeContext> = {
|
||||
id: 'ungrouped',
|
||||
name: `Ungrouped (${tablesWithoutArea.length})`,
|
||||
name: 'Ungrouped',
|
||||
suffix: `${visibleCount}/${tablesWithoutArea.length}`,
|
||||
type: 'area',
|
||||
isFolder: true,
|
||||
icon: Layers,
|
||||
@@ -144,33 +146,7 @@ export const generateTreeDataByAreas = ({
|
||||
isUngrouped: true,
|
||||
} satisfies AreaContext,
|
||||
className: !ungroupedVisible ? 'opacity-50' : '',
|
||||
children: tablesWithoutArea.map(
|
||||
(table): TreeNode<NodeType, NodeContext> => {
|
||||
const tableVisible = filterTable({
|
||||
table: {
|
||||
id: table.id,
|
||||
schema: table.schema,
|
||||
},
|
||||
filter,
|
||||
options: {
|
||||
defaultSchema: defaultSchemas[databaseType],
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
id: table.id,
|
||||
name: table.name,
|
||||
type: 'table',
|
||||
isFolder: false,
|
||||
icon: Table,
|
||||
context: {
|
||||
tableSchema: table.schema,
|
||||
visible: tableVisible,
|
||||
} satisfies TableContext,
|
||||
className: !tableVisible ? 'opacity-50' : '',
|
||||
};
|
||||
}
|
||||
),
|
||||
children: createTableChildren(tablesWithVisibility),
|
||||
};
|
||||
nodes.push(ungroupedNode);
|
||||
}
|
||||
@@ -191,7 +167,7 @@ export const generateTreeDataBySchemas = ({
|
||||
}): TreeNode<NodeType, NodeContext>[] => {
|
||||
const nodes: TreeNode<NodeType, NodeContext>[] = [];
|
||||
|
||||
// Group tables by schema (existing logic)
|
||||
// Group tables by schema
|
||||
const tablesBySchema = new Map<string, RelevantTableData[]>();
|
||||
|
||||
relevantTableData.forEach((table) => {
|
||||
@@ -211,42 +187,21 @@ export const generateTreeDataBySchemas = ({
|
||||
});
|
||||
|
||||
tablesBySchema.forEach((schemaTables, schemaName) => {
|
||||
let schemaVisible;
|
||||
|
||||
if (databaseWithSchemas) {
|
||||
schemaVisible = !schemaTables.some(
|
||||
(table) =>
|
||||
filterTable({
|
||||
table: {
|
||||
id: table.id,
|
||||
schema: table.schema,
|
||||
},
|
||||
filter,
|
||||
options: {
|
||||
defaultSchema: defaultSchemas[databaseType],
|
||||
},
|
||||
}) === false
|
||||
);
|
||||
} else {
|
||||
// if at least one table is visible, the schema is considered visible
|
||||
schemaVisible = !schemaTables.some(
|
||||
(table) =>
|
||||
filterTable({
|
||||
table: {
|
||||
id: table.id,
|
||||
schema: table.schema,
|
||||
},
|
||||
filter,
|
||||
options: {
|
||||
defaultSchema: defaultSchemas[databaseType],
|
||||
},
|
||||
}) === false
|
||||
);
|
||||
}
|
||||
// Pre-compute visibility for all tables in this schema (single pass)
|
||||
const tablesWithVisibility = computeTableVisibility(
|
||||
schemaTables,
|
||||
filter,
|
||||
databaseType
|
||||
);
|
||||
const visibleCount = tablesWithVisibility.filter(
|
||||
(t) => t.visible
|
||||
).length;
|
||||
const schemaVisible = visibleCount === schemaTables.length;
|
||||
|
||||
const schemaNode: TreeNode<NodeType, NodeContext> = {
|
||||
id: `schema-${schemaName}`,
|
||||
name: `${schemaName} (${schemaTables.length})`,
|
||||
name: schemaName,
|
||||
suffix: `${visibleCount}/${schemaTables.length}`,
|
||||
type: 'schema',
|
||||
isFolder: true,
|
||||
icon: Database,
|
||||
@@ -255,35 +210,7 @@ export const generateTreeDataBySchemas = ({
|
||||
visible: schemaVisible,
|
||||
} satisfies SchemaContext,
|
||||
className: !schemaVisible ? 'opacity-50' : '',
|
||||
children: schemaTables.map(
|
||||
(table): TreeNode<NodeType, NodeContext> => {
|
||||
const tableVisible = filterTable({
|
||||
table: {
|
||||
id: table.id,
|
||||
schema: table.schema,
|
||||
},
|
||||
filter,
|
||||
options: {
|
||||
defaultSchema: defaultSchemas[databaseType],
|
||||
},
|
||||
});
|
||||
|
||||
const hidden = !tableVisible;
|
||||
|
||||
return {
|
||||
id: table.id,
|
||||
name: table.name,
|
||||
type: 'table',
|
||||
isFolder: false,
|
||||
icon: Table,
|
||||
context: {
|
||||
tableSchema: table.schema,
|
||||
visible: tableVisible,
|
||||
} satisfies TableContext,
|
||||
className: hidden ? 'opacity-50' : '',
|
||||
};
|
||||
}
|
||||
),
|
||||
children: createTableChildren(tablesWithVisibility),
|
||||
};
|
||||
nodes.push(schemaNode);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user