Feature #40
closedImplement parent-child ticket relationships in ticketing module
100%
## Spec — Parent/child ticket relationships (ticketing module)
Frontend-only. The board payload already returns every task with parent_id + status (TaskSummary), so parent→children grouping is derived **client-side** (Map<parentId, TaskSummary[]>) — **no backend change**.
**List view:** a parent task shows an expand/collapse arrow (▶/▼); expanding reveals subtickets indented (title, StatusPill, 1-line preview = subject), sorted by status; clicking a subticket opens its detail + "Back to parent". One level of nesting only.
**Detail view:** if parent_id set → clickable "Parent Ticket" section (status from board) navigating to parent; if it's a parent → collapsible "Subtickets" section (title/status/preview), clicking replaces view + "Back to parent".
**Reuse** existing StatusPill (stageKey/statusLabel) — no new status color logic. i18n keys in en.ts only. Designer appends a design-system section first.
**AC:** expand/reveal indented subtickets; subticket click → detail + back; parent section navigates; subtickets section + replace-view; sort by status; one level; back-to-parent only on subticket; visual match; vitest/tsc/eslint/build green + TDD on the grouping/sort/nav logic. Guard orphan parent_ids (render as top-level).
Description
### Problem
Currently, the ticketing module does not display parent-child relationships between tickets. Users cannot see:
- The parent ticket of a subticket (or vice versa).
- The status of related tickets (parent/subtickets).
- A clear way to navigate between parent and subtickets without losing context.
### Context
Users need to visualize and navigate hierarchical ticket relationships (e.g., epics → subtasks) directly in the interface. This is critical for tracking dependencies and progress in complex projects. The app already tracks ticket statuses and supports basic navigation, but lacks visibility into parent-child links.
### Proposed Behaviour
Implement a hybrid approach (Option 3) to show parent-child relationships:
#### **1. Ticket List View (Board/Table)**
- Tickets with subtickets display a small **expand/collapse arrow (▶/▼)** next to their title.
- Clicking the arrow **expands subtickets indented under the parent**, showing:
- Title (e.g., "Subticket #124: Fix login bug").
- Status (e.g., "In Review") in a colored tag.
- A 1-line preview of the ticket content.
- Clicking a subticket **opens its detail view** (replacing the current view) with a "Back to parent" button.
#### **2. Ticket Detail View (Single Ticket)**
- If the ticket has a **parent**, show a "Parent Ticket" section at the top with:
- A clickable link to the parent (e.g., "Parent: [Ticket #123] – In Development").
- Clicking the link navigates to the parent’s detail view.
- If the ticket has **subtickets**, show a collapsible "Subtickets" section below the main details, listing:
- Title, status, and preview for each subticket.
- Clicking a subticket **replaces the current view** with its details (with a "Back to parent" button).
#### **3. Sorting and Nesting**
- Subtickets are **sorted by status** (e.g., "In Development" first).
- **One-level nesting only**: Subtickets of subtickets are not displayed (to avoid complexity).
#### **4. Visual Design**
- Use existing app styling for expandable arrows, status tags, and section borders.
- "Back to parent" button appears only when viewing a subticket.
## Acceptance criteria
- [ ] **Ticket List View**: Tickets with subtickets show an expandable arrow. Expanding reveals indented subtickets with title, status, and preview.
- [ ] **Ticket List View**: Clicking a subticket opens its detail view with a "Back to parent" button.
- [ ] **Ticket Detail View**: Parent tickets display a clickable "Parent Ticket" section (if parent exists).
- [ ] **Ticket Detail View**: Subtickets display in a collapsible section with title, status, and preview (if subtickets exist).
- [ ] **Ticket Detail View**: Clicking a subticket replaces the view with its details and shows a "Back to parent" button.
- [ ] **Sorting**: Subtickets are sorted by status (e.g., "In Development" first).
- [ ] **Nesting**: Only one level of subtickets is shown (no nested subtickets of subtickets).
- [ ] **Visual Design**: Expandable arrows, status tags, and sections match the app’s existing styling.
- [ ] **Navigation**: "Back to parent" button returns users to the parent ticket’s detail view.
Files
RA Updated by Redmine Admin about 2 months ago
- Status changed from Submitted to Spec
- % Done changed from 0 to 100
- spec_ref updated (diff)
**Spec rédigée + validée (frontend-only, pas de découpe).** Aucun changement backend nécessaire — le board expose déjà `parent_id` + statut par tâche ; le groupement parent→enfants se dérive côté client. `spec_ref` rempli + `done_ratio=100` ⇒ **prêt pour le dev** (cible : agent `frontend-dev`). Reste en `Spec`.
RA Updated by Redmine Admin about 2 months ago
- Status changed from Spec to In development
RA Updated by Redmine Admin about 2 months ago
- Status changed from In development to QA
- branch set to feat/40-parent-child-tickets
- pr_url set to https://github.com/omdev-tech/PipeLiner-Client/pull/22
Implemented frontend-only parent/child ticket relationships and opened a PR to `dev`.
**PR:** https://github.com/omdev-tech/PipeLiner-Client/pull/22
**Branch:** `feat/40-parent-child-tickets`
What shipped:
- Hierarchy derived client-side from each task's `parent_id` (`src/features/tasks/hierarchy.ts`) — no backend change. Sort by status (in_development first), one level of nesting, orphan `parent_id` guard (renders as a top-level row).
- List view: parent rows get an expand/collapse chevron revealing indented subtickets (title, StatusPill, 1-line preview); subticket click opens its detail.
- Detail view: clickable "Parent ticket" section (navigates + back-to-parent) and a collapsible "Subtickets" section with click-to-open.
- Reuses the existing TaskStatusPill (`stageKey`/`statusLabel`) — no new status color logic. New i18n keys in `en.ts` only. TDD on the grouping/sort/navigation logic.
Gate (all green): vitest 421 passed · tsc clean · eslint clean · build succeeds (the `/admin` ENVIRONMENT_FALLBACK prerender log is pre-existing/non-fatal).
Moving to QA.
RA Updated by Redmine Admin about 2 months ago
- Status changed from QA to In development
Code review found issues requiring dev work (duplicate subticket preview, triplicated stage order, i18n plural, detail parent fallback, defensive dedup, render-level one-level test). Moving back to In development to apply fixes on PR #22.
RA Updated by Redmine Admin about 2 months ago
- Status changed from In development to QA
Review fixes applied and pushed to PR #22 (commit 0a59ec3). Back to QA.
Fixes:
1. **Duplicate subticket preview (bug)** — the showPreview line re-rendered the subject verbatim (TaskSummary has no description). Replaced with the status label as distinct secondary info in `TaskRow.tsx` and the subtickets list in `TaskDetailPane.tsx`.
2. **Triplicated stage order (maintainability)** — `hierarchy.ts` STATUS_SORT_ORDER is now derived from the canonical `STAGE_ORDER` (`overview/kpis.ts`, now exported) via `stageKey`, hoisting `in_development` to the front. No third hand-maintained list; a future status insertion (e.g. Design=15) sorts correctly for free. `sortByStatus` behaviour unchanged (in_development first, stable).
3. **i18n plural (bug)** — `tasks.hierarchy.expandSubtickets` now uses ICU plural: count=1 reads "Show 1 subticket", not "Show 1 subtickets". Key added to en.ts only.
4. **Detail parent fallback (edge)** — a deep-linked subticket not on the loaded board now falls back to `detail.parent_id` so "Back to parent" still appears (resolving parent data from the board when available, else a minimal id-only summary).
5. **Defensive dedup (safety)** — `buildTaskHierarchy` dedups by `issue_id` before grouping so a board returning the same issue_id in two stage groups can't produce duplicate child rows / React duplicate-key warnings (mirrors the Kuma duplicate-series gotcha).
7. **Render-level one-level test (coverage)** — added a `TaskStageGroup.test.tsx` test asserting a child that is itself a parent renders with NO expand chevron at depth=1 (one-level guarantee enforced at render).
Tests updated/added (duplicate-preview assertions, plural string, dedup unit test, deep-link parent-fallback page test). Did NOT touch stage-header count semantics or detail one-level behaviour (team design decisions).
Gate green: vitest 424 passed, tsc clean, eslint clean, build OK (the /admin ENVIRONMENT_FALLBACK prerender log is the documented pre-existing non-fatal one).
RA Updated by Redmine Admin about 2 months ago
RA Updated by Redmine Admin about 2 months ago
**QA — VERDICT: PASS** ✅ (manual QA smoke test)
Branch `feat/40-parent-child-tickets` @ `0a59ec3` (PR #22 → dev). Frontend-only.
**Smoke strategy:** a live authed screenshot was attempted via the gstack browse binary; the login page (https://pipeliner.omdev.tech/login) returned HTTP 200 but rendered blank/unauthenticated (no test credentials, flapping auth, no project guaranteed to carry parent-child tickets). Per the QA fallback, the component/render suites that exercise the feature serve as the smoke proof — they render the real `TaskStageGroup` / `TaskRow` / `TaskDetailPane` / ticketing page and assert the exact user-visible behaviour of each AC.
**Frontend gate (project CLAUDE.md):**
- `npx vitest run` → 83 files, **424 tests passed**
- `npx tsc --noEmit` → exit 0 (clean)
- `npx eslint .` → exit 0 (clean)
- `npm run build` → exit 0 (all routes emitted incl. `/projects/[id]/ticketing`; the single `/admin` `ENVIRONMENT_FALLBACK` prerender log is the documented pre-existing non-fatal one)
**AC-relevant suites:** 35 tests passed — `hierarchy.test.ts` (9), `TaskStageGroup.test.tsx` (15), `TaskDetailPane.test.tsx` (9), ticketing `page.test.tsx` (2).
**Acceptance criteria → result:**
- [OK] List View — parent shows expandable arrow; expanding reveals indented subtickets (title/status/preview)
- [OK] List View — clicking a subticket opens its detail view with a "Back to parent" button
- [OK] Detail View — clickable "Parent Ticket" section navigates to the parent (when parent exists)
- [OK] Detail View — collapsible "Subtickets" section with title/status/preview (when subtickets exist)
- [OK] Detail View — clicking a subticket replaces the view and shows "Back to parent"
- [OK] Sorting — subtickets sorted by status ("In Development" first; stable for ties)
- [OK] Nesting — one level only (a child that is itself a parent shows no chevron at depth 1)
- [OK] Visual Design — arrows, status tags (reused StatusPill/Badge) and sections match existing styling
- [OK] Navigation — "Back to parent" returns to the parent's detail view
**Robustness (covered):** orphan `parent_id` renders as a normal top-level row (no crash); dedup of a task appearing in two stage groups; a real child never renders as a standalone row.
**Proof attached:** `qa40-parent-child-tickets-proof.txt` (technical log, filtered of secrets — no env values/tokens/DB URLs/cookies).
All 9 acceptance criteria OK → ticket **stays in QA** (preprod promotion is handled by a separate agent).
RA Updated by Redmine Admin about 2 months ago
- Status changed from QA to Shipped
Shipped to production. Flow dev→preprod→master, all fast-forward (no divergence/loss):
- QA PASS (vitest 424, all 9 AC verified via render suites) — proof attached.
- PR #22 merged to dev; dev build #53 SUCCESS @ 9b5108a.
- dev→preprod (ff 33554b6..9b5108a); preprod build #61 SUCCESS (:preprod images).
- preprod→master (ff); master build #63 SUCCESS — prod gate approved, :preprod→:prod promoted + deployed.
- Prod live: https://pipeliner.omdev.tech HTTP 200, /api/health 200.