feat: per-project workflow settings, statistics and "my work" home

Per-project settings (owners/admins, «Configuración» tab):
- Statuses with category (TODO, IN_PROGRESS, QA, DONE, CANCELLED), colour and
  "requires reason"; configurable transitions; ticket types (icon, colour) and
  areas. Defaults are the previous fixed values; projects created before keep
  them without migration.
- Values are archived, never deleted: tickets, history and statistics keep
  referring to them. Validated in the domain (ProjectSettingsChangedEvent).
- Releases, board, tracking and "my work" use categories, so custom statuses
  keep working. Status changes carry name/category metadata for history.
- GraphQL: status/type/area are strings (breaking for clients sending enum
  literals); Project.settings, updateProjectSettings, statusCategory,
  myIssues(categories). REST: PUT /projects/{project}/settings, category filters.
- Read model: statusCategory on issues (backfilled at startup).

Statistics («Estadísticas» tab):
- New issue_timeline projection; pure functions compute created vs resolved,
  cumulative flow, cycle/lead time (avg + p85), throughput per person, QA
  pass/fail and release burnup. GraphQL Project.analytics(days),
  Release.burnup; REST /projects/{project}/analytics, /releases/{id}/burnup.
- Web: SVG charts with validated light/dark palette, crosshair tooltip,
  keyboard navigation, legend and data table.

Home "Mi trabajo": my open tickets across projects grouped by category (QA
failures first), tickets waiting for QA, upcoming releases and "Ir a…".

Tests: domain/application specs, e2e scenario for settings and analytics,
facade tests. Docs, READMEs, schema.gql/openapi.json and Word/PDF updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 20:53:45 +02:00
parent 240ea9a539
commit 0e301ad545
143 changed files with 10291 additions and 1052 deletions

View File

@@ -19,6 +19,8 @@ describe('appRoutes', () => {
'issues/:issueKey',
'releases',
'dashboard',
'analytics',
'settings',
'activity',
'members',
]);

View File

@@ -114,6 +114,31 @@ html {
--ts-t-ui: #d6409f;
--ts-t-task: #64748b;
--ts-t-qa: #d97706;
// Palette of the per-project settings (statuses and ticket types pick one).
--ts-c-slate: #64748b;
--ts-c-stone: #8b8b9a;
--ts-c-violet: #a347e1;
--ts-c-blue: #1879bf;
--ts-c-sky: #0284c7;
--ts-c-teal: #0f766e;
--ts-c-green: #15803d;
--ts-c-amber: #b7791f;
--ts-c-orange: #ea580c;
--ts-c-red: #dc2626;
--ts-c-pink: #d6409f;
// Charts (validated with the dataviz checks against --ts-surface, light and
// dark): two categorical slots, an ordinal ramp for flow stages and the
// reserved good/critical status colours.
--ts-viz-1: #2a78d6;
--ts-viz-2: #eb6834;
--ts-viz-ramp-1: #86b6ef;
--ts-viz-ramp-2: #5598e7;
--ts-viz-ramp-3: #256abf;
--ts-viz-ramp-4: #104281;
--ts-viz-good: #0ca30c;
--ts-viz-critical: #d03b3b;
}
html,
@@ -316,6 +341,19 @@ h3 {
}
}
/* Text for screen readers only (same helper as pireon.dev). */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.ts-prewrap {
white-space: pre-wrap;
}
@@ -501,6 +539,25 @@ html.dark-mode .pi-shell {
--ts-t-task: #94a3b8;
--ts-t-qa: #fbbf24;
--ts-c-slate: #94a3b8;
--ts-c-stone: #8a91a4;
--ts-c-violet: #ae5de5;
--ts-c-blue: #339be6;
--ts-c-sky: #38bdf8;
--ts-c-teal: #2dd4bf;
--ts-c-green: #34c98a;
--ts-c-amber: #eab308;
--ts-c-orange: #fb923c;
--ts-c-red: #f07070;
--ts-c-pink: #f472b6;
--ts-viz-1: #3987e5;
--ts-viz-2: #d95926;
--ts-viz-ramp-1: #184f95;
--ts-viz-ramp-2: #2a78d6;
--ts-viz-ramp-3: #6da7ec;
--ts-viz-ramp-4: #b7d3f6;
--pi-color-grey-10: #12141a;
--pi-color-grey-25: #161922;
--pi-color-grey-50: #1a1e28;