TaskBoard

Project Documentation

Internal Kanban-style task manager — Angular · PHP · MySQL

Generated on 2026-10-19
15 chapters · 33 documents

1Project README

1.1README.md

TaskBoard

TaskBoard

Internal Kanban-style task manager for a single team. Angular frontend, plain PHP API, MySQL database. Full product documentation lives in docs/.

Current build stage: all steps complete — Step 7.1 (deploy) artifacts and guide are ready (docs Part 12). The live cut-over to https://task.scoaladeweb.ro/ runs once production credentials are filled into api/config/config.php (copy from config.production.sample.php).

Layout

  • api/ — plain PHP (no framework) JSON API. Front controller at api/public/index.php.
  • frontend/ — Angular 20 single-page app.
  • docs/ — the written specification (Parts 1–11).

Requirements

  • PHP 8.4 (MAMP: /Applications/MAMP/bin/php/php8.4.17/bin/php); Node 22 + npm;

Angular CLI 20; MySQL (MAMP, port 8889).

First-time setup

  1. Create the API config from the sample (holds secrets, git-ignored):

``bash cp api/config/config.sample.php api/config/config.php `` The committed sample already has working MAMP defaults; edit if yours differ.

  1. Install frontend dependencies:

``bash npm install --prefix frontend ``

  1. Create the database and load schema + seed (MAMP MySQL on port 8889).

MYSQL below is MAMP's client; adjust the path if your MAMP differs: ``bash MYSQL="/Applications/MAMP/Library/bin/mysql80/bin/mysql --protocol=TCP -h127.0.0.1 -P8889 -uroot -proot" $MYSQL -e "CREATE DATABASE IF NOT EXISTS taskboard CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" $MYSQL taskboard < api/database/schema.sql $MYSQL taskboard < api/database/seed.sql ` The seed creates the admin office@pixeltime.ro with the initial password ChangeMe-TaskBoard-2026! and must_set_password = 1`, so that password is replaced on first login (docs Part 5). The seed is idempotent — safe to re-run.

Run it locally

Start the API (PHP built-in server on port 8000):

/Applications/MAMP/bin/php/php8.4.17/bin/php -S localhost:8000 -t api/public

Start the frontend (Angular dev server on port 4200) in a second terminal:

npm start --prefix frontend

Then open http://localhost:4200. The home page calls GET /api/health and shows whether the API is reachable.

If port 4200 is busy, Angular offers another port (e.g. 4300). Ports 4200 and 4300 (localhost and 127.0.0.1) are already in the API's CORS allowlist (api/config/config.php).

Verify

# Health (expect 200 + JSON {status:"ok", service:"taskboard-api", ...}):
curl http://localhost:8000/api/health

# Admin login (expect 200 + a token):
curl -X POST http://localhost:8000/api/auth/login -H "Content-Type: application/json" \
  -d '{"email":"office@pixeltime.ro","password":"ChangeMe-TaskBoard-2026!"}'

# Protected endpoint with that token (200 with it, 401 without):
curl http://localhost:8000/api/me -H "Authorization: Bearer <TOKEN>"

The home page shows a green Connected badge; visiting any page while signed out redirects to /login.

Configuration

Server config (JWT lifetime in hours, SMTP placeholders, DB credentials, CORS) lives in api/config/config.php — never in the database or the repo. See api/config/config.sample.php for every field.

Notes

  • No framework or Composer for the API: a small custom PSR-4 autoloader and

router keep the code readable (a teaching example). Nothing is ever hard-deleted (the golden rule, docs Part 6).

2Documentation Index

2.1docs/00-contents.md

TaskBoard — Documentation

TaskBoard is an internal Kanban-style task manager for a single team, built with Angular (frontend), plain PHP (backend API), and MySQL (database). This documentation is written before any code. It describes what we are building, why, and how, in plain language.

Status: documentation complete (Parts 1-12 written and approved). Code complete — all build steps done (Phases 0–7). Step 7.1 deploy artifacts + guide (Part 12) are ready; the live cut-over to task.scoaladeweb.ro runs once production credentials are filled into config.php. See Part 10 — Build Plan, Part 12 — Deploy and ../README.md.

Table of Contents

  1. Overview & Glossary — what the app is, who it's for, the core ideas, and shared vocabulary.
  2. People & Roles — the two roles (admin, member) and exactly what each can and cannot do. Split into:
  3. Features — a grouped list of everything the app does. Split into:
  4. Data Model — what we store and how it relates. Split into:
  5. Main User Journeys — step-by-step walkthroughs of the key flows. Split into:
  6. Rules & States — statuses, transitions, archiving, permissions. Split into:
  7. Architecture & API — how frontend, backend, database and email fit together. Split into:
  8. Screens — every screen and how you move between them. Split into:
  9. Non-Negotiable Basics — security, mobile, performance, accessibility, data safety.
  10. Build Plan — the order we build things in, small verifiable steps. Split by phase into:
  11. Design Guidelines — the light-theme visual system from the reference screenshots. Split into:

Foundations are implemented as design tokens in frontend/src/styles.scss.

  1. Deploy — taking TaskBoard live on task.scoaladeweb.ro (app) + /api (API). Split into:

Key decisions locked so far

  • Single team. Two roles: admin and member.
  • Hierarchy: Projects → Tasks. Every task belongs to exactly one project.
  • Board: Kanban, global, with fixed columns To Do / In Progress / Done plus a hidden Archived column. When several projects are shown at once, each project gets its own swimlane.
  • Tasks are never deleted, only archived (the golden rule).
  • Each task has one reporter (creator) and one assignee (required). Reassignment is admin-only.
  • Task extras: comments (with @mentions), one label per task (labels are global, colored, admin-managed colors), priority (Low / Medium / High), and time log entries.
  • No deadlines, no overdue, no attachments, no subtasks.
  • Time log: hours only (plus who and an automatic date). Only the current assignee can log time; the admin can log on any task. Everyone edits/deletes only their own entries.
  • Activity log per task records everything: creation, comments, status changes, time log added/edited/deleted, archiving — who and when.
  • Notifications: in-app (with unread counter and "mark all as read") plus email. Email delivery is configurable per user in settings.
  • Auth: JWT with a configurable expiry (in hours). Accounts are created by the admin; a generated password is emailed to the user; on first login the user sets their own password (min. 8 characters).
  • Interface language: English only (UI, messages, emails).
  • Structure: tasks/frontend (Angular) and tasks/api (PHP). MySQL database named taskboard, no table prefix.

This page is kept up to date as each part is written.

3Overview & Glossary

3.1docs/01-overview.md

Part 1 — Overview & Glossary

← Table of Contents

What TaskBoard is

TaskBoard is a small, focused task manager for one team. People organize their work as tasks, tasks live inside projects, and everyone shares a single Kanban board where work moves through three stages: To Do, In Progress, and Done. It is part to-do list, part shared team workspace.

It is built to be used internally by our own team and hosted on our own server. It is also a teaching example: the code is written to be read and understood, so it favors clear, well-commented, object-oriented PHP over clever shortcuts.

Who it's for

A single team of roughly 7-8 people (with room for a few more later). There is no concept of multiple teams or companies. Everyone who uses the app is a member of the same team. One person (the owner) is the admin; everyone else is a member.

Expected scale is small and comfortable: at most a few hundred projects, up to a few hundred tasks per project, and a handful of comments per task. Realistically much less. The design keeps things simple and fast at this size rather than optimizing for thousands of users.

The main problem it solves

We want to see, at a glance, the real state of the team's work:

  • what tasks exist and which stage each one is in,
  • who each task is assigned to and who created it,
  • the discussion around a task (comments and explanations),
  • how much time has gone into each task and project.

Everything else in the app serves that goal: knowing what is being worked on, by whom, and where it stands.

The core ideas

  • Project — a container for related tasks. Only the admin creates and manages projects. Members work inside existing projects.
  • Task — a single unit of work. It belongs to one project, is created by a reporter, is worked on by an assignee, has a priority and at most one label, and moves across the board by status.
  • Board — the shared Kanban view. It is global (shows tasks across projects) and can be filtered by project, user, label, and priority. Columns are fixed: To Do, In Progress, Done. Archived tasks live in a separate hidden column you reveal with a button. When several projects are shown at once, each project appears as its own horizontal section (swimlane).
  • Comment — a text note on a task. Comments can @mention people, which notifies them.
  • Time log — an entry recording how many hours someone spent on a task. Time logs feed the admin's time reports.
  • Activity log — the per-task history of everything that happened: created, commented, status changed, time logged/edited/deleted, archived — always with who and when.
  • Notification — a signal that something relevant happened (you were assigned, mentioned, someone commented, etc.), shown inside the app and optionally sent by email.

What we're building now vs. later

This project has one version, not a stripped-down v1 followed by later phases. The full feature set described in this documentation is what we build. That said, the documentation is honest about scope: features we explicitly decided not to build are listed here so no one expects them.

Deliberately out of scope (not now, not planned):

  • No deadlines / due dates, and therefore no "overdue" concept. Time is tracked only by logging hours.
  • No file attachments. Tasks and comments are text only.
  • No subtasks or checklists.
  • No multiple assignees. Exactly one assignee per task.
  • No multiple teams or workspaces. One team only.

Glossary

So we all use the same words for the same things:

  • Admin — the single system-wide superuser. Manages projects, users, labels, reassigns tasks, archives/unarchives anything, sees time reports, and configures notification settings. There is one admin: office@pixeltime.ro.
  • Member — a regular user. Sees everything, creates tasks assigned to themselves, comments, logs time on their own tasks, moves their own tasks on the board, and manages their own profile.
  • Project — a named container of tasks, created and managed by the admin.
  • Task — a unit of work inside one project.
  • Reporter — the user who created a task.
  • Assignee — the single user currently responsible for a task. Required at all times.
  • Status — the task's stage: To Do, In Progress, or Done. (Archived is a separate state, not a status.)
  • Priority — Low, Medium, or High, each shown with a color.
  • Label — a short, colored tag. Labels are global (shared across all projects). A task has at most one label. Label colors are managed by the admin.
  • Board — the shared Kanban view of tasks arranged in status columns.
  • Column — a vertical stack on the board for one status. Fixed set: To Do, In Progress, Done, plus a hidden Archived column.
  • Swimlane — a horizontal section grouping one project's tasks, used when the board shows more than one project at once.
  • Comment — a text note on a task; may include @mentions.
  • Mention — writing @SomeUser in a comment to notify that user.
  • Time log — an entry of hours spent on a task by a user, with an automatic date.
  • Time report — an admin-only summary of hours by project, task, and user.
  • Activity log — the full chronological history of a single task.
  • Notification — an in-app (and optionally email) signal about a relevant event.
  • Archived — a task set aside without deleting it. Reversible. Tasks are never deleted.
  • JWT — the login token the frontend sends with each request to prove who you are. Its lifetime (in hours) is configurable on the backend.

← Table of Contents · Next: Part 2 — People & Roles (to be written)

4People & Roles

4.1docs/02-people-and-roles.md

Part 2 — People & Roles

← Table of Contents · ← Part 1

TaskBoard has one team and two roles: admin and member. This part is the permissions map — who can do what, and just as importantly, what each role cannot do. Getting permissions right is what keeps the data trustworthy, so we spell them out precisely. The detailed per-area tables continue in Part 2a.

The roles at a glance

  • Admin — one system-wide superuser (office@pixeltime.ro). Runs the whole app: manages users, projects, and labels; can reassign, move, archive, and unarchive any task; can log time on any task; sees time reports; configures notification settings for the team. There is exactly one admin account, seeded into the database from the start.
  • Member — a regular team member. Does their own work: creates tasks for themselves, comments anywhere, logs time and moves the board only for tasks currently assigned to them, and manages their own profile. Members see everything (nothing is hidden) but can only change their own things.

A guiding principle runs through the whole app: visibility is open, editing is scoped. Everyone can see all projects, tasks, comments, time logs, and the user list. What differs by role is what you are allowed to change.

Accounts & profile

ActionAdminMember
Create user accounts (generates password, emailed to user)YesNo
Deactivate / reactivate a user accountYesNo
Edit own profile (full name, email)YesYes
Change own passwordYesYes
See the full list of usersYesYes
Change another user's profile or passwordNoNo

Notes:

  • No one can delete a user; accounts are deactivated instead. A deactivated user cannot log in, and their tasks appear slightly transparent on the board so it is clear the person is inactive. Their data stays intact.
  • Even the admin does not edit other people's profiles or reset their passwords directly; a user manages their own credentials.

Projects

ActionAdminMember
Create a projectYesNo
Rename / edit a projectYesNo
Archive / manage a projectYesNo
View projectsYesYes
Add tasks inside an existing projectYesYes

Projects are entirely the admin's domain to create and manage. Members live inside the projects the admin has set up.

Tasks

ActionAdminMember
Create a taskYesYes
Choose the assignee when creatingAnyoneOnly themselves
Reassign a task (change assignee) after creationYesNo
Edit a task's details (title, description, priority, label)Any taskOwn tasks (created by or assigned to them)
Move a task across board columns (status change)Any taskOnly tasks currently assigned to them
Archive a taskAny taskOnly tasks they created
Unarchive a taskAny taskOnly tasks they created
Delete a taskNoNo

Notes:

  • The golden rule: tasks are never deleted. They are archived instead, which is reversible. There is no delete action for tasks anywhere, for anyone.
  • Assignee is always required. A member creating a task assigns it to themselves. The admin may assign to anyone.
  • Moving on the board: a member can drag only their own assigned tasks. The admin can drag anyone's task.

← Table of Contents · Part 2a — more permissions →

4.2docs/02a-roles-permissions.md

Part 2a — Roles: comments, time, labels, notifications

← Part 2

Continuation of the permissions map from Part 2: comments, time log, labels, notifications, the universal prohibitions, and the seeded admin.

Comments & mentions

ActionAdminMember
Add a comment to any taskYesYes
Mention someone with @YesYes
Edit own commentYesYes
Delete own commentYesYes
Edit someone else's commentNoNo
Delete someone else's commentYesNo

Anyone can comment on any task (visibility is open). You manage only your own comments, except the admin, who may also delete others' comments (moderation) but never edit them.

Time log

ActionAdminMember
Log time on a task assigned to themYesYes
Log time on a task not assigned to themYesNo
Edit / delete own time entriesYesYes
Edit / delete someone else's time entriesNoNo
See time reports (by project / task / user)YesNo

Notes:

  • A member can log time on a task only while it is currently assigned to them. After a reassignment they can no longer add time, though hours they already logged remain counted in the task's total and history.
  • The admin can log time on any task, regardless of assignee.
  • Everyone edits or deletes only their own entries. Any such edit/deletion is recorded in the task's activity log, so history is never silently lost.
  • Time reports are admin-only.

Labels

ActionAdminMember
Attach an existing label to a taskYesYes (on tasks they can edit)
Create a new label while creating/editing a taskYesYes (creator of the task)
Set / change a label's colorYesNo
Manage the global label listYesNo

Labels are global and shared across all projects. A task has at most one label. When creating a task, the admin or the task's creator may pick an existing label or add a new one. Label colors are controlled by the admin only.

Notifications & settings

ActionAdminMember
Receive in-app notificationsYesYes
Configure which events send emailYes (team-wide events)Yes (limited, see below)
Configure team-wide notification settingsYesNo

Notes:

  • In-app notifications are always delivered; only whether an event also sends an email is configurable.
  • The admin can turn email on/off for: task assigned, task created by someone else, new comment, status change, time log added, and mention.
  • A member also has email settings, but only for the projects where they have at least one task, or for tasks they follow (a task they have already commented on is considered followed).
  • Exactly which events notify whom lives in Part 3 (Features) and Part 6 (Rules & States).

What no one can do

  • Delete a task. (Archive instead.)
  • Delete a user. (Deactivate instead.)
  • Edit another person's comment, profile, password, or time entries.
  • Members cannot create or manage projects, reassign tasks, move tasks that are not theirs, set label colors, or view time reports.

The seeded admin

The database ships with one admin account already created: email office@pixeltime.ro, with an initial password set at seed time, replaced after first use. This is the only account that exists before anyone else is invited; every other account is created by this admin.


← Part 2 · Table of Contents →

5Features

5.1docs/03-features.md

Part 3 — Features

← Table of Contents · ← Part 2

This part lists everything the app does, grouped by area. Because it is long, it is split into three linked files. Read them in order.

How to read the feature lists

TaskBoard has a single version — there is no "v1 then later." Everything below is what we build. To stay honest about scope, each area ends with an Out of scope note listing things we deliberately do not build, so no one expects them.

Each feature is written as a short capability ("the system lets X do Y"), in plain language. Detailed rules (allowed status transitions, exact permission edges, edge cases) live in Part 6 — Rules & States; this part is about what exists, not the fine print.


← Table of Contents · Next: Part 3a — Accounts, Projects, Tasks

5.2docs/03a-features-core.md

Part 3a — Features: Accounts, Projects, Tasks

← Part 3 index

Accounts & login

  • Admin creates accounts. The admin adds a person by full name and email. The system generates a random password and emails it to that person. (There is no public sign-up.)
  • Generated password by email. The new user receives their initial password by email and uses it to log in the first time.
  • First-login password setup. On first login, the user is required to set their own password (minimum 8 characters) before continuing. From then on they log in with that password.
  • Login. A user logs in with email + password and receives a JWT that the frontend sends with every request. The token's lifetime (in hours) is a backend setting the admin can change.
  • Logout. The user can log out, which discards the token on the client.
  • Change own password. Any logged-in user can change their own password (min. 8 characters) from their profile.
  • Edit own profile. A user can edit their own full name and email.
  • Deactivate / reactivate. The admin can deactivate an account (the user can no longer log in) and reactivate it later. Deactivated users are never deleted; their tasks and history remain, and their tasks appear slightly transparent on the board.
  • User list. Everyone can see the list of users (needed for assignment and @mentions). Only the admin can create, deactivate, or reactivate accounts.

Out of scope: public self-registration; social/SSO login; password-strength rules beyond the 8-character minimum; admin resetting another user's password directly; deleting users.

Projects

  • Create project. The admin creates a project with a name (and optional short description).
  • Edit project. The admin can rename a project or edit its description.
  • Archive project. The admin (only) can archive a project that is no longer active. Archiving a project automatically archives all of its tasks, and they disappear from the board until the project is reactivated. (Projects, like tasks, are archived rather than deleted.)
  • View projects. Everyone sees all projects and can open any of them.
  • Add tasks to a project. Any user can add tasks inside an existing project; only the admin creates the projects themselves.

Out of scope: members creating or editing projects; nested projects/sub-projects; per-project custom columns (columns are fixed team-wide); deleting projects.

Tasks

A task is the core unit of work. It always belongs to exactly one project and always has exactly one assignee.

  • Create task. A user creates a task inside a project with: a title, an optional description (text), a priority, and at most one label. A member's new task is assigned to themselves; the admin may assign it to anyone. The creator becomes the task's reporter.
  • Create another (Jira-style). The create-task dialog offers a "create another" action that keeps the shared fields already filled in — project and label — while clearing assignee, title, and description, so several related tasks can be added quickly.
  • Reporter & assignee. Every task tracks its reporter (creator, fixed) and its assignee (current owner). Assignee is required at all times.
  • Priority. Every task has a priority: Low, Medium, or High, each shown with a color.
  • Label. A task may carry at most one label (global, colored). The task's creator or the admin can pick an existing label or add a new one; label colors are admin-managed.
  • Status. Every task has a status: To Do, In Progress, or Done. Status changes by moving the task on the board.
  • Edit task. The admin can edit any task's details (title, description, priority, label). A member can edit details of tasks they created or are assigned to.
  • Reassign. Only the admin changes a task's assignee.
  • Archive / unarchive. Tasks are archived instead of deleted. The admin can archive/unarchive any task; a member can archive/unarchive only tasks they created. Archived tasks leave the normal columns and appear in a hidden Archived column revealed by a button.
  • Activity log. Every task keeps a full history (see Part 3b): created, comments, status changes, time logged/edited/deleted, archiving — each with who and when.

Out of scope: deleting tasks; due dates / deadlines / overdue; multiple assignees; multiple labels per task; subtasks and checklists; file attachments; moving a task to a different project after creation (a task's project is fixed once created).


← Part 3 index · Next: Part 3b — Board, Comments, Time Log

5.3docs/03b-features-work.md

Part 3b — Features: Board, Comments, Time Log

← Part 3 index · ← Part 3a

The board (Kanban)

  • Global board. There is one shared board showing tasks across projects, not a separate board per project.
  • Fixed columns. Three status columns, in order: To Do, In Progress, Done. A fourth Archived column is hidden by default and revealed with a button.
  • Swimlanes per project. When the board shows more than one project at once, each project appears as its own horizontal section (swimlane) so tasks stay grouped by project. When a single project is in view, its tasks simply fill the three columns.
  • Card contents. Each task shows as a card with its title, priority (color), label (color), assignee, and project. Cards for tasks owned by a deactivated user appear slightly transparent.
  • Ordering. Within a column, tasks are ordered newest-first (by creation). There is no manual vertical reordering.
  • Drag and drop to change status. Moving a card to another column changes the task's status. A member can drag only tasks currently assigned to them; the admin can drag any task (typically after filtering the board to one user).
  • Filtering. The board can be filtered by one or more projects, one or more users, label, and priority. Filters combine, and can show one, several, or all projects/users.
  • Archived view. A button reveals archived tasks so they can be reviewed and, where allowed, unarchived.

Out of scope: custom or per-project columns; manual card ordering within a column; WIP limits; multiple independent boards.

Comments & mentions

  • Comment on any task. Any user can add a text comment to any task.
  • @mentions. A comment can mention a user with @ (for example @GB). Mentioned users are notified.
  • Edit own comment. The author can edit their own comment.
  • Delete comments. The author can delete their own comment. The admin can also delete anyone's comment (moderation) but cannot edit someone else's.
  • Following via comments. Commenting on a task marks the commenter as following that task, which affects which notifications they may receive (see Part 3c).

Out of scope: attachments in comments; threaded replies; reactions/emojis; editing another person's comment.

Time log

  • Log hours. A user records how many hours they spent on a task. An entry stores the number of hours, who logged it, and an automatic date. There is no description field on an entry.
  • Multiple entries. A task can receive many time-log entries over time, building a history of effort.
  • Who can log. Only the task's current assignee can log time on it, and only while they remain the assignee. The admin can log time on any task regardless of assignee (for example, time spent reviewing).
  • After reassignment. If a task moves from user A to user B, A can no longer log time, but the hours A already logged remain counted in the task's total. B can now log time. This lets the task's total reflect everyone's effort.
  • Edit / delete own entries. Everyone (admin included) can edit or delete only their own time entries. Every add, edit, and delete is written to the task's activity log, so nothing is silently lost.

Out of scope: logging against a deadline/estimate; start/stop timers; billing/rates; logging time you are not entitled to (non-assignee members).

Activity log (per task)

  • Full history. Each task keeps a chronological record of what happened to it, with who and when:
    • task created,
    • comment added / edited / deleted,
    • status changed (from → to),
    • time logged / edited / deleted,
    • task archived / unarchived,
    • assignee changed (reassignment).
  • Read-only. The activity log is a record; it is shown on the task detail view and is not editable.

Out of scope: editing or pruning history; exporting the activity log (not planned now).


← Part 3 index · Next: Part 3c — Search, Notifications, Reports, Settings

5.4docs/03c-features-support.md

Part 3c — Features: Search, Notifications, Reports, Settings

← Part 3 index · ← Part 3b

Search & filtering

  • Filter the board. As covered in Part 3b, the board filters by project(s), user(s), label, and priority.
  • Find tasks. A search lets a user find tasks by text in the title (and description), narrowing quickly to what they need.
  • Combine with filters. Search results respect the active filters so a user can drill down (for example, High-priority tasks assigned to one person in one project).

Out of scope: full-text search across comments/history; saved searches; advanced query syntax.

Notifications

Notifications come in two channels: in-app (always delivered, shown in a dedicated section with an unread counter) and email (configurable — see Settings). The events differ by role.

Admin receives

  • a task was assigned (to anyone),
  • a task was created by someone else (not self-created),
  • a new comment was added (on any task),
  • a task's status was changed (any task),
  • time was logged on a task,
  • the admin was @mentioned.

Member receives

  • a task was assigned to them,
  • a new comment was added on a task assigned to them,
  • a new comment was added on a task they follow (a task they have commented on before),
  • they were @mentioned.

In-app behavior

  • Unread state and counter. Notifications have a read/unread state, with a badge showing the unread count.
  • Mark as read. A user can mark a single notification as read, and there is a "mark all as read" action.
  • Open target. Selecting a notification opens the related task.

Out of scope: push/mobile notifications; digest emails; per-notification snooze; notifying on every field edit.

Time reports (admin only)

  • Who sees them. Only the admin can view time reports.
  • What they show. Totals of logged hours, viewable by project, by task, and by user (for example, total hours on a project, hours per user within a project, hours per task).
  • Archived handling. Reports present two figures where relevant: the total excluding archived tasks, and — in parentheses — the total including archived tasks. This keeps active effort clear while still accounting for archived work.

Out of scope: exporting reports (CSV/PDF); charts/graphs (numbers first); date-range filtering (not planned now); members seeing reports.

Settings

User profile (all users)

  • Edit own full name and email.
  • Change own password (min. 8 characters).
  • Manage own email-notification preferences (members: limited to projects where they have a task, and tasks they follow).

Admin settings (admin only)

  • User management. Create accounts, deactivate/reactivate users.
  • Label management. Manage the global label list and set label colors.
  • Project management. Create, edit, and archive projects.
  • Email notification settings. Turn email on/off per event type for the team: task assigned, task created by someone else, new comment, status change, time log added, mention. In-app notifications are always on; only email is configurable.
  • Token lifetime. The JWT expiry (in hours) is a backend configuration the admin can change.

Out of scope: team/workspace management (there is one team); custom roles/permissions; theming.


← Part 3 index · Next: Part 4 — Data Model (to be written)

6Data Model

6.1docs/04-data-model.md

Part 4 — Data Model

← Table of Contents · ← Part 3c

What the app remembers and how the pieces relate, in plain language. The table-by-table field lists are in Part 4a. The database is MySQL, named taskboard, no table prefix. Exact column types and indexes are refined in Part 7; here we focus on what is stored and how it connects.

The things we store, in plain words

  • A user is a person who can log in. Full name, email, hashed password, a role (admin or member), and an active/inactive flag. One user is the admin.
  • A project is a container the admin creates. A name, an optional description, and an archived flag. A project has many tasks.
  • A task belongs to exactly one project. Title, optional description, priority, status, an archived flag, and timestamps. It always points to one reporter (creator) and one assignee (currently responsible), and to at most one label.
  • A label is a global, colored tag shared across projects. A task has at most one label; a label can be on many tasks.
  • A comment belongs to one task and one author. Text + timestamps. A task has many comments.
  • A mention links a comment to a user who was @mentioned in it; used to drive notifications.
  • A time log entry belongs to one task and one user. Hours + the date logged. Totals feed the admin's reports.
  • An activity log entry belongs to one task: what happened, which user did it, and when.
  • A notification belongs to one recipient. A type, a reference to the related task, a read/unread flag, and a timestamp.
  • Notification settings record, per user, which event types also send email (in-app is always on).

How they relate

  • A project has many tasks; each task belongs to one project. (Archiving a project archives its tasks.)
  • A task has one reporter and one assignee (both users) and at most one label.
  • A label can be applied to many tasks; a task has zero or one label.
  • A task has many comments; each comment belongs to one task and one author.
  • A comment can mention many users (many-to-many via mentions).
  • A task has many time-log entries and many activity-log entries; each belongs to one task and one user.
  • A user has many notifications; each references one task.
  • A user has one set of notification settings.

← Table of Contents · Part 4a — tables & fields →

6.2docs/04a-data-model-tables.md

Part 4a — Data Model: tables & fields

← Part 4

The table-by-table field lists for the taskboard database. Names are indicative; final naming/types are settled in Part 7. See Part 4 for the plain-words overview and relationships.

users

id · full_name · email (unique) · password_hash · role (admin|member) · is_active · must_set_password (true until first-login setup done) · created_at · updated_at

projects

id · name · description (optional) · is_archived · created_at · updated_at

labels

id · name · color (admin-managed) · created_at · updated_at

tasks

id · project_id → projects (fixed once created) · reporter_id → users (creator, fixed) · assignee_id → users (required, changeable by admin) · label_id → labels (nullable, at most one) · title · description (optional) · priority (low|medium|high; colors fixed in the design theme, not admin-editable) · status (todo|in_progress|done) · is_archived · archived_by_project (set when archived by a project-archive cascade, so unarchiving the project restores exactly those tasks — see Part 6) · created_at · updated_at

comments

id · task_id → tasks · author_id → users · body · is_edited (explicit flag, since timestamps are only second-precise) · created_at · updated_at

comment_mentions

id · comment_id → comments · mentioned_user_id → users · (a row per user mentioned in a comment)

time_logs

id · task_id → tasks · user_id → users (who logged) · hours · logged_on (date, automatic) · created_at · updated_at

activity_logs

id · task_id → tasks · actor_id → users · type (e.g. task_created, comment_added/edited/deleted, status_changed, time_logged/edited/deleted, task_archived/unarchived, assignee_changed) · detail (small structured note: from→to status, old→new assignee, hours) · created_at

The activity log is shown two ways: per task (that task's history, visible to everyone) and per project (all activity across the project's tasks, admin-only — see Part 8).

notifications

id · user_id → users (recipient) · type (matches the events in Part 3c) · task_id → tasks · is_read · created_at

notification_settings

id · user_id → users · one on/off flag per configurable email event: email_task_assigned, email_task_created_by_other, email_new_comment, email_status_changed, email_time_logged, email_mention

System configuration (not a table)

System-wide configuration lives in a PHP config file on the server, not in the database: the JWT lifetime (in hours) and SMTP credentials. This keeps secrets out of the database.

Notes and open points

  • Following a task is derived, not stored: a user follows a task if they are the admin, the current assignee, the reporter, or have at least one comment on it. A task_followers table can be added later if performance needs it.
  • Deactivated users are kept (never deleted); their tasks and history remain, shown slightly transparent on the board.
  • Archiving is a flag on tasks and projects; nothing is ever hard-deleted. Archiving a project cascades to its tasks (Part 6/7).
  • Reassignment changes tasks.assignee_id and writes an activity_logs row; prior time logs keep their original user_id, so history stays accurate.

← Part 4 · Table of Contents →

7User Journeys

7.1docs/05-user-journeys.md

Part 5 — Main User Journeys

← Table of Contents · ← Part 4

The important flows from beginning to end. For each, we say what the user does, what the system does behind the scenes, and what they see at the end. Journeys 7–12 continue in Part 5a.


Journey 1 — Admin creates an account and the user's first login

User does (admin): Users → "Add user". Enters full name and email. Confirms. System does: Creates a user (role member, is_active true, must_set_password true), generates a random password, stores it hashed, emails the plain password, records the creation. User sees (admin): The new user appears, marked pending first login. User does (new user): Goes to login, enters email + the generated password. System does: Verifies credentials; because must_set_password is true it requires a new password (min. 8 chars) before completing login. User does (new user): Enters and confirms a new password (≥ 8 chars). System does: Stores the new hash, sets must_set_password false, issues a JWT. User sees: The dashboard, logged in.


Journey 2 — Logging in (returning user)

User does: Enters email + password. System does: Verifies credentials and that the account is active. If active, issues a JWT (lifetime from server config); if deactivated, refuses with a clear message. User sees: The dashboard, or an "account is deactivated" message.


Journey 3 — Admin creates a project

User does (admin): Projects → "New project". Enters a name (and optional description). Saves. System does: Creates the project (is_archived false). User sees: The new project appears; it can be selected on the board and receive tasks. (Members can see it but cannot create or edit projects.)


Journey 4 — Creating a task and assigning it

User does: "New task", picks a project, types a title + optional description, chooses a priority, optionally picks or creates a label, sets the assignee (a member can only assign to themselves; the admin to anyone). Confirms. System does: Creates the task (status todo, not archived), sets reporter = creator and assignee = chosen user, links the label if any, writes a task_created activity entry. Notifies the assignee ("assigned to you") and the admin if the creator was not the admin. Emails go where the recipient's settings allow. User sees: The task appears in the To Do column of its project's swimlane. "Create another" keeps project + label, clearing the rest.


Journey 5 — Moving a task across the board

User does: Drags a card between columns. A member can drag only tasks assigned to them; the admin can drag any task. System does: Checks permission, updates status, writes a status_changed activity entry (from → to), notifies the admin and followers per the rules. Emails where allowed. User sees: The card in the new column. Transitions are free between the three columns (exact rules in Part 6).


Journey 6 — Commenting and mentioning someone

User does: Opens a task's detail, types a comment, optionally @Name-mentions a person, posts it. System does: Saves the comment, records mentions, writes a comment_added entry, marks the author as a follower. Notifies: each mentioned user; the task's assignee (if not the commenter); other followers; the admin. Emails go where each recipient's settings allow (members only within their followed scope). User sees: Their comment in the list and the activity log; interested users get a notification.


← Table of Contents · Part 5a — journeys 7–12 →

7.2docs/05a-user-journeys-more.md

Part 5a — Main User Journeys (7–12)

← Part 5

Continuation of the flows from Part 5: time, notifications, archiving, projects, user state, and reports.


Journey 7 — Logging time

User does: On a task assigned to them, "Log time" and enters hours. (The admin can do this on any task.) Confirms. System does: Verifies the logger is the current assignee (or admin), creates a time-log entry (hours, user, automatic date), writes a time_logged entry, notifies the admin, updates report totals. User sees: The new entry in the task's time list and activity log; the total updated. Editing/deleting their own entry is recorded in the activity log too.


Journey 8 — Getting notified

User does: Sees the badge with an unread count; opens notifications. System does: Lists that user's notifications, newest first, with read/unread state. User does: Clicks one (opens the task), or marks one / all as read. System does: Opens the task; updates read state and the counter. User sees: The relevant task and a cleared/reduced badge. Where settings allowed, they also got the matching email.


Journey 9 — Archiving and unarchiving a task

User does: On a task, "Archive". A member can archive only tasks they created; the admin any task. System does: Sets is_archived, writes a task_archived entry; the task leaves the normal columns. User sees: The task disappears from To Do / In Progress / Done; the hidden Archived column shows it. Unarchiving (admin for any; member for their own created tasks) returns it to its previous status column and writes task_unarchived.


Journey 10 — Admin archives a project

User does (admin): On a project, "Archive project". System does: Archives the project and automatically archives all its tasks; those tasks leave the board until the project is reactivated. Records the change. User sees: The project's tasks disappear from the board. Reactivating restores visibility.


Journey 11 — Deactivating and reactivating a user

User does (admin): In Users, "Deactivate" on a person. System does: Sets is_active false; that user can no longer log in. Tasks and history remain; their cards appear slightly transparent. User sees: The user marked inactive, their tasks faded. Reactivating restores login.


Journey 12 — Admin views time reports

User does (admin): Reports. Chooses by project, by task, or by user. System does: Sums logged hours across the grouping; presents the total excluding archived tasks and, in parentheses, the total including archived. User sees (admin only): The hour totals per project / task / user, with the archived-inclusive figure alongside. Members do not see reports.


← Part 5 · Table of Contents →

8Rules & States

8.1docs/06-rules-and-states.md

Part 6 — Rules & States

← Table of Contents · ← Part 5

The logic that keeps the app predictable: what states things can be in, which changes are allowed, and who may do what. This page covers states and the permission summary; notification rules and edge cases are in Part 6a.

Task status

A task has exactly one status at all times: todo · in_progress · done.

Allowed transitions: any status can move to any other — To Do → Done directly is allowed, as is Done → To Do. There is no forced order. Every change is recorded in the activity log (from → to, who, when). A new task always starts in todo.

Archived is a separate state, not a status

Archived is a flag (is_archived), independent of status. A task keeps its status underneath while archived; unarchiving returns it to that status column.

  • Archiving removes the task from To Do / In Progress / Done and places it in the hidden Archived column.
  • Tasks are never deleted. Archive is the only way to set a task aside — the golden rule, for everyone including the admin.
  • Who can archive/unarchive: the admin any task; a member only tasks they created (reporter).
  • Both archiving and unarchiving are recorded in the activity log.

Project state

A project is either active or archived (is_archived):

  • Only the admin creates, edits, or archives a project.
  • Archiving cascades: all of its tasks are automatically archived and leave the board until the project is reactivated.
  • Reactivating restores the project and its tasks; tasks return to the status they held.
  • Projects are never deleted, only archived.

Assignment rules

  • Assignee is required at all times. A task cannot exist without one.
  • On creation: a member must assign to themselves; the admin may assign to anyone.
  • Reassignment is admin-only, and is recorded in the activity log (old → new).
  • The reporter (creator) is fixed and never changes.

Who can edit / move / act

Summary of the enforced rules (full map in Part 2):

  • Edit task details (title, description, priority, label): admin any; member on tasks they created or are assigned to.
  • Move task status: admin any; member only tasks currently assigned to them.
  • Archive/unarchive task: admin any; member only tasks they created.
  • Comment: anyone, on any task. Edit own comment: author only. Delete comment: author (own) or admin (any).
  • Log time: current assignee (while assigned) or admin (any). Edit/delete time entry: only the entry's author.
  • Reports; projects, users, label colors, team email settings: admin only.

Time-log rules

  • An entry stores hours, the user who logged it, and an automatic date. No description.
  • Only the current assignee may add time, and only while assigned. After a reassignment they can no longer add time; existing entries stay and keep counting.
  • The admin may add time to any task.
  • A task's total = the sum of all entries across everyone who ever logged.
  • Editing/deleting is allowed only for the entry's author, and every change is written to the activity log.
  • Hours must be positive; fractional allowed (comma normalized: 1,5 → 1.5); max 8 per entry — add several entries for more.

← Table of Contents · Part 6a — notifications & edge cases →

8.2docs/06a-rules-notifications.md

Part 6a — Rules: following, notifications & edge cases

← Part 6

Continuation of the rulebook from Part 6: how following is derived, the notification matrix, deactivated users, and the edge-case guarantees.

Following a task (derived)

A user is a follower of a task if any of these is true:

  • they are the admin, or
  • they are the task's current assignee, or
  • they are the task's reporter (creator), or
  • they have at least one comment on the task.

Following is computed, not stored. It drives which comment notifications a member may receive.

Notification rules

In-app notifications are always created for the relevant recipients. Email is sent only when the recipient's settings allow that event type.

Admin receives (in-app always; email per admin settings):

  • task assigned (any), task created by someone else, new comment (any), status changed (any), time logged (any), mentioned.
  • The admin is never self-notified for their own actions.

Member receives (in-app always; email per their settings, limited to their followed scope):

  • a task was assigned to them,
  • a new comment on a task assigned to them,
  • a new comment on a task they follow,
  • they were mentioned.

Configurable email events (on/off): task assigned, task created by someone else, new comment, status changed, time logged, mention. In-app is not configurable — always on.

Deactivated users

  • A deactivated user cannot log in.
  • Their data (tasks, comments, time logs, history) is preserved; nothing is deleted.
  • Their task cards appear slightly transparent on the board.
  • The admin can reactivate them later.
  • Their tasks remain assigned to them after deactivation; the admin may reassign if desired.

Edge cases and guarantees

  • Deleting is never possible for tasks, projects, or users. Archive (tasks/projects) or deactivate (users) instead.
  • An archived task is read-only: viewable, but no edits, no status move, no comments, no time logging — until unarchived.
  • Archived project + its tasks: hidden and frozen (read-only) until the project is reactivated.
  • Reassignment preserves history: the old assignee's time logs keep their user_id; the change is logged.
  • First login: a user with must_set_password true cannot use the app until they set a password (≥ 8 chars).
  • JWT expiry: tokens expire after the configured number of hours; an expired token forces re-login.
  • Priority colors are fixed in code (per theme), not admin-editable. Label colors are admin-editable.
  • One label per task; labels are global and shared across projects.

← Part 6 · Table of Contents →

9Architecture & API

9.1docs/07-architecture.md

Part 7 — Architecture & API

← Table of Contents · ← Part 6

This part describes how the pieces fit together and the main choices behind them. Because it is detailed, it is split into two files:

  • This file (7): the big picture — frontend, backend, database, email, security, performance.
  • Part 7a — API Endpoints: the concrete endpoints in plain terms.

The pieces

  • Frontend — Angular. A single-page app in tasks/frontend. It renders the board, task panels, dashboards, settings, and notifications. It talks to the backend only over HTTP (JSON) and holds no database access itself.
  • Backend — plain PHP (no framework). A JSON API in tasks/api, written in clear, well-commented, object-oriented PHP with many small classes and functions, following good practices. It enforces all rules and permissions (the frontend is never trusted for authorization). Chosen deliberately: no framework, so students can read exactly how routing, auth, validation, and database access work.
  • Database — MySQL. Named taskboard, no table prefix, accessed from PHP via PDO with prepared statements (safe against SQL injection). Schema follows Part 4.
  • Email — authenticated SMTP from the backend. All emails (generated passwords, notifications) are sent from PHP through a dedicated SMTP account (credentials in the server config file, not the database). The Mailer speaks SMTP directly over a socket (implicit TLS on 465 / STARTTLS on 587, AUTH LOGIN) because the host blocks PHP's mail(). Emails are sent as multipart/alternative (plain text + a styled HTML layout) and include a clickable link built from app.url (e.g. /login for the welcome email, /tasks/{id} for notifications). On success it also best-effort appends the message to the account's IMAP Sent folder (port 993/SSL) so sent mail is visible in webmail (Roundcube); this needs the PHP IMAP extension and never fails the send. Deliverability (anti-spam) is a DNS concern, not code: the sending domain needs SPF, DKIM, and DMARC records (configured in the hosting panel) or mail from a brand-new address lands in spam.

How they talk

  1. Angular sends an HTTP request with a JWT in the Authorization: Bearer <token> header.
  2. PHP verifies the token, loads the current user, and checks permission for the action.
  3. PHP validates input, runs the operation against MySQL via PDO, writes any activity-log and notification rows, and triggers emails where settings allow.
  4. PHP returns JSON (data or a clear error with an appropriate HTTP status).
  5. Angular updates the UI.

Same domain in production; different ports locally under MAMP (handled with CORS in development).

Key technical choices and why

  • Plain PHP, OOP, heavily commented. The project doubles as a teaching example; readability beats cleverness. Structure: a small router, controllers per resource, model/repository classes for data, service classes for rules (auth, notifications, reports), and helpers (JWT, validation, email).
  • JWT auth with configurable lifetime. Stateless tokens fit an Angular + PHP split cleanly (no server session store). The token's lifetime in hours lives in the PHP config file so the admin can change it any time.
  • PDO + prepared statements everywhere. Prevents SQL injection and keeps data access uniform.
  • Authorization enforced server-side, on every endpoint. The frontend hides controls a user may not use, but the backend independently checks each request against the Part 2 / Part 6 rules. The UI is a convenience; the API is the gate.
  • Config in a PHP file, not the DB. JWT lifetime and SMTP credentials live in a server config file, keeping secrets out of the database.
  • Soft state, never delete. Archiving/deactivation are flags; the API has no delete operations for tasks, projects, or users.

Security

  • Passwords stored hashed (PHP password_hash / password_verify), never in plain text. Generated initial passwords are emailed once and stored only as a hash.
  • First-login password change enforced server-side via must_set_password.
  • JWT signed with a server secret (in config), verified on every request; expired tokens force re-login.
  • Every endpoint authorizes the action for the current user and role. Members cannot escalate by calling the API directly.
  • Input validation on the server for all writes (types, lengths, allowed values like status/priority, hours ≤ 8, min password length 8).
  • Prepared statements for all queries.
  • CORS locked to the app's origin(s); permissive only in local development.
  • Deactivated accounts cannot obtain or use a token.

Performance (at our small scale)

Realistic size is tiny (a handful of users; tens of projects; tens of tasks each), so the goal is "comfortably fast and simple," not large-scale engineering:

  • Indexes on the columns we filter/join by: tasks.project_id, tasks.assignee_id, tasks.reporter_id, tasks.status, tasks.is_archived, comments.task_id, time_logs.task_id, notifications.user_id + is_read.
  • Board loads fetch only non-archived tasks matching the active filters, with assignee/label joined in one query, so a board render is a couple of queries, not one-per-card.
  • Counts (unread notifications, project/task hour totals) computed with aggregate queries rather than looping in PHP.
  • Pagination where lists can grow (activity log, notifications) even though volumes are small, to keep responses light.
  • Emails sent from the backend as part of the request; if delivery is slow, it is kept simple now (synchronous) and can move to a queue later if ever needed.

← Table of Contents · Next: Part 7a — API Endpoints

9.2docs/07a-api-endpoints.md

Part 7a — API Endpoints

Part 7a — API Endpoints

← Part 7

The main backend endpoints in plain terms. All return JSON. All except login require a valid JWT. Every endpoint checks the caller's role and the Part 6 rules before acting. Paths are indicative and settled during implementation. Base path: /api.

Legend: (admin) = admin only; (self) = acts on the caller's own data; otherwise available to any logged-in user per the rules.

Auth & account

  • POST /auth/login — log in with email + password; returns a JWT (and whether a first-login password change is required).
  • POST /auth/set-password — complete first-login password setup, or change own password (min. 8 chars). (self)
  • POST /auth/logout — client discards the token (server is stateless; provided for symmetry).
  • GET /me — the current user's profile and role.
  • PUT /me — update own full name / email. (self)
  • PUT /me/notification-settings — update own email-notification preferences. (self)

Users

  • GET /users — list all users (everyone can see, for assignment and mentions).
  • POST /users — create a user (generates password, emails it). (admin) (While SMTP is unconfigured, the API runs in dev-mail mode: the email is logged to api/storage/mail/ and the response includes generatedPassword so the admin can relay it.)
  • PUT /users/{id}/deactivate — deactivate an account. (admin)
  • PUT /users/{id}/reactivate — reactivate an account. (admin)

Projects

  • GET /projects — list projects (active by default; option to include archived).
  • POST /projects — create a project. (admin)
  • PUT /projects/{id} — edit name/description. (admin)
  • PUT /projects/{id}/archive — archive project (cascades to its tasks). (admin)
  • PUT /projects/{id}/unarchive — reactivate project and its tasks. (admin)
  • GET /projects/{id}/activity — project-level activity log. (admin)

Tasks

  • GET /tasks — list tasks for the board, with filters: project (one/many), assignee (one/many), label, priority, status, archived (default false), and text search (title/description). Returns cards with assignee, label, project, priority, status.
  • GET /tasks/{id} — full task detail (description, comments, time logs, activity log).
  • GET /tasks/{id}/activity — the task's activity log, newest-first, paginated (limit, offset).
  • POST /tasks — create a task (title, optional description, project, priority, optional label, assignee). Member may assign only to self; admin to anyone. Starts in todo.
  • PUT /tasks/{id} — edit details (title, description, priority, label). Admin any; member on own/assigned. Blocked if the task is archived.
  • PUT /tasks/{id}/status — change status (board move). Admin any; member only if currently assigned. Blocked if archived.
  • PUT /tasks/{id}/assignee — reassign. (admin)
  • PUT /tasks/{id}/archive — archive. Admin any; member only tasks they created.
  • PUT /tasks/{id}/unarchive — unarchive. Admin any; member only tasks they created.

Comments

  • GET /tasks/{id}/comments — list a task's comments.
  • POST /tasks/{id}/comments — add a comment (may include @mentions). Blocked if the task is archived. Triggers notifications.
  • PUT /comments/{id} — edit own comment. (self, author only)
  • DELETE /comments/{id} — delete a comment: author (own) or admin (any). (This "delete" removes a comment only; it is not a task/user delete, which never exist.)

Time logs

  • GET /tasks/{id}/time-logs — list a task's time entries.
  • POST /tasks/{id}/time-logs — add hours (≤ 8, fractional allowed). Only current assignee or admin. Blocked if the task or its project is archived.
  • PUT /time-logs/{id} — edit own entry. (self, author only)
  • DELETE /time-logs/{id} — delete own entry. (self, author only) Recorded in the activity log.

Labels

  • GET /labels — list global labels (with colors).
  • POST /labels — create a label (name; color set by admin). Creatable while making/editing a task by the task's creator or admin.
  • PUT /labels/{id} — edit label name/color. Color changes (admin).

Notifications

  • GET /notifications — the caller's notifications, newest first, with read/unread. (self)
  • GET /notifications/unread-count — the unread badge count. (self)
  • PUT /notifications/{id}/read — mark one as read. (self)
  • PUT /notifications/read-all — mark all as read. (self)

Reports

  • GET /reports/time — time totals grouped by project, task, or user. Returns totals excluding archived, plus the archived-inclusive figure alongside. (admin)

Errors & status codes

  • 200/201 success; 400 invalid input; 401 missing/expired token; 403 not allowed by role/rule; 404 not found; 409 conflict (e.g. acting on an archived task). Error bodies carry a short, clear message (English).

cURL examples (indicative)

# Login
curl -X POST https://app.example/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"office@pixeltime.ro","password":"..."}'

# List board tasks for two projects, one assignee, high priority
curl "https://app.example/api/tasks?project=1,2&assignee=5&priority=high" \
  -H "Authorization: Bearer <JWT>"

← Part 7 · Next: Part 8 — Screens

10Screens

10.1docs/08-screens.md

Part 8 — Screens

← Table of Contents · ← Part 7a

Every screen, what it shows, its main actions, and how you move between them. English-only, desktop and phone. Screens 7–11 and responsive behavior are in Part 8a.

Navigation map

Login ─→ (first login) Set Password ─→ Dashboard / Board
                                          │
   ┌──────────────────────────────────────┼───────────────────────────────┐
   │              │            │           │            │                   │
 Board        Notifications  Search     Profile &     Admin Area        Task views:
 (Kanban)                    results    Settings    (admin only)        - Right panel (peek)
                                                                        - Full page (rich)

A persistent top bar is on every screen after login: app name/home, global search, notifications bell with unread badge, and the user menu (profile, settings, logout). Admins also see an entry to the Admin Area.

1. Login

  • Shows: email + password, sign-in button, error messages (bad credentials, deactivated account).
  • Actions: log in.
  • Goes to: Set Password (if first login), otherwise Dashboard/Board.

2. Set Password (first login / change password)

  • Shows: new password + confirm, the 8-character minimum rule.
  • Actions: save new password.
  • Goes to: Dashboard/Board. (Also reachable later from Settings.)

3. Dashboard / Board (home)

The main working screen — the global Kanban board.

  • Shows: three fixed columns (To Do, In Progress, Done) plus a toggle to reveal the hidden Archived column. With several projects, each is a swimlane. Cards show title, priority color, label color, assignee, project. Deactivated users' cards appear faded.
  • Filters: by project(s), user(s), label, priority; text search feeds in here.
  • Actions: drag cards to change status (members: own assigned only; admin: any); open a card's right panel; open its full page; "New task" (with "create another"); reveal/hide Archived.
  • Goes to: Task right panel, Task full page, New Task dialog.

4. Task — Right Panel (quick peek)

Opens beside the board from a card button, without leaving the board.

  • Shows: the essentials — title, project, status, priority, label, reporter, assignee, description, latest activity, total hours.
  • Actions: a link to open the full page; close to return to the board. (Deeper actions live on the full page.)
  • Goes to: Task full page; back to board.

5. Task — Full Page (rich overview)

The complete view of one task.

  • Shows: everything — full description, all comments (with mentions), the complete time-log list, and the task's activity log.
  • Actions (subject to rules): edit details; comment / edit-own / delete (own, or admin any); log time / edit-own / delete-own; archive/unarchive; admin-only reassign. Archived tasks are read-only (Part 6).
  • Goes to: back to board.

6. New Task dialog

  • Shows: project, title, description, priority, label (pick existing or add new), assignee (member: self only; admin: anyone).
  • Actions: create; "create another" (keeps project and label, clears the rest).
  • Goes to: the board with the new card in To Do.

← Table of Contents · Part 8a — notifications, search, admin →

10.2docs/08a-screens-admin.md

Part 8a — Screens: notifications, search, settings & admin

← Part 8

Continuation of the screen list from Part 8: notifications, search, profile/settings, the admin area, the project page, and responsive behavior.

7. Notifications

  • Shows: the user's notifications, newest first, with read/unread styling and an unread count in the top-bar bell.
  • Actions: open a notification (jumps to the related task), mark one as read, "mark all as read".
  • Goes to: the related Task (panel or full page).

8. Search results

  • Shows: tasks matching a text search on title/description, respecting active filters.
  • Actions: open a result (right panel or full page).
  • Goes to: Task views.

9. Profile & Settings (all users)

  • Shows: own full name and email; change-password section; own email-notification preferences (members limited to projects where they have a task and tasks they follow).
  • Actions: edit profile, change password, toggle email preferences.

10. Admin Area (admin only)

A grouped settings space, only for the admin:

  • Users: list; add user (emails a generated password); deactivate/reactivate.
  • Projects: list; create; edit; archive/unarchive; open a project page with the project-level activity log (admin-only).
  • Labels: global label list; create; edit name; set/change colors.
  • Email notification settings: turn email on/off per event (task assigned, task created by someone else, new comment, status change, time logged, mention). In-app stays always on.
  • Note: JWT lifetime and SMTP live in the server config file, not this UI.

11. Project page (admin only)

  • Shows: the project's details and its project-level activity log (all activity across the project's tasks).
  • Actions: edit project, archive/unarchive.
  • Goes to: the board filtered to this project.

Responsive behavior

  • On phones, the board scrolls horizontally across columns; the right panel becomes a full-width sheet; menus collapse into the top bar. All actions remain reachable.

← Part 8 · Table of Contents →

11Non-Negotiable Basics

11.1docs/09-non-negotiable-basics.md

Part 9 — Non-Negotiable Basics

← Table of Contents · ← Part 8

These are the qualities the app must have regardless of features. They are stated plainly so we never trade them away for speed of delivery. Every part of the build is checked against this list.

Security

  • Real authentication. Every user logs in; every API request (except login) carries a valid JWT. Expired tokens force re-login.
  • Passwords hashed, never stored or logged in plain text. Initial generated passwords are emailed once and kept only as a hash. First login forces a password change (min. 8 characters).
  • Permissions enforced on the server, everywhere. The frontend hides what a user cannot do, but the backend independently authorizes every request against the rules in Part 2 and Part 6. A member cannot escalate by calling the API directly.
  • Safe data access. All queries use PDO prepared statements; no string-built SQL. Input is validated server-side (types, lengths, allowed values, hours ≤ 8).
  • Secrets stay out of the database and the repo. JWT signing secret and SMTP credentials live in the server config file, not in code committed to the repo or in the database.
  • Deactivated accounts cannot obtain or use a token.

Works well on a phone

  • Responsive by default. The board, task right panel, task full page, forms, and settings all adapt to small screens (columns scroll horizontally; the right panel becomes a full-width sheet; menus collapse).
  • Touch-friendly. Drag-and-drop and all actions are usable by touch, with targets large enough to tap.
  • No feature is desktop-only; everything essential is reachable on mobile.

Fast enough, always

  • Comfortable at our scale (a handful of users, tens of projects, tens of tasks each) and still responsive if data grows.
  • Indexed queries on the columns we filter and join by (see Part 7); board loads in a couple of queries, not one per card.
  • Aggregates for counts and totals (unread notifications, report hours) instead of looping in code.
  • Pagination on lists that can grow (notifications, activity log).
  • The UI stays responsive during actions (optimistic where safe, clear loading states otherwise).

Accessible

  • Keyboard usable. All actions reachable without a mouse; a keyboard alternative exists for drag-and-drop status changes.
  • Screen-reader friendly. Meaningful labels on controls, proper roles/landmarks, and status changes announced.
  • Readable by default. Sufficient color contrast; color is never the only signal (priority and labels also carry text, so they are distinguishable without relying on color alone).
  • Focus and errors are visible; forms explain what went wrong in plain English.
  • Note: full accessibility (WCAG) conformance requires manual testing with assistive technologies and expert review; we design to these principles and verify what we can.

Doesn't lose data

  • Nothing is hard-deleted. Tasks and projects are archived; users are deactivated. All history is preserved.
  • Full activity log per task records creation, comments, status changes, time logged/edited/deleted, archiving, and reassignment — with who and when — so changes are traceable and recoverable in meaning even after edits.
  • Reassignment preserves history: prior time logs keep their original author.
  • Validation before writes prevents obviously bad data (empty required fields, invalid status/priority, hours over the limit).
  • Regular database backups on the server (operational responsibility once hosted).

English only

  • All UI text, messages, and emails are in English.

← Table of Contents · Next: Part 10 — Build Plan (to be written)

12Build Plan

12.1docs/10-build-plan.md

Part 10 — Build Plan

← Table of Contents · ← Part 9

The order we actually build TaskBoard in. Each step is small enough to finish and see working on its own, building from the foundations (accounts, projects) up to the richer features (board drag-and-drop, comments, notifications, reports) and finally the deploy. Each step lists what we build and how we know it works ("done when"), plus an As built note recording what actually shipped.

The plan is split by phase so each page stays short and readable:

Status at a glance

PhaseScopeState
0Skeleton, config, schema, seed✅ Done
1Login/JWT, passwords, user management✅ Done
2Projects, tasks core✅ Done
3Board render, drag-and-drop, archive✅ Done
4Task detail, comments, time logs✅ Done
5Reassignment, notifications, email settings✅ Done
6Reports, project activity, non-negotiables✅ Done
7Deploy✅ Prepared (awaiting live credentials)

Notes

  • Documentation stays in sync. As code is built, the relevant .md files are updated in the same change if behavior differs from the plan.
  • Test as we go. Each step has a "done when" we verify before moving on.
  • No deletes, ever — enforced from the schema up.

← Table of Contents · Part 10a — start the plan → · Part 12 — Deploy →

All build steps are complete; the deploy artifacts and guide (Part 12) are ready for the live cut-over once production credentials are provided.

12.2docs/10a-plan-phase0-1.md

Part 10a — Build Plan: Phases 0–1 (foundations, accounts & auth)

← Part 10 index

Phase 0 — Foundations

Step 0.1 — Project skeleton & config. ✅ Done. Create tasks/api (plain PHP, PDO-ready, custom router + PSR-4 autoloader, config file for JWT lifetime + SMTP placeholders) and tasks/frontend (Angular app shell, top bar, routing, design tokens). Done when: both run locally and the frontend can call a GET /api/health returning JSON. ✅ As built: the API runs on PHP's built-in server (php -S localhost:8000 -t api/public) rather than MAMP's Apache, which is simpler for students; MySQL still comes from MAMP in Step 0.2. CORS allows the Angular dev origin on ports 4200 and 4300. See tasks/README.md to run it.

Step 0.2 — Database & seed. ✅ Done. Create the taskboard schema from Part 4 (users, projects, labels, tasks, comments, comment_mentions, time_logs, activity_logs, notifications, notification_settings), with indexes. Seed the admin office@pixeltime.ro with the initial password (hashed) and a couple of labels. Done when: schema loads cleanly and the admin row exists. ✅ As built: api/database/schema.sql (10 tables, InnoDB/utf8mb4, 15 foreign keys, indexes per Part 7) and api/database/seed.sql (admin + its notification settings + labels Bug/Feature; idempotent). Verified against MySQL 8.0.44 on MAMP (port 8889). Initial admin password is ChangeMe-TaskBoard-2026! with must_set_password = 1, so it is replaced on first login (docs Part 5).

Phase 1 — Accounts & auth

Step 1.1 — Login + JWT. ✅ Done. POST /auth/login, JWT issue/verify, server-side auth middleware, login screen. Done when: admin logs in, gets a token, and a protected endpoint accepts it / rejects a missing or expired one. ✅ As built: backend — Database (PDO), Jwt (hand-rolled HS256, expiry from jwt.lifetimeHours), User + UserRepository, AuthService, AuthController (POST /api/auth/login), Auth::requireUser middleware, and a protected GET /api/me. Frontend — AuthService (token + user in localStorage), HTTP interceptor (attaches Bearer; on 401 logs out and routes to /login), route guard, and a login screen.

Test caseExpectedResult
Login with correct admin password200 + token + mustSetPassword + user✅
GET /me with valid / no / expired token200 / 401 / 401✅
Login with wrong password401 (generic message)✅
GET /me after deactivation / login while deactivated401 / 403✅

Step 1.2 — First-login password + change password. ✅ Done. must_set_password flow, POST /auth/set-password (min. 8 chars), Set Password screen; GET/PUT /me. Done when: a seeded/new user is forced to set a password on first login and can later change it. ✅ As built: backend — POST /api/auth/set-password (first-login setup needs no current password; a later change requires and verifies the current one; both enforce the 8-char minimum and matching confirmation, then clear must_set_password and reissue a token) and PUT /api/me (own name/email, unique email → 409). Frontend — a Set Password screen, an authGuard that funnels mustSetPassword users to it, and a Profile & Settings screen.

Test caseExpectedResult
Login as a first-login user200, mustSetPassword: true✅
Set password < 8 chars / mismatch400✅
Valid first-login set200, mustSetPassword: false, new token✅
Change password with no / wrong / correct current password400 / 400 / 200✅
PUT /me with valid / invalid email200 / 400✅

Step 1.3 — User management (admin). ✅ Done. GET /users, POST /users (generate password + email via SMTP), deactivate/reactivate. Admin Users screen. Done when: admin creates a user who receives the email and logs in; deactivation blocks login. ✅ As built: backend — GET /api/users (any authenticated user), POST /api/users, PUT /api/users/{id}/deactivate|reactivate (admin-only). POST generates a strong password, stores it hashed, creates the member (with must_set_password and a default notification-settings row), and emails it. Guards: cannot deactivate yourself or the last active admin. Frontend — an admin Users screen and an adminGuard. Dev-mail fallback: while SMTP is unset the Mailer writes each email to api/storage/mail/*.log and POST /api/users returns the generated password so the admin can pass it on.

Test caseExpectedResult
Admin GET /users / POST /users (valid)200 / 201 (+ dev password)✅
POST /users with duplicate email409✅
New user logs in with the generated password200, mustSetPassword: true✅
Admin deactivates a user → that user logs in200 → 403✅
Admin deactivates self / the last admin409✅
Member calls any admin users endpoint403✅

← Part 10 index · Phase 2–3 →

12.3docs/10b-plan-phase2-3.md

Part 10b — Build Plan: Phases 2–3 (projects, tasks, the board)

← Part 10 index

Phase 2 — Projects & tasks (no board yet)

Step 2.1 — Projects (admin). ✅ Done. CRUD-without-delete: create, edit, archive/unarchive (cascade to tasks). Projects list + project page. Done when: admin manages projects; archiving hides their tasks. ✅ As built: backend — GET /api/projects (any user; ?includeArchived=1), GET /api/projects/{id}, POST/PUT /api/projects, PUT /api/projects/{id}/archive|unarchive (admin-only). Archive/unarchive run in a transaction. A tasks.archived_by_project flag lets a project-unarchive restore exactly the tasks the cascade archived, leaving individually-archived tasks archived. Frontend — an admin Projects screen (create, inline edit, archive/unarchive, show-archived toggle, task counts) and a project page.

Test caseExpectedResult
Admin list / create / edit200 / 201 / 200✅
Create with blank name400✅
Archive a project (active + pre-archived task)active archived (archived_by_project), pre-archived untouched✅
Unarchive the projectcascade-archived task restored; pre-archived stays archived✅
Member views / attempts a write200 / 403✅

Step 2.2 — Tasks core. ✅ Done. Create task (title, description, priority, label, assignee rules), edit details, GET /tasks with filters + search, GET /tasks/{id}. New Task dialog with "create another". Done when: a member creates a self-assigned task; the admin assigns to anyone; listing/filtering/search work. ✅ As built: backend — labels (GET/POST /api/labels) and tasks (GET /api/tasks with project/assignee/label/priority/status/archived + text search; GET/POST /api/tasks; PUT /api/tasks/{id}). Create sets status todo, reporter = creator, writes a task_created activity row, and enforces the assignee rule (member → self only; admin → any active user). PUT /tasks/{id} is a full replace of editable fields. Frontend — a New Task dialog and a Tasks browse view.

Test caseExpectedResult
Admin creates task assigned to a member201 (status todo, reporter = admin)✅
Member creates task for someone else / for self403 / 201✅
Blank title / invalid priority / missing project400 / 400 / 404✅
List + filters + search (title/description)correct subsets✅
Member edits an assigned task / a task that is neither theirs200 / 403✅
Edit a task whose project is archived409 (read-only)✅

Phase 3 — The board

Step 3.1 — Kanban render. ✅ Done. Global board: fixed columns, project swimlanes, cards, deactivated-user transparency, newest-first, filters. Done when: tasks appear in the right columns and filters change what is shown. ✅ As built: a Board (home route /) fetches active tasks and groups them into To Do / In Progress / Done. One project → plain columns; several → a swimlane each (heading + count). Cards carry title, a priority pill, the label chip, the assignee avatar; a deactivated assignee's cards render faded. A filter bar (project/assignee/label/priority) re-queries the API.

Step 3.2 — Drag-and-drop status + rules. ✅ Done. PUT /tasks/{id}/status with permissions, activity log entry, keyboard alternative. Done when: allowed moves persist; disallowed moves are blocked server-side. ✅ As built: PUT /api/tasks/{id}/status validates the status, blocks archived tasks (409), and authorizes the mover (admin any; member only a task assigned to them), writing a status_changed activity row ({from,to}) in a transaction. On the board, cards drag between columns via Angular CDK; drag is disabled where not allowed, with an optimistic update that reverts on error. Note (later change): the per-card "Move to" select (the keyboard alternative) was removed from the UI on request; status changes are now drag-only.

Test caseExpectedResult
Admin moves any task / member moves own assigned200 / 200✅
Member moves a task not assigned to them403✅
Invalid status / archived project / missing task400 / 409 / 404✅
Activity log after movesstatus_changed with correct {from,to}✅

Step 3.3 — Archive/unarchive tasks + Archived column. ✅ Done. PUT /tasks/{id}/archive|unarchive, hidden Archived column toggle, read-only rule for archived tasks. Done when: archiving removes a card from normal columns and it appears under Archived; archived tasks reject edits/comments/time. ✅ As built: PUT /api/tasks/{id}/archive|unarchive — admin any task; a member only a task they created (reporter). A manual archive leaves archived_by_project untouched, so it stays archived even if its project is later unarchived. Status is preserved so unarchiving returns the task to the same column. The board's "Show archived" toggle reveals a 4th Archived column. Note (later change): the archive/unarchive action was moved off the board cards onto the task detail page (/tasks/:id) on request.

Test caseExpectedResult
Member archives own / someone else's task200 / 403✅
Admin archives / unarchives any task200 (status preserved)✅
Re-archive an already-archived task409✅
Edit / move an archived task409✅
Activity logtask_archived then task_unarchived✅

← Phase 0–1 · Phase 4–5 →

12.4docs/10c-plan-phase4-5.md

Part 10c — Build Plan: Phases 4–5 (detail/comments/time, notifications)

← Part 10 index

Phase 4 — Task detail, comments, time

Step 4.1 — Task right panel + full page. ✅ Done. Right panel (quick peek) from the card button; full page (rich overview) with description + activity log. Done when: both open with correct data and respect rules. ✅ As built: GET /api/tasks/{id}/activity (newest-first, paginated, 404 on a missing task) returns each entry with the actor's name and decoded detail. Each board card has an open button that slides in the right panel; the full page at /tasks/:id shows the full description, meta, and the complete activity log, with a read-only badge on archived tasks.

Test caseExpectedResult
GET /tasks/{id}/activity after create + two moves3 entries, newest-first, with {from,to}✅
Activity pagination (?limit=1)1 of 3, total correct✅
Open the right panel / full pageessentials + activity load✅

Step 4.2 — Comments + mentions. ✅ Done. List/add/edit-own/delete (own or admin), @mentions, following derived. Blocked on archived tasks. Done when: comments post, mentions are recorded, permissions hold. ✅ As built: GET/POST /api/tasks/{id}/comments and PUT/DELETE /api/comments/{id}. Anyone reads/adds; only the author edits; author or admin deletes; archived tasks are closed (409). @mentions resolve against active users (longest-match wins) and are stored in comment_mentions. Each change writes a comment_added/edited/deleted activity row; a comments.is_edited flag marks edited comments reliably (DATETIME is only second-precise).

Test caseExpectedResult
Add a comment that @mentions a user201, mention stored✅
Member edits / deletes someone else's comment403 / 403✅
Admin deletes a member's comment (moderation)200✅
Add / edit a comment on an archived task409✅

Step 4.3 — Time logs. ✅ Done. Add (current assignee or admin, ≤ 8h, fractional, comma normalized), edit/delete own, activity entries. Blocked on archived task/project. Done when: entries save with correct rules and totals compute. ✅ As built: GET/POST /api/tasks/{id}/time-logs and PUT/DELETE /api/time-logs/{id}. Only the current assignee or the admin may add; everyone edits/deletes only their own; archived tasks are closed. A shared helper validates hours (positive, fractional, max 8, comma→dot). The task total sums across everyone, so reassignment keeps the earlier assignee's hours counted.

Test caseExpectedResult
Assignee logs "1,5"201, stored as 1.5✅
Admin logs on any task / a non-assignee member logs201 / 403✅
Hours > 8 or ≤ 0400✅
After reassignment: old assignee logs / earlier hours403 / still in total✅
Edit/delete own / someone else's entry200 / 403✅

Phase 5 — Notifications & reassignment

Step 5.1 — Reassignment (admin). ✅ Done. PUT /tasks/{id}/assignee, activity log, notification. Done when: admin reassigns; history and notification recorded. ✅ As built: PUT /api/tasks/{id}/assignee (admin only). New assignee must be active; a no-op reassignment → 409; an archived task → 409. In one transaction it updates the assignee, writes assignee_changed ({from,to} by name), and creates a task_assigned notification (not for the admin assigning to themselves). Prior time logs keep their original author.

Test caseExpectedResult
Admin reassigns A → B200, assignee updated✅
A member attempts reassignment403✅
Reassign to current assignee / a deactivated user / an archived task409 / 409 / 409✅
Old assignee's time logs after reassignmentpreserved and still counted✅

Step 5.2 — Notifications engine. ✅ Done. In-app notifications per Part 6 for assign/create/comment/status/time/mention; unread count, list, mark one/all read. Done when: the right recipients get the right in-app notifications and the badge works. ✅ As built: a central Notifier holds all recipient rules in one place, called from create/status/reassign/comment/time. It never notifies the actor and dedupes; "follower" is derived (admin, assignee, reporter, commenter). Read side: GET /api/notifications (+ unread-count), PUT /api/notifications/{id}/read, PUT /api/notifications/read-all, all caller-scoped. The top-bar bell shows a live unread badge + dropdown.

Event (actor)In-app recipientsResult
Member creates a self-assigned taskadmin: task_created_by_other + task_assigned; creator: none✅
Status change / time loggedadmin: status_changed / time_logged✅
Comment with @mentionmentioned: mention; assignee/followers/admin: new_comment (not the author)✅
Mark one / all; marking another user'scorrect; → 404✅

Step 5.3 — Email notifications + settings. ✅ Done. SMTP send per event; admin team-wide + member limited email settings. Done when: enabling an event emails the right people; disabling stops the email while in-app stays on. ✅ As built: each user has a notification_settings row (six per-event flags). When the Notifier creates an in-app notification it also sends an email only if that recipient's flag for the event is on — so each person's own preferences gate their own email. Send is best-effort (a failure never breaks the request) via the Mailer (dev-log until SMTP is set). GET/PUT /api/me/notification-settings back a six-toggle card on Profile & Settings. In-app stays always-on. Email format (post-deploy): messages are multipart/alternative — a styled HTML layout plus a plain-text fallback — and carry a clickable link built from app.url: the welcome email links to /login and shows the temporary credentials; notification emails deep-link to /tasks/{id}. The host blocks mail(), so the Mailer sends over authenticated SMTP (465/SSL) and appends a copy to the IMAP Sent folder for Roundcube. Landing in spam from a new address is fixed in DNS (SPF ✅ + DKIM ✅ already present on scoaladeweb.ro; add a DMARC record), not in code.

Test caseExpectedResult
Default settings for a new userall six enabled✅
Event flag ON / OFFin-app + email / in-app only✅
GET / PUT own settingsonly the caller's flags✅

← Phase 2–3 · Phase 6–7 →

12.5docs/10d-plan-phase6-7.md

Part 10d — Build Plan: Phases 6–7 (reports, polish, ship)

← Part 10 index

Phase 6 — Reports & polish

Step 6.1 — Time reports (admin). ✅ Done. GET /reports/time by project/task/user, with an archived-inclusive figure in parentheses. Done when: totals match logged hours and the archived figure is shown. ✅ As built: GET /api/reports/time?by=project|task|user (admin only; invalid grouping → 400; default project). Each row carries activeHours (non-archived) and totalHours (incl. archived), via SQL aggregates. The admin Reports screen shows the active figure plus "(N h incl. archived)" where they differ, with a footer total.

Test caseExpectedResult
By project (2h active + 3h archived task)active 2h, total 5h✅
By taskactive 2/2; archived 0/3✅
By useradmin 2/5✅
A member requests the report / invalid grouping403 / 400✅

Step 6.2 — Project activity log (admin). ✅ Done. Project-level activity view. Done when: a project shows all its tasks' activity. ✅ As built: GET /api/projects/{id}/activity (admin only; 404 on a missing project; ?limit/?offset) rolls up every activity entry across the project's tasks, newest-first, each tagged with its task title and actor. The admin project page's placeholder is replaced with that list.

Test caseExpectedResult
Project with two tasks + several eventsall entries, newest-first, with task titles✅
A member requests the project log / missing project403 / 404✅
Pagination (?limit=2)2 of 4, correct total✅

Step 6.3 — Non-negotiables pass. ✅ Done. Responsive/mobile check, accessibility pass, security review, performance check. Done when: the Part 9 checklist is satisfied and verified where possible. ✅ As built: a review-and-harden sweep against Part 9. The only code change needed was adding Escape-to-close to the two overlays (New Task dialog, task right panel); everything else already met the bar.

Part 9 areaCheckedResult
Security — authz on every endpointall controllers call requireUser/requireAdmin except public health + login✅
Security — prepared statementsall SQL bound; dynamic bits whitelisted/placeholder-only✅
Security — secrets in configconfig.php + storage/mail/ git-ignored; none in source✅
Security — live spot-checkno-token → 401; member on admin → 403✅
Performance — indexes / pagination & aggregatesindexes cover filter/join columns; activity + notifications paginate; board one joined query✅
Accessibility — keyboard / labelsdialogs close on Escape + global :focus-visible; icon-only buttons have aria-label; forms use <label>✅
Responsive — mobileboard scrolls horizontally; filters wrap; top bar collapses ≤560px; panel is a full-width sheet✅

Caveat: full WCAG conformance needs manual testing with assistive technologies and expert review.

Phase 7 — Ship

Step 7.1 — Deploy. ✅ Prepared (awaiting live credentials). Configure production (single domain), SMTP credentials, JWT secret/lifetime, database backups. Done when: the app runs on the server, the admin logs in, and a full journey works end to end. As built: the deploy artifacts and guide are in place; the live cut-over runs on the server once credentials are filled into config.php. Target: https://task.scoaladeweb.ro/ (app) + /api (API) on one domain, so the production frontend already calls a relative /api.

  • Production config template api/config/config.production.sample.php — env => 'production', CORS locked to the real origin, placeholders for the DB password, a fresh random jwt.secret, and SMTP.
  • Web-server rules — api/public/.htaccess (front controller + Authorization pass-through), api/.htaccess (blocks config/,src/,database/,storage/), frontend/public/.htaccess (SPA deep-link fallback, ships in the build).
  • Database backups — api/database/backup.sh, a cron-friendly mysqldump wrapper (timestamped .sql.gz, prune by RETENTION_DAYS, env-driven credentials).
  • Deploy guide — docs Part 12 (12a server setup + 12b verify & backups).

Verified locally: production build passes and ships the SPA .htaccess; the config template is valid PHP. Live checks (docs 12b) run on the server once credentials are in.


← Phase 4–5 · Part 10 index →

13Design Guidelines

13.1docs/11-design-guidelines.md

Part 11 — Design Guidelines

← Table of Contents · ← Part 10

This part defines TaskBoard's visual system — the "2027 light theme" taken from the reference dashboards we reviewed. It is the single source of truth for how every screen should look and feel, so the board, cards, forms, and settings all read as one product.

Because it is detailed, it is split into two linked files:

radii, shadows, motion. The raw tokens everything else is built from.

buttons, inputs, cards, pills/badges, avatars, the sidebar, the top bar, and the Kanban card.

The look in one paragraph

A calm, bright interface: a soft off-white background with pure-white cards that float on gentle, diffuse shadows. Corners are generously rounded. A single vivid accent (indigo/violet) carries primary actions and the active navigation state; everything else is a quiet neutral grey. Status and priority appear as soft-tinted pills — a pale colored fill with saturated text — never as color alone. Type is clean and modern (Inter), with bold dark headings over muted grey secondary text. People appear as small rounded avatars.

Principles

  1. Light and airy. Whitespace is a feature. Let content breathe; avoid

dense borders — separate with space and soft shadow instead.

  1. One accent, used sparingly. The accent marks the primary action and the

current location. If everything is accented, nothing is.

  1. Color is never the only signal. Every status/priority also carries text

(and often an icon), so it is readable without color (docs Part 9).

  1. Soft, not flat, not heavy. Rounded corners and low-opacity shadows give a

gentle lift. No hard 1px drop shadows, no harsh outlines.

  1. Consistent tokens. Components reference the tokens in Part 11a; they do

not hard-code colors or sizes. The live tokens are in frontend/src/styles.scss and must match this document.

Where the tokens live

The foundations in Part 11a are already implemented as CSS custom properties in frontend/src/styles.scss (e.g. --color-accent, --radius-lg, --shadow-md). When this document and the stylesheet disagree, treat it as a bug and bring them back in sync in the same change (docs Part 10 — docs stay in sync).


← Table of Contents · Next: Part 11a — Foundations

13.2docs/11a-foundations.md

Part 11a — Design Foundations

← Part 11 index

The raw design tokens. These map one-to-one to the CSS custom properties in frontend/src/styles.scss. Keep the two in sync.

Color

Neutrals

TokenValueUse
--color-bg#f5f6faApp background (soft off-white)
--color-surface#ffffffCards, bars, menus
--color-surface-muted#eef0f6Subtle fills (search, chips, hover)
--color-border#e4e7efHairline borders / dividers
--color-text#1c2130Primary text (soft near-black)
--color-text-muted#727a8cSecondary text, labels

Accent

TokenValueUse
--color-accent#6c5ce7Primary buttons, active nav, focus ring
--color-accent-strong#5a4bd4Hover / pressed accent
--color-accent-soft#ece9fcTinted accent background (active row)

Status & priority tints

Each is a (soft fill, saturated text) pair for pills. Always shown with text.

Meaning (example)Soft fillText
Success — Done / Resolved--color-success-soft #dcfce7--color-success #16a34a
Warning — In Progress--color-warning-soft #fef3c7--color-warning #d97706
Info — To Do / Open--color-info-soft #dbeafe--color-info #2563eb
Danger — High / Blocked--color-danger-soft #ffe4ea--color-danger #e11d48

Priority mapping (fixed in code, docs Part 4/6): Low → info, Medium → warning, High → danger. Status mapping: To Do → info, In Progress → warning, Done → success. Label colors are admin-managed and arbitrary.

Typography

  • Family: Inter, with a system-sans fallback (--font-sans).
  • Base: 15px, line-height 1.4. Weights used: 400, 500, 600, 700.
RoleSizeWeight
Page title (h1)~1.6rem700
Section title (h2)~1.1rem600
Body1rem (15px)400–500
Secondary / meta0.8–0.85rem500, muted color
Pill / badge0.78rem600

Spacing

A 4px base scale (--space-1 … --space-7 = 4, 8, 12, 16, 20, 24, 40px). Use tokens, not raw pixels. Default card padding is --space-6 (24px); gaps between cards are --space-5/--space-6.

Radii

TokenValueUse
--radius-sm8pxSmall controls, inputs
--radius-md12pxButtons
--radius-lg16pxCards
--radius-xl20pxLarge panels / sheets
--radius-pill999pxPills, avatars, icon buttons

Shadows

TokenUse
--shadow-smResting chips / subtle lift
--shadow-mdCards (default)
--shadow-lgRaised elements: dragged card, dialogs, right panel

Shadows are low-opacity and large-blur (soft). Never use hard 1px drop shadows.

Motion

  • --transition: 150ms ease for hover/focus/background changes.
  • Keep motion subtle; respect prefers-reduced-motion by avoiding large,

essential animations.


← Part 11 index · Next: Part 11b — Components

13.3docs/11b-components.md

Part 11b — Components

← Part 11 index · ← Part 11a

How the foundation tokens combine into the UI pieces. Shared base classes .card, .pill, and .btn already exist in frontend/src/styles.scss.

Buttons

  • Primary (.btn--primary): accent fill, white text, --radius-md,

padding ~10/18px. Hover → --color-accent-strong. One primary action per view (e.g. "New task", "Create ticket" in the references).

  • Ghost / secondary (.btn--ghost): transparent fill, --color-border

outline, neutral text; hover fills with --color-surface-muted.

  • All buttons show a visible focus ring (accent, 2px, 2px offset) for keyboard

users (docs Part 9). Minimum touch target ~40px high.

Inputs & forms

  • Fields sit on --color-surface-muted or white with a --color-border edge,

--radius-sm/--radius-md, comfortable padding.

  • Focus: border switches to --color-accent (and/or focus ring). No glow.
  • Labels are above fields in --color-text; helper/error text below. Errors use

--color-danger and plain-English messages (docs Part 9).

  • The top-bar search is a pill (--radius-pill) with a leading search icon.

Cards

  • .card: white surface, --radius-lg, --shadow-md, --space-6 padding,

hairline border. Used for dashboard panels, settings sections, the status card. Group a title row (h2 + optional pill/action) above the content.

Pills / badges

  • .pill + a modifier (--success, --warning, --info, --danger,

--neutral): fully rounded, soft fill, saturated text, 0.78rem/600. Carries text always (status label, priority name) so color is not the only signal.

Avatars

  • Round (--radius-pill), small (28–32px) for inline/card use. Image when

available; otherwise the person's initial on an accent background. Cards for a deactivated user's tasks render at reduced opacity (docs Part 3b/6).

Sidebar navigation (to build)

The references use a left sidebar; TaskBoard adds one as screens grow (docs Part 8). Spec:

  • White surface, right hairline border; app brand at the top.
  • Items are icon + label rows with --radius-md. The active item uses an

--color-accent-soft background with accent text/icon (pill-highlighted).

  • Hover: --color-surface-muted. Collapses behind a menu button on phones.

Top bar

  • 64px tall, white, bottom hairline border. Brand/home at the left, pill search,

then right-aligned: notifications bell (round icon button with a danger-colored unread badge) and the user menu (rounded avatar + name). Implemented in shared/top-bar.

Kanban board & card (to build)

  • Columns: To Do / In Progress / Done as light lanes with a header showing

the column name and a count. Generous gaps; the hidden Archived column is revealed by a button (docs Part 3b).

  • Swimlanes: when multiple projects show, each is a horizontal section with a

project heading.

  • Card: white, --radius-md, --shadow-sm at rest and --shadow-lg while

dragging. Contents: title (600), a priority pill and an optional label chip (label's own color), the project name (muted), and the assignee avatar. Newest-first order; no manual reordering. A dedicated button opens the task right panel (docs Part 8).

  • Drag-and-drop changes status; a keyboard alternative must exist (docs

Part 9). The drop target column shows a subtle --color-accent-soft highlight.

Right panel & dialogs

  • Right panel: a raised surface (--shadow-lg) sliding beside the board;

becomes a full-width sheet on phones (--radius-xl top corners). Dialogs (New Task) are centered cards with a dimmed backdrop. Both trap focus and close on Escape.

Icons

  • Icons use Lucide (outline, MIT-licensed) via lucide-angular, rendered as

inline SVG that bundles with the app — no network request, cached with the JS.

  • Only the icons in use are registered (LucideAngularModule.pick) in

app.config.ts, keeping the bundle small. Icons draw with currentColor and take a size attribute.

  • In use: add (plus), edit (pencil), delete (trash-2), archive/unarchive

(archive / archive-restore), expand a card (maximize-2), notifications (bell), close (x), log out (log-out), search (search), brand (square-kanban). Icon-only buttons keep an aria-label + title.


← Part 11 index · Back to Table of Contents

14Deploy

14.1docs/12-deploy.md

Part 12 — Deploy

← Table of Contents · ← Part 11

How TaskBoard goes live on https://task.scoaladeweb.ro/ (the app) with the API under https://task.scoaladeweb.ro/api (docs Part 7 — one domain). This is the final build step (Part 10, Step 7.1).

Parts

config, database load.

automated database backup.

The shape of it

One domain serves two things:

URLServed fromWhat it is
https://task.scoaladeweb.ro/the Angular build (dist/)the app (SPA)
https://task.scoaladeweb.ro/api/...api/public/index.phpthe JSON API

The frontend already knows to call a relative /api in production (src/environments/environment.production.ts), so the production build needs no URL edits. Because the app and API share one origin, requests are same-origin and CORS is effectively a non-issue — the allowlist still names the real origin as a safety net.

What ships vs. what stays secret

  • Ships (committed): all source, the two .htaccess files, the

config.production.sample.php template, the schema.sql/seed.sql, and the backup.sh script.

  • Never ships: api/config/config.php (the real JWT secret, DB and SMTP

credentials) and api/storage/mail/ — both git-ignored. You create config.php on the server by copying the production sample and filling in the values (docs Part 9 — secrets stay out of the repo and the database).

One-line summary of the flow

Build the frontend → upload dist/ to the web root → upload api/ outside or beside it → point /api at api/public → copy + fill config.php → load the schema + seed → verify → schedule backups. The next two pages walk each of these in order.


← Table of Contents · 12a — Server setup →

14.2docs/12a-deploy-server.md

Part 12a — Deploy: server setup

← Part 12

Steps to put the files on the server and wire the two document roots. Commands assume an Apache host (cPanel/Plesk style); adapt paths to yours.

1. Build the frontend (locally)

npm install --prefix frontend
npm run build --prefix frontend

The production bundle lands in frontend/dist/frontend/browser/. It already targets a relative /api (production environment file) and includes the SPA .htaccess (from frontend/public/.htaccess) at the root of that folder.

2. Upload

  • Upload the contents of frontend/dist/frontend/browser/ (including the

hidden .htaccess) into the site document root so index.html sits at https://task.scoaladeweb.ro/.

  • Upload the api/ folder to the server. The preferred layout keeps api/

outside the web root and points a sub-path at it (next step).

3. Point /api at api/public

The API routes expect the request path to start with /api (e.g. /api/health). Two common ways to achieve that:

  • Alias (best): map the URL path /api to the api/public directory, e.g.

an Apache Alias /api "/full/path/api/public" with a <Directory> grant. The api/public/.htaccess then routes everything to index.php.

  • Subdirectory fallback: if you can only drop api/ under the web root,

the API becomes reachable at /api/public/.... The included api/.htaccess still blocks config/, src/, database/, and storage/ from the web. In this layout, serve the app from the root and set the frontend's apiBaseUrl to /api/public before building (one-line edit in environment.production.ts).

Either way, confirm the Authorization header reaches PHP (the API .htaccess forwards it) so Bearer-token auth works.

4. Create the real config

cp api/config/config.production.sample.php api/config/config.php
# then edit api/config/config.php and fill in every «...»:
#  - db.name / db.user / db.pass  (from the hosting panel)
#  - jwt.secret  (generate: php -r "echo bin2hex(random_bytes(32));")
#  - smtp.*      (mail provider credentials)

Keep env => 'production' so errors are not shown to clients.

5. Load the database

Create the database + user in the hosting panel, then load schema and seed:

mysql -h 127.0.0.1 -P 3306 -u <DB_USER> -p <DB_NAME> < api/database/schema.sql
mysql -h 127.0.0.1 -P 3306 -u <DB_USER> -p <DB_NAME> < api/database/seed.sql

The seed creates the admin office@pixeltime.ro with must_set_password = 1, so its password is replaced on first login. Change the seeded admin email first if the live admin differs.


← Part 12 · 12b — Verify & backups →

14.3docs/12b-deploy-verify.md

Part 12b — Deploy: verify & backups

← Part 12a

The go-live checklist and the automated database backup. "Done when" for Step 7.1 (docs Part 10): the app runs on the server, the admin logs in, and a full journey works end to end.

Verify the API

curl https://task.scoaladeweb.ro/api/health

Expected: HTTP 200 with JSON { "status": "ok", "service": "taskboard-api", ... }. If you see an HTML error page instead, the /api document root or the rewrite rules are not yet pointing at api/public.

# Admin login returns a token.
curl -X POST https://task.scoaladeweb.ro/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"office@pixeltime.ro","password":"<seeded-or-changed>"}'

Verify the app

  • Open https://task.scoaladeweb.ro/ — the login screen loads over HTTPS.
  • Hard-refresh a deep link (e.g. /admin/users) — it still loads (SPA

.htaccess fallback working, not a 404).

  • Sign in as admin → set a new password → create a user → create a task → move

it on the board → comment → log time → check the notification bell. This is the Step 7.1 end-to-end journey.

Full go-live checklist

CheckExpected
GET /api/health over HTTPS200 JSON
api/config/config.php exists, env => 'production'errors hidden from clients
jwt.secret is a fresh long random value (not the dev default)tokens cannot be forged
SMTP filled ingenerated-password + notification emails actually send
Fetch /api/config/config.php in a browserblocked (403/404), never the file
Deep-link hard refreshapp loads, no 404
Backups scheduled (below)daily dump present

Database backups

api/database/backup.sh dumps the database to a timestamped .sql.gz and prunes dumps older than RETENTION_DAYS (default 14). It reads credentials from the environment, so no secrets live in the script.

# Manual run:
DB_NAME=<name> DB_USER=<user> DB_PASS=<pass> bash api/database/backup.sh

# Daily cron at 03:15 (keep 14 days), credentials from a protected env file:
15 3 * * * set -a; . /home/you/taskboard.env; set +a; \
  bash /home/you/taskboard/api/database/backup.sh >> /home/you/taskboard-backup.log 2>&1

Restore a dump with:

gunzip < taskboard_YYYY-MM-DD_HHMMSS.sql.gz | \
  mysql -h 127.0.0.1 -u <DB_USER> -p <DB_NAME>

← Part 12a · ← Table of Contents

15Build & Infrastructure

15.1_deploy/connection-details.md

TaskBoard — deploy connection details (DO NOT COMMIT)

type: deploy-notes project: _devschool/projects/tasks target: https://task.scoaladeweb.ro/ (app) + /api (API)

This file holds live secrets. It lives under _deploy/ which is git-ignored. Never commit it or paste its contents into the repo.

FTP (explicit FTPS, port 21)

  • host: ftp.scoaladeweb.ro
  • user: files@scoaladeweb.ro
  • pass: Ti2nu7242.
  • port: 21

Database (MySQL)

  • host: 127.0.0.1 : 3306
  • db_name: scoaladeweb_taskschool
  • db_user: scoaladeweb_taskschool
  • db_pass: Ti2nu7242.
  • panel user: scoaladeweb_school

Mail / SMTP (task notifications sender)

  • account: task@scoaladeweb.ro
  • pass: Ti2nu7242.
  • incoming: mail.scoaladeweb.ro — IMAP 993 / POP3 995
  • outgoing: mail.scoaladeweb.ro — SMTP 465 (SSL), auth required

Notes

  • mail() is blocked on the host → Mailer uses direct authenticated SMTP (465/SSL)

and appends sent messages to the IMAP Sent folder for Roundcube visibility.

  • JWT secret (in api/config/config.php): fresh 64-char value, generated at deploy.
15.2frontend/README.md

Frontend

This project was generated using Angular CLI version 20.3.5.

Development server

To start a local development server, run:

ng serve

Once the server is running, open your browser and navigate to http://localhost:4200/. The application will automatically reload whenever you modify any of the source files.

Code scaffolding

Angular CLI includes powerful code scaffolding tools. To generate a new component, run:

ng generate component component-name

For a complete list of available schematics (such as components, directives, or pipes), run:

ng generate --help

Building

To build the project run:

ng build

This will compile your project and store the build artifacts in the dist/ directory. By default, the production build optimizes your application for performance and speed.

Running unit tests

To execute unit tests with the Karma test runner, use the following command:

ng test

Running end-to-end tests

For end-to-end (e2e) testing, run:

ng e2e

Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.

Additional Resources

For more information on using the Angular CLI, including detailed command references, visit the Angular CLI Overview and Command Reference page.