Home / Function/ TriggersList() — supabase Function Reference

TriggersList() — supabase Function Reference

Architecture documentation for the TriggersList() function in TriggersList.tsx from the supabase codebase.

Entity Profile

Dependency Diagram

graph TD
  80f16831_d05a_3ddc_4b5f_e2ba883198f6["TriggersList()"]
  e4649650_1257_de82_1d38_de2f1750bc35["generateTriggerCreateSQL()"]
  80f16831_d05a_3ddc_4b5f_e2ba883198f6 -->|calls| e4649650_1257_de82_1d38_de2f1750bc35
  style 80f16831_d05a_3ddc_4b5f_e2ba883198f6 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

apps/studio/components/interfaces/Database/Triggers/TriggersList/TriggersList.tsx lines 36–296

export const TriggersList = () => {
  const [selectedTrigger, setSelectedTrigger] = useState<PostgresTrigger>()
  const deletingTriggerIdRef = useRef<string | null>(null)
  const { data: project } = useSelectedProjectQuery()
  const aiSnap = useAiAssistantStateSnapshot()
  const { openSidebar } = useSidebarManagerSnapshot()
  const { selectedSchema, setSelectedSchema } = useQuerySchemaState()

  const [filterString, setFilterString] = useQueryState(
    'search',
    parseAsString.withDefault('').withOptions({ history: 'replace', clearOnDefault: true })
  )

  const isInlineEditorEnabled = useIsInlineEditorEnabled()
  const {
    templates: editorPanelTemplates,
    setValue: setEditorPanelValue,
    setTemplates: setEditorPanelTemplates,
    setInitialPrompt: setEditorPanelInitialPrompt,
  } = useEditorPanelStateSnapshot()

  const { data: protectedSchemas } = useProtectedSchemas()
  const { isSchemaLocked } = useIsProtectedSchema({ schema: selectedSchema })

  const { data = [] } = useTablesQuery({
    projectRef: project?.ref,
    connectionString: project?.connectionString,
  })
  const hasTables =
    data.filter((a) => !protectedSchemas.find((s) => s.name === a.schema)).length > 0

  const {
    data: triggers,
    error,
    isPending,
    isError,
  } = useDatabaseTriggersQuery({
    projectRef: project?.ref,
    connectionString: project?.connectionString,
  })

  const { can: canCreateTriggers } = useAsyncCheckPermissions(
    PermissionAction.TENANT_SQL_ADMIN_WRITE,
    'triggers'
  )

  const [showCreateTriggerForm, setShowCreateTriggerForm] = useQueryState(
    'new',
    parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
  )
  const { setValue: setTriggerToEdit, value: triggerToEdit } = useQueryStateWithSelect({
    urlKey: 'edit',
    select: (id: string) => (id ? triggers?.find((fn) => fn.id.toString() === id) : undefined),
    enabled: !!triggers,
    onError: () => toast.error(`Trigger not found`),
  })

  const { setValue: setTriggerToDuplicate, value: triggerToDuplicate } = useQueryStateWithSelect({
    urlKey: 'duplicate',
    select: (id: string) => {
      if (!id) return undefined
      const original = triggers?.find((trigger) => trigger.id.toString() === id)
      return original ? { ...original, name: `${original.name}_duplicate` } : undefined
    },
    enabled: !!triggers,
    onError: () => toast.error(`Trigger not found`),
  })

  const { setValue: setTriggerToDelete, value: triggerToDelete } = useQueryStateWithSelect({
    urlKey: 'delete',
    select: (id: string) => (id ? triggers?.find((fn) => fn.id.toString() === id) : undefined),
    enabled: !!triggers,
    onError: (_error, selectedId) =>
      handleErrorOnDelete(deletingTriggerIdRef, selectedId, `Database Trigger not found`),
  })

  const { mutate: deleteDatabaseTrigger, isPending: isDeletingTrigger } =
    useDatabaseTriggerDeleteMutation({
      onSuccess: (_, variables) => {
        toast.success(`Successfully removed ${variables.trigger.name}`)
        setTriggerToDelete(null)
      },
      onError: () => {
        deletingTriggerIdRef.current = null
      },
    })

  const createTrigger = () => {
    setTriggerToDuplicate(null)
    if (isInlineEditorEnabled) {
      setEditorPanelInitialPrompt('Create a new database trigger that...')
      setEditorPanelValue(`create trigger trigger_name
after insert or update or delete on table_name
for each row
execute function function_name();`)
      if (editorPanelTemplates.length > 0) {
        setEditorPanelTemplates([])
      }
      openSidebar(SIDEBAR_KEYS.EDITOR_PANEL)
    } else {
      setSelectedTrigger(undefined)
      setShowCreateTriggerForm(true)
    }
  }

  const editTrigger = (trigger: PostgresTrigger) => {
    setTriggerToDuplicate(null)
    if (isInlineEditorEnabled) {
      setEditorPanelValue(generateTriggerCreateSQL(trigger))
      setEditorPanelTemplates([])
      openSidebar(SIDEBAR_KEYS.EDITOR_PANEL)
    } else {
      setTriggerToEdit(trigger.id.toString())
    }
  }

  const duplicateTrigger = (trigger: PostgresTrigger) => {
    if (isInlineEditorEnabled) {
      const dupTrigger = {
        ...trigger,
        name: `${trigger.name}_duplicate`,
      }
      setEditorPanelValue(generateTriggerCreateSQL(dupTrigger))
      setEditorPanelTemplates([])
      openSidebar(SIDEBAR_KEYS.EDITOR_PANEL)
    } else {
      setTriggerToDuplicate(trigger.id.toString())
    }
  }

  const deleteTrigger = (trigger: PostgresTrigger) => {
    setTriggerToDelete(trigger.id.toString())
  }

  if (isPending) {
    return <GenericSkeletonLoader />
  }

  if (isError) {
    return <AlertError error={error} subject="Failed to retrieve database triggers" />
  }

  const schemaTriggers = triggers.filter((x) => x.schema === selectedSchema)

  return (
    <>
      <div className="space-y-4">
        <div className="flex flex-col lg:flex-row lg:items-center justify-between gap-2 flex-wrap">
          <div className="flex flex-col lg:flex-row lg:items-center gap-2 flex-wrap">
            <SchemaSelector
              className="w-full lg:w-[180px]"
              size="tiny"
              showError={false}
              selectedSchemaName={selectedSchema}
              onSelectSchema={setSelectedSchema}
            />
            <Input
              placeholder="Search for a trigger"
              size="tiny"
              icon={<Search />}
              value={filterString}
              className="w-full lg:w-52"
              onChange={(e) => setFilterString(e.target.value)}
            />
          </div>
          <div className="flex items-center gap-2">
            <DocsButton href={`${DOCS_URL}/guides/database/postgres/triggers`} />
            {!isSchemaLocked && (
              <CreateTriggerButtons
                hasTables={hasTables}
                canCreateTriggers={canCreateTriggers}
                selectedSchema={selectedSchema}
                onCreateTrigger={createTrigger}
                showPlusIcon={true}
              />
            )}
          </div>
        </div>

        {isSchemaLocked && <ProtectedSchemaWarning schema={selectedSchema} entity="triggers" />}

        {!isSchemaLocked && (schemaTriggers ?? []).length === 0 ? (
          <EmptyStatePresentational
            icon={DatabaseZap}
            title="Add your first trigger"
            description="Make your database reactive. Send updates in realtime, call edge functions, or validate data as it comes in."
          >
            <CreateTriggerButtons
              hasTables={hasTables}
              canCreateTriggers={canCreateTriggers}
              selectedSchema={selectedSchema}
              onCreateTrigger={createTrigger}
              showPlusIcon={false}
              buttonType="default"
            />
          </EmptyStatePresentational>
        ) : (
          <div className="w-full overflow-hidden overflow-x-auto">
            <Card>
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead key="name">Name</TableHead>
                    <TableHead key="table">Table</TableHead>
                    <TableHead key="function">Function</TableHead>
                    <TableHead key="events">Events</TableHead>
                    <TableHead key="orientation">Orientation</TableHead>
                    <TableHead key="enabled" className="w-20">
                      Enabled
                    </TableHead>
                    <TableHead key="buttons" className="w-1/12"></TableHead>
                  </TableRow>
                </TableHeader>
                <TableBody>
                  <TriggerList
                    schema={selectedSchema}
                    filterString={filterString}
                    isLocked={isSchemaLocked}
                    editTrigger={editTrigger}
                    duplicateTrigger={duplicateTrigger}
                    deleteTrigger={deleteTrigger}
                  />
                </TableBody>
              </Table>
            </Card>
          </div>
        )}
      </div>

      <TriggerSheet
        selectedTrigger={selectedTrigger}
        open={showCreateTriggerForm}
        onClose={() => {
          setShowCreateTriggerForm(false)
        }}
        isDuplicatingTrigger={false}
      />

      <TriggerSheet
        selectedTrigger={triggerToEdit || triggerToDuplicate}
        open={!!triggerToEdit || !!triggerToDuplicate}
        onClose={() => {
          setTriggerToEdit(null)
          setTriggerToDuplicate(null)
        }}
        isDuplicatingTrigger={!!triggerToDuplicate}
      />

      <DeleteTrigger
        trigger={triggerToDelete}
        visible={!!triggerToDelete}
        setVisible={setTriggerToDelete}
        onDelete={(params: Parameters<typeof deleteDatabaseTrigger>[0]) => {
          deletingTriggerIdRef.current = params.trigger.id.toString()
          deleteDatabaseTrigger(params)
        }}
        isLoading={isDeletingTrigger}
      />
    </>
  )
}

Subdomains

Frequently Asked Questions

What does TriggersList() do?
TriggersList() is a function in the supabase codebase.
What does TriggersList() call?
TriggersList() calls 1 function(s): generateTriggerCreateSQL.

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free