Vedant Jigarbhai Mehta commited on
Commit
dbab84d
·
1 Parent(s): eac153b

add landing page with logo and routing to dashboard

Browse files
frontend/src/App.jsx CHANGED
@@ -1,5 +1,6 @@
1
  import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
2
  import MainLayout from './components/layout/MainLayout'
 
3
  import Overview from './pages/Overview'
4
  import TimeSeries from './pages/TimeSeries'
5
  import Network from './pages/Network'
@@ -7,19 +8,28 @@ import Clusters from './pages/Clusters'
7
  import Search from './pages/Search'
8
  import Embeddings from './pages/Embeddings'
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  function App() {
11
  return (
12
  <Router>
13
- <MainLayout>
14
- <Routes>
15
- <Route path="/" element={<Overview />} />
16
- <Route path="/timeseries" element={<TimeSeries />} />
17
- <Route path="/network" element={<Network />} />
18
- <Route path="/clusters" element={<Clusters />} />
19
- <Route path="/search" element={<Search />} />
20
- <Route path="/embeddings" element={<Embeddings />} />
21
- </Routes>
22
- </MainLayout>
23
  </Router>
24
  )
25
  }
 
1
  import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
2
  import MainLayout from './components/layout/MainLayout'
3
+ import Landing from './pages/Landing'
4
  import Overview from './pages/Overview'
5
  import TimeSeries from './pages/TimeSeries'
6
  import Network from './pages/Network'
 
8
  import Search from './pages/Search'
9
  import Embeddings from './pages/Embeddings'
10
 
11
+ function DashboardRoutes() {
12
+ return (
13
+ <MainLayout>
14
+ <Routes>
15
+ <Route path="/" element={<Overview />} />
16
+ <Route path="/timeseries" element={<TimeSeries />} />
17
+ <Route path="/network" element={<Network />} />
18
+ <Route path="/clusters" element={<Clusters />} />
19
+ <Route path="/search" element={<Search />} />
20
+ <Route path="/embeddings" element={<Embeddings />} />
21
+ </Routes>
22
+ </MainLayout>
23
+ )
24
+ }
25
+
26
  function App() {
27
  return (
28
  <Router>
29
+ <Routes>
30
+ <Route path="/" element={<Landing />} />
31
+ <Route path="/dashboard/*" element={<DashboardRoutes />} />
32
+ </Routes>
 
 
 
 
 
 
33
  </Router>
34
  )
35
  }
frontend/src/components/common/Logo.jsx ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export default function Logo({ size = 32, className = '' }) {
2
+ return (
3
+ <svg
4
+ width={size}
5
+ height={size}
6
+ viewBox="0 0 64 64"
7
+ fill="none"
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ className={className}
10
+ >
11
+ <defs>
12
+ <linearGradient id="logo-gradient" x1="0" y1="0" x2="64" y2="64" gradientUnits="userSpaceOnUse">
13
+ <stop offset="0%" stopColor="#f59e0b" />
14
+ <stop offset="100%" stopColor="#ea580c" />
15
+ </linearGradient>
16
+ <linearGradient id="logo-gradient-light" x1="0" y1="0" x2="64" y2="64" gradientUnits="userSpaceOnUse">
17
+ <stop offset="0%" stopColor="#fbbf24" />
18
+ <stop offset="100%" stopColor="#f97316" />
19
+ </linearGradient>
20
+ </defs>
21
+
22
+ {/* Outer ring */}
23
+ <circle cx="32" cy="32" r="28" stroke="url(#logo-gradient)" strokeWidth="3" fill="none" />
24
+
25
+ {/* Middle ring */}
26
+ <circle cx="32" cy="32" r="20" stroke="url(#logo-gradient-light)" strokeWidth="2" fill="none" opacity="0.7" />
27
+
28
+ {/* Crosshair lines */}
29
+ <line x1="32" y1="2" x2="32" y2="14" stroke="url(#logo-gradient)" strokeWidth="2" strokeLinecap="round" />
30
+ <line x1="32" y1="50" x2="32" y2="62" stroke="url(#logo-gradient)" strokeWidth="2" strokeLinecap="round" />
31
+ <line x1="2" y1="32" x2="14" y2="32" stroke="url(#logo-gradient)" strokeWidth="2" strokeLinecap="round" />
32
+ <line x1="50" y1="32" x2="62" y2="32" stroke="url(#logo-gradient)" strokeWidth="2" strokeLinecap="round" />
33
+
34
+ {/* Network nodes inside */}
35
+ <circle cx="22" cy="24" r="3" fill="url(#logo-gradient)" />
36
+ <circle cx="42" cy="22" r="3" fill="url(#logo-gradient)" />
37
+ <circle cx="32" cy="38" r="3" fill="url(#logo-gradient)" />
38
+ <circle cx="20" cy="40" r="2.5" fill="url(#logo-gradient-light)" />
39
+ <circle cx="44" cy="40" r="2.5" fill="url(#logo-gradient-light)" />
40
+
41
+ {/* Connecting lines */}
42
+ <line x1="22" y1="24" x2="42" y2="22" stroke="url(#logo-gradient-light)" strokeWidth="1.5" opacity="0.6" />
43
+ <line x1="22" y1="24" x2="32" y2="38" stroke="url(#logo-gradient-light)" strokeWidth="1.5" opacity="0.6" />
44
+ <line x1="42" y1="22" x2="32" y2="38" stroke="url(#logo-gradient-light)" strokeWidth="1.5" opacity="0.6" />
45
+ <line x1="20" y1="40" x2="32" y2="38" stroke="url(#logo-gradient-light)" strokeWidth="1.5" opacity="0.6" />
46
+ <line x1="44" y1="40" x2="32" y2="38" stroke="url(#logo-gradient-light)" strokeWidth="1.5" opacity="0.6" />
47
+
48
+ {/* Center dot */}
49
+ <circle cx="32" cy="32" r="2" fill="url(#logo-gradient)" />
50
+ </svg>
51
+ )
52
+ }
frontend/src/components/layout/TopNavbar.jsx CHANGED
@@ -1,12 +1,13 @@
1
- import { NavLink } from 'react-router-dom'
 
2
 
3
  const navItems = [
4
- { path: '/', label: 'Overview' },
5
- { path: '/timeseries', label: 'Time Series' },
6
- { path: '/network', label: 'Network' },
7
- { path: '/clusters', label: 'Topics' },
8
- { path: '/search', label: 'SearchAI' },
9
- { path: '/embeddings', label: 'Embeddings' },
10
  ]
11
 
12
  export default function TopNavbar({ darkMode, setDarkMode }) {
@@ -17,10 +18,8 @@ export default function TopNavbar({ darkMode, setDarkMode }) {
17
  : 'bg-white/70 border-gray-200/50'
18
  }`}>
19
  <div className="max-w-7xl mx-auto h-full flex items-center justify-between px-6">
20
- <div className="flex items-center gap-2.5">
21
- <div className="w-8 h-8 bg-gradient-to-br from-amber-500 to-orange-600 rounded-lg flex items-center justify-center shadow-md">
22
- <span className="text-white font-bold text-sm">T</span>
23
- </div>
24
  <div>
25
  <h1 className={`text-base font-bold leading-tight ${darkMode ? 'text-white' : 'text-gray-900'}`} style={{ fontFamily: "'DM Serif Display', Georgia, serif" }}>
26
  TheScope
@@ -29,14 +28,14 @@ export default function TopNavbar({ darkMode, setDarkMode }) {
29
  Political Discourse Analysis
30
  </p>
31
  </div>
32
- </div>
33
 
34
  <div className="flex items-center gap-1">
35
  {navItems.map(({ path, label }) => (
36
  <NavLink
37
  key={path}
38
  to={path}
39
- end={path === '/'}
40
  className={({ isActive }) =>
41
  `px-3 py-1.5 text-sm rounded-lg transition-all ${
42
  isActive
 
1
+ import { NavLink, Link } from 'react-router-dom'
2
+ import Logo from '../common/Logo'
3
 
4
  const navItems = [
5
+ { path: '/dashboard', label: 'Overview' },
6
+ { path: '/dashboard/timeseries', label: 'Time Series' },
7
+ { path: '/dashboard/network', label: 'Network' },
8
+ { path: '/dashboard/clusters', label: 'Topics' },
9
+ { path: '/dashboard/search', label: 'SearchAI' },
10
+ { path: '/dashboard/embeddings', label: 'Embeddings' },
11
  ]
12
 
13
  export default function TopNavbar({ darkMode, setDarkMode }) {
 
18
  : 'bg-white/70 border-gray-200/50'
19
  }`}>
20
  <div className="max-w-7xl mx-auto h-full flex items-center justify-between px-6">
21
+ <Link to="/" className="flex items-center gap-2.5 hover:opacity-80 transition-opacity">
22
+ <Logo size={32} />
 
 
23
  <div>
24
  <h1 className={`text-base font-bold leading-tight ${darkMode ? 'text-white' : 'text-gray-900'}`} style={{ fontFamily: "'DM Serif Display', Georgia, serif" }}>
25
  TheScope
 
28
  Political Discourse Analysis
29
  </p>
30
  </div>
31
+ </Link>
32
 
33
  <div className="flex items-center gap-1">
34
  {navItems.map(({ path, label }) => (
35
  <NavLink
36
  key={path}
37
  to={path}
38
+ end={path === '/dashboard'}
39
  className={({ isActive }) =>
40
  `px-3 py-1.5 text-sm rounded-lg transition-all ${
41
  isActive
frontend/src/pages/Clusters.jsx CHANGED
@@ -77,7 +77,7 @@ export default function Clusters() {
77
  <p className="text-xs text-gray-400">
78
  {clusters.length} clusters · {totalPosts.toLocaleString()} posts · KMeans on 384-dim embeddings
79
  </p>
80
- <Link to="/embeddings" className="text-xs text-indigo-600 hover:text-indigo-800 font-medium">
81
  Explore full embedding map →
82
  </Link>
83
  </div>
 
77
  <p className="text-xs text-gray-400">
78
  {clusters.length} clusters · {totalPosts.toLocaleString()} posts · KMeans on 384-dim embeddings
79
  </p>
80
+ <Link to="/dashboard/embeddings" className="text-xs text-indigo-600 hover:text-indigo-800 font-medium">
81
  Explore full embedding map →
82
  </Link>
83
  </div>
frontend/src/pages/Landing.jsx ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useEffect, useState } from 'react'
2
+ import { useNavigate } from 'react-router-dom'
3
+ import Logo from '../components/common/Logo'
4
+
5
+ export default function Landing() {
6
+ const navigate = useNavigate()
7
+ const [mounted, setMounted] = useState(false)
8
+
9
+ useEffect(() => {
10
+ setMounted(true)
11
+ }, [])
12
+
13
+ return (
14
+ <div className="min-h-screen bg-gray-950 text-white overflow-hidden relative">
15
+ {/* Subtle grid background */}
16
+ <div className="absolute inset-0 opacity-[0.07]"
17
+ style={{
18
+ backgroundImage: `linear-gradient(rgba(255, 255, 255, 0.5) 1px, transparent 1px),
19
+ linear-gradient(90deg, rgba(255, 255, 255, 0.5) 1px, transparent 1px)`,
20
+ backgroundSize: '80px 80px',
21
+ }}
22
+ />
23
+
24
+ <style>{`
25
+ @keyframes fade-up {
26
+ from { opacity: 0; transform: translateY(20px); }
27
+ to { opacity: 1; transform: translateY(0); }
28
+ }
29
+ @keyframes fade-in {
30
+ from { opacity: 0; }
31
+ to { opacity: 1; }
32
+ }
33
+ @keyframes scale-in {
34
+ from { opacity: 0; transform: scale(0.5) rotate(-180deg); }
35
+ to { opacity: 1; transform: scale(1) rotate(0deg); }
36
+ }
37
+ @keyframes letter-in {
38
+ from { opacity: 0; transform: translateY(40px); }
39
+ to { opacity: 1; transform: translateY(0); }
40
+ }
41
+ @keyframes glow-pulse {
42
+ 0%, 100% { filter: drop-shadow(0 0 20px rgba(245, 158, 11, 0.3)); }
43
+ 50% { filter: drop-shadow(0 0 40px rgba(245, 158, 11, 0.6)); }
44
+ }
45
+ .fade-up {
46
+ animation: fade-up 0.8s ease-out forwards;
47
+ opacity: 0;
48
+ }
49
+ .fade-in {
50
+ animation: fade-in 1.2s ease-out forwards;
51
+ opacity: 0;
52
+ }
53
+ .scale-in {
54
+ animation: scale-in 1.2s cubic-bezier(0.34, 1.56, 0.64, 1) forwards, glow-pulse 4s ease-in-out 1.2s infinite;
55
+ opacity: 0;
56
+ }
57
+ .letter-in {
58
+ animation: letter-in 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
59
+ opacity: 0;
60
+ display: inline-block;
61
+ }
62
+ `}</style>
63
+
64
+ {/* Navigation Bar */}
65
+ <nav className={`relative z-10 px-8 py-6 flex items-center justify-between border-b border-white/5 ${mounted ? 'fade-in' : 'opacity-0'}`}>
66
+ <div className="flex items-center gap-2.5">
67
+ <Logo size={28} />
68
+ <h1 className="text-xl font-bold tracking-tight" style={{ fontFamily: "'DM Serif Display', Georgia, serif" }}>
69
+ TheScope
70
+ </h1>
71
+ </div>
72
+
73
+ <div className="flex items-center gap-8 text-sm text-gray-500">
74
+ <a href="#features" className="hover:text-white transition-colors">Features</a>
75
+ <a href="#stats" className="hover:text-white transition-colors">Stats</a>
76
+ <a href="https://github.com/coderved63/research-engineering-intern-assignment" target="_blank" rel="noopener noreferrer"
77
+ className="hover:text-white transition-colors">GitHub</a>
78
+ <button onClick={() => navigate('/dashboard')}
79
+ className="px-4 py-2 bg-white text-gray-950 font-medium rounded-full hover:bg-gray-200 transition-colors text-xs">
80
+ Open Dashboard
81
+ </button>
82
+ </div>
83
+ </nav>
84
+
85
+ {/* Hero Section */}
86
+ <main className="relative z-10 max-w-6xl mx-auto px-8 pt-32 pb-24">
87
+ <div className="text-center flex flex-col items-center">
88
+ {/* Centered Logo with scale-in + glow */}
89
+ <div className={`mb-10 ${mounted ? 'scale-in' : 'opacity-0'}`}>
90
+ <Logo size={120} />
91
+ </div>
92
+
93
+ {/* TheScope name with letter-by-letter animation */}
94
+ <h1 className="text-7xl md:text-9xl font-bold mb-8 tracking-tight"
95
+ style={{ fontFamily: "'DM Serif Display', Georgia, serif" }}>
96
+ {'TheScope'.split('').map((letter, i) => (
97
+ <span
98
+ key={i}
99
+ className={mounted ? 'letter-in' : 'opacity-0'}
100
+ style={{ animationDelay: `${0.6 + i * 0.08}s` }}
101
+ >
102
+ {letter}
103
+ </span>
104
+ ))}
105
+ </h1>
106
+
107
+ {/* Tagline */}
108
+ <p className={`text-2xl md:text-3xl text-gray-400 mb-14 ${mounted ? 'fade-up' : 'opacity-0'}`}
109
+ style={{ animationDelay: '1.6s' }}>
110
+ Tracing how{' '}
111
+ <span className="bg-gradient-to-r from-amber-400 via-orange-500 to-red-500 bg-clip-text text-transparent font-medium">
112
+ narratives spread
113
+ </span>
114
+ </p>
115
+
116
+ {/* CTA Button */}
117
+ <div className={`mb-32 ${mounted ? 'fade-up' : 'opacity-0'}`}
118
+ style={{ animationDelay: '1.8s' }}>
119
+ <button onClick={() => navigate('/dashboard')}
120
+ className="px-10 py-4 bg-white text-gray-950 font-semibold rounded-full hover:bg-gray-200 hover:scale-105 transition-all shadow-2xl shadow-white/10">
121
+ Enter Dashboard →
122
+ </button>
123
+ </div>
124
+ </div>
125
+
126
+ {/* Stats Grid */}
127
+ <div id="stats" className={`grid grid-cols-2 md:grid-cols-4 gap-6 mb-24 ${mounted ? 'fade-up' : 'opacity-0'}`}
128
+ style={{ animationDelay: '0.8s' }}>
129
+ {[
130
+ { value: '8,799', label: 'Reddit Posts', sublabel: 'analyzed semantically' },
131
+ { value: '10', label: 'Subreddits', sublabel: 'across the spectrum' },
132
+ { value: '1,500%', label: 'Activity Surge', sublabel: 'after inauguration' },
133
+ { value: '320', label: 'Network Nodes', sublabel: '773 interaction edges' },
134
+ ].map((stat, i) => (
135
+ <div key={i} className="bg-white/[0.03] backdrop-blur-sm border border-white/10 rounded-2xl p-6 hover:border-amber-500/30 hover:bg-white/[0.05] transition-all">
136
+ <div className="text-4xl font-bold bg-gradient-to-br from-amber-400 to-orange-500 bg-clip-text text-transparent mb-2"
137
+ style={{ fontFamily: "'DM Serif Display', Georgia, serif" }}>
138
+ {stat.value}
139
+ </div>
140
+ <div className="text-sm font-medium text-gray-300">{stat.label}</div>
141
+ <div className="text-xs text-gray-500 mt-0.5">{stat.sublabel}</div>
142
+ </div>
143
+ ))}
144
+ </div>
145
+
146
+ {/* Features Section */}
147
+ <div id="features" className="mb-24">
148
+ <div className="mb-12 max-w-3xl">
149
+ <div className="text-amber-400 text-xs font-medium tracking-[0.2em] uppercase mb-4">What you can explore</div>
150
+ <h2 className="text-4xl md:text-5xl font-bold leading-tight"
151
+ style={{ fontFamily: "'DM Serif Display', Georgia, serif" }}>
152
+ Six lenses on political discourse
153
+ </h2>
154
+ <p className="text-gray-500 mt-4 text-base">
155
+ Each section is built around a specific question — from how communities discussed events over time, to who bridges them, to how topics evolved.
156
+ </p>
157
+ </div>
158
+
159
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-px bg-white/5 border border-white/10 rounded-2xl overflow-hidden">
160
+ {[
161
+ {
162
+ number: '01',
163
+ title: 'Time Series',
164
+ desc: 'Post volume, engagement, and topic trends over time. Each chart includes a dynamically generated plain-language summary.',
165
+ },
166
+ {
167
+ number: '02',
168
+ title: 'Network Analysis',
169
+ desc: 'Force-directed graph of 320 connected authors. Compute PageRank, betweenness, and Louvain communities. Simulate node removal.',
170
+ },
171
+ {
172
+ number: '03',
173
+ title: 'Topic Clustering',
174
+ desc: 'KMeans clustering on sentence embeddings with a tunable k. Click any cluster to see its top posts and subreddit breakdown.',
175
+ },
176
+ {
177
+ number: '04',
178
+ title: 'SearchAI',
179
+ desc: 'Semantic search ranked by meaning, not keywords. Handles non-English queries through detection and translation.',
180
+ },
181
+ {
182
+ number: '05',
183
+ title: 'Embedding Map',
184
+ desc: 'Interactive 2D projection of all 8,799 posts via UMAP. Zoom, pan, and search to discover topic neighborhoods.',
185
+ },
186
+ {
187
+ number: '06',
188
+ title: 'Investigative Story',
189
+ desc: 'Methodology, verified key findings, and an event-annotated timeline. Designed like a research report, not a chart wall.',
190
+ },
191
+ ].map((feature) => (
192
+ <div key={feature.number}
193
+ className="group bg-gray-950 hover:bg-white/[0.02] p-8 transition-colors">
194
+ <div className="text-amber-500/60 text-xs font-mono tracking-wider mb-6">{feature.number}</div>
195
+ <h3 className="text-xl font-semibold text-white mb-3 tracking-tight">{feature.title}</h3>
196
+ <p className="text-sm text-gray-500 leading-relaxed">{feature.desc}</p>
197
+ </div>
198
+ ))}
199
+ </div>
200
+ </div>
201
+
202
+ {/* Tech Stack Strip */}
203
+ <div className="text-center mb-20">
204
+ <p className="text-xs text-gray-600 tracking-wider uppercase mb-4">Built with</p>
205
+ <div className="flex flex-wrap items-center justify-center gap-x-8 gap-y-3 text-sm text-gray-500">
206
+ <span>React</span>
207
+ <span className="text-gray-700">·</span>
208
+ <span>Flask</span>
209
+ <span className="text-gray-700">·</span>
210
+ <span>sentence-transformers</span>
211
+ <span className="text-gray-700">·</span>
212
+ <span>NetworkX</span>
213
+ <span className="text-gray-700">·</span>
214
+ <span>UMAP</span>
215
+ <span className="text-gray-700">·</span>
216
+ <span>KMeans</span>
217
+ <span className="text-gray-700">·</span>
218
+ <span>Gemma 3 27B</span>
219
+ <span className="text-gray-700">·</span>
220
+ <span>Datamapplot</span>
221
+ </div>
222
+ </div>
223
+
224
+ {/* Final CTA */}
225
+ <div className="text-center">
226
+ <h3 className="text-3xl md:text-4xl font-bold mb-4"
227
+ style={{ fontFamily: "'DM Serif Display', Georgia, serif" }}>
228
+ Ready to investigate?
229
+ </h3>
230
+ <p className="text-gray-400 mb-8">
231
+ Step into the dashboard and start exploring political narratives.
232
+ </p>
233
+ <button onClick={() => navigate('/dashboard')}
234
+ className="px-10 py-4 bg-gradient-to-r from-amber-500 to-orange-600 text-white font-semibold rounded-full shadow-2xl shadow-amber-500/30 hover:shadow-amber-500/50 hover:scale-105 transition-all text-lg">
235
+ Launch TheScope →
236
+ </button>
237
+ </div>
238
+ </main>
239
+
240
+ {/* Footer */}
241
+ <footer className="relative z-10 border-t border-white/5 px-8 py-6 text-center text-xs text-gray-600">
242
+ TheScope · Built for the SimPPL Research Engineering Intern Assignment ·{' '}
243
+ <a href="https://github.com/coderved63/research-engineering-intern-assignment" target="_blank" rel="noopener noreferrer"
244
+ className="hover:text-amber-400 transition-colors">GitHub</a>
245
+ </footer>
246
+ </div>
247
+ )
248
+ }