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
3 changes: 0 additions & 3 deletions .env

This file was deleted.

5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ node_modules
dist
dist-ssr
*.local
.env

# Editor directories and files
.vscode/*
Expand All @@ -23,3 +22,7 @@ dist-ssr
*.njsproj
*.sln
*.sw?

.env
__pycache__/
*.pyc
Binary file removed backend/__pycache__/__init__.cpython-312.pyc
Binary file not shown.
Binary file removed backend/__pycache__/__init__.cpython-313.pyc
Binary file not shown.
Binary file removed backend/__pycache__/__init__.cpython-314.pyc
Binary file not shown.
Binary file removed backend/__pycache__/app.cpython-312.pyc
Binary file not shown.
Binary file removed backend/__pycache__/app.cpython-313.pyc
Binary file not shown.
Binary file removed backend/__pycache__/app.cpython-314.pyc
Binary file not shown.
Binary file removed backend/__pycache__/db.cpython-312.pyc
Binary file not shown.
Binary file removed backend/__pycache__/db.cpython-313.pyc
Binary file not shown.
Binary file removed backend/__pycache__/db.cpython-314.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed backend/__pycache__/master_agent.cpython-312.pyc
Binary file not shown.
Binary file removed backend/__pycache__/master_agent.cpython-313.pyc
Binary file not shown.
Binary file removed backend/__pycache__/master_agent.cpython-314.pyc
Binary file not shown.
Binary file removed backend/__pycache__/services.cpython-312.pyc
Binary file not shown.
Binary file removed backend/__pycache__/services.cpython-313.pyc
Binary file not shown.
Binary file removed backend/__pycache__/services.cpython-314.pyc
Binary file not shown.
Binary file removed checkwise_stats/__pycache__/__init__.cpython-312.pyc
Binary file not shown.
Binary file removed checkwise_stats/__pycache__/__init__.cpython-313.pyc
Binary file not shown.
Binary file removed checkwise_stats/__pycache__/__init__.cpython-314.pyc
Binary file not shown.
Binary file removed checkwise_stats/__pycache__/agent.cpython-312.pyc
Binary file not shown.
Binary file removed checkwise_stats/__pycache__/agent.cpython-313.pyc
Binary file not shown.
Binary file removed checkwise_stats/__pycache__/agent.cpython-314.pyc
Binary file not shown.
Binary file removed checkwise_stats/__pycache__/cli.cpython-312.pyc
Binary file not shown.
Binary file removed checkwise_stats/__pycache__/cli.cpython-314.pyc
Binary file not shown.
Binary file removed checkwise_stats/__pycache__/state.cpython-312.pyc
Binary file not shown.
Binary file removed checkwise_stats/__pycache__/state.cpython-313.pyc
Binary file not shown.
Binary file removed checkwise_stats/__pycache__/state.cpython-314.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
12 changes: 6 additions & 6 deletions src/components/AgentCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,23 @@ const AgentCard = ({ agent, index, isOpen, onToggle, modalContent }: AgentCardPr
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1, duration: 0.4 }}
onClick={onToggle}
className="group cursor-pointer rounded-lg border border-border bg-card p-5 transition-all hover:border-primary/40 hover:cyber-glow"
className="group cursor-pointer rounded-lg border border-slate-200 bg-white p-5 shadow-sm transition-all hover:border-yellow-500/40 hover:shadow-md dark:border-border dark:bg-card dark:shadow-none dark:hover:border-primary/40 dark:hover:cyber-glow"
>
<div className={`mb-3 inline-flex rounded-md bg-gradient-to-br ${agent.color} p-2.5 text-primary-foreground`}>
{iconMap[agent.icon]}
</div>
<h3 className="mb-2 text-sm font-semibold leading-snug">{agent.name}</h3>
<p className="text-xs leading-relaxed text-muted-foreground">{agent.description}</p>
<h3 className="mb-2 text-sm font-semibold leading-snug text-slate-900 dark:text-foreground">{agent.name}</h3>
<p className="text-xs leading-relaxed text-slate-600 dark:text-muted-foreground">{agent.description}</p>
</motion.div>

<Dialog open={isOpen} onOpenChange={(open) => !open && onToggle()}>
<DialogContent className="max-h-[85vh] overflow-y-auto border-border bg-card sm:max-w-xl">
<DialogContent className="max-h-[85vh] overflow-y-auto border-slate-200 bg-white sm:max-w-xl dark:border-border dark:bg-card">
<DialogHeader>
<div className={`mb-3 inline-flex w-fit rounded-md bg-gradient-to-br ${agent.color} p-3 text-primary-foreground`}>
{iconMap[agent.icon]}
</div>
<DialogTitle>{agent.name}</DialogTitle>
<DialogDescription className="max-w-none text-sm leading-7 text-muted-foreground">
<DialogTitle className="text-slate-900 dark:text-foreground">{agent.name}</DialogTitle>
<DialogDescription className="max-w-none text-sm leading-7 text-slate-600 dark:text-muted-foreground">
{agent.details}
</DialogDescription>
</DialogHeader>
Expand Down
310 changes: 310 additions & 0 deletions src/components/AnalysisSummaryModal.tsx

Large diffs are not rendered by default.

40 changes: 21 additions & 19 deletions src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,57 +18,57 @@ const Navbar = ({ isLoggedIn, userEmail, onLogout }: NavbarProps) => {
const links = [
{ to: "/", label: "Home" },
{ to: "/checker", label: "Checker" },
...(isLoggedIn ? [{ to: "/dashboard", label: "Dashboard" }, { to: "/settings", label: "Settings" }] : []),
...(isLoggedIn ? [{ to: "/dashboard", label: "History" }, { to: "/settings", label: "Settings" }] : []),
];

const isActive = (path: string) => location.pathname === path;

return (
<nav className="fixed top-0 left-0 right-0 z-50 border-b border-border bg-background/80 backdrop-blur-xl">
<div className="container mx-auto flex h-16 items-center justify-between px-4">
<Link to="/" className="flex items-center gap-2">
<nav className="fixed top-0 left-0 right-0 z-50 border-b border-slate-200 bg-white backdrop-blur-xl dark:border-border dark:bg-background/80">
<div className="container mx-auto grid h-16 grid-cols-[1fr_auto] items-center px-4 md:grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)]">
<Link to="/" className="flex items-center gap-2 justify-self-start">
<div className="flex h-8 w-8 items-center justify-center rounded-md bg-primary">
<span className="text-sm font-bold text-primary-foreground">✓</span>
</div>
<span className="text-lg font-bold tracking-tight">
Check<span className="text-primary">Wise</span>
<span className="text-lg font-bold tracking-tight text-slate-900 dark:text-foreground">
Check<span className="text-yellow-600 dark:text-primary">Wise</span>
</span>
</Link>

{/* Desktop links */}
<div className="hidden items-center gap-1 md:flex">
<div className="hidden items-center justify-center gap-1 md:flex">
{links.map((l) => (
<Link
key={l.to}
to={l.to}
className={`rounded-md px-3 py-2 text-sm font-medium transition-colors ${
isActive(l.to)
? "bg-primary/10 text-primary"
: "text-muted-foreground hover:text-foreground"
? "bg-yellow-100 text-yellow-700 dark:bg-primary/10 dark:text-primary"
: "text-slate-600 hover:text-slate-900 dark:text-muted-foreground dark:hover:text-foreground"
}`}
>
{l.label}
</Link>
))}
</div>

<div className="flex items-center gap-2">
<div className="flex items-center gap-2 justify-self-end">
<button
onClick={toggle}
className="flex h-9 w-9 items-center justify-center rounded-md border border-border text-muted-foreground transition-colors hover:text-foreground"
className="flex h-9 w-9 items-center justify-center rounded-md border border-slate-200 text-slate-600 transition-colors hover:text-slate-900 dark:border-border dark:text-muted-foreground dark:hover:text-foreground"
>
{isDark ? <Sun size={16} /> : <Moon size={16} />}
</button>

{isLoggedIn ? (
<>
<div className="hidden items-center gap-2 rounded-md border border-border bg-card px-3 py-2 text-sm text-muted-foreground md:flex">
<Mail size={14} className="text-primary" />
<div className="hidden items-center gap-2 rounded-md border border-slate-200 bg-white px-3 py-2 text-sm text-slate-600 lg:flex dark:border-border dark:bg-card dark:text-muted-foreground">
<Mail size={14} className="text-yellow-600 dark:text-primary" />
<span className="max-w-[220px] truncate">{userEmail ?? "Signed in"}</span>
</div>
<button
onClick={onLogout}
className="hidden items-center gap-2 rounded-md border border-border px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:text-foreground md:flex"
className="hidden items-center gap-2 rounded-md border border-slate-200 px-3 py-2 text-sm font-medium text-slate-600 transition-colors hover:text-slate-900 md:flex dark:border-border dark:text-muted-foreground dark:hover:text-foreground"
>
<LogOut size={14} />
Logout
Expand All @@ -85,7 +85,7 @@ const Navbar = ({ isLoggedIn, userEmail, onLogout }: NavbarProps) => {

<button
onClick={() => setMobileOpen(!mobileOpen)}
className="flex h-9 w-9 items-center justify-center rounded-md border border-border text-muted-foreground md:hidden"
className="flex h-9 w-9 items-center justify-center rounded-md border border-slate-200 text-slate-600 md:hidden dark:border-border dark:text-muted-foreground"
>
{mobileOpen ? <X size={16} /> : <Menu size={16} />}
</button>
Expand All @@ -99,7 +99,7 @@ const Navbar = ({ isLoggedIn, userEmail, onLogout }: NavbarProps) => {
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
className="overflow-hidden border-t border-border bg-background md:hidden"
className="overflow-hidden border-t border-slate-200 bg-white md:hidden dark:border-border dark:bg-background"
>
<div className="flex flex-col gap-1 p-4">
{links.map((l) => (
Expand All @@ -108,18 +108,20 @@ const Navbar = ({ isLoggedIn, userEmail, onLogout }: NavbarProps) => {
to={l.to}
onClick={() => setMobileOpen(false)}
className={`rounded-md px-3 py-2 text-sm font-medium ${
isActive(l.to) ? "bg-primary/10 text-primary" : "text-muted-foreground"
isActive(l.to)
? "bg-yellow-100 text-yellow-700 dark:bg-primary/10 dark:text-primary"
: "text-slate-600 dark:text-muted-foreground"
}`}
>
{l.label}
</Link>
))}
{isLoggedIn ? (
<>
<div className="rounded-md border border-border bg-card px-3 py-2 text-sm text-muted-foreground">
<div className="rounded-md border border-slate-200 bg-white px-3 py-2 text-sm text-slate-600 dark:border-border dark:bg-card dark:text-muted-foreground">
{userEmail ?? "Signed in"}
</div>
<button onClick={() => { onLogout(); setMobileOpen(false); }} className="rounded-md px-3 py-2 text-left text-sm font-medium text-muted-foreground">
<button onClick={() => { onLogout(); setMobileOpen(false); }} className="rounded-md px-3 py-2 text-left text-sm font-medium text-slate-600 dark:text-muted-foreground">
Logout
</button>
</>
Expand Down
80 changes: 52 additions & 28 deletions src/pages/DashboardPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { motion } from "framer-motion";
import { useQuery } from "@tanstack/react-query";
import { useState } from "react";
import { Calendar, Percent, FileText } from "lucide-react";
import AnalysisSummaryModal from "@/components/AnalysisSummaryModal";
import { fetchHistory, type HistoryEntry } from "@/lib/statistical-agent";

interface DashboardPageProps {
Expand Down Expand Up @@ -34,6 +36,7 @@ const formatFactCheckingHistoryScore = (item: HistoryEntry) => {
};

const DashboardPage = ({ userEmail }: DashboardPageProps) => {
const [selectedEntry, setSelectedEntry] = useState<HistoryEntry | null>(null);
const historyQuery = useQuery({
queryKey: ["history", userEmail],
queryFn: () => fetchHistory(userEmail ?? ""),
Expand All @@ -45,16 +48,21 @@ const DashboardPage = ({ userEmail }: DashboardPageProps) => {
? `${Math.round(history.reduce((total, item) => total + item.statistical_percentage, 0) / history.length)}%`
: "0%";
const lastCheck = history.length ? new Date(history[0].created_at).toLocaleString() : "No checks yet";
const closeSummaryModal = (open: boolean) => {
if (!open) {
setSelectedEntry(null);
}
};

return (
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} className="min-h-screen px-4 pt-24 pb-12">
<div className="container mx-auto max-w-5xl">
<div className="container mx-auto max-w-6xl">
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }}>
<h1 className="mb-2 text-2xl font-bold">Dashboard</h1>
<p className="mb-6 text-sm text-muted-foreground">Your analysis history at a glance.</p>
<h1 className="mb-2 text-3xl font-bold">History</h1>
<p className="mb-7 text-sm text-muted-foreground">Your analysis history at a glance.</p>
</motion.div>

<div className="mb-6 grid grid-cols-1 gap-3 sm:grid-cols-3">
<div className="mb-6 grid grid-cols-1 gap-4 sm:grid-cols-3">
{[
{ icon: FileText, label: "Total Checks", value: history.length },
{ icon: Percent, label: "Avg AI Likelihood", value: averageScore },
Expand All @@ -65,11 +73,13 @@ const DashboardPage = ({ userEmail }: DashboardPageProps) => {
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
className="rounded-lg border border-border bg-card p-4"
className="min-h-[118px] rounded-lg border border-border bg-card p-5 shadow-sm"
>
<item.icon size={16} className="mb-2 text-primary" />
<div className="text-xl font-bold">{item.value}</div>
<div className="text-xs text-muted-foreground">{item.label}</div>
<div className="mb-4 flex h-9 w-9 items-center justify-center rounded-md border border-border bg-background/50">
<item.icon size={18} className="text-primary" />
</div>
<div className="text-xs font-medium text-muted-foreground">{item.label}</div>
<div className="mt-1 break-words text-lg font-semibold leading-snug text-foreground sm:text-xl">{item.value}</div>
</motion.div>
))}
</div>
Expand All @@ -78,20 +88,20 @@ const DashboardPage = ({ userEmail }: DashboardPageProps) => {
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="overflow-hidden rounded-lg border border-border bg-card"
className="overflow-hidden rounded-lg border border-border bg-card shadow-sm"
>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<table className="w-full min-w-[1040px] border-collapse text-sm">
<thead>
<tr className="border-b border-border bg-muted/50">
<th className="px-4 py-3 text-left font-medium text-muted-foreground">User</th>
<th className="px-4 py-3 text-left font-medium text-muted-foreground">Input Type</th>
<th className="px-4 py-3 text-left font-medium text-muted-foreground">Preview</th>
<th className="px-4 py-3 text-left font-medium text-muted-foreground">Rating</th>
<th className="px-4 py-3 text-left font-medium text-muted-foreground">Statistical Agent</th>
<th className="px-4 py-3 text-left font-medium text-muted-foreground">Grammatical Agent</th>
<th className="px-4 py-3 text-left font-medium text-muted-foreground">Fact-Checking Agent</th>
<th className="px-4 py-3 text-left font-medium text-muted-foreground">Timestamp</th>
<tr className="border-b border-border bg-muted/40">
<th className="px-5 py-4 text-left text-xs font-semibold text-muted-foreground">User</th>
<th className="px-5 py-4 text-left text-xs font-semibold text-muted-foreground">Input Type</th>
<th className="px-5 py-4 text-left text-xs font-semibold text-muted-foreground">Preview</th>
<th className="px-5 py-4 text-left text-xs font-semibold text-muted-foreground">Rating</th>
<th className="px-5 py-4 text-left text-xs font-semibold text-muted-foreground">Statistical Agent</th>
<th className="px-5 py-4 text-left text-xs font-semibold text-muted-foreground">Grammatical Agent</th>
<th className="px-5 py-4 text-left text-xs font-semibold text-muted-foreground">Fact-Checking Agent</th>
<th className="px-5 py-4 text-left text-xs font-semibold text-muted-foreground">Timestamp</th>
</tr>
</thead>
<tbody>
Expand Down Expand Up @@ -124,22 +134,36 @@ const DashboardPage = ({ userEmail }: DashboardPageProps) => {
</tr>
)}
{history.map((item) => (
<tr key={item.id} className="border-b border-border last:border-0 hover:bg-muted/30">
<td className="px-4 py-3 text-muted-foreground">{item.user_email}</td>
<td className="px-4 py-3">{item.input_type}</td>
<td className="max-w-[280px] truncate px-4 py-3">{item.text_preview}</td>
<td className="px-4 py-3">{item.verification_rating}</td>
<td className="px-4 py-3">{item.statistical_percentage}% ({item.confidence})</td>
<td className="px-4 py-3">{formatGrammaticalHistoryScore(item)}</td>
<td className="px-4 py-3">{formatFactCheckingHistoryScore(item)}</td>
<td className="px-4 py-3 text-muted-foreground">{new Date(item.created_at).toLocaleString()}</td>
<tr
key={item.id}
role="button"
tabIndex={0}
aria-label={`Open analysis summary for ${item.text_preview}`}
onClick={() => setSelectedEntry(item)}
onKeyDown={(event) => {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
setSelectedEntry(item);
}
}}
className="cursor-pointer border-b border-border transition-colors last:border-0 hover:bg-muted/40 focus-visible:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary/50"
>
<td className="px-5 py-4 text-muted-foreground">{item.user_email}</td>
<td className="px-5 py-4">{item.input_type}</td>
<td className="max-w-[300px] truncate px-5 py-4">{item.text_preview}</td>
<td className="px-5 py-4 font-medium">{item.verification_rating}</td>
<td className="px-5 py-4">{item.statistical_percentage}% ({item.confidence})</td>
<td className="px-5 py-4">{formatGrammaticalHistoryScore(item)}</td>
<td className="px-5 py-4">{formatFactCheckingHistoryScore(item)}</td>
<td className="px-5 py-4 text-muted-foreground">{new Date(item.created_at).toLocaleString()}</td>
</tr>
))}
</tbody>
</table>
</div>
</motion.div>
</div>
<AnalysisSummaryModal entry={selectedEntry} open={Boolean(selectedEntry)} onOpenChange={closeSummaryModal} />
</motion.div>
);
};
Expand Down
Loading
Loading