Skip to content

enhance(apps/frontend-manage): display basic metadata on evaluation screen if embedded/not active#4920

Draft
rschlaefli wants to merge 7 commits intov3from
activity-info-on-eval
Draft

enhance(apps/frontend-manage): display basic metadata on evaluation screen if embedded/not active#4920
rschlaefli wants to merge 7 commits intov3from
activity-info-on-eval

Conversation

@rschlaefli
Copy link
Copy Markdown
Member

@rschlaefli rschlaefli commented Sep 16, 2025

Summary by CodeRabbit

  • New Features
    • Status indicator for evaluation elements (Scheduled / Active / Executed) with tooltips showing last refresh or execution date.
    • Visual status overlay for live quiz elements and passing of timing/context to evaluations.
  • Improvements
    • Enhanced "Evaluation unavailable" notice including activity and course details plus an "Activity details" link.
    • Evaluation view now shows activity status, course name and tracks last refetch time for live updates.
  • Internationalization
    • Added English/German labels for new evaluation metadata and instance statuses.
  • Chores
    • Public assessment flag enabled in frontend config.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Sep 16, 2025

📝 Walkthrough

Walkthrough

Adds evaluation status and courseName to the GraphQL evaluation model and service, surfaces them through the frontend evaluation flow, introduces a BlockStatusIndicator, extends Activity/Element evaluation components and EvaluationUnavailableNotification with contextual props, tracks lastRefetchTime, updates i18n, and adds an assessment config flag.

Changes

Cohort / File(s) Summary
Frontend evaluation components
apps/frontend-manage/src/components/evaluation/ActivityEvaluation.tsx, apps/frontend-manage/src/components/evaluation/ElementEvaluation.tsx, apps/frontend-manage/src/components/evaluation/EvaluationUnavailableNotification.tsx, apps/frontend-manage/src/components/evaluation/BlockStatusIndicator.tsx
Add/propagate new props: courseName, activityStatus (PublicationStatus), activityId/activityName, and lastRefetchTime; render detailed EvaluationUnavailableNotification; add BlockStatusIndicator overlay for block statuses (Scheduled/Active/Executed).
Quiz evaluation page
apps/frontend-manage/src/pages/quizzes/[id]/evaluation.tsx
Track lastRefetchTime (useEffect), strengthen loading guard, render EvaluationUnavailableNotification for empty evaluations, and pass activityStatus, courseName, lastRefetchTime into ActivityEvaluation.
GraphQL schema & service
packages/graphql/src/schema/evaluation.ts, packages/graphql/src/services/liveQuizzes.ts
Schema: make displayName required, add status: PublicationStatus and courseName. Service: stop filtering live quizzes/blocks by status, include course name/language, and return status and courseName.
i18n additions
packages/i18n/messages/en.ts, packages/i18n/messages/de.ts
Add evaluation-related translation keys (courseName, activityName, activityStatus, elementName, elementType, linkActivityDetails, instanceScheduled/Active/Executed/LastRefetch/ExecutionDate) and minor text reflows.
Deployment config
deploy/charts/klicker-uzh-v2/templates/cm-frontend-assessment.yaml
Add NEXT_PUBLIC_IS_ASSESSMENT: "true" to ConfigMap data.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Manager as Manager
  participant Page as quizzes/[id]/evaluation page
  participant API as GraphQL API
  participant CompA as ActivityEvaluation
  participant CompE as ElementEvaluation
  participant UI1 as EvaluationUnavailableNotification
  participant UI2 as BlockStatusIndicator

  Manager->>Page: Open evaluation
  Page->>API: getLiveQuizEvaluation(id)
  API-->>Page: { displayName, status, courseName, stacks, blocks, ... }
  Note over Page: useEffect updates lastRefetchTime on data change
  alt loading or missing data
    Page->>Page: Show Loader
  else empty evaluation
    Page->>UI1: Render with { activityId, activityName, activityStatus, courseName }
  else data available
    Page->>CompA: Render with { courseName, activityStatus, lastRefetchTime, ... }
    alt no active instance
      CompA->>UI1: Render with { courseName, activityName, activityId, activityStatus }
    else instance present
      CompA->>CompE: Render element with { lastRefetchTime, courseId, courseName, activityName, activityId, activityStatus }
      alt LiveQuiz block with status
        CompE->>UI2: Show status indicator (Scheduled/Active/Executed)
        opt Scheduled
          CompE->>UI1: Render unavailable notice with element/activity/course context
        end
      end
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • sjschlapbach

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title accurately and concisely describes the main intent of the changeset: exposing and showing basic metadata on the evaluation screen when embedded or when an instance is not active. The modifications across ActivityEvaluation, ElementEvaluation, EvaluationUnavailableNotification, GraphQL/evaluation schema, and i18n align with and implement that purpose, and the scope "apps/frontend-manage" is correctly specified.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 97bbc6d and 4d35043.

⛔ Files ignored due to path filters (5)
  • packages/graphql/src/ops.schema.json is excluded by !**/**/ops.schema.json
  • packages/graphql/src/ops.ts is excluded by !**/**/ops.ts
  • packages/graphql/src/public/client.json is excluded by !**/**/public/**
  • packages/graphql/src/public/schema.graphql is excluded by !**/**/public/**
  • packages/graphql/src/public/server.json is excluded by !**/**/public/**
📒 Files selected for processing (2)
  • packages/graphql/src/services/liveQuizzes.ts (3 hunks)
  • packages/i18n/messages/de.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/graphql/src/services/liveQuizzes.ts
  • packages/i18n/messages/de.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: test
  • GitHub Check: SonarCloud
  • GitHub Check: cypress-run-cloud
  • GitHub Check: claude-review
  • GitHub Check: build
  • GitHub Check: Analyze (javascript)
  • GitHub Check: check
  • GitHub Check: format
  • GitHub Check: test
  • GitHub Check: build

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rschlaefli rschlaefli changed the title initial implementation enhance(apps/frontend-manage): display basic metadata on evaluation screen if embedded Sep 16, 2025
@rschlaefli rschlaefli changed the title enhance(apps/frontend-manage): display basic metadata on evaluation screen if embedded enhance(apps/frontend-manage): display basic metadata on evaluation screen if embedded/not active Sep 16, 2025
…ity-info-on-eval

# Conflicts:
#	apps/frontend-manage/src/components/evaluation/ActivityEvaluation.tsx
#	apps/frontend-manage/src/pages/quizzes/[id]/evaluation.tsx
#	packages/graphql/src/ops.ts
#	packages/graphql/src/public/client.json
#	packages/graphql/src/public/server.json
#	packages/graphql/src/services/liveQuizzes.ts
@rschlaefli rschlaefli marked this pull request as ready for review September 17, 2025 20:10
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. enhancement labels Sep 17, 2025
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (12)
packages/graphql/src/schema/evaluation.ts (1)

16-16: Reusing PublicationStatus enum is fine; watch for duplicate registrations

Importing PublicationStatus from practiceQuiz is OK with a shared builder. If multiple modules later attempt to define the same enum, centralize it in a dedicated module to avoid accidental re‑declarations.

packages/graphql/src/services/liveQuizzes.ts (1)

2221-2221: Selecting course.name is necessary for new field; consider selecting id too

Selecting language and name is correct for courseName. If clients also link to course details (as UI hints suggest), consider selecting course.id here to avoid future query changes.

-      course: { select: { language: true, name: true } },
+      course: { select: { id: true, language: true, name: true } },
packages/i18n/messages/de.ts (2)

671-673: Fix typo: “Aktiväten” → “Aktivitäten”.

User‑facing text; low‑risk quick win.

- Grundsätzlich ist eine anonyme Teilnahme an allen Aktiväten in KlickerUZH ausser Gruppenaktivitäten möglich. Für Live‑Quizzes finden Sie die laufenden Quizzes eines Accounts unter
+ Grundsätzlich ist eine anonyme Teilnahme an allen Aktivitäten in KlickerUZH ausser Gruppenaktivitäten möglich. Für Live‑Quizzes finden Sie die laufenden Quizzes eines Accounts unter

2450-2463: Use i18n placeholders for dates; remove manual concatenation in the UI

  • Add a {date} interpolation to instanceLastRefetch and instanceExecutionDate in packages/i18n/messages/en.ts and packages/i18n/messages/de.ts (e.g. "Last data refresh: {date}" / "Letztes Daten-Update: {date}", "Element executed at {date}" / "Element durchgeführt am {date}").
  • Update apps/frontend-manage/src/components/evaluation/BlockStatusIndicator.tsx (around lines 41–56) to call t('manage.evaluation.instanceLastRefetch', { date: lastRefetchTime.toLocaleString() }) and t('manage.evaluation.instanceExecutionDate', { date: new Date(expiresAt).toLocaleString() }) and remove the manual ": " + date concatenation.
apps/frontend-manage/src/components/evaluation/ElementEvaluation.tsx (2)

47-53: Unused prop: courseId.

The prop is accepted but never used in ElementEvaluation. Either use it (e.g., for links) or drop it from props and callers to avoid drift.


158-167: Overlay indicator layering and hit‑testing.

To ensure the status badge isn’t occluded and doesn’t intercept clicks, consider adding z-index and disabling pointer events on the container.

-                <div className="absolute bottom-4 left-4">
+                <div className="absolute bottom-4 left-4 z-10 pointer-events-none">
                   <BlockStatusIndicator
                     status={currentStack.status}
                     lastRefetchTime={lastRefetchTime}
                     expiresAt={currentStack.expiresAt}
                   />
                 </div>
apps/frontend-manage/src/components/evaluation/BlockStatusIndicator.tsx (2)

39-43: Locale-aware date/time formatting.

Use next-intl’s formatter (or a centralized util) instead of toLocaleString for consistent i18n and timezone handling.

-import { useTranslations } from 'next-intl'
+import { useFormatter, useTranslations } from 'next-intl'
@@
-  const t = useTranslations()
+  const t = useTranslations()
+  const f = useFormatter()
@@
-                {t('manage.evaluation.instanceLastRefetch')}: {lastRefetchTime.toLocaleString()}
+                {t('manage.evaluation.instanceLastRefetch')}: {f.dateTime(lastRefetchTime, { dateStyle: 'medium', timeStyle: 'short' })}
@@
-                {t('manage.evaluation.instanceExecutionDate')}: {new Date(expiresAt).toLocaleString()}
+                {t('manage.evaluation.instanceExecutionDate')}: {f.dateTime(new Date(expiresAt), { dateStyle: 'medium', timeStyle: 'short' })}

Also applies to: 55-57


85-96: Improve a11y for the status dot.

Expose a text label (aria-label/title) for screen readers and non-hover devices. You can set it on the wrapper using the same tooltip text.

-  return (
-    <div className={twMerge('flex justify-center', className)}>
+  return (
+    <div
+      className={twMerge('flex justify-center', className)}
+      aria-label={t(
+        status === ElementBlockStatus.Scheduled
+          ? 'manage.evaluation.instanceScheduled'
+          : status === ElementBlockStatus.Active
+          ? 'manage.evaluation.instanceActive'
+          : 'manage.evaluation.instanceExecuted'
+      )}
+      title={t(
+        status === ElementBlockStatus.Scheduled
+          ? 'manage.evaluation.instanceScheduled'
+          : status === ElementBlockStatus.Active
+          ? 'manage.evaluation.instanceActive'
+          : 'manage.evaluation.instanceExecuted'
+      )}
+    >
       <Tooltip
         tooltip={getTooltipContent()}
apps/frontend-manage/src/components/evaluation/EvaluationUnavailableNotification.tsx (1)

44-50: Localize element type label.

Currently prints the enum. Use existing shared translations for element type.

-              : {elementType}
+              : {t(`shared.${elementType}.typeLabel`, { fallback: elementType })}
apps/frontend-manage/src/pages/quizzes/[id]/evaluation.tsx (1)

38-41: Empty-state condition may miss the “no leaderboard” case.

If leaderboard is null/undefined, === 0 is false. Consider treating “absent or empty” as empty.

-  data.liveQuizEvaluation.results.length === 0 &&
-  data.liveQuizLeaderboard?.length === 0 &&
-  data.liveQuizEvaluation.feedbacks?.length === 0 &&
-  data.liveQuizEvaluation.confusionFeedbacks?.length === 0
+  data.liveQuizEvaluation.results.length === 0 &&
+  (!data.liveQuizLeaderboard || data.liveQuizLeaderboard.length === 0) &&
+  ((data.liveQuizEvaluation.feedbacks?.length ?? 0) === 0) &&
+  ((data.liveQuizEvaluation.confusionFeedbacks?.length ?? 0) === 0)
apps/frontend-manage/src/components/evaluation/ActivityEvaluation.tsx (2)

89-95: Memoize instanceResults to avoid recomputation.

Minor perf tidy-up on re-renders.

-import { useReducer, useState } from 'react'
+import { useMemo, useReducer, useState } from 'react'
@@
-  const instanceResults = stacks.flatMap((stack, stackIx) =>
-    stack.instances.map((instance) => ({
-      ...instance,
-      stackIx,
-    }))
-  )
+  const instanceResults = useMemo(
+    () =>
+      stacks.flatMap((stack, stackIx) =>
+        stack.instances.map((instance) => ({ ...instance, stackIx }))
+      ),
+    [stacks]
+  )

189-214: Prop surface expansion LGTM; note pass-through of unused courseId to child.

If ElementEvaluation doesn’t use courseId, consider removing the prop here too in a follow-up to keep APIs tight.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f6ec47 and 97bbc6d.

⛔ Files ignored due to path filters (6)
  • packages/graphql/src/graphql/ops/QGetLiveQuizEvaluation.graphql is excluded by !**/**/graphql/ops/**
  • packages/graphql/src/ops.schema.json is excluded by !**/**/ops.schema.json
  • packages/graphql/src/ops.ts is excluded by !**/**/ops.ts
  • packages/graphql/src/public/client.json is excluded by !**/**/public/**
  • packages/graphql/src/public/schema.graphql is excluded by !**/**/public/**
  • packages/graphql/src/public/server.json is excluded by !**/**/public/**
📒 Files selected for processing (10)
  • apps/frontend-manage/src/components/evaluation/ActivityEvaluation.tsx (7 hunks)
  • apps/frontend-manage/src/components/evaluation/BlockStatusIndicator.tsx (1 hunks)
  • apps/frontend-manage/src/components/evaluation/ElementEvaluation.tsx (5 hunks)
  • apps/frontend-manage/src/components/evaluation/EvaluationUnavailableNotification.tsx (2 hunks)
  • apps/frontend-manage/src/pages/quizzes/[id]/evaluation.tsx (3 hunks)
  • deploy/charts/klicker-uzh-v2/templates/cm-frontend-assessment.yaml (1 hunks)
  • packages/graphql/src/schema/evaluation.ts (2 hunks)
  • packages/graphql/src/services/liveQuizzes.ts (3 hunks)
  • packages/i18n/messages/de.ts (3 hunks)
  • packages/i18n/messages/en.ts (3 hunks)
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-03-11T15:07:47.117Z
Learnt from: sjschlapbach
PR: uzh-bf/klicker-uzh#4550
File: apps/frontend-manage/src/components/courses/modals/TemplateConversionModal.tsx:132-138
Timestamp: 2025-03-11T15:07:47.117Z
Learning: The translation keys for template conversion error messages (`noInstances`, `resourcesRequiredMissing`, `noResourceAccessRequired`) are properly defined in the translation files (packages/i18n/messages/en.ts and packages/i18n/messages/de.ts) for the KlickerUZH project.

Applied to files:

  • packages/i18n/messages/de.ts
📚 Learning: 2025-09-13T17:39:50.281Z
Learnt from: sjschlapbach
PR: uzh-bf/klicker-uzh#4902
File: packages/util/src/blocks.ts:84-107
Timestamp: 2025-09-13T17:39:50.281Z
Learning: FLASHCARD elements are not allowed to be included in live quizzes in the KlickerUZH system, so FLASHCARD handling is not needed in live quiz submission and result processing logic.

Applied to files:

  • packages/i18n/messages/de.ts
  • packages/i18n/messages/en.ts
📚 Learning: 2025-04-17T12:52:17.002Z
Learnt from: sjschlapbach
PR: uzh-bf/klicker-uzh#4606
File: packages/graphql/src/services/liveQuizzes.ts:1100-1106
Timestamp: 2025-04-17T12:52:17.002Z
Learning: In the application architecture, permission checks for operations like activateLiveQuizBlock, deactivateLiveQuizBlock, endLiveQuiz, cancelLiveQuiz, and deleteLiveQuiz are performed at the GraphQL mutation resolver level using the checkAccess function, rather than in the service functions themselves. This separation of concerns means that ownerId filters within service functions are no longer necessary.

Applied to files:

  • packages/graphql/src/services/liveQuizzes.ts
📚 Learning: 2025-04-17T12:47:09.182Z
Learnt from: sjschlapbach
PR: uzh-bf/klicker-uzh#4625
File: packages/graphql/src/services/liveQuizzes.ts:771-779
Timestamp: 2025-04-17T12:47:09.182Z
Learning: In the KlickerUZH system, running live quizzes (with status PublicationStatus.PUBLISHED) cannot be deleted as prevented by validation logic in the deleteLiveQuiz function, making the isDeleted: false filter redundant when querying for published quizzes.

Applied to files:

  • packages/graphql/src/services/liveQuizzes.ts
📚 Learning: 2025-09-08T19:16:04.791Z
Learnt from: sjschlapbach
PR: uzh-bf/klicker-uzh#4884
File: apps/frontend-pwa/src/components/liveQuiz/LiveQuizSubscriber.tsx:21-37
Timestamp: 2025-09-08T19:16:04.791Z
Learning: For RunningLiveQuizUpdatedDocument subscription in apps/frontend-pwa/src/components/liveQuiz/LiveQuizSubscriber.tsx, the backend implementation guarantees non-null values, so frontend null checks would be unnecessary and falsify return types. The subscription schema should be updated to reflect non-nullable return types instead.

Applied to files:

  • packages/graphql/src/services/liveQuizzes.ts
📚 Learning: 2025-04-17T10:22:49.381Z
Learnt from: sjschlapbach
PR: uzh-bf/klicker-uzh#4624
File: packages/graphql/src/services/feedbacks.ts:298-304
Timestamp: 2025-04-17T10:22:49.381Z
Learning: In the klicker-uzh project, relation filters like `where: { id, feedback: { liveQuizId } }` work with Prisma's `findUnique` method despite documentation suggesting otherwise.

Applied to files:

  • packages/graphql/src/services/liveQuizzes.ts
📚 Learning: 2024-08-19T20:18:26.837Z
Learnt from: sjschlapbach
PR: uzh-bf/klicker-uzh#4185
File: packages/graphql/src/services/practiceQuizzes.ts:86-91
Timestamp: 2024-08-19T20:18:26.837Z
Learning: In the `getPracticeQuizData` function, only one feedback entry can exist per participant due to a uniqueness constraint, making it unnecessary to fetch more than one feedback.

Applied to files:

  • packages/graphql/src/services/liveQuizzes.ts
📚 Learning: 2025-06-23T12:32:29.344Z
Learnt from: CR
PR: uzh-bf/klicker-uzh#0
File: apps/frontend-manage/CLAUDE.md:0-0
Timestamp: 2025-06-23T12:32:29.344Z
Learning: For GraphQL integration, always import operation documents from klicker-uzh/graphql and use Apollo Client for data fetching and mutations, handling loading, error, and success states consistently.

Applied to files:

  • apps/frontend-manage/src/components/evaluation/ElementEvaluation.tsx
  • apps/frontend-manage/src/components/evaluation/ActivityEvaluation.tsx
🧬 Code graph analysis (5)
apps/frontend-manage/src/components/evaluation/EvaluationUnavailableNotification.tsx (1)
packages/graphql/src/schema/practiceQuiz.ts (1)
  • PublicationStatus (24-26)
apps/frontend-manage/src/components/evaluation/BlockStatusIndicator.tsx (1)
packages/graphql/src/schema/liveQuiz.ts (1)
  • ElementBlockStatus (11-13)
apps/frontend-manage/src/components/evaluation/ElementEvaluation.tsx (2)
packages/graphql/src/schema/practiceQuiz.ts (1)
  • PublicationStatus (24-26)
packages/graphql/src/schema/liveQuiz.ts (1)
  • ElementBlockStatus (11-13)
packages/graphql/src/schema/evaluation.ts (1)
packages/graphql/src/schema/practiceQuiz.ts (1)
  • PublicationStatus (24-26)
apps/frontend-manage/src/components/evaluation/ActivityEvaluation.tsx (2)
packages/graphql/src/schema/practiceQuiz.ts (1)
  • PublicationStatus (24-26)
packages/graphql/src/schema/evaluation.ts (1)
  • ActivityEvaluation (240-272)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: cypress-run-cloud
  • GitHub Check: test
  • GitHub Check: build
  • GitHub Check: test
  • GitHub Check: Analyze (javascript)
  • GitHub Check: SonarCloud
  • GitHub Check: build
  • GitHub Check: check
  • GitHub Check: claude-review
🔇 Additional comments (14)
packages/graphql/src/schema/evaluation.ts (2)

244-251: GraphQL exposure of status and courseName aligns with service outputs

status: PublicationStatus (nullable) and courseName?: string are correctly exposed; displayName now non-nullable matches the interface above. LGTM.


22-27: Confirm DB + migrations guarantee displayName is non-null for LiveQuiz

Prisma schema and migrations show displayName NOT NULL for Quiz/related activity tables (see packages/prisma/src/prisma/schema/quiz.prisma and migrations such as packages/prisma/src/prisma/schema/migrations/20220925181035_initial_migration/migration.sql and 20240119171242_element_structure_and_unique/migration.sql), but I couldn't find an explicit LiveQuiz model — LiveQuiz is likely represented by Quiz (type='LIVE_QUIZ'). Verify that this GraphQL type maps to that non-null DB column and that all production/legacy rows were migrated/backfilled (no existing nulls remain).

packages/graphql/src/services/liveQuizzes.ts (1)

2278-2286: Expose status and courseName in evaluation payload: LGTM

status and courseName are correctly sourced from liveQuiz and match the GraphQL schema changes.

packages/i18n/messages/de.ts (2)

599-604: LGTM – clearer LMS path info for Live Quizzes.

String reads well and keeps punctuation consistent with surrounding content.


666-668: LGTM – onboarding flow wording is fine.

No grammar issues detected in this change.

packages/i18n/messages/en.ts (3)

597-602: Student docs reflow looks good.

The content changes read well and keep consistent tone.


664-671: Join-course section text tweaks LGTM.

No issues spotted in the link formatting and guidance text.


2413-2424: New evaluation metadata keys — verify parity and usage.

  • Ensure corresponding keys exist in all supported locales (e.g., de.ts) and are wired in UI where referenced (BlockStatusIndicator/EvaluationUnavailableNotification).
  • Confirm copy consistency: “real-time” vs “real time” used elsewhere.
apps/frontend-manage/src/components/evaluation/ElementEvaluation.tsx (1)

168-177: Scheduled-state message placement LGTM.

Rendering the unavailable notice in scheduled state is a good UX touch.

apps/frontend-manage/src/components/evaluation/EvaluationUnavailableNotification.tsx (1)

75-81: Activity status rendering LGTM.

Uses shared status translations consistently.

apps/frontend-manage/src/pages/quizzes/[id]/evaluation.tsx (3)

26-31: Refetch timestamp tracking LGTM.

Simple and effective for UI hints.


33-35: Stronger loading guard LGTM.


61-72: Prop plumb-through of status/course/refetch time LGTM.

apps/frontend-manage/src/components/evaluation/ActivityEvaluation.tsx (1)

131-143: Unavailable-notice enrichment LGTM.

Good use of course/activity metadata for context.

Comment on lines +51 to +64
{activityId && (
<a
href={`https://manage.klicker.com/activities?openActivityDetailsId=${activityId}&openActivityDetailsType=LIVE_QUIZ`}
target="_blank"
rel="noreferrer"
>
<FontAwesomeIcon
size="sm"
icon={faExternalLinkAlt}
className="mr-2"
/>
{t('manage.evaluation.linkActivityDetails')}
</a>
)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid hardcoded host; make link environment‑agnostic and secure.

Use a relative path (or env base URL) and include noopener.

-            <a
-              href={`https://manage.klicker.com/activities?openActivityDetailsId=${activityId}&openActivityDetailsType=LIVE_QUIZ`}
-              target="_blank"
-              rel="noreferrer"
-            >
+            <a
+              href={`/activities?openActivityDetailsId=${activityId}&openActivityDetailsType=LIVE_QUIZ`}
+              target="_blank"
+              rel="noopener noreferrer"
+            >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{activityId && (
<a
href={`https://manage.klicker.com/activities?openActivityDetailsId=${activityId}&openActivityDetailsType=LIVE_QUIZ`}
target="_blank"
rel="noreferrer"
>
<FontAwesomeIcon
size="sm"
icon={faExternalLinkAlt}
className="mr-2"
/>
{t('manage.evaluation.linkActivityDetails')}
</a>
)}
{activityId && (
<a
href={`/activities?openActivityDetailsId=${activityId}&openActivityDetailsType=LIVE_QUIZ`}
target="_blank"
rel="noopener noreferrer"
>
<FontAwesomeIcon
size="sm"
icon={faExternalLinkAlt}
className="mr-2"
/>
{t('manage.evaluation.linkActivityDetails')}
</a>
)}
🤖 Prompt for AI Agents
In
apps/frontend-manage/src/components/evaluation/EvaluationUnavailableNotification.tsx
around lines 51 to 64, the anchor currently uses a hardcoded
https://manage.klicker.com host and only rel="noreferrer"; change it to build
the URL from a relative path or a configured base (e.g.
process.env.MANAGE_BASE_URL or window.location.origin + '/activities') instead
of the fixed host, keep the query string parameter, preserve target="_blank",
and add rel="noopener noreferrer" to avoid reverse tabnabbing and make the link
environment‑agnostic and secure.

data:
# SSR-only GraphQL endpoint for Assessment PWA (internal service DNS)
API_URL_SSR: "http://{{ include "chart.fullname" . }}-backend-assessment:3000/api/graphql"
NEXT_PUBLIC_IS_ASSESSMENT: "true"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Public flag looks good; confirm parsing and env split across frontends

Setting NEXT_PUBLIC_IS_ASSESSMENT to "true" is appropriate for Next.js public envs (strings). Please verify:

  • All consumers parse it as a boolean (e.g., process.env.NEXT_PUBLIC_IS_ASSESSMENT === 'true').
  • Other frontend ConfigMaps explicitly set it to "false" or omit it to avoid accidental leakage of assessment-only UI.

🏁 Script executed:

#!/bin/bash
# Ensure all consumers compare to the string 'true' and not rely on truthy values.
rg -nP --type=ts --type=tsx "NEXT_PUBLIC_IS_ASSESSMENT" -C2

Length of output: 27


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) All occurrences of NEXT_PUBLIC_IS_ASSESSMENT"
rg -n --hidden --no-ignore -S "NEXT_PUBLIC_IS_ASSESSMENT" -C3 || true

echo
echo "2) process.env usage"
rg -n --hidden --no-ignore -S -F "process.env.NEXT_PUBLIC_IS_ASSESSMENT" -C3 || true
rg -n --hidden --no-ignore -S -F "process.env['NEXT_PUBLIC_IS_ASSESSMENT']" -C3 || true
rg -n --hidden --no-ignore -S -F 'process.env["NEXT_PUBLIC_IS_ASSESSMENT"]' -C3 || true

echo
echo "3) Programmatic comparisons to 'true' / true (JS/TS)"
rg -n --hidden --no-ignore -S -P "NEXT_PUBLIC_IS_ASSESSMENT\s*(?:===|==)\s*'true'" -C3 || true
rg -n --hidden --no-ignore -S -P 'NEXT_PUBLIC_IS_ASSESSMENT\s*(?:===|==)\s*"true"' -C3 || true
rg -n --hidden --no-ignore -S -P "NEXT_PUBLIC_IS_ASSESSMENT\s*(?:===|==)\s*true\b" -C3 || true

echo
echo "4) YAML/Helm/ConfigMap assignments (look for : true/false or =)"
rg -n --hidden --no-ignore -S -P "NEXT_PUBLIC_IS_ASSESSMENT\s*[:=]\s*\"?(true|false)\"?" -C3 || true

echo
echo "5) .env / env.* files"
rg -n --hidden --no-ignore -S -g "**/.env*" -P "NEXT_PUBLIC_IS_ASSESSMENT\s*=\s*\"?(true|false)\"?" -C3 || true

echo
echo "Done."

Length of output: 42493


Fix truthy env checks; ensure non‑assessment frontends unset/false

  • Replace truthy checks with explicit string comparison (=== 'true') — e.g. apps/frontend-pwa/src/pages/index.tsx:351 uses process.env.NEXT_PUBLIC_IS_ASSESSMENT && → change to process.env.NEXT_PUBLIC_IS_ASSESSMENT === 'true' &&.
  • Ensure non‑assessment frontends explicitly set NEXT_PUBLIC_IS_ASSESSMENT='false' or omit it. Assessment-only settings found at deploy/charts/klicker-uzh-v2/templates/cm-frontend-assessment.yaml:11, apps/frontend-pwa/.env.assessment:31, apps/frontend-pwa/.env.assessment.qa:29; no 'false' assignments were found.
🤖 Prompt for AI Agents
In deploy/charts/klicker-uzh-v2/templates/cm-frontend-assessment.yaml around
line 11, the NEXT_PUBLIC_IS_ASSESSMENT variable is set truthily; change
deployment/environment values so assessment charts explicitly set
NEXT_PUBLIC_IS_ASSESSMENT: "true" and non‑assessment charts or .env files
explicitly set NEXT_PUBLIC_IS_ASSESSMENT: "false" (or omit it) and audit the
other mentioned files (apps/frontend-pwa/.env.assessment, .env.assessment.qa) to
ensure consistency; then update all runtime checks in the frontend code to use
explicit string comparison (process.env.NEXT_PUBLIC_IS_ASSESSMENT === 'true')
instead of truthy checks.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex Review: Here are some suggestions.

const blockEvaluations = computeStackEvaluation(
typeof activeBlockWithResults !== 'undefined'
? [...liveQuiz.blocks, { ...activeBlockWithResults, active: true }]
: liveQuiz.blocks

[P1] Avoid duplicating active block in evaluation results

Since the Prisma query now includes all blocks (no status filter), the subsequent spread [..., liveQuiz.blocks, { ...activeBlockWithResults, active: true }] adds the currently active block a second time. The first entry still carries stale database results while the appended one is live, so the evaluation UI will display the active block twice and may show inconsistent numbers. Consider excluding the active block from liveQuiz.blocks before appending the real-time copy or dropping the extra append when the list already contains the active block.


Reply with @codex fix comments to fix any unresolved comments.

About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you open a pull request for review, mark a draft as ready, or comment "@codex review". If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex fix this CI failure" or "@codex address that feedback".

@cypress
Copy link
Copy Markdown

cypress bot commented Sep 17, 2025

klicker-uzh    Run #6385

Run Properties:  status check failed Failed #6385  •  git commit a886a64b40 ℹ️: Merge 4d35043fc60747629dbbf4fc2d2e4cd8ddaa76fe into 482c00c2b40af147d096ae3ecb56...
Project klicker-uzh
Branch Review activity-info-on-eval
Run status status check failed Failed #6385
Run duration 12m 50s
Commit git commit a886a64b40 ℹ️: Merge 4d35043fc60747629dbbf4fc2d2e4cd8ddaa76fe into 482c00c2b40af147d096ae3ecb56...
Committer Roland Schläfli
View all properties for this run ↗︎

Test results
Tests that failed  Failures 4
Tests that were flaky  Flaky 1
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 754
⚠️ You've recorded test results over your free plan limit.
Upgrade your plan to view test results.
View all changes introduced in this branch ↗︎

Tests for review

Copy link
Copy Markdown
Member

@sjschlapbach sjschlapbach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The evaluation page seems to be broken from a UI point of view and might not even refresh correctly anymore (?) - the reason for the latter is unknown... (the timestamps on the new status point also don't update, etc.)

For some reason, the evaluation does not seem to load the results anymore? Even after having successfully completed hatchet tasks and correct data in the redis cache, the evaluation (both with hmac and without) does not show any results?

When opening an evaluation, the actual element evaluation is no longer hidden, but can be found partially overlapping with the footer when scrolling down:
Screenshot 2025-09-18 at 12 41 52
Screenshot 2025-09-18 at 12 41 13

message={t('manage.evaluation.evaluationNotYetAvailable')}
/>

<div className="mt-4 flex flex-row gap-8 text-xs text-gray-500">
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we maybe stick to at least small text here? text-xs seems very very small and is barely readable

Suggested change
<div className="mt-4 flex flex-row gap-8 text-xs text-gray-500">
<div className="mt-4 flex flex-row gap-8 text-sm text-gray-500">

<span className="font-semibold">
{t('manage.evaluation.elementType')}
</span>
: {elementType}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should definitely use the properly translated strings here instead of the GraphQL enums

Suggested change
: {elementType}
: {t(`shared.${elementType}.typeLabel`)}


function LiveQuizEvaluation() {
const router = useRouter()
const [lastRefetchTime, setLastRefetchTime] = useState<Date>(new Date())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it valid to initialize this time with the current timestamp? - probably sufficient as an approximation though

},
evaluation: {
courseName: 'Course',
activityName: 'Activity',
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of these translations are already defined under shared.generic.course, shared.generic.activity, shared.generic.element, manage.general.elementType, etc. - we should probably try to re-use these in case we decide to rename them again later?

Copy link
Copy Markdown
Member

@sjschlapbach sjschlapbach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional bug that was observed after closing the block is that the evaluation (observed / checked for the regular one) now seems to show each completed block twice - once with results and once without:

Screenshot 2025-09-18 at 13 00 45 Screenshot 2025-09-18 at 13 00 52

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
6.4% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@rschlaefli rschlaefli self-assigned this Sep 20, 2025
@rschlaefli rschlaefli marked this pull request as draft October 15, 2025 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement size:L This PR changes 100-499 lines, ignoring generated files.

Development

Successfully merging this pull request may close these issues.

2 participants