From d46a9c43fdc6b954ba4e03a9de858cd62a8b1efb Mon Sep 17 00:00:00 2001 From: Chris Stockton <180184+cstockton@users.noreply.github.com> Date: Thu, 25 Jun 2026 08:20:01 -0700 Subject: [PATCH 001/240] feat(Auth/EmailTemplates): Add SiteURL variable for notifications (#46393) This will make email templates more consistent and may be merged after https://github.com/supabase/auth/pull/2532 is deployed. ## Summary by CodeRabbit * **New Features** * Authentication notification emails now include access to site URL references. This enhancement applies to multiple notification types: password change alerts, email change confirmations, phone number change notifications, identity linking and unlinking events, and multi-factor authentication enrollment and unenrollment notifications. [![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46393?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) Co-authored-by: Chris Stockton --- .../AuthTemplatesValidation.tsx | 38 ++++++++++++++++--- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/apps/studio/components/interfaces/Auth/EmailTemplates/AuthTemplatesValidation.tsx b/apps/studio/components/interfaces/Auth/EmailTemplates/AuthTemplatesValidation.tsx index cccd998524b..f5fa434afa3 100644 --- a/apps/studio/components/interfaces/Auth/EmailTemplates/AuthTemplatesValidation.tsx +++ b/apps/studio/components/interfaces/Auth/EmailTemplates/AuthTemplatesValidation.tsx @@ -256,7 +256,7 @@ const PASSWORD_CHANGED_NOTIFICATION: AuthTemplate = { MAILER_SUBJECTS_PASSWORD_CHANGED_NOTIFICATION: { title: 'Subject', type: 'string' }, MAILER_TEMPLATES_PASSWORD_CHANGED_NOTIFICATION_CONTENT: { title: 'Body', type: 'code' }, }, - variables: [TemplateVariables.Email, TemplateVariables.Data], + variables: [TemplateVariables.Email, TemplateVariables.Data, TemplateVariables.SiteURL], validationSchema: z.object({ MAILER_SUBJECTS_PASSWORD_CHANGED_NOTIFICATION: z.string().min(1, 'Subject is required.'), }), @@ -274,7 +274,12 @@ const EMAIL_CHANGED_NOTIFICATION: AuthTemplate = { MAILER_SUBJECTS_EMAIL_CHANGED_NOTIFICATION: { title: 'Subject', type: 'string' }, MAILER_TEMPLATES_EMAIL_CHANGED_NOTIFICATION_CONTENT: { title: 'Body', type: 'code' }, }, - variables: [TemplateVariables.Email, TemplateVariables.OldEmail, TemplateVariables.Data], + variables: [ + TemplateVariables.Email, + TemplateVariables.OldEmail, + TemplateVariables.Data, + TemplateVariables.SiteURL, + ], validationSchema: z.object({ MAILER_SUBJECTS_EMAIL_CHANGED_NOTIFICATION: z.string().min(1, 'Subject is required.'), }), @@ -297,6 +302,7 @@ const PHONE_CHANGED_NOTIFICATION: AuthTemplate = { TemplateVariables.Phone, TemplateVariables.OldPhone, TemplateVariables.Data, + TemplateVariables.SiteURL, ], validationSchema: z.object({ MAILER_SUBJECTS_PHONE_CHANGED_NOTIFICATION: z.string().min(1, 'Subject is required.'), @@ -315,7 +321,12 @@ const IDENTITY_LINKED_NOTIFICATION: AuthTemplate = { MAILER_SUBJECTS_IDENTITY_LINKED_NOTIFICATION: { title: 'Subject', type: 'string' }, MAILER_TEMPLATES_IDENTITY_LINKED_NOTIFICATION_CONTENT: { title: 'Body', type: 'code' }, }, - variables: [TemplateVariables.Email, TemplateVariables.Provider, TemplateVariables.Data], + variables: [ + TemplateVariables.Email, + TemplateVariables.Provider, + TemplateVariables.Data, + TemplateVariables.SiteURL, + ], validationSchema: z.object({ MAILER_SUBJECTS_IDENTITY_LINKED_NOTIFICATION: z.string().min(1, 'Subject is required.'), }), @@ -333,7 +344,12 @@ const IDENTITY_UNLINKED_NOTIFICATION: AuthTemplate = { MAILER_SUBJECTS_IDENTITY_UNLINKED_NOTIFICATION: { title: 'Subject', type: 'string' }, MAILER_TEMPLATES_IDENTITY_UNLINKED_NOTIFICATION_CONTENT: { title: 'Body', type: 'code' }, }, - variables: [TemplateVariables.Email, TemplateVariables.Provider, TemplateVariables.Data], + variables: [ + TemplateVariables.Email, + TemplateVariables.Provider, + TemplateVariables.Data, + TemplateVariables.SiteURL, + ], validationSchema: z.object({ MAILER_SUBJECTS_IDENTITY_UNLINKED_NOTIFICATION: z.string().min(1, 'Subject is required.'), }), @@ -351,7 +367,12 @@ const MFA_FACTOR_ENROLLED_NOTIFICATION: AuthTemplate = { MAILER_SUBJECTS_MFA_FACTOR_ENROLLED_NOTIFICATION: { title: 'Subject', type: 'string' }, MAILER_TEMPLATES_MFA_FACTOR_ENROLLED_NOTIFICATION_CONTENT: { title: 'Body', type: 'code' }, }, - variables: [TemplateVariables.Email, TemplateVariables.FactorType, TemplateVariables.Data], + variables: [ + TemplateVariables.Email, + TemplateVariables.FactorType, + TemplateVariables.Data, + TemplateVariables.SiteURL, + ], validationSchema: z.object({ MAILER_SUBJECTS_MFA_FACTOR_ENROLLED_NOTIFICATION: z.string().min(1, 'Subject is required.'), }), @@ -369,7 +390,12 @@ const MFA_FACTOR_UNENROLLED_NOTIFICATION: AuthTemplate = { MAILER_SUBJECTS_MFA_FACTOR_UNENROLLED_NOTIFICATION: { title: 'Subject', type: 'string' }, MAILER_TEMPLATES_MFA_FACTOR_UNENROLLED_NOTIFICATION_CONTENT: { title: 'Body', type: 'code' }, }, - variables: [TemplateVariables.Email, TemplateVariables.FactorType, TemplateVariables.Data], + variables: [ + TemplateVariables.Email, + TemplateVariables.FactorType, + TemplateVariables.Data, + TemplateVariables.SiteURL, + ], validationSchema: z.object({ MAILER_SUBJECTS_MFA_FACTOR_UNENROLLED_NOTIFICATION: z.string().min(1, 'Subject is required.'), }), From d5bceb8db886afd4a22a872b0b5d0b8efea030d0 Mon Sep 17 00:00:00 2001 From: Jordi Enric <37541088+jordienr@users.noreply.github.com> Date: Thu, 25 Jun 2026 17:46:23 +0200 Subject: [PATCH 002/240] feat(studio): route Logs Explorer to OTEL endpoint via flag DEBUG-145 (#47265) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem The Logs Explorer (SQL editor) queries the BigQuery-backed `logs.all` endpoint and exposed a manual "OTEL endpoint" toggle behind a separate flag. ## Fix - Drive the explorer endpoint purely from the `otelLegacyLogs` flag: on -> `logs.all.otel`, off -> `logs.all`. - Remove the manual toggle from `LogsQueryPanel` (and its `showChToggleInLogExplorer` gate). ## Dependencies None. Standalone, behind `otelLegacyLogs` (off by default), so no user-facing change. Part of DEBUG-145 (split from #47087). Note: PR for the deterministic BigQuery->ClickHouse rewrite + banner builds on top of this one. ## How to test - Enable `otelLegacyLogs`, open `/project/[ref]/logs/explorer`, confirm queries hit the OTEL endpoint and run. Toggle off, confirm BigQuery path unchanged. Confirm the old manual OTEL switch is gone. ## Summary by CodeRabbit * **Bug Fixes** * Simplified the Logs Explorer experience by removing the OTEL endpoint toggle from query settings. * OTEL behavior now follows the configured feature flag, driving the editor’s initial placeholder/query shape. * On first load, the editor automatically switches to the OTEL placeholder only if the content is still the untouched default (not after user navigation or custom edits). --------- Co-authored-by: Claude Opus 4.8 (1M context) --- .../Settings/Logs/LogsQueryPanel.tsx | 33 ------------------ .../project/[ref]/logs/explorer/index.tsx | 34 +++++++++++++------ 2 files changed, 23 insertions(+), 44 deletions(-) diff --git a/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx b/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx index 7afed9f5cc4..a6b583f1587 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx @@ -1,4 +1,3 @@ -import { useFlag } from 'common' import { BookOpen, Check, ChevronDown, ChevronsUpDown, Copy, ExternalLink, X } from 'lucide-react' import Link from 'next/link' import { ReactNode, useEffect, useState } from 'react' @@ -18,12 +17,10 @@ import { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, - Label, Popover, PopoverContent, PopoverTrigger, SidePanel, - Switch, Tooltip, TooltipContent, TooltipTrigger, @@ -48,8 +45,6 @@ export interface LogsQueryPanelProps { onSelectTemplate: (template: LogTemplate) => void onSelectSource: (source: string) => void onDateChange: (value: DatePickerValue) => void - useOtel?: boolean - onUseOtelChange?: (value: boolean) => void } function DropdownMenuItemContent({ name, desc }: { name: ReactNode; desc?: string }) { @@ -68,13 +63,9 @@ const LogsQueryPanel = ({ onSelectTemplate, onSelectSource, onDateChange, - useOtel = false, - onUseOtelChange, }: LogsQueryPanelProps) => { const [showReference, setShowReference] = useState(false) const { logsTemplates } = useIsFeatureEnabled(['logs:templates']) - const showChToggleInLogExplorer = useFlag('showChToggleInLogExplorer') - const otelToggleEnabled = !!showChToggleInLogExplorer && !!onUseOtelChange const { projectAuthAll: authEnabled, @@ -167,30 +158,6 @@ const LogsQueryPanel = ({ helpers={EXPLORER_DATEPICKER_HELPERS} /> - {otelToggleEnabled && ( - - -
- onUseOtelChange?.(checked)} - /> - -
-
- - Run this query against the new ClickHouse-backed OTEL endpoint instead of - BigQuery. Use to validate ClickHouse SQL before relying on it. - -
- )} -
{ useEditorHints() const monaco = useMonaco() @@ -90,10 +93,14 @@ export const LogsExplorerPage: NextPageWithLayout = () => { }, [timestampStart, timestampEnd, defaultHelper]) const [datePickerValue, setDatePickerValue] = useState(initialDatePickerValue) + const useOtelEndpoint = useFlag('otelLegacyLogs') + const { logsDefaultQuery } = useCustomContent(['logs:default_query']) - const PLACEHOLDER_QUERY = IS_PLATFORM - ? (logsDefaultQuery ?? PLATFORM_PLACEHOLDER_QUERY) - : LOCAL_PLACEHOLDER_QUERY + const PLACEHOLDER_QUERY = useOtelEndpoint + ? OTEL_PLACEHOLDER_QUERY + : IS_PLATFORM + ? (logsDefaultQuery ?? PLATFORM_PLACEHOLDER_QUERY) + : LOCAL_PLACEHOLDER_QUERY const [editorValue, setEditorValue] = useState(PLACEHOLDER_QUERY) const [saveModalOpen, setSaveModalOpen] = useState(false) @@ -105,11 +112,6 @@ export const LogsExplorerPage: NextPageWithLayout = () => { [] ) - const [useOtelEndpoint, setUseOtelEndpoint] = useLocalStorage( - `logs-explorer-use-otel-endpoint-${projectRef}`, - false - ) - const { getEntitlementNumericValue } = useCheckEntitlements('log.retention_days') const entitledToAuditLogDays = getEntitlementNumericValue() @@ -332,6 +334,18 @@ export const LogsExplorerPage: NextPageWithLayout = () => { } }, [q, search, setSearch]) + useEffect(() => { + if (!useOtelEndpoint || q || search || queryId) return + if (editorValue === OTEL_PLACEHOLDER_QUERY) return + const isUntouchedDefault = + editorValue === LOCAL_PLACEHOLDER_QUERY || + editorValue === PLATFORM_PLACEHOLDER_QUERY || + editorValue === logsDefaultQuery + if (!isUntouchedDefault) return + setEditorValue(OTEL_PLACEHOLDER_QUERY) + editorRef.current?.setValue(OTEL_PLACEHOLDER_QUERY) + }, [useOtelEndpoint, q, search, queryId, editorValue, logsDefaultQuery]) + useEffect(() => { // prevents overwriting when the user selects a helper. // without this, if the user selects "last 3 days" it would overwrite it with "last hour" @@ -386,8 +400,6 @@ export const LogsExplorerPage: NextPageWithLayout = () => { templates={allTemplates.filter((template) => template.mode === 'custom')} onSelectTemplate={onSelectTemplate} warnings={warnings} - useOtel={useOtelEndpoint} - onUseOtelChange={setUseOtelEndpoint} /> Date: Thu, 25 Jun 2026 11:11:01 -0500 Subject: [PATCH 003/240] Updated brand-assets.zip (#47310) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added full white and black logos in .png and .svg formats to brand-assets.zip linked at https://supabase.com/brand-assets ## I have read the CONTRIBUTING.md file. YES ## What kind of change does this PR introduce? Asset update ## What is the current behavior? The brand-assets.zip downloadable on supabase.com/brand-assets contains the previous set of brand logos. ## What is the new behavior? Updated brand-assets.zip with the latest brand assets, including full white and black logo variants in .png and .svg (DEBR-337). ## Additional context File replaced at apps/www/public/brand-assets.zip. Served statically — no code changes required. --- apps/www/public/brand-assets.zip | Bin 211417 -> 263304 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/www/public/brand-assets.zip b/apps/www/public/brand-assets.zip index fca943f9002669f7d2b1be873bbcab93d4a10ec7..3ddb481d3ec696e653daa6701758a8ff1b52d11b 100644 GIT binary patch delta 53144 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 L1_noSW*`p$w*uR9 From 968fa3f052c9600b85d9f46306ef07057141a4db Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Thu, 25 Jun 2026 18:25:08 +0200 Subject: [PATCH 004/240] chore: remove old `Input` component (#47259) ## Problem Every inputs and textarea have been migrated to the new shadcn components. This `Input` is no longer needed ## Solution - Delete it - Clean up the `defaultTheme` accordingly ## Summary by CodeRabbit * **Breaking Changes** * Removed the built-in Input component from the UI library, including its attached TextArea export. * Removed input styling support from the default theme (standard/error variants and related icon/action/textarea spacing). * Cleared the Input module styles, so prior textarea action UI styling is no longer available. * Removed the Reports filtering UI in Studio (including the popover component and the associated report-filter hook), which may affect report filtering screens. --- .../Reports/ReportFilterPopover.tsx | 387 ------------------ .../interfaces/Reports/useReportFilters.ts | 328 --------------- .../ui/src/components/Input/Input.module.css | 68 --- packages/ui/src/components/Input/Input.tsx | 287 ------------- packages/ui/src/components/Input/index.tsx | 1 - packages/ui/src/lib/theme/defaultTheme.ts | 52 --- 6 files changed, 1123 deletions(-) delete mode 100644 apps/studio/components/interfaces/Reports/ReportFilterPopover.tsx delete mode 100644 apps/studio/components/interfaces/Reports/useReportFilters.ts delete mode 100644 packages/ui/src/components/Input/Input.module.css delete mode 100644 packages/ui/src/components/Input/Input.tsx delete mode 100644 packages/ui/src/components/Input/index.tsx diff --git a/apps/studio/components/interfaces/Reports/ReportFilterPopover.tsx b/apps/studio/components/interfaces/Reports/ReportFilterPopover.tsx deleted file mode 100644 index cf658389bac..00000000000 --- a/apps/studio/components/interfaces/Reports/ReportFilterPopover.tsx +++ /dev/null @@ -1,387 +0,0 @@ -import { isEqual } from 'lodash' -import { ChevronDown, Filter as FilterIcon, Plus, X } from 'lucide-react' -import { KeyboardEvent, useCallback, useEffect, useMemo, useState } from 'react' -import { - Button, - cn, - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, - Input, - Popover, - PopoverContent, - PopoverSeparator, - PopoverTrigger, -} from 'ui' -import { sizes } from 'ui/src/lib/commonCva' -import defaultTheme from 'ui/src/lib/theme/defaultTheme' - -import type { ReportFilter, ReportFilterProperty } from './Reports.types' -import { DropdownControl } from '@/components/grid/components/common/DropdownControl' - -const FilterableInput = ({ - value, - onChange, - onKeyDown, - placeholder, - availableOptions = [], -}: { - value: string | number - onChange: (value: string | number) => void - onKeyDown: (event: KeyboardEvent) => void - placeholder?: string - type?: string - availableOptions?: string[] -}) => { - const [isOpen, setIsOpen] = useState(false) - const [inputValue, setInputValue] = useState(String(value || '')) - - // Ensure we always have a valid array - const safeOptions = Array.isArray(availableOptions) ? availableOptions : [] - - useEffect(() => { - setInputValue(String(value || '')) - }, [value]) - - const handleInputChange = (newValue: string) => { - setInputValue(newValue) - onChange(newValue) - } - - const handleOptionSelect = (option: string) => { - setInputValue(option) - onChange(option) - setIsOpen(false) - } - - const handleKeyDown = (event: KeyboardEvent) => { - if (event.key === 'Escape') { - setIsOpen(false) - return - } - - // When dropdown is open, let Command component handle Enter for selection - if (event.key === 'Enter' && isOpen) { - // Don't call onKeyDown to prevent interference with Command's selection - return - } - - // When dropdown is closed, allow custom input - if (event.key === 'Enter' && !isOpen) { - return - } - - onKeyDown(event) - } - - return ( -
- - setIsOpen(true)} - onKeyDown={handleKeyDown} - className={cn('h-6 text-sm', defaultTheme.input.variants.standard, sizes.tiny)} - wrapperClassName="p-0! border! border-control! rounded-md" - showSearchIcon={false} - /> -
0 && 'opacity-100 pointer-events-auto' - )} - > - - - No matching options found. Press Enter to use "{inputValue}" - - - {safeOptions.map((option, index) => ( - handleOptionSelect(option)} - className="px-3 py-2 text-sm cursor-pointer" - > - {option} - - ))} - - -
-
- {isOpen &&
setIsOpen(false)} />} -
- ) -} - -export interface ReportFilterPopoverProps { - buttonText?: string - filters: ReportFilter[] - filterProperties: ReportFilterProperty[] - onFiltersChange: (filters: ReportFilter[]) => void - disabled?: boolean -} - -const ReportFilterRow = ({ - filter, - filterIdx, - filterProperties, - onChange, - onDelete, - onKeyDown, -}: { - filter: ReportFilter - filterIdx: number - filterProperties: ReportFilterProperty[] - onChange: (index: number, filter: ReportFilter) => void - onDelete: (index: number) => void - onKeyDown: (event: KeyboardEvent) => void -}) => { - const property = filterProperties.find((p) => p.name === filter.propertyName) - - const propertyOptions = filterProperties.map((prop) => ({ - value: prop.name, - label: prop.label, - postLabel: prop.type, - })) - - const operatorOptions = - property?.operators.map((op) => ({ - value: op, - label: op, - })) || [] - - const valueOptions = property?.options || [] - - const handlePropertyChange = (newPropertyName: string | number) => { - const newProperty = filterProperties.find((p) => p.name === newPropertyName) - if (newProperty) { - onChange(filterIdx, { - propertyName: newPropertyName, - operator: newProperty.operators[0] || '=', - value: '', - }) - } - } - - const handleOperatorChange = (newOperator: string | number) => { - onChange(filterIdx, { - ...filter, - operator: newOperator, - }) - } - - const handleValueChange = (value: string | number) => { - onChange(filterIdx, { - ...filter, - value, - }) - } - - return ( -
- {filterIdx > 0 && ( -
-
- AND -
-
- )} -
- -
- } - className="w-32 justify-start" - > - {property?.label ?? 'Select property'} - - - - -
- } - className="w-20 justify-start" - > - {filter.operator} - - - - {valueOptions?.length > 0 ? ( - String(option.value)) || []} - /> - ) : ( - handleValueChange(event.target.value)} - onKeyDown={onKeyDown} - /> - )} -
-
- ) -} - -export const ReportFilterPopover = ({ - buttonText, - filters, - filterProperties, - onFiltersChange, - disabled = false, -}: ReportFilterPopoverProps) => { - const [open, setOpen] = useState(false) - const [localFilters, setLocalFilters] = useState(filters) - - // Update local state when filters prop changes - useMemo(() => { - setLocalFilters(filters) - }, [filters]) - - const displayButtonText = - buttonText ?? - (filters.length > 0 - ? `Filtered by ${filters.length} rule${filters.length > 1 ? 's' : ''}` - : 'Filter') - - const onAddFilter = () => { - const firstProperty = filterProperties[0] - if (firstProperty) { - setLocalFilters([ - ...localFilters, - { - propertyName: firstProperty.name, - operator: firstProperty.operators[0] || '=', - value: '', - }, - ]) - } - } - - const onChangeFilter = useCallback((index: number, filter: ReportFilter) => { - setLocalFilters((currentFilters) => [ - ...currentFilters.slice(0, index), - filter, - ...currentFilters.slice(index + 1), - ]) - }, []) - - const onDeleteFilter = useCallback((index: number) => { - setLocalFilters((currentFilters) => [ - ...currentFilters.slice(0, index), - ...currentFilters.slice(index + 1), - ]) - }, []) - - const onApplyFilters = () => { - // Filter out empty values - const validFilters = localFilters.filter( - (f) => f.value !== null && f.value !== undefined && f.value !== '' - ) - onFiltersChange(validFilters) - setOpen(false) - } - - const onResetFilters = () => { - setLocalFilters([]) - onFiltersChange([]) - setOpen(false) - } - - function handleEnterKeyDown(event: KeyboardEvent) { - if (event.key === 'Enter') onApplyFilters() - } - - const hasChanges = !isEqual(localFilters, filters) - - return ( - - - - - -
-
- {localFilters.map((filter, index) => ( - - ))} - {localFilters.length === 0 && ( -
-
No filters applied
-

- Add a filter below to narrow down the report data -

-
- )} -
- -
-
- - {filters.length > 0 && ( - - )} -
- -
-
-
-
- ) -} diff --git a/apps/studio/components/interfaces/Reports/useReportFilters.ts b/apps/studio/components/interfaces/Reports/useReportFilters.ts deleted file mode 100644 index a913df57453..00000000000 --- a/apps/studio/components/interfaces/Reports/useReportFilters.ts +++ /dev/null @@ -1,328 +0,0 @@ -import { parseAsString, useQueryStates } from 'nuqs' -import { useCallback, useEffect, useMemo, useRef, useState } from 'react' - -import type { ReportFilter, ReportFilterItem, ReportFilterProperty } from './Reports.types' - -export enum ReportFilterKeys { - PATH = 'request.path', - SEARCH = 'request.search', - X_CLIENT_INFO = 'request.headers.x_client_info', - USER_AGENT = 'request.headers.user_agent', - STATUS_CODE = 'response.status_code', -} - -export const REPORT_FILTER_PARAMS_PARSER = { - [ReportFilterKeys.PATH]: parseAsString, - [ReportFilterKeys.SEARCH]: parseAsString, - [ReportFilterKeys.X_CLIENT_INFO]: parseAsString, - [ReportFilterKeys.USER_AGENT]: parseAsString, - [ReportFilterKeys.STATUS_CODE]: parseAsString, -} - -interface UseReportFiltersProps { - onAddFilter: (filter: ReportFilterItem) => void - onRemoveFilters: (filters: ReportFilterItem[]) => void - filters: ReportFilterItem[] -} - -export const useReportFilters = ({ - onAddFilter, - onRemoveFilters, - filters, -}: UseReportFiltersProps) => { - // URL-safe operator mappings - const URL_OPERATOR_MAP = { - '=': 'eq', - '!=': 'neq', - '>=': 'gte', - '<=': 'lte', - '>': 'gt', - '<': 'lt', - CONTAINS: 'contains', - 'STARTS WITH': 'startswith', - 'ENDS WITH': 'endswith', - } - - const REVERSE_URL_OPERATOR_MAP = Object.fromEntries( - Object.entries(URL_OPERATOR_MAP).map(([k, v]) => [v, k]) - ) - - // Parse encoded filter value (e.g., "gte:300" -> { operator: ">=", value: "300" }) - const parseFilterValue = useCallback((encodedValue: string | number | null) => { - if (!encodedValue || typeof encodedValue !== 'string') { - return { operator: '=', value: encodedValue?.toString() || '' } - } - - const parts = encodedValue.split(':') - if (parts.length === 2) { - const [urlOperator, value] = parts - const operator = REVERSE_URL_OPERATOR_MAP[urlOperator] || '=' - return { operator, value } - } - - return { operator: '=', value: encodedValue } - }, []) - - const encodeFilterValue = useCallback((operator: string | number, value: string | number) => { - const urlOperator = URL_OPERATOR_MAP[operator as keyof typeof URL_OPERATOR_MAP] || 'eq' - return `${urlOperator}:${value}` - }, []) - - // Convert query filters to ReportFilter format - const convertQueryFiltersToReportFilters = useCallback( - (queryState: Record): ReportFilter[] => { - const filters: ReportFilter[] = [] - - Object.entries(queryState).forEach(([key, value]) => { - if (value !== null && value !== undefined && value !== '') { - const { operator, value: filterValue } = parseFilterValue(value) - filters.push({ - propertyName: key, - value: filterValue, - operator: operator, - }) - } - }) - - return filters - }, - [parseFilterValue] - ) - - // Convert operator from FilterBar format to ReportFilterItem format - const getCompareOperator = useCallback( - (operator: string | number): 'matches' | 'is' | '>=' | '<=' | '>' | '<' | '!=' => { - switch (operator) { - case '=': - return 'is' - case '!=': - return '!=' - case '>=': - return '>=' - case '<=': - return '<=' - case '>': - return '>' - case '<': - return '<' - case 'CONTAINS': - case 'STARTS WITH': - case 'ENDS WITH': - return 'matches' - default: - return 'matches' - } - }, - [] - ) - - // Convert ReportFilter to ReportFilterItem format for the report system - const convertReportFiltersToReportFilterItems = useMemo( - () => - (reportFilters: ReportFilter[]): ReportFilterItem[] => { - const reportFilterItems: ReportFilterItem[] = [] - - reportFilters.forEach((filter) => { - if (filter.value !== null && filter.value !== '' && filter.value !== undefined) { - reportFilterItems.push({ - key: filter.propertyName.toString(), - value: filter.value.toString(), - compare: getCompareOperator(filter.operator), - }) - } - }) - - return reportFilterItems - }, - [getCompareOperator] - ) - - // Convert ReportFilter back to query filters format - const convertReportFiltersToQueryFilters = useMemo( - () => (reportFilters: ReportFilter[]) => { - const queryUpdate: Record = {} - - // Clear all existing filters first - Object.keys(REPORT_FILTER_PARAMS_PARSER).forEach((key) => { - queryUpdate[key] = null - }) - - // Add new filters with encoded operator+value - reportFilters.forEach((filter) => { - if (filter.propertyName in REPORT_FILTER_PARAMS_PARSER) { - const encodedValue = encodeFilterValue(filter.operator, filter.value as string | number) - queryUpdate[filter.propertyName] = encodedValue - } - }) - - return queryUpdate - }, - [encodeFilterValue] - ) - - const [queryFilters, setQueryFilters] = useQueryStates(REPORT_FILTER_PARAMS_PARSER) - const [localFilters, setLocalFilters] = useState([]) - - const [isInitialized, setIsInitialized] = useState(false) - const lastAppliedFilterState = useRef('') - const preventUrlSync = useRef(false) - - // Initialize local state from URL params (only once on mount) - useEffect(() => { - if (!isInitialized) { - const initialFilters = convertQueryFiltersToReportFilters(queryFilters) - setLocalFilters(initialFilters) - setIsInitialized(true) - } - }, [isInitialized, convertQueryFiltersToReportFilters, queryFilters]) - - // Sync URL changes back to local state (when URL changes externally) - useEffect(() => { - if (!isInitialized || preventUrlSync.current) return - - const filtersFromUrl = convertQueryFiltersToReportFilters(queryFilters) - const currentFilterJson = JSON.stringify(localFilters) - const urlFilterJson = JSON.stringify(filtersFromUrl) - - if (currentFilterJson !== urlFilterJson) { - console.log('Syncing local filters from URL change:', { filtersFromUrl, localFilters }) - setLocalFilters(filtersFromUrl) - } - }, [queryFilters, isInitialized, convertQueryFiltersToReportFilters]) - - // Sync local filter changes back to URL state (only after initialization) - useEffect(() => { - if (!isInitialized) return - - const queryUpdate = convertReportFiltersToQueryFilters(localFilters) - - // Prevent feedback loop during URL updates - preventUrlSync.current = true - setQueryFilters(queryUpdate).finally(() => { - preventUrlSync.current = false - }) - }, [localFilters, isInitialized, setQueryFilters, convertReportFiltersToQueryFilters]) - - // Separate effect for report filter updates (only for filters with values) - useEffect(() => { - if (!isInitialized) return - - const reportFilterItems = convertReportFiltersToReportFilterItems(localFilters) - - // Create a state string for comparison (only for the filters we manage) - const newFilterState = reportFilterItems - .map((filter) => `${filter.key}:${filter.value}:${filter.compare}`) - .sort() - .join('|') - - if (lastAppliedFilterState.current !== newFilterState) { - lastAppliedFilterState.current = newFilterState - - // Separate product filters (managed by dropdown) from other filters (managed by ReportFilterPopover) - const PRODUCT_FILTER_KEYS = ['request.path'] - const PRODUCT_FILTER_VALUES = ['/rest', '/auth', '/storage', '/realtime', '/graphql'] - - const productFilters = filters.filter( - (f) => - PRODUCT_FILTER_KEYS.includes(f.key) && PRODUCT_FILTER_VALUES.includes(f.value.toString()) - ) - const otherFilters = filters.filter( - (f) => - !( - PRODUCT_FILTER_KEYS.includes(f.key) && - PRODUCT_FILTER_VALUES.includes(f.value.toString()) - ) - ) - - console.log('Filter Update:', { - localFilters, - reportFilterItems, - productFilters, - otherFilters, - newFilterState, - }) - - // Remove only the non-product filters that we manage - if (otherFilters.length > 0) { - onRemoveFilters(otherFilters) - } - - // Add all the new filters from the popover - reportFilterItems.forEach((filter) => onAddFilter(filter)) - } - }, [localFilters, isInitialized]) - - const handleFilterChange = (newFilters: ReportFilter[]) => { - console.log('handleFilterChange called with:', newFilters) - setLocalFilters(newFilters) - } - - // Get filter properties based on product type - const getFilterProperties = (): ReportFilterProperty[] => { - const baseProperties: ReportFilterProperty[] = [ - { - label: 'Status Code', - name: ReportFilterKeys.STATUS_CODE, - type: 'number' as const, - options: [ - { label: '200', value: '200' }, - { label: '300', value: '300' }, - { label: '400', value: '400' }, - { label: '401', value: '401' }, - { label: '403', value: '403' }, - { label: '404', value: '404' }, - { label: '409', value: '409' }, - { label: '411', value: '411' }, - { label: '413', value: '413' }, - { label: '416', value: '416' }, - { label: '423', value: '423' }, - { label: '500', value: '500' }, - { label: '503', value: '503' }, - { label: '504', value: '504' }, - ], - operators: ['=', '!=', '>', '<', '>=', '<='], - placeholder: '200', - }, - { - label: 'User Agent', - name: ReportFilterKeys.USER_AGENT, - type: 'string' as const, - operators: ['=', '!=', 'CONTAINS', 'STARTS WITH', 'ENDS WITH'], - placeholder: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36', - }, - { - label: 'Search Params', - name: ReportFilterKeys.SEARCH, - type: 'string' as const, - operators: ['=', '!=', 'CONTAINS', 'STARTS WITH', 'ENDS WITH'], - placeholder: '?foo=bar', - }, - { - label: 'Client Info', - name: ReportFilterKeys.X_CLIENT_INFO, - type: 'string' as const, - operators: ['=', '!=', 'CONTAINS', 'STARTS WITH', 'ENDS WITH'], - placeholder: 'supabase-js/1.0.0', - }, - ] - - return [ - { - label: 'Path', - name: ReportFilterKeys.PATH, - type: 'string' as const, - operators: ['=', '!=', 'CONTAINS', 'STARTS WITH', 'ENDS WITH'], - placeholder: '/rest/v1/tablename', - }, - ...baseProperties, - ] - } - - return { - localFilters, - filterProperties: getFilterProperties(), - handleFilterChange, - ReportFilterKeys, - } -} diff --git a/packages/ui/src/components/Input/Input.module.css b/packages/ui/src/components/Input/Input.module.css deleted file mode 100644 index 7469c5600a5..00000000000 --- a/packages/ui/src/components/Input/Input.module.css +++ /dev/null @@ -1,68 +0,0 @@ -/* .sbui-input-container { - @apply relative; -} */ - -/* .sbui-input { - @apply block box-border pl-3 pr-3 py-2 w-full rounded-md shadow-xs text-sm border border-solid transition-all; - @apply bg-white text-input-value-light border-input-border-light; - @apply focus:ring-input-border-focus-light focus:border-input-border-focus-light focus:outline-hidden; - - @apply dark:bg-transparent dark:text-input-value-dark dark:border-input-border-dark; - @apply dark:focus:border-input-border-focus-dark dark:focus:ring-input-border-focus-dark; - - - font-family: inherit; - transition: box-shadow 0.3s ease-in-out; -} */ - -/* .sbui-input:focus { - box-shadow: 0 0 0 2px rgba(62, 207, 142, 0.1); -} */ - -/* .sbui-input--error { - @apply border-red-500 dark:border-red-500; -} - -.sbui-input--borderless { - @apply border-transparent shadow-none; -} - -.sbui-input-actions-container { - @apply absolute inset-y-0 right-0 pl-3 pr-1 flex items-center; -} */ - -.sbui-textarea-actions-container { - @apply absolute inset-y-1 right-0 pl-3 pr-1 flex items-start; -} - -.sbui-textarea-actions-container__items { - @apply flex items-center; -} - -/* - Input sizes -*/ - -/* .sbui-input--tiny { - @apply px-2.5 py-1.5 text-xs; -} -.sbui-input--small { - @apply px-3 py-2 text-sm leading-4; -} -.sbui-input--medium { - @apply px-4 py-2 text-sm; -} -.sbui-input--large { - @apply px-4 py-2 text-base; -} -.sbui-input--xlarge { - @apply px-6 py-3 text-base; -} */ - -/* - Input icon -*/ - -/* .sbui-input--with-icon { - @apply pl-7; -} */ diff --git a/packages/ui/src/components/Input/Input.tsx b/packages/ui/src/components/Input/Input.tsx deleted file mode 100644 index c8b4568ee1a..00000000000 --- a/packages/ui/src/components/Input/Input.tsx +++ /dev/null @@ -1,287 +0,0 @@ -'use client' - -import { Copy } from 'lucide-react' -import React, { useState } from 'react' - -import { HIDDEN_PLACEHOLDER } from '../../lib/constants' -import { FormLayout } from '../../lib/Layout/FormLayout/FormLayout' -import InputErrorIcon from '../../lib/Layout/InputErrorIcon' -import InputIconContainer from '../../lib/Layout/InputIconContainer' -import styleHandler from '../../lib/theme/styleHandler' -import { copyToClipboard } from '../../lib/utils' -import { cn } from '../../lib/utils/cn' -import { Button } from '../Button' - -export interface Props extends Omit< - React.InputHTMLAttributes, - 'size' | 'onCopy' -> { - inputClassName?: string - iconContainerClassName?: string - copy?: boolean - onCopy?: () => void - defaultValue?: string | number - descriptionText?: string | React.ReactNode | undefined - disabled?: boolean - error?: string - icon?: any - inputRef?: React.LegacyRef - label?: string | React.ReactNode - afterLabel?: string - beforeLabel?: string - labelOptional?: string | React.ReactNode - layout?: 'horizontal' | 'vertical' - reveal?: boolean - actions?: React.ReactNode - size?: 'tiny' | 'small' | 'medium' | 'large' | 'xlarge' - borderless?: boolean - validation?: (x: any) => void -} - -/** - * @deprecated Use `import { Input } from 'ui'` instead or `import { Input } from 'ui-patterns/DataInputs/Input'` - */ -function Input({ - autoComplete, - autoFocus, - className, - inputClassName, - iconContainerClassName, - copy, - defaultValue, - descriptionText, - disabled, - error, - icon, - id = '', - name = '', - inputRef, - label, - afterLabel, - beforeLabel, - labelOptional, - layout, - onCopy, - placeholder, - type = 'text', - value = undefined, - style, - reveal = false, - actions, - size = 'medium', - borderless = false, - validation, - ...props -}: Props) { - const [copyLabel, setCopyLabel] = useState('Copy') - const [hidden, setHidden] = useState(true) - - const __styles = styleHandler('input') - - function _onCopy(value: any) { - copyToClipboard(value, () => { - setCopyLabel('Copied') - setTimeout(() => { - setCopyLabel('Copy') - }, 3000) - onCopy?.() - }) - } - - function onReveal() { - setHidden(false) - } - - let inputClasses = ['peer/input', __styles.base] - - if (error) inputClasses.push(__styles.variants.error) - if (!error) inputClasses.push(__styles.variants.standard) - if (size) inputClasses.push(__styles.size[size]) - if (icon) inputClasses.push(__styles.with_icon[size]) - if (disabled) inputClasses.push(__styles.disabled) - if (inputClassName) inputClasses.push(inputClassName) - - return ( - -
- - {icon && } - {copy || error || actions ? ( -
- {error && } - {copy && !(reveal && hidden) ? ( - - ) : null} - {reveal && hidden ? ( - - ) : null} - {actions && actions} -
- ) : null} -
-
- ) -} - -/** - * @deprecated Use ./TextArea_Shadcn_ instead - */ -export interface TextAreaProps extends Omit< - React.InputHTMLAttributes, - 'size' | 'onCopy' -> { - textAreaClassName?: string - descriptionText?: string | React.ReactNode | undefined - error?: string - icon?: any - label?: string | React.ReactNode - afterLabel?: string - beforeLabel?: string - labelOptional?: string | React.ReactNode - layout?: 'horizontal' | 'vertical' - rows?: number - limit?: number - size?: 'tiny' | 'small' | 'medium' | 'large' | 'xlarge' - borderless?: boolean - validation?: (x: any) => void - copy?: boolean - onCopy?: () => void - actions?: React.ReactNode -} - -function TextArea({ - className, - textAreaClassName, - descriptionText, - disabled, - error, - icon, - id = '', - name = '', - label, - afterLabel, - beforeLabel, - labelOptional, - layout, - placeholder, - value, - style, - rows = 4, - limit, - size = 'medium', - borderless = false, - validation, - copy = false, - onCopy, - actions, - ...props -}: TextAreaProps) { - const [copyLabel, setCopyLabel] = useState('Copy') - - function _onCopy(value: any) { - copyToClipboard(value, () => { - /* clipboard successfully set */ - setCopyLabel('Copied') - setTimeout(() => { - setCopyLabel('Copy') - }, 3000) - onCopy?.() - }) - } - - const __styles = styleHandler('input') - - let classes = [__styles.base] - - if (error) classes.push(__styles.variants.error) - if (!error) classes.push(__styles.variants.standard) - if (icon) classes.push(__styles.with_icon[size]) - if (size) classes.push(__styles.size[size]) - if (disabled) classes.push(__styles.disabled) - if (textAreaClassName) classes.push(textAreaClassName) - - return ( - -
-