SPB Git

spb/vquant Public MIT

VibeQuant — AI-powered institutional-grade financial intelligence platform.

TypeScript 84.3% Python 11.7% JavaScript 1.6% CSS 1.5% HTML 0.7%
30.5 KB · 792 lines tsx
Raw Blame History
1/*2 * =============================================================================3 *  VibeQuant (vquant) — AI-Powered Financial Intelligence Platform4 * -----------------------------------------------------------------------------5 *  File:      client/src/pages/admin.tsx6 *7 *  Author:    Simon-Pierre Boucher8 *  Contact:   contact@spboucher.ai9 *  Website:   https://www.spboucher.ai10 *  Demo:      https://www.vquant.ai11 *  License:   MIT (see LICENSE)12 *13 *  Copyright © 2026 Simon-Pierre Boucher. All rights reserved.14 * =============================================================================15 */1617import { useState, useEffect } from "react";18import { useLocation } from "wouter";19import { Shield, Database, FileText, MessageSquare, Trash2, Sparkles, DollarSign, TrendingUp, Users as UsersIcon, Activity, Zap, AlertCircle, RefreshCw } from "lucide-react";20import { Button } from "@/components/ui/button";21import { Input } from "@/components/ui/input";22import { Label } from "@/components/ui/label";23import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";24import { Alert, AlertDescription } from "@/components/ui/alert";25import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";26import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";27import { Badge } from "@/components/ui/badge";2829interface User {30  id: string;31  displayName: string;32  createdAt: string;33}3435interface Session {36  id: string;37  sessionId: string;38  userId: string | null;39  title: string;40  createdAt: string;41  updatedAt: string;42  inputTokens: number;43  outputTokens: number;44  totalCost: number;45}4647interface SharedReport {48  id: string;49  shareId: string;50  question: string;51  createdAt: string;52}5354interface UserMetric {55  userId: string;56  displayName: string;57  inputTokens: number;58  outputTokens: number;59  totalTokens: number;60  cost: number;61  sessionCount: number;62}6364interface Metrics {65  totalInputTokens: number;66  totalOutputTokens: number;67  totalTokens: number;68  totalCost: number;69  totalSessions: number;70  averageCostPerSession: number;71  userMetrics: UserMetric[];72}7374interface DatabaseData {75  users: User[];76  sessions: Session[];77  sharedReports: SharedReport[];78  metrics: Metrics;79}8081interface ActiveUsersData {82  total: number;83  generating: number;84  idle: number;85  error: number;86  users: Array<{87    sessionId: string;88    userId: string | null;89    status: string;90    currentQuery: string | null;91    lastHeartbeat: string;92    userAgent: string | null;93  }>;94}9596interface RealTimeStats {97  activeUsers: {98    total: number;99    generating: number;100  };101  sessions: {102    total: number;103    today: number;104  };105  tokens: {106    total: number;107    input: number;108    output: number;109  };110  cost: {111    total: number;112    today: number;113  };114  errors: {115    lastHour: number;116    rate: number;117  };118}119120export default function Admin() {121  const [, setLocation] = useLocation();122  const [isAuthenticated, setIsAuthenticated] = useState(false);123  const [password, setPassword] = useState("");124  const [error, setError] = useState("");125  const [isLoading, setIsLoading] = useState(true);126  const [databaseData, setDatabaseData] = useState<DatabaseData | null>(null);127  const [activeUsersData, setActiveUsersData] = useState<ActiveUsersData | null>(null);128  const [realTimeStats, setRealTimeStats] = useState<RealTimeStats | null>(null);129  const [autoRefresh, setAutoRefresh] = useState(true);130131  // Auto-login on mount (no password required)132  useEffect(() => {133    const autoLogin = async () => {134      try {135        const response = await fetch("/api/admin/login", {136          method: "POST",137          headers: {138            "Content-Type": "application/json",139          },140          body: JSON.stringify({}),141        });142143        if (response.ok) {144          setIsAuthenticated(true);145          await refreshAllData();146        }147      } catch (err) {148        console.error("Auto-login failed:", err);149      } finally {150        setIsLoading(false);151      }152    };153154    autoLogin();155  }, []);156157  // Auto-refresh data every 10 seconds if enabled158  useEffect(() => {159    if (!isAuthenticated || !autoRefresh) return;160161    const interval = setInterval(() => {162      refreshAllData();163    }, 10000); // Refresh every 10 seconds164165    return () => clearInterval(interval);166  }, [isAuthenticated, autoRefresh]);167168  const handleLogin = async (e: React.FormEvent) => {169    e.preventDefault();170    setError("");171    setIsLoading(true);172173    try {174      const response = await fetch("/api/admin/login", {175        method: "POST",176        headers: {177          "Content-Type": "application/json",178        },179        body: JSON.stringify({ password }),180      });181182      const data = await response.json();183184      if (!response.ok) {185        throw new Error(data.error || "Failed to login");186      }187188      setIsAuthenticated(true);189      await loadDatabaseData();190    } catch (err: any) {191      setError(err.message || "Failed to login");192    } finally {193      setIsLoading(false);194    }195  };196197  const loadDatabaseData = async () => {198    try {199      const response = await fetch("/api/admin/database");200201      if (!response.ok) {202        if (response.status === 403) {203          setIsAuthenticated(false);204          return;205        }206        throw new Error("Failed to load database data");207      }208209      const data = await response.json();210      setDatabaseData(data);211    } catch (err: any) {212      setError(err.message || "Failed to load database data");213    }214  };215216  const loadActiveUsers = async () => {217    try {218      const response = await fetch("/api/analytics/active-users");219220      if (response.ok) {221        const data = await response.json();222        setActiveUsersData(data);223      }224    } catch (err: any) {225      console.error("Failed to load active users:", err);226    }227  };228229  const loadRealTimeStats = async () => {230    try {231      const response = await fetch("/api/analytics/real-time-stats");232233      if (response.ok) {234        const data = await response.json();235        setRealTimeStats(data);236      }237    } catch (err: any) {238      console.error("Failed to load real-time stats:", err);239    }240  };241242  const refreshAllData = async () => {243    await Promise.all([244      loadDatabaseData(),245      loadActiveUsers(),246      loadRealTimeStats(),247    ]);248  };249250  const deleteSession = async (id: string) => {251    if (!confirm("Are you sure you want to delete this session?")) {252      return;253    }254255    try {256      const response = await fetch(`/api/admin/sessions/${id}`, {257        method: "DELETE",258      });259260      if (!response.ok) {261        throw new Error("Failed to delete session");262      }263264      await loadDatabaseData();265    } catch (err: any) {266      setError(err.message || "Failed to delete session");267    }268  };269270  const deleteReport = async (id: string) => {271    if (!confirm("Are you sure you want to delete this shared report?")) {272      return;273    }274275    try {276      const response = await fetch(`/api/admin/reports/${id}`, {277        method: "DELETE",278      });279280      if (!response.ok) {281        throw new Error("Failed to delete report");282      }283284      await loadDatabaseData();285    } catch (err: any) {286      setError(err.message || "Failed to delete report");287    }288  };289290  const formatDate = (dateString: string) => {291    return new Date(dateString).toLocaleString();292  };293294  // Show loading screen during auto-login295  if (!isAuthenticated && isLoading) {296    return (297      <div className="min-h-screen bg-gradient-to-br from-background via-background to-primary/5 flex items-center justify-center">298        <div className="text-center">299          <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-primary/10 mb-4">300            <Database className="w-8 h-8 text-primary animate-pulse" />301          </div>302          <p className="text-muted-foreground">Loading admin panel...</p>303        </div>304      </div>305    );306  }307308  if (!isAuthenticated) {309    return (310      <div className="min-h-screen bg-gradient-to-br from-background via-background to-primary/5 flex flex-col relative overflow-hidden">311        {/* Decorative gradient blobs */}312        <div className="absolute inset-0 overflow-hidden pointer-events-none">313          <div className="absolute top-0 right-0 w-96 h-96 bg-primary/10 rounded-full blur-3xl transform translate-x-1/2 -translate-y-1/2" />314          <div className="absolute bottom-0 left-0 w-96 h-96 bg-purple-500/10 rounded-full blur-3xl transform -translate-x-1/2 translate-y-1/2" />315        </div>316317        {/* Header */}318        <header className="sticky top-0 z-50 w-full border-b border-border/40 bg-background/60 backdrop-blur-xl">319          <div className="container flex h-16 items-center justify-between px-4">320            <button321              onClick={() => setLocation('/')}322              className="flex items-center gap-2 hover-elevate active-elevate-2 px-3 py-1.5 rounded-md cursor-pointer transition-all duration-200"323            >324              <Sparkles className="w-6 h-6 text-primary" />325              <div className="hidden md:flex items-baseline gap-2">326                <span className="text-xl font-semibold bg-gradient-to-r from-primary to-purple-500 bg-clip-text text-transparent">327                  VQuant328                </span>329                <span className="text-sm text-muted-foreground">330                  by Agentilab.ai331                </span>332              </div>333            </button>334          </div>335        </header>336337        {/* Main Content */}338        <main className="flex-1 flex items-center justify-center px-4 py-12 relative z-10">339          <div className="w-full max-w-md">340            <div className="text-center mb-8 animate-in fade-in slide-in-from-top-4 duration-700">341              <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-primary/10 mb-4">342                <Shield className="w-8 h-8 text-primary" />343              </div>344              <h1 className="text-4xl font-bold mb-3 bg-gradient-to-r from-primary via-purple-500 to-primary bg-clip-text text-transparent">345                Admin Access346              </h1>347              <p className="text-muted-foreground">348                Enter the admin password to access the database panel349              </p>350            </div>351352            <Card className="border-border/50 shadow-2xl backdrop-blur-sm bg-card/95">353              <CardHeader className="space-y-3 pb-6">354                <CardTitle className="flex items-center gap-2 text-2xl">355                  <Database className="w-5 h-5 text-primary" />356                  Database Administration357                </CardTitle>358                <CardDescription className="text-base">359                  View and manage all database records360                </CardDescription>361              </CardHeader>362              <CardContent className="pt-0">363                <form onSubmit={handleLogin} className="space-y-5">364                  <div className="space-y-2">365                    <Label htmlFor="admin-password" className="text-sm font-medium">Admin Password</Label>366                    <Input367                      id="admin-password"368                      type="password"369                      placeholder="Enter admin password"370                      value={password}371                      onChange={(e) => setPassword(e.target.value)}372                      required373                      disabled={isLoading}374                      className="h-11 bg-background/50 border-border/50 focus:border-primary transition-all"375                    />376                  </div>377378                  {error && (379                    <Alert variant="destructive">380                      <AlertDescription>{error}</AlertDescription>381                    </Alert>382                  )}383384                  <Button385                    type="submit"386                    className="w-full h-11 bg-gradient-to-r from-primary to-purple-500 hover:from-primary/90 hover:to-purple-500/90 text-white shadow-lg hover:shadow-xl transition-all duration-200"387                    disabled={isLoading}388                  >389                    {isLoading ? "Authenticating..." : "Access Admin Panel"}390                  </Button>391                </form>392              </CardContent>393            </Card>394          </div>395        </main>396      </div>397    );398  }399400  return (401    <div className="min-h-screen bg-gradient-to-br from-background via-background to-primary/5">402      {/* Header */}403      <header className="sticky top-0 z-50 w-full border-b border-border/40 bg-background/60 backdrop-blur-xl">404        <div className="container flex h-16 items-center justify-between px-4">405          <button406            onClick={() => setLocation('/')}407            className="flex items-center gap-2 hover-elevate active-elevate-2 px-3 py-1.5 rounded-md cursor-pointer transition-all duration-200"408          >409            <Sparkles className="w-6 h-6 text-primary" />410            <div className="hidden md:flex items-baseline gap-2">411              <span className="text-xl font-semibold bg-gradient-to-r from-primary to-purple-500 bg-clip-text text-transparent">412                VQuant Admin413              </span>414            </div>415          </button>416          <Button417            variant="outline"418            onClick={() => {419              setIsAuthenticated(false);420              setPassword("");421            }}422          >423            Logout424          </Button>425        </div>426      </header>427428      {/* Main Content */}429      <main className="container px-4 py-8">430        <div className="mb-8">431          <h1 className="text-3xl font-bold mb-2 flex items-center gap-2">432            <Database className="w-8 h-8 text-primary" />433            Database Administration434          </h1>435          <p className="text-muted-foreground">436            View and manage all database records437          </p>438        </div>439440        {error && (441          <Alert variant="destructive" className="mb-6">442            <AlertDescription>{error}</AlertDescription>443          </Alert>444        )}445446        {databaseData && (447          <Tabs defaultValue="metrics" className="w-full">448            <div className="flex items-center justify-between mb-4">449              <TabsList className="grid w-full max-w-2xl grid-cols-3 h-12 bg-muted/50 backdrop-blur-sm">450                <TabsTrigger value="metrics" className="data-[state=active]:bg-background data-[state=active]:shadow-md transition-all">451                  <Activity className="w-4 h-4 mr-2" />452                  Live Metrics453                </TabsTrigger>454                <TabsTrigger value="sessions" className="data-[state=active]:bg-background data-[state=active]:shadow-md transition-all">455                  <MessageSquare className="w-4 h-4 mr-2" />456                  Sessions ({databaseData.sessions.length})457                </TabsTrigger>458                <TabsTrigger value="reports" className="data-[state=active]:bg-background data-[state=active]:shadow-md transition-all">459                  <FileText className="w-4 h-4 mr-2" />460                  Shared Reports ({databaseData.sharedReports.length})461                </TabsTrigger>462              </TabsList>463464              <div className="flex items-center gap-3">465                <Badge variant={autoRefresh ? "default" : "outline"} className="cursor-pointer" onClick={() => setAutoRefresh(!autoRefresh)}>466                  <RefreshCw className={`w-3 h-3 mr-1 ${autoRefresh ? 'animate-spin' : ''}`} />467                  Auto-refresh {autoRefresh ? 'ON' : 'OFF'}468                </Badge>469                <Button variant="outline" size="sm" onClick={refreshAllData}>470                  <RefreshCw className="w-4 h-4 mr-2" />471                  Refresh Now472                </Button>473              </div>474            </div>475476            {/* Metrics Tab */}477            <TabsContent value="metrics" className="mt-6 space-y-6">478              {/* Real-Time Active Users Section */}479              {activeUsersData && (480                <div className="space-y-4">481                  <div className="flex items-center justify-between">482                    <h3 className="text-lg font-semibold flex items-center gap-2">483                      <Zap className="w-5 h-5 text-yellow-500 animate-pulse" />484                      Live Activity Monitor485                    </h3>486                    <div className="flex items-center gap-2">487                      <Badge variant="default" className="bg-green-500 hover:bg-green-600">488                        {activeUsersData.generating} Generating489                      </Badge>490                      <Badge variant="secondary">491                        {activeUsersData.total} Active492                      </Badge>493                    </div>494                  </div>495496                  {activeUsersData.users.length > 0 ? (497                    <Card>498                      <CardContent className="pt-6">499                        <Table>500                          <TableHeader>501                            <TableRow>502                              <TableHead>Session</TableHead>503                              <TableHead>Status</TableHead>504                              <TableHead>Current Query</TableHead>505                              <TableHead>Last Activity</TableHead>506                            </TableRow>507                          </TableHeader>508                          <TableBody>509                            {activeUsersData.users.map((user) => (510                              <TableRow key={user.sessionId}>511                                <TableCell className="font-mono text-xs">512                                  {user.sessionId.substring(0, 12)}...513                                </TableCell>514                                <TableCell>515                                  <Badge516                                    variant={517                                      user.status === 'generating' ? 'default' :518                                      user.status === 'error' ? 'destructive' :519                                      'secondary'520                                    }521                                  >522                                    {user.status === 'generating' && <Zap className="w-3 h-3 mr-1 animate-pulse" />}523                                    {user.status === 'error' && <AlertCircle className="w-3 h-3 mr-1" />}524                                    {user.status}525                                  </Badge>526                                </TableCell>527                                <TableCell className="max-w-md truncate">528                                  {user.currentQuery || '-'}529                                </TableCell>530                                <TableCell className="text-xs text-muted-foreground">531                                  {new Date(user.lastHeartbeat).toLocaleTimeString()}532                                </TableCell>533                              </TableRow>534                            ))}535                          </TableBody>536                        </Table>537                      </CardContent>538                    </Card>539                  ) : (540                    <Card>541                      <CardContent className="pt-6">542                        <div className="text-center py-8 text-muted-foreground">543                          <UsersIcon className="w-12 h-12 mx-auto mb-3 opacity-50" />544                          <p>No active users at the moment</p>545                        </div>546                      </CardContent>547                    </Card>548                  )}549                </div>550              )}551552              {/* Real-Time Stats Overview */}553              {realTimeStats && (554                <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">555                  <Card className="border-green-500/50 bg-green-500/5">556                    <CardHeader className="pb-3">557                      <CardTitle className="text-sm font-medium text-muted-foreground flex items-center gap-2">558                        <Activity className="w-4 h-4 text-green-500" />559                        Active Users Now560                      </CardTitle>561                    </CardHeader>562                    <CardContent>563                      <div className="text-3xl font-bold text-green-500">564                        {realTimeStats.activeUsers.total}565                      </div>566                      <p className="text-xs text-muted-foreground mt-1">567                        {realTimeStats.activeUsers.generating} generating568                      </p>569                    </CardContent>570                  </Card>571572                  <Card className="border-blue-500/50 bg-blue-500/5">573                    <CardHeader className="pb-3">574                      <CardTitle className="text-sm font-medium text-muted-foreground flex items-center gap-2">575                        <MessageSquare className="w-4 h-4 text-blue-500" />576                        Sessions Today577                      </CardTitle>578                    </CardHeader>579                    <CardContent>580                      <div className="text-3xl font-bold text-blue-500">581                        {realTimeStats.sessions.today}582                      </div>583                      <p className="text-xs text-muted-foreground mt-1">584                        of {realTimeStats.sessions.total} total585                      </p>586                    </CardContent>587                  </Card>588589                  <Card className="border-purple-500/50 bg-purple-500/5">590                    <CardHeader className="pb-3">591                      <CardTitle className="text-sm font-medium text-muted-foreground flex items-center gap-2">592                        <DollarSign className="w-4 h-4 text-purple-500" />593                        Cost Today594                      </CardTitle>595                    </CardHeader>596                    <CardContent>597                      <div className="text-3xl font-bold text-purple-500">598                        ${realTimeStats.cost.today.toFixed(3)}599                      </div>600                      <p className="text-xs text-muted-foreground mt-1">601                        ${realTimeStats.cost.total.toFixed(3)} total602                      </p>603                    </CardContent>604                  </Card>605606                  <Card className={`border-${realTimeStats.errors.rate > 10 ? 'red' : 'yellow'}-500/50 bg-${realTimeStats.errors.rate > 10 ? 'red' : 'yellow'}-500/5`}>607                    <CardHeader className="pb-3">608                      <CardTitle className="text-sm font-medium text-muted-foreground flex items-center gap-2">609                        <AlertCircle className={`w-4 h-4 text-${realTimeStats.errors.rate > 10 ? 'red' : 'yellow'}-500`} />610                        Error Rate611                      </CardTitle>612                    </CardHeader>613                    <CardContent>614                      <div className={`text-3xl font-bold text-${realTimeStats.errors.rate > 10 ? 'red' : 'yellow'}-500`}>615                        {realTimeStats.errors.rate.toFixed(1)}%616                      </div>617                      <p className="text-xs text-muted-foreground mt-1">618                        {realTimeStats.errors.lastHour} errors last hour619                      </p>620                    </CardContent>621                  </Card>622                </div>623              )}624625              {/* Historical Overview Cards */}626              <div className="space-y-4">627                <h3 className="text-lg font-semibold flex items-center gap-2">628                  <TrendingUp className="w-5 h-5 text-primary" />629                  Cumulative Metrics630                </h3>631                <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">632                  <Card>633                    <CardHeader className="pb-3">634                      <CardTitle className="text-sm font-medium text-muted-foreground">Total Cost</CardTitle>635                    </CardHeader>636                    <CardContent>637                      <div className="text-2xl font-bold text-primary">638                        ${databaseData.metrics.totalCost.toFixed(4)}639                      </div>640                      <p className="text-xs text-muted-foreground mt-1">641                        Across {databaseData.metrics.totalSessions} sessions642                      </p>643                    </CardContent>644                  </Card>645646                  <Card>647                    <CardHeader className="pb-3">648                      <CardTitle className="text-sm font-medium text-muted-foreground">Total Tokens</CardTitle>649                    </CardHeader>650                    <CardContent>651                      <div className="text-2xl font-bold">652                        {databaseData.metrics.totalTokens.toLocaleString()}653                      </div>654                      <p className="text-xs text-muted-foreground mt-1">655                        {databaseData.metrics.totalInputTokens.toLocaleString()} in / {databaseData.metrics.totalOutputTokens.toLocaleString()} out656                      </p>657                    </CardContent>658                  </Card>659660                  <Card>661                    <CardHeader className="pb-3">662                      <CardTitle className="text-sm font-medium text-muted-foreground">Avg Cost/Session</CardTitle>663                    </CardHeader>664                    <CardContent>665                      <div className="text-2xl font-bold text-green-600">666                        ${databaseData.metrics.averageCostPerSession.toFixed(4)}667                      </div>668                      <p className="text-xs text-muted-foreground mt-1">669                        Per conversation670                      </p>671                    </CardContent>672                  </Card>673674                  <Card>675                    <CardHeader className="pb-3">676                      <CardTitle className="text-sm font-medium text-muted-foreground">Model</CardTitle>677                    </CardHeader>678                    <CardContent>679                      <div className="text-lg font-bold">680                        Opus 4.8681                      </div>682                      <p className="text-xs text-muted-foreground mt-1">683                        $5/$25 per M tokens684                      </p>685                    </CardContent>686                  </Card>687                </div>688              </div>689690            </TabsContent>691692            {/* Sessions Tab */}693            <TabsContent value="sessions" className="mt-6">694              <Card>695                <CardHeader>696                  <CardTitle>All Conversation Sessions</CardTitle>697                  <CardDescription>Manage conversation history</CardDescription>698                </CardHeader>699                <CardContent>700                  <Table>701                    <TableHeader>702                      <TableRow>703                        <TableHead>Session ID</TableHead>704                        <TableHead>User ID</TableHead>705                        <TableHead>Title</TableHead>706                        <TableHead className="text-right">Tokens</TableHead>707                        <TableHead className="text-right">Cost</TableHead>708                        <TableHead>Updated At</TableHead>709                        <TableHead className="text-right">Actions</TableHead>710                      </TableRow>711                    </TableHeader>712                    <TableBody>713                      {databaseData.sessions.map((session) => (714                        <TableRow key={session.id}>715                          <TableCell className="font-mono text-xs">{session.sessionId.substring(0, 8)}...</TableCell>716                          <TableCell className="font-mono text-xs">717                            {session.userId ? `${session.userId.substring(0, 8)}...` : "Anonymous"}718                          </TableCell>719                          <TableCell className="max-w-md truncate">{session.title}</TableCell>720                          <TableCell className="text-right font-mono text-xs">721                            {(session.inputTokens + session.outputTokens).toLocaleString()}722                            <span className="text-muted-foreground ml-1">723                              ({session.inputTokens}↑/{session.outputTokens}↓)724                            </span>725                          </TableCell>726                          <TableCell className="text-right font-medium text-primary">727                            ${session.totalCost.toFixed(4)}728                          </TableCell>729                          <TableCell>{formatDate(session.updatedAt)}</TableCell>730                          <TableCell className="text-right">731                            <Button732                              variant="destructive"733                              size="sm"734                              onClick={() => deleteSession(session.id)}735                            >736                              <Trash2 className="w-4 h-4" />737                            </Button>738                          </TableCell>739                        </TableRow>740                      ))}741                    </TableBody>742                  </Table>743                </CardContent>744              </Card>745            </TabsContent>746747            {/* Shared Reports Tab */}748            <TabsContent value="reports" className="mt-6">749              <Card>750                <CardHeader>751                  <CardTitle>All Shared Reports</CardTitle>752                  <CardDescription>Manage publicly shared reports</CardDescription>753                </CardHeader>754                <CardContent>755                  <Table>756                    <TableHeader>757                      <TableRow>758                        <TableHead>Share ID</TableHead>759                        <TableHead>Question Preview</TableHead>760                        <TableHead>Created At</TableHead>761                        <TableHead className="text-right">Actions</TableHead>762                      </TableRow>763                    </TableHeader>764                    <TableBody>765                      {databaseData.sharedReports.map((report) => (766                        <TableRow key={report.id}>767                          <TableCell className="font-mono text-xs">{report.shareId}</TableCell>768                          <TableCell className="max-w-md truncate">{report.question}</TableCell>769                          <TableCell>{formatDate(report.createdAt)}</TableCell>770                          <TableCell className="text-right">771                            <Button772                              variant="destructive"773                              size="sm"774                              onClick={() => deleteReport(report.id)}775                            >776                              <Trash2 className="w-4 h-4" />777                            </Button>778                          </TableCell>779                        </TableRow>780                      ))}781                    </TableBody>782                  </Table>783                </CardContent>784              </Card>785            </TabsContent>786          </Tabs>787        )}788      </main>789    </div>790  );791}792