ChooseFunctionForm.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import { useParams } from 'common'
  2. import { map as lodashMap, uniqBy } from 'lodash'
  3. import { HelpCircle, Terminal } from 'lucide-react'
  4. import Link from 'next/link'
  5. import { useRouter } from 'next/router'
  6. import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Button, SidePanel } from 'ui'
  7. import ProductEmptyState from '@/components/to-be-cleaned/ProductEmptyState'
  8. import InformationBox from '@/components/ui/InformationBox'
  9. import SqlEditor from '@/components/ui/SqlEditor'
  10. import {
  11. useDatabaseFunctionsQuery,
  12. type DatabaseFunction,
  13. } from '@/data/database-functions/database-functions-query'
  14. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  15. export interface ChooseFunctionFormProps {
  16. visible: boolean
  17. setVisible: (value: boolean) => void
  18. onChange: (fn: DatabaseFunction) => void
  19. }
  20. const ChooseFunctionForm = ({ visible, onChange, setVisible }: ChooseFunctionFormProps) => {
  21. const { data: project } = useSelectedProjectQuery()
  22. const { data = [] } = useDatabaseFunctionsQuery({
  23. projectRef: project?.ref,
  24. connectionString: project?.connectionString,
  25. })
  26. const triggerFunctions = data.filter((fn) => fn.return_type === 'trigger')
  27. const hasPublicSchemaFunctions = triggerFunctions.length >= 1
  28. const functionSchemas = lodashMap(uniqBy(triggerFunctions, 'schema'), 'schema')
  29. const selectFunction = (id: number) => {
  30. const fn = triggerFunctions.find((x) => x.id === id)
  31. if (!!fn) onChange(fn)
  32. setVisible(!visible)
  33. }
  34. return (
  35. <SidePanel
  36. size="large"
  37. header="Pick a function"
  38. visible={visible}
  39. onCancel={() => setVisible(!visible)}
  40. className="hooks-sidepanel"
  41. >
  42. <div className="py-6">
  43. {hasPublicSchemaFunctions ? (
  44. <div className="space-y-6">
  45. <NoticeBox />
  46. {functionSchemas.map((schema: string) => (
  47. <SchemaFunctionGroup
  48. key={schema}
  49. schema={schema}
  50. functions={triggerFunctions.filter((x) => x.schema == schema)}
  51. selectFunction={selectFunction}
  52. />
  53. ))}
  54. </div>
  55. ) : (
  56. <NoFunctionsState />
  57. )}
  58. </div>
  59. </SidePanel>
  60. )
  61. }
  62. export default ChooseFunctionForm
  63. const NoticeBox = () => {
  64. const { ref } = useParams()
  65. return (
  66. <div className="px-6">
  67. <InformationBox
  68. icon={<HelpCircle size="20" strokeWidth={1.5} />}
  69. title="Only functions that return a trigger will be displayed below"
  70. description={`You can make functions by using the Database Functions`}
  71. button={
  72. <Button asChild type="default">
  73. <Link href={`/project/${ref}/database/functions`}>Go to Functions</Link>
  74. </Button>
  75. }
  76. />
  77. </div>
  78. )
  79. }
  80. const NoFunctionsState = () => {
  81. // for the empty 'no tables' state link
  82. const router = useRouter()
  83. const { ref } = router.query
  84. return (
  85. <ProductEmptyState
  86. title="No Trigger Functions found in database"
  87. ctaButtonLabel="Create a trigger function"
  88. onClickCta={() => {
  89. router.push(`/project/${ref}/database/functions`)
  90. }}
  91. >
  92. <p className="text-sm text-foreground-light">
  93. You will need to create a trigger based function before you can add it to your trigger.
  94. </p>
  95. </ProductEmptyState>
  96. )
  97. }
  98. export interface SchemaFunctionGroupProps {
  99. schema: string
  100. functions: DatabaseFunction[]
  101. selectFunction: (id: number) => void
  102. }
  103. const SchemaFunctionGroup = ({ schema, functions, selectFunction }: SchemaFunctionGroupProps) => {
  104. return (
  105. <div className="space-y-4">
  106. <div className="sticky top-0 flex items-center space-x-1 px-6 backdrop-blur-sm backdrop-filter">
  107. <h5 className="text-foreground-light">schema</h5>
  108. <h5>{schema}</h5>
  109. </div>
  110. <div className="space-y-0 divide-y border-t border-b border-default">
  111. {functions.map((x) => (
  112. <Function
  113. id={x.id}
  114. key={x.id}
  115. completeStatement={x.complete_statement}
  116. name={x.name}
  117. onClick={selectFunction}
  118. />
  119. ))}
  120. </div>
  121. </div>
  122. )
  123. }
  124. export interface FunctionProps {
  125. id: number
  126. completeStatement: string
  127. name: string
  128. onClick: (id: number) => void
  129. }
  130. const Function = ({ id, completeStatement, name, onClick }: FunctionProps) => {
  131. return (
  132. <div className="cursor-pointer rounded-sm p-3 px-6 hover:bg-studio" onClick={() => onClick(id)}>
  133. <Accordion type="single" collapsible>
  134. <AccordionItem value="definition" className="border-none">
  135. <div className="flex items-center justify-between space-x-3">
  136. <div className="flex items-center space-x-3">
  137. <div className="flex items-center justify-center rounded-sm bg-foreground p-1 text-background">
  138. <Terminal strokeWidth={2} size={14} />
  139. </div>
  140. <p className="mb-0 text-sm">{name}</p>
  141. </div>
  142. <AccordionTrigger
  143. onClick={(e) => e.stopPropagation()}
  144. className="py-0 text-xs font-normal text-foreground-light hover:no-underline"
  145. >
  146. View definition
  147. </AccordionTrigger>
  148. </div>
  149. <AccordionContent className="[&>div]:pb-0" onClick={(e) => e.stopPropagation()}>
  150. <div className="mt-4 h-64 border border-default">
  151. <SqlEditor defaultValue={completeStatement} readOnly={true} contextmenu={false} />
  152. </div>
  153. </AccordionContent>
  154. </AccordionItem>
  155. </Accordion>
  156. </div>
  157. )
  158. }