feat(widgets): business-only Marketing home; omit widgets without read #27

Merged
KofTwentyTwo merged 1 commits from feature/marketing-business-home into develop 2026-10-16 15:19:33 +00:00
KofTwentyTwo commented 2026-10-16 14:12:43 +00:00 (Migrated from github.com)

Summary

Wave 0 item W0-8. The Marketing home is now business content only, and widgets are omitted (not locked) for users without read.

  • Configuration Status and Agent Activity are off the home. They stay defined, gated on marketing.admin.read (HIDDEN), and are exposed as the public MarketingWidgets.ADMIN_WIDGETS for a host to place on its Admin area.
  • Needs Attention gets one row per active channel that cannot send (transport not registered or not operational/test-only): "Sends on are paused - ask an admin", linking to the channel record. Nothing when every channel can send. No provider detail appears on the business page.
  • Every widget (home and the two record widgets) now requires marketing.read, HIDDEN, through a new MarketingPermissionChecker (QQQ widget rules can only express <base>.hasAccess, so a custom checker is the only way to require a read permission; same pattern as qbit-alerts). Base.render no longer emits "You need marketing.read ..."; it throws permission denied as defense in depth.
  • Agent Activity judgement: the "drafts/findings created" outcome column is arguably business content, but the same rows carry model/tool calls and tokens, and failed runs already surface in Needs Attention. I moved the whole widget; if a business-facing "agent prepared N drafts" is wanted, it should be a new small widget or KPI tile (audit item 2).

Rubric item moved

A1 0 -> 1 and A8 0 -> 1 on Marketing (marketing)

Audit rows fixed

Marketing (marketing) - Content app home:

  • A1: "marketing_configuration_status lists integrations with status/detail (provider configuration, A-0); marketing_agent_activity reports model/tool calls and tokens (system usage)..."
  • A8: "Locked shell: Base.render returns "You need marketing.read to see marketing data." instead of omitting the widget for users with access but no read"

Tests

./mvnw -o verify: 98 tests, 0 failures (BUILD SUCCESS; guardrails included). New in ComponentsTest: home widget list is exactly Needs Attention + Campaigns (no configuration status, no agent activity); a user with app access but no read gets no permission for any read-requiring widget and render refuses with no "You need" text; a reader gets the home widgets but not the admin ones; admins can still render the moved widgets; the blocked-channel row appears only when a channel is blocked (and the existing starter-data test now expects the row for the starter email channel, whose transport is configured but unverified).

Screenshots

Widget-level preview (unit-test render into standalone HTML, Playwright), before/after at 390, 768, 1024, 1440, 1920, 3200, attached locally: C:/Users/james/AppData/Local/Temp/claude/R--Git-Local-KofTwentyTwo-Praetor/83da0ae2-628c-4edb-9fd7-a93d49e86d27/scratchpad/dashboard-standard/pr-shots/qbit-marketing-business-home (before-<w>.png shows the old home widgets Needs Attention + Agent Activity + Configuration Status; after-<w>.png shows Needs Attention with the blocked channel). Full-page shots happen when kof22 pins the release.

Release

A qbit-marketing release is needed (next RC after 0.1.0-RC.6, the version Website-Backend pins on origin/main). kof22 then bumps qbit-marketing.version and, in a follow-up (not done here), places MarketingWidgets.ADMIN_WIDGETS on an Admin page. Until then the two moved widgets are not shown anywhere.

🤖 Generated with Claude Code

## Summary Wave 0 item W0-8. The Marketing home is now business content only, and widgets are omitted (not locked) for users without read. - **Configuration Status** and **Agent Activity** are off the home. They stay defined, gated on `marketing.admin.read` (HIDDEN), and are exposed as the public `MarketingWidgets.ADMIN_WIDGETS` for a host to place on its Admin area. - **Needs Attention** gets one row per active channel that cannot send (transport not registered or not operational/test-only): "Sends on <channel> are paused - ask an admin", linking to the channel record. Nothing when every channel can send. No provider detail appears on the business page. - Every widget (home and the two record widgets) now requires `marketing.read`, HIDDEN, through a new `MarketingPermissionChecker` (QQQ widget rules can only express `<base>.hasAccess`, so a custom checker is the only way to require a read permission; same pattern as qbit-alerts). `Base.render` no longer emits "You need marketing.read ..."; it throws permission denied as defense in depth. - Agent Activity judgement: the "drafts/findings created" outcome column is arguably business content, but the same rows carry model/tool calls and tokens, and failed runs already surface in Needs Attention. I moved the whole widget; if a business-facing "agent prepared N drafts" is wanted, it should be a new small widget or KPI tile (audit item 2). ## Rubric item moved A1 0 -> 1 and A8 0 -> 1 on Marketing (`marketing`) ## Audit rows fixed Marketing (`marketing`) - Content app home: - A1: "`marketing_configuration_status` lists integrations with status/detail (provider configuration, A-0); `marketing_agent_activity` reports model/tool calls and tokens (system usage)..." - A8: "Locked shell: `Base.render` returns "You need marketing.read to see marketing data." instead of omitting the widget for users with access but no read" ## Tests `./mvnw -o verify`: 98 tests, 0 failures (BUILD SUCCESS; guardrails included). New in `ComponentsTest`: home widget list is exactly Needs Attention + Campaigns (no configuration status, no agent activity); a user with app access but no read gets no permission for any read-requiring widget and render refuses with no "You need" text; a reader gets the home widgets but not the admin ones; admins can still render the moved widgets; the blocked-channel row appears only when a channel is blocked (and the existing starter-data test now expects the row for the starter email channel, whose transport is configured but unverified). ## Screenshots Widget-level preview (unit-test render into standalone HTML, Playwright), before/after at 390, 768, 1024, 1440, 1920, 3200, attached locally: `C:/Users/james/AppData/Local/Temp/claude/R--Git-Local-KofTwentyTwo-Praetor/83da0ae2-628c-4edb-9fd7-a93d49e86d27/scratchpad/dashboard-standard/pr-shots/qbit-marketing-business-home` (`before-<w>.png` shows the old home widgets Needs Attention + Agent Activity + Configuration Status; `after-<w>.png` shows Needs Attention with the blocked channel). Full-page shots happen when kof22 pins the release. ## Release A qbit-marketing release is needed (next RC after 0.1.0-RC.6, the version Website-Backend pins on origin/main). kof22 then bumps `qbit-marketing.version` and, in a follow-up (not done here), places `MarketingWidgets.ADMIN_WIDGETS` on an Admin page. Until then the two moved widgets are not shown anywhere. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.