gaurv007 commited on
Commit
ce35a9f
·
verified ·
1 Parent(s): a2da3ec

v3.0: Upload web/app/dashboard-pages/dashboard/page.tsx — zero emojis, Lucide icons, responsive

Browse files
web/app/dashboard-pages/dashboard/page.tsx CHANGED
@@ -1 +1,176 @@
1
- /app/web_final/dashboard.tsx
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createClient } from "@/lib/supabase/server";
2
+ import Link from "next/link";
3
+ import {
4
+ ScanText, ShieldCheck, Tag, AlertTriangle, ClipboardList,
5
+ GitCompare, Cpu, Layers, Clock
6
+ } from "lucide-react";
7
+
8
+ export default async function DashboardPage() {
9
+ const supabase = await createClient();
10
+ const { data: { user } } = await supabase.auth.getUser();
11
+
12
+ const { data: profile } = await supabase
13
+ .from("profiles").select("*").eq("id", user?.id).single();
14
+
15
+ const { data: analyses, count } = await supabase
16
+ .from("analyses").select("*", { count: "exact" }).eq("user_id", user?.id)
17
+ .order("created_at", { ascending: false }).limit(10);
18
+
19
+ const plan = profile?.plan || "free";
20
+ const usedThisMonth = profile?.analyses_this_month || 0;
21
+ const limit = plan === "free" ? 10 : "Unlimited";
22
+
23
+ const avgRisk = analyses && analyses.length > 0
24
+ ? Math.round(analyses.reduce((s: number, a: any) => s + a.risk_score, 0) / analyses.length)
25
+ : null;
26
+ const totalEntities = analyses?.reduce((s: number, a: any) => s + (a.entities?.length || 0), 0) || 0;
27
+ const totalContradictions = analyses?.reduce((s: number, a: any) => s + (a.contradictions?.length || 0), 0) || 0;
28
+ const totalObligations = analyses?.reduce((s: number, a: any) => s + (a.obligations?.length || 0), 0) || 0;
29
+
30
+ return (
31
+ <div className="min-h-screen bg-zinc-50/30">
32
+ <div className="max-w-6xl mx-auto px-4 sm:px-6 py-8 sm:py-12">
33
+ {/* Header */}
34
+ <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-8 sm:mb-10">
35
+ <div>
36
+ <h1 className="text-xl sm:text-2xl font-bold text-gray-900 flex items-center gap-2">
37
+ <ShieldCheck className="w-5 h-5 sm:w-6 sm:h-6 text-indigo-500" />
38
+ Dashboard
39
+ </h1>
40
+ <p className="text-gray-500 text-sm mt-1">Welcome back, {profile?.full_name || user?.email}</p>
41
+ </div>
42
+ <Link href="/dashboard-pages/analyze"
43
+ className="bg-indigo-600 text-white px-5 sm:px-6 py-2.5 sm:py-3 rounded-xl font-semibold hover:bg-indigo-700 transition text-sm whitespace-nowrap">
44
+ + New Scan
45
+ </Link>
46
+ </div>
47
+
48
+ {/* Primary Stats */}
49
+ <div className="grid grid-cols-2 lg:grid-cols-4 gap-3 sm:gap-6 mb-8 sm:mb-10">
50
+ {[
51
+ { label: "Plan", value: plan, capitalize: true },
52
+ { label: "Scans This Month", value: `${usedThisMonth} / ${limit}` },
53
+ { label: "Total Scans", value: String(count || 0) },
54
+ { label: "Avg Risk Score", value: avgRisk !== null ? String(avgRisk) : "\u2014" },
55
+ ].map((s) => (
56
+ <div key={s.label} className="bg-white rounded-xl p-4 sm:p-6 border border-gray-200">
57
+ <p className="text-xs sm:text-sm text-gray-500">{s.label}</p>
58
+ <p className={`text-xl sm:text-2xl font-bold text-gray-900 mt-1 ${s.capitalize ? "capitalize" : ""}`}>{s.value}</p>
59
+ </div>
60
+ ))}
61
+ </div>
62
+
63
+ {/* Analysis Stats */}
64
+ <div className="grid grid-cols-1 sm:grid-cols-3 gap-3 sm:gap-6 mb-8 sm:mb-10">
65
+ {[
66
+ { icon: Tag, label: "Entities Extracted", value: totalEntities, sublabel: "via Legal-BERT NER", color: "bg-blue-50 text-blue-600" },
67
+ { icon: AlertTriangle, label: "Contradictions Found", value: totalContradictions, sublabel: "via DeBERTa NLI model", color: "bg-amber-50 text-amber-600" },
68
+ { icon: ClipboardList, label: "Obligations Tracked", value: totalObligations, sublabel: "with priority scoring", color: "bg-emerald-50 text-emerald-600" },
69
+ ].map((s) => (
70
+ <div key={s.label} className="bg-white rounded-xl p-4 sm:p-6 border border-gray-200 flex items-center gap-4">
71
+ <div className={`w-10 h-10 rounded-lg flex items-center justify-center ${s.color.split(" ")[0]}`}>
72
+ <s.icon className={`w-5 h-5 ${s.color.split(" ")[1]}`} />
73
+ </div>
74
+ <div>
75
+ <p className="text-xs sm:text-sm text-gray-500">{s.label}</p>
76
+ <p className="text-lg sm:text-xl font-bold text-gray-900">{s.value}</p>
77
+ <p className="text-[10px] text-gray-400">{s.sublabel}</p>
78
+ </div>
79
+ </div>
80
+ ))}
81
+ </div>
82
+
83
+ {/* Quick Actions */}
84
+ <div className="grid sm:grid-cols-2 gap-3 sm:gap-6 mb-8 sm:mb-10">
85
+ <Link href="/dashboard-pages/analyze" className="bg-white rounded-xl p-5 sm:p-6 border border-gray-200 hover:border-indigo-200 hover:shadow-sm transition-all group">
86
+ <div className="flex items-center gap-3 mb-2">
87
+ <div className="w-10 h-10 rounded-lg bg-indigo-50 flex items-center justify-center group-hover:bg-indigo-100 transition-colors">
88
+ <ScanText className="w-5 h-5 text-indigo-600" />
89
+ </div>
90
+ <h3 className="font-semibold text-gray-900">Analyze Contract</h3>
91
+ </div>
92
+ <p className="text-sm text-gray-500">Scan with 3 ML models: clause classifier, Legal NER, and NLI contradiction detection.</p>
93
+ </Link>
94
+ <Link href="/dashboard-pages/compare" className="bg-white rounded-xl p-5 sm:p-6 border border-gray-200 hover:border-indigo-200 hover:shadow-sm transition-all group">
95
+ <div className="flex items-center gap-3 mb-2">
96
+ <div className="w-10 h-10 rounded-lg bg-indigo-50 flex items-center justify-center group-hover:bg-indigo-100 transition-colors">
97
+ <GitCompare className="w-5 h-5 text-indigo-600" />
98
+ </div>
99
+ <h3 className="font-semibold text-gray-900">Compare Contracts</h3>
100
+ </div>
101
+ <p className="text-sm text-gray-500">Side-by-side diff with semantic similarity scoring and risk delta.</p>
102
+ </Link>
103
+ </div>
104
+
105
+ {/* Recent Scans */}
106
+ <div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
107
+ <div className="px-4 sm:px-6 py-4 border-b border-gray-100">
108
+ <h2 className="font-semibold text-gray-900">Recent Scans</h2>
109
+ </div>
110
+ {analyses && analyses.length > 0 ? (
111
+ <div className="divide-y divide-gray-100">
112
+ {analyses.map((a: any) => (
113
+ <div key={a.id} className="px-4 sm:px-6 py-4 flex flex-col sm:flex-row sm:items-center justify-between hover:bg-gray-50 gap-2 sm:gap-4">
114
+ <div className="flex-1 min-w-0">
115
+ <p className="text-sm font-medium text-gray-900 truncate">{a.source_url || "Manual scan"}</p>
116
+ <div className="flex items-center gap-2 mt-1 flex-wrap">
117
+ <p className="text-xs text-gray-500">
118
+ {new Date(a.created_at).toLocaleDateString()} · {a.total_clauses} clauses · {a.flagged_count} flagged
119
+ </p>
120
+ {a.entities && a.entities.length > 0 && (
121
+ <span className="inline-flex items-center gap-1 text-[10px] bg-blue-50 text-blue-600 px-1.5 py-0.5 rounded border border-blue-100">
122
+ <Tag className="w-2.5 h-2.5" />{a.entities.length}
123
+ </span>
124
+ )}
125
+ {a.contradictions && a.contradictions.length > 0 && (
126
+ <span className="inline-flex items-center gap-1 text-[10px] bg-amber-50 text-amber-600 px-1.5 py-0.5 rounded border border-amber-100">
127
+ <AlertTriangle className="w-2.5 h-2.5" />{a.contradictions.length}
128
+ </span>
129
+ )}
130
+ {a.obligations && a.obligations.length > 0 && (
131
+ <span className="inline-flex items-center gap-1 text-[10px] bg-emerald-50 text-emerald-600 px-1.5 py-0.5 rounded border border-emerald-100">
132
+ <ClipboardList className="w-2.5 h-2.5" />{a.obligations.length}
133
+ </span>
134
+ )}
135
+ {a.model && a.model !== "regex" && (
136
+ <span className="inline-flex items-center gap-1 text-[10px] bg-indigo-50 text-indigo-600 px-1.5 py-0.5 rounded border border-indigo-100">
137
+ <Cpu className="w-2.5 h-2.5" />ML
138
+ </span>
139
+ )}
140
+ </div>
141
+ </div>
142
+ <span className={`self-start sm:self-auto text-sm font-bold px-3 py-1 rounded-full whitespace-nowrap ${
143
+ a.grade === "F" ? "bg-red-100 text-red-700" :
144
+ a.grade === "D" ? "bg-orange-100 text-orange-700" :
145
+ a.grade === "C" ? "bg-yellow-100 text-yellow-700" :
146
+ "bg-green-100 text-green-700"
147
+ }`}>
148
+ {a.grade} · {a.risk_score}
149
+ </span>
150
+ </div>
151
+ ))}
152
+ </div>
153
+ ) : (
154
+ <div className="px-6 py-12 text-center">
155
+ <Layers className="w-10 h-10 text-zinc-200 mx-auto mb-3" />
156
+ <p className="text-sm text-gray-400">No scans yet. <Link href="/dashboard-pages/analyze" className="text-indigo-600 hover:underline">Start your first scan</Link></p>
157
+ </div>
158
+ )}
159
+ </div>
160
+
161
+ {/* Upgrade CTA */}
162
+ {plan === "free" && (
163
+ <div className="mt-8 bg-indigo-50 border border-indigo-200 rounded-xl p-5 sm:p-6 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
164
+ <div>
165
+ <p className="font-semibold text-indigo-900">Upgrade to Pro</p>
166
+ <p className="text-sm text-indigo-700 mt-1">Unlimited scans, contract comparison, PDF exports, obligation tracking, and team features.</p>
167
+ </div>
168
+ <Link href="/#pricing" className="bg-indigo-600 text-white px-6 py-2.5 rounded-lg font-semibold text-sm hover:bg-indigo-700 transition whitespace-nowrap">
169
+ View Plans
170
+ </Link>
171
+ </div>
172
+ )}
173
+ </div>
174
+ </div>
175
+ );
176
+ }