Skip to content

Commit fd84f57

Browse files
committed
feat: add results app at /results route
1 parent 109da71 commit fd84f57

14 files changed

Lines changed: 1378 additions & 3 deletions

‎src/main.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import { StrictMode } from "react";
22
import { createRoot } from "react-dom/client";
33
import { BrowserRouter, Routes, Route } from "react-router-dom";
44
import { App } from "./App";
5+
import { App as ResultsApp } from "./results/App";
56
import "./index.css";
67

78
createRoot(document.getElementById("root")!).render(
89
<StrictMode>
910
<BrowserRouter basename="/temporal-binding-predictive-processing">
1011
<Routes>
1112
<Route path="/" element={<App />} />
12-
{/* Results route - add after creating src/results/App.tsx */}
13-
{/* <Route path="/results" element={<ResultsApp />} /> */}
13+
<Route path="/results" element={<ResultsApp />} />
1414
</Routes>
1515
</BrowserRouter>
1616
</StrictMode>

‎src/results/App.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import References from "./components/References";
88

99
export function App() {
1010
return (
11-
<div className="min-h-screen bg-[#F5F0E6] text-slate-900">
11+
<div className="min-h-screen bg-white text-slate-900">
1212
<Navbar />
1313
<Hero />
1414
<OverviewCards />
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import {
2+
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip,
3+
Legend, ResponsiveContainer, ReferenceLine,
4+
} from "recharts";
5+
6+
export interface DataPoint {
7+
block: string;
8+
[key: string]: string | number;
9+
}
10+
11+
export interface SeriesConfig {
12+
key: string;
13+
label: string;
14+
color: string;
15+
}
16+
17+
interface GroupedBarChartProps {
18+
data: DataPoint[];
19+
series: SeriesConfig[];
20+
yAxisLabel: string;
21+
yDomain?: [number, number];
22+
referenceY?: number;
23+
height?: number;
24+
}
25+
26+
const CustomTooltip = ({ active, payload, label }: any) => {
27+
if (active && payload && payload.length) {
28+
return (
29+
<div className="rounded-lg border border-slate-200 bg-white px-3 py-2 shadow-lg text-sm">
30+
<p className="font-semibold text-slate-800 mb-1">{label}</p>
31+
{payload.map((p: any) => (
32+
<p key={p.name} style={{ color: p.color }}>
33+
{p.name}: <span className="font-medium">{Number(p.value).toFixed(3)}</span>
34+
</p>
35+
))}
36+
</div>
37+
);
38+
}
39+
return null;
40+
};
41+
42+
export default function GroupedBarChart({
43+
data,
44+
series,
45+
yAxisLabel,
46+
yDomain,
47+
referenceY = 1,
48+
height = 320,
49+
}: GroupedBarChartProps) {
50+
const blocks = Array.from(new Set(data.map((d) => d.block)));
51+
52+
// Build pivot: one row per block, columns = series keys
53+
const chartData = blocks.map((block) => {
54+
const entry: Record<string, string | number> = { block };
55+
series.forEach((s) => {
56+
const row = data.find((d) => {
57+
if (d.block !== block) return false;
58+
const discrimKey = Object.keys(d).find(
59+
(k) => k !== "block" && typeof d[k] === "string"
60+
);
61+
if (!discrimKey) return true;
62+
return d[discrimKey] === s.label;
63+
});
64+
const valKey = row
65+
? Object.keys(row).find((k) => typeof row[k] === "number")
66+
: undefined;
67+
entry[s.key] = row && valKey ? (row[valKey] as number) : 0;
68+
});
69+
return entry;
70+
});
71+
72+
return (
73+
<ResponsiveContainer width="100%" height={height}>
74+
<BarChart
75+
data={chartData}
76+
margin={{ top: 16, right: 24, left: 52, bottom: 64 }}
77+
barCategoryGap="28%"
78+
barGap={2}
79+
>
80+
<CartesianGrid strokeDasharray="3 3" stroke="#e2e8f0" />
81+
<XAxis
82+
dataKey="block"
83+
tick={{ fontSize: 11, fill: "#475569" }}
84+
angle={-22}
85+
textAnchor="end"
86+
interval={0}
87+
height={64}
88+
/>
89+
<YAxis
90+
domain={yDomain}
91+
label={{
92+
value: yAxisLabel,
93+
angle: -90,
94+
position: "insideLeft",
95+
offset: -36,
96+
style: { fontSize: 12, fill: "#475569", fontWeight: 600 },
97+
}}
98+
tick={{ fontSize: 11, fill: "#475569" }}
99+
tickFormatter={(v) => Number(v).toFixed(2)}
100+
/>
101+
<Tooltip content={<CustomTooltip />} />
102+
<Legend wrapperStyle={{ fontSize: 12, paddingTop: 8 }} />
103+
{referenceY !== undefined && (
104+
<ReferenceLine
105+
y={referenceY}
106+
stroke="#94a3b8"
107+
strokeDasharray="4 2"
108+
strokeWidth={1.5}
109+
/>
110+
)}
111+
{series.map((s) => (
112+
<Bar
113+
key={s.key}
114+
dataKey={s.key}
115+
name={s.label}
116+
fill={s.color}
117+
radius={[3, 3, 0, 0]}
118+
maxBarSize={36}
119+
/>
120+
))}
121+
</BarChart>
122+
</ResponsiveContainer>
123+
);
124+
}

‎src/results/components/Hero.tsx‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
export default function Hero() {
2+
return (
3+
<div
4+
id="overview"
5+
className="relative overflow-hidden bg-gradient-to-br from-violet-900 via-indigo-900 to-slate-900 text-white"
6+
>
7+
{/* subtle grid pattern */}
8+
<div
9+
className="absolute inset-0 opacity-10"
10+
style={{
11+
backgroundImage:
12+
"linear-gradient(to right, #fff 1px, transparent 1px), linear-gradient(to bottom, #fff 1px, transparent 1px)",
13+
backgroundSize: "40px 40px",
14+
}}
15+
/>
16+
<div className="relative max-w-6xl mx-auto px-4 py-16 md:py-24">
17+
<div className="max-w-3xl">
18+
<div className="inline-flex items-center gap-2 rounded-full border border-violet-400/40 bg-violet-400/10 px-3 py-1 text-xs font-medium text-violet-300 mb-6">
19+
<span className="h-1.5 w-1.5 rounded-full bg-violet-400 animate-pulse" />
20+
Master's Thesis · Bogazici University
21+
</div>
22+
<h1 className="text-4xl md:text-5xl font-extrabold leading-tight mb-4">
23+
Temporal Binding &amp;<br />
24+
<span className="bg-gradient-to-r from-violet-400 to-indigo-300 bg-clip-text text-transparent">
25+
Predictive Processing
26+
</span>
27+
</h1>
28+
<p className="text-slate-300 text-lg leading-relaxed mb-8 max-w-2xl">
29+
Investigating the role of temporal predictability, identity validity, and causal inference
30+
in the temporal binding effect across three sets of experiments using interval reproduction tasks.
31+
</p>
32+
33+
{/* Quick stats */}
34+
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
35+
{[
36+
{ value: "3", label: "Experiment Sets" },
37+
{ value: "96", label: "Participants (total)" },
38+
{ value: "8", label: "Block Conditions" },
39+
{ value: "LMM", label: "Analysis Method" },
40+
].map((s) => (
41+
<div
42+
key={s.label}
43+
className="rounded-xl border border-white/10 bg-white/5 backdrop-blur px-4 py-3 text-center"
44+
>
45+
<p className="text-2xl font-bold text-white">{s.value}</p>
46+
<p className="text-xs text-slate-400 mt-0.5">{s.label}</p>
47+
</div>
48+
))}
49+
</div>
50+
</div>
51+
</div>
52+
</div>
53+
);
54+
}

‎src/results/components/Navbar.tsx‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { useState } from "react";
2+
3+
const navItems = [
4+
{ href: "#overview", label: "Overview" },
5+
{ href: "#exp1", label: "Exp 1a & 1b" },
6+
{ href: "#exp2", label: "Exp 2a & 2b" },
7+
{ href: "#exp3", label: "Exp 3a & 3b" },
8+
{ href: "#references", label: "References" },
9+
];
10+
11+
export default function Navbar() {
12+
const [open, setOpen] = useState(false);
13+
14+
return (
15+
<nav className="sticky top-0 z-50 bg-white/90 backdrop-blur border-b border-slate-200 shadow-sm">
16+
<div className="max-w-6xl mx-auto px-4 flex items-center justify-between h-14">
17+
{/* Logo / title */}
18+
<a href="#" className="flex items-center gap-2 group">
19+
<div className="h-8 w-8 rounded-lg bg-gradient-to-br from-violet-600 to-indigo-600 flex items-center justify-center shadow">
20+
<svg className="h-4 w-4 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.5}>
21+
<circle cx="12" cy="12" r="9" />
22+
<path d="M12 7v5l3 3" />
23+
</svg>
24+
</div>
25+
<span className="font-bold text-slate-900 text-sm leading-tight hidden sm:block">
26+
Temporal Binding<br />
27+
<span className="font-normal text-slate-500 text-xs">Predictive Processing Thesis</span>
28+
</span>
29+
</a>
30+
31+
{/* Desktop links */}
32+
<div className="hidden md:flex items-center gap-1">
33+
{navItems.map((item) => (
34+
<a
35+
key={item.href}
36+
href={item.href}
37+
className="px-3 py-1.5 rounded-lg text-sm font-medium text-slate-600 hover:text-violet-700 hover:bg-violet-50 transition-colors"
38+
>
39+
{item.label}
40+
</a>
41+
))}
42+
</div>
43+
44+
{/* Mobile hamburger */}
45+
<button
46+
className="md:hidden p-2 rounded-lg hover:bg-slate-100"
47+
onClick={() => setOpen(!open)}
48+
aria-label="Toggle menu"
49+
>
50+
<svg className="h-5 w-5 text-slate-700" fill="none" stroke="currentColor" strokeWidth={2} viewBox="0 0 24 24">
51+
{open ? (
52+
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
53+
) : (
54+
<path strokeLinecap="round" strokeLinejoin="round" d="M4 6h16M4 12h16M4 18h16" />
55+
)}
56+
</svg>
57+
</button>
58+
</div>
59+
60+
{/* Mobile menu */}
61+
{open && (
62+
<div className="md:hidden border-t border-slate-200 bg-white px-4 py-3 space-y-1">
63+
{navItems.map((item) => (
64+
<a
65+
key={item.href}
66+
href={item.href}
67+
className="block px-3 py-2 rounded-lg text-sm font-medium text-slate-600 hover:text-violet-700 hover:bg-violet-50 transition-colors"
68+
onClick={() => setOpen(false)}
69+
>
70+
{item.label}
71+
</a>
72+
))}
73+
</div>
74+
)}
75+
</nav>
76+
);
77+
}

0 commit comments

Comments
 (0)