Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.DS_Store
.venv
.tinyb
.tb_error.txt
.tmp
.tb_error*
tinybird/fixtures
Expand Down
3 changes: 2 additions & 1 deletion apps/web/.env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
ANTHROPIC_API_KEY=
NEXT_PUBLIC_VERCEL_URL=
NEXT_PUBLIC_VERCEL_URL=
NEXT_PUBLIC_TINYBIRD_API_HOST=
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@radix-ui/react-checkbox": "^1.1.3",
"@radix-ui/react-collapsible": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.4",
"@radix-ui/react-dropdown-menu": "^2.1.4",
"@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-popover": "^1.1.4",
"@radix-ui/react-scroll-area": "^1.2.2",
Expand Down
100 changes: 100 additions & 0 deletions apps/web/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

86 changes: 37 additions & 49 deletions apps/web/src/components/tools/vercel/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import { useQueryState } from 'nuqs'
import { useEffect, useState } from 'react'
import { addDays, format } from 'date-fns'
import { DateRange } from 'react-day-picker'
import { pipe } from '@/lib/tinybird'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { TimeRange, type TimeRange as TR } from '@/components/time-range'
import MetricCard from '@/components/metric-card'
import { DeploymentsChart, DeploymentsData } from './deployments-chart'
import { DurationChart, DurationData } from './duration-chart'
import { ProjectsChart, ProjectData } from './projects-chart'
import { GitAnalyticsChart, GitData } from './git-analytics-chart'
import { VercelDeploymentDuration } from './deployment-duration-ts'
import { VercelDeploymentsOverTime } from './deployments-over-time'
import { GitAnalytics } from './git-analytics'
import { VercelProjects } from './project-stats'
import { pipe } from '@/lib/tinybird'

interface VercelMetrics {
total_deployments: number
Expand All @@ -26,13 +26,9 @@ export default function VercelDashboard() {
from: addDays(new Date(), -7),
to: new Date()
})
const [metrics, setMetrics] = useState<VercelMetrics>()

const [, setIsLoading] = useState(true)
const [metrics, setMetrics] = useState<VercelMetrics>()
const [deploymentsData, setDeploymentsData] = useState<DeploymentsData[]>([])
const [durationData, setDurationData] = useState<DurationData[]>([])
const [projectsData, setProjectsData] = useState<ProjectData[]>([])
const [gitData, setGitData] = useState<GitData[]>([])

useEffect(() => {
async function fetchData() {
Expand All @@ -48,24 +44,10 @@ export default function VercelDashboard() {
setIsLoading(true)
const [
metricsResult,
deploymentsResult,
durationResult,
projectsResult,
gitAnalyticsResult,

] = await Promise.all([
pipe<{ data: VercelMetrics[] }>(token, 'vercel_deployment_metrics', params),
pipe<{ data: DeploymentsData[] }>(token, 'vercel_deployments_over_time', params),
pipe<{ data: DurationData[] }>(token, 'vercel_deployment_duration', params),
pipe<{ data: ProjectData[] }>(token, 'vercel_project_stats', params),
pipe<{ data: GitData[] }>(token, 'vercel_git_analytics', params)
])

setMetrics(metricsResult?.data?.[0])
setDeploymentsData(deploymentsResult?.data ?? [])
setDurationData(durationResult?.data ?? [])
setProjectsData(projectsResult?.data ?? [])
setGitData(gitAnalyticsResult?.data ?? [])
} catch (error) {
console.error('Failed to fetch data:', error)
} finally {
Expand All @@ -78,31 +60,29 @@ export default function VercelDashboard() {

return (
<div className="space-y-8">
<TimeRange
timeRange={timeRange}
onTimeRangeChange={setTimeRange}
dateRange={dateRange}
onDateRangeChange={(range) => setDateRange(range || { from: addDays(new Date(), -7), to: new Date() })}
className="mb-8"
/>
<div className="flex gap-2">
<TimeRange
timeRange={timeRange}
onTimeRangeChange={setTimeRange}
dateRange={dateRange}
onDateRangeChange={(range) => setDateRange(range || { from: addDays(new Date(), -7), to: new Date() })}
className="mb-8"
/>
</div>

{/* Metrics Row */}
<div className="grid gap-4 md:grid-cols-4">
<div className="grid gap-4 md:grid-cols-3">
<MetricCard
title="Total Deployments"
value={metrics?.total_deployments ?? 'N/A'}
/>
<MetricCard
title="Success Rate"
value={metrics?.success_rate ? `${metrics.success_rate}%` : 'N/A'}
/>
<MetricCard
title="Average Deploy Time"
value={durationData.length > 0 ? `${Math.round(durationData.reduce((acc, curr) => acc + curr.avg_duration, 0) / durationData.length)}s` : 'N/A'}
value={metrics?.success_rate ?? 'N/A'}
/>
<MetricCard
title="Error Rate"
value={metrics?.error_rate ? `${metrics.error_rate}%` : 'N/A'}
value={metrics?.error_rate ?? 'N/A'}
/>
</div>

Expand All @@ -113,9 +93,11 @@ export default function VercelDashboard() {
<CardTitle>Deployments Over Time</CardTitle>
</CardHeader>
<CardContent>
<DeploymentsChart
data={deploymentsData}
timeRange={timeRange}
{/* Deployments Over Time chart */}
<VercelDeploymentsOverTime
time_range={timeRange}
date_from={dateRange.from ? format(dateRange.from, 'yyyy-MM-dd HH:mm:ss') : undefined}
date_to={dateRange.to ? format(dateRange.to, 'yyyy-MM-dd HH:mm:ss') : undefined}
/>
</CardContent>
</Card>
Expand All @@ -125,22 +107,26 @@ export default function VercelDashboard() {
<CardTitle>Deploy Duration</CardTitle>
</CardHeader>
<CardContent>
<DurationChart
data={durationData}
timeRange={timeRange} />
{/* Deploy Duration chart */}
<VercelDeploymentDuration
time_range={timeRange}
date_from={dateRange.from ? format(dateRange.from, 'yyyy-MM-dd HH:mm:ss') : undefined}
date_to={dateRange.to ? format(dateRange.to, 'yyyy-MM-dd HH:mm:ss') : undefined}
/>
</CardContent>
</Card>
</div>

{/* Tables and Additional Charts */}
<div className="grid gap-4 md:grid-cols-2">
<Card>
<CardHeader>
<CardTitle>Top Projects</CardTitle>
</CardHeader>
<CardContent>
<ProjectsChart
data={projectsData}
{/* Top Projects table */}
<VercelProjects
date_from={dateRange.from ? format(dateRange.from, 'yyyy-MM-dd HH:mm:ss') : undefined}
date_to={dateRange.to ? format(dateRange.to, 'yyyy-MM-dd HH:mm:ss') : undefined}
/>
</CardContent>
</Card>
Expand All @@ -150,8 +136,10 @@ export default function VercelDashboard() {
<CardTitle>Git Analytics</CardTitle>
</CardHeader>
<CardContent>
<GitAnalyticsChart
data={gitData}
{/* Git Analytics chart */}
<GitAnalytics
date_from={dateRange.from ? format(dateRange.from, 'yyyy-MM-dd HH:mm:ss') : undefined}
date_to={dateRange.to ? format(dateRange.to, 'yyyy-MM-dd HH:mm:ss') : undefined}
/>
</CardContent>
</Card>
Expand Down
24 changes: 24 additions & 0 deletions apps/web/src/components/tools/vercel/deployment-duration-ts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use client'

import { LineChart } from '@tinybirdco/charts'
import { useQueryState } from 'nuqs'

export function VercelDeploymentDuration(params: {
date_from?: string
date_to?: string
time_range?: string
}) {
const [token] = useQueryState('token')
return (
<LineChart
endpoint="https://api.tinybird.co/v0/pipes/vercel_deployment_duration.json"
token={token ?? ''}
index="period"
categories={['avg_duration', 'p95_duration']}
colorPalette={['#27F795', '#008060', '#0EB1B9', '#9263AF', '#5A6FC0', '#86BFDB', '#FFC145', '#FF6B6C', '#DC82C8', '#FFC0F1']}
stacked={true}
height="500px"
params={params}
/>
)
}
Loading
Loading