Files
forge/CHANGELOG.md

166 lines
9.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
All notable changes to **FORGE** are documented here.
Format based on [Keep a Changelog](https://keepachangelog.com/); this project uses
[Semantic Versioning](https://semver.org/).
## [2.3.2] — 2026-08-29
### Changed
- **PM Insights — full-width category items.** Inside **Alert categories** and
**Watch list**, each category is now a **full-width accordion** stacked in a single
column (was a narrow multi-column grid), with more breathing room: larger inter-card
gap, roomier card headers and ticket rows, and a taller expanded list. The click-to-
expand behaviour per category is unchanged. (CSS-only: the `.grid` becomes a flex
column; `.cardHead`/`.row` padding widened.)
## [2.3.1] — 2026-08-29
### Changed
- **PM Insights — collapsible sections.** The **Alert categories** and **Watch list**
sections are now accordions: each heading is a button (chevron + a category-count
badge, `aria-expanded`/`aria-controls`) that collapses/expands its card grid. Both
default to open; the per-card ticket expand and the **Per-PM roll-up** table are
unchanged. Adds a `ChevronIcon` (rotates with the section) and a `.grid[hidden]` rule
so a collapsed grid actually hides.
## [2.3.0] — 2026-08-27
### Added
- **Jira status durations (chart #17)** — a new "Jira status durations (avg days)"
chart on the Active-stats page: average time each Jira workflow status is held,
in the board's column order, min-2-tickets. Server endpoint
`GET /api/analytics/jira-durations` (PM+) aggregates `jira.statusDurations` (ms)
across tickets.
- The extension's Jira collector now fetches each issue's **changelog**
(`expand=changelog`) and computes **`statusDurations`** (time per status) +
**`movements`** (`{at, who, from, to}`), sent inside the `jira` object.
- Seed enrichment: `statusDurations` + the Jira **workflow column order** are ingested
from the dump's `jira_board_state` (so the chart is populated before any live sync —
73 seeded tickets carry durations). `JiraInfo` type extended with `statusDurations`/`movements`.
## [2.2.0] — 2026-08-27
### Added
- **Jira sync in the extension** (per the sync ADR). One "Sync now" now refreshes
**SNOW then Jira**: SNOW stays as the in-page same-origin session collector →
`/api/sync`; Jira is pulled by a new service-worker collector via **direct REST**
(`/rest/agile/1.0/board/{id}/issue`) authenticated with a **Jira API token**
Basic (email+token, Cloud) or **Bearer PAT** (Server/DC, e.g. `support.dataart.com`).
Issue links are built as `<baseUrl>/browse/<KEY>` (custom Jira domains supported).
- New extension options: Jira base URL, email, API token/PAT, and board id(s).
- New server endpoint **`POST /api/sync/jira`** — **attach-only**: updates only the
`jira` JSONB (merged) + `jira_key` on existing tickets, keyed by RITM number; never
touches status/state/assignee/activity (avoids the ticket-upsert clobber). Token-authed.
Jira RITM↔issue resolved via `customfield_26001` or a `RITM\d+` regex; Jira failures are
best-effort and never fail a successful SNOW sync.
### Added
- **Polished Excel export** — the board's Excel export is now filterable and readable:
the **ticket Number is a clickable ServiceNow hyperlink** (a real OOXML hyperlink
relationship — locale-proof, unlike a `HYPERLINK()` formula which breaks in `;`-locale
Excel), **auto-filter** on every column, **zebra-striped rows**, taller rows + wider
columns, a frozen indigo header, right-aligned numbers, `#,##0` cost, short dates, and
a **dated filename** (`forge-active-tickets-YYYY-MM-DD.xlsx`). The standalone Link column
was folded into the Number cell. (auto-filter + hyperlinks injected via `fflate`.)
- App **favicon** (indigo snowflake) + `theme-color`.
- A proper **Excel-brand button icon** (green tile) replacing the placeholder glyph.
### Changed
- RITM number links out to ServiceNow in the ticket table + board cards (new tab).
- Deploy: ported Husky's `push-to-nas.sh` + on-NAS `deploy.sh` (SSH key pinning,
openrsync→tar fallback, Synology PATH/sudo, health poll, `forge-db` volume preserved
on `--fresh`); macOS AppleDouble (`._*`) files excluded from sync/build/tests.
### Dependencies
- Added `fflate` (xlsx auto-filter post-processing). `write-excel-file` pinned at 2.x
(v4 drops the default export + still has no hyperlink support).
## [2.0.0] — 2026-08-27 · **FORGE 2.0**
The analytics release. FORGE grows from a ticket board into a full ServiceNow
analytics portal ported from the *Let it Snow* Chrome extension, with role-based
access, admin tooling, and the complete statistics suite.
### Added
- **Full analytics data ingest.** The seed now loads the entire `analytics_data`
master (954 ticket records) merged with the operational cache, plus SLA norms,
FX rates, size thresholds, brand palette, and the colleague roster. New ticket
columns (all additive): `currency_code, opened_by, opened_date, closed_date,
to_do_at, in_uat_at, jira_key, ticket_year, size, po_number, invoiced`. A
reproducible `dump-to-archives``reseed` pipeline regenerates seed data from a
storage dump. `app_config` key/value table for norms/thresholds/palettes.
- **Server analytics engine** (`server/analytics.ts`, `server/insights.ts`):
`/api/analytics/overview`, `/api/analytics/sla`, `/api/insights`, `/api/config`.
- **Overall statistics** page — year-overlay grouped bars (opened/closed/revenue,
bars↔line + YoY%), a Cost↔Tickets toggle, a ticket-share donut (#/% + top-N),
brand→market and requester→brand drill-downs, KPI tiles, lifetime histogram.
- **Active statistics** page — by-status, age distribution, opened-per-month,
median days-in-status, by-brand.
- **PM KPIs — SLA** page — six PM×size heatmaps (TTFR, PM response, avg-close,
OTD, assign, preview) scored against per-size norms, green/red vs target.
- **PM Insights** page — problem/alert KPIs over the active backlog (unassigned,
on-hold, WIP-stalled, customer-replied, awaiting, lifetime, inactive), Jira-breach
and client-owed rules, waiting-PO with L1/L2/L3 escalation, revenue-at-risk, and
a per-PM roll-up. (Rev-at-risk matches the initial app's own figure within rounding.)
- **Board redesign** — 7-state kanban (Unassigned → Open/Assigned → On Hold → WIP →
Customer replied → Awaiting) with the WIP→"Customer replied" pseudo-status, SLA
subtitles, per-column counts, brand/market/PM filters, board/list toggle, and a
ticket detail modal.
- **Role-based access control** — four roles (Viewer, PM, Project Leadership, Admin)
enforced server-side per route; the client hides nav/pages above a user's role.
Only admins can create admins.
- **Admin page** — user management (Project Leadership+) and API-token management
(Admin only): create/list/revoke `fg_…` sync tokens from the UI.
- **Filtered Excel export** — the board's ⭳ Excel button exports the currently
filtered tickets as a styled `.xlsx` (branded indigo frozen header, per-column
widths, right-aligned numbers, `#,##0` cost, short dates, grid borders).
- **Finance ingest** — cost + PO + invoiced per ticket from the finance sheet,
enriching active tickets with cost/size and powering waiting-PO.
- Config bundle exposed via `/api/config` (brand colours, colleagues, norms, FX).
### Changed
- All analytics pages are **full-width** (removed the 1280px cap).
- `/api/tokens` is now gated by the **admin session role** instead of an `ADMIN_KEY`
header. The `mint-token` CLI is unchanged.
- Non-destructive sync upsert: a thin sync no longer nulls seeded-rich fields
(`COALESCE`/`CASE` preserve brand/market/description/jira/activity/analytics).
### Fixed
- `due_date` timezone off-by-one (emit local `YYYY-MM-DD`, not `toISOString()`).
- Activity render-crash vector (ingest sanitizes activity entries; `parseDate` guards).
- Seed "active wins" ordering on tickets present in both archives.
- Ticket-share donut used a truncated distribution (center 375); now the full 966.
### Security
- Read-API authentication (session, username+password, bcrypt, Postgres session store).
- Least-privilege database role (`forge_app`, not a superuser) owns the app tables.
- API tokens: `fg_<id>_<secret>`, only the SHA-256 stored, constant-time verify,
revocable; `token_hash` never leaked by the list endpoint.
- RBAC hardening (post-audit): only an admin can delete an admin; the **last admin**
cannot be deleted (lockout guard); deleting a user **purges their sessions**; the
session id is **regenerated on login** (anti session-fixation); delete of a missing
user returns 404.
## [1.0.0] — 2026-08-27
Initial full-stack scaffold, ported from the *Let it Snow* Chrome extension onto the
*Husky repo list* template.
### Added
- Vite + React 18 + TypeScript client (SCSS modules, react-router) with a Sidebar/
TopBar layout; Active board, Closed, and basic Statistics pages.
- Express + TypeScript server on Postgres (`forge`); self-bootstrapping schema
(`initDB`) that seeds bundled ticket archives on first boot.
- REST API: `/api/tickets`, `/api/tickets/:number`, `/api/stats`, `/healthz`, and a
token-authed `/api/sync` ingest.
- Chrome sync extension ("FORGE Snow Sync") — reads active RITM tickets from the
ServiceNow Table API and pushes them to the portal with a bearer token.
- Multi-stage Dockerfile + docker-compose (bundled Postgres) targeting
`forge.mycloud.dp.ua`; README + SETUP docs.
[2.0.0]: #200--2026-08-27--forge-20
[1.0.0]: #100--2026-08-27