From 34c1f372912022d2fefc7cde2e5ba1b383e94b49 Mon Sep 17 00:00:00 2001 From: "@roth-dev" Date: Thu, 30 Jan 2025 12:14:47 +0700 Subject: [PATCH 1/4] fix save columns data empty space --- .../data-catalog/data-model-tab.tsx | 65 +++++++++++++------ 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/src/extensions/data-catalog/data-model-tab.tsx b/src/extensions/data-catalog/data-model-tab.tsx index 79bd05df..10088388 100644 --- a/src/extensions/data-catalog/data-model-tab.tsx +++ b/src/extensions/data-catalog/data-model-tab.tsx @@ -1,5 +1,5 @@ import SchemaNameSelect from "@/components/gui/schema-editor/schema-name-select"; -import { Toolbar } from "@/components/gui/toolbar"; +import { Toolbar, ToolbarFiller } from "@/components/gui/toolbar"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -10,6 +10,7 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { useConfig } from "@/context/config-provider"; @@ -63,6 +64,34 @@ function DataCatalogTableColumnModal({ .finally(() => setSampleLoading(false)); }, [databaseDriver, columnName, schemaName, tableName]); + const onSaveUpdateColumn = useCallback(() => { + setLoading(true); + + driver + .updateColumn(schemaName, tableName, columnName, { + definition, + samples: + samples && samples.trim() + ? samples.split(",").map((s) => s.trim()) + : [], + hideFromEzql: modelColumn?.hideFromEzql ?? false, + }) + .then() + .finally(() => { + setLoading(false); + onClose(); + }); + }, [ + driver, + modelColumn, + samples, + columnName, + onClose, + schemaName, + tableName, + definition, + ]); + return ( <> @@ -113,24 +142,7 @@ function DataCatalogTableColumnModal({ - @@ -227,6 +239,7 @@ function DataCatalogTableAccordion({ export default function DataCatalogModelTab() { const { currentSchemaName, schema } = useSchema(); + const [search, setSearch] = useState(""); const [selectedSchema, setSelectedSchema] = useState(currentSchemaName); const { extensions } = useConfig(); @@ -236,6 +249,10 @@ export default function DataCatalogModelTab() { const driver = dataCatalogExtension?.driver; + const onChangeText = useCallback((value: string) => { + setSearch(value); + }, []); + const currentSchema = useMemo(() => { if (!selectedSchema) return []; const result = (schema[selectedSchema] || []) @@ -259,6 +276,16 @@ export default function DataCatalogModelTab() { value={selectedSchema} onChange={setSelectedSchema} /> + +
+ { + onChangeText(e.currentTarget.value); + }} + placeholder="Search tables, columns" + /> +
From 43b465c4546e640de2bbbc74a387fd1bf6de9337 Mon Sep 17 00:00:00 2001 From: "@roth-dev" Date: Thu, 30 Jan 2025 14:20:39 +0700 Subject: [PATCH 2/4] search tables & columns --- src/components/listview/index.tsx | 32 ++-------------- src/components/ui/highlight-text.tsx | 29 ++++++++++++++ .../data-catalog/data-model-tab.tsx | 38 +++++++++++++++---- 3 files changed, 64 insertions(+), 35 deletions(-) create mode 100644 src/components/ui/highlight-text.tsx diff --git a/src/components/listview/index.tsx b/src/components/listview/index.tsx index 17206b45..3e2be101 100644 --- a/src/components/listview/index.tsx +++ b/src/components/listview/index.tsx @@ -19,6 +19,7 @@ import React, { useRef, useState, } from "react"; +import HighlightText from "../ui/highlight-text"; export interface ListViewItem { key: string; @@ -57,31 +58,6 @@ interface ListViewRendererProps extends ListViewProps { contextOpen: boolean; } -function Highlight({ text, highlight }: { text: string; highlight?: string }) { - if (!highlight) return {text}; - - const regex = new RegExp( - "(" + (highlight ?? "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + ")", - "i" - ); - - const splitedText = text.split(regex); - - return ( - - {splitedText.map((text, idx) => { - return text.toLowerCase() === (highlight ?? "").toLowerCase() ? ( - - {text} - - ) : ( - {text} - ); - })} - - ); -} - function Indentation({ depth }: { depth: number }) { if (depth <= 0) return null; @@ -219,7 +195,7 @@ function renderList(props: ListViewRendererProps): React.ReactElement { {item.iconBadgeColor && (
@@ -228,7 +204,7 @@ function renderList(props: ListViewRendererProps): React.ReactElement { )}
- + {item.badgeContent && ( (props: ListViewRendererProps): React.ReactElement {
{item.progressBarValue && item.progressBarMax && ( -
+
{text}; + + const regex = new RegExp( + "(" + (highlight ?? "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + ")", + "i" + ); + + const splitedText = text.split(regex); + + return ( + + {splitedText.map((text, idx) => { + return text.toLowerCase() === (highlight ?? "").toLowerCase() ? ( + + {text} + + ) : ( + {text} + ); + })} + + ); +} diff --git a/src/extensions/data-catalog/data-model-tab.tsx b/src/extensions/data-catalog/data-model-tab.tsx index 10088388..1948eca9 100644 --- a/src/extensions/data-catalog/data-model-tab.tsx +++ b/src/extensions/data-catalog/data-model-tab.tsx @@ -10,6 +10,7 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; +import HighlightText from "@/components/ui/highlight-text"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; @@ -155,12 +156,14 @@ interface DataCatalogTableColumnProps { table: DatabaseTableSchema; column: DatabaseTableColumn; driver: DataCatalogDriver; + search?: string; } function DataCatalogTableColumn({ column, table, driver, + search, }: DataCatalogTableColumnProps) { const modelColumn = driver.getColumn( table.schemaName, @@ -175,7 +178,9 @@ function DataCatalogTableColumn({ return (
-
{column.name}
+
+ +
{definition || "No description"}
@@ -211,25 +216,47 @@ function DataCatalogTableColumn({ interface DataCatalogTableAccordionProps { table: DatabaseTableSchema; driver: DataCatalogDriver; + search?: string; } function DataCatalogTableAccordion({ table, driver, + search, }: DataCatalogTableAccordionProps) { + // Check if any of the column match? + const matchColumns = useMemo(() => { + return !search || search.toLowerCase() === table.tableName!.toLowerCase() + ? table.columns + : table.columns.filter((column) => + column.name.toLowerCase().includes(search.toLowerCase()) + ); + }, [search, table]); + + const matchedTableName = useMemo(() => { + return search + ? table.tableName!.toLowerCase().includes(search?.toLowerCase()) + : true; + }, [search, table]); + + if (!matchedTableName && matchColumns.length === 0 && search) { + return null; + } + return (
{table.tableName}
No description
- {table.columns.map((column) => { + {matchColumns.map((column) => { return ( ); })} @@ -249,10 +276,6 @@ export default function DataCatalogModelTab() { const driver = dataCatalogExtension?.driver; - const onChangeText = useCallback((value: string) => { - setSearch(value); - }, []); - const currentSchema = useMemo(() => { if (!selectedSchema) return []; const result = (schema[selectedSchema] || []) @@ -281,7 +304,7 @@ export default function DataCatalogModelTab() { { - onChangeText(e.currentTarget.value); + setSearch(e.currentTarget.value); }} placeholder="Search tables, columns" /> @@ -292,6 +315,7 @@ export default function DataCatalogModelTab() {
{currentSchema.map((table) => ( Date: Thu, 30 Jan 2025 21:42:39 +0700 Subject: [PATCH 3/4] toggle show table by column definition --- .../data-catalog/data-model-tab.tsx | 83 +++++++++++++++++-- .../data-catalog/driver-inmemory.ts | 42 +++++++--- src/extensions/data-catalog/driver.tsx | 5 ++ 3 files changed, 109 insertions(+), 21 deletions(-) diff --git a/src/extensions/data-catalog/data-model-tab.tsx b/src/extensions/data-catalog/data-model-tab.tsx index 1948eca9..3e1d85eb 100644 --- a/src/extensions/data-catalog/data-model-tab.tsx +++ b/src/extensions/data-catalog/data-model-tab.tsx @@ -1,6 +1,7 @@ import SchemaNameSelect from "@/components/gui/schema-editor/schema-name-select"; import { Toolbar, ToolbarFiller } from "@/components/gui/toolbar"; import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; import { Dialog, DialogContent, @@ -157,6 +158,7 @@ interface DataCatalogTableColumnProps { column: DatabaseTableColumn; driver: DataCatalogDriver; search?: string; + hasDefinitionOnly?: boolean; } function DataCatalogTableColumn({ @@ -164,6 +166,7 @@ function DataCatalogTableColumn({ table, driver, search, + hasDefinitionOnly, }: DataCatalogTableColumnProps) { const modelColumn = driver.getColumn( table.schemaName, @@ -176,6 +179,10 @@ function DataCatalogTableColumn({ const [open, setOpen] = useState(false); + if (hasDefinitionOnly && !definition) { + return null; + } + return (
@@ -217,28 +224,67 @@ interface DataCatalogTableAccordionProps { table: DatabaseTableSchema; driver: DataCatalogDriver; search?: string; + columnName?: string; + hasDefinitionOnly?: boolean; } function DataCatalogTableAccordion({ table, driver, search, + hasDefinitionOnly, }: DataCatalogTableAccordionProps) { + const modelTable = driver.getTable(table.schemaName, table.tableName!); + + const [definition, setDefinition] = useState(modelTable?.definition || ""); + + const onUpdateTable = useCallback(() => { + if ( + definition && + definition.trim() && + definition !== modelTable?.definition + ) { + driver.updateTable(table?.schemaName, table.tableName!, { + definition, + }); + } + }, [driver, table, definition, modelTable]); + // Check if any of the column match? const matchColumns = useMemo(() => { - return !search || search.toLowerCase() === table.tableName!.toLowerCase() - ? table.columns - : table.columns.filter((column) => - column.name.toLowerCase().includes(search.toLowerCase()) - ); + if (!search || search.toLowerCase() === table.tableName!.toLowerCase()) { + return table.columns; + } + return table.columns.filter((column) => + column.name.toLowerCase().includes(search.toLowerCase()) + ); }, [search, table]); const matchedTableName = useMemo(() => { - return search - ? table.tableName!.toLowerCase().includes(search?.toLowerCase()) - : true; + if (search) { + return table.tableName!.toLowerCase().includes(search?.toLowerCase()); + } + return true; }, [search, table]); + // this will work only toggle check box + if (hasDefinitionOnly) { + const columnsDefinition = table.columns + .map((col) => { + const modelColumn = driver.getColumn( + table.schemaName, + table.tableName!, + col.name + ); + return !!modelColumn?.definition; + }) + .filter(Boolean); + + if (columnsDefinition.length === 0) { + return null; + } + } + if (!matchedTableName && matchColumns.length === 0 && search) { return null; } @@ -247,7 +293,16 @@ function DataCatalogTableAccordion({
{table.tableName}
-
No description
+ { + e.preventDefault(); + setDefinition(e.currentTarget.value); + }} + className="h-[30px] w-[150px] p-0 text-[13px] focus-visible:outline-none" + />
{matchColumns.map((column) => { return ( @@ -257,6 +312,7 @@ function DataCatalogTableAccordion({ column={column} driver={driver} search={search} + hasDefinitionOnly={hasDefinitionOnly} /> ); })} @@ -267,6 +323,7 @@ function DataCatalogTableAccordion({ export default function DataCatalogModelTab() { const { currentSchemaName, schema } = useSchema(); const [search, setSearch] = useState(""); + const [hasDefinitionOnly, setHasDefinitionOnly] = useState(false); const [selectedSchema, setSelectedSchema] = useState(currentSchemaName); const { extensions } = useConfig(); @@ -299,6 +356,13 @@ export default function DataCatalogModelTab() { value={selectedSchema} onChange={setSelectedSchema} /> +
+ setHasDefinitionOnly(!hasDefinitionOnly)} + /> + +
))}
diff --git a/src/extensions/data-catalog/driver-inmemory.ts b/src/extensions/data-catalog/driver-inmemory.ts index f2aefef8..23d156e6 100644 --- a/src/extensions/data-catalog/driver-inmemory.ts +++ b/src/extensions/data-catalog/driver-inmemory.ts @@ -1,4 +1,10 @@ -import DataCatalogDriver, { DataCatalogModelColumn, DataCatalogModelColumnInput, DataCatalogModelTable, DataCatalogModelTableInput, DataCatalogSchemas } from "./driver"; +import DataCatalogDriver, { + DataCatalogModelColumn, + DataCatalogModelColumnInput, + DataCatalogModelTable, + DataCatalogModelTableInput, + DataCatalogSchemas, +} from "./driver"; interface DataCatalogInmemoryDriverOptions { delay?: number; @@ -6,16 +12,19 @@ interface DataCatalogInmemoryDriverOptions { export default class DataCatalogInmemoryDriver implements DataCatalogDriver { protected schemas: DataCatalogSchemas; - protected options: DataCatalogInmemoryDriverOptions + protected options: DataCatalogInmemoryDriverOptions; - constructor(schemas: DataCatalogSchemas, options: DataCatalogInmemoryDriverOptions) { + constructor( + schemas: DataCatalogSchemas, + options: DataCatalogInmemoryDriverOptions + ) { this.schemas = schemas; this.options = options; } async load(): Promise { if (this.options.delay) { - await new Promise(resolve => setTimeout(resolve, this.options.delay)); + await new Promise((resolve) => setTimeout(resolve, this.options.delay)); } return this.schemas; @@ -28,7 +37,7 @@ export default class DataCatalogInmemoryDriver implements DataCatalogDriver { data: DataCatalogModelColumnInput ): Promise { if (this.options.delay) { - await new Promise(resolve => setTimeout(resolve, this.options.delay)); + await new Promise((resolve) => setTimeout(resolve, this.options.delay)); } const normalizedSchemaName = schemaName.toLowerCase(); @@ -46,7 +55,7 @@ export default class DataCatalogInmemoryDriver implements DataCatalogDriver { schemaName: normalizedSchemaName, tableName: normalizedTableName, columns: {}, - definition: '', + definition: "", }; } @@ -54,7 +63,7 @@ export default class DataCatalogInmemoryDriver implements DataCatalogDriver { if (!table.columns[normalizedColumnName]) { table.columns[normalizedColumnName] = { name: normalizedColumnName, - definition: '', + definition: "", samples: [], hideFromEzql: false, }; @@ -71,7 +80,7 @@ export default class DataCatalogInmemoryDriver implements DataCatalogDriver { data: DataCatalogModelTableInput ): Promise { if (this.options.delay) { - await new Promise(resolve => setTimeout(resolve, this.options.delay)); + await new Promise((resolve) => setTimeout(resolve, this.options.delay)); } const normalizedSchemaName = schemaName.toLowerCase(); @@ -88,7 +97,7 @@ export default class DataCatalogInmemoryDriver implements DataCatalogDriver { schemaName: normalizedSchemaName, tableName: normalizedTableName, columns: {}, - definition: '', + definition: "", }; } @@ -103,9 +112,18 @@ export default class DataCatalogInmemoryDriver implements DataCatalogDriver { tableName: string, columnName: string ): DataCatalogModelColumn | undefined { + const normalizedColumnName = columnName.toLowerCase(); + + const table = this.getTable(schemaName, tableName); + return table?.columns[normalizedColumnName]; + } + + getTable( + schemaName: string, + tableName: string + ): DataCatalogModelTable | undefined { const normalizedSchemaName = schemaName.toLowerCase(); const normalizedTableName = tableName.toLowerCase(); - const normalizedColumnName = columnName.toLowerCase(); if (!this.schemas[normalizedSchemaName]) { return; @@ -118,6 +136,6 @@ export default class DataCatalogInmemoryDriver implements DataCatalogDriver { } const table = schemas[normalizedTableName]; - return table.columns[normalizedColumnName]; + return table; } -} \ No newline at end of file +} diff --git a/src/extensions/data-catalog/driver.tsx b/src/extensions/data-catalog/driver.tsx index fd69d637..e0c1631e 100644 --- a/src/extensions/data-catalog/driver.tsx +++ b/src/extensions/data-catalog/driver.tsx @@ -48,4 +48,9 @@ export default abstract class DataCatalogDriver { tableName: string, columnName: string ): DataCatalogModelColumn | undefined; + + abstract getTable( + schemaName: string, + tableName: string + ): DataCatalogModelTable | undefined; } From bc07a60bf041ab8419c5e25c0c39798e87d9b788 Mon Sep 17 00:00:00 2001 From: "Visal .In" Date: Fri, 31 Jan 2025 08:09:38 +0700 Subject: [PATCH 4/4] change the data catalog icon --- src/extensions/data-catalog/index.tsx | 6 +++--- src/extensions/data-catalog/sidebar.tsx | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/extensions/data-catalog/index.tsx b/src/extensions/data-catalog/index.tsx index 6e4c84ce..0029a451 100644 --- a/src/extensions/data-catalog/index.tsx +++ b/src/extensions/data-catalog/index.tsx @@ -1,7 +1,7 @@ import { StudioExtension } from "@/core/extension-base"; import { StudioExtensionContext } from "@/core/extension-manager"; import { createTabExtension } from "@/core/extension-tab"; -import { LucideDatabase, LucideNotepadText } from "lucide-react"; +import { Book } from "@phosphor-icons/react"; import DataCatalogModelTab from "./data-model-tab"; import DataCatalogDriver from "./driver"; import DataCatalogSidebar from "./sidebar"; @@ -11,7 +11,7 @@ export const dataCatalogModelTab = createTabExtension({ key: () => "data-catalog-model", name: "Data Model", generate: () => ({ - icon: LucideNotepadText, + icon: Book, title: "Data Model", component: , }), @@ -29,7 +29,7 @@ export default class DataCatalogExtension extends StudioExtension { studio.registerSidebar({ key: "data-catalog", name: "Data Catalog", - icon: , + icon: , content: , }); diff --git a/src/extensions/data-catalog/sidebar.tsx b/src/extensions/data-catalog/sidebar.tsx index c8eec4c4..f7182144 100644 --- a/src/extensions/data-catalog/sidebar.tsx +++ b/src/extensions/data-catalog/sidebar.tsx @@ -1,12 +1,15 @@ -import { SidebarMenuItem } from "@/components/sidebar-menu"; +import { SidebarMenuHeader, SidebarMenuItem } from "@/components/sidebar-menu"; +import { Database } from "@phosphor-icons/react"; import { dataCatalogModelTab } from "."; export default function DataCatalogSidebar() { return (
+ dataCatalogModelTab.open({})} + icon={Database} />
);