From 5173320bb508918a26136776013366d679e06cdc Mon Sep 17 00:00:00 2001 From: Lakee Sivaraya Date: Fri, 16 Jan 2026 11:50:44 -0800 Subject: [PATCH] clean comments --- apps/sim/app/api/table/[tableId]/route.ts | 45 +----- .../api/table/[tableId]/rows/[rowId]/route.ts | 73 +-------- .../sim/app/api/table/[tableId]/rows/route.ts | 148 +----------------- .../api/table/[tableId]/rows/upsert/route.ts | 24 +-- apps/sim/app/api/table/route.ts | 66 +------- apps/sim/app/api/table/utils.ts | 4 +- .../[tableId]/components/table-action-bar.tsx | 7 - .../components/table-query-builder.tsx | 2 - .../components/cell-viewer-modal.tsx | 10 -- .../table-data-viewer/components/index.ts | 4 - .../components/row-context-menu.tsx | 10 -- .../components/schema-viewer-modal.tsx | 10 -- .../components/table-body-states.tsx | 10 -- .../components/table-cell-renderer.tsx | 10 -- .../components/table-header-bar.tsx | 10 -- .../components/table-pagination.tsx | 10 -- .../[tableId]/table-data-viewer/constants.ts | 3 - .../table-data-viewer/hooks/index.ts | 4 - .../hooks/use-context-menu.ts | 5 - .../hooks/use-row-selection.ts | 23 --- .../table-data-viewer/hooks/use-table-data.ts | 1 - .../table-data-viewer/table-data-viewer.tsx | 28 ---- .../[tableId]/table-data-viewer/types.ts | 16 -- .../[tableId]/table-data-viewer/utils.ts | 6 - .../tables/components/create-table-modal.tsx | 38 ----- .../tables/components/table-card.tsx | 6 - .../workspace/[workspaceId]/tables/layout.tsx | 3 - .../workspace/[workspaceId]/tables/tables.tsx | 29 ---- 28 files changed, 15 insertions(+), 590 deletions(-) diff --git a/apps/sim/app/api/table/[tableId]/route.ts b/apps/sim/app/api/table/[tableId]/route.ts index b87419260..02b66fe40 100644 --- a/apps/sim/app/api/table/[tableId]/route.ts +++ b/apps/sim/app/api/table/[tableId]/route.ts @@ -13,47 +13,15 @@ import { const logger = createLogger('TableDetailAPI') -/** - * Zod schema for validating get table requests. - * - * The workspaceId is required and validated against the table. - */ const GetTableSchema = z.object({ workspaceId: z.string().min(1, 'Workspace ID is required'), }) -/** - * Route params for table detail endpoints. - */ interface TableRouteParams { params: Promise<{ tableId: string }> } -/** - * GET /api/table/[tableId]?workspaceId=xxx - * - * Retrieves details for a specific table. - * - * @param request - The incoming HTTP request - * @param context - Route context containing tableId param - * @returns JSON response with table details or error - * - * @example Response: - * ```json - * { - * "success": true, - * "data": { - * "table": { - * "id": "tbl_abc123", - * "name": "customers", - * "schema": { "columns": [...] }, - * "rowCount": 150, - * "maxRows": 10000 - * } - * } - * } - * ``` - */ +/** GET /api/table/[tableId] - Retrieves a single table's details. */ export async function GET(request: NextRequest, { params }: TableRouteParams) { const requestId = generateRequestId() const { tableId } = await params @@ -70,7 +38,6 @@ export async function GET(request: NextRequest, { params }: TableRouteParams) { workspaceId: searchParams.get('workspaceId'), }) - // Check table access (similar to knowledge base access control) const accessCheck = await checkTableAccess(tableId, authResult.userId) if (!accessCheck.hasAccess) { @@ -92,7 +59,6 @@ export async function GET(request: NextRequest, { params }: TableRouteParams) { return NextResponse.json({ error: 'Invalid workspace ID' }, { status: 400 }) } - // Get table using service layer const table = await getTableById(tableId) if (!table) { @@ -139,11 +105,7 @@ export async function GET(request: NextRequest, { params }: TableRouteParams) { } } -/** - * DELETE /api/table/[tableId]?workspaceId=xxx - * - * Deletes a table and all its rows (hard delete, requires write access). - */ +/** DELETE /api/table/[tableId] - Deletes a table and all its rows. */ export async function DELETE(request: NextRequest, { params }: TableRouteParams) { const requestId = generateRequestId() const { tableId } = await params @@ -160,7 +122,6 @@ export async function DELETE(request: NextRequest, { params }: TableRouteParams) workspaceId: searchParams.get('workspaceId'), }) - // Check table write access (similar to knowledge base write access control) const accessCheck = await checkTableWriteAccess(tableId, authResult.userId) if (!accessCheck.hasAccess) { @@ -174,7 +135,6 @@ export async function DELETE(request: NextRequest, { params }: TableRouteParams) return NextResponse.json({ error: 'Access denied' }, { status: 403 }) } - // Security check: verify workspaceId matches the table's workspace const isValidWorkspace = await verifyTableWorkspace(tableId, validated.workspaceId) if (!isValidWorkspace) { logger.warn( @@ -183,7 +143,6 @@ export async function DELETE(request: NextRequest, { params }: TableRouteParams) return NextResponse.json({ error: 'Invalid workspace ID' }, { status: 400 }) } - // Soft delete table using service layer await deleteTable(tableId, requestId) return NextResponse.json({ diff --git a/apps/sim/app/api/table/[tableId]/rows/[rowId]/route.ts b/apps/sim/app/api/table/[tableId]/rows/[rowId]/route.ts index 57faf66d6..0b4e81f8e 100644 --- a/apps/sim/app/api/table/[tableId]/rows/[rowId]/route.ts +++ b/apps/sim/app/api/table/[tableId]/rows/[rowId]/route.ts @@ -17,61 +17,24 @@ import { const logger = createLogger('TableRowAPI') -/** - * Zod schema for validating get row requests. - * - * The workspaceId is required and validated against the table. - */ const GetRowSchema = z.object({ workspaceId: z.string().min(1, 'Workspace ID is required'), }) -/** - * Zod schema for validating update row requests. - */ const UpdateRowSchema = z.object({ workspaceId: z.string().min(1, 'Workspace ID is required'), data: z.record(z.unknown(), { required_error: 'Row data is required' }), }) -/** - * Zod schema for validating delete row requests. - */ const DeleteRowSchema = z.object({ workspaceId: z.string().min(1, 'Workspace ID is required'), }) -/** - * Route params for single row endpoints. - */ interface RowRouteParams { params: Promise<{ tableId: string; rowId: string }> } -/** - * GET /api/table/[tableId]/rows/[rowId]?workspaceId=xxx - * - * Retrieves a single row by its ID. - * - * @param request - The incoming HTTP request - * @param context - Route context containing tableId and rowId params - * @returns JSON response with row data or error - * - * @example Response: - * ```json - * { - * "success": true, - * "data": { - * "row": { - * "id": "row_abc123", - * "data": { "name": "John", "email": "john@example.com" }, - * "createdAt": "2024-01-01T00:00:00.000Z", - * "updatedAt": "2024-01-01T00:00:00.000Z" - * } - * } - * } - * ``` - */ +/** GET /api/table/[tableId]/rows/[rowId] - Retrieves a single row. */ export async function GET(request: NextRequest, { params }: RowRouteParams) { const requestId = generateRequestId() const { tableId, rowId } = await params @@ -87,7 +50,6 @@ export async function GET(request: NextRequest, { params }: RowRouteParams) { workspaceId: searchParams.get('workspaceId'), }) - // Check table access (centralized access control) const accessCheck = await checkTableAccess(tableId, authResult.userId) if (!accessCheck.hasAccess) { @@ -101,7 +63,6 @@ export async function GET(request: NextRequest, { params }: RowRouteParams) { return NextResponse.json({ error: 'Access denied' }, { status: 403 }) } - // Security check: verify workspaceId matches the table's workspace const actualWorkspaceId = validated.workspaceId const isValidWorkspace = await verifyTableWorkspace(tableId, validated.workspaceId) if (!isValidWorkspace) { @@ -111,7 +72,6 @@ export async function GET(request: NextRequest, { params }: RowRouteParams) { return NextResponse.json({ error: 'Invalid workspace ID' }, { status: 400 }) } - // Get row const [row] = await db .select({ id: userTableRows.id, @@ -159,11 +119,7 @@ export async function GET(request: NextRequest, { params }: RowRouteParams) { } } -/** - * PATCH /api/table/[tableId]/rows/[rowId] - * - * Updates an existing row with new data (full replacement, not partial). - */ +/** PATCH /api/table/[tableId]/rows/[rowId] - Updates a single row. */ export async function PATCH(request: NextRequest, { params }: RowRouteParams) { const requestId = generateRequestId() const { tableId, rowId } = await params @@ -177,11 +133,9 @@ export async function PATCH(request: NextRequest, { params }: RowRouteParams) { const body: unknown = await request.json() const validated = UpdateRowSchema.parse(body) - // Check table write access const accessResult = await checkAccessOrRespond(tableId, authResult.userId, requestId, 'write') if (accessResult instanceof NextResponse) return accessResult - // Security check: verify workspaceId matches the table's workspace const actualWorkspaceId = validated.workspaceId const isValidWorkspace = await verifyTableWorkspace(tableId, validated.workspaceId) if (!isValidWorkspace) { @@ -191,7 +145,6 @@ export async function PATCH(request: NextRequest, { params }: RowRouteParams) { return NextResponse.json({ error: 'Invalid workspace ID' }, { status: 400 }) } - // Get table definition const table = await getTableById(tableId) if (!table) { return NextResponse.json({ error: 'Table not found' }, { status: 404 }) @@ -199,7 +152,6 @@ export async function PATCH(request: NextRequest, { params }: RowRouteParams) { const rowData = validated.data as RowData - // Validate row data (size, schema, unique constraints) const validation = await validateRowData({ rowData, schema: table.schema as TableSchema, @@ -208,7 +160,6 @@ export async function PATCH(request: NextRequest, { params }: RowRouteParams) { }) if (!validation.valid) return validation.response - // Update row const now = new Date() const [updatedRow] = await db @@ -257,22 +208,7 @@ export async function PATCH(request: NextRequest, { params }: RowRouteParams) { } } -/** - * DELETE /api/table/[tableId]/rows/[rowId] - * - * Permanently deletes a single row. - * - * @param request - The incoming HTTP request - * @param context - Route context containing tableId and rowId params - * @returns JSON response confirming deletion or error - * - * @example Request body: - * ```json - * { - * "workspaceId": "ws_123" - * } - * ``` - */ +/** DELETE /api/table/[tableId]/rows/[rowId] - Deletes a single row. */ export async function DELETE(request: NextRequest, { params }: RowRouteParams) { const requestId = generateRequestId() const { tableId, rowId } = await params @@ -286,11 +222,9 @@ export async function DELETE(request: NextRequest, { params }: RowRouteParams) { const body: unknown = await request.json() const validated = DeleteRowSchema.parse(body) - // Check table write access const accessResult = await checkAccessOrRespond(tableId, authResult.userId, requestId, 'write') if (accessResult instanceof NextResponse) return accessResult - // Security check: verify workspaceId matches the table's workspace const actualWorkspaceId = validated.workspaceId const isValidWorkspace = await verifyTableWorkspace(tableId, validated.workspaceId) if (!isValidWorkspace) { @@ -300,7 +234,6 @@ export async function DELETE(request: NextRequest, { params }: RowRouteParams) { return NextResponse.json({ error: 'Invalid workspace ID' }, { status: 400 }) } - // Delete row const [deletedRow] = await db .delete(userTableRows) .where( diff --git a/apps/sim/app/api/table/[tableId]/rows/route.ts b/apps/sim/app/api/table/[tableId]/rows/route.ts index 95152340d..85dca0763 100644 --- a/apps/sim/app/api/table/[tableId]/rows/route.ts +++ b/apps/sim/app/api/table/[tableId]/rows/route.ts @@ -21,17 +21,11 @@ import { checkAccessOrRespond, checkAccessWithFullTable, verifyTableWorkspace } const logger = createLogger('TableRowsAPI') -/** - * Zod schema for inserting a single row into a table. - * - * The workspaceId is required and validated against the table. - */ const InsertRowSchema = z.object({ workspaceId: z.string().min(1, 'Workspace ID is required'), data: z.record(z.unknown(), { required_error: 'Row data is required' }), }) -/** Zod schema for batch inserting multiple rows (max 1000 per batch) */ const BatchInsertRowsSchema = z.object({ workspaceId: z.string().min(1, 'Workspace ID is required'), rows: z @@ -40,9 +34,6 @@ const BatchInsertRowsSchema = z.object({ .max(1000, 'Cannot insert more than 1000 rows per batch'), }) -/** - * Zod schema for querying rows with filtering, sorting, and pagination. - */ const QueryRowsSchema = z.object({ workspaceId: z.string().min(1, 'Workspace ID is required'), filter: z.record(z.unknown()).optional(), @@ -62,7 +53,6 @@ const QueryRowsSchema = z.object({ .default(0), }) -/** Zod schema for updating multiple rows by filter (max 1000 per operation) */ const UpdateRowsByFilterSchema = z.object({ workspaceId: z.string().min(1, 'Workspace ID is required'), filter: z.record(z.unknown(), { required_error: 'Filter criteria is required' }), @@ -75,7 +65,6 @@ const UpdateRowsByFilterSchema = z.object({ .optional(), }) -/** Zod schema for deleting multiple rows by filter (max 1000 per operation) */ const DeleteRowsByFilterSchema = z.object({ workspaceId: z.string().min(1, 'Workspace ID is required'), filter: z.record(z.unknown(), { required_error: 'Filter criteria is required' }), @@ -87,24 +76,10 @@ const DeleteRowsByFilterSchema = z.object({ .optional(), }) -/** - * Route params for table row endpoints. - */ interface TableRowsRouteParams { params: Promise<{ tableId: string }> } -/** - * Handles batch insertion of multiple rows into a table. - * - * @param requestId - Request tracking ID for logging - * @param tableId - ID of the target table - * @param body - Validated batch insert request body - * @param userId - ID of the authenticated user - * @returns NextResponse with inserted rows or error - * - * @internal - */ async function handleBatchInsert( requestId: string, tableId: string, @@ -113,13 +88,11 @@ async function handleBatchInsert( ): Promise { const validated = BatchInsertRowsSchema.parse(body) - // Check table write access and get full table data in one query const accessResult = await checkAccessWithFullTable(tableId, userId, requestId, 'write') if (accessResult instanceof NextResponse) return accessResult const table = accessResult.table - // Security check: verify workspaceId matches the table's workspace if (validated.workspaceId !== table.workspaceId) { logger.warn( `[${requestId}] Workspace ID mismatch for table ${tableId}. Provided: ${validated.workspaceId}, Actual: ${table.workspaceId}` @@ -129,7 +102,6 @@ async function handleBatchInsert( const workspaceId = validated.workspaceId - // Check row count limit const remainingCapacity = table.maxRows - table.rowCount if (remainingCapacity < validated.rows.length) { return NextResponse.json( @@ -140,7 +112,6 @@ async function handleBatchInsert( ) } - // Validate all rows (size, schema, unique constraints) const validation = await validateBatchRows({ rows: validated.rows as RowData[], schema: table.schema as TableSchema, @@ -148,7 +119,6 @@ async function handleBatchInsert( }) if (!validation.valid) return validation.response - // Insert all rows const now = new Date() const rowsToInsert = validated.rows.map((data) => ({ id: `row_${crypto.randomUUID().replace(/-/g, '')}`, @@ -179,35 +149,7 @@ async function handleBatchInsert( }) } -/** - * POST /api/table/[tableId]/rows - * - * Inserts a new row into the table. - * Supports both single row and batch insert (when `rows` array is provided). - * - * @param request - The incoming HTTP request - * @param context - Route context containing tableId param - * @returns JSON response with inserted row(s) or error - * - * @example Single row insert: - * ```json - * { - * "workspaceId": "ws_123", - * "data": { "name": "John", "email": "john@example.com" } - * } - * ``` - * - * @example Batch insert: - * ```json - * { - * "workspaceId": "ws_123", - * "rows": [ - * { "name": "John", "email": "john@example.com" }, - * { "name": "Jane", "email": "jane@example.com" } - * ] - * } - * ``` - */ +/** POST /api/table/[tableId]/rows - Inserts row(s). Supports single or batch insert. */ export async function POST(request: NextRequest, { params }: TableRowsRouteParams) { const requestId = generateRequestId() const { tableId } = await params @@ -220,7 +162,6 @@ export async function POST(request: NextRequest, { params }: TableRowsRouteParam const body: unknown = await request.json() - // Check if this is a batch insert if ( typeof body === 'object' && body !== null && @@ -235,10 +176,8 @@ export async function POST(request: NextRequest, { params }: TableRowsRouteParam ) } - // Single row insert const validated = InsertRowSchema.parse(body) - // Check table write access and get full table data in one query const accessResult = await checkAccessWithFullTable( tableId, authResult.userId, @@ -249,7 +188,6 @@ export async function POST(request: NextRequest, { params }: TableRowsRouteParam const table = accessResult.table - // Security check: verify workspaceId matches the table's workspace if (validated.workspaceId !== table.workspaceId) { logger.warn( `[${requestId}] Workspace ID mismatch for table ${tableId}. Provided: ${validated.workspaceId}, Actual: ${table.workspaceId}` @@ -260,7 +198,6 @@ export async function POST(request: NextRequest, { params }: TableRowsRouteParam const workspaceId = validated.workspaceId const rowData = validated.data as RowData - // Validate row data (size, schema, unique constraints) const validation = await validateRowData({ rowData, schema: table.schema as TableSchema, @@ -268,7 +205,6 @@ export async function POST(request: NextRequest, { params }: TableRowsRouteParam }) if (!validation.valid) return validation.response - // Check row count limit if (table.rowCount >= table.maxRows) { return NextResponse.json( { error: `Table row limit reached (${table.maxRows} rows max)` }, @@ -276,7 +212,6 @@ export async function POST(request: NextRequest, { params }: TableRowsRouteParam ) } - // Insert row const rowId = `row_${crypto.randomUUID().replace(/-/g, '')}` const now = new Date() @@ -320,20 +255,7 @@ export async function POST(request: NextRequest, { params }: TableRowsRouteParam } } -/** - * GET /api/table/[tableId]/rows?workspaceId=xxx&filter=...&sort=...&limit=100&offset=0 - * - * Queries rows from the table with filtering, sorting, and pagination. - * - * @param request - The incoming HTTP request with query params - * @param context - Route context containing tableId param - * @returns JSON response with matching rows and pagination info - * - * @example Query with filter: - * ``` - * GET /api/table/tbl_123/rows?filter={"status":{"eq":"active"}}&limit=50&offset=0 - * ``` - */ +/** GET /api/table/[tableId]/rows - Queries rows with filtering, sorting, and pagination. */ export async function GET(request: NextRequest, { params }: TableRowsRouteParams) { const requestId = generateRequestId() const { tableId } = await params @@ -373,7 +295,6 @@ export async function GET(request: NextRequest, { params }: TableRowsRouteParams offset, }) - // Check table access with full table data (includes schema for type-aware sorting) const accessResult = await checkAccessWithFullTable( tableId, authResult.userId, @@ -384,7 +305,6 @@ export async function GET(request: NextRequest, { params }: TableRowsRouteParams const { table } = accessResult - // Security check: verify workspaceId matches the table's workspace const isValidWorkspace = await verifyTableWorkspace(tableId, validated.workspaceId) if (!isValidWorkspace) { logger.warn( @@ -393,13 +313,11 @@ export async function GET(request: NextRequest, { params }: TableRowsRouteParams return NextResponse.json({ error: 'Invalid workspace ID' }, { status: 400 }) } - // Build base where conditions const baseConditions = [ eq(userTableRows.tableId, tableId), eq(userTableRows.workspaceId, validated.workspaceId), ] - // Add filter conditions if provided if (validated.filter) { const filterClause = buildFilterClause(validated.filter as Filter, 'user_table_rows') if (filterClause) { @@ -407,7 +325,6 @@ export async function GET(request: NextRequest, { params }: TableRowsRouteParams } } - // Build query with combined conditions let query = db .select({ id: userTableRows.id, @@ -418,7 +335,6 @@ export async function GET(request: NextRequest, { params }: TableRowsRouteParams .from(userTableRows) .where(and(...baseConditions)) - // Apply sorting with type-aware column handling if (validated.sort) { const schema = table.schema as TableSchema const sortClause = buildSortClause(validated.sort, 'user_table_rows', schema.columns) @@ -429,7 +345,6 @@ export async function GET(request: NextRequest, { params }: TableRowsRouteParams query = query.orderBy(userTableRows.createdAt) as typeof query } - // Get total count with same filters (without pagination) const countQuery = db .select({ count: sql`count(*)` }) .from(userTableRows) @@ -437,7 +352,6 @@ export async function GET(request: NextRequest, { params }: TableRowsRouteParams const [{ count: totalCount }] = await countQuery - // Apply pagination const rows = await query.limit(validated.limit).offset(validated.offset) logger.info( @@ -472,23 +386,7 @@ export async function GET(request: NextRequest, { params }: TableRowsRouteParams } } -/** - * PUT /api/table/[tableId]/rows - * - * Updates multiple rows matching filter criteria. - * - * @param request - The incoming HTTP request with filter and update data - * @param context - Route context containing tableId param - * @returns JSON response with count of updated rows - * - * @example Update all rows where status is "pending": - * ```json - * { - * "filter": { "status": { "eq": "pending" } }, - * "data": { "status": "processed" } - * } - * ``` - */ +/** PUT /api/table/[tableId]/rows - Updates rows matching filter criteria. */ export async function PUT(request: NextRequest, { params }: TableRowsRouteParams) { const requestId = generateRequestId() const { tableId } = await params @@ -502,7 +400,6 @@ export async function PUT(request: NextRequest, { params }: TableRowsRouteParams const body: unknown = await request.json() const validated = UpdateRowsByFilterSchema.parse(body) - // Check table write access and get full table data in one query const accessResult = await checkAccessWithFullTable( tableId, authResult.userId, @@ -513,7 +410,6 @@ export async function PUT(request: NextRequest, { params }: TableRowsRouteParams const table = accessResult.table - // Security check: verify workspaceId matches the table's workspace if (validated.workspaceId !== table.workspaceId) { logger.warn( `[${requestId}] Workspace ID mismatch for table ${tableId}. Provided: ${validated.workspaceId}, Actual: ${table.workspaceId}` @@ -523,7 +419,6 @@ export async function PUT(request: NextRequest, { params }: TableRowsRouteParams const updateData = validated.data as RowData - // Validate new data size const sizeValidation = validateRowSize(updateData) if (!sizeValidation.valid) { return NextResponse.json( @@ -532,19 +427,16 @@ export async function PUT(request: NextRequest, { params }: TableRowsRouteParams ) } - // Build base where conditions const baseConditions = [ eq(userTableRows.tableId, tableId), eq(userTableRows.workspaceId, validated.workspaceId), ] - // Add filter conditions const filterClause = buildFilterClause(validated.filter as Filter, 'user_table_rows') if (filterClause) { baseConditions.push(filterClause) } - // First, get the rows that match the filter to validate against schema let matchingRowsQuery = db .select({ id: userTableRows.id, @@ -572,12 +464,10 @@ export async function PUT(request: NextRequest, { params }: TableRowsRouteParams ) } - // Log warning for large operations but allow them if (matchingRows.length > TABLE_LIMITS.MAX_BULK_OPERATION_SIZE) { logger.warn(`[${requestId}] Updating ${matchingRows.length} rows. This may take some time.`) } - // Validate that merged data matches schema for each row for (const row of matchingRows) { const existingData = row.data as RowData const mergedData = { ...existingData, ...updateData } @@ -594,10 +484,8 @@ export async function PUT(request: NextRequest, { params }: TableRowsRouteParams } } - // Check unique constraints if any unique columns exist const uniqueColumns = getUniqueColumns(table.schema as TableSchema) if (uniqueColumns.length > 0) { - // Fetch all rows (not just matching ones) to check for uniqueness const allRows = await db .select({ id: userTableRows.id, @@ -606,7 +494,6 @@ export async function PUT(request: NextRequest, { params }: TableRowsRouteParams .from(userTableRows) .where(eq(userTableRows.tableId, tableId)) - // Validate each updated row for unique constraints for (const row of matchingRows) { const existingData = row.data as RowData const mergedData = { ...existingData, ...updateData } @@ -614,7 +501,7 @@ export async function PUT(request: NextRequest, { params }: TableRowsRouteParams mergedData, table.schema as TableSchema, allRows.map((r) => ({ id: r.id, data: r.data as RowData })), - row.id // Exclude the current row being updated + row.id ) if (!uniqueValidation.valid) { @@ -630,13 +517,11 @@ export async function PUT(request: NextRequest, { params }: TableRowsRouteParams } } - // Update rows by merging existing data with new data in a transaction const now = new Date() await db.transaction(async (trx) => { let totalUpdated = 0 - // Process updates in batches for (let i = 0; i < matchingRows.length; i += TABLE_LIMITS.UPDATE_BATCH_SIZE) { const batch = matchingRows.slice(i, i + TABLE_LIMITS.UPDATE_BATCH_SIZE) const updatePromises = batch.map((row) => { @@ -684,22 +569,7 @@ export async function PUT(request: NextRequest, { params }: TableRowsRouteParams } } -/** - * DELETE /api/table/[tableId]/rows - * - * Deletes multiple rows matching filter criteria. - * - * @param request - The incoming HTTP request with filter criteria - * @param context - Route context containing tableId param - * @returns JSON response with count of deleted rows - * - * @example Delete all rows where seen is false: - * ```json - * { - * "filter": { "seen": { "eq": false } } - * } - * ``` - */ +/** DELETE /api/table/[tableId]/rows - Deletes rows matching filter criteria. */ export async function DELETE(request: NextRequest, { params }: TableRowsRouteParams) { const requestId = generateRequestId() const { tableId } = await params @@ -713,11 +583,9 @@ export async function DELETE(request: NextRequest, { params }: TableRowsRoutePar const body: unknown = await request.json() const validated = DeleteRowsByFilterSchema.parse(body) - // Check table write access const accessResult = await checkAccessOrRespond(tableId, authResult.userId, requestId, 'write') if (accessResult instanceof NextResponse) return accessResult - // Security check: verify workspaceId matches the table's workspace const isValidWorkspace = await verifyTableWorkspace(tableId, validated.workspaceId) if (!isValidWorkspace) { logger.warn( @@ -726,19 +594,16 @@ export async function DELETE(request: NextRequest, { params }: TableRowsRoutePar return NextResponse.json({ error: 'Invalid workspace ID' }, { status: 400 }) } - // Build base where conditions const baseConditions = [ eq(userTableRows.tableId, tableId), eq(userTableRows.workspaceId, validated.workspaceId), ] - // Add filter conditions const filterClause = buildFilterClause(validated.filter as Filter, 'user_table_rows') if (filterClause) { baseConditions.push(filterClause) } - // Get matching rows first (for reporting and limit enforcement) let matchingRowsQuery = db .select({ id: userTableRows.id }) .from(userTableRows) @@ -763,18 +628,15 @@ export async function DELETE(request: NextRequest, { params }: TableRowsRoutePar ) } - // Log warning for large operations but allow them if (matchingRows.length > TABLE_LIMITS.DELETE_BATCH_SIZE) { logger.warn(`[${requestId}] Deleting ${matchingRows.length} rows. This may take some time.`) } - // Delete the matching rows in a transaction to ensure atomicity const rowIds = matchingRows.map((r) => r.id) await db.transaction(async (trx) => { let totalDeleted = 0 - // Delete rows in batches to avoid stack overflow for (let i = 0; i < rowIds.length; i += TABLE_LIMITS.DELETE_BATCH_SIZE) { const batch = rowIds.slice(i, i + TABLE_LIMITS.DELETE_BATCH_SIZE) await trx.delete(userTableRows).where( diff --git a/apps/sim/app/api/table/[tableId]/rows/upsert/route.ts b/apps/sim/app/api/table/[tableId]/rows/upsert/route.ts index 84dd7331a..a42e45d00 100644 --- a/apps/sim/app/api/table/[tableId]/rows/upsert/route.ts +++ b/apps/sim/app/api/table/[tableId]/rows/upsert/route.ts @@ -12,25 +12,16 @@ import { checkAccessOrRespond, getTableById, verifyTableWorkspace } from '../../ const logger = createLogger('TableUpsertAPI') -/** Zod schema for upsert requests - inserts new row or updates if unique fields match */ const UpsertRowSchema = z.object({ workspaceId: z.string().min(1, 'Workspace ID is required'), data: z.record(z.unknown(), { required_error: 'Row data is required' }), }) -/** - * Route params for upsert endpoint. - */ interface UpsertRouteParams { params: Promise<{ tableId: string }> } -/** - * POST /api/table/[tableId]/rows/upsert - * - * Inserts or updates a row based on unique column constraints. - * Requires at least one unique column in the table schema. - */ +/** POST /api/table/[tableId]/rows/upsert - Inserts or updates based on unique columns. */ export async function POST(request: NextRequest, { params }: UpsertRouteParams) { const requestId = generateRequestId() const { tableId } = await params @@ -44,11 +35,9 @@ export async function POST(request: NextRequest, { params }: UpsertRouteParams) const body: unknown = await request.json() const validated = UpsertRowSchema.parse(body) - // Check table write access const accessResult = await checkAccessOrRespond(tableId, authResult.userId, requestId, 'write') if (accessResult instanceof NextResponse) return accessResult - // Security check: If workspaceId is provided, verify it matches the table's workspace const actualWorkspaceId = validated.workspaceId || accessResult.table.workspaceId if (validated.workspaceId) { const isValidWorkspace = await verifyTableWorkspace(tableId, validated.workspaceId) @@ -60,7 +49,6 @@ export async function POST(request: NextRequest, { params }: UpsertRouteParams) } } - // Get table definition const table = await getTableById(tableId) if (!table) { return NextResponse.json({ error: 'Table not found' }, { status: 404 }) @@ -69,16 +57,14 @@ export async function POST(request: NextRequest, { params }: UpsertRouteParams) const schema = table.schema as TableSchema const rowData = validated.data as RowData - // Validate row data (size and schema only - unique constraints handled by upsert logic) const validation = await validateRowData({ rowData, schema, tableId, - checkUnique: false, // Upsert uses unique columns differently - to find existing rows + checkUnique: false, }) if (!validation.valid) return validation.response - // Get unique columns for upsert matching const uniqueColumns = getUniqueColumns(schema) if (uniqueColumns.length === 0) { @@ -91,7 +77,6 @@ export async function POST(request: NextRequest, { params }: UpsertRouteParams) ) } - // Build filter to find existing row by unique fields const uniqueFilters = uniqueColumns.map((col) => { const value = rowData[col.name] if (value === undefined || value === null) { @@ -100,7 +85,6 @@ export async function POST(request: NextRequest, { params }: UpsertRouteParams) return sql`${userTableRows.data}->>${col.name} = ${String(value)}` }) - // Filter out null conditions (for optional unique fields that weren't provided) const validUniqueFilters = uniqueFilters.filter((f): f is Exclude => f !== null) if (validUniqueFilters.length === 0) { @@ -112,7 +96,6 @@ export async function POST(request: NextRequest, { params }: UpsertRouteParams) ) } - // Find existing row with matching unique field(s) const [existingRow] = await db .select() .from(userTableRows) @@ -127,10 +110,8 @@ export async function POST(request: NextRequest, { params }: UpsertRouteParams) const now = new Date() - // Perform upsert in a transaction to ensure atomicity const result = await db.transaction(async (trx) => { if (existingRow) { - // Update existing row const [updatedRow] = await trx .update(userTableRows) .set({ @@ -146,7 +127,6 @@ export async function POST(request: NextRequest, { params }: UpsertRouteParams) } } - // Insert new row const [insertedRow] = await trx .insert(userTableRows) .values({ diff --git a/apps/sim/app/api/table/route.ts b/apps/sim/app/api/table/route.ts index 952dd0739..70c2029de 100644 --- a/apps/sim/app/api/table/route.ts +++ b/apps/sim/app/api/table/route.ts @@ -11,11 +11,6 @@ import { normalizeColumn } from './utils' const logger = createLogger('TableAPI') -/** - * Zod schema for validating a table column definition. - * - * Columns must have a name, type, and optional required/unique flags. - */ const ColumnSchema = z.object({ name: z .string() @@ -37,11 +32,6 @@ const ColumnSchema = z.object({ unique: z.boolean().optional().default(false), }) -/** - * Zod schema for validating create table requests. - * - * Requires a name, schema with columns, and workspace ID. - */ const CreateTableSchema = z.object({ name: z .string() @@ -73,30 +63,15 @@ const CreateTableSchema = z.object({ workspaceId: z.string().min(1, 'Workspace ID is required'), }) -/** - * Zod schema for validating list tables requests. - */ const ListTablesSchema = z.object({ workspaceId: z.string().min(1, 'Workspace ID is required'), }) -/** - * Result of a workspace access check. - */ interface WorkspaceAccessResult { - /** Whether the user has any access to the workspace */ hasAccess: boolean - /** Whether the user can write (modify tables) in the workspace */ canWrite: boolean } -/** - * Checks if a user has access to a workspace and determines their permission level. - * - * @param workspaceId - The workspace to check access for - * @param userId - The user requesting access - * @returns Access result with read and write permissions - */ async function checkWorkspaceAccess( workspaceId: string, userId: string @@ -114,12 +89,10 @@ async function checkWorkspaceAccess( return { hasAccess: false, canWrite: false } } - // Owner has full access if (workspaceData.ownerId === userId) { return { hasAccess: true, canWrite: true } } - // Check permissions const [permission] = await db .select({ permissionType: permissions.permissionType, @@ -146,29 +119,7 @@ async function checkWorkspaceAccess( } } -/** - * POST /api/table - * - * Creates a new user-defined table in a workspace. - * - * @param request - The incoming HTTP request containing table definition - * @returns JSON response with the created table or error - * - * @example Request body: - * ```json - * { - * "name": "customers", - * "description": "Customer records", - * "workspaceId": "ws_123", - * "schema": { - * "columns": [ - * { "name": "email", "type": "string", "required": true, "unique": true }, - * { "name": "name", "type": "string", "required": true } - * ] - * } - * } - * ``` - */ +/** POST /api/table - Creates a new user-defined table. */ export async function POST(request: NextRequest) { const requestId = generateRequestId() @@ -181,7 +132,6 @@ export async function POST(request: NextRequest) { const body: unknown = await request.json() const params = CreateTableSchema.parse(body) - // Check workspace access const { hasAccess, canWrite } = await checkWorkspaceAccess( params.workspaceId, authResult.userId @@ -191,12 +141,10 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: 'Access denied' }, { status: 403 }) } - // Normalize schema to ensure all fields have explicit defaults const normalizedSchema: TableSchema = { columns: params.schema.columns.map(normalizeColumn), } - // Create table using service layer const table = await createTable( { name: params.name, @@ -238,7 +186,6 @@ export async function POST(request: NextRequest) { ) } - // Handle service layer errors with specific messages if (error instanceof Error) { if ( error.message.includes('Invalid table name') || @@ -255,14 +202,7 @@ export async function POST(request: NextRequest) { } } -/** - * GET /api/table?workspaceId=xxx - * - * Lists all tables in a workspace. - * - * @param request - The incoming HTTP request with workspaceId query param - * @returns JSON response with array of tables or error - */ +/** GET /api/table - Lists all tables in a workspace. */ export async function GET(request: NextRequest) { const requestId = generateRequestId() @@ -285,14 +225,12 @@ export async function GET(request: NextRequest) { const params = validation.data - // Check workspace access const { hasAccess } = await checkWorkspaceAccess(params.workspaceId, authResult.userId) if (!hasAccess) { return NextResponse.json({ error: 'Access denied' }, { status: 403 }) } - // Get tables using service layer const tables = await listTables(params.workspaceId) logger.info(`[${requestId}] Listed ${tables.length} tables in workspace ${params.workspaceId}`) diff --git a/apps/sim/app/api/table/utils.ts b/apps/sim/app/api/table/utils.ts index c9d190a7a..e5b5f3435 100644 --- a/apps/sim/app/api/table/utils.ts +++ b/apps/sim/app/api/table/utils.ts @@ -10,9 +10,7 @@ const logger = createLogger('TableUtils') type PermissionLevel = 'read' | 'write' | 'admin' -/** - * @deprecated Use TableDefinition from '@/lib/table' instead - */ +/** @deprecated Use TableDefinition from '@/lib/table' instead */ export type TableData = TableDefinition export interface TableAccessResult { diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-action-bar.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-action-bar.tsx index 8522b8cac..a8bbfe054 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-action-bar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-action-bar.tsx @@ -9,13 +9,6 @@ interface TableActionBarProps { onClearSelection: () => void } -/** - * Action bar displayed when rows are selected in the table. - * - * Shows the count of selected rows and provides actions for - * bulk operations like deletion. - * - */ export function TableActionBar({ selectedCount, onDelete, onClearSelection }: TableActionBarProps) { return (
diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-query-builder.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-query-builder.tsx index ddf3d7aba..499a2e271 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-query-builder.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-query-builder.tsx @@ -9,7 +9,6 @@ import { useFilterBuilder } from '@/lib/table/filters/use-builder' import { filterRulesToFilter, sortRuleToSort } from '@/lib/table/filters/utils' import type { ColumnDefinition, Filter, Sort } from '@/lib/table/types' -/** Query result containing API-ready filter and sort objects. */ export interface BuilderQueryResult { filter: Filter | null sort: Sort | null @@ -24,7 +23,6 @@ interface TableQueryBuilderProps { isLoading?: boolean } -/** Visual query builder for filtering and sorting table data. */ export function TableQueryBuilder({ columns, onApply, diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/cell-viewer-modal.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/cell-viewer-modal.tsx index a0babd8d3..5a9f7f433 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/cell-viewer-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/cell-viewer-modal.tsx @@ -1,7 +1,3 @@ -/** - * Modal for viewing cell details. - */ - import { Copy, X } from 'lucide-react' import { Badge, Button, Modal, ModalBody, ModalContent } from '@/components/emcn' import type { CellViewerData } from '../types' @@ -13,12 +9,6 @@ interface CellViewerModalProps { copied: boolean } -/** - * Displays cell value details in a modal. - * - * @param props - Component props - * @returns Cell viewer modal or null if no cell is selected - */ export function CellViewerModal({ cellViewer, onClose, onCopy, copied }: CellViewerModalProps) { if (!cellViewer) return null diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/index.ts b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/index.ts index fe3508758..7658c1d17 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/index.ts +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/index.ts @@ -1,7 +1,3 @@ -/** - * Table data viewer sub-components. - */ - export * from './cell-viewer-modal' export * from './row-context-menu' export * from './schema-viewer-modal' diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/row-context-menu.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/row-context-menu.tsx index f0dc44e83..663a52977 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/row-context-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/row-context-menu.tsx @@ -1,7 +1,3 @@ -/** - * Context menu for row actions. - */ - import { Edit, Trash2 } from 'lucide-react' import { Popover, @@ -19,12 +15,6 @@ interface RowContextMenuProps { onDelete: () => void } -/** - * Renders a context menu for row actions. - * - * @param props - Component props - * @returns Row context menu popover - */ export function RowContextMenu({ contextMenu, onClose, onEdit, onDelete }: RowContextMenuProps) { return ( diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-body-states.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-body-states.tsx index 8cffbc108..d5c8cf874 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-body-states.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-body-states.tsx @@ -1,7 +1,3 @@ -/** - * Table body placeholder states (loading and empty). - */ - import { Plus } from 'lucide-react' import { Button, TableCell, TableRow } from '@/components/emcn' import { Skeleton } from '@/components/ui/skeleton' @@ -11,9 +7,6 @@ interface TableLoadingRowsProps { columns: ColumnDefinition[] } -/** - * Renders skeleton rows while table data is loading. - */ export function TableLoadingRows({ columns }: TableLoadingRowsProps) { return ( <> @@ -56,9 +49,6 @@ interface TableEmptyRowsProps { onAddRow: () => void } -/** - * Renders an empty state when no rows are present. - */ export function TableEmptyRows({ columnCount, hasFilter, onAddRow }: TableEmptyRowsProps) { return ( diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-cell-renderer.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-cell-renderer.tsx index 5019a4090..b9537e257 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-cell-renderer.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-cell-renderer.tsx @@ -1,7 +1,3 @@ -/** - * Cell value renderer for different column types. - */ - import type { ColumnDefinition } from '@/lib/table' import { STRING_TRUNCATE_LENGTH } from '../constants' import type { CellViewerData } from '../types' @@ -12,12 +8,6 @@ interface TableCellRendererProps { onCellClick: (columnName: string, value: unknown, type: CellViewerData['type']) => void } -/** - * Renders a cell value with appropriate formatting based on column type. - * - * @param props - Component props - * @returns Formatted cell content - */ export function TableCellRenderer({ value, column, onCellClick }: TableCellRendererProps) { const isNull = value === null || value === undefined diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-header-bar.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-header-bar.tsx index 7422739a8..d25afd26c 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-header-bar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-header-bar.tsx @@ -1,7 +1,3 @@ -/** - * Header bar for the table data viewer. - */ - import { Info, RefreshCw } from 'lucide-react' import { Badge, Button, Tooltip } from '@/components/emcn' import { Skeleton } from '@/components/ui/skeleton' @@ -15,12 +11,6 @@ interface TableHeaderBarProps { onRefresh: () => void } -/** - * Renders the header bar with navigation, title, and actions. - * - * @param props - Component props - * @returns Table header bar - */ export function TableHeaderBar({ tableName, totalCount, diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-pagination.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-pagination.tsx index 0ff8fd0ad..2c2f7aa1d 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-pagination.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/components/table-pagination.tsx @@ -1,7 +1,3 @@ -/** - * Pagination controls for the table. - */ - import { Button } from '@/components/emcn' interface TablePaginationProps { @@ -12,12 +8,6 @@ interface TablePaginationProps { onNextPage: () => void } -/** - * Renders pagination controls for navigating table pages. - * - * @param props - Component props - * @returns Table pagination controls or null if only one page - */ export function TablePagination({ currentPage, totalPages, diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/constants.ts b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/constants.ts index 45284bc59..86d773b9a 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/constants.ts +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/constants.ts @@ -1,5 +1,2 @@ -/** Number of rows to fetch per page */ export const ROWS_PER_PAGE = 100 - -/** Maximum length for string display before truncation */ export const STRING_TRUNCATE_LENGTH = 50 diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/index.ts b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/index.ts index 1a46391eb..df77547cc 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/index.ts +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/index.ts @@ -1,7 +1,3 @@ -/** - * Custom hooks for the table data viewer. - */ - export * from './use-context-menu' export * from './use-row-selection' export * from './use-table-data' diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/use-context-menu.ts b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/use-context-menu.ts index f63ae0e36..a5ca1b336 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/use-context-menu.ts +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/use-context-menu.ts @@ -8,11 +8,6 @@ interface UseContextMenuReturn { closeContextMenu: () => void } -/** - * Manages context menu state for row interactions. - * - * @returns Context menu state and handlers - */ export function useContextMenu(): UseContextMenuReturn { const [contextMenu, setContextMenu] = useState({ isOpen: false, diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/use-row-selection.ts b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/use-row-selection.ts index e08e6bb98..dec640547 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/use-row-selection.ts +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/use-row-selection.ts @@ -1,7 +1,3 @@ -/** - * Hook for managing row selection state. - */ - import { useCallback, useEffect, useState } from 'react' import type { TableRow } from '@/lib/table' @@ -12,19 +8,9 @@ interface UseRowSelectionReturn { clearSelection: () => void } -/** - * Manages row selection state and provides selection handlers. - * - * @param rows - The current rows to select from - * @returns Selection state and handlers - */ export function useRowSelection(rows: TableRow[]): UseRowSelectionReturn { const [selectedRows, setSelectedRows] = useState>(new Set()) - /** - * Filter out selected rows that are no longer in the current row set. - * This handles pagination, filtering, and data refresh scenarios. - */ useEffect(() => { setSelectedRows((prev) => { if (prev.size === 0) return prev @@ -37,9 +23,6 @@ export function useRowSelection(rows: TableRow[]): UseRowSelectionReturn { }) }, [rows]) - /** - * Toggles selection of all visible rows. - */ const handleSelectAll = useCallback(() => { if (selectedRows.size === rows.length) { setSelectedRows(new Set()) @@ -48,9 +31,6 @@ export function useRowSelection(rows: TableRow[]): UseRowSelectionReturn { } }, [rows, selectedRows.size]) - /** - * Toggles selection of a single row. - */ const handleSelectRow = useCallback((rowId: string) => { setSelectedRows((prev) => { const newSet = new Set(prev) @@ -63,9 +43,6 @@ export function useRowSelection(rows: TableRow[]): UseRowSelectionReturn { }) }, []) - /** - * Clears all selections. - */ const clearSelection = useCallback(() => { setSelectedRows(new Set()) }, []) diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/use-table-data.ts b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/use-table-data.ts index 1f5cadbcf..6edd4e6ec 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/use-table-data.ts +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/hooks/use-table-data.ts @@ -20,7 +20,6 @@ interface UseTableDataReturn { refetchRows: () => void } -/** Fetches table metadata and rows with filtering/sorting/pagination. */ export function useTableData({ workspaceId, tableId, diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/table-data-viewer.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/table-data-viewer.tsx index c8d003993..511e4b912 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/table-data-viewer.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/table-data-viewer.tsx @@ -28,7 +28,6 @@ import { import { useContextMenu, useRowSelection, useTableData } from './hooks' import type { CellViewerData } from './types' -/** Table data viewer with filtering, sorting, pagination, and CRUD operations. */ export function TableDataViewer() { const params = useParams() const router = useRouter() @@ -67,30 +66,18 @@ export function TableDataViewer() { const hasSelection = selectedCount > 0 const isAllSelected = rows.length > 0 && selectedCount === rows.length - /** - * Navigates back to the tables list. - */ const handleNavigateBack = useCallback(() => { router.push(`/workspace/${workspaceId}/tables`) }, [router, workspaceId]) - /** - * Opens the schema viewer modal. - */ const handleShowSchema = useCallback(() => { setShowSchemaModal(true) }, []) - /** - * Opens the add row modal. - */ const handleAddRow = useCallback(() => { setShowAddModal(true) }, []) - /** - * Applies new query options and resets pagination. - */ const handleApplyQueryOptions = useCallback( (options: QueryOptions) => { setQueryOptions(options) @@ -100,16 +87,10 @@ export function TableDataViewer() { [refetchRows] ) - /** - * Opens the delete modal for selected rows. - */ const handleDeleteSelected = useCallback(() => { setDeletingRows(Array.from(selectedRows)) }, [selectedRows]) - /** - * Handles edit action from context menu. - */ const handleContextMenuEdit = useCallback(() => { if (contextMenu.row) { setEditingRow(contextMenu.row) @@ -117,9 +98,6 @@ export function TableDataViewer() { closeContextMenu() }, [contextMenu.row, closeContextMenu]) - /** - * Handles delete action from context menu. - */ const handleContextMenuDelete = useCallback(() => { if (contextMenu.row) { setDeletingRows([contextMenu.row.id]) @@ -127,9 +105,6 @@ export function TableDataViewer() { closeContextMenu() }, [contextMenu.row, closeContextMenu]) - /** - * Copies the current cell value to clipboard. - */ const handleCopyCellValue = useCallback(async () => { if (cellViewer) { let text: string @@ -146,9 +121,6 @@ export function TableDataViewer() { } }, [cellViewer]) - /** - * Opens the cell viewer modal. - */ const handleCellClick = useCallback( (columnName: string, value: unknown, type: CellViewerData['type']) => { setCellViewer({ columnName, value, type }) diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/types.ts b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/types.ts index 61f55095e..172ee9b77 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/types.ts +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/types.ts @@ -1,29 +1,13 @@ -/** - * Type definitions for the table data viewer. - */ - import type { TableRow } from '@/lib/table' -/** - * Data for the cell viewer modal. - */ export interface CellViewerData { - /** Name of the column being viewed */ columnName: string - /** Value being displayed */ value: unknown - /** Display type for formatting */ type: 'json' | 'text' | 'date' | 'boolean' | 'number' } -/** - * State for the right-click context menu. - */ export interface ContextMenuState { - /** Whether the menu is visible */ isOpen: boolean - /** Screen position of the menu */ position: { x: number; y: number } - /** Row the menu was opened on */ row: TableRow | null } diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/utils.ts b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/utils.ts index b1a15cacb..7e6632f81 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/utils.ts +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table-data-viewer/utils.ts @@ -1,9 +1,3 @@ -/** - * Gets the badge variant for a column type. - * - * @param type - The column type - * @returns Badge variant name - */ export function getTypeBadgeVariant( type: string ): 'green' | 'blue' | 'purple' | 'orange' | 'teal' | 'gray' { diff --git a/apps/sim/app/workspace/[workspaceId]/tables/components/create-table-modal.tsx b/apps/sim/app/workspace/[workspaceId]/tables/components/create-table-modal.tsx index 81089c5a1..08cc4921b 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/components/create-table-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/components/create-table-modal.tsx @@ -37,13 +37,9 @@ const COLUMN_TYPE_OPTIONS: Array<{ value: ColumnDefinition['type']; label: strin ] interface ColumnWithId extends ColumnDefinition { - /** Stable ID for React key */ id: string } -/** - * Creates an empty column definition with default values and a stable ID. - */ function createEmptyColumn(): ColumnWithId { return { id: nanoid(), name: '', type: 'string', required: true, unique: false } } @@ -59,31 +55,16 @@ export function CreateTableModal({ isOpen, onClose }: CreateTableModalProps) { const createTable = useCreateTable(workspaceId) - /** - * Adds a new empty column to the schema. - */ const handleAddColumn = () => { setColumns([...columns, createEmptyColumn()]) } - /** - * Removes a column from the schema by ID. - * - * @param columnId - ID of the column to remove - */ const handleRemoveColumn = (columnId: string) => { if (columns.length > 1) { setColumns(columns.filter((col) => col.id !== columnId)) } } - /** - * Updates a column field by ID. - * - * @param columnId - ID of the column to update - * @param field - Field name to update - * @param value - New value for the field - */ const handleColumnChange = ( columnId: string, field: keyof ColumnDefinition, @@ -92,9 +73,6 @@ export function CreateTableModal({ isOpen, onClose }: CreateTableModalProps) { setColumns(columns.map((col) => (col.id === columnId ? { ...col, [field]: value } : col))) } - /** - * Validates and submits the form to create the table. - */ const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setError(null) @@ -140,9 +118,6 @@ export function CreateTableModal({ isOpen, onClose }: CreateTableModalProps) { } } - /** - * Resets all form fields to their initial state. - */ const resetForm = () => { setTableName('') setDescription('') @@ -150,9 +125,6 @@ export function CreateTableModal({ isOpen, onClose }: CreateTableModalProps) { setError(null) } - /** - * Handles modal close and resets form state. - */ const handleClose = () => { resetForm() onClose() @@ -282,23 +254,13 @@ export function CreateTableModal({ isOpen, onClose }: CreateTableModalProps) { ) } -/** - * Props for the ColumnRow component. - */ interface ColumnRowProps { - /** The column definition with stable ID */ column: ColumnWithId - /** Whether the remove button should be enabled */ isRemovable: boolean - /** Callback when a column field changes */ onChange: (columnId: string, field: keyof ColumnDefinition, value: string | boolean) => void - /** Callback to remove this column */ onRemove: (columnId: string) => void } -/** - * A single row in the column definition list. - */ function ColumnRow({ column, isRemovable, onChange, onRemove }: ColumnRowProps) { return (
diff --git a/apps/sim/app/workspace/[workspaceId]/tables/components/table-card.tsx b/apps/sim/app/workspace/[workspaceId]/tables/components/table-card.tsx index d2882f602..2d7929c63 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/components/table-card.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/components/table-card.tsx @@ -36,12 +36,6 @@ interface TableCardProps { workspaceId: string } -/** - * Card component for displaying a table summary. - * - * Shows table name, column/row counts, description, and provides - * actions for viewing schema and deleting the table. - */ export function TableCard({ table, workspaceId }: TableCardProps) { const router = useRouter() const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false) diff --git a/apps/sim/app/workspace/[workspaceId]/tables/layout.tsx b/apps/sim/app/workspace/[workspaceId]/tables/layout.tsx index 24395b4b3..035303ac2 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/layout.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/layout.tsx @@ -1,6 +1,3 @@ -/** - * Tables layout - applies sidebar padding for all table routes. - */ export default function TablesLayout({ children }: { children: React.ReactNode }) { return (
diff --git a/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx b/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx index 339d29aea..92fdae6a8 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx @@ -11,20 +11,6 @@ import { useDebounce } from '@/hooks/use-debounce' import { CreateTableModal } from './components/create-table-modal' import { TableCard } from './components/table-card' -/** - * Tables page component that displays a list of all tables in a workspace. - * - * @remarks - * This component provides functionality to: - * - View all tables in the workspace - * - Search tables by name or description - * - Create new tables (with write permission) - * - * @example - * ```tsx - * - * ``` - */ export function Tables() { const params = useParams() const workspaceId = params.workspaceId as string @@ -118,9 +104,6 @@ export function Tables() { ) } -/** - * Loading skeleton component for table cards. - */ function LoadingSkeletons() { return ( <> @@ -153,12 +136,6 @@ function LoadingSkeletons() { ) } -/** - * Error state component displayed when table loading fails. - * - * @param props - Component props - * @param props.error - The error that occurred - */ function ErrorState({ error }: { error: unknown }) { return (
@@ -172,12 +149,6 @@ function ErrorState({ error }: { error: unknown }) { ) } -/** - * Empty state component displayed when no tables exist or match the search. - * - * @param props - Component props - * @param props.hasSearchQuery - Whether a search query is active - */ function EmptyState({ hasSearchQuery }: { hasSearchQuery: boolean }) { return (