Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package de.tum.cit.aet.artemis.lecture.dto;

import java.time.ZonedDateTime;

import jakarta.annotation.Nullable;
import jakarta.validation.constraints.NotNull;

public record LectureCreateDTO(@NotNull String title, @Nullable String channelName, @Nullable ZonedDateTime visibleDate, @Nullable ZonedDateTime startDate,
@Nullable ZonedDateTime endDate) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import de.tum.cit.aet.artemis.lecture.domain.Attachment;
import de.tum.cit.aet.artemis.lecture.domain.AttachmentVideoUnit;
import de.tum.cit.aet.artemis.lecture.domain.Lecture;
import de.tum.cit.aet.artemis.lecture.dto.LectureCreateDTO;
import de.tum.cit.aet.artemis.lecture.dto.SlideDTO;
import de.tum.cit.aet.artemis.lecture.repository.LectureRepository;
import de.tum.cit.aet.artemis.lecture.repository.SlideRepository;
Expand Down Expand Up @@ -133,6 +134,31 @@ public ResponseEntity<Lecture> createLecture(@RequestBody Lecture lecture) throw
return ResponseEntity.created(new URI("/api/lecture/lectures/" + savedLecture.getId())).body(savedLecture);
}

// TODO: add javaDoc
@PostMapping("courses/{courseId}/lectures")
@EnforceAtLeastEditor
public ResponseEntity<Void> createLectureSeries(@PathVariable long courseId, @RequestBody List<LectureCreateDTO> lectureDTOs) throws URISyntaxException {
log.debug("REST request to save Lecture series for courseId {} with lectures: {}", courseId, lectureDTOs);
Course course = courseRepository.findByIdElseThrow(courseId);
User user = userRepository.getUserWithGroupsAndAuthorities();
authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, course, user);
List<Lecture> lectures = lectureDTOs.stream().map(lectureDTO -> createLectureUsing(lectureDTO, course)).toList();
List<Lecture> savedLectures = lectureRepository.saveAll(lectures);
savedLectures.forEach(lecture -> channelService.createLectureChannel(lecture, Optional.ofNullable(lecture.getChannelName())));
return ResponseEntity.noContent().build();
}

private Lecture createLectureUsing(LectureCreateDTO lectureDTO, Course course) {
Lecture lecture = new Lecture();
lecture.setCourse(course);
lecture.setTitle(lectureDTO.title());
lecture.setChannelName(lectureDTO.channelName());
lecture.setVisibleDate(lectureDTO.visibleDate());
lecture.setStartDate(lectureDTO.startDate());
lecture.setEndDate(lectureDTO.endDate());
return lecture;
}

/**
* PUT /lectures : Updates an existing lecture.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<div class="frame">
<p jhiTranslate="artemisApp.lecture.createSeries.explanationText"></p>
<div class="from-section">
<p-floatlabel variant="on">
<p-select
inputId="weekday"
[options]="weekdayOptions()"
[ngModel]="selectedWeekdayIndex()"
(ngModelChange)="onSelectedWeekdayOptionChange($event)"
optionLabel="label"
optionValue="weekdayIndex"
style="width: 9rem"
/>
<label for="weekday" jhiTranslate="artemisApp.lecture.createSeries.inputLabel.weekday"></label>
</p-floatlabel>
<p-floatlabel variant="on">
<p-inputMask
inputId="start-time"
mask="99 : 99"
slotChar="-"
[ngModel]="startTime() ?? ''"
(ngModelChange)="onStartTimeChange($event)"
[invalid]="isStartAndEndTimeCombinationInvalid()"
[style]="{ width: '9rem' }"
/>
<label for="start-time" jhiTranslate="artemisApp.lecture.createSeries.inputLabel.startTime"></label>
</p-floatlabel>
<p-floatlabel variant="on">
<p-inputMask
inputId="end-time"
mask="99 : 99"
slotChar="-"
[ngModel]="endTime() ?? ''"
(ngModelChange)="onEndTimeChange($event)"
[invalid]="isStartAndEndTimeCombinationInvalid()"
[style]="{ width: '9rem' }"
/>
<label for="end-time" jhiTranslate="artemisApp.lecture.createSeries.inputLabel.endTime"></label>
</p-floatlabel>
<p-floatlabel variant="on">
<p-datepicker
inputId="start-date"
[ngModel]="startDate() ?? null"
(ngModelChange)="onStartDateChange($event)"
style="width: 9rem"
[firstDayOfWeek]="1"
[minDate]="minimumStartDate"
/>
<label for="start-date" jhiTranslate="artemisApp.lecture.createSeries.inputLabel.seriesStartDate"></label>
</p-floatlabel>
<p-floatlabel variant="on">
<p-datepicker
inputId="end-date"
[ngModel]="endDate() ?? null"
(ngModelChange)="onEndDateChange($event)"
style="width: 9rem"
[firstDayOfWeek]="1"
[minDate]="minimumEndDate()"
[invalid]="isEndDateInvalid()"
/>
<label for="end-date" jhiTranslate="artemisApp.lecture.createSeries.inputLabel.seriesEndDate"></label>
</p-floatlabel>
</div>
@let drafts = lectureDrafts();
@if (drafts.length > 0) {
<h3 jhiTranslate="artemisApp.lecture.createSeries.generatedLecturesHeading"></h3>
<div class="container w-100 px-0">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3">
@for (draft of lectureDrafts(); track draft.key; let index = $index) {
@if (draft.state !== LectureDraftState.DELETED) {
<div class="col">
<div class="draft-card">
<span class="card-number" [ngClass]="{ edited: draft.state === LectureDraftState.EDITED }">
{{ index + 1 }}
</span>
<span class="draft-title">{{ draft.dto.title }}</span>
<button type="button" class="card-button" (click)="editModal.open(draft)">
<fa-icon [icon]="faPenToSquare" />
</button>
<button type="button" class="card-button" (click)="deleteLectureDraft(draft)">
<fa-icon [icon]="faXmark" />
</button>
</div>
</div>
}
}
</div>
</div>

<div class="controls-section">
<button pButton severity="secondary" size="small" (click)="cancel()" jhiTranslate="entity.action.cancel"></button>
<button pButton size="small" (click)="save()" jhiTranslate="entity.action.save"></button>
</div>
}
@if (isLoading()) {
<div class="loading-overlay">
<div class="spinner-border" role="status">
<span class="visually-hidden" jhiTranslate="loading"></span>
</div>
</div>
}
</div>

<jhi-lecture-series-edit-modal #editModal />
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
.frame {
display: flex;
flex-direction: column;
gap: 1.5rem;

p {
margin-top: 1.5rem;
margin-bottom: 0;
}

.from-section {
display: flex;
gap: 1rem;
}

h3 {
margin: 0;
}

.draft-card {
display: flex;
align-items: center;
padding: 0.5rem;
gap: 0.5rem;
background-color: var(--body-bg);
border-radius: 0.5rem;

.card-number {
display: flex;
justify-content: center;
align-items: center;
width: 1.3rem;
height: 1.3rem;
border-radius: 0.3rem;
background-color: var(--primary);
color: white;

&.edited {
background-color: var(--warning);
color: black;
}
}

.draft-title {
flex: 1;
min-width: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}

.card-button {
all: unset;
cursor: pointer;
}

.card-button:hover {
color: var(--primary);
}
}

.controls-section {
display: flex;
justify-content: end;
gap: 1rem;
}

.loading-overlay {
position: absolute;
inset: 0;
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
animation: delayLoading 0s 0.5s forwards;
opacity: 0;
}
}

@keyframes delayLoading {
to {
opacity: 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { LectureSeriesCreateComponent } from './lecture-series-create.component';

describe('LectureSeriesCreate', () => {
let component: LectureSeriesCreateComponent;
let fixture: ComponentFixture<LectureSeriesCreateComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [LectureSeriesCreateComponent],
}).compileComponents();

fixture = TestBed.createComponent(LectureSeriesCreateComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading
Loading