Skip to content

Commit ffa6577

Browse files
authoredJan 15, 2025··
feat: Icbd timetable (#91)
1 parent 46ea62c commit ffa6577

8 files changed

+279
-13
lines changed
 

‎directus

Submodule directus updated 47 files

‎src/components/Timetable.tsx

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import { getTranslation, useTranslationTable } from "@/locales";
2+
import styles from "@/styles/Timetable.module.scss";
3+
import { ICBDActivity } from "@/types/aliases";
4+
import { useRouter } from "next/router";
5+
6+
type Timeslot = {
7+
start_time: string;
8+
end_time: string;
9+
room: string;
10+
};
11+
12+
// Convert string representation "hh:mm" to minutes
13+
function timeToMinutes(time: string) {
14+
const [hours, minutes, seconds = 0] = time.split(":").map(Number);
15+
return hours * 60 + minutes + seconds / 60;
16+
}
17+
18+
function Hours(props: { startTime: string; endTime: string }) {
19+
let startHour = timeToMinutes(props.startTime) / 60;
20+
const endHour = timeToMinutes(props.endTime) / 60;
21+
const numberOfHours = endHour - startHour;
22+
23+
let hours: any[] = [];
24+
25+
for (; startHour <= endHour; startHour++) {
26+
let style =
27+
startHour == endHour ? {} : { height: `${100 / numberOfHours}%` };
28+
hours.push(
29+
<div key={startHour} style={style} className={styles.hourEntry}>
30+
<span className={styles.line} />
31+
<p>{`${startHour}h`}</p>
32+
</div>
33+
);
34+
}
35+
36+
return <>{hours}</>;
37+
}
38+
39+
function TimeTableEvent(props: {
40+
timeslot: Timeslot;
41+
activity: ICBDActivity;
42+
startTime: string;
43+
endTime: string;
44+
}) {
45+
// In minutes
46+
const startTime = timeToMinutes(props.startTime);
47+
const endTime = timeToMinutes(props.endTime);
48+
const timeslot = props.timeslot;
49+
const tStartTime = timeToMinutes(timeslot.start_time);
50+
const tEndTime = timeToMinutes(timeslot.end_time);
51+
52+
const duration = endTime - startTime;
53+
54+
const size = (tEndTime - tStartTime) / duration;
55+
56+
const position = (tStartTime - startTime) / duration;
57+
58+
const style = { height: `calc(${size * 100}% - 0.4rem)` };
59+
60+
const router = useRouter();
61+
62+
const translation = getTranslation(props.activity, router.locale);
63+
64+
return (
65+
<div
66+
className={styles.event}
67+
style={{
68+
top: `${position * 100}%`,
69+
backgroundColor: props.activity.color || "orange",
70+
...style,
71+
}}
72+
>
73+
<p>{translation.name || ""}</p>
74+
</div>
75+
);
76+
}
77+
78+
function TimetableEntry(props: {
79+
timeslots: [Timeslot, ICBDActivity][];
80+
startTime: string;
81+
endTime: string;
82+
}) {
83+
let key = 0;
84+
85+
return (
86+
<div className={`${styles.entry} ${styles.vertical}`}>
87+
{props.timeslots.map((t) => {
88+
return (
89+
<TimeTableEvent
90+
key={key++}
91+
startTime={props.startTime}
92+
endTime={props.endTime}
93+
timeslot={t[0]}
94+
activity={t[1]}
95+
/>
96+
);
97+
})}
98+
</div>
99+
);
100+
}
101+
102+
export function Timetable(props: {
103+
activities: ICBDActivity[];
104+
startTime: string;
105+
endTime: string;
106+
}) {
107+
let rooms: Record<string, [Timeslot, ICBDActivity][]> = {};
108+
109+
let tt = useTranslationTable();
110+
111+
props.activities.forEach((activity) => {
112+
const timeslots: Timeslot[] = JSON.parse(
113+
JSON.stringify(activity.timeslots)
114+
);
115+
116+
timeslots.forEach((timeslot) => {
117+
let groupKey = timeslot.room;
118+
if (!rooms[groupKey]) {
119+
rooms[groupKey] = [];
120+
}
121+
rooms[groupKey].push([timeslot, activity]);
122+
});
123+
});
124+
125+
return (
126+
<>
127+
<table className={styles.table}>
128+
<tbody>
129+
<tr>
130+
<th />
131+
{Object.keys(rooms).map((room) => (
132+
<th className={styles.header} key={"header|" + room}>
133+
<p>{room}</p>
134+
</th>
135+
))}
136+
</tr>
137+
<tr>
138+
<td className={styles.hours}>
139+
<Hours startTime={props.startTime} endTime={props.endTime} />
140+
</td>
141+
{Object.keys(rooms).map((room) => (
142+
<td key={"entry|" + room}>
143+
<TimetableEntry
144+
timeslots={rooms[room]}
145+
startTime={props.startTime}
146+
endTime={props.endTime}
147+
/>
148+
</td>
149+
))}
150+
</tr>
151+
</tbody>
152+
</table>
153+
<p>{`${tt["icbd-end-of-event"]} ${props.endTime}`}</p>
154+
</>
155+
);
156+
}

‎src/pages/icbd.tsx

+15-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import DirectusImage from "@/components/DirectusImage";
33
import IcbdActivityCard from "@/components/IcbdActivityCard";
44
import ParticlesComponent from "@/components/Particles";
55
import TabTitle from "@/components/TabTitle";
6+
import { Timetable } from "@/components/Timetable";
67
import { directus, getDirectusImageUrl, populateLayoutProps } from "@/directus";
78
import { getTranslation } from "@/locales";
89
import style from "@/styles/ICBDPage.module.scss";
@@ -175,11 +176,10 @@ export default function ICBDPage(
175176

176177
<div className={pageStyle.main}>
177178
<h1>Timetable</h1>
178-
<DirectusImage
179-
img={props.icbd.timetable}
180-
name="timetable"
181-
sizes="50rem"
182-
className={style.timetable}
179+
<Timetable
180+
activities={props.activities}
181+
startTime="10:00"
182+
endTime="18:00"
183183
/>
184184
</div>
185185

@@ -225,17 +225,24 @@ export const getServerSideProps: GetServerSideProps<{
225225
),
226226
speakers: await directus().request(
227227
readItems("icbd_speakers", {
228-
fields: ["picture", "first_name", "last_name", "company"],
228+
fields: ["id", "picture", "first_name", "last_name", "company"],
229229
})
230230
),
231231
phds: await directus().request(
232232
readItems("icbd_phds", {
233-
fields: ["picture", "first_name", "last_name", "laboratory"],
233+
fields: ["id", "picture", "first_name", "last_name", "laboratory"],
234234
})
235235
),
236236
activities: await directus().request(
237237
readItems("icbd_activities", {
238-
fields: ["hosts", "icon", { translations: ["*"] }],
238+
fields: [
239+
"id",
240+
"hosts",
241+
"icon",
242+
"timeslots",
243+
"color",
244+
{ translations: ["*"] },
245+
],
239246
})
240247
),
241248
},

‎src/styles/ICBDPage.module.scss

+7
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,13 @@
121121
width: 10rem;
122122
}
123123
}
124+
125+
@media (max-width: 1000px) {
126+
.partnersList {
127+
gap: 1rem;
128+
padding: 2rem;
129+
}
130+
}
124131
}
125132

126133
.alumni {

‎src/styles/IcbdActivityCard.module.scss

+4
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,8 @@
3232
.description {
3333
width: 100%;
3434
}
35+
36+
@media (max-width: 1000px) {
37+
width: 65%;
38+
}
3539
}

‎src/styles/SubsonicPage.module.scss

-4
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,4 @@
101101
margin: 0;
102102
text-align: justify;
103103
}
104-
105-
// img {
106-
// border-radius: variables.$small-border-radius;
107-
// }
108104
}

‎src/styles/Timetable.module.scss

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
@use "./utilities/variables";
2+
3+
.table {
4+
width: min(90%, 100rem);
5+
height: 800px;
6+
border-collapse: collapse;
7+
overflow: hidden;
8+
9+
@media (max-width: 1000px) {
10+
height: 600px;
11+
}
12+
13+
td {
14+
padding: 2rem 0 2rem 0;
15+
height: 100%;
16+
}
17+
18+
tbody {
19+
td:nth-child(odd):not(.hours) {
20+
background-color: variables.$icbd-table-gray-column; // Apply style to even items
21+
}
22+
23+
td:nth-child(even) {
24+
background-color: white; // Apply style to odd items
25+
}
26+
}
27+
28+
.hours {
29+
width: 50px;
30+
border-radius: variables.$small-border-radius 0 0
31+
variables.$small-border-radius;
32+
background-color: variables.$icbd-table-gray-hours;
33+
34+
.hourEntry {
35+
text-align: right;
36+
padding-right: 0.5rem;
37+
position: relative;
38+
39+
p {
40+
transform: translateY(-0.75rem);
41+
margin: 0;
42+
width: 100%;
43+
text-align: right;
44+
color: grey;
45+
}
46+
47+
.line {
48+
position: absolute;
49+
height: 1px;
50+
width: 100vw;
51+
background-color: variables.$icbd-table-gray-hours;
52+
53+
transform: translateY(-1px);
54+
left: 100%;
55+
}
56+
}
57+
}
58+
59+
.header {
60+
width: 10rem;
61+
}
62+
63+
.entry {
64+
height: 100%;
65+
display: flex;
66+
position: relative;
67+
overflow: hidden;
68+
69+
border-right: variables.$icbd-table-gray-hours;
70+
border-top: variables.$icbd-table-gray-hours;
71+
border-bottom: variables.$icbd-table-gray-hours;
72+
73+
.event {
74+
display: flex;
75+
position: absolute;
76+
align-items: center;
77+
justify-content: center;
78+
79+
width: calc(100% - 0.4rem);
80+
height: calc(100% - 0.4rem);
81+
82+
margin: 0.2rem;
83+
84+
border-radius: 10px;
85+
86+
p {
87+
color: white;
88+
text-align: center;
89+
font-weight: normal;
90+
}
91+
}
92+
}
93+
}

‎src/styles/utilities/_variables.scss

+3
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,6 @@ $icbd-gradient: linear-gradient(
2828
rgba(254, 95, 3, 1) 0%,
2929
rgb(255, 185, 64) 100%
3030
);
31+
32+
$icbd-table-gray-column: rgb(246, 246, 246);
33+
$icbd-table-gray-hours: rgb(235, 235, 235);

0 commit comments

Comments
 (0)
Please sign in to comment.