Select Git revision
CourseListing.tsx
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
CourseListing.tsx 13.55 KiB
import { useLoaderData } from "react-router-dom";
import { useBackendContext } from "./BackendProvider";
import { semesterToHuman } from "@/misc/Formatting";
import Link from "next/link";
import { Backend } from "@/api/Backend";
import {
OMCreate,
OMDelete,
OMEdit,
OMHistory,
EmbeddedOMFieldComponent,
} from "./OMConfigComponent";
import { useEditMode } from "./EditModeProvider";
import { DownloadSources } from "./Player";
import Title from "./TitleComponent";
import DownloadAllModal from "./DownloadManager";
import type { GetCourseResponse, lecture } from "@/api/api_v1_types";
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">
<EmbeddedOMFieldComponent
object_type="course"
object_id={course.id!}
field_id="full_name"
field_type="string"
field_value={course.full_name}
/>
</span>
<div>
{editMode && (
<>
<OMHistory object_type="course" object_id={course.id!} />
<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">
<Link
href={`/courses#course-${course.id}`}
className="btn btn-primary mb-1"
>
<span className="bi bi-chevron-left" /> Zur Kursliste
</Link>
<table className="table table-sm">
<tbody>
<tr>
<td>Semester:</td>
<td>{semesterToHuman(course.semester, true)}</td>
</tr>
<tr>
<td>Veranstalter:</td>
<td>
<EmbeddedOMFieldComponent
object_type="course"
object_id={course.id!}
field_id="organizer"
field_type="string"
field_value={course.organizer}
/>
</td>
</tr>
<tr>
<td className="w-25">Bemerkungen:</td>
<td>
<EmbeddedOMFieldComponent
object_type="course"
object_id={course.id!}
field_id="description"
field_type="string"
field_value={course.description}
allowMarkdown={true}
/>
</td>
</tr>
</tbody>
</table>
<DownloadAllModal course={course} />
</div>
</div>
</div>
</div>
);
}
export function LectureListItem({
lecture,
course_id_string,
...props
}: {
lecture: lecture;
course_id_string?: string;
[key: string]: any;
}) {
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 = <></>;
if (lecture.media_sources && lecture.media_sources.length > 0) {
let thumbUrl = lecture.thumbnail_url ?? `${api.assetUrl()}/thumbnail/l_${lecture.id}.jpg`;
content = (
<>
<div
style={{
backgroundImage: `url('${thumbUrl}')`,
}}
className="col-sm-2 col-12 thumbnailimg"
>
<Link href={`/${course_id_string ?? lecture.course_id}/${lecture.id}`}>
<span aria-hidden="true" className={"playpreviewbtn bi bi-play-circle"} />
</Link>
</div>
<ul className="list-unstyled col-sm-3 col-12">
<li>
<EmbeddedOMFieldComponent
object_type="lecture"
object_id={lecture.id!}
field_id="title"
field_type="string"
field_value={lecture.title}
/>
</li>
{lecture.speaker ? (
<li>
Gehalten von{" "}
<EmbeddedOMFieldComponent
object_type="lecture"
object_id={lecture.id!}
field_id="speaker"
field_type="string"
field_value={lecture.speaker}
allowMarkdown={true}
/>
</li>
) : (
<></>
)}
<li>
{
// Format: Thu, 14.04.2016, 10:15 Uhr
date_str
}
</li>
</ul>
<ul className="list-unstyled col-sm-3 col-12">
<li>
<EmbeddedOMFieldComponent
object_type="lecture"
object_id={lecture.id!}
field_id="description"
field_type="string"
field_value={lecture.description}
allowMarkdown={true}
/>
</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">
{lecture.media_sources && (
<DownloadSources
media_sources={lecture.media_sources}
direction="down"
tabIndex="-1"
/>
)}
<li className="flex-fill" />
<li>
{editMode && (
<>
<OMHistory object_type="lecture" object_id={lecture.id!} />
<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>
<EmbeddedOMFieldComponent
object_type="lecture"
object_id={lecture.id!}
field_id="title"
field_type="string"
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>
<EmbeddedOMFieldComponent
object_type="lecture"
object_id={lecture.id!}
field_id="description"
field_type="string"
field_value={lecture.description}
allowMarkdown={true}
/>
</li>
</ul>
</>
);
}
return (
<li
className={`list-group-item lecture-list-item ${lecture.no_recording ? "text-muted" : ""}`}
id={`lecture-${lecture.id}`}
{...props}
>
<div className="row">{content}</div>
</li>
);
}
function ListingBody({ course }: { course: GetCourseResponse }) {
const { editMode } = useEditMode();
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) => {
// time looks like this: 2015-10-20T12:15:00
return new Date(a.time).valueOf() - new Date(b.time).valueOf();
})
.map((lecture) => (
<LectureListItem
key={lecture.id}
lecture={lecture}
course_id_string={course.id_string}
/>
))}
</ul>
</div>
</div>
);
}
export default function CourseListing() {
const course = useLoaderData() as GetCourseResponse | undefined;
if (!course) return <>invalid course listing</>;
return (
<>
<Title title={course.full_name} />
<ListingHeader course={course} />
<ListingBody course={course} />
</>
);
}