diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 864fa3f..0e1a10c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -8,7 +8,6 @@ body: attributes: value: | Thanks for taking the time to suggest an improvement! - Datary is a database management tool, so features related to databases, UX, performance, or integrations are especially welcome. - type: textarea attributes: diff --git a/.github/scripts/telegram-notify.js b/.github/scripts/telegram-notify.js index 6b52de8..3734bbc 100644 --- a/.github/scripts/telegram-notify.js +++ b/.github/scripts/telegram-notify.js @@ -25,7 +25,7 @@ if (GITHUB_EVENT_PATH && fs.existsSync(GITHUB_EVENT_PATH)) { if (GITHUB_EVENT_NAME === 'push') { message += `\n*Push*\n\n` - message += `Branch: ${GITHUB_REF?.replace('refs/heads/', '')}\n` + message += `Branch: ${GITHUB_REF?.replace('refs/heads/', '')}\n\n` payload.commits?.slice(0, 3).forEach((c, i) => { message += `${i + 1}. ${c.message.split('\n')[0]}\n` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a6dc8fe..1f99225 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,8 +28,6 @@ We aim to maintain a friendly and inclusive environment. Please respect all part - Give constructive feedback. - Report problems or abusive behavior to maintainers. -Full code of conduct can be found in [`CODE_OF_CONDUCT.md`](./CODE_OF_CONDUCT.md). - --- ## How to Contribute diff --git a/apps/desktop/renderer/package.json b/apps/desktop/renderer/package.json index 46fd223..316b358 100644 --- a/apps/desktop/renderer/package.json +++ b/apps/desktop/renderer/package.json @@ -13,6 +13,7 @@ "@datary/core": "workspace:", "@hookform/resolvers": "^5.2.2", "@tailwindcss/vite": "^4.1.18", + "@tanstack/react-virtual": "^3.13.18", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.563.0", diff --git a/apps/desktop/renderer/src/app/store/table-data.store.ts b/apps/desktop/renderer/src/app/store/table-data.store.ts index e40e94c..b56f21b 100644 --- a/apps/desktop/renderer/src/app/store/table-data.store.ts +++ b/apps/desktop/renderer/src/app/store/table-data.store.ts @@ -26,15 +26,48 @@ export const useTableDataStore = create((set, get) => ({ set({ loading: true }) - const data = await window.datary.db.loadTableData(schema, table) + try { + const [columnsRaw, tableDataRaw] = await Promise.all([ + window.datary.db.loadColumns(schema, table), + window.datary.db.loadTableData(schema, table) + ]) - set(state => ({ - tables: { - ...state.tables, - [key]: data - }, - loading: false - })) + // @ts-ignore + const columns = columnsRaw.map(col => { + let type = col.type + if (col.enumValues && col.enumValues.length > 0) { + type = `enum(${col.enumValues.join(', ')})` + } + return { + columnName: col.columnName, + type, + nullable: col.nullable, + enumValues: col.enumValues + } + }) + + const tableData: TableData = { + // @ts-ignore + columns: columns.map(col => ({ + name: col.columnName, + type: col.type, + nullable: col.nullable + })), + rows: tableDataRaw.rows + } + + set(state => ({ + tables: { + ...state.tables, + [key]: tableData + }, + loading: false + })) + } catch (err: any) { + toast.error(`Failed to load table ${table}: ${err.message}`) + console.error(err) + set({ loading: false }) + } }, reset: () => set({ diff --git a/apps/desktop/renderer/src/main.tsx b/apps/desktop/renderer/src/main.tsx index 22a1a01..3e2112b 100644 --- a/apps/desktop/renderer/src/main.tsx +++ b/apps/desktop/renderer/src/main.tsx @@ -1,8 +1,4 @@ -import type { - ColumnMetadataContract, - DatabaseMetadataContract, - TableMetadataContract -} from '@datary/core' +import type { DatabaseMetadataContract, TableMetadataContract } from '@datary/core' import ReactDOM from 'react-dom/client' import { HashRouter } from 'react-router-dom' @@ -26,11 +22,7 @@ declare global { schema: string | null ): Promise loadViews(database: string, schema: string | null): Promise - loadColumns( - database: string, - schema: string, - table: string - ): Promise + loadColumns: any loadTableData: any disconnect: Function } diff --git a/apps/desktop/renderer/src/modules/explorer/components/tabs/ExplorerTabContextMenu.tsx b/apps/desktop/renderer/src/modules/explorer/components/tabs/ExplorerTabContextMenu.tsx new file mode 100644 index 0000000..f5ab81c --- /dev/null +++ b/apps/desktop/renderer/src/modules/explorer/components/tabs/ExplorerTabContextMenu.tsx @@ -0,0 +1,65 @@ +import type { ReactNode } from 'react' + +import { useContextMenu } from '@/shared/hooks/useContextMenu' +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem } from '@/shared/ui/dropdown-menu' + +interface Props { + children: ReactNode + disabledCloseOthers: boolean + onClose: () => void + onCloseOthers: () => void + onCloseAll: () => void +} +export function ExplorerTabContextMenu({ + children, + disabledCloseOthers, + onClose, + onCloseOthers, + onCloseAll +}: Props) { + const { open, position, onContextMenu, close } = useContextMenu() + + return ( + <> +
{children}
+ + {open && ( + !val && close()}> + + { + onClose() + close() + }} + > + Close + + { + onCloseOthers() + close() + }} + > + Close Others + + { + onCloseAll() + close() + }} + > + Close All + + + + )} + + ) +} diff --git a/apps/desktop/renderer/src/modules/explorer/components/tabs/ExplorerTabs.tsx b/apps/desktop/renderer/src/modules/explorer/components/tabs/ExplorerTabs.tsx index cdb83dc..58cb030 100644 --- a/apps/desktop/renderer/src/modules/explorer/components/tabs/ExplorerTabs.tsx +++ b/apps/desktop/renderer/src/modules/explorer/components/tabs/ExplorerTabs.tsx @@ -2,8 +2,9 @@ import type { ReactNode } from 'react' import type { Tab } from '../../types/explorer.types' +import { ExplorerTabContextMenu } from './ExplorerTabContextMenu' import { TabContent } from './TabContent' -import { TableTabs } from '@/shared/components/table-tabs' +import { TableTabs } from './TableTabs' import { cn } from '@/shared/lib/utils' interface Props { @@ -11,17 +12,41 @@ interface Props { activeTab: string onSelectTab: (id: string) => void onCloseTab: (id: string) => void + onCloseOthers: (id: string) => void + onCloseAll: () => void children?: ReactNode } -export function ExplorerTabs({ tabs, activeTab, onSelectTab, onCloseTab }: Props) { +export function ExplorerTabs({ + tabs, + activeTab, + onSelectTab, + onCloseTab, + onCloseOthers, + onCloseAll +}: Props) { return ( -
+
( + onCloseTab(tab.id)} + onCloseOthers={() => { + onSelectTab(tab.id) + onCloseOthers(tab.id) + }} + onCloseAll={() => { + onCloseAll() + }} + > +
{tabNode}
+
+ )} > {tabs.map(tab => (
+ const gridData = useMemo(() => { + return { + rows: data.rows, + columns: data.columns.map(col => ({ + name: col.name, + type: col.type + })) + } + }, [data]) + + return } diff --git a/apps/desktop/renderer/src/shared/components/table-tabs.tsx b/apps/desktop/renderer/src/modules/explorer/components/tabs/TableTabs.tsx similarity index 62% rename from apps/desktop/renderer/src/shared/components/table-tabs.tsx rename to apps/desktop/renderer/src/modules/explorer/components/tabs/TableTabs.tsx index 148114e..61be7d3 100644 --- a/apps/desktop/renderer/src/shared/components/table-tabs.tsx +++ b/apps/desktop/renderer/src/modules/explorer/components/tabs/TableTabs.tsx @@ -1,9 +1,8 @@ import { ChevronLeft, ChevronRight, Table, X } from 'lucide-react' -import React from 'react' -import { useEffect, useRef, useState } from 'react' +import { type ReactNode, useEffect, useRef, useState } from 'react' -import { cn } from '../lib/utils' -import { Button } from '../ui/button' +import { cn } from '../../../../shared/lib/utils' +import { Button } from '../../../../shared/ui/button' export interface Tab { id: string @@ -16,10 +15,18 @@ interface TableTabsProps { activeTab: string onSelectTab: (id: string) => void onCloseTab: (id: string) => void - children: React.ReactNode + renderTab?: (tab: Tab, defaultTab: ReactNode) => ReactNode + children: ReactNode } -export function TableTabs({ tabs, activeTab, onSelectTab, onCloseTab, children }: TableTabsProps) { +export function TableTabs({ + tabs, + activeTab, + onSelectTab, + onCloseTab, + renderTab, + children +}: TableTabsProps) { const tabsRef = useRef(null) const [showScrollButtons, setShowScrollButtons] = useState(false) @@ -61,38 +68,42 @@ export function TableTabs({ tabs, activeTab, onSelectTab, onCloseTab, children } )}
- {tabs.map(tab => ( -