Skip to content
Snippets Groups Projects
Verified Commit 969163ca authored by Dorian Koch's avatar Dorian Koch
Browse files

add timetable page

parent c5356b43
No related branches found
No related tags found
1 merge request!14fix layout shift, improve footer, add timetable, housekeeping
Pipeline #6130 canceled
......@@ -261,6 +261,11 @@ function UserField({ isUnavailable }: { isUnavailable: boolean }) {
Feedback
</Dropdown.Item>
</li>
<li>
<Dropdown.Item as={Link} href="/internal/timetable">
Drehplan
</Dropdown.Item>
</li>
<li className="dropdown-divider" />
<li>
<Dropdown.Item as={Link} href="/internal/user">
......
import ModeratorBarrier from "@/components/ModeratorBarrier";
import { DateTime } from "luxon";
import Link from "next/link";
interface Days {
index: number;
date: DateTime;
lectures: Lecture[];
maxcol: number;
}
interface Lecture {
time: DateTime;
time_end: DateTime;
duration: number;
timetable_col: number;
course_id: number;
id: number;
place: string;
visible: boolean;
course: {
visible: boolean;
short: string;
};
}
function TimetableTable() {
// example data
const blocks: DateTime[] = [];
for (let i = 0; i < (20 - 8) * 4; i++) {
blocks.push(
DateTime.now()
.startOf("week")
.plus({ days: 0, hours: 8 + Math.floor(i / 4), minutes: (i % 4) * 15 }),
);
}
const days: Days[] = [];
for (let i = 0; i < 5; i++) {
days.push({
index: i,
date: DateTime.now().startOf("week").plus({ days: i }),
lectures: [],
maxcol: 1,
});
}
// example lecture
days[0].lectures.push({
time: DateTime.now().startOf("week").plus({ days: 0, hours: 9, minutes: 30 }),
time_end: DateTime.now().startOf("week").plus({ days: 0, hours: 10, minutes: 45 }),
duration: 75,
timetable_col: 0,
course_id: 1,
id: 1,
place: "Online",
visible: true,
course: {
visible: true,
short: "algomod",
},
});
return (
<div className="row table-responsive">
<table
className="table table-bordered timetable col-xs-12 w-auto"
style={{ minWidth: "100%" }}
>
<thead>
<tr>
<th style={{ width: "30px" }}></th>
{days.map((d) => (
<th key={d.index} style={{ minWidth: "10em" }} colSpan={d.maxcol}>
{d.date.toLocaleString(DateTime.DATE_MED_WITH_WEEKDAY)}
</th>
))}
</tr>
</thead>
<tbody>
{blocks.map((t, time_index) => (
<tr key={time_index} className={t.minute === 0 ? "hourlytime" : ""}>
{time_index % 4 === 0 && (
<td
rowSpan={time_index === blocks.length - 1 ? undefined : 4}
style={{ verticalAlign: "top" }}
>
{t.toFormat("HH:mm")}
</td>
)}
{days.map((d) => {
const cols = [];
for (let i = 0; i < d.maxcol; i++) {
cols.push(i);
}
return cols.map((col) => {
const l = d.lectures.find(
(l) =>
l.timetable_col === col &&
l.time >= t &&
l.time < blocks[time_index + 1],
);
if (l) {
const rowSpan = Math.ceil(
(l.duration +
Math.abs(
t.minute +
t.hour * 60 -
(l.time.minute + l.time.hour * 60),
)) /
15,
);
return (
<td
key={col}
rowSpan={rowSpan}
className={
l.visible && l.course.visible
? "course"
: "course-invisible"
}
>
<p className="small">
<strong>
<Link
href={`/${l.course_id}#lecture-${l.id}`}
>
{l.course.short}
</Link>
</strong>
<br />
{l.time.toFormat("HH:mm")} -{" "}
{l.time_end.toFormat("HH:mm")}
<br />
{l.place}
</p>
</td>
);
} else {
// if the lecture is in the current column but not the current time slot
const l = d.lectures.find(
(l: any) =>
l.timetable_col === col &&
l.time < t &&
l.time_end > t,
);
if (l) {
return null; // this part is covered by another lecture using rowspan
} else {
return (
<td
key={col}
className={col === 0 ? "newday" : ""}
></td>
); // no lecture right now, just end column
}
}
});
})}
</tr>
))}
</tbody>
</table>
</div>
);
}
function TimetableImpl() {
return (
<div className="card">
<div className="card-header d-flex">Timetable</div>
<div className="card-body">
This page is a work in progress, the data here is not real.
<hr />
<TimetableTable />
</div>
</div>
/* TODO: pagination
<div className="hidden-print">
<div style="margin-top: 10px; padding: 15px;" className="col-xs-12">
{% if user %}
<a href="{{url_for('timetable_user', user=user.id, kw=kw-1) }}" className="pull-left btn btn-default">{{ "<<" }}</a>
<a href="{{url_for('timetable_user', user=user.id, kw=kw+1) }}" className="pull-right btn btn-default">{{ ">>" }}</a>
<a href="{{url_for('timetable_user', user=user.id, kw=0) }}" style="width: 80px;" className="btn btn-default center-block">today</a>
{% else %}
<a href="{{url_for('timetable', kw=kw-1) }}" className="pull-left btn btn-default">{{ "<<" }}</a>
<a href="{{url_for('timetable', kw=kw+1) }}" className="pull-right btn btn-default">{{ ">>" }}</a>
<a href="{{url_for('timetable', kw=0) }}" style="width: 80px;" className="btn btn-default center-block">today</a>
{% endif %}
</div>
<input id="weeksel" type="week" className="center-block" value="{{ weekofyear }}"/>
<script>
$( function () {
$("#weeksel").on("change", function() {
{% if user %}
window.location.href="{{ url_for('timetable_user', user=user.id) }}?date="+$("#weeksel").val()
{% else %}
window.location.href="{{ url_for('timetable') }}?date="+$("#weeksel").val()
{% endif %}
});
});
</script>
</div>
</div>
</div>
*/
);
}
export default function Timetable() {
return (
<ModeratorBarrier>
<TimetableImpl />
</ModeratorBarrier>
);
}
......@@ -7,6 +7,8 @@ $link-decoration: none;
@import 'bootstrap-icons/font/bootstrap-icons.css';
@import 'timetable.scss';
[data-bs-theme="light"] {
/* We use the light theme for dark and light (Theme can't be set by css)*/
--toastify-color-light: #f2f2f2;
......
......@@ -11,31 +11,6 @@
text-shadow: 0 0 2px black;
}
#timetable.table-bordered td:first-child {
border-left: 2px solid #ddd;
}
#timetable.table-bordered {
border-collapse: separate;
table-layout: fixed;
}
#timetable.table-bordered tr.hourlytime > td {
border-top-width: 2px;
border-top-color: #ccc;
}
#timetable.table-bordered tr > td {
border-bottom: 0px;
}
#timetable.table-bordered td.newday {
border-left: 2px solid black;
}
#timetable.table-bordered td {
border-right: 0px !important;
}
.thumbnailimg {
height: 130px;
position: relative;
......@@ -78,10 +53,6 @@
transform: translate(-50%, -50%);
}
.plot-error {
}
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
......@@ -120,7 +91,6 @@ th.rotate > div > span {
.tooltip-inner {
max-width: 500px;
}
#cutprogress td {
......
[data-bs-theme="light"] {
.timetable.table-bordered td:first-child {
border-left: 2px solid #ddd;
}
.timetable.table-bordered tr.hourlytime>td {
border-top-width: 2px;
border-top-color: #ccc;
}
.timetable.table-bordered td.newday {
border-left: 2px solid black;
}
.timetable.table-bordered tr>td.course {
background: lightgray;
}
.timetable.table-bordered tr>td.course-invisible {
background: #f2dede;
}
}
[data-bs-theme="dark"] {
.timetable.table-bordered td:first-child {
// border-left: 2px solid #ddd;
}
.timetable.table-bordered tr.hourlytime>td {
border-top-width: 2px;
border-top-color: rgb(82, 90, 97);
}
.timetable.table-bordered td.newday {
border-left: 2px solid rgb(82, 90, 97);
}
.timetable.table-bordered tr>td.course {
background: #404046;
}
.timetable.table-bordered tr>td.course-invisible {
background: #504040;
}
}
.timetable.table-bordered {
// border-collapse: separate;
table-layout: fixed;
}
.timetable.table-bordered tr>td {
border-bottom: 0px;
}
.timetable.table-bordered td {
border-right: 0px !important;
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment