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 && (