Skip to content

Theme/staff admin workflows - #531

Closed
gauravmyana2002 wants to merge 2052 commits into
thoth-tech:developmentfrom
ontrack-features-t2-2026:theme/staff-admin-workflows
Closed

gauravmyana2002 wants to merge 2052 commits into
thoth-tech:developmentfrom
ontrack-features-t2-2026:theme/staff-admin-workflows

Conversation

@gauravmyana2002

Copy link
Copy Markdown

Description

Draft PR — this is a first, verified slice of THM-M03, not the complete ticket. Opening now given the due date, with the remaining scope filed as follow-up tickets rather than holding this back.

Migrates every hardcoded colour in the task inbox / marking queue — inbox.component, staff-task-list.component, moderation.component, inbox-dashboard.component — onto the --ot-color-* token layer established by THM-M01. This is THM-M03 checklist item 2 ("marking queues, task assessment panels") and the surface the ticket's own purpose statement calls out as highest-stakes: "poor contrast could cause incorrect decisions."

Every substitution follows THM-M01's own pattern exactly (var(--ot-color-X, <original-light-hex>)), so light mode is pixel-identical — only dark mode changes. One exception needed a real override rather than a token swap: staff-task-list's --background-gray (a hover/selected-row tint) is rgba(0,0,0,0.04), which reads fine in light but would be nearly invisible or wrong-direction on a dark surface, so it gets a :host-context([data-ot-theme='dark']) override to an rgba(255,255,255,0.06) tint instead. Also migrates four Tailwind arbitrary-hex classes and one inline style in inbox-dashboard's empty states onto the same tokens, via Tailwind's var() arbitrary-value syntax.

No dependencies required for this change.

Branch and commit combination:

  • doubtfire-web: theme/staff-admin-workflows @ 29fc4b683, branched from origin/11.0.x @ 518abe8f1
  • doubtfire-api: feature/peer-progress-indicator @ 5af61f965
  • doubtfire-deploy: fix/local-dev-run @ 89a999abf

Not covered by this PR (filed as follow-up scope, not silently dropped): route confirmation beyond this surface (THM-D01 doesn't exist yet, self-audited from app.routes.ts instead), student lists, task lists, group views, extension/date controls, unit-chair decision surfaces, unit/task/rubric/learning-outcome/feedback-template editors, staff reports/analytics/filters/bulk actions, dense-table/nav/narrow-layout checks, and the rest of the marking queue itself (task-claim, confirm-moderation-modal, batch-feedback-workflow-dialog).

Fixes # (ticket THM-M03, tracked on the team Planner board — no linked GitHub issue)

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

  • npx ngc -p src/tsconfig.app.json --noEmit — clean.
  • npx eslint <touched files> --max-warnings 0 — clean.
  • npx ng test --no-watch --include=<4 touched spec files> — 25/25 pass.
  • npx ng build — succeeds.

Added a new spec for InboxDashboardComponent (previously untested) asserting the empty-state icon/message carry the token class rather than a bare hex. Verified it fails against the pre-migration markup (expected '...text-[#c5c5c5]' to contain 'var(--ot-color-text-muted,#c5c5c5)') and passes after the fix.

Not covered by automated tests: actual rendered appearance in Light/Dark. jsdom doesn't evaluate CSS custom-property resolution across global stylesheets, so the tests confirm the correct token is wired in, not what it looks like. Docker's local database was reset today by an unrelated crash, so Light/Dark screenshots haven't been captured yet — flagging this honestly rather than skipping it silently.

Testing Checklist:

  • Tested in latest Chrome
  • Tested in latest Safari
  • Tested in latest Firefox

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have requested a review from @macite and @jakerenzella on the Pull Request

Clupai8o0 and others added 30 commits August 27, 2026 11:46
@angular/build, @angular/cli and ngx-monaco-editor-v2-alternative were
pinned to exact versions. In a change whose purpose is remediating audit
findings that is the wrong direction: it stops the next patch release
arriving and stops a later npm audit fix from taking it, and @angular/build
is one of the packages carrying an advisory on the base branch.

Restored as ^22.0.9, ^22.0.9 and ^22.0.0. The resolved versions in the
lockfile are unchanged and still satisfy the ranges, so nothing is upgraded
here. The root declaration in package-lock.json is updated to match, which
is what npm ci compares against.

The exact dompurify pin inside overrides is deliberate and is left alone.

npm audit on origin/11.0.x: 12 total, 4 high, 5 moderate, 3 low.
npm audit with this branch: 0.
…y-integration-prs-20260826

ci: add weekly integration PR automation
…on-submission-only-20260826

fix: only reset dashboard after completed submission
project-dashboard.component.spec.ts came out as add/add. 11.0.x gained one
via #96 covering task selection, this branch has one covering route reuse.
Kept both, the describe names were already distinct.

The merge also broke the build in a way neither side could see on its own.
#96 added taskService as the third constructor parameter of
ProjectDashboardComponent, and this branch's spec constructs the component
directly rather than through TestBed, so all three call sites were passing
six arguments to a seven argument constructor:

  TS2554: Expected 7 arguments, but got 6.
  An argument for 'breakpointObserver' was not provided.

Added a TaskService stub in third position at each of them. It only needs
taskSubmissionCompleted$, since that is the one member ngOnInit subscribes
to, so a bare Subject is enough and the tests stay about routing.

ng test --include **/project-dashboard.component.spec.ts: 6 passed.
…260824

fix(deps): resolve inherited npm audit findings
….x-all-features-20260824

release(web): OnTrack 11.0.x all-features handover
…-mvp-20260827

# Conflicts:
#	src/app/projects/states/dashboard/project-dashboard/project-dashboard.component.spec.ts
The target grade selector lost its static disabled attribute, so it was live for
anyone viewing the dashboard, staff included, and the API permits the change
because tutors hold :change on a project. The component already computes
viewingOtherStudentProject, so the guard uses that. The Task Planner carried the
same unguarded selector and now matches.

Deselecting had moved into applyFilters, which is bound to the search box, so
every keystroke closed the open task and rewrote the URL, losing an unsent
comment with it. It now only deselects when the open task has gone or a view
filter hides it.

Hiding tasks above the target grade is counted by the filter badge, and a task
with unread comments stays visible the way the hide-completed filter already
allows, so tutor feedback cannot disappear behind the new default.
Swapping first() for takeUntil made the subscription permanent, which is what
unit switching needs. It also meant every emission re-ran activateProject, which
resets projectReady and clears the selected task.

The portfolios progress tab re-emits on every change detection pass, because its
taskSelectionUrlBase getter returned a new array each call and ngOnChanges pushed
into the project subject unconditionally. The tab reset itself continuously.

activateProject now returns early when the project is already the active one, the
url base is memoised, and ngOnChanges only pushes when the project input actually
changed.
contrastingTextColor left the always-rendered Awaiting Feedback card at 4.44:1,
under the 4.5:1 AA wants for normal text. The dark foreground moves to #000000
and the dark-contrast divisor now matches it.

The hidden-series legend pill fell to 2.97:1 while still being an interactive
control, which misses the 3:1 in 1.4.11. It uses an explicit dimmed state rather
than an opacity.

Hiding a series zeroed its values instead of removing it, so the pill read as off
while the chart still drew the entry. Hidden series are filtered out of the data.

The clipping guard asserted a removed class string, so it could not fail for the
right reason. It reads computed maxHeight and overflow instead.
…layout

refactor(layout): standardise the grid & container layout (doubtfire-lms#1208)
One conflict, in project-plan.component.html. 11.0.x rewrapped the page in
f-page-container while this branch added aria-label and the disabled binding to
the target grade selector. Both are kept: the resolution takes the 11.0.x layout
and grafts the two mat-select attributes onto it.

project-plan.component.spec.ts finds the selector by its aria-label under
NO_ERRORS_SCHEMA, so it does not depend on the wrapper either way. It still
passes, as does project-card.component.spec.ts, prettier and ng lint.
…rack-mvp-20260827

Validate and hand over the OnTrack Web MVP streams
Adds docs/theme/THEME-CONTRACT.md, the contract THM-F01 through
THM-M05 build against. It fixes the three stored preference states
and the two resolved ones, the data-ot-theme marker on <html>, the
ontrack.theme.preference key and its allowlist, the semantic --ot-*
token set with a light and a dark value for every role, the split
between fixed brand colour and theme-aware colour, the WCAG AA
floor, and the rules for no-flash startup, live operating-system
changes, reduced motion and print.

The feature is specified in two phases so that every card on the
board has something to build. Phase one stores the preference in
localStorage. Phase two, section 6.2, adds the user account as a
second store and settles how the two reconcile: local is always the
boot store, the account carries the stored preference and never the
resolved theme, conflicts go to whichever side was written last, a
device with no local value adopts the account value, and the sync
never blocks startup. Section 10 configures the Tailwind dark
variant against the same root marker rather than banning the
prefix, because an unconfigured darkMode is not an absent dark
variant, it is one that silently follows the operating system and
ignores the stored preference.

Section 2 audits the current stylesheet, because THM-D01 has not
started and there was no register to work from. Every count has the
command printed beside it and Appendix B collects them for
re-running. Every contrast ratio is computed with the formula in
Appendix A rather than estimated, and all seventy-five were
recomputed in one pass before this was handed over.

The audit found that eight of thirteen distinct status chip colour
pairs already fail AA in light mode, and that task status colour has
two sources of truth that agree by hand. Section 8.1 lifts every
failing pair to 4.5:1 while leaving thirteen of fifteen fills alone.

No code, no styles and no configuration change. The document stays a
draft until the four approvals in section 17 are recorded.
THM-MVP01 validates the finished Theme Support MVP objective. The
objective has not started, so this file is the instrument the validation
will be run from and not a record of one. The title, the first line and
the head of every results section say so, because a document made of
empty tables is easy to mistake for a document somebody forgot to fill.

Every result cell reads NOT RUN and every empty list carries a
placeholder row, so the blank state is visibly deliberate.

The dependency register is dated. On 27 August 2026 all seventeen THM
tickets and the two migration tickets are Not started, and the file
carries the commands that show it: no branch matching theme, thm or dark
on any of the four org repositories, 0 of 160 heads, and no pull request
with a theme head or title in any state, 0 of 210. Nothing in the
register is overdue, the earliest due date is 9 September. All eighteen
register rows were machine compared against the board a second time
after the rebase and none of them moved.

Everything that would cite the DOM marker, the storage key or the theme
enum is marked as depending on THM-D02, which is Not started. Its
contract document is absent from every one of the 81 branches on origin.
A draft is committed on a local branch that was never pushed, so nobody
else can read it and this plan does not quote it. Those three things are
named by role and never by value, so nobody substitutes a guess.

The check that proves the contract is missing now walks every head
rather than 11.0.x alone, because the one branch version did not support
the sentence it was cited for.

The scheduling clash stays. THM-MVP01 is due 20 September and THM-JL01,
THM-D01, THM-F02 and THM-Q01 are due the same day. Dates and statuses
only, no fault attached to anyone.

No application code changes. Built against doubtfire-web 11.0.x at
4034e7d. The four npm scripts the plan
names, lint, typecheck, test:ci and build, all exist in package.json on
that commit.
Rebases onto the closure merge of PR #105 and fixes four things review
found, plus three more the re-audit turned up.

The audit was taken on efda579 at 09:24. The closure branch landed at
22:00 the same day and moved nine of the counts, so every Appendix B
command was re-run and every number re-derived on the new base. The
loose hex literals under src/app went 105 to 226, which matters because
section 10 quotes that figure as the migration baseline for another
card. Two files new to this base carry 94 of the 226 between them. What
did not move is recorded too: the three palette files, all 15 status
colours and every contrast ratio in sections 7, 8 and appendix A were
re-checked and are unchanged, so the token tables stand as written.

Section 15 claimed the theme is not sent to Google Analytics and cited
src/index.html:6-14. There is no Google Analytics in this app. No GA, no
tag manager, no analytics package in package.json, and those lines are
the Google Fonts stylesheet links. Replaced with the greps that show the
absence and with Sentry, which is real and is the reporter the bullet
should have been about.

Section 12 cited src/index.html:34 for the theme-color meta. It is at
line 24; line 34 is a respond.js shim behind an IE conditional comment.

Section 6.2 rule 3 left the case that decides the whole feature
undefined: a valid local preference, no updatedAt key, and an account
field that has never been written. That is not an edge case, it is day
one of THM-B01 for every existing user, because phase one never writes
the timestamp key. Read naively the rule discarded the local value and
reset everyone to system. Now settled as a four-row table where presence
beats recency and timestamps are only consulted when both sides hold a
value, with the two intended consequences spelled out and a test for
each row added to section 14.

The Angular Material line citation for rejecting option B could not be
checked because node_modules is not installed here, so the line numbers
are dropped rather than left unverifiable. The argument does not depend
on them. Section 2.3's palette reference is marked the same way, with
the four flattened values shown to be reproducible without it.

Three more from the re-audit. The burndown chart palette moved and its
fifth slot changed from transparent to a real colour. The localStorage
key convention moved and now has a second call site. And the focus
counts read level at 11 against 11 where they were 11 against 7, which
looks like an improvement and is not: only one of the ten files that
suppress an outline declares a focus-visible rule, so nine still have no
replacement ring.

Appendix B now carries the commands for the section 2.4 palette split
and remainder, the focus-set overlap, the custom-property count, the
template file count and the analytics check, so every number in the
document has a command beside it. Each one was run and matches.
 Improved clarity in the user guide.

(cherry picked from commit 67e732c)
Seven templates gated an action or an explanation on (mouseover) and
(mouseout) alone, so a keyboard user could never reach what the pointer
revealed. mouse-events-have-key-events moves from off to error, which
catches the bare case where a hover handler has no keyboard counterpart
at all.

That rule is a floor and not a guarantee, so it is worth being precise
about what it does and does not stop. Its selector is
Element:has(BoundEvent[mouseover]):not(:has(BoundEvent[focus])), and
:has matches descendants, so an element can keep (mouseover) and
(mouseout) with no keyboard equivalent of its own and still lint clean
off a nested control's (focus). It also never checks that whatever
takes focus is reachable, so a div carrying (focus) and no tabindex
passes. The rule stops the pattern being reintroduced bare. The tests
added here are what hold these particular fixes in place.

Where the reveal hid a control, it is done with opacity now rather than
display or [hidden]. Both of those take the control out of the tab order,
so :focus-within can never become true and the focus path is shut before
it starts. staff-notes, tutor-notes and task-comments-viewer show their
actions on :hover or :focus-within of the surrounding card, and those
actions are real buttons instead of mat-icon elements carrying a click
handler, so there is something in the tab order for :focus-within to
answer to.

The staff task inbox keeps its options button in the tab order, stacked
over the pin indicator and revealed on row hover or on its own focus.
Pointer and keyboard hold the row open independently, so tabbing off the
button no longer fades it out from under a pointer still on the row. The
row div is one of the cases described above: it keeps (mouseover) and
(mouseout) and depends on that nested options button for its keyboard
path, which is a real path rather than a lint technicality.

The pin beside that button is no longer a button itself. It reports
whether a task is pinned and has no action, so it was an unlabelled tab
stop on every row and empty on the rows that were not pinned. Worse, it
was hidden whenever the adjacent options button took focus, so a forward
tab landed on it while a shift-tab skipped it and the row's tab order
read differently in each direction. It is a div with role="img" and a
label now, and it is out of the tab order in both directions.

The planner's gantt bar carries role="button" and tabindex="0", so the
(focus) binding on it can actually fire, and Enter and Space open the
prerequisites modal the way a click does. Its focus outline is drawn in
currentColor, which is already picked per bar to contrast with that
bar's own background. The planner spec blanks the component template, so
those attributes were previously unguarded by any test. It now holds the
#bar TemplateRef and stamps that template directly, because ngx-gantt
sizes the chart from real layout and draws no bars under jsdom. Deleting
role, tabindex or either keydown binding fails those tests.

tutor-discussion wrote task.hover from its mouse handlers and fed it to
an [ngClass] with no matching .hover rule in that component's stylesheet,
so the path was a no-op before and after. It is removed rather than left
looking like it does something, and allowHover goes with it since nothing
there ever set it false. hoveredCommentId in the engagement dialog was
likewise only ever written and never read.

Three rule ignores are left in the config, covering keyboard activation
and focus for click handlers elsewhere in the app. Clearing them is
A11Y-F03 and A11Y-F04.
maplefoxgit and others added 29 commits September 14, 2026 12:39
…le-form

refactor: replace flex-layout with tailwindcss in edit profile form
refactor: replace flex-layout with tailwindcss in audio player
refactor: replace flex-layout with tailwindcss in admin users
refactor: replace flex-layout with tailwindcss in header
refactor: replace flex-layout with tailwindcss in edit profile
…endar-2026-09

feat(calendar): land CAL-F07, F08, F09 and PR-CAL-14 on 11.0.x
FEPERF-20: Optimise comment cache reconciliation
…ixed-20260914

fix(a11y): label the task badges used by students
Both unit dropdowns bound [value] on the select, but their options are
created later by @for, so the browser kept the first option selected. A
staff member managing two units could see SIT111 in Manage updates while
saves went to SIT222, and the filter showed All my units for ?unit=222.
Mark the matching option as selected and add a regression spec that holds
the reload open until the options re-render.

Also open a Teams draft for a session saved without a description. The API
returns null for it, which the plain text check rejected as too long.
…nnouncements-sessions

feat: centralize unit updates, Teams meetings and study essentials
Bumps [hono](https://github.com/honojs/hono) from 4.13.3 to 4.13.7.
- [Release notes](https://github.com/honojs/hono/releases)
- [Commits](honojs/hono@v4.13.3...v4.13.7)

---
updated-dependencies:
- dependency-name: hono
  dependency-version: 4.13.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
The old base feature/peer-progress-indicator is already on 11.0.x in a newer
form, so every conflicted peer progress file takes the 11.0.x version. The
only change left is outputHashing in angular.json.
…and_yarn/hono-4.13.7

chore(deps): bump hono from 4.13.3 to 4.13.7
…ogress-indicator-JE-live

chore(web): enable outputHashing=all for production caching
fix(a11y): two empty states are a single aria-hidden icon with no text
…ailwind

refactor: replace ng-flex-layout with Tailwind in task-viewer-state
First slice of THM-M03 (tutor/unit-chair/admin workflow theming), covering
checklist item 2: the task inbox / marking queue tutors use to review and
assess submissions (inbox.component, staff-task-list.component,
moderation.component, inbox-dashboard.component).

Migrates every hardcoded colour in these four components onto the THM-M01
token layer (--ot-color-*), following the exact pattern THM-M01 established
in header.component.scss: var(--ot-color-X, <original-light-value>), so
light mode is pixel-identical and only dark mode changes. One exception
needed a real override rather than a token swap: staff-task-list's
--background-gray (a hover/selected row tint) is an rgba(black) value that
reads correctly in light but would be nearly invisible or wrong-direction on
a dark surface, so it gets a :host-context([data-ot-theme='dark']) override
to an rgba(white) tint instead.

Also migrates the four Tailwind arbitrary-hex classes and one inline style
in inbox-dashboard's empty states (the icons/text shown when no task is
selected, or a task has no PDF/task sheet) onto the same tokens via
Tailwind's var() arbitrary-value syntax.

Adds a focused spec for InboxDashboardComponent (previously untested) that
asserts the empty-state icon and message carry the token class rather than
a bare hex, verified to fail against the pre-migration classes and pass
after.

Checklist items 1, 3-10 (route confirmation beyond this surface, student
lists, unit/task editors, staff reports, dense-table/nav/narrow-layout
checks, and the remaining marking-queue sub-surfaces - task-claim,
confirm-moderation-modal, batch-feedback-workflow-dialog) are follow-up
work, not covered by this PR. No business, permission, or marking-transition
logic changed - colour/class values only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.