Skip to content

Commit 050a2dc

Browse files
authored
Merge pull request #57 from codersforcauses/issue-5-About_us/committee_page
Issue 5 about us/committee page
2 parents 7b9b17d + 298fda1 commit 050a2dc

13 files changed

Lines changed: 419 additions & 6 deletions

client/src/hooks/useCommittee.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { useQuery } from "@tanstack/react-query";
2+
import { AxiosError } from "axios";
3+
4+
import api from "@/lib/api";
5+
6+
export type ApiMember = {
7+
name: string;
8+
profile_picture: string;
9+
pronouns: string;
10+
about: string;
11+
};
12+
13+
export function useCommittee() {
14+
return useQuery<ApiMember[], AxiosError>({
15+
queryKey: ["role"],
16+
queryFn: async () => {
17+
const response = await api.get<ApiMember[]>("/about/");
18+
console.log(response.data);
19+
return response.data;
20+
},
21+
retry: (failureCount, error) => {
22+
if (error?.response?.status === 404) {
23+
return false;
24+
}
25+
return failureCount < 3;
26+
},
27+
});
28+
}

client/src/pages/about.tsx

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import Image from "next/image";
2+
3+
import { ApiMember, useCommittee } from "@/hooks/useCommittee";
4+
5+
export default function AboutPage() {
6+
const { data: committee, isPending, error, isError } = useCommittee();
7+
8+
const topRow: ApiMember[] = [];
9+
const bottomRow: ApiMember[] = [];
10+
//lists that will be populated with member objects in the committee
11+
const roleOrder = [
12+
"President",
13+
"Vice President",
14+
"Secretary",
15+
"Treasurer",
16+
"Marketing",
17+
"Events OCM",
18+
"Projects OCM",
19+
"Fresher Rep",
20+
];
21+
22+
const about = (
23+
<>
24+
<div className="mx-auto max-w-6xl space-y-20 px-6 py-16 md:px-20">
25+
<section className="flex flex-col justify-between gap-12 md:flex-row md:gap-20">
26+
<div className="flex-1">
27+
<h1 className="mb-4 font-jersey10 text-5xl text-primary md:text-6xl">
28+
About Us
29+
</h1>
30+
<div className="mb-6 w-full border-t" aria-hidden="true" />
31+
<div className="space-y-4 font-sans text-base leading-relaxed text-white md:text-lg">
32+
<p>
33+
Description of the clubs aims, why it exists, its mission, etc
34+
etc. Second paragraph here, a second paragraph would be pretty
35+
cool. The more info the better yippee!!
36+
</p>
37+
<p>
38+
Lorem ipsum dolor such and such I can&apos;t remember the rest.
39+
</p>
40+
</div>
41+
</div>
42+
<div className="relative aspect-[4/3] w-full flex-shrink-0 overflow-hidden rounded-2xl bg-light_2 md:w-96 lg:w-[32rem]">
43+
<div className="flex h-full w-full items-center justify-center">
44+
<Image
45+
src="/landing_placeholder.png"
46+
alt="/landing_placeholder.png"
47+
width={128}
48+
height={128}
49+
className="h-[90%] w-[90%]"
50+
/>
51+
</div>
52+
</div>
53+
</section>
54+
</div>
55+
{/* Our Committee Title Section - LIGHT - Full Width */}
56+
<section className="w-full bg-card px-6 py-6 md:px-10 md:py-6">
57+
<div className="mx-auto max-w-6xl">
58+
<h2 className="font-jersey10 text-3xl text-light_2">Our Committee</h2>
59+
</div>
60+
</section>
61+
</>
62+
);
63+
64+
if (isPending) {
65+
for (let i = 0; i < 8; i++) {
66+
if (i < 4) {
67+
topRow.push({
68+
name: "Loading...",
69+
pronouns: "",
70+
profile_picture: "/landing_placeholder.png",
71+
about: "",
72+
});
73+
} else {
74+
bottomRow.push({
75+
name: "Loading...",
76+
pronouns: "",
77+
profile_picture: "/landing_placeholder.png",
78+
about: "",
79+
});
80+
}
81+
}
82+
} else if (isError) {
83+
const errorMessage =
84+
error?.response?.status === 404
85+
? "Committee Members not found."
86+
: "Failed to load Committee Members.";
87+
88+
return (
89+
<>
90+
{about}
91+
<main className="mx-auto min-h-screen max-w-6xl px-6 py-16 md:px-20">
92+
<p className="text-red-500" role="alert">
93+
{errorMessage}
94+
</p>
95+
</main>
96+
</>
97+
);
98+
} else {
99+
for (let i = 0; i < 8; i++) {
100+
if (i < 4) {
101+
topRow.push(committee[i]);
102+
} else {
103+
bottomRow.push(committee[i]);
104+
}
105+
}
106+
}
107+
108+
return (
109+
<main className="min-h-screen bg-background">
110+
{about}
111+
{/* Portraits Section - DARK - Full Width */}
112+
<section className="w-full bg-background px-6 py-10 pt-16 md:px-10">
113+
<div className="mx-auto max-w-6xl">
114+
{/* Top row - 4 Presidents */}
115+
<div className="flex flex-wrap justify-center gap-6 md:gap-10">
116+
{topRow.map((member, idx) => (
117+
<div
118+
key={`top-${idx}`}
119+
className="flex flex-col items-start gap-0"
120+
>
121+
<div className="relative flex h-[11.25rem] w-[11.25rem] items-center justify-center bg-[url('/frame.svg')] bg-contain bg-center bg-no-repeat">
122+
<Image
123+
src={
124+
member.profile_picture === null
125+
? "/landing_placeholder.png"
126+
: member.profile_picture
127+
}
128+
alt="/landing_placeholder.png"
129+
width={108}
130+
height={108}
131+
className="mb-2 h-[6.75rem] w-[6.75rem]"
132+
/>
133+
</div>
134+
<div className="w-[11.25rem] pl-3 text-left font-firaCode text-[0.5rem] leading-tight">
135+
<p className="inline-block bg-card px-2 py-1 text-white">
136+
{member.name} {member.pronouns}
137+
</p>
138+
<p className="inline-block bg-card px-2 py-1 text-primary">
139+
{roleOrder[idx]}
140+
</p>
141+
</div>
142+
</div>
143+
))}
144+
</div>
145+
146+
{/* Bottom row - 4 other roles */}
147+
<div className="mt-10 flex flex-wrap justify-center gap-6 md:gap-10">
148+
{bottomRow.map((member, idx) => (
149+
<div
150+
key={`bottom-${idx}`}
151+
className="flex flex-col items-start gap-0"
152+
>
153+
<div className="relative flex h-[11.25rem] w-[11.25rem] items-center justify-center bg-[url('/frame.svg')] bg-contain bg-center bg-no-repeat">
154+
<Image
155+
src={
156+
member.profile_picture === null
157+
? "/landing_placeholder.png"
158+
: member.profile_picture
159+
}
160+
alt="/landing_placeholder.png"
161+
width={108}
162+
height={108}
163+
className="mb-2 h-[6.75rem] w-[6.75rem]"
164+
/>
165+
</div>
166+
<div className="w-[11.25rem] pl-3 text-left font-firaCode text-[0.5rem] leading-tight">
167+
<p className="inline-block bg-card px-2 py-1 text-white">
168+
{member.name} {member.pronouns}
169+
</p>
170+
<p className="inline-block bg-card px-2 py-1 text-primary">
171+
{roleOrder[4 + idx]}
172+
</p>
173+
</div>
174+
</div>
175+
))}
176+
</div>
177+
</div>
178+
</section>
179+
</main>
180+
);
181+
}

server/game_dev/admin.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
from django.contrib import admin
2-
from .models import Member, Game, Event, GameContributor, GameShowcase
2+
from .models import Member, Game, Event, GameContributor, GameShowcase, Committee
33

44

55
class MemberAdmin(admin.ModelAdmin):
6-
pass
6+
list_display = ("id", "name", "active", "profile_picture", "about", "pronouns")
7+
search_fields = ["name", "about"]
78

89

910
# Sample EventsAdmin Class made
@@ -24,8 +25,13 @@ class GamesAdmin(admin.ModelAdmin):
2425
search_fields = ["name", "description"]
2526

2627

28+
class CommitteeAdmin(admin.ModelAdmin):
29+
raw_id_fields = ["id"]
30+
31+
2732
admin.site.register(Member, MemberAdmin)
2833
admin.site.register(Event, EventAdmin)
2934
admin.site.register(Game, GamesAdmin)
3035
admin.site.register(GameContributor, GameContributorAdmin)
3136
admin.site.register(GameShowcase, GameShowcaseAdmin)
37+
admin.site.register(Committee, CommitteeAdmin)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 5.1.15 on 2026-01-09 09:46
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('game_dev', '0004_alter_event_date'),
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name='Committee',
16+
fields=[
17+
('id', models.OneToOneField(on_delete=django.db.models.deletion.DO_NOTHING, primary_key=True, serialize=False, to='game_dev.member')),
18+
],
19+
),
20+
]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 5.1.15 on 2026-01-09 09:59
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('game_dev', '0005_committee'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='committee',
15+
name='role',
16+
field=models.CharField(choices=[('P', 'President'), ('VP', 'Vice-President'), ('SEC', 'Secretary'),
17+
('TRE', 'Treasurer'), ('MARK', 'Marketing'), ('EV', 'Events OCM'),
18+
('PRO', 'Projects OCM'), ('FRE', 'Fresher Rep')], default='FRE', max_length=9),
19+
),
20+
]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Generated by Django 5.1.15 on 2026-01-21 07:59
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('game_dev', '0006_committee_role'),
11+
]
12+
13+
operations = [
14+
migrations.AlterField(
15+
model_name='committee',
16+
name='id',
17+
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='game_dev.member'),
18+
),
19+
]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 5.1.15 on 2026-01-21 08:11
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('game_dev', '0007_alter_committee_id'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='committee',
15+
name='role',
16+
field=models.CharField(choices=[('P', 'President'), ('VP', 'Vice-President'), ('SEC', 'Secretary'),
17+
('TRE', 'Treasurer'), ('MARK', 'Marketing'), ('EV', 'Events OCM'),
18+
('PRO', 'Projects OCM'), ('FRE', 'Fresher Rep')], default='FRE', max_length=9, unique=True),
19+
),
20+
]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Generated by Django 5.1.15 on 2026-01-29 13:04
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('game_dev', '0005_alter_member_profile_picture'),
10+
('game_dev', '0008_alter_committee_role'),
11+
]
12+
13+
operations = [
14+
]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Generated by Django 5.1.15 on 2026-01-31 03:18
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('game_dev', '0009_merge_20260129_2104'),
10+
('game_dev', '0009_merge_20260131_1044'),
11+
]
12+
13+
operations = [
14+
]

server/game_dev/models.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,24 @@ class GameShowcase(models.Model):
7474

7575
def __str__(self):
7676
return f"{self.game.name}"
77+
78+
79+
class Committee(models.Model):
80+
id = models.OneToOneField(Member, on_delete=models.CASCADE, primary_key=True)
81+
roles = {
82+
"P": "President",
83+
"VP": "Vice-President",
84+
"SEC": "Secretary",
85+
"TRE": "Treasurer",
86+
"MARK": "Marketing",
87+
"EV": "Events OCM",
88+
"PRO": "Projects OCM",
89+
"FRE": "Fresher Rep"
90+
}
91+
role = models.CharField(max_length=9, choices=roles, default="FRE", unique=True)
92+
93+
def get_member(self):
94+
return self.id
95+
96+
def __str__(self):
97+
return self.id.name

0 commit comments

Comments
 (0)