Home / Function/ ReportFilterBar() — supabase Function Reference

ReportFilterBar() — supabase Function Reference

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

Entity Profile

Relationship Graph

Source Code

apps/studio/components/interfaces/Reports/ReportFilterBar.tsx lines 82–370

const ReportFilterBar = ({
  filters,
  isLoading = false,
  onAddFilter,
  onDatepickerChange,
  hideDatepicker = false,
  onRemoveFilters,
  onRefresh,
  datepickerHelpers,
  initialDatePickerValue,
  className,
  selectedProduct,
  showDatabaseSelector = true,
}: ReportFilterBarProps) => {
  const { ref } = useParams()
  const { data: loadBalancers } = useLoadBalancersQuery({ projectRef: ref })

  const filterKeys = [
    'request.path',
    'request.method',
    'request.search',
    'request.headers.x_client_info',
    'request.headers.user_agent',
    'response.status_code',
  ]
  const [showAdder, setShowAdder] = useState(false)
  const [currentProductFilter, setCurrentProductFilter] = useState<
    null | (typeof PRODUCT_FILTERS)[number]
  >(null)
  const [addFilterValues, setAddFilterValues] = useState<ReportFilterItem>({
    key: filterKeys[0],
    compare: 'is',
    value: '',
  })

  const resetFilterValues = () => {
    setAddFilterValues({
      key: filterKeys[0],
      compare: 'is',
      value: '',
    })
  }

  const handleDatepickerChange = (vals: DatePickerValue) => {
    onDatepickerChange && onDatepickerChange(vals)
    setSelectedRange(vals)
  }

  const handleProductFilterChange = async (
    nextProductFilter: null | (typeof PRODUCT_FILTERS)[number]
  ) => {
    const toRemove = PRODUCT_FILTERS.map(
      (productFilter) =>
        ({
          key: productFilter.filterKey,
          compare: 'matches',
          value: productFilter.filterValue,
        }) as ReportFilterItem
    )
    onRemoveFilters(toRemove)
    if (nextProductFilter) {
      onAddFilter({
        key: nextProductFilter.filterKey,
        compare: 'matches',
        value: nextProductFilter.filterValue,
      })
    }
    setCurrentProductFilter(nextProductFilter)
  }

  useEffect(() => {
    if (selectedProduct) {
      handleProductFilterChange(PRODUCT_FILTERS.find((p) => p.key === selectedProduct) ?? null)
    }
  }, [])

  const getInitialDatePickerValue = () => {
    if (initialDatePickerValue) {
      return initialDatePickerValue
    }
    const defaultHelper = datepickerHelpers.find((h) => h.default) || datepickerHelpers[0]
    return {
      to: defaultHelper.calcTo(),
      from: defaultHelper.calcFrom(),
      isHelper: true,
      text: defaultHelper.text,
    }
  }

  const [selectedRange, setSelectedRange] = useState<DatePickerValue>(getInitialDatePickerValue())

  // Sync selectedRange when initialDatePickerValue changes
  useEffect(() => {
    if (initialDatePickerValue) {
      setSelectedRange(initialDatePickerValue)
    }
  }, [initialDatePickerValue])

  return (
    <div className={cn('flex items-center justify-between', className)}>
      <div className="flex flex-row justify-start items-center flex-wrap gap-2">
        {onRefresh && (
          <ButtonTooltip
            type="default"
            disabled={isLoading}
            icon={<RefreshCw className={isLoading ? 'animate-spin' : ''} />}
            className="w-7"
            tooltip={{ content: { side: 'bottom', text: 'Refresh report' } }}
            onClick={() => onRefresh()}
          />
        )}
        {!hideDatepicker && (
          <LogsDatePicker
            onSubmit={handleDatepickerChange}
            value={selectedRange}
            helpers={datepickerHelpers}
          />
        )}
        {!selectedProduct && (
          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <Button
                type="default"
                className="inline-flex flex-row gap-2"
                iconRight={<ChevronDown size={14} />}
              >
                <span>
                  {currentProductFilter === null ? 'All Requests' : currentProductFilter.label}
                </span>
              </Button>
            </DropdownMenuTrigger>
            <DropdownMenuContent side="bottom" align="start">
              <DropdownMenuItem onClick={() => handleProductFilterChange(null)}>
                <Network size={14} strokeWidth={1.5} className="mr-2" />
                All Requests
              </DropdownMenuItem>
              <DropdownMenuSeparator />
              {PRODUCT_FILTERS.map((productFilter) => {
                const Icon = productFilter.icon

                return (
                  <DropdownMenuItem
                    key={productFilter.key}
                    className="space-x-2"
                    disabled={productFilter.key === currentProductFilter?.key}
                    onClick={() => handleProductFilterChange(productFilter)}
                  >
                    {productFilter.key === 'graphql' ? (
                      <SVG
                        src={`${BASE_PATH}/img/graphql.svg`}
                        className="w-[14px] h-[14px] mr-2"
                        preProcessor={(code) =>
                          code.replace(/svg/, 'svg class="m-auto text-color-inherit"')
                        }
                      />
                    ) : Icon !== null ? (
                      <Icon size={14} strokeWidth={1.5} className="mr-2" />
                    ) : null}
                    <div className="flex flex-col">
                      <p
                        className={cn(
                          productFilter.key === currentProductFilter?.key ? 'font-bold' : '',
                          'inline-block'
                        )}
                      >
                        {productFilter.label}
                      </p>
                    </div>
                  </DropdownMenuItem>
                )
              })}
            </DropdownMenuContent>
          </DropdownMenu>
        )}
        {filters
          .filter(
            (filter) =>
              filter.value !== currentProductFilter?.filterValue ||
              filter.key !== currentProductFilter?.filterKey
          )
          .map((filter) => (
            <div
              key={`${filter.key}-${filter.compare}-${filter.value}`}
              className="text-xs rounded-md font-mono bg-surface-300 px-2 h-[26px] flex flex-row justify-center gap-1 items-center"
            >
              <span className="">{filter.key}</span>
              <span className="text-foreground-lighter">{filter.compare}</span>
              <span className="">{filter.value}</span>
              <Button
                type="text"
                size="tiny"
                className="!p-0 !space-x-0"
                onClick={() => onRemoveFilters([filter])}
                icon={<X className="text-foreground-light" />}
              >
                <span className="sr-only">Remove</span>
              </Button>
            </div>
          ))}
        <Popover open={showAdder} onOpenChange={(openValue) => setShowAdder(openValue)}>
          <PopoverTrigger>
            <Button
              asChild
              type="default"
              size="tiny"
              icon={<Plus className={`text-foreground-light `} />}
            >
              <span>Add filter</span>
            </Button>
          </PopoverTrigger>
          <PopoverContent align={filters.length > 0 ? 'end' : 'start'} className="p-0 w-60">
            <div className="flex flex-col gap-3 p-3">
              <Select
                size="tiny"
                value={addFilterValues.key}
                onChange={(e) => {
                  setAddFilterValues((prev) => ({ ...prev, key: e.target.value }))
                }}
                label="Attribute Filter"
                className="gap-[2px]"
              >
                {filterKeys.map((key) => (
                  <Select.Option key={key} value={key}>
                    {key}
                  </Select.Option>
                ))}
              </Select>
              <Select
                size="tiny"
                value={addFilterValues.compare}
                onChange={(e) => {
                  setAddFilterValues((prev) => ({
                    ...prev,
                    compare: e.target.value as ReportFilterItem['compare'],
                  }))
                }}
                label="Comparison"
                className="gap-[2px]"
              >
                {['is', 'matches'].map((value) => (
                  <Select.Option key={value} value={value}>
                    {value}
                  </Select.Option>
                ))}
              </Select>
              <Input
                size="tiny"
                label="Value"
                className="gap-[2px]"
                placeholder={
                  addFilterValues.compare === 'matches'
                    ? 'Provide a regex expression'
                    : 'Provide a string'
                }
                onChange={(e) => {
                  setAddFilterValues((prev) => ({ ...prev, value: e.target.value }))
                }}
              />
            </div>

            <div className="flex items-center justify-end gap-2 border-t border-default p-2">
              <Button
                type="primary"
                size="tiny"
                onClick={() => {
                  onAddFilter(addFilterValues)
                  setShowAdder(false)
                  resetFilterValues()
                }}
              >
                Add filter
              </Button>
            </div>
          </PopoverContent>
        </Popover>
      </div>

      {showDatabaseSelector && (
        <DatabaseSelector
          additionalOptions={
            (loadBalancers ?? []).length > 0
              ? [{ id: `${ref}-all`, name: 'API Load Balancer' }]
              : []
          }
        />
      )}
    </div>
  )
}

Subdomains

Analyze Your Own Codebase

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

Try Supermodel Free