PolicyTemplates.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import type { PGPolicy } from '@supabase/pg-meta'
  2. import { Search } from 'lucide-react'
  3. import { useState } from 'react'
  4. import {
  5. Badge,
  6. cn,
  7. HoverCard,
  8. HoverCardContent,
  9. HoverCardTrigger,
  10. InputGroup,
  11. InputGroupAddon,
  12. InputGroupInput,
  13. } from 'ui'
  14. import { SimpleCodeBlock } from 'ui-patterns/SimpleCodeBlock'
  15. import {
  16. getGeneralPolicyTemplates,
  17. getQueuePolicyTemplates,
  18. getRealtimePolicyTemplates,
  19. } from '../PolicyEditorModal/PolicyEditorModal.constants'
  20. import { Markdown } from '@/components/interfaces/Markdown'
  21. import CardButton from '@/components/ui/CardButton'
  22. import CopyButton from '@/components/ui/CopyButton'
  23. import { NoSearchResults } from '@/components/ui/NoSearchResults'
  24. interface PolicyTemplatesProps {
  25. schema: string
  26. table: string
  27. selectedPolicy?: PGPolicy
  28. selectedTemplate?: string
  29. onSelectTemplate: (template: any) => void
  30. }
  31. export const PolicyTemplates = ({
  32. schema,
  33. table,
  34. selectedPolicy,
  35. selectedTemplate,
  36. onSelectTemplate,
  37. }: PolicyTemplatesProps) => {
  38. const [search, setSearch] = useState('')
  39. const templates =
  40. schema === 'realtime'
  41. ? getRealtimePolicyTemplates()
  42. : schema === 'pgmq'
  43. ? getQueuePolicyTemplates()
  44. : getGeneralPolicyTemplates(schema, table.length > 0 ? table : 'table_name')
  45. const baseTemplates =
  46. selectedPolicy !== undefined
  47. ? templates.filter((t) => t.command === selectedPolicy.command)
  48. : templates
  49. const filteredTemplates =
  50. search.length > 0
  51. ? baseTemplates.filter(
  52. (template) =>
  53. template.name.toLowerCase().includes(search.toLowerCase()) ||
  54. template.command.toLowerCase().includes(search.toLowerCase())
  55. )
  56. : baseTemplates
  57. return (
  58. <div className="h-full px-content py-content flex flex-col gap-3">
  59. <label className="sr-only" htmlFor="template-search">
  60. Search templates
  61. </label>
  62. <InputGroup>
  63. <InputGroupInput
  64. id="template-search"
  65. placeholder="Search templates"
  66. value={search}
  67. onChange={(e) => setSearch(e.target.value)}
  68. />
  69. <InputGroupAddon>
  70. <Search />
  71. </InputGroupAddon>
  72. </InputGroup>
  73. {search.length > 0 && filteredTemplates.length === 0 && (
  74. <NoSearchResults searchString={search} className="min-w-full" />
  75. )}
  76. <div className="flex flex-col gap-1.5">
  77. {filteredTemplates.map((template) => {
  78. return (
  79. <HoverCard key={template.id} openDelay={100} closeDelay={100}>
  80. <HoverCardTrigger>
  81. <CardButton
  82. title={template.name}
  83. titleClass="text-sm"
  84. className={cn(
  85. 'transition w-full',
  86. template.id === selectedTemplate
  87. ? 'border-stronger! bg-surface-200 hover:border-stronger!'
  88. : ''
  89. )}
  90. key={template.id}
  91. onClick={() => onSelectTemplate(template)}
  92. hideChevron
  93. fixedHeight={false}
  94. icon={
  95. <div className="min-w-16 flex items-start">
  96. <Badge
  97. className={cn(
  98. 'rounded-sm! font-mono',
  99. template.command === 'UPDATE'
  100. ? 'bg-blue-900/50 text-blue-200 border border-blue-800'
  101. : ''
  102. )}
  103. variant={
  104. template.command === 'ALL'
  105. ? 'default'
  106. : template.command === 'SELECT'
  107. ? 'success'
  108. : template.command === 'UPDATE'
  109. ? 'default'
  110. : template.command === 'DELETE'
  111. ? 'destructive'
  112. : 'warning'
  113. }
  114. >
  115. {template.command}
  116. </Badge>
  117. </div>
  118. }
  119. >
  120. <Markdown content={template.description} className="[&>p]:m-0 space-y-2" />
  121. </CardButton>
  122. </HoverCardTrigger>
  123. <HoverCardContent
  124. hideWhenDetached
  125. side="left"
  126. align="center"
  127. className="w-[500px] flex"
  128. animate="slide-in"
  129. >
  130. <SimpleCodeBlock
  131. showCopy={false}
  132. className="sql"
  133. parentClassName="p-0! [&>div>span]:text-xs"
  134. >
  135. {template.statement}
  136. </SimpleCodeBlock>
  137. <CopyButton
  138. iconOnly
  139. type="default"
  140. className="px-1 absolute top-1.5 right-1.5"
  141. text={template.statement}
  142. />
  143. </HoverCardContent>
  144. </HoverCard>
  145. )
  146. })}
  147. </div>
  148. </div>
  149. )
  150. }