AddRowOperationItem.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import { useQueryClient } from '@tanstack/react-query'
  2. import { Undo2 } from 'lucide-react'
  3. import { Card, CardContent, CardHeader } from 'ui'
  4. import { formatOperationItemValue } from './OperationQueueSidePanel.utils'
  5. import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
  6. import { tableRowKeys } from '@/data/table-rows/keys'
  7. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  8. import { useTableEditorStateSnapshot } from '@/state/table-editor'
  9. import { AddRowPayload } from '@/state/table-editor-operation-queue.types'
  10. interface AddRowOperationItemProps {
  11. operationId: string
  12. tableId: number
  13. content: AddRowPayload
  14. }
  15. export const AddRowOperationItem = ({
  16. operationId,
  17. tableId,
  18. content,
  19. }: AddRowOperationItemProps) => {
  20. const { table, rowData } = content
  21. const tableSchema = table.schema
  22. const tableName = table.name
  23. const queryClient = useQueryClient()
  24. const { data: project } = useSelectedProjectQuery()
  25. const snap = useTableEditorStateSnapshot()
  26. const fullTableName = `${tableSchema}.${tableName}`
  27. // Get first 3 column values for preview
  28. const columns = Object.entries(rowData).filter(([key]) => !key.startsWith('__') && key !== 'idx')
  29. const previewColumns = columns.slice(0, 3)
  30. const remainingCount = columns.length - previewColumns.length
  31. const handleDelete = () => {
  32. // Remove the operation from the queue
  33. snap.removeOperation(operationId)
  34. // Invalidate the query to revert the optimistic update
  35. if (project) {
  36. queryClient.invalidateQueries({
  37. queryKey: tableRowKeys.tableRowsAndCount(project.ref, tableId),
  38. })
  39. }
  40. }
  41. return (
  42. <Card className="overflow-hidden">
  43. <CardHeader className="py-2 px-3 flex flex-row gap-2 space-y-0 items-center">
  44. <div className="min-w-0 flex-1 flex items-start gap-2">
  45. <div className="min-w-0 flex-1">
  46. <code className="text-code-inline dark:bg-surface-300 dark:border-foreground-muted/50">
  47. {fullTableName}
  48. </code>
  49. <div className="text-xs text-foreground mt-1 ml-0.5">
  50. <span>New row</span>
  51. </div>
  52. </div>
  53. </div>
  54. <ButtonTooltip
  55. type="text"
  56. aria-label="Discard change"
  57. className="w-7"
  58. icon={<Undo2 />}
  59. onClick={handleDelete}
  60. tooltip={{
  61. content: {
  62. side: 'left',
  63. align: 'end',
  64. text: 'Discard change',
  65. },
  66. }}
  67. />
  68. </CardHeader>
  69. <CardContent className="py-2 px-3 font-mono text-xs text-brand-link">
  70. {previewColumns.length === 0 && (
  71. <span className="text-foreground-light">
  72. No data provided, default values will be used
  73. </span>
  74. )}
  75. {previewColumns.map(([key, value]) => (
  76. <div key={key} className="flex gap-2 py-0.5">
  77. <span className="text-brand-link select-none font-medium">+</span>
  78. <span className="shrink-0">{key}:</span>
  79. <span className="truncate min-w-0" title={formatOperationItemValue(value)}>
  80. {formatOperationItemValue(value)}
  81. </span>
  82. </div>
  83. ))}
  84. {remainingCount > 0 && (
  85. <div className="flex gap-2 py-0.5">
  86. <span className="text-brand-link select-none font-medium">+</span>
  87. <span>+{remainingCount} more column(s)</span>
  88. </div>
  89. )}
  90. </CardContent>
  91. </Card>
  92. )
  93. }