diff --git a/apps/studio/components/interfaces/QueryInsights/QueryQuickGlance.tsx b/apps/studio/components/interfaces/QueryInsights/QueryQuickGlance.tsx
index e599503674a..5681cbc700a 100644
--- a/apps/studio/components/interfaces/QueryInsights/QueryQuickGlance.tsx
+++ b/apps/studio/components/interfaces/QueryInsights/QueryQuickGlance.tsx
@@ -1,7 +1,118 @@
+import { useMemo } from 'react'
+import { useQueryInsightsGlanceQuery } from 'data/query-insights/query-glance-query'
+import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
+import { Card, CardContent } from 'ui'
+import { Database, BarChart3, Clock, AlertCircle } from 'lucide-react'
+
export const QueryQuickGlance = () => {
+ const { data: project } = useSelectedProjectQuery()
+
+ // Memoize time range to prevent constant re-renders
+ const { startTime, endTime } = useMemo(() => {
+ const endTime = new Date().toISOString()
+ const startTime = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()
+ return { startTime, endTime }
+ }, []) // Empty dependency array - only calculate once
+
+ const {
+ data: glanceData,
+ isLoading,
+ error,
+ } = useQueryInsightsGlanceQuery({
+ projectRef: project?.ref,
+ connectionString: project?.connectionString,
+ startTime,
+ endTime,
+ })
+
+ const cards = useMemo(() => {
+ if (!glanceData) return []
+
+ return [
+ {
+ icon: ,
+ title: 'Queries',
+ values: [
+ {
+ label: 'Unique',
+ value: glanceData.unique_queries || 0,
+ },
+ {
+ label: 'Total',
+ value: glanceData.total_queries || 0,
+ },
+ ],
+ },
+ {
+ icon: ,
+ title: 'Query Time',
+ values: [
+ {
+ label: 'Average',
+ value: glanceData.avg_query_time?.toFixed(2) || 0,
+ },
+ {
+ label: 'Max',
+ value: glanceData.max_query_time?.toFixed(2) || 0,
+ },
+ ],
+ },
+ {
+ icon: ,
+ title: 'Errors',
+ values: [
+ {
+ label: 'Count',
+ value: glanceData.error_count || 0,
+ },
+ {
+ label: 'Rate',
+ value: glanceData.avg_error_rate?.toFixed(2) || 0,
+ },
+ ],
+ },
+ ]
+ }, [glanceData])
+
+ if (isLoading) {
+ return (
+
+ {/* {[...Array(6)].map((_, i) => (
+
+ ))} */}
+ Loading...
+
+ )
+ }
+
+ if (error) {
+ return Error loading query insights: {error.message}
+ }
+
return (
- <>
- At a glance blocks here...
- >
+
+ {cards.map((card, index) => (
+
+
+
+ {card.icon}
+
+ {card.title}
+
+
+
+ {card.values.map((value, index) => (
+
+
+ {value.label}{' '}
+
+ {value.value}
+
+ ))}
+
+
+
+ ))}
+
)
}
diff --git a/apps/studio/data/query-insights/keys.ts b/apps/studio/data/query-insights/keys.ts
new file mode 100644
index 00000000000..6db7854a9c4
--- /dev/null
+++ b/apps/studio/data/query-insights/keys.ts
@@ -0,0 +1,13 @@
+import type { QueryKey } from '@tanstack/react-query'
+
+export const queryInsightsKeys = {
+ all: (projectRef: string | undefined) => ['projects', projectRef, 'query-insights'] as const,
+ metrics: (projectRef: string | undefined, metric: string, startTime: string, endTime: string) =>
+ ['projects', projectRef, 'query-insights', 'metrics', metric, startTime, endTime] as const,
+ queries: (projectRef: string | undefined, startTime: string, endTime: string) =>
+ ['projects', projectRef, 'query-insights', 'queries', startTime, endTime] as const,
+ queriesWithErrors: (projectRef: string | undefined, startTime: string, endTime: string) =>
+ ['projects', projectRef, 'query-insights', 'queries-with-errors', startTime, endTime] as const,
+ glance: (projectRef: string | undefined, startTime: string, endTime: string) =>
+ ['projects', projectRef, 'query-insights', 'glance', startTime, endTime] as const,
+}
\ No newline at end of file
diff --git a/apps/studio/data/query-insights/query-glance-query.ts b/apps/studio/data/query-insights/query-glance-query.ts
new file mode 100644
index 00000000000..bdb301b0bd6
--- /dev/null
+++ b/apps/studio/data/query-insights/query-glance-query.ts
@@ -0,0 +1,73 @@
+import { useQuery, UseQueryOptions } from '@tanstack/react-query'
+import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
+import { queryInsightsKeys } from './keys'
+
+export type QueryInsightsGlance = {
+ unique_queries: number
+ total_queries: number
+ avg_query_time: number
+ max_query_time: number
+ error_count: number
+ avg_error_rate: number
+}
+
+export const getQueriesGlanceMetrics = (startTime: string, endTime: string) => /* SQL */ `
+ SELECT
+ COUNT(DISTINCT query) as unique_queries,
+ SUM(calls) as total_queries,
+ AVG(mean_exec_time) as avg_query_time,
+ MAX(mean_exec_time) as max_query_time,
+ COUNT(CASE WHEN mean_exec_time > 1000 AND calls > 1 THEN 1 END) as error_count,
+ CASE
+ WHEN SUM(calls) > 0 THEN
+ (COUNT(CASE WHEN mean_exec_time > 1000 AND calls > 1 THEN 1 END)::float / SUM(calls)::float) * 100
+ ELSE 0
+ END as avg_error_rate
+ FROM pg_stat_monitor
+ WHERE bucket_start_time >= '${startTime}'::timestamptz
+ AND bucket_start_time <= '${endTime}'::timestamptz
+ AND bucket_done = true
+`.trim()
+
+export type QueryInsightsGlanceVariables = {
+ projectRef?: string
+ connectionString?: string | null
+ startTime: string
+ endTime: string
+}
+
+export async function getQueryInsightsGlance(
+ { projectRef, connectionString, startTime, endTime }: QueryInsightsGlanceVariables,
+ signal?: AbortSignal
+) {
+ const sql = getQueriesGlanceMetrics(startTime, endTime)
+
+ const { result } = await executeSql(
+ { projectRef, connectionString, sql, queryKey: queryInsightsKeys.glance(projectRef, startTime, endTime) },
+ signal
+ )
+
+ return result[0] as QueryInsightsGlance
+}
+
+export type QueryInsightsGlanceData = Awaited>
+export type QueryInsightsGlanceError = ExecuteSqlError
+
+export const useQueryInsightsGlanceQuery = (
+ { projectRef, connectionString, startTime, endTime }: QueryInsightsGlanceVariables,
+ { enabled = true, ...options }: UseQueryOptions = {}
+) => {
+ return useQuery(
+ queryInsightsKeys.glance(projectRef, startTime, endTime),
+ ({ signal }) => getQueryInsightsGlance({ projectRef, connectionString, startTime, endTime }, signal),
+ {
+ enabled: enabled && typeof projectRef !== 'undefined',
+ staleTime: 5 * 60 * 1000, // 5 minutes
+ cacheTime: 10 * 60 * 1000, // 10 minutes
+ refetchOnWindowFocus: false,
+ refetchOnMount: false,
+ refetchOnReconnect: false,
+ ...options
+ }
+ )
+}