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 (
}
+ className="h-5 rounded-full px-1.5 py-1 font-mono text-[10px] [&>span]:-translate-y-[0.6px] space-x-1"
onClick={(e) => {
e.stopPropagation()
column?.setFilterValue(undefined)
@@ -29,13 +28,8 @@ export function DataTableFilterResetButton({ value: _value }: DataTableFi
column?.setFilterValue(undefined)
}
}}
- icon={}
- asChild
>
- {/* REMINDER: `AccordionTrigger` is also a button(!) and we get Hydration error when rendering button within button */}
-
- {filters.length}
-
+ {filters.length}
)
}
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 || {},