Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions docs/CPD-Q06-privacy-safe-screenshot-checklist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Cross-Project Dashboard Privacy-Safe Screenshot Checklist

Use this checklist before sharing a dashboard screenshot in a pull request, Teams message,
report, portfolio, presentation, or handover document.

## Before sharing

- [ ] Real student names have been removed or replaced with mock names.
- [ ] Student IDs and email addresses are not visible.
- [ ] Personal account details and profile images are not visible.
- [ ] Unit and project information does not reveal a student's private enrolment details.
- [ ] Task titles do not contain sensitive or personally identifying information.
- [ ] Marks, feedback text, and individual assessment results are removed, permanently
redacted in the final exported image, or replaced with mock data.
- [ ] Extension details and changed due dates do not reveal private student circumstances.
- [ ] Browser tabs, URLs, tokens, local file paths, and account information are cropped out or
permanently redacted in the final exported image.
- [ ] Mock or seeded data has been used wherever possible.
- [ ] The screenshot contains only the area needed to prove the result.
- [ ] The final exported screenshot has been reopened and checked at full size to confirm that
no sensitive data remains.
- [ ] The original unredacted screenshot is not committed to Git, attached to the pull request,
or posted in a shared project channel.

## Safe example

A screenshot of the dashboard populated entirely with seeded demo data. For example, students
named "Test Student 1" and "Test Student 2" with placeholder emails, generic task titles, and
sample marks that were never tied to a real person. The screenshot is cropped tightly to the
feature being demonstrated, with no browser chrome, URL bar, or login or account menu visible.

## Information that must be removed

A screenshot taken from a real unit that shows an actual student's name, ID, or email next to
their real mark, feedback comment, or an approved extension with a reason attached. This kind
of detail must be cropped out, permanently redacted in the final exported image, or replaced
with mock data before sharing. Do not rely on light blur or pixelation to protect sensitive
information.

## Recommendation

The safest default for future contributors is to demonstrate features using mock data rather
than real student or account data, even when it takes a little longer to set up. When redaction
is unavoidable, export a flattened copy, reopen that final file, and check it again before
sharing.
132 changes: 132 additions & 0 deletions docs/cpd-data-source-map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
**Cross-unit Dashboard (CPD): Data Sources & Ownership Rules**

**Branch:** docs/cpd-data-source-map

**Purpose:** Provide frontend, backend, and security contributors with a
single source of truth regarding dashboard data sources, role
permissions, cross-unit visibility rules.

**Field-to-Source Mapping Table**

This table maps user-facing fields on the Cross-unit Dashboard to their
underlying backend services and models:
| Dashboard Field / Element | Angular Component / Data Binding | API Endpoint & Method | Backend Model / Entity | Access Scope & Permissions |
| :--- | :--- | :--- | :--- | :--- |
| **Unit Code & Name** | `DashboardUnit.code`, `DashboardUnit.name` | `GET /projects` | `Unit#code`, `Unit#name` via `Project.eager_load(:unit)` | Authenticated Student (`for_user current_user`) |
| **Task Title & Subtitle** | `DashboardTask.title`, `DashboardTask.subtitle` | `GET /projects` (`include_task_definitions=true`) | `TaskDefinition#name`, `TaskDefinition#abbreviation`, `TaskDefinition#targetGradeText` | Authenticated Student (`current_user`) |
| **Task Description** | `DashboardTask.description` | `GET /projects` (`include_task_definitions=true`) | `TaskDefinition#description` | Authenticated Student (`current_user`) |
| **Task Status & Color** | `DashboardTask.status`, `statusLabel`, `color` | `GET /projects` | `Task#status`, mapped via `TaskStatus.STATUS_LABELS/COLORS` | Authenticated Student (`current_user`) |
| **New Comments Count** | `DashboardTask.comments` | `GET /projects` | `Task#numNewComments` | Authenticated Student (`current_user`) |
| **Due Date** | `DashboardTask.dueDate` | `GET /projects` | `TaskDefinition#targetDate` | Authenticated Student (`current_user`) |
| **Task Weight / Priority** | `DashboardTask.weight` | `GET /projects` | `Task#topWeight` (calculated via `project.calcTopTasks()`) | Authenticated Student (`current_user`) |
| **Project ID / Unit Key** | `DashboardUnit.projectId` | `GET /projects` | `Project#id` | Authenticated Student (`current_user`) |

**Data Ownership, Enrolment & Visibility Rules**

**Active vs. Inactive Enrolment Filtering**

- **Backend Inactive Toggle:** GET /projects accepts an optional query
parameter. By default (false), only active unit projects for
current_user are fetched.

- **Frontend Active Task Scope:** CrossDashboardComponent populates
unit tasks via project.activeTasks(), ensuring archived or inactive
tasks are excluded from the default view.

**Client-Side State, Filtering & Sorting Rules**

- **Hide Completed Filter (Filter.HideCompleted):** Managed via
private filters: Map\<number, Filter\[\]\>. When active, tasks with
task.status == \'complete\' (completedTypes) are removed from
unitsProcessed.

- **Default Task Ordering:** In all sort modes, completed tasks are
pushed to the bottom of the list (completedTypes.includes(a.status)
evaluation).

- **Due Date Sort (SortMode.SubmissionDate):** Orders active tasks
chronologically using a.dueDate.getTime() - b.dueDate.getTime()
(TaskDefinition#targetDate).

- **Default Weight Sort (SortMode.Default):** Orders tasks by
calculated priority weight using a.weight - b.weight
(Task#topWeight).

**Role-Based Access Control & Data Isolation**

- **Student Access:** All dashboard data subscriptions execute through
GlobalStateService.currentUserProjects, which calls ProjectsApi
endpoints protected by authenticated?. Queries are strictly scoped
to the authenticated session (for_user current_user).

- **Write Safeguards:** Modifying target grades or submitted grades
via PUT /projects/:id is restricted once portfolio_exists? evaluates
to true (returns HTTP 403 Forbidden).

**Technical Gaps**

During this documentation audit, the following technical gaps were
identified directly in the source code:

1. **SortMode.Recommended Stub:** In
CrossDashboardComponent.processTasks(), the recommended sort mode is
stubbed out with // TODO: Connect to recommender\'s points and
returns 0. Recommended sorting currently falls back to default
ordering and is not connected to the backend Task Prioritisation
Recommender Service.

2. **In-Memory Filtering & Performance:** Task filtering (Hide
Completed) and sorting are processed entirely client-side on the
Angular main thread (this.unitsProcessed = this.units.map(\...)).
Heavy task volumes across multiple units may impact frontend
rendering performance.

3. **Missing Inactive Unit Filter Toggle in UI:** While ProjectsApi
supports include_inactive: Boolean, CrossDashboardComponent does not
currently expose an interface control to pass this query parameter
to GlobalStateService.

**Data-Flow Diagram**

```text
[ CrossDashboardComponent ]
|
| (1) Subscribes to currentUserProjects
v
[ Frontend: GlobalStateService ]
|
| (2) GET /projects?include_task_definitions=true
v
[ Backend API: ProjectsApi ]
|
| (3) Project.eager_load(:unit, :user).for_user(current_user)
v
[ PostgreSQL Database ]
|
| (4) Represents array via Entities::ProjectEntity
v
[ CrossDashboardComponent: mapProjects() & mapTasks() ]
|
| (5) In-Memory Filter/Sort via processTasks()
v
[ Rendered Dashboard Unit Cards ]
```

**Recommended Follow-Up Tickets**

- **Integrate Task Prioritisation Recommender with
SortMode.Recommended:** Connect SortMode.Recommended in
CrossDashboardComponent to the backend recommender service priority
scores, replacing the current return 0 stub.

- **Add UI Toggle for Inactive/Archived Units:** Add a filter option
to pass include_inactive=true to GlobalStateService, allowing
students to view historical unit projects.

- **Batch Priority Points in ProjectEntity:** Extend
Entities::ProjectEntity to include pre-calculated recommender
weights per task definition to streamline frontend sorting.

- **Unit Test Coverage for CrossDashboardComponent:** Write Angular
unit tests for toggleFilter(), setSort(), and processTasks() to
prevent sorting regressions.
115 changes: 115 additions & 0 deletions docs/cpd-user-guide-handover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Cross-Project Dashboard: User Guide & Contributor Handover

This document is the user guide for students using the Cross-Project Dashboard and a handover reference for contributors maintaining the feature on `11.0.x`. The historical `feature/cross-unit` work is already integrated into that release line.

---

## Part 1: Student User Guide

### 1. Unit Scopes

The dashboard allows students to filter visible projects using the **Unit scope** dropdown.

- **Active units:** Displays currently enrolled and active projects.
- **Previous units:** Displays authorised projects from past teaching periods. If no historical data exists, a "No previous units are available" message is displayed.
- **All units:** Combines active and previous authorised projects in one view.

![Unit Scope Dropdown](assets/unit-scope-dropdown.png)

### 2. Searching and Filtering

The dashboard features a global toolbar to filter tasks across all visible units.

- **Global search:** Matches unit code/name and task title, abbreviation, status, target grade or due date across visible units.
- **Per-unit search:** Matches a task's title, subtitle, description, abbreviation or status inside one unit card.
- **Task statuses:** Filters by any task state offered in the dropdown.
- **Target grades:** Filters by Pass, Credit, Distinction or High Distinction tiers.
- **Date filters:** Start and end date fields filter tasks by their local due date. An invalid range is announced and is not applied.
- **Clear controls:** "Clear all" restores the Active-units scope and resets all global toolbar filters, including dates. "Clear dates" resets only the date range. Per-unit search and card filters remain independent.
- **Empty states:** The dashboard distinguishes between no available tasks and no tasks matching the current filters.

![Global Search and Filters](assets/global-filters.png)

### 3. Phone Layout

On screens narrower than 640 px, the dashboard uses a stacked layout designed
for touch and keyboard use rather than a horizontally scrolling row of unit
cards.

- The unit scope and global search stay at the top of the page.
- Secondary filters move into a **More filters** disclosure. Its label includes
the number of active secondary filters so hidden filter state remains visible.
- Projects become full-width disclosures, and opening one project closes the
previously open project. Each closed header still shows the unit, project
state, matching-task count and nearest actionable deadline.
- Task actions wrap within the viewport. The page must not require horizontal
panning at supported phone widths down to 320 px.

At 640 px and wider, the desktop/tablet layout remains a horizontally arranged
set of independently scrolling unit cards, with the full filter toolbar visible.

### 4. Grade Completion Summaries

When one or more target-grade filters are selected, each visible unit card displays a **Grade task completion** summary for those tiers.

- It displays counts and percentages, such as "Pass: 1 of 3 complete (33%)".
- These percentages represent _task completion counts_ for the selected tier. They are **not** predicted final grades or official academic marks.

### 5. Due Date Warnings & Limitations

The dashboard provides visual urgency indicators for approaching task deadlines.

- **Overdue:** Displayed in red.
- **Within 24 hours:** Displayed in orange.
- **Within 3 days:** Displayed in yellow.
- **Within 7 days:** Displayed in blue.

Warnings are shown only for tasks that have not been submitted. The dashboard uses `Task.localDueDate()`, which prefers the task-specific due date supplied by the API before falling back to the task definition. The dashboard does not independently grant or calculate extensions; the project record remains the authoritative deadline source.

### 6. Accessibility

- **Keyboard navigation:** Filter controls and task-detail toggles are keyboard operable, and per-unit search controls have visible focus styling.
- **Project disclosures:** Phone project headers expose their expanded state,
keep a visible focus indicator and preserve focus when filters are cleared.
- **Screen readers:** Task-detail buttons expose their expanded state and use task-specific Expand/Collapse labels. Decorative warning icons are hidden from assistive technology.
- **Non-colour meaning:** Due warnings include visible text such as "Overdue" or "Due within 3 days", so colour is not the only signal.

### 7. Authorized Data Boundary (Privacy)

The Cross-Project Dashboard uses the authenticated student's authorised project and task responses.

- It displays task statuses, local due dates and definitions only for projects returned to that student by the API.
- It does not introduce official numerical marks, staff feedback comments or peer-assessment information.

---

## Part 2: Contributor Handover

### 1. Architecture & Key Files

- **Target branch:** `11.0.x`
- **Main component:** `src/app/dashboard/f-cross-dashboard.component.ts` handles unit scopes, global and per-unit filtering, grade completion summaries, previous-unit loading and the single-open phone disclosure state.
- **Main template:** `src/app/dashboard/f-cross-dashboard.component.html` defines the filter toolbar, responsive project disclosures/cards and empty/error states.
- **Responsive styling:** `src/app/dashboard/f-cross-dashboard.component.scss` switches the stacked phone experience at 640 px while retaining the wider horizontal card layout.
- **Due-warning component:** `src/app/dashboard/list-item/dashboard-list-item.component.ts` owns warning thresholds and labels.
- **Test suites:** The adjacent component specs cover unit scopes, global/per-unit search, status/grade/date filters, summaries, warnings and accessibility state.

### 2. Linked Tickets & Context

- **CPD-F04:** Local frontend date-range filtering alongside unit scopes and existing task data.
- **CPD-F05:** Accessible global-toolbar date controls and targeted tests.
- **CPD-Q03:** Accessible due-date warning badges in the dashboard task presentation.
- **CPD-Q04:** Responsive-sizing regression coverage and visual evidence.
- **CPD-Q05:** UI comparison and acceptance evidence; keep the evidence link with the ticket rather than treating this guide as the comparison matrix itself.

### 3. Known Gaps & Post-MVP Work

The following limitations remain post-MVP work:

1. **Deadline source:** Web displays the local due date supplied through the task model. Any new extension representation must be added to the API/model contract before the dashboard can display it.
2. **Visualisation scale:** High task volumes can create substantial scrolling. Adaptive scale controls or compact toggles remain a possible follow-up.
3. **Filter persistence:** Global toolbar filters reset after a page refresh; no account-level filter preference is stored.

### 4. Verification Basis

This guide was reconciled with the merged `11.0.x` implementation after the Cross-Project Dashboard closure and phone-layout merges. The two screenshots show the wider toolbar using demonstration units and contain no real student records. Before changing the guide, rerun the dashboard-focused component specs, verify the screenshots still match the current wider toolbar and unit-scope labels, and check computed layout at 320 px, 390 px, 640 px and a desktop width.
Loading