OperationItem.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  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 { EditCellContentPayload } from '@/state/table-editor-operation-queue.types'
  10. interface OperationItemProps {
  11. operationId: string
  12. tableId: number
  13. content: EditCellContentPayload
  14. }
  15. export const OperationItem = ({ operationId, tableId, content }: OperationItemProps) => {
  16. const { table, columnName, oldValue, newValue, rowIdentifiers } = content
  17. const tableSchema = table.schema
  18. const tableName = table.name
  19. const queryClient = useQueryClient()
  20. const { data: project } = useSelectedProjectQuery()
  21. const snap = useTableEditorStateSnapshot()
  22. const fullTableName = `${tableSchema}.${tableName}`
  23. const whereClause = Object.entries(rowIdentifiers)
  24. .map(([key, value]) => `${key} = ${formatOperationItemValue(value)}`)
  25. .join(', ')
  26. const formattedOldValue = formatOperationItemValue(oldValue)
  27. const formattedNewValue = formatOperationItemValue(newValue)
  28. const handleDelete = () => {
  29. // Remove the operation from the queue
  30. snap.removeOperation(operationId)
  31. // Invalidate the query to revert the optimistic update
  32. if (project) {
  33. queryClient.invalidateQueries({
  34. queryKey: tableRowKeys.tableRowsAndCount(project.ref, tableId),
  35. })
  36. }
  37. }
  38. return (
  39. <Card className="overflow-hidden">
  40. <CardHeader className="py-2 px-3 flex flex-row gap-2 space-y-0 items-center">
  41. <div className="min-w-0 flex-1">
  42. <code className="text-code-inline dark:bg-surface-300 dark:border-foreground-muted/50">
  43. {fullTableName}
  44. </code>
  45. <div className="text-xs text-foreground mt-1 ml-0.5">
  46. <span>{columnName}</span>
  47. <span className="text-foreground-muted mx-1.5">·</span>
  48. <span>where {whereClause}</span>
  49. </div>
  50. </div>
  51. <ButtonTooltip
  52. type="text"
  53. aria-label="Discard change"
  54. className="px-1.5"
  55. icon={<Undo2 />}
  56. onClick={handleDelete}
  57. tooltip={{
  58. content: {
  59. side: 'left',
  60. align: 'end',
  61. text: 'Discard change',
  62. },
  63. }}
  64. />
  65. </CardHeader>
  66. <CardContent className="py-2 px-3 font-mono text-xs">
  67. <div className="flex gap-2 py-0.5">
  68. <span className="text-destructive select-none font-medium">-</span>
  69. <span className="text-destructive truncate max-w-full" title={formattedOldValue}>
  70. {formattedOldValue}
  71. </span>
  72. </div>
  73. <div className="flex gap-2 py-0.5">
  74. <span className="text-brand-link select-none font-medium">+</span>
  75. <span className="text-brand-link truncate max-w-full" title={formattedNewValue}>
  76. {formattedNewValue}
  77. </span>
  78. </div>
  79. </CardContent>
  80. </Card>
  81. )
  82. }