diff --git a/apps/studio/components/interfaces/Connect/Connect.tsx b/apps/studio/components/interfaces/Connect/Connect.tsx index fffa529023bc0..bc1bfbeb6ac38 100644 --- a/apps/studio/components/interfaces/Connect/Connect.tsx +++ b/apps/studio/components/interfaces/Connect/Connect.tsx @@ -2,13 +2,13 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' import { useParams } from 'common' import { ExternalLink, Plug } from 'lucide-react' import { parseAsBoolean, useQueryState } from 'nuqs' -import { useState, useMemo } from 'react' +import { useMemo, useState } from 'react' import { DatabaseConnectionString } from 'components/interfaces/Connect/DatabaseConnectionString' import { ButtonTooltip } from 'components/ui/ButtonTooltip' import Panel from 'components/ui/Panel' -import { getAPIKeys, useProjectSettingsV2Query } from 'data/config/project-settings-v2-query' import { useAPIKeysQuery } from 'data/api-keys/api-keys-query' +import { getAPIKeys, useProjectSettingsV2Query } from 'data/config/project-settings-v2-query' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useSelectedProject } from 'hooks/misc/useSelectedProject' import { PROJECT_STATUS } from 'lib/constants' diff --git a/apps/studio/components/interfaces/Connect/DatabaseConnectionString.tsx b/apps/studio/components/interfaces/Connect/DatabaseConnectionString.tsx index 698e5e7b3281b..74241727200e9 100644 --- a/apps/studio/components/interfaces/Connect/DatabaseConnectionString.tsx +++ b/apps/studio/components/interfaces/Connect/DatabaseConnectionString.tsx @@ -239,7 +239,7 @@ export const DatabaseConnectionString = () => { - + {isLoading && ( diff --git a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.fields.tsx b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.fields.tsx index 71c79e477590c..48cea7695ce9d 100644 --- a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.fields.tsx +++ b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.fields.tsx @@ -52,9 +52,10 @@ export const filterFields = [ return (
- {props.label} + {props.label.replace('_', ' ')} - {props.value} + {/* [Joshen] Temporarily hiding, this feels excessive */} + {/* {props.value} */}
) }, diff --git a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.ts b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.ts index 8156978dfd528..45decf308f89b 100644 --- a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.ts +++ b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.queries.ts @@ -35,7 +35,11 @@ const buildQueryConditions = (search: QuerySearchParamsType) => { // Handle scalar values if (value !== null && value !== undefined) { - whereConditions.push(`${key} = '${value}'`) + if (['host', 'pathname'].includes(key)) { + whereConditions.push(`${key} LIKE '%${value}%'`) + } else { + whereConditions.push(`${key} = '${value}'`) + } } }) @@ -234,7 +238,7 @@ const getEdgeLogsQuery = () => { WHEN edge_logs_response.status_code >= 500 THEN 'error' ELSE 'success' END as level, - edge_logs_request.path as path, + edge_logs_request.path as pathname, edge_logs_request.host as host, null as event_message, edge_logs_request.method as method, @@ -273,7 +277,7 @@ const getPostgrestLogsQuery = () => { WHEN edge_logs_response.status_code >= 500 THEN 'error' ELSE 'success' END as level, - edge_logs_request.path as path, + edge_logs_request.path as pathname, edge_logs_request.host as host, null as event_message, edge_logs_request.method as method, @@ -311,7 +315,7 @@ const getPostgresLogsQuery = () => { WHEN pgl_parsed.error_severity = 'ERROR' THEN 'error' ELSE null END as level, - null as path, + null as pathname, null as host, event_message as event_message, null as method, @@ -341,7 +345,7 @@ const getEdgeFunctionLogsQuery = () => { WHEN fel_response.status_code >= 500 THEN 'error' ELSE 'success' END as level, - fel_request.url as path, + fel_request.url as pathname, fel_request.host as host, COALESCE(function_logs_agg.last_event_message, '') as event_message, fel_request.method as method, @@ -387,7 +391,7 @@ const getAuthLogsQuery = () => { WHEN el_in_al_response.status_code >= 500 THEN 'error' ELSE 'success' END as level, - el_in_al_request.path as path, + el_in_al_request.path as pathname, el_in_al_request.host as host, null as event_message, el_in_al_request.method as method, @@ -428,7 +432,7 @@ const getSupavisorLogsQuery = () => { WHEN LOWER(svl_metadata.level) = 'warn' OR LOWER(svl_metadata.level) = 'warning' THEN 'warning' ELSE 'success' END as level, - null as path, + null as pathname, null as host, null as event_message, null as method, @@ -478,7 +482,7 @@ SELECT log_type, status, level, - path, + pathname, host, event_message, method, @@ -498,8 +502,9 @@ ${finalWhere} * Also returns facets for all filter dimensions */ export const getLogsCountQuery = (search: QuerySearchParamsType): string => { - // Use the buildQueryConditions helper const { finalWhere } = buildQueryConditions(search) + const methodWhere = + finalWhere.length > 0 ? `${finalWhere} AND method is NOT NULL` : 'WHERE method IS NOT NULL' // Create a count query using the same unified logs CTE const sql = ` @@ -530,8 +535,7 @@ UNION ALL -- Get counts by method SELECT 'method' as dimension, method as value, COUNT(*) as count FROM unified_logs -${finalWhere} -WHERE method IS NOT NULL +${methodWhere} GROUP BY method ` diff --git a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.tsx b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.tsx index 911f62f9cf883..f4bd6be13d12e 100644 --- a/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.tsx +++ b/apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.tsx @@ -27,7 +27,6 @@ import { FilterSideBar } from 'components/ui/DataTable/FilterSideBar' import { LiveButton } from 'components/ui/DataTable/LiveButton' import { LiveRow } from 'components/ui/DataTable/LiveRow' import { DataTableProvider } from 'components/ui/DataTable/providers/DataTableProvider' -import { RefreshButton } from 'components/ui/DataTable/RefreshButton' import { TimelineChart } from 'components/ui/DataTable/TimelineChart' import { useUnifiedLogsChartQuery } from 'data/logs/unified-logs-chart-query' import { useUnifiedLogsCountQuery } from 'data/logs/unified-logs-count-query' @@ -45,6 +44,7 @@ import { TabsList_Shadcn_ as TabsList, TabsTrigger_Shadcn_ as TabsTrigger, } from 'ui' +import { RefreshButton } from '../../ui/DataTable/RefreshButton' import { UNIFIED_LOGS_COLUMNS } from './components/Columns' import { MemoizedDataTableSheetContent } from './components/DataTableSheetContent' import { FunctionLogsTab } from './components/FunctionLogsTab' @@ -100,19 +100,36 @@ export const UnifiedLogs = () => { isLoading, isFetching, hasNextPage, - refetch, + refetch: refetchLogs, fetchNextPage, fetchPreviousPage, } = useUnifiedLogsInfiniteQuery({ projectRef, search: searchParameters }) - const { data: counts, isLoading: isLoadingCounts } = useUnifiedLogsCountQuery({ + const { + data: counts, + isLoading: isLoadingCounts, + isFetching: isFetchingCounts, + refetch: refetchCounts, + } = useUnifiedLogsCountQuery({ projectRef, search: searchParameters, }) - const { data: unifiedLogsChart = [] } = useUnifiedLogsChartQuery({ + const { + data: unifiedLogsChart = [], + isFetching: isFetchingCharts, + refetch: refetchCharts, + } = useUnifiedLogsChartQuery({ projectRef, search: searchParameters, }) + const refetchAllData = () => { + refetchLogs() + refetchCounts() + refetchCharts() + } + + const isRefetchingData = isFetching || isFetchingCounts || isFetchingCharts + const rawFlatData = useMemo(() => { return unifiedLogsData?.pages?.flatMap((page) => page.data ?? []) ?? [] }, [unifiedLogsData?.pages]) @@ -275,10 +292,13 @@ export const UnifiedLogs = () => {
- + [ - , + , fetchPreviousPage ? ( > + placeholder?: string } -export function DataTableFilterCommand({ searchParamsParser }: DataTableFilterCommandProps) { +export function DataTableFilterCommand({ + searchParamsParser, + placeholder = 'Search data table...', +}: DataTableFilterCommandProps) { const { table, isLoading, filterFields: _filterFields, getFacetedUniqueValues } = useDataTable() const columnFilters = table.getState().columnFilters const inputRef = useRef(null) @@ -59,7 +62,8 @@ export function DataTableFilterCommand({ searchParamsParser }: DataTableFilterCo const trimmedInputValue = inputValue.trim() - useHotKey(() => setOpen((open) => !open), 'k') + // [Joshen] Temporarily disabling as this conflicts with our current CMD K behaviour + // useHotKey(() => setOpen((open) => !open), 'k') useEffect(() => { // TODO: we could check for ARRAY_DELIMITER or SLIDER_DELIMITER to auto-set filter when typing @@ -139,13 +143,15 @@ export function DataTableFilterCommand({ searchParamsParser }: DataTableFilterCo trimmedInputValue ? 'text-foreground' : 'text-foreground-light' )} > - {trimmedInputValue ? inputValue : 'Search data table...'} + {trimmedInputValue ? inputValue : placeholder} - + {/* [Joshen] Temporarily disabling as this conflicts with existing CMD K shortcut */} + {/* K - + */} + div]:border-none bg', @@ -185,7 +191,7 @@ export function DataTableFilterCommand({ searchParamsParser }: DataTableFilterCo const word = getWordByCaretPosition({ value, caretPosition }) setCurrentWord(word) }} - placeholder="Search data table..." + placeholder={placeholder} className="text-foreground" />
diff --git a/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterResetButton.tsx b/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterResetButton.tsx index d066553265306..2279eb48b17ef 100644 --- a/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterResetButton.tsx +++ b/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterResetButton.tsx @@ -9,8 +9,6 @@ export function DataTableFilterResetButton({ value: _value }: DataTableFi const value = _value as string const column = table.getColumn(value) const filterValue = columnFilters.find((f) => f.id === value)?.value - - // TODO: check if we could useMemo const filters = filterValue ? (Array.isArray(filterValue) ? filterValue : [filterValue]) : [] if (filters.length === 0) return null @@ -18,7 +16,8 @@ export function DataTableFilterResetButton({ value: _value }: DataTableFi return ( ) } diff --git a/apps/studio/components/ui/DataTable/RefreshButton.tsx b/apps/studio/components/ui/DataTable/RefreshButton.tsx index 6aaf245d3aaf1..2adee376572e4 100644 --- a/apps/studio/components/ui/DataTable/RefreshButton.tsx +++ b/apps/studio/components/ui/DataTable/RefreshButton.tsx @@ -1,21 +1,18 @@ import { LoaderCircle, RefreshCcw } from 'lucide-react' import { Button } from 'ui' -import { useDataTable } from './providers/DataTableProvider' - interface RefreshButtonProps { - onClick: () => void + isLoading: boolean + onRefresh: () => void } -export function RefreshButton({ onClick }: RefreshButtonProps) { - const { isLoading } = useDataTable() - +export const RefreshButton = ({ isLoading, onRefresh }: RefreshButtonProps) => { return (
- + {additionalOptions.length > 0 && ( diff --git a/apps/studio/data/logs/unified-logs-infinite-query.ts b/apps/studio/data/logs/unified-logs-infinite-query.ts index ff2f8cba4b4f5..ee7a1cd677f42 100644 --- a/apps/studio/data/logs/unified-logs-infinite-query.ts +++ b/apps/studio/data/logs/unified-logs-infinite-query.ts @@ -116,7 +116,7 @@ async function getUnifiedLogs( status: row.status || 200, method: row.method, host: row.host, - pathname: (row.url || '').replace(/^https?:\/\/[^\/]+/, '') || row.path || '', + pathname: (row.url || '').replace(/^https?:\/\/[^\/]+/, '') || row.pathname || '', event_message: row.event_message || row.body || '', headers: typeof row.headers === 'string' ? JSON.parse(row.headers || '{}') : row.headers || {},