Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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 client/public/frame.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions client/src/hooks/useCommittee.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useQuery } from "@tanstack/react-query";
import { AxiosError } from "axios";

import api from "@/lib/api";

export type ApiMember = {
name: string;
profile_picture: string;
pronouns: string;
about: string;
};

export function useCommittee() {
return useQuery<ApiMember[], AxiosError>({
queryKey: ["role"],
queryFn: async () => {
const response = await api.get<ApiMember[]>("/about/");
console.log(response.data);
return response.data;
},
retry: (failureCount, error) => {
if (error?.response?.status === 404) {
return false;
}
return failureCount < 3;
},
});
}
180 changes: 180 additions & 0 deletions client/src/pages/about.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import Image from "next/image";

Check warning on line 1 in client/src/pages/about.tsx

View workflow job for this annotation

GitHub Actions / Run ESLint

Run autofix to sort these imports!
import { ImageIcon } from "lucide-react";
import { useCommittee, ApiMember } from "@/hooks/useCommittee";
import { useRouter } from "next/router";

Check failure on line 4 in client/src/pages/about.tsx

View workflow job for this annotation

GitHub Actions / Run ESLint

'useRouter' is defined but never used

export default function AboutPage() {
//const router = useRouter();
//const { id } = router.query;
// don't know if necessary

const { data: committee, isPending, error, isError } = useCommittee();

const topRow: ApiMember[] = [];
const bottomRow: ApiMember[] = [];
//lists that will be populated with member objects in the committee
const roleOrder = [
"President",
"Vice President",
"Secretary",
"Treasurer",
"Marketing",
"Events OCM",
"Projects OCM",
"Fresher Rep",
];

if (isPending) {
for (let i = 0; i < 8; i++) {
if (i < 4) {
topRow.push({
name: "Loading...",
pronouns: "",
profile_picture: "",
about: "",
});
} else {
bottomRow.push({
name: "Loading...",
pronouns: "",
profile_picture: "",
about: "",
});
}
}
} else if (isError) {
const errorMessage =
error?.response?.status === 404
? "Committee Page not found."
: "Failed to load Committee Page.";

return (
<main className="mx-auto min-h-screen max-w-6xl px-6 py-16 md:px-20">
<p className="text-red-500" role="alert">
{errorMessage}
</p>
</main>
);
} else {
for (let i = 0; i < 8; i++) {
if (i < 4) {
topRow.push(committee[i]);
} else {
bottomRow.push(committee[i]);
}
}
}

return (
<main className="min-h-screen bg-[#090A19]">
<div className="mx-auto max-w-6xl space-y-20 px-6 py-16 md:px-20">
<section className="flex flex-col justify-between gap-12 md:flex-row md:gap-20">
<div className="flex-1">
<h1 className="mb-4 font-jersey10 text-5xl text-[#9B9BDE] md:text-6xl">
About Us
</h1>
<div
className="mb-6 w-full border-t border-[#5E5ECC]"
aria-hidden="true"
/>
<div className="space-y-4 font-jersey10 text-base leading-relaxed text-white md:text-lg">
<p>
Description of the clubs aims, why it exists, its mission, etc
etc. Second paragraph here, a second paragraph would be pretty
cool. The more info the better yippee!!
</p>
<p>Lorem ipsum dolor such and such I can't remember the rest.</p>

Check failure on line 86 in client/src/pages/about.tsx

View workflow job for this annotation

GitHub Actions / Run ESLint

`'` can be escaped with `&apos;`, `&lsquo;`, `&#39;`, `&rsquo;`
</div>
</div>
<div className="relative aspect-[4/3] w-full flex-shrink-0 overflow-hidden rounded-2xl bg-[#C5C5E8] md:w-96 lg:w-[512px]">
<div className="flex h-full w-full items-center justify-center">
<ImageIcon className="h-32 w-32 text-white/40" />
</div>
</div>
</section>
</div>

{/* Our Committee Title Section - LIGHT - Full Width */}
<section className="w-full bg-[#1B1F4C] px-6 py-6 md:px-10 md:py-6">
<div className="mx-auto max-w-6xl">
<h2 className="font-jersey10 text-3xl text-white">Our Committee</h2>
</div>
</section>

{/* Portraits Section - DARK - Full Width */}
<section className="w-full bg-[#090A19] px-6 py-10 pt-16 md:px-10">
<div className="mx-auto max-w-6xl">
{/* Top row - 4 Presidents */}
<div className="flex flex-wrap justify-center gap-6 md:gap-10">
{topRow.map((member, idx) => (
<div
key={`top-${idx}`}
className="flex flex-col items-start gap-0"
>
<div
className="relative flex items-center justify-center bg-contain bg-center bg-no-repeat"
style={{
width: "180px",
height: "185px",
backgroundImage: "url('/frame.png')",
}}
>
<Image
src={member.profile_picture}
alt=""
width={108}
height={1}
className="mb-3 h-[106px] w-[106px]"
/>
</div>
<div className="w-[180px] text-left font-firaCode text-[9px] leading-tight">
<p className="inline-block bg-[#1B1F4C] px-2 py-1 text-white">
{member.name} {member.pronouns}
</p>
<p className="inline-block bg-[#1B1F4C] px-2 py-1 text-[#9ca4fd]">
{roleOrder[idx]}
</p>
</div>
</div>
))}
</div>

{/* Bottom row - 3 other roles */}
<div className="mt-10 flex flex-wrap justify-center gap-6 md:gap-10">
{bottomRow.map((member, idx) => (
<div
key={`bottom-${idx}`}
className="flex flex-col items-start gap-0"
>
<div
className="relative flex items-center justify-center bg-contain bg-center bg-no-repeat"
style={{
width: "180px",
height: "185px",
backgroundImage: "url('/frame.png')",
}}
>
<Image
src={member.profile_picture}
alt=""
width={108}
height={1}
className="mb-3 h-[106px] w-[106px]"
/>
</div>
<div className="w-[180px] text-left font-firaCode text-[9px] leading-tight">
<p className="inline-block bg-[#1B1F4C] px-2 py-1 text-white">
{member.name} {member.pronouns}
</p>
<p className="inline-block bg-[#1B1F4C] px-2 py-1 text-[#9ca4fd]">
{roleOrder[4 + idx]}
</p>
</div>
</div>
))}
</div>
</div>
</section>
</main>
);
}
7 changes: 6 additions & 1 deletion server/game_dev/admin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.contrib import admin
from .models import Member, Event
from .models import Member, Event, Committee


class MemberAdmin(admin.ModelAdmin):
Expand All @@ -10,5 +10,10 @@
list_display = ("name", "date", "location", "publicationDate")


class CommitteeAdmin(admin.ModelAdmin):
pass


admin.site.register(Member, MemberAdmin)
admin.site.register(Event, EventAdmin)
admin.site.register(Committee, CommitteeAdmin)

Check warning on line 19 in server/game_dev/admin.py

View workflow job for this annotation

GitHub Actions / Run Flake8

no newline at end of file
20 changes: 20 additions & 0 deletions server/game_dev/migrations/0005_committee.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 5.1.15 on 2026-01-09 09:46

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('game_dev', '0004_alter_event_date'),
]

operations = [
migrations.CreateModel(
name='Committee',
fields=[
('id', models.OneToOneField(on_delete=django.db.models.deletion.DO_NOTHING, primary_key=True, serialize=False, to='game_dev.member')),
],
),
]
18 changes: 18 additions & 0 deletions server/game_dev/migrations/0006_committee_role.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.15 on 2026-01-09 09:59

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('game_dev', '0005_committee'),
]

operations = [
migrations.AddField(
model_name='committee',
name='role',
field=models.CharField(choices=[('P', 'President'), ('VP', 'Vice-President'), ('SEC', 'Secretary'), ('TRE', 'Treasurer'), ('MARK', 'Marketing'), ('EV', 'Events OCM'), ('PRO', 'Projects OCM'), ('FRE', 'Fresher Rep')], default='FRE', max_length=9),

Check failure on line 16 in server/game_dev/migrations/0006_committee_role.py

View workflow job for this annotation

GitHub Actions / Run Flake8

line too long (258 > 150 characters)
),
]
19 changes: 19 additions & 0 deletions server/game_dev/migrations/0007_alter_committee_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 5.1.15 on 2026-01-21 07:59

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('game_dev', '0006_committee_role'),
]

operations = [
migrations.AlterField(
model_name='committee',
name='id',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='game_dev.member'),
),
]
18 changes: 18 additions & 0 deletions server/game_dev/migrations/0008_alter_committee_role.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.15 on 2026-01-21 08:11

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('game_dev', '0007_alter_committee_id'),
]

operations = [
migrations.AlterField(
model_name='committee',
name='role',
field=models.CharField(choices=[('P', 'President'), ('VP', 'Vice-President'), ('SEC', 'Secretary'), ('TRE', 'Treasurer'), ('MARK', 'Marketing'), ('EV', 'Events OCM'), ('PRO', 'Projects OCM'), ('FRE', 'Fresher Rep')], default='FRE', max_length=9, unique=True),

Check failure on line 16 in server/game_dev/migrations/0008_alter_committee_role.py

View workflow job for this annotation

GitHub Actions / Run Flake8

line too long (271 > 150 characters)
),
]
21 changes: 21 additions & 0 deletions server/game_dev/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,24 @@ class Event(models.Model):

def __str__(self):
return self.name


class Committee(models.Model):
id = models.OneToOneField(Member, on_delete=models.CASCADE, primary_key=True)
roles = {
"P": "President",
"VP": "Vice-President",
"SEC": "Secretary",
"TRE": "Treasurer",
"MARK": "Marketing",
"EV": "Events OCM",
"PRO": "Projects OCM",
"FRE": "Fresher Rep"
}
role = models.CharField(max_length=9, choices=roles, default="FRE", unique=True)

def get_member(self):
return self.id

def __str__(self):
return self.id.name
49 changes: 48 additions & 1 deletion server/game_dev/tests.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.test import TestCase
from .models import Member, Event
from .models import Member, Event, Committee
import datetime
from django.core.files.uploadedfile import SimpleUploadedFile
from django.utils import timezone
Expand Down Expand Up @@ -73,6 +73,53 @@
self.assertEqual(event.date, self.event_datetime)


class CommitteeModelTest(TestCase):
def setUp(self):
self.member = Member.objects.create(
name = "Linus Torvalds",

Check failure on line 79 in server/game_dev/tests.py

View workflow job for this annotation

GitHub Actions / Run Flake8

unexpected spaces around keyword / parameter equals

Check failure on line 79 in server/game_dev/tests.py

View workflow job for this annotation

GitHub Actions / Run Flake8

unexpected spaces around keyword / parameter equals
about = "Linux creator",

Check failure on line 80 in server/game_dev/tests.py

View workflow job for this annotation

GitHub Actions / Run Flake8

unexpected spaces around keyword / parameter equals

Check failure on line 80 in server/game_dev/tests.py

View workflow job for this annotation

GitHub Actions / Run Flake8

unexpected spaces around keyword / parameter equals
pronouns = "He/Him"

Check failure on line 81 in server/game_dev/tests.py

View workflow job for this annotation

GitHub Actions / Run Flake8

unexpected spaces around keyword / parameter equals

Check failure on line 81 in server/game_dev/tests.py

View workflow job for this annotation

GitHub Actions / Run Flake8

unexpected spaces around keyword / parameter equals
)
try:
Member.objects.get(name="Linus Torvalds")
except Member.DoesNotExist:
self.fail("Member was not properly created before testing Committee model; check Member model")
self.committee = Committee.objects.create(id = self.member)

Check failure on line 87 in server/game_dev/tests.py

View workflow job for this annotation

GitHub Actions / Run Flake8

unexpected spaces around keyword / parameter equals

Check failure on line 87 in server/game_dev/tests.py

View workflow job for this annotation

GitHub Actions / Run Flake8

unexpected spaces around keyword / parameter equals

def test_committee_creation(self):
try:
Committee.objects.get(id=self.member)
except Member.DoesNotExist:
self.fail("Committee Member was not properly created")

def test_role_is_unique(self):
Member.objects.create(
name = "Jane Doe",
about = "Placeholder",
pronouns = "She/Her"
)
try:
Committee.objects.create(id = Member.objects.get(name="Jane Doe"), role="P")
self.fail("Committee Member with a duplicate role was created")
except:
return True

Check warning on line 106 in server/game_dev/tests.py

View workflow job for this annotation

GitHub Actions / Run Flake8

blank line contains whitespace
def test_cascade_from_committee(self):
self.committee.delete()
try:
Member.objects.get(name = self.member.name)
except:
self.fail("Deleting Committee object deleted it's corresponding Member object (undesired behaviour)")

Check warning on line 113 in server/game_dev/tests.py

View workflow job for this annotation

GitHub Actions / Run Flake8

blank line contains whitespace
def test_cascade_from_member(self):
self.member.delete()
try:
Committee.objects.get(id = self.member)
self.fail("Deleting Member Object did not delete a possible corresponding Committee object (undesired behaviour)")
except:
return True


class EventListAPITest(TestCase):
def setUp(self):
self.url = reverse("events-list")
Expand Down
Loading
Loading