diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e432ec5..e3fb3976 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,6 +7,9 @@ on: concurrency: ${{ github.workflow }}-${{ github.ref }} +permissions: + id-token: write # Required for OIDC + jobs: release: name: Release diff --git a/packages/typegen/src/typegen.ts b/packages/typegen/src/typegen.ts index 4ca54bd9..844c11f1 100644 --- a/packages/typegen/src/typegen.ts +++ b/packages/typegen/src/typegen.ts @@ -177,11 +177,11 @@ const generateTypedClientsSingle = async ( apiKey: undefined, username: envNames?.auth && "username" in envNames.auth - ? envNames.auth.username ?? defaultEnvNames.username + ? (envNames.auth.username ?? defaultEnvNames.username) : defaultEnvNames.username, password: envNames?.auth && "password" in envNames.auth - ? envNames.auth.password ?? defaultEnvNames.password + ? (envNames.auth.password ?? defaultEnvNames.password) : defaultEnvNames.password, }, db: envNames?.db ?? defaultEnvNames.db, diff --git a/packages/typegen/web/package.json b/packages/typegen/web/package.json index 2748e154..0773f85d 100644 --- a/packages/typegen/web/package.json +++ b/packages/typegen/web/package.json @@ -26,6 +26,7 @@ "@tailwindcss/vite": "^4.1.18", "@tanstack/react-query": "^5.90.12", "@tanstack/react-table": "^8.21.3", + "@tanstack/react-virtual": "^3.13.13", "@uidotdev/usehooks": "^2.4.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/packages/typegen/web/src/components/MetadataFieldsDialog.tsx b/packages/typegen/web/src/components/MetadataFieldsDialog.tsx index 1983cc7b..fc3b0221 100644 --- a/packages/typegen/web/src/components/MetadataFieldsDialog.tsx +++ b/packages/typegen/web/src/components/MetadataFieldsDialog.tsx @@ -7,11 +7,11 @@ import { getSortedRowModel, getFilteredRowModel, type ColumnDef, + flexRender, } from "@tanstack/react-table"; +import { useVirtualizer } from "@tanstack/react-virtual"; import { DataGrid, DataGridContainer } from "./ui/data-grid"; -import { DataGridTable } from "./ui/data-grid-table"; import { DataGridColumnHeader } from "./ui/data-grid-column-header"; -import { ScrollArea, ScrollBar } from "./ui/scroll-area"; import { Input, InputWrapper } from "./ui/input"; import { Switch } from "./ui/switch"; import { Skeleton } from "./ui/skeleton"; @@ -131,6 +131,13 @@ export function MetadataFieldsDialog({ const [globalFilter, setGlobalFilter] = useState(""); + // Reset search filter when dialog opens or table changes + useEffect(() => { + if (open) { + setGlobalFilter(""); + } + }, [open, tableName]); + // Get the config type to validate we're working with fmodata const configType = useWatch({ control, @@ -923,7 +930,7 @@ export function MetadataFieldsDialog({ )} {info.getValue() as string} @@ -1055,6 +1062,40 @@ export function MetadataFieldsDialog({ return fieldsData.filter((row) => !row.isExcluded).length; }, [fieldsData]); + // Ref for the scrollable container + const tableContainerRef = useRef(null); + + // Get filtered rows for virtualization + const { rows } = fieldsTable.getRowModel(); + + // Setup virtualizer + const rowVirtualizer = useVirtualizer({ + count: rows.length, + getScrollElement: () => tableContainerRef.current, + estimateSize: () => 57, // Estimated row height in pixels + overscan: 10, // Number of items to render outside of the visible area + }); + + // Recalculate virtualizer when dialog opens or rows change + useEffect(() => { + if (open && tableContainerRef.current && rows.length > 0) { + // Small delay to ensure container is fully rendered + const timeoutId = setTimeout(() => { + rowVirtualizer.measure(); + }, 0); + return () => clearTimeout(timeoutId); + } + }, [open, rows.length, rowVirtualizer]); + + const virtualRows = rowVirtualizer.getVirtualItems(); + const totalSize = rowVirtualizer.getTotalSize(); + + const paddingTop = virtualRows.length > 0 ? virtualRows?.[0]?.start || 0 : 0; + const paddingBottom = + virtualRows.length > 0 + ? totalSize - (virtualRows?.[virtualRows.length - 1]?.end || 0) + : 0; + return ( - - - - - + +
+ + + {fieldsTable.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + + {isLoading ? ( + + + + ) : rows.length === 0 ? ( + + + + ) : virtualRows.length === 0 && rows.length > 0 ? ( + // Fallback: if virtualizer hasn't initialized yet, show all rows + rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + ))} + + )) + ) : ( + <> + {paddingTop > 0 && ( + + + )} + {virtualRows.map((virtualRow) => { + const row = rows[virtualRow.index]; + return ( + + {row.getVisibleCells().map((cell) => ( + + ))} + + ); + })} + {paddingBottom > 0 && ( + + + )} + + )} + +
+ {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} +
+
+ Loading fields... +
+
+
+ No fields found. +
+
+ {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} +
+
+ {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} +
+
+
)} diff --git a/packages/typegen/web/src/components/MetadataTablesEditor.tsx b/packages/typegen/web/src/components/MetadataTablesEditor.tsx index 7ff817df..2d42f825 100644 --- a/packages/typegen/web/src/components/MetadataTablesEditor.tsx +++ b/packages/typegen/web/src/components/MetadataTablesEditor.tsx @@ -23,6 +23,7 @@ import { ScrollArea, ScrollBar } from "./ui/scroll-area"; import { Skeleton } from "./ui/skeleton"; import { Badge } from "./ui/badge"; import { DropdownMenuItem } from "./ui/dropdown-menu"; +import { Tooltip, TooltipTrigger, TooltipContent } from "./ui/tooltip"; interface MetadataTablesEditorProps { configIndex: number; @@ -138,6 +139,72 @@ function FieldCountCell({ return {fieldCount}; } +// Helper component to fetch and display relationship count for a table +function RelationshipCountCell({ + tableName, + isIncluded, + configIndex, +}: { + tableName: string; + isIncluded: boolean; + configIndex: number; +}) { + const { data: parsedMetadata, isLoading } = useTableMetadata( + configIndex, + tableName, + isIncluded, // Only fetch when table is included + ); + + const relationships = useMemo(() => { + if (!parsedMetadata?.entitySets || !parsedMetadata?.entityTypes) { + return []; + } + + const entitySet = Object.values(parsedMetadata.entitySets).find( + (es) => es.Name === tableName, + ); + if (!entitySet) return []; + + const entityType = parsedMetadata.entityTypes[entitySet.EntityType]; + if (!entityType?.NavigationProperties) return []; + + const navProps = entityType.NavigationProperties; + // Handle both Array and other formats + if (Array.isArray(navProps)) { + return navProps.map((np) => np.Name); + } + return []; + }, [parsedMetadata, tableName]); + + if (isLoading) { + return ; + } + + if (!isIncluded) { + return null; + } + + const count = relationships.length; + + if (count === 0) { + return -; + } + + const relationshipNames = relationships.join(", "); + + return ( + + + {count} + + +
Relationships:
+
{relationshipNames}
+
+
+ ); +} + export function MetadataTablesEditor({ configIndex, }: MetadataTablesEditorProps) { @@ -358,12 +425,15 @@ export function MetadataTablesEditor({ ), enableSorting: true, + // Use a large size relative to other columns so it takes most space + // In fixed layout, space is distributed proportionally + cell: (info) => { const row = info.row.original; return ( {info.getValue() as string} @@ -380,7 +450,9 @@ export function MetadataTablesEditor({ ), enableSorting: false, - size: 100, + size: 50, + minSize: 50, + maxSize: 100, cell: (info) => { const row = info.row.original; if (!row.isIncluded) { @@ -398,6 +470,32 @@ export function MetadataTablesEditor({ skeleton: , }, }, + { + id: "relationships", + header: ({ column }) => ( + + ), + enableSorting: false, + size: 50, + minSize: 10, + maxSize: 100, + cell: (info) => { + const row = info.row.original; + if (!row.isIncluded) { + return null; + } + return ( + + ); + }, + meta: { + skeleton: , + }, + }, { id: "actions", header: () => null, @@ -600,7 +698,11 @@ export function MetadataTablesEditor({ recordCount={tablesTable.getFilteredRowModel().rows.length} isLoading={isLoadingTables} emptyMessage="No tables found." - tableLayout={{ width: "fixed", headerSticky: true }} + tableLayout={{ + width: "auto", + headerSticky: true, + dense: true, + }} > diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c64fa2a2..e088c58e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -840,6 +840,9 @@ importers: '@tanstack/react-table': specifier: ^8.21.3 version: 8.21.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/react-virtual': + specifier: ^3.13.13 + version: 3.13.13(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@uidotdev/usehooks': specifier: ^2.4.1 version: 2.4.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -4348,10 +4351,19 @@ packages: react: '>=16.8' react-dom: '>=16.8' + '@tanstack/react-virtual@3.13.13': + resolution: {integrity: sha512-4o6oPMDvQv+9gMi8rE6gWmsOjtUZUYIJHv7EB+GblyYdi8U6OqLl8rhHWIUZSL1dUU2dPwTdTgybCKf9EjIrQg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@tanstack/table-core@8.21.3': resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} engines: {node: '>=12'} + '@tanstack/virtual-core@3.13.13': + resolution: {integrity: sha512-uQFoSdKKf5S8k51W5t7b2qpfkyIbdHMzAn+AMQvHPxKUPeo1SsGaA4JRISQT87jm28b7z8OEqPcg1IOZagQHcA==} + '@tanstack/vite-config@0.2.0': resolution: {integrity: sha512-WpL1C9iR5/U7g3GpvHIssN5QvKnDnWhW05BQhaD6bAqoPCkQyBepxUF8ZRO4IGZRGVAZeMVqTbUA05BAQH/88g==} engines: {node: '>=18'} @@ -13524,8 +13536,16 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) + '@tanstack/react-virtual@3.13.13(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@tanstack/virtual-core': 3.13.13 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + '@tanstack/table-core@8.21.3': {} + '@tanstack/virtual-core@3.13.13': {} + '@tanstack/vite-config@0.2.0(@types/node@22.17.1)(rollup@4.40.2)(typescript@5.9.3)(vite@6.3.5(@types/node@22.17.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.3)(yaml@2.8.0))': dependencies: rollup-plugin-preserve-directives: 0.4.0(rollup@4.40.2)