diff --git a/backend/src/main/java/com/softspm/webinar/interaction/PublicTaskOfferController.java b/backend/src/main/java/com/softspm/webinar/interaction/PublicTaskOfferController.java index aa8e394..377700e 100644 --- a/backend/src/main/java/com/softspm/webinar/interaction/PublicTaskOfferController.java +++ b/backend/src/main/java/com/softspm/webinar/interaction/PublicTaskOfferController.java @@ -67,7 +67,9 @@ public class PublicTaskOfferController { @GetMapping("/tasks") @Operation(summary = "Zadania webinaru i wlasne zgloszenia") public TasksView taskList(@PathVariable String token) { - TaskService.ParticipantDetails participant = participant(token); + // odczyt, wiec takze dla prelegenta: prowadzacy ma widziec zadania, + // ktore sam oglosil + TaskService.ParticipantDetails participant = tasks.viewer(token); return new TasksView(tasks.listViews(participant.webinarId(), participant.registrationId())); } @@ -153,7 +155,7 @@ public class PublicTaskOfferController { @GetMapping("/banners") @Operation(summary = "Banery obowiazujace w tej chwili dla tego webinaru") public BannersView activeBanners(@PathVariable String token) { - TaskService.ParticipantDetails participant = participant(token); + TaskService.ParticipantDetails participant = tasks.viewer(token); return new BannersView( banners.activeFor(participant.webinarId()), banners.rotationSeconds()); @@ -169,7 +171,9 @@ public class PublicTaskOfferController { @PostMapping("/banners/{bannerId}/impression") @Operation(summary = "Zapis wyswietlenia banera, raz na uczestnika") public void bannerImpression(@PathVariable String token, @PathVariable Long bannerId) { - TaskService.ParticipantDetails participant = participant(token); + // takze prelegent: `banner_events.registration_id` jest nullowalne + // wlasnie po to, bo zdarzenie moze pochodzic od osoby bez zapisu + TaskService.ParticipantDetails participant = tasks.viewer(token); banners.recordImpression(bannerId, participant.registrationId()); } @@ -177,7 +181,7 @@ public class PublicTaskOfferController { @PostMapping("/banners/{bannerId}/click") @Operation(summary = "Zapis klikniecia w baner") public void bannerClick(@PathVariable String token, @PathVariable Long bannerId) { - TaskService.ParticipantDetails participant = participant(token); + TaskService.ParticipantDetails participant = tasks.viewer(token); banners.recordClick(bannerId, participant.registrationId()); } diff --git a/backend/src/main/java/com/softspm/webinar/interaction/TaskService.java b/backend/src/main/java/com/softspm/webinar/interaction/TaskService.java index 6388c70..4bb15e1 100644 --- a/backend/src/main/java/com/softspm/webinar/interaction/TaskService.java +++ b/backend/src/main/java/com/softspm/webinar/interaction/TaskService.java @@ -40,19 +40,22 @@ public class TaskService { private final WebinarRepository webinarRepository; private final UserRepository userRepository; private final PlainTextSanitizer sanitizer; + private final com.softspm.webinar.repository.WebinarSpeakerRepository speakerRepository; public TaskService(TaskRepository taskRepository, InteractionRepository interactionRepository, RegistrationRepository registrationRepository, WebinarRepository webinarRepository, UserRepository userRepository, - PlainTextSanitizer sanitizer) { + PlainTextSanitizer sanitizer, + com.softspm.webinar.repository.WebinarSpeakerRepository speakerRepository) { this.taskRepository = taskRepository; this.interactionRepository = interactionRepository; this.registrationRepository = registrationRepository; this.webinarRepository = webinarRepository; this.userRepository = userRepository; this.sanitizer = sanitizer; + this.speakerRepository = speakerRepository; } @Transactional @@ -265,6 +268,43 @@ public class TaskService { * Odczyt jest w serwisie, a nie w kontrolerze: @Transactional na metodzie * wolanej z tej samej klasy nie dziala, bo samowywolanie omija proxy Springa. */ + /** + * Osoba na sali: uczestnik ALBO prelegent. + * + * DWA RODZAJE TOKENU, JEDNO WEJSCIE + * ----------------------------------------------------------------------- + * Prelegent nie jest wierszem w `registrations`: nie przechodzi przez + * formularz zapisu i ma wlasny token w `webinar_speakers`. Metoda + * `participant` szuka wylacznie zapisu, wiec kazdy endpoint sali oparty + * na niej odpowiadal prelegentowi bledem 404. Objawialo sie to tym, ze + * prowadzacy **nie widzial wlasnych zadan** ani banerow, a panel pokazywal + * "Nie znaleziono zapisu" przy poprawnie ogloszonym zadaniu. + * + * `registrationId` rowne null oznacza prelegenta i jest **poprawna + * wartoscia**, nie brakiem danych: `listViews` traktuje go jako osobe bez + * wlasnych zgloszen, bo prelegent nie zapisuje sie na wlasne zadanie. + */ + @Transactional(readOnly = true) + public ParticipantDetails viewer(String token) { + return registrationRepository.findByConfirmationToken(token) + .map(registration -> new ParticipantDetails( + registration.getId(), + registration.getWebinar().getId(), + (registration.getFirstName() + " " + registration.getLastName()).trim(), + registration.getEmail(), + registration.getPhone(), + registration.getCompany())) + .orElseGet(() -> speakerRepository.findByAccessToken(token) + .map(speaker -> new ParticipantDetails( + null, + speaker.getWebinar().getId(), + speaker.getFullName(), + speaker.getEmail(), + null, + null)) + .orElseThrow(() -> new NotFoundException("Nie znaleziono zapisu"))); + } + @Transactional(readOnly = true) public ParticipantDetails participant(String confirmationToken) { Registration registration = registrationRepository.findByConfirmationToken(confirmationToken) diff --git a/frontend/src/pages/public/InteractionPanel.tsx b/frontend/src/pages/public/InteractionPanel.tsx index 2bdc781..72d6141 100644 --- a/frontend/src/pages/public/InteractionPanel.tsx +++ b/frontend/src/pages/public/InteractionPanel.tsx @@ -161,7 +161,9 @@ export function InteractionPanel({ )} - {tab === 'tasks' && } + {tab === 'tasks' && ( + + )} {tab === 'offer' && !history.speaker && } {tab === 'chat' ? ( diff --git a/frontend/src/pages/public/TasksTab.tsx b/frontend/src/pages/public/TasksTab.tsx index 63f7a40..51aac82 100644 --- a/frontend/src/pages/public/TasksTab.tsx +++ b/frontend/src/pages/public/TasksTab.tsx @@ -25,7 +25,21 @@ export type TaskView = { createdAt: string } -export function TasksTab({ token, webinarId }: { token: string; webinarId: number }) { +export function TasksTab({ + token, + webinarId, + readOnly = false, +}: { + token: string + webinarId: number + /** + * Podglad bez mozliwosci zapisu. + * + * Prowadzacy widzi zadania, ktore sam oglosil, ale nie zapisuje sie na nie: + * nie ma wiersza w `registrations`, wiec backend i tak odrzucilby zgloszenie. + */ + readOnly?: boolean +}) { const [tasks, setTasks] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) @@ -129,16 +143,18 @@ export function TasksTab({ token, webinarId }: { token: string; webinarId: numbe )} {!task.open && zapisy zamknięte} - + {!readOnly && ( + + )} {task.signedUp && (