Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
18e5e7e
Add features section to README
CATalamantes Jul 5, 2026
700ac5e
Revise developer names in README
CATalamantes Jul 5, 2026
123dbf0
Add wireframe images and update wireframes documentation
javohirergashev Jul 13, 2026
2c1c6fa
feat: update wireframe images folder name and update references in wi…
javohirergashev Jul 13, 2026
c659847
feat: add ER diagram images for project planning
javohirergashev Jul 13, 2026
e9bf294
feat: update entity relationship diagram and enhance table descriptions
javohirergashev Jul 13, 2026
2ede8d6
feat: update milestone 2 checklist and reflection responses
javohirergashev Jul 13, 2026
5d4ffb9
Rename Wireframe Sign up:Log In page.png to Wireframe Sign up Log In …
CATalamantes Jul 19, 2026
00b150f
Updating details
CATalamantes Jul 19, 2026
f5283d2
Set up Project and completed Milestone 1
CATalamantes Jul 20, 2026
d6dd3d8
Restructure baseline
CATalamantes Jul 23, 2026
cab364e
added passport backend auth (github strat)
SynthicProgram Jul 26, 2026
fa0b710
chore: update reset script path in package.json and remove peer flags…
javohirergashev Jul 26, 2026
40f1476
refactor: remove reset script and user data for database setup
javohirergashev Jul 26, 2026
7ceaeb2
feat: implement database schema, seed data, and connection handling
javohirergashev Jul 26, 2026
a2cbbdf
feat: add example environment configuration file
javohirergashev Jul 26, 2026
fc16060
refactor: update SQL queries and improve error handling in controllers
javohirergashev Jul 26, 2026
01575fc
fixed cors/dotenv import issue
SynthicProgram Jul 26, 2026
eefafd2
added client github oauth functionality
SynthicProgram Jul 26, 2026
5b13f3c
added github oauth DB schema compatability with passwords
SynthicProgram Jul 26, 2026
02da3b4
Merge main into secure-login: port OAuth onto the new DB schema
SynthicProgram Jul 26, 2026
efaa5a2
fix(auth): derive a free username for GitHub sign-ins
SynthicProgram Jul 26, 2026
ef69bfc
feat(auth): add username/password signup flow
SynthicProgram Jul 26, 2026
532f196
Google OAuth removed. Required approval and app verification to setup…
SynthicProgram Jul 27, 2026
c4d68ad
fix(auth): harden sessions, OAuth CSRF, and account linking. Must cre…
SynthicProgram Jul 27, 2026
818235a
[feat]: added api implementation
rijulpoudel Jul 27, 2026
667090c
Frontend build: foundation, shared components, all pages, mock data +…
ShalomDee Jul 27, 2026
34ed319
Exclude login/auth pages (deferring to PR #16), finalize Milestone 4 …
ShalomDee Jul 27, 2026
b6fe766
Merge pull request #17 from CATalamantes/frontend-submit
ShalomDee Jul 27, 2026
791b35f
Merge main into secure-login: layer auth onto the new dashboard frontend
SynthicProgram Jul 28, 2026
576a9c8
Merge pull request #16 from CATalamantes/secure-login
SynthicProgram Jul 28, 2026
bd28b6d
added authentication to progress report 4
SynthicProgram Jul 28, 2026
ce98d44
frontend audit before further development
Jul 28, 2026
4e48aa1
Use real session user instead of hardcoded demo id
ShalomDee Jul 28, 2026
4257773
Wire Follow a Team toggle to real follows API
ShalomDee Jul 28, 2026
6a49379
Wire Fixture Calendar filtering to real matches API
ShalomDee Jul 28, 2026
41eb39b
Wire MatchDetail to real GET /api/matches/:id
ShalomDee Jul 28, 2026
c73782e
finalize wiring real API data, redesign auth, add fallback UI + states
ShalomDee Jul 28, 2026
aff6aad
chore: stop tracking CLAUDE.md and frontend_audit.md
ShalomDee Jul 28, 2026
1939da8
chore: finish tracking CLAUDE.md and frontend_audit.md
ShalomDee Jul 28, 2026
55f9d1d
update comments
ShalomDee Jul 28, 2026
780fc5f
Merge pull request #18 from CATalamantes/frontend
ShalomDee Jul 28, 2026
282913e
Merge branch 'main' into api
CATalamantes Jul 28, 2026
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
Binary file added .DS_Store
Binary file not shown.
16 changes: 16 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"permissions": {
"allow": [
"PowerShell(Add-Type -AssemblyName System.Drawing; $img = [System.Drawing.Image]::FromFile\\('c:\\\\Users\\\\shalo\\\\MatchLens\\\\Code\\\\client\\\\src\\\\assets\\\\ball.png'\\); \"$\\($img.Width\\)x$\\($img.Height\\)\")",
"Bash(chromium-cli --help)",
"Bash(npx --yes playwright --version)",
"Bash(curl -s http://localhost:5173/ -o /dev/null -w \"%{http_code}\\\\n\")",
"Bash(npx playwright *)",
"mcp__claude_ai_Figma__get_design_context",
"Bash(python3 -c ' *)",
"Bash(taskkill //PID 27472 //F)",
"Bash(git status *)",
"Bash(powershell.exe -NoProfile -Command \"\\(Get-Content 'Code/client/src/pages/Leaderboard.jsx' -Raw\\) -replace 'dash-mlx','primary' | Set-Content -NoNewline -Encoding utf8 'Code/client/src/pages/Leaderboard.jsx'\")"
]
}
}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Code/CLAUDE.md
planning/frontend_audit.md
Code/CLAUDE.md
planning/frontend_audit.md
28 changes: 28 additions & 0 deletions Code/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local
.env

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

# vite build output (client vite.config.js builds to ../server/public)
server/public
13 changes: 13 additions & 0 deletions Code/client/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# API origin. Leave unset for both local dev (the Vite proxy forwards /api) and
# production (the client build is served by Express itself, so same-origin).
# Only needed if the client and API are deployed to different origins.
#
# NOTE: VITE_* values are embedded into the public client bundle. Never secrets.
VITE_API_URL=

# Cloudinary unsigned upload preset — used for real avatar uploads on the
# Profile page. No backend involvement: the browser uploads directly to
# Cloudinary using these two public values. Fill in your own account's
# cloud name and unsigned upload preset name.
VITE_CLOUDINARY_CLOUD_NAME=
VITE_CLOUDINARY_UPLOAD_PRESET=
16 changes: 16 additions & 0 deletions Code/client/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MatchLens</title>
<link rel="icon" type="image/png" href="/favicon.png" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
</head>
<body>
<div id="root"></div>
<script type="module" src="./src/main.jsx"></script>
</body>
</html>
6 changes: 6 additions & 0 deletions Code/client/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
Binary file added Code/client/public/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 40 additions & 0 deletions Code/client/src/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Routes, Route } from 'react-router-dom'
import RequireAuth from './components/RequireAuth'
import Sidebar from './components/Sidebar'
import Home from './pages/Home'
import Matches from './pages/Matches'
import MatchDetail from './pages/MatchDetail'
import Discover from './pages/Discover'
import TeamDetail from './pages/TeamDetail'
import PlayerDetail from './pages/PlayerDetail'
import Leaderboard from './pages/Leaderboard'
import Profile from './pages/Profile'
import Login from './pages/Login'
import Signup from './pages/Signup'
import NotFound from './pages/NotFound'

export default function App() {
return (
<Routes>
<Route path="/login" element={<Login title="MatchLens | Sign In" />} />
<Route path="/signup" element={<Signup title="MatchLens | Sign Up" />} />

{/* Everything below the guard needs a live server session. RequireAuth
bounces anonymous visitors to /login before the layout renders. */}
<Route element={<RequireAuth />}>
<Route element={<Sidebar />}>
<Route path="/" element={<Home />} />
<Route path="/matches" element={<Matches />} />
<Route path="/matches/:matchId" element={<MatchDetail />} />
<Route path="/discover" element={<Discover />} />
<Route path="/teams/:teamId" element={<TeamDetail />} />
<Route path="/players/:playerId" element={<PlayerDetail />} />
<Route path="/leaderboard" element={<Leaderboard />} />
<Route path="/profile" element={<Profile />} />
</Route>
</Route>

<Route path="*" element={<NotFound />} />
</Routes>
)
}
9 changes: 9 additions & 0 deletions Code/client/src/assets/ball-frame.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Code/client/src/assets/ball.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
76 changes: 76 additions & 0 deletions Code/client/src/components/AuthFields.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { useState } from 'react'

function FieldShell({ children }) {
return (
<div className="flex w-full items-center justify-between gap-2 rounded-lg border border-dash bg-dash-input px-4 py-[14px]">
{children}
</div>
)
}

export function TextField({ id, label, type = 'text', placeholder, value, onChange, autoComplete }) {
return (
<div className="flex w-full flex-col gap-2">
<label htmlFor={id} className="text-[12px] font-semibold uppercase tracking-wide text-secondary">
{label}
</label>
<FieldShell>
<input
id={id}
type={type}
placeholder={placeholder}
value={value}
onChange={onChange}
autoComplete={autoComplete}
className="w-full bg-transparent text-[15px] text-white placeholder:text-secondary focus:outline-none"
/>
</FieldShell>
</div>
)
}

export function PasswordField({ id, label, placeholder, value, onChange, autoComplete }) {
const [visible, setVisible] = useState(false)

return (
<div className="flex w-full flex-col gap-2">
<label htmlFor={id} className="text-[12px] font-semibold uppercase tracking-wide text-secondary">
{label}
</label>
<FieldShell>
<input
id={id}
type={visible ? 'text' : 'password'}
placeholder={placeholder}
value={value}
onChange={onChange}
autoComplete={autoComplete}
className="w-full bg-transparent text-[15px] text-white placeholder:text-secondary focus:outline-none"
/>
<button
type="button"
onClick={() => setVisible((v) => !v)}
aria-label={visible ? 'Hide password' : 'Show password'}
className="shrink-0 text-secondary transition-colors hover:text-white"
>
<EyeIcon revealed={visible} />
</button>
</FieldShell>
</div>
)
}

function EyeIcon({ revealed }) {
return (
<svg viewBox="0 0 20 20" fill="none" className="size-5" aria-hidden="true">
<path
d="M2 10s3-6 8-6 8 6 8 6-3 6-8 6-8-6-8-6Z"
stroke="currentColor"
strokeWidth="1.4"
strokeLinejoin="round"
/>
<circle cx="10" cy="10" r="2.4" stroke="currentColor" strokeWidth="1.4" />
{revealed && <path d="M3 3l14 14" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />}
</svg>
)
}
78 changes: 78 additions & 0 deletions Code/client/src/components/AuthLayout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Link } from 'react-router-dom'
import { AUTH_ORIGIN } from '../config/api'
import GitHubMark from './GitHubMark'
import ballMark from '../assets/ball.png'
import ballFrame from '../assets/ball-frame.svg'

const AUTH_URL = `${AUTH_ORIGIN}/auth`

// Shared shell for /login and /signup — the two-column layout, brand marks,
// GitHub button and footer are pixel-identical between the two Figma frames;
// only the heading copy, form fields and spacing differ per page.
export default function AuthLayout({
title,
subtitle,
paddingClassName,
gapClassName,
footerPrompt,
footerLinkLabel,
footerLinkTo,
children,
}) {
return (
<div className="flex min-h-screen bg-dashboard">
<div
className={`flex w-full flex-col justify-between px-6 sm:px-10 lg:w-[600px] lg:shrink-0 lg:border-r lg:border-dash lg:px-16 ${paddingClassName}`}
>
<div className="flex items-center gap-2">
<img src={ballMark} alt="" className="size-8 shrink-0" />
<p className="whitespace-nowrap text-[22px] font-extrabold tracking-tight text-white">
Match<span className="text-primary">L</span>ens
</p>
</div>

<div className={`flex w-full flex-col ${gapClassName}`}>
<div className="flex flex-col gap-2">
<h1 className="text-[32px] font-bold text-white">{title}</h1>
<p className="text-[15px] text-secondary">{subtitle}</p>
</div>

{children}

<div className="flex w-full items-center gap-3">
<div className="h-px flex-1 bg-dash" />
<p className="whitespace-nowrap text-[11px] font-semibold text-secondary">OR CONTINUE WITH</p>
<div className="h-px flex-1 bg-dash" />
</div>

<a
href={`${AUTH_URL}/github`}
className="flex w-full items-center justify-center gap-2.5 rounded-lg border border-dash py-[14px] text-[14px] font-semibold text-white transition-colors hover:border-white/40 hover:bg-white/5"
>
<GitHubMark />
Continue with GitHub
</a>

<p className="text-center text-[14px] font-semibold text-secondary">
{footerPrompt} <Link to={footerLinkTo} className="text-primary">{footerLinkLabel}</Link>
</p>
</div>

<div className="flex flex-col items-center gap-2 pt-10 text-center text-secondary">
<p className="text-[13px]">Terms of Service&nbsp;&nbsp;·&nbsp;&nbsp;Privacy Policy&nbsp;&nbsp;·&nbsp;&nbsp;Help Center</p>
<p className="text-[11px]">v2.4.0-Stable&nbsp;&nbsp;·&nbsp;&nbsp;Systems Operational</p>
</div>
</div>

<div className="hidden flex-1 items-center justify-center bg-dashboard/65 lg:flex">
<div className="flex flex-col items-center gap-4">
<img src={ballFrame} alt="" className="size-[120px]" />
<p className="text-[36px] font-extrabold tracking-tight text-white">
Match<span className="text-primary">L</span>ens
</p>
<p className="text-[16px] text-secondary">Your ultimate arena companion</p>
</div>
</div>
</div>
)
}
33 changes: 33 additions & 0 deletions Code/client/src/components/Avatar.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { getInitials, paletteFor } from '../utilities/monogram'

// Drop-in replacement for the old `bg-white/10` avatar/photo blocks. Renders
// the real image when a URL is available (e.g. `profile_image_url`);
// otherwise falls back to a deterministic initials badge instead of a blank
// grey block — used for user avatars and player photos alike, since neither
// has a guaranteed image source.
export default function Avatar({ name, src, className = '', textClassName = 'text-[13px]' }) {
const safeName = name || '?'

if (src) {
return (
<div
className={`bg-cover bg-center ${className}`}
style={{ backgroundImage: `url(${src})` }}
role="img"
aria-label={safeName}
/>
)
}

const palette = paletteFor(safeName)

return (
<div
className={`flex shrink-0 items-center justify-center overflow-hidden font-bold uppercase ${palette.bg} ${palette.text} ${className}`}
title={safeName}
aria-hidden="true"
>
<span className={`${textClassName} leading-none`}>{getInitials(safeName)}</span>
</div>
)
}
33 changes: 33 additions & 0 deletions Code/client/src/components/CommentThread.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Avatar from './Avatar'

// Static placeholder pending real comment data + posting (Match Comments,
// #7). No Figma reference exists for this yet, so it's a minimal mock
// list matching the app's existing card styling — swap the mock array
// and wire the input up when that's ready.
const mockComments = [
{ id: 1, author: 'AlbicelesteArmy', text: 'What a finish from Álvarez, that lead feels safe now.' },
{ id: 2, author: 'LesBleusForever', text: "Mbappé's equalizer came out of nowhere, still anyone's game." },
{ id: 3, author: 'MessiFanatic', text: 'Midfield control has been all Argentina since the 60th minute.' },
]

export default function CommentThread() {
return (
<div className="flex flex-col gap-4 rounded-2xl border border-dash bg-dash-card p-5">
<p className="text-[16px] font-bold text-white">💬 Match Comments</p>
<div className="flex flex-col gap-4">
{mockComments.map((comment) => (
<div key={comment.id} className="flex gap-3">
<Avatar name={comment.author} className="size-8 shrink-0 rounded-full" textClassName="text-[10px]" />
<div className="flex flex-col gap-1">
<p className="text-[12px] font-bold text-white">{comment.author}</p>
<p className="text-[12px] text-secondary">{comment.text}</p>
</div>
</div>
))}
</div>
<div className="rounded-lg border border-dash bg-dash-sidebar p-3">
<p className="text-[12px] text-secondary">Add a comment...</p>
</div>
</div>
)
}
21 changes: 21 additions & 0 deletions Code/client/src/components/Crest.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { paletteFor, shortLabel } from '../utilities/monogram'

// Drop-in replacement for the old `bg-white/10` crest blocks — pass the same
// size/shape classes via `className`. No team, match, or player endpoint
// returns a crest/badge image, so this is the honest fallback: a
// deterministic initials badge, never an invented logo.
// `compact` hides the label for slots too small to render legible text.
export default function Crest({ label, compact = false, className = '', textClassName = 'text-[11px]' }) {
const safeLabel = label || '?'
const palette = paletteFor(safeLabel)

return (
<div
className={`flex shrink-0 items-center justify-center overflow-hidden font-extrabold uppercase ${palette.bg} ${palette.text} ${className}`}
title={safeLabel}
aria-hidden="true"
>
{!compact && <span className={`${textClassName} leading-none`}>{shortLabel(safeLabel)}</span>}
</div>
)
}
14 changes: 14 additions & 0 deletions Code/client/src/components/GitHubMark.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Inline rather than an icon package — it's the only icon the app needs, and
// currentColor lets it inherit the button's text colour on hover.
const GitHubMark = () => (
<svg
className='size-[18px] shrink-0'
viewBox='0 0 16 16'
fill='currentColor'
aria-hidden='true'
>
<path d='M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8Z' />
</svg>
)

export default GitHubMark
Loading