Skip to content

Commit bb11ebe

Browse files
authored
Merge pull request #139 from codersforcauses/issue-108-add_workshop_URL_field_to_events
Issue 108 - Add Workshop URL Field
2 parents 9e05d20 + 479704b commit bb11ebe

8 files changed

Lines changed: 78 additions & 19 deletions

File tree

client/src/components/ui/EventDateDisplay.tsx

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,31 @@ export function getEventDateParts(dateString: string): EventDateParts | null {
3232
}
3333
}
3434

35+
const getOrdinal = (d: number) => {
36+
if (d > 3 && d < 21) return `${d}th`;
37+
switch (d % 10) {
38+
case 1:
39+
return `${d}st`;
40+
case 2:
41+
return `${d}nd`;
42+
case 3:
43+
return `${d}rd`;
44+
default:
45+
return `${d}th`;
46+
}
47+
};
48+
3549
type EventDateDisplayProps = { date: string };
3650

3751
/** Renders event date as: weekday・ day month・ time. */
3852
export function EventDateDisplay({ date }: EventDateDisplayProps) {
3953
const parts = getEventDateParts(date);
4054
if (!parts) return null;
4155
return (
42-
<div className="flex flex-wrap items-baseline gap-x-1">
43-
<span className="whitespace-nowrap text-primary">
44-
{parts.weekday}
45-
{"・"}
46-
</span>
47-
<span className="whitespace-nowrap text-primary">
48-
{parts.day} {parts.month}
56+
<div className="flex flex-wrap items-baseline gap-x-1 font-firaCode">
57+
<span className="whitespace-nowrap text-primary">{parts.weekday},</span>
58+
<span className="whitespace-nowrap font-medium text-primary">
59+
{getOrdinal(parseInt(parts.day))} {parts.month}
4960
{"・"}
5061
</span>
5162
<span className="text-secondary">{parts.time}</span>

client/src/hooks/useEvent.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ type ApiEvent = {
1111
date: string;
1212
startTime: string | null;
1313
location: string;
14-
cover_image: string | null;
14+
coverImage: string | null;
15+
workshopLink: string;
1516
};
1617

17-
type UiEvent = Omit<ApiEvent, "cover_image"> & {
18+
type UiEvent = Omit<ApiEvent, "coverImage"> & {
1819
coverImage: string;
1920
};
2021

@@ -32,7 +33,7 @@ function normalizeEventId(
3233
function transformApiEventToUiEvent(data: ApiEvent): UiEvent {
3334
return {
3435
...data,
35-
coverImage: data.cover_image ?? "/game_dev_club_logo.svg",
36+
coverImage: data.coverImage ?? "/game_dev_club_logo.svg",
3637
};
3738
}
3839

client/src/hooks/useEvents.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@ type ApiEvent = {
1010
publicationDate: string;
1111
date: string;
1212
location: string;
13-
cover_image: string | null;
13+
coverImage: string | null;
14+
workshopLink: string;
1415
};
1516

16-
export type UiEvent = Omit<ApiEvent, "cover_image"> & {
17+
export type UiEvent = Omit<ApiEvent, "coverImage"> & {
1718
coverImage: string;
19+
workshopLink: string;
1820
};
1921

2022
function transformApiEventToUiEvent(data: ApiEvent): UiEvent {
2123
return {
2224
...data,
23-
coverImage: data.cover_image ?? "/game_dev_club_logo.svg",
25+
coverImage: data.coverImage ?? "/game_dev_club_logo.svg",
2426
};
2527
}
2628

client/src/pages/events/[id].tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,25 @@ export default function EventPage() {
5858
<p className="mt-6 text-lg">
5959
<EventDateDisplay date={event.date} />
6060
</p>
61-
<div className="text-primary">{event.location} </div>
61+
<div className="font-firaCode text-sm text-primary">
62+
{event.location}{" "}
63+
</div>
6264
<p className="mt-4 max-w-lg text-base leading-relaxed">
6365
{event.description}
6466
</p>
67+
{event.workshopLink && (
68+
<p className="mt-4 font-firaCode">
69+
<span className="font-medium text-primary">Workshop link:</span>{" "}
70+
<a
71+
href={event.workshopLink}
72+
target="_blank"
73+
rel="noreferrer"
74+
className="underline"
75+
>
76+
{event.workshopLink}
77+
</a>
78+
</p>
79+
)}
6580
</div>
6681
<div className="lg:w-128 relative aspect-[4/3] w-full flex-shrink-0 overflow-hidden rounded-lg bg-gray-700 md:w-96">
6782
<Image
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Generated by Django 5.1.15 on 2026-03-01 05:30
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('game_dev', '0028_alter_game_hosturl'),
10+
]
11+
12+
operations = [
13+
migrations.RenameField(
14+
model_name='event',
15+
old_name='cover_image',
16+
new_name='coverImage',
17+
),
18+
migrations.AddField(
19+
model_name='event',
20+
name='workshopLink',
21+
field=models.URLField(blank=True, max_length=2083),
22+
),
23+
]

server/game_dev/models.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ class Event(models.Model):
1717
date = models.DateTimeField()
1818
description = models.CharField(max_length=256, blank=True)
1919
publicationDate = models.DateField()
20-
cover_image = models.ImageField(upload_to="events/", null=True)
20+
coverImage = models.ImageField(upload_to="events/", null=True)
2121
location = models.CharField(max_length=256)
22+
workshopLink = models.URLField(max_length=2083, blank=True)
2223

2324
def __str__(self):
2425
return self.name

server/game_dev/serializers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ class Meta:
1111
"date",
1212
"description",
1313
"publicationDate",
14-
"cover_image",
14+
"coverImage",
1515
"location",
16+
"workshopLink",
1617
]
1718

1819

server/game_dev/tests.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ def setUp(self):
4444
date=self.event_datetime,
4545
description="Yayayyayayay!",
4646
publicationDate=self.pub_date,
47-
cover_image=image_file,
47+
coverImage=image_file,
4848
location="Ezone",
49+
workshopLink="https://example.com/workshop",
4950
)
5051

5152
def test_publication_date_is_date(self):
@@ -57,10 +58,10 @@ def test_publication_date_matches(self):
5758
self.assertEqual(event.publicationDate, self.pub_date)
5859

5960
def test_cover_image_not_empty(self):
60-
self.assertIsNotNone(self.event.cover_image)
61+
self.assertIsNotNone(self.event.coverImage)
6162

6263
def test_cover_image_is_saved_in_correct_folder(self):
63-
self.assertTrue(self.event.cover_image.name.startswith("events/"))
64+
self.assertTrue(self.event.coverImage.name.startswith("events/"))
6465

6566
def test_publication_date_not_empty(self):
6667
self.assertTrue(bool(self.event.publicationDate))
@@ -73,6 +74,10 @@ def test_event_datetime_matches(self):
7374
event = Event.objects.get(pk=self.event.pk)
7475
self.assertEqual(event.date, self.event_datetime)
7576

77+
def test_workshop_link_matches(self):
78+
event = Event.objects.get(pk=self.event.pk)
79+
self.assertEqual(event.workshopLink, "https://example.com/workshop")
80+
7681

7782
class CommitteeModelTest(TestCase):
7883
def setUp(self):

0 commit comments

Comments
 (0)