Select Git revision
config.py.example
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
CourseListing.tsx 11.88 KiB
import { useLoaderData, useParams } from "react-router-dom";
import { useBackendContext } from "./BackendProvider";
import { semesterToHuman } from "@/misc/Formatting";
import Link from "next/link";
import { Backend } from "@/api/Backend";
import { useEffect, useState } from "react";
import { OMCreate, OMDelete, OMEdit, StandaloneOMStringComponent } from "./OMConfigComponent";
import { useEditMode } from "./EditModeProvider";
import { DownloadSources } from "./Player";
export function getCourseDataLoader(api: Backend) {
const actualLoader = async ({ params }: { params: { course_id_string?: string } }) => {
return api.getCourse(params.course_id_string!, true);
};
return actualLoader;
}
function ListingHeader({ course }: { course: GetCourseResponse }) {
const { editMode } = useEditMode();
return (
<div className="card mb-3">
<div className="card-body">
<h5 className="card-title d-flex">
<span className="panel-title flex-fill align-self-center">
<StandaloneOMStringComponent
object_type="course"
object_id={course.id!}
field_id="full_name"
field_value={course.full_name}
/>
</span>
<div>
{editMode && (
<>
<OMEdit object_type="course" object_id={course.id!} />
<OMDelete object_type="course" object_id={course.id!} />
</>
)}
</div>
</h5>
<div className="row">
<div className="col-12">
<table className="table table-sm">
<tbody>
<tr>
<td>Semester:</td>
<td>{semesterToHuman(course.semester, true)}</td>
</tr>
<tr>
<td>Veranstalter:</td>
<td>
<StandaloneOMStringComponent
object_type="course"
object_id={course.id!}
field_id="organizer"
field_value={course.organizer}
/>
</td>
</tr>
<tr>
<td className="w-25">Bemerkungen:</td>
<td>
<StandaloneOMStringComponent
object_type="course"
object_id={course.id!}
field_id="description"
field_value={course.description}
allowMarkdown={true}
/>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
);
}
function LectureListItem({ lecture }: { lecture: lecture }) {
const api = useBackendContext();
const { editMode } = useEditMode();
let date_parsed = new Date(lecture.time);
let date_str = `${date_parsed.toLocaleDateString([], { weekday: "short", year: "numeric", month: "2-digit", day: "2-digit" })}, ${date_parsed.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`;
let content = <></>;
let { course_id_string } = useParams();
if (lecture.media_sources && lecture.media_sources.length > 0) {
content = (
<>
<div
style={{
backgroundImage: `url('${api.assetUrl()}/thumbnail/l_${lecture.id}.jpg')`,
}}
className="col-sm-2 col-12 thumbnailimg"
>
<Link href={`/${course_id_string}/${lecture.id}`}>
<span
aria-hidden="true"
className={"playpreviewbtn bi bi-play-circle"}
></span>
</Link>
</div>
<ul className="list-unstyled col-sm-3 col-12">
<li>
<StandaloneOMStringComponent
object_type="lecture"
object_id={lecture.id!}
field_id="title"
field_value={lecture.title}
/>
</li>
{lecture.speaker ? (
<li>
Gehalten von{" "}
<StandaloneOMStringComponent
object_type="lecture"
object_id={lecture.id!}
field_id="speaker"
field_value={lecture.speaker}
/>
</li>
) : (
<></>
)}
<li>
{
// Format: Thu, 14.04.2016, 10:15 Uhr
date_str
}
</li>
</ul>
<ul className="list-unstyled col-sm-3 col-12">
<li>
<StandaloneOMStringComponent
object_type="lecture"
object_id={lecture.id!}
field_id="description"
field_value={lecture.description}
/>
</li>
{lecture.chapters?.map((chapter) => (
<li key={chapter.name}>
<span className="bi bi-play" />
<Link
href={"/${course_id_string}/${lecture.id}?t=" + chapter.start_time}
>
{chapter.name}
</Link>
</li>
))}
{/*% if ismod() %}
<li>{{ moderator_editor(['lectures',lecture.id,'internal'], lecture.internal) }}</li>
<li>Sichtbar: {{ moderator_checkbox(['lectures',lecture.id,'visible'], lecture.visible) }}</li>
<li>Livestream geplant: {{ moderator_checkbox(['lectures',lecture.id,'live'], lecture.live) }}</li>
<li>Wird nicht aufgenommen: {{ moderator_checkbox(['lectures',lecture.id,'norecording'], lecture.norecording) }}</li>
<li>Hörsaal: {{ moderator_editor(['lectures',lecture.id,'place'], lecture.place) }} </li>
{% endif %*/}
</ul>
<ul className="col-sm-4 col-12 list-unstyled d-flex">
<DownloadSources media_sources={lecture.media_sources} direction="down" />
<li className="flex-fill" />
<li>
{editMode && (
<>
<OMEdit object_type="lecture" object_id={lecture.id!} />
<OMDelete object_type="lecture" object_id={lecture.id!} />
</>
)}
</li>
</ul>
</>
);
} else {
content = (
<>
<div className="col-sm-2 col-12"></div>
<ul className="list-unstyled col-sm-3 col-12">
<li>
<StandaloneOMStringComponent
object_type="lecture"
object_id={lecture.id!}
field_id="title"
field_value={lecture.title}
/>
{/*{livelabel((lecture.live and lecture.time > datetime.now()-timedelta(days=1)), videos|selectattr("livehandle")|list|length)}*/}
{/*% if lecture.chapter_count|d(0) > 0 and ismod() %}
<span className="label label-info" data-toggle="tooltip" title="Nicht freigegebene Kapitel">{{ lecture.chapter_count }}</span>
{% endif %*/}
</li>
</ul>
<ul className="list-unstyled col-sm-3 col-12">
{/*% if ismod() %}
<li>{{ moderator_editor(['lectures',lecture.id,'time'], lecture.time) }} </li>
{% else %*/}
<li>{date_str}</li>
</ul>
<ul className="list-inline col-sm-4 col-12">
<li>
<StandaloneOMStringComponent
object_type="lecture"
object_id={lecture.id!}
field_id="description"
field_value={lecture.description}
/>
</li>
</ul>
</>
);
}
return (
<li
className={`list-group-item ${lecture.no_recording ? "text-muted" : ""}`}
id={`lecture-${lecture.id}`}
>
<div className="row">{content}</div>
</li>
);
}
function ListingBody({ course }: { course: GetCourseResponse }) {
const { editMode } = useEditMode();
useEffect(() => {
import("bootstrap/js/dist/dropdown"); // Für download button
}, []);
return (
<div className="card mb-3">
<div className="card-body">
<h5 className="card-title d-flex align-items-center">
Videos
<div className="flex-fill" />
{editMode && (
<>
<OMCreate
object_type="lecture"
parent_type="course"
parent_id={course.id!}
>
<button type="button" className="btn btn-secondary mx-2">
<i className="bi bi-plus" />
Neues Video
</button>
</OMCreate>
<Link
href={`/internal/import?course_id=${course.id}`}
className="btn btn-secondary"
>
<i className="bi bi-box-arrow-in-down" />
{" Import"}
</Link>
</>
)}
</h5>
<ul className="list-group lectureslist">
{course
.lectures!.sort((a, b) => {
return parseInt(a.time) - parseInt(b.time);
})
.map((lecture) => (
<LectureListItem key={lecture.id} lecture={lecture} />
))}
</ul>
</div>
</div>
);
}
export default function CourseListing() {
const course = useLoaderData() as GetCourseResponse | undefined;
if (!course) return <>invalid course listing</>;
return (
<>
<ListingHeader course={course} />
<ListingBody course={course} />
</>
);
}