Conversation
Spec for the Container/Block/Tabs primitive set that replaces the duplicated title/subtitle/time/divider/tab+toolbar SCSS across the student contest dashboard and the admin contest dashboard. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Step-by-step plan covering 6 phases: typography, structure, tabs/toolbar, and migrations of student dashboard / admin overview / contest hero. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PageTitle / SectionTitle / MetricBlock / TimeDisplay built on Carbon typography tokens. PageTitle uses heading-04 size with weight 400; others align with the more compact admin scale. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DashboardPage handles scroll + max-width clamp; DashboardContainer exposes stack/split/grid layouts and auto-injects dividers between siblings; DashboardBlock provides padding variants without owning borders; BlockHeader composes title (page or section size) plus description and actions slots. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DashboardTabs context provider coordinates DashboardTabBar (Carbon Tabs wrapper with toolbar slot and mobile stacking) and DashboardTabPanel (renders only when its tabId matches the active context). DashboardToolbar provides Search and Filter sub-components preset for the borderless in-tab-bar appearance. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the bespoke .root/.dashboard/.layout/.titleRow/.detailRow/.timerValue SCSS with DashboardPage, DashboardContainer (split + grid + stack), DashboardBlock, BlockHeader and TimeDisplay/MetricBlock primitives. Inner Carbon Tabs are kept; only the outer page layout, hero title and metric cells are migrated. Also switch BlockHeader description from <p> to <div> so it can host arbitrary nodes (e.g. tag rows) without nesting block elements in <p>. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AdminOverviewScreen renderContestHeader now uses DashboardBlock + BlockHeader; the bespoke .contestHeader / .dashboardTitleBlock / .dashboardTitleRow / .dashboardDescription SCSS is gone. AdminOverviewCommandCenter exam status summary now uses two DashboardContainer grids (people-count and schedule) with MetricBlock size=lg, plus a stacked DashboardBlock with TimeDisplay countdown for the timeline progress. The hardcoded 1.5rem font sizes and the .examStatusMatrix / .examStatusMetric / .examScheduleGrid / .examScheduleItem / .examProgressTitle / .examProgressValue / .examStatusPanel / .panelHeader rules are removed. DashboardPage gains an opt-in fullBleed prop so admin pages can render without the 1180px clamp. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ContestHero metadata block now composes two MetricBlock instances; the ad-hoc .timeLabel / .timeValue SCSS rules are removed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DashboardContainer split layout now accepts proportions="equal" | "main-aside" | "aside-main". Student dashboard switches to main-aside so the right summary rail clamps to 18-22rem instead of sharing 50/50 with the main content. Mobile breakpoint still collapses to a single column. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…port Announcement items now compose BlockHeader instead of bespoke .announcementHeader / .announcementTitle SCSS, so the title typography matches the rest of the dashboard. DashboardPage .root and .inner switch to flex-column with the inner direct child stretched to fill remaining height. With fullBleed, the bordered split container therefore reaches the bottom of the viewport when content is short, instead of leaving a dark band underneath. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…d primitives Six admin panels (AdminPreparationDashboard, OverviewActionWidgets, OverviewInsightsPanel, OverviewEventSummaryPanel, StudentStatusBreakdown, DraftChecklistPanel) now use BlockHeader / SectionTitle from @/shared/components/dashboard for their section titles and title-with-description-and-actions headers. Their bespoke .title / .subtitle / .panelHeader / .widgetTitle SCSS rules are removed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 答題畫面新增唯讀通知 banner + modal,互動回 dashboard - 已讀狀態用 localStorage(不引入新後端表) - 教師考試前後皆可發公告;既有 readOnly 限制需解開 - 拆掉 ContestClarifications.tsx 巨檔 - ClarificationViewSet 加 status / problem filter Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… primitives ContestLayout header timer now uses TimeDisplay variant=header (the typography rules are removed from the wrapper SCSS). AdminInsightRail card headers now use MetricBlock size=lg, eliminating the hardcoded 1.5rem font-size in .cardHeader strong and the bespoke label/value typography. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…cing
Remove the participant-list description ("依異常程度排序,快速掃描考生狀態。")
since the title is self-explanatory.
Drop the .card min-height: 8rem in AdminInsightRail; the rail cards now
size to their content + padding so the participant-distribution card
doesn't leave empty space below the meter chart.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…bar bell - 移除 Clarification,改為 ContestConversation + ContestMessage 雙向 thread - ContestAnnouncement 維持純廣播(取消 target_user 計畫) - 通知入口改為 ContestLayout navbar 鈴鐺(不再用 banner) - 教師可從 AdminProctoringPanel 對特定學生主動開啟對話 - 學生可在 thread 中回覆教師 - 已讀仍用 localStorage(key 含 ann: / msg: 前綴) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
兩個欄位都可由訊息推算出來,不必額外存: - 「進行中/已回覆」看 last_message.sender_role - 「誰發起的」看第一則 message 的 sender_role - list endpoint 多回傳 first_sender_role 供前端使用 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…shboard - ContestConversation 砍掉 problem 欄位 - (contest, student) 加 unique 約束:每個學生在一個 contest 內一條對話 - 學生端 dashboard 直接展開 thread(不需要列表) - 教師端 filter 簡化為「全部 / 未回 / 已回」 - Proctoring「傳送訊息」走 ensure-and-append(idempotent) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… rail dividers through DashboardContainer Add a shared CountdownProgress component that owns its own per-second ticker and renders TimeDisplay + ProgressBar from the dashboard primitives. Both the student dashboard's primary status block and the admin overview's exam status panel now use it, so the admin's countdown ticks live like the student side. Replace the bespoke .rightOverviewColumn div and AdminInsightRail.root div with DashboardContainer layout=stack dividers="auto", and drop the .card border-bottom inside the rail. Dividers between every direct child in the right rail (examStatusSummary, distribution, grading, events) are now produced uniformly by the DashboardContainer pattern. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
22 個任務分 6 階段:後端 model+API+測試 → 前端 hooks/utils → 元件 → 通知鈴鐺 → 螢幕接線 → 清理。每步含完整代碼與命令。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ard primitives The participants/answer-distribution tab strip and its inline search + filter toolbar now use DashboardTabs / DashboardTabBar / DashboardTabPanel / DashboardToolbar instead of bespoke .tabRow / .tabRowToolbar / .tabRowSearch / .tabRowFilterMenu. Mobile stacking, borderless search input, and toolbar alignment are all owned by the shared primitives. DashboardToolbar.Search now accepts id, size, and onClear so existing callers that need clearable search can opt in without dropping back to raw Carbon Search. DashboardTabPanel switches from conditional render to always-render with hidden=true on inactive panels, matching Carbon Tabs semantics so screen readers and tests can locate every tabpanel by role. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the contest is in the during phase, CountdownProgress overlays a 2s linear-gradient shimmer on the progress bar so the bar feels alive between per-second ticks. Before/after phases stay static. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
scripts/check-typography-tokens.js scans the dashboard primitive library (src/shared/components/dashboard) for hardcoded font-size values and exits non-zero if any escape onto a primitive. Carbon CSS tokens (var(--cds-*)), keywords (inherit/normal/etc.) and SCSS variables remain allowed. font-weight numbers stay free since not all weights have semantic Carbon tokens. Run via `npm run check:typography`. Scope intentionally stops at the dashboard library so feature-level legacy SCSS isn't disturbed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add *.sql.gz, *.dump, *.backup, and backups/ to .gitignore so local pg_dump artifacts stay out of the working tree.
…ults published Previously dashboard-summary was teacher/admin only. Now participants of a contest can read it once `results_published` is true; staff still has unrestricted access. Adds two regression tests for the new branches.
- Split classroom contest routes: dashboard renders inside MainLayout via new ContestWorkspaceLayout; the full ContestLayout shell is now reserved for the answering runtime (solve / paper). - ExamNavigator: collapsible side rail with mini view, optional overview entry, refined typography, marker tone, and toolbar slot when the surrounding layout exposes its own actions. - PaperExamAnsweringScreen: persist marked questions in localStorage and forward the submit button + save status into the layout header slot via ContestLayoutHeaderSlotContext when available. - AnswerDisplay: gain an explicit selected-answer indicator and a showCorrectness toggle so the same component covers both the live answering view and the post-grading review. - Extract PaperQuestionReportCard from ParticipantDashboardPane so the per-question grading card can be reused across report views.
Pre-push ESLint flagged the inline `render: () => { ... useState ... }` for
violating react-hooks/rules-of-hooks because the arrow function is not
recognized as a React component. Moves the body into a top-level
`TabsWithToolbarStory` so hooks fire from a proper component.
Student dashboard inner tabs (規則說明 / 作答紀錄) now use BlockHeader instead of bespoke .sectionHeader / .inlineRecordsHeader / .inlineRecordsTitle / .sectionDescription markup. The unused .metricLabel / .metricValue / .chartHeader rules from before the KPIBlock migration are deleted. AdminOverviewScreen drops dead .publishSummary / .publishSummaryIntro / .publishSummaryGrid / .publishSummaryItem / .publishSummaryLabel / .publishSummaryValue rules — none of them are referenced anywhere. AdminOverviewCommandCenter .participantSingleMetric strong replaces its hardcoded font-size: 1.25rem with the heading-03 Carbon token, so the participant card emphasis aligns with MetricBlock size=lg. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11 個任務分 5 階段:偵測 hook + RuntimeRouteWrapper → top nav/UserMenu → SideMenu idle/runtime → 路由合併 → 清理。每步含完整代碼與命令。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mitive
The student dashboard's 規則說明 / 作答紀錄 tab strip was still on raw
Carbon Tabs/TabList/TabPanels. Migrate to DashboardTabs +
DashboardTabBar + DashboardTabPanel so the visual treatment and
mobile stacking match the rest of the dashboard primitives.
Drop the .markdownBlock / .feedbackBlock { padding-left: 0.75rem }
indent on PaperQuestionReportCard. The prompt and the question header
now share the same horizontal axis instead of the prompt being
mysteriously inset.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DashboardTabBar now accepts padding="default" | "flush". The default adds 1rem inline padding so the tab strip stops hugging the block edge — matching the inline padding of DashboardBlock padding="default" and giving the tabs visual breathing room. flush remains available for callers that need the bar to sit exactly at the container edge. The toolbar drops its own 0 1rem margin in favour of the tab bar's padding, keeping the spacing model consistent in both desktop and the mobile column layout. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The embedded ContestLogsScreen tab strip (違規事件 / 考試事件 + refresh button) was the last raw Carbon Tabs site in the admin overview. It now uses DashboardTabs / DashboardTabBar / DashboardTabPanel with the refresh button passed through the toolbar slot. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The student dashboard's top-level DashboardContainer no longer sets bordered. Now that the page is rendered fullBleed inside the contest workspace shell, the outer frame just doubled the chrome and its bottom edge could be clipped by the scroll container. Internal dividers between the main column and summary rail are unaffected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ogress The right-rail "考生分佈總覽" KPI now reads "學生作答進度" with the completion percent as its highlighted value (e.g. "36%") instead of the raw headcount. The breakdown formatter under the meter is trimmed: it just states "已交卷 X / Y 人" without re-stating the completion rate that the value already shows. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add openMonitor callback to ContestRuntimeContextValue and wire it to setMonitoringOpen in RuntimeRouteWrapper so sibling components like ExamStatusBadge can trigger the monitoring modal via context. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…hooks on every page Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…testProvider dependency SideMenuContestRuntimeSection no longer calls useContest() (which throws outside ContestProvider). SideMenu now fetches contest data for all contest URLs (not just admin) via contestIdToFetch, and passes problems down as a prop. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The student dashboard 公告 block is gated behind SHOW_ANNOUNCEMENTS, currently false. Data fetching, state, and renderer are kept intact; flipping the constant to true brings the block back without revisiting the call site. The corresponding integration test is marked as skipped with a note. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…classes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…o preserve precheck handoff Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…obal navbar) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The 管理入口 entry tile grid in AdminPreparationDashboard was a hand-
rolled copy of the Container's bordered grid pattern: a 2-column grid
with 1px outer border, per-cell border-right + border-bottom, and
nth-child / nth-last-child resets plus a mobile single-column
fallback. Replace the wrapper with
<DashboardContainer layout="grid" columns={2} bordered dividers="auto">
and drop the duplicated SCSS — divider behaviour now comes from a
single source.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Release 25 commits from
devtomain. The bulk is a multi-step migration of the contest UI to the new dashboard primitive library, plus a focused paper-exam answering UX refresh and one small backend permission tweak.Highlights
Dashboard primitives (new shared layer)
feat(dashboard): typography (PageTitle,SectionTitle,MetricBlock,TimeDisplay), structure (DashboardPage/Container/Block/Header), tabs + toolbar, and split proportions for main+aside layouts.frontend/src/shared/components/dashboard/.Contest UI migration to primitives
CountdownProgresswith shimmer; right-rail dividers now flow throughDashboardContainer.BlockHeader, duplicate answer progress rail removed, participant list header / insight card spacing tidied.Paper exam answering UX (this batch)
MainLayout/newContestWorkspaceLayout; fullContestLayoutshell now reserved for solving/answering.ExamNavigator: collapsible side rail with mini view, optional overview entry, tightened typography + marker tone.PaperExamAnsweringScreen: marked questions now persist inlocalStorage; submit button + save status forward into the layout header slot via the newContestLayoutHeaderSlotContext.AnswerDisplay: explicit selected indicator +showCorrectnesstoggle so the same component handles live answering and post-grading review.PaperQuestionReportCardextracted fromParticipantDashboardPanefor reuse across report views.Backend
feat(contest):dashboard-summaryendpoint now lets contest participants read it onceresults_published=true; staff access unchanged. Two regression tests added.Docs / chore
.gitignore: extra db dump/backup formats.fix(dashboard): refactor tabs story to satisfyreact-hooks/rules-of-hooks(pre-push hook surfaced this on first push).Testing
tsc --noEmitclean (0 errors)tabs.stories.tsxfixtest_participant_can_view_dashboard_summary_after_results_publishedtest_participant_cannot_view_dashboard_summary_before_results_publishedtest_dashboard_summary_cache_invalidates_on_submission(regression)lint-architecture.jscleanlint-naming.jsreports only pre-existing categories (not introduced by this batch); no new violations added — these lints are not wired into CI/husky.Deployment notes
MainLayout. Verify navbar/sidebar rendering on/classrooms/:cid/contest/:cid.qjudge.exam.marked.<contestId>— no migration needed.Rollout / rollback
results_published).