mirror of
https://github.com/chartdb/chartdb.git
synced 2026-02-09 21:19:45 -06:00
fix: improve UI for views (#1043)
* fix: improve UI for views * fix --------- Co-authored-by: Guy Ben-Aharon <baguy3@gmail.com>
This commit is contained in:
@@ -336,9 +336,13 @@ export const ChartDBProvider: React.FC<
|
||||
|
||||
const createTable: ChartDBContext['createTable'] = useCallback(
|
||||
async (attributes) => {
|
||||
const isView = attributes?.isView ?? false;
|
||||
const count = isView
|
||||
? tables.filter((t) => t.isView).length + 1
|
||||
: tables.filter((t) => !t.isView).length + 1;
|
||||
const table: DBTable = {
|
||||
id: generateId(),
|
||||
name: `table_${tables.length + 1}`,
|
||||
name: isView ? `view_${count}` : `table_${count}`,
|
||||
x: 0,
|
||||
y: 0,
|
||||
fields: [
|
||||
|
||||
@@ -66,6 +66,7 @@ export const CanvasFilter: React.FC<CanvasFilterProps> = ({ onClose }) => {
|
||||
name: table.name,
|
||||
schema: table.schema,
|
||||
parentAreaId: table.parentAreaId,
|
||||
isView: table.isView,
|
||||
})),
|
||||
[tables, showDBViews, checkIfNewTable]
|
||||
);
|
||||
|
||||
@@ -24,4 +24,5 @@ export type RelevantTableData = {
|
||||
name: string;
|
||||
schema?: string | null;
|
||||
parentAreaId?: string | null;
|
||||
isView?: boolean;
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
TableContext,
|
||||
} from './types';
|
||||
import type { TreeNode } from '@/components/tree-view/tree';
|
||||
import { Box, Database, Layers, Table } from 'lucide-react';
|
||||
import { Box, Database, Layers, Table, View } from 'lucide-react';
|
||||
import { filterTable } from '@/lib/domain/diagram-filter/filter';
|
||||
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||
|
||||
@@ -42,7 +42,7 @@ const createTableChildren = (
|
||||
name: table.name,
|
||||
type: 'table' as const,
|
||||
isFolder: false,
|
||||
icon: Table,
|
||||
icon: table.isView ? View : Table,
|
||||
context: {
|
||||
tableSchema: table.schema,
|
||||
visible: table.visible,
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
SquareDot,
|
||||
SquarePlus,
|
||||
SquareMinus,
|
||||
View,
|
||||
} from 'lucide-react';
|
||||
import { Label } from '@/components/label/label';
|
||||
import {
|
||||
@@ -344,6 +345,7 @@ export const TableNode: React.FC<NodeProps<TableNodeType>> = React.memo(
|
||||
() =>
|
||||
cn(
|
||||
'flex w-full flex-col border-2 bg-slate-50 dark:bg-slate-950 rounded-lg shadow-sm transition-transform duration-300',
|
||||
table.isView ? 'border-dashed' : '',
|
||||
selected || isTarget || isPartOfCreatingRelationship
|
||||
? 'border-pink-600'
|
||||
: 'border-slate-500 dark:border-slate-700',
|
||||
@@ -390,6 +392,7 @@ export const TableNode: React.FC<NodeProps<TableNodeType>> = React.memo(
|
||||
isTarget,
|
||||
editTableMode,
|
||||
isPartOfCreatingRelationship,
|
||||
table.isView,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -537,6 +540,8 @@ export const TableNode: React.FC<NodeProps<TableNodeType>> = React.memo(
|
||||
Table Changed
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
) : table.isView ? (
|
||||
<View className="size-3.5 shrink-0 text-gray-600 dark:text-primary" />
|
||||
) : (
|
||||
<Table2 className="size-3.5 shrink-0 text-gray-600 dark:text-primary" />
|
||||
)}
|
||||
|
||||
@@ -207,71 +207,82 @@ export const TableListItemContent: React.FC<TableListItemContentProps> = ({
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem value="indexes" className="mb-2 border-y-0">
|
||||
<AccordionTrigger
|
||||
iconPosition="right"
|
||||
className="group flex flex-1 p-0 px-2 py-1 text-xs text-subtitle hover:bg-secondary"
|
||||
asChild
|
||||
>
|
||||
<div className="flex flex-1 items-center justify-between">
|
||||
<div className="flex flex-row items-center gap-1">
|
||||
<FileKey2 className="size-4" />
|
||||
{t('side_panel.tables_section.table.indexes')}
|
||||
</div>
|
||||
{!readonly ? (
|
||||
<div className="flex flex-row-reverse">
|
||||
<div className="hidden flex-row-reverse group-hover:flex">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="size-4 p-0 text-xs hover:bg-primary-foreground"
|
||||
onClick={createIndexHandler}
|
||||
>
|
||||
<Plus className="size-4 shrink-0 text-muted-foreground transition-transform duration-200" />
|
||||
</Button>
|
||||
{!table.isView ? (
|
||||
<AccordionItem value="indexes" className="mb-2 border-y-0">
|
||||
<AccordionTrigger
|
||||
iconPosition="right"
|
||||
className="group flex flex-1 p-0 px-2 py-1 text-xs text-subtitle hover:bg-secondary"
|
||||
asChild
|
||||
>
|
||||
<div className="flex flex-1 items-center justify-between">
|
||||
<div className="flex flex-row items-center gap-1">
|
||||
<FileKey2 className="size-4" />
|
||||
{t(
|
||||
'side_panel.tables_section.table.indexes'
|
||||
)}
|
||||
</div>
|
||||
{!readonly ? (
|
||||
<div className="flex flex-row-reverse">
|
||||
<div className="hidden flex-row-reverse group-hover:flex">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="size-4 p-0 text-xs hover:bg-primary-foreground"
|
||||
onClick={createIndexHandler}
|
||||
>
|
||||
<Plus className="size-4 shrink-0 text-muted-foreground transition-transform duration-200" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className="pb-0 pt-1">
|
||||
{[...table.indexes]
|
||||
.sort((a, b) => {
|
||||
// Sort PK indexes first
|
||||
if (a.isPrimaryKey && !b.isPrimaryKey)
|
||||
return -1;
|
||||
if (!a.isPrimaryKey && b.isPrimaryKey)
|
||||
return 1;
|
||||
return 0;
|
||||
})
|
||||
.map((index) => (
|
||||
<TableIndex
|
||||
key={index.id}
|
||||
index={index}
|
||||
removeIndex={() =>
|
||||
removeIndex(table.id, index.id)
|
||||
}
|
||||
updateIndex={(
|
||||
attrs: Partial<DBIndex>
|
||||
) =>
|
||||
updateIndex(
|
||||
table.id,
|
||||
index.id,
|
||||
attrs
|
||||
)
|
||||
}
|
||||
fields={table.fields}
|
||||
/>
|
||||
))}
|
||||
|
||||
{!readonly ? (
|
||||
<div className="flex justify-start p-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="flex h-7 items-center gap-1 px-2 text-xs"
|
||||
onClick={createIndexHandler}
|
||||
>
|
||||
<Plus className="size-4 text-muted-foreground" />
|
||||
{t(
|
||||
'side_panel.tables_section.table.add_index'
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className="pb-0 pt-1">
|
||||
{[...table.indexes]
|
||||
.sort((a, b) => {
|
||||
// Sort PK indexes first
|
||||
if (a.isPrimaryKey && !b.isPrimaryKey)
|
||||
return -1;
|
||||
if (!a.isPrimaryKey && b.isPrimaryKey) return 1;
|
||||
return 0;
|
||||
})
|
||||
.map((index) => (
|
||||
<TableIndex
|
||||
key={index.id}
|
||||
index={index}
|
||||
removeIndex={() =>
|
||||
removeIndex(table.id, index.id)
|
||||
}
|
||||
updateIndex={(attrs: Partial<DBIndex>) =>
|
||||
updateIndex(table.id, index.id, attrs)
|
||||
}
|
||||
fields={table.fields}
|
||||
/>
|
||||
))}
|
||||
|
||||
{!readonly ? (
|
||||
<div className="flex justify-start p-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="flex h-7 items-center gap-1 px-2 text-xs"
|
||||
onClick={createIndexHandler}
|
||||
>
|
||||
<Plus className="size-4 text-muted-foreground" />
|
||||
{t(
|
||||
'side_panel.tables_section.table.add_index'
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
) : null}
|
||||
|
||||
<AccordionItem value="comments" className="border-y-0">
|
||||
<AccordionTrigger
|
||||
@@ -317,14 +328,16 @@ export const TableListItemContent: React.FC<TableListItemContentProps> = ({
|
||||
|
||||
{!readonly ? (
|
||||
<div className="flex gap-1">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 p-2 text-xs"
|
||||
onClick={createIndexHandler}
|
||||
>
|
||||
<FileKey2 className="h-4" />
|
||||
{t('side_panel.tables_section.table.add_index')}
|
||||
</Button>
|
||||
{!table.isView ? (
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 p-2 text-xs"
|
||||
onClick={createIndexHandler}
|
||||
>
|
||||
<FileKey2 className="h-4" />
|
||||
{t('side_panel.tables_section.table.add_index')}
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 p-2 text-xs"
|
||||
|
||||
@@ -184,18 +184,20 @@ export const TableListItemHeader: React.FC<TableListItemHeaderProps> = ({
|
||||
)}
|
||||
<FileType2 className="size-3.5" />
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="flex justify-between gap-4"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
createIndex(table.id);
|
||||
}}
|
||||
>
|
||||
{t(
|
||||
'side_panel.tables_section.table.table_actions.add_index'
|
||||
)}
|
||||
<FileKey2 className="size-3.5" />
|
||||
</DropdownMenuItem>
|
||||
{!table.isView ? (
|
||||
<DropdownMenuItem
|
||||
className="flex justify-between gap-4"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
createIndex(table.id);
|
||||
}}
|
||||
>
|
||||
{t(
|
||||
'side_panel.tables_section.table.table_actions.add_index'
|
||||
)}
|
||||
<FileKey2 className="size-3.5" />
|
||||
</DropdownMenuItem>
|
||||
) : null}
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
@@ -226,6 +228,7 @@ export const TableListItemHeader: React.FC<TableListItemHeaderProps> = ({
|
||||
),
|
||||
[
|
||||
table.id,
|
||||
table.isView,
|
||||
createField,
|
||||
createIndex,
|
||||
deleteTableHandler,
|
||||
@@ -299,10 +302,15 @@ export const TableListItemHeader: React.FC<TableListItemHeaderProps> = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-row-reverse">
|
||||
<div className="flex flex-row-reverse items-center">
|
||||
{!editMode ? (
|
||||
<>
|
||||
{!readonly ? <div>{renderDropDownMenu()}</div> : null}
|
||||
{table.isView ? (
|
||||
<span className="rounded bg-muted px-1.5 py-0 text-[10px] font-medium text-muted-foreground">
|
||||
View
|
||||
</span>
|
||||
) : null}
|
||||
<div className="flex flex-row-reverse md:hidden md:group-hover:flex">
|
||||
{!readonly ? (
|
||||
<ListItemHeaderButton onClick={enterEditMode}>
|
||||
|
||||
Reference in New Issue
Block a user