Skip to content
Open
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
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- uses: actions/setup-node@v3
- working-directory: backend # Change this to the name of your backend directory
run: |
npm ci
npm ci --legacy-peer-deps
npm run lint-check
frontend:
name: Frontend lint and style check
Expand All @@ -24,5 +24,5 @@ jobs:
- uses: actions/setup-node@v3
- working-directory: frontend # Change this to the name of your frontend directory
run: |
npm ci
npm ci --legacy-peer-deps
npm run lint-check
13 changes: 2 additions & 11 deletions frontend/app/submit/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import SubmissionForm from "../../src/components/SubmissionForm";
// =============================================================================
// SCREEN: Social Submission Form (/submit)
// =============================================================================
Expand Down Expand Up @@ -38,17 +39,7 @@
export default function SubmitPage() {
return (
<main>
<h1>Submit a Social</h1>
{/* TODO: Field 1 — Submission name (text input) */}
{/* TODO: Field 2 — Activity type (text input) */}
{/* TODO: Field 3 — Who attended (multi-select dropdown, fetch from /api/members) */}
{/* TODO: Field 4 — Date & time (date + time picker) */}
{/* TODO: Field 5 — Photo upload (required) */}
{/* TODO: Field 6 — Point assignment per team */}
{/* TODO: Field 7 — PVP flag (if applicable) */}
{/* TODO: Validate min 3 attendees before submit */}
{/* TODO: POST to /api/submissions, then redirect to /submit/confirmation */}
<p>Submission form goes here.</p>
<SubmissionForm />
</main>
);
}
3 changes: 3 additions & 0 deletions frontend/public/img/flag.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
215 changes: 215 additions & 0 deletions frontend/src/components/PointAllocation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
"use client";

import Image from "next/image";

import teamChipStyles from "../styles/teamChip.module.css";

import TeamChip from "./TeamChip";

type Team = {
id: string;
name: string;
members?: number;
};

type Props = {
teams: Team[];
onNext?: () => void;
};

export default function PointAllocation({ teams, onNext }: Props) {
const teamById = Object.fromEntries(teams.map((team) => [team.id, team]));
const teamPairs: [Team, Team | undefined][] = [];

if (teamById.dbc && teamById.pvp) {
teamPairs.push([teamById.dbc, teamById.pvp]);
} else if (teamById.dbc) {
teamPairs.push([teamById.dbc, undefined]);
} else if (teamById.pvp) {
teamPairs.push([teamById.pvp, undefined]);
}

if (teamById.f3) {
teamPairs.push([teamById.f3, undefined]);
}

if (teamById.homestart) {
teamPairs.push([teamById.homestart, undefined]);
}

if (teamById.test) {
teamPairs.push([teamById.test, undefined]);
}

return (
<div
style={{
width: "402px",
paddingTop: "13px",
paddingRight: "30px",
paddingLeft: "34px",
display: "flex",
flexDirection: "column",
gap: "12px",
opacity: 1,
}}
>
<h2
style={{
width: "115px",
height: "18px",
margin: "0",
fontFamily: "Rubik, sans-serif",
fontSize: "15px",
fontWeight: 500,
fontStyle: "normal",
lineHeight: "100%",
letterSpacing: "0%",
color: "#0C2B35",
opacity: 1,
transform: "rotate(0deg)",
}}
>
Point Allocation
</h2>

{/* Rows with team pairs and scores */}
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
{teamPairs.map((pair, pairIndex) => (
<div
key={`pair-${pairIndex}`}
style={{
display: "flex",
alignItems: "center",
gap: "12px",
flexWrap: "wrap",
}}
>
{/* First team chip */}
<TeamChip team={pair[0]} />

{/* Second team chip (if exists) */}
{pair[1] && <TeamChip team={pair[1]} />}

{/* Spacer */}
<div style={{ flex: 1 }}></div>

{/* 1+1 = label */}
<span
style={{
fontFamily: "Rubik, sans-serif",
fontSize: "13px",
fontWeight: 400,
fontStyle: "normal",
lineHeight: "100%",
letterSpacing: "0%",
color: "#8FA7B0",
}}
>
1+1 =
</span>

{/* Score bubble */}
<div
className={[
teamChipStyles.scoreBubble,
pair[0].id === "dbc"
? teamChipStyles.dbcScore
: pair[0].id === "f3"
? teamChipStyles.f3Score
: pair[0].id === "homestart"
? teamChipStyles.homestartScore
: pair[0].id === "test"
? teamChipStyles.testScore
: teamChipStyles.pvpScore,
]
.filter(Boolean)
.join(" ")}
>
2
</div>

{/* pts label */}
<span
style={{
fontFamily: "Rubik, sans-serif",
fontSize: "10px",
fontWeight: 500,
fontStyle: "normal",
lineHeight: "100%",
letterSpacing: "0%",
color: "#8FA7B0",
}}
>
pts
</span>
</div>
))}
</div>

<div
style={{
display: "flex",
flexDirection: "column",
gap: "8px",
marginTop: "auto",
alignItems: "flex-end",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
gap: "8px",
width: "fit-content",
}}
>
<Image src="/img/flag.svg?v=original" alt="Flag" width={8} height={10} />
<span
style={{
width: "75px",
height: "15px",
fontFamily: "Karla, sans-serif",
fontSize: "10px",
fontWeight: 700,
fontStyle: "normal",
lineHeight: "150%",
letterSpacing: "0%",
color: "#B93B3B",
opacity: 1,
transform: "rotate(0deg)",
}}
>
Status: Flagged
</span>
</div>

{/* Next Button */}
<button
onClick={onNext}
style={{
width: "131px",
height: "48px",
padding: "6px 25px",
borderRadius: "100px",
background: "#0f3a47",
color: "white",
fontSize: "16px",
fontWeight: 600,
border: "none",
cursor: "pointer",
boxSizing: "border-box",
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: "10px",
opacity: 1,
}}
>
Next
</button>
</div>
</div>
);
}
46 changes: 46 additions & 0 deletions frontend/src/components/SubmissionForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"use client";

import { useEffect, useState } from "react";

import PointAllocation from "./PointAllocation";
import TeamHosting from "./TeamHosting";

type Team = {
id: string;
name: string;
};

export default function SubmissionForm() {
const [teams, setTeams] = useState<Team[]>([
{ id: "dbc", name: "DBC" },
{ id: "f3", name: "F3" },
{ id: "homestart", name: "Homestart" },
{ id: "test", name: "TEST" },
{ id: "pvp", name: "PVP" },
]);
const visibleTeamIds = new Set(["dbc", "homestart", "pvp"]);
const visibleTeams = teams.filter((team) => visibleTeamIds.has(team.id));

useEffect(() => {
const fetchTeams = async () => {
try {
const response = await fetch("/api/teams");
if (response.ok) {
const data = (await response.json()) as Team[];
setTeams(data);
}
} catch (err) {
console.error("Failed to fetch teams", err);
}
};

void fetchTeams();
}, []);

return (
<div>
<TeamHosting teams={visibleTeams} />
<PointAllocation teams={visibleTeams} />
</div>
);
}
20 changes: 20 additions & 0 deletions frontend/src/components/TeamChip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import teamChipStyles from "../styles/teamChip.module.css";

type Team = {
id: string;
name: string;
};

type TeamChipProps = {
team: Team;
};

export default function TeamChip({ team }: TeamChipProps) {
const variantClassName = teamChipStyles[team.id as keyof typeof teamChipStyles] ?? "";
const className = [teamChipStyles.teamChip, variantClassName].filter(Boolean).join(" ");
return (
<div className={className}>
<span className={teamChipStyles.label}>{team.name}</span>
</div>
);
}
Loading
Loading