feat: deals board by stage, PM dashboard recent activity, What's New management
Summary
Three web UI features, each added beside what is already there (nothing is removed or replaced), plus one API permission fix:
- Deals board (#1660). A pipeline's Deals page gets an opt-in Board view: one column per stage, a card per deal. Drag a card to another column to move the deal to that stage.
- Recent activity (#4848, partial). The Project Management dashboard gets a Recent activity panel: what was created, changed or deleted in the organization, newest first, ten at a time with "Show more".
- What's New management (#3470). Settings > What's New lets a super admin add, edit, delete and restore the entries of the What's New panel and the feature cards on the sign-up page. Until now they could only come from the seed.
- Deals API. Updating and deleting a deal now require
EDIT_SALES_PIPELINES, matching the UI.
Why
- #1660 asks for deals listed by stage, with stages as columns, a card per deal, drag and drop to change a deal's stage, and a loader while the change saves.
- #4848: the last open goal was "User should be able to get all recent activities". This PR adds the panel for it. The illustrated layout and the employee/project filters are not built (see "What's left for #4848").
- #3470 asks where the What's New data comes from and how to add more. The data is the
changelogtable, which had API endpoints but no screen.
Decisions
-
#4848 is "Refs", not "Closes". The recent activity panel is done, but the illustrated dashboard layout and the employee/project filters are not built.
-
Hiding What's New from super admins who aren't the operator is left for a follow-up. On a multi-tenant install they still see the menu item, and the 403 message tells them only the installation's operator can change the entries.
-
The board/list choice lives only in the URL (
?view=board) in this PR. Remembering it per user is a follow-up. -
Deals: updating and deleting require
EDIT_SALES_PIPELINES, matching the UI. Every place in the web app that edits, moves, deletes or restores a deal already requires it:- the Edit and Delete buttons, behind
ngxPermissionsOnly; - dragging on the new board, and the board cards' edit link.
Over REST,
PUT /deals/:id,DELETE /deals/:id,DELETE /deals/:id/softandPUT /deals/:id/recovernow state it. Over GraphQL,updateDeal,deleteDeal,softDeleteDealandrecoverDealstate the same.POST /deals/createDealalready did. Reads keepVIEW_SALES_PIPELINES. - the Edit and Delete buttons, behind
-
The egress audit's UI baseline gains 6 entries for
/pages/settings/whats-new, andBASELINE_REWALKis'true'for this change only. The browser walk now opens the new route and finds the shared footer and sidebar links on it (docs.gauzy.co x4, gauzy.co, ever.co). Each one is already excused on the 318 routes the baseline records, and the page looks up and requests no Ever host. This is the same case and fix as 760b72cbac.check-baseline-rewalk.mjsreports ok, with 6 entries added. The next change setsBASELINE_REWALKback to'false'.
What changed
packages/core (deals API)
DealControlleroverrides the four write routes it inherits fromCrudController, only to stateEDIT_SALES_PIPELINES. Each hands the request to the base route unchanged, so the body rules (for example, the tenant is never taken from the body) and the responses stay the same.DealResolver: the four write fields state the same permission. The resolver spec's parity checks hold the two protocols to the same permission for each operation.
apps/gauzy, pipelines (#1660)
- New standalone
ga-pipeline-deals-board(pipeline-deals/pipeline-deals-board/). Columns follow the stages' order. Each card shows the deal's title, client, probability chip and creator. A deal at a stage added after the page loaded still gets a column, from the stage it was read with. - Dragging a card to another column moves it right away and shows a spinner on it while the deal saves. The save uses the existing
PUT /deals/:id, and the body carries onlystageIdplus the organization and tenant, so stale values for other fields are never written back. If the save fails, the card goes back to its column and the error is shown. Dragging needsEDIT_SALES_PIPELINES; users without it see a read-only board. pipeline-deals.componentgets a Board toggle button, driven by?view=board. While the board shows, the stage filter and the layout selector are hidden. The table and cards views are unchanged and stay the default.- When the list is read again (after the board's moves, or a delete), it stays narrowed to the stage the stage filter shows. Before, a re-read built a new, unfiltered table source while the filter still showed the stage.
- Leaving the board reloads the list once the board's moves have settled, including saves still under way when the board was left. The board reports each move when it starts, with a promise of the save, and
DealBoardMoveskeeps track of them. If every move failed, nothing reloads. - Until those saves settle and the list is read again, the board is held back (the page shows its spinner). Reopening the board meanwhile would build it from deals read before the moves, with the moved card back at its old stage and free to be dragged again while its first save is still under way.
- The board reuses the tasks board's column and card styles and the deals table's probability chip. The chip's level logic moved into a shared
deal-probability.tshelper used by both.
apps/gauzy, dashboard (#4848)
- New standalone
ga-pm-recent-activitypanel, shown next to the Inbox panel (which stays). Each row has an icon for the kind of action, the record's name (tasks and projects link to their pages; deleted records don't link), what was done and by whom, which fields an update changed, and how long ago. - The panel can be narrowed to tasks or projects and refreshed. It has loading, empty and error states with Retry, and "Show more" paging that skips rows a previous page already showed.
- The "how long ago" labels are recomputed every minute while the panel is shown, and for every row whenever a page loads. They are worded by the browser (
Intl.RelativeTimeFormat) in the UI's language ("5 minutes ago", "5分钟前"), with the same thresholds Moment uses, and are worded again when the user switches language.
apps/gauzy, settings (#3470)
- New lazy route
/pages/settings/whats-new(settings/changelog/). Its guard asks the API whether the user isSUPER_ADMINand sends anyone else, or a failed check, back to/pages/settings. - The page has two sections, What's New panel (
isFeature = false) and Sign-up page features (isFeature = true). Each has Add, Edit and Delete. Entries deleted while the page is open are listed with Restore: deletes are soft and restore uses the existing recover endpoint. - The add/edit dialog shows a live preview built with the same
ngx-changelog-entrycard the panel uses. Validation:- Title, icon and text must contain more than spaces.
- The icon must be one the app can draw. The preview shows the default icon while the name is unknown.
- Links must spell out
http://orhttps://and a host. The image may also be one bundled with the app underassets/(as the seeded sign-up features' images are), so those entries can be edited without replacing their picture. - Every text field is capped at 255 characters. The columns are plain
varchars, which MySQL sizes at 255. - Dates are stored at midnight UTC, as the seeded entries are.
- If the API answers 403 (a super admin who is not the installation's operator), a toast says that only the installation's operator can change the entries, and the dialog keeps what was typed.
- While an entry saves, the dialog cannot be closed (Cancel, the close button and Escape wait), so a save is never mistaken for a cancel. A click beside the dialog never closes it, so typed text is not lost.
- Menu: a What's New item in the Settings menu, shown only to super admins.
tools/egress-audit/ui-routes.jsonlists the new/pages/settings/whats-newroute, as the egress audit's route check requires.tools/egress-audit/ui-baseline.jsongains that route's 6 shared footer and sidebar links, copied from/pages/settings/usage-statistics..github/workflows/egress-audit.ymlsetsBASELINE_REWALK: 'true'for this change (see Decisions 5).
packages/ui-core/shared
- The shared What's New card (
ngx-changelog-entry) shows its date in UTC. Entries are dated at midnight UTC, so for readers west of UTC the sidebar, the sign-up page and the preview showed the day before; they now agree with the management table.
packages/ui-core/core
DealsService.updateStage(id, stageId, scope).- New
ActivityLogService.getPage(query)over the existingGET /activity-log. It sends the page asskip(the API reads it as a 1-based page number), always sends the organization, and leaves out filters that are absent. ChangelogService:find()reads without touching the sidebar'schangelogs$.create,update,delete(soft) andrecovercall the existing changelog endpoints.- After each change it re-reads the sidebar's query, so an open What's New panel shows the change. If that re-read fails, the change still counts as saved.
- Overlapping re-reads (two changes in a row) are numbered, so an older answer arriving last never replaces newer entries.
- Settings menu item
settings-whats-new.
packages/ui-core/i18n
- New keys only, added to every language file with no reformatting:
PIPELINE_DEALS_PAGE.BOARD*/LIST_VIEWDASHBOARD_PAGE.PROJECT_MANAGEMENT_VIEW.RECENT_ACTIVITY.*CHANGELOG_MENU.MANAGE.*MENU.WHATS_NEW
- One existing value fixed in
es,fr,ptandzh:CHANGELOG_MENU.GAUZY_FEATURES(the sign-up page's feature list) translated "gauzy" as an adjective ("blurred", "vaporous", "translucent" features); it now names the product. zh.jsonwas merged with the completed Chinese UI (#11593) by JSON structure: this PR's keys sit whereen.jsonhas them, in Chinese.
Flags / settings and defaults
- No new flags, settings, environment variables, database changes or migrations, and no new dependencies.
- The deals board is opt-in through the toggle (
?view=board). The table and cards views stay the default. - The What's New page and menu item are visible to super admins only. Writes are still limited by the API's existing
RoleGuard(SUPER_ADMIN)plusChangelogWriteGuard. - Deals: a role with
VIEW_SALES_PIPELINESbut notEDIT_SALES_PIPELINEScan no longer update, delete, soft-delete or restore deals through the API (REST or GraphQL). The web app never offered those actions to such a role.
Tests
Run from the repo root with Node 26. Angular specs run without NODE_OPTIONS; Nest specs run with NODE_OPTIONS=--experimental-vm-modules.
npx jest -c apps/gauzy/jest.config.ts --rootDir apps/gauzy pipeline-deals deal-probability recent-activity project-management-details pages/settings/changelog project-board
# Test Suites: 12 passed, 12 total — Tests: 114 passed, 114 total
npx jest -c packages/ui-core/jest.config.ts --rootDir packages/ui-core deals.service activity-log.service changelog.service changelog-entry.component
# Test Suites: 4 passed, 4 total — Tests: 14 passed, 14 total
NODE_OPTIONS=--experimental-vm-modules npx jest -c packages/core/jest.config.ts --rootDir packages/core src/lib/deal/ src/lib/pipeline/
# Test Suites: 4 passed, 4 total — Tests: 76 passed, 76 total
New specs:
- Deals board component and utils (including
DealBoardMoves: settling, holding the board, re-reading only after a saved move; columns for stages added since the page loaded), and the deal probability helper. - Deals page: the stage filter survives a re-read of the deals.
DealsService.updateStage.ActivityLogService.- Recent activity component and utils, including the minute clock,
withRelativeTimes, the age thresholds, and switching language. ChangelogService: HTTP calls, sidebar refresh, refresh failure, refusal, overlapping refreshes.- What's New page, entry dialog (blank text, unknown icons, links, bundled images, 403, other failures, double submit, no closing while saving), utils and guard.
ChangelogEntryComponent: the card shows the entry's UTC day to readers west and east of UTC (both cases fail on the old template, on any host).deal.controller.permissions.spec.ts:- Every write route states
EDIT_SALES_PIPELINESbehindPermissionGuard, and the reads keepVIEW_SALES_PIPELINES. - The real
PermissionGuardrefuses update, delete, soft delete and restore to a view-only role (Nest answers 403) and lets an editor through. - The overrides hand requests to the base routes unchanged.
- Every write route states
Updated spec: deal.resolver.spec.ts, where the write fields now carry the edit permission, still matched route by route against the controller.
Re-run as regression checks: project-board, project-management-details, and the pipeline resolver and pipeline service specs.
node --test tools/egress-audit/*.test.mjs: 17 of 21 pass locally, and the 4 that fail need what CI has and this checkout doesn't:
static-hostnamesflags only the git-ignored localpackages/ui-config/src/lib/environments/environment*.tscopies;- the 3
patch-mock-clocktests needtools/egress-audit/node_modules, which isn't installed here.
node tools/egress-audit/check-baseline-rewalk.mjs --base-file <integration's ui-baseline.json>: ok (6 entries added, each a link the baseline already had).
i18n: no duplicate keys in any of the 14 files (json.load with an object_pairs_hook). zh.json follows en.json's key order throughout. In the other files this PR's keys sit where en.json has them; their existing order differences are unchanged.
Type checks:
npx tsc -p apps/gauzy/tsconfig.app.json --noEmitpasses (exit 0).npx tsc -p packages/core/tsconfig.lib.json --noEmitandnpx tsc -p packages/core/tsconfig.spec.json --noEmitboth pass (exit 0).npx tsc -p packages/ui-core/tsconfig.lib.json --noEmitreports no errors outside spec files. Spec files fail there for every existing spec too, because that config has no jest types.npx tsc -p apps/gauzy/tsconfig.spec.json --noEmitreports nothing new in this PR's files. It does print two kinds of errors that already appear hundreds of times across the repo:TS1272on@Input()properties typed with interfaces, andTS2349on theimport * as momentstyle.
eslint on the touched files reports no new problems. The errors it prints are on lines this PR doesn't touch: inferrable boolean types and empty ngOnDestroy in pipeline-deals.component.ts, and an empty ngOnDestroy in base-nav-menu.component.ts.
Codacy shows action_required with an empty summary and 0 new issues on this PR since it targets integration/issue-triage-2026-10-31. The other open PRs into that branch show the same.
Manual verification still needed
The UI hasn't been run in a browser. Please check:
- Deals:
- Board toggle.
- Dragging between columns with and without
EDIT_SALES_PIPELINES. - The spinner and rollback when the save fails (stop the API to test).
- Leaving the board during and after moves reloads the table, and reopening it right away waits for the saves.
- A view-only user gets 403 from
PUT/DELETE /api/deals/:id, and an editor doesn't.
- Project Management dashboard: the Recent activity panel with real activity, the Tasks/Projects filter, Show more, and the labels ageing.
- Settings > What's New, as a super admin:
- Add, edit, delete and restore in both sections.
- The What's New sidebar and the sign-in and sign-up pages show the change.
- On a multi-tenant install, a super admin of a later tenant gets the "only the installation's operator" toast.
What's left for #4848
- Filtering the activity by employee or project.
- The richer dashboard layout shown in the issue's illustration.
Notes and known limits
- The drag and drop has no keyboard alternative, the same as the tasks board. Deals can still change stage from the edit form.
- Cards keep no order within a column (deals have no position field), so dropping only changes the stage.
- What's New lists the newest 20 entries of each kind. That is the public endpoint's cap, and also what the panel and sign-up page show.
- Follow-ups: hiding What's New from super admins who aren't the operator (it needs a small "can I write?" endpoint), and remembering the board/list choice per user.
Risk and rollback
- The web changes are additive. Two shared UI pieces change:
ChangelogService.getAll()now also remembers its query so a change can refresh the sidebar (otherwise unchanged), and the shared What's New card shows its date in UTC. - The deals API change narrows who may update or delete deals to roles with
EDIT_SALES_PIPELINES. Any integration that wrote deals with a view-only role will now get 403. The web app never did this. - The workflow change is a single line,
BASELINE_REWALK: 'true', and it applies only while this change's baseline entries are checked. The next change sets it back. - Roll back by reverting this PR. Nothing in the database to undo.
Closes #1660
Refs #4848
Closes #3470
🤖 Generated with Claude Code