All files / features / dashboard DashboardPage.tsx

56.25% Statements 27/48
50% Branches 7/14
35% Functions 7/20
58.69% Lines 27/46

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91                      69x   69x   82x 82x 82x 82x 82x 82x 82x 82x 82x                                                                     82x         1x                           410x                          
import { useState, useRef } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { Activity, Calendar, Ban, LogIn, LogOut, Play } from 'lucide-react'
import { useDashboardStats } from './dashboardApi'
import { useRunColumns } from '@/features/runs/hooks/useRunColumns'
import { StatsCard } from '@/shared/components/StatsCard'
import { DataTable } from '@/shared/components/DataTable'
import { CrudModal } from '@/shared/components/CrudModal'
import { Page, PageHeader } from '@/shared/components/Page'
import { RunForm } from '@/features/runs/components/RunForm'
import type { components } from '@/types/api'
import type { CrudFormRef } from '@/features/runs/components/RunForm'
import { Button } from '@/components/ui/button'
 
type Run = components['schemas']['Run']
 
export function DashboardPage() {
  const [searchParams, setSearchParams] = useSearchParams()
  const nav = useNavigate()
  const { stats, runs, isLoading, error, refetch } = useDashboardStats()
  const columns = useRunColumns()
  const [formDirty, setFormDirty] = useState(false)
  const [formPending, setFormPending] = useState(false)
  const formRef = useRef<CrudFormRef>(null)
 
  const statCards = [
    { icon: Activity, label: 'Running', value: stats.running, accent: 'border-blue-500', onClick: () => nav('/runs?phase=running&sort=created_at&order=desc') },
    { icon: Calendar, label: 'Today', value: stats.today, accent: 'border-gray-400', onClick: () => nav('/runs?created_after=24h&sort=created_at&order=desc') },
    { icon: Ban, label: 'Failed', value: stats.failed, accent: 'border-red-500', onClick: () => nav('/runs?phase=failed&created_after=30d&sort=created_at&order=desc') },
    { icon: LogIn, label: 'Token In', value: stats.tokenInput, accent: 'border-purple-500' },
    { icon: LogOut, label: 'Token Out', value: stats.tokenOutput, accent: 'border-orange-500' },
  ]
 
  return (
    <Page>
      <PageHeader title="Dashboard" actions={
        <Button variant="outline" onClick={() => nav('/runs?sort=created_at&order=desc')}>
          View All Runs →
        </Button>
      } />
 
      <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5">
        {statCards.map((card) => (
          <div key={card.label} onClick={card.onClick} className={card.onClick ? 'cursor-pointer' : ''}>
            <StatsCard icon={card.icon} label={card.label} value={card.value} accent={card.accent} />
          </div>
        ))}
      </div>
      <p className="-mt-2 text-xs text-muted-foreground">Token stats based on last 50 runs</p>
 
      <div className="flex items-center justify-end">
        <Button
          onClick={() => setSearchParams(prev => { prev.set('create', 'true'); return prev })}
          size="sm"
        >
          <Play className="mr-2 h-4 w-4" />
          New Run
        </Button>
      </div>
 
      <DataTable<Run>
        columns={columns}
        data={runs}
        isLoading={isLoading}
        error={error}
        onRetry={refetch}
        entityName="Run"
        onRowClick={(item) => nav(`/runs/${item.run_id}`)}
      />
 
      <CrudModal
        open={searchParams.get('create') === 'true'}
        title="Trigger Run"
        isDirty={formDirty}
        isPending={formPending}
        saveLabel="Trigger Run"
        onClose={() => setSearchParams(prev => { prev.delete('create'); return prev })}
        onSave={() => formRef.current?.submit()}
      >
        <RunForm
          ref={formRef}
          onSuccess={() => setSearchParams({})}
          onClose={() => setSearchParams(prev => { prev.delete('create'); return prev })}
          onDirtyChange={setFormDirty}
          onPendingChange={setFormPending}
        />
      </CrudModal>
    </Page>
  )
}