| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- // @ts-nocheck
- import { QueryKey, useQueryClient } from '@tanstack/react-query'
- import { useCallback } from 'react'
- import { toast } from 'sonner'
- import type { PendingAddRow } from '../types'
- import type { SupaRow } from '@/components/grid/types'
- import {
- queueCellEditWithOptimisticUpdate,
- queueRowAddWithOptimisticUpdate,
- queueRowDeletesWithOptimisticUpdate,
- } from '@/components/grid/utils/queueOperationUtils'
- import { useIsQueueOperationsEnabled } from '@/components/interfaces/Account/Preferences/useDashboardSettings'
- import { isTableLike, type Entity } from '@/data/table-editor/table-editor-types'
- import { tableRowKeys } from '@/data/table-rows/keys'
- import { useTableRowCreateMutation } from '@/data/table-rows/table-row-create-mutation'
- import { useTableRowUpdateMutation } from '@/data/table-rows/table-row-update-mutation'
- import type { TableRowsData } from '@/data/table-rows/table-rows-query'
- import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
- import { useGetImpersonatedRoleState } from '@/state/role-impersonation-state'
- import { useTableEditorStateSnapshot } from '@/state/table-editor'
- import type { Dictionary } from '@/types'
- export interface EditCellParams {
- table: Entity
- tableId: number
- row: SupaRow
- rowIdentifiers: Dictionary<unknown>
- columnName: string
- oldValue: unknown
- newValue: unknown
- enumArrayColumns?: string[]
- /** When true, shows a success toast on non-queue save (used by side panel, not grid inline edits) */
- onSuccess?: () => void
- }
- export interface AddRowParams {
- table: Entity
- tableId: number
- rowData: PendingAddRow
- enumArrayColumns?: string[]
- }
- export interface UpdateRowParams {
- table: Entity
- tableId: number
- row: SupaRow
- rowIdentifiers: Dictionary<unknown>
- payload: Dictionary<unknown>
- enumArrayColumns?: string[]
- onSuccess?: () => void
- }
- export interface DeleteRowsParams {
- rows: SupaRow[]
- table: Entity
- allRowsSelected?: boolean
- totalRows?: number
- callback?: () => void
- }
- export function useTableRowOperations() {
- const isQueueEnabled = useIsQueueOperationsEnabled()
- const queryClient = useQueryClient()
- const { data: project } = useSelectedProjectQuery()
- const tableEditorSnap = useTableEditorStateSnapshot()
- const getImpersonatedRoleState = useGetImpersonatedRoleState()
- // Non-queue mutation for cell edits with optimistic updates
- const { mutateAsync: mutateUpdateTableRow, isPending: isEditPending } = useTableRowUpdateMutation(
- {
- async onMutate({ projectRef, table, configuration, payload }) {
- const primaryKeyColumns = new Set(Object.keys(configuration.identifiers))
- const queryKey = tableRowKeys.tableRows(projectRef, { table: { id: table.id } })
- await queryClient.cancelQueries({ queryKey })
- const previousRowsQueries = queryClient.getQueriesData<TableRowsData>({ queryKey })
- queryClient.setQueriesData<TableRowsData>({ queryKey }, (old) => {
- if (!old) return old
- return {
- rows: old.rows.map((row) => {
- if (
- Object.entries(row)
- .filter(([key]) => primaryKeyColumns.has(key))
- .every(([key, value]) => value === configuration.identifiers[key])
- ) {
- return { ...row, ...payload }
- }
- return row
- }),
- }
- })
- return { previousRowsQueries }
- },
- onError(error, _variables, context) {
- const { previousRowsQueries } = (context ?? { previousRowsQueries: [] }) as {
- previousRowsQueries: [QueryKey, TableRowsData | undefined][]
- }
- previousRowsQueries.forEach(([queryKey, previousRows]) => {
- if (previousRows) {
- queryClient.setQueriesData<TableRowsData>({ queryKey }, previousRows)
- }
- queryClient.invalidateQueries({ queryKey })
- })
- toast.error(error?.message ?? error)
- },
- }
- )
- // Non-queue mutation for row creation
- const { mutateAsync: mutateCreateTableRow } = useTableRowCreateMutation({
- onSuccess() {
- toast.success('Successfully created row')
- },
- })
- const editCell = useCallback(
- async (params: EditCellParams) => {
- if (isQueueEnabled) {
- queueCellEditWithOptimisticUpdate({
- queueOperation: tableEditorSnap.queueOperation,
- tableId: params.tableId,
- table: params.table,
- row: params.row,
- rowIdentifiers: params.rowIdentifiers,
- columnName: params.columnName,
- oldValue: params.oldValue,
- newValue: params.newValue,
- enumArrayColumns: params.enumArrayColumns,
- })
- return
- }
- if (!project) return
- const updatedData = { [params.columnName]: params.newValue }
- await mutateUpdateTableRow({
- projectRef: project.ref,
- connectionString: project.connectionString,
- table: params.table,
- configuration: { identifiers: params.rowIdentifiers },
- payload: updatedData,
- enumArrayColumns: params.enumArrayColumns ?? [],
- roleImpersonationState: getImpersonatedRoleState(),
- })
- params.onSuccess?.()
- },
- [isQueueEnabled, project, tableEditorSnap, mutateUpdateTableRow, getImpersonatedRoleState]
- )
- const updateRow = useCallback(
- async (params: UpdateRowParams) => {
- if (isQueueEnabled) {
- // Queue individual cell edits per changed column
- for (const columnName of Object.keys(params.payload)) {
- queueCellEditWithOptimisticUpdate({
- queueOperation: tableEditorSnap.queueOperation,
- tableId: params.tableId,
- table: params.table,
- row: params.row,
- rowIdentifiers: params.rowIdentifiers,
- columnName,
- oldValue: params.row[columnName],
- newValue: params.payload[columnName],
- enumArrayColumns: params.enumArrayColumns,
- })
- }
- return
- }
- if (!project) return
- await mutateUpdateTableRow({
- projectRef: project.ref,
- connectionString: project.connectionString,
- table: params.table,
- configuration: { identifiers: params.rowIdentifiers },
- payload: params.payload,
- enumArrayColumns: params.enumArrayColumns ?? [],
- roleImpersonationState: getImpersonatedRoleState(),
- })
- params.onSuccess?.()
- },
- [isQueueEnabled, project, tableEditorSnap, mutateUpdateTableRow, getImpersonatedRoleState]
- )
- const addRow = useCallback(
- async (params: AddRowParams) => {
- // Only queue if the table has primary keys (required for queue conflict resolution)
- const hasPrimaryKeys = isTableLike(params.table) && params.table.primary_keys.length > 0
- if (isQueueEnabled && hasPrimaryKeys) {
- queueRowAddWithOptimisticUpdate({
- queueOperation: tableEditorSnap.queueOperation,
- tableId: params.tableId,
- table: params.table,
- rowData: params.rowData,
- enumArrayColumns: params.enumArrayColumns,
- })
- return
- }
- if (!project) return
- await mutateCreateTableRow({
- projectRef: project.ref,
- connectionString: project.connectionString,
- table: params.table,
- payload: params.rowData,
- enumArrayColumns: params.enumArrayColumns ?? [],
- roleImpersonationState: getImpersonatedRoleState(),
- })
- },
- [isQueueEnabled, project, tableEditorSnap, mutateCreateTableRow, getImpersonatedRoleState]
- )
- const deleteRows = useCallback(
- (params: DeleteRowsParams) => {
- // When queue is enabled and not all rows are selected, queue the deletes
- if (isQueueEnabled && !params.allRowsSelected) {
- queueRowDeletesWithOptimisticUpdate({
- rows: params.rows,
- table: params.table,
- queueOperation: tableEditorSnap.queueOperation,
- projectRef: project?.ref,
- })
- params.callback?.()
- return
- }
- // Otherwise, open the confirmation dialog
- tableEditorSnap.onDeleteRows(params.rows, {
- allRowsSelected: params.allRowsSelected ?? false,
- numRows: params.allRowsSelected ? params.totalRows : params.rows.length,
- callback: params.callback,
- })
- },
- [isQueueEnabled, project, tableEditorSnap]
- )
- return {
- editCell,
- updateRow,
- addRow,
- deleteRows,
- isQueueEnabled,
- isEditPending,
- }
- }
|