feat/discovery
needs attentionviewing older commitd3f875d · incrementalPR #305reviewed 2026-07-17 18:01 UTC1H · 2M · 4L · 2I- Purpose
- Introduce an in-app feature discovery system so new users find key controls (CFE monitoring, payment monitoring, site IDs) without requiring support intervention.
- Goal
- Feature Discovery v1 — ambient spotlight coachmarks, a guided first-run hub (DescubreMenu), and a per-RPU sub-tour, backed by a discovery-state domain that persists per-user acknowledgements.
- Sub-goals
- Discovery domain (FCIS): decisions, queries, shells, errors, type-checks
- API surface: discovery contract, handler, mapper
- UI: FeatureSpotlight, GuidedTour, RpuSubTour, TourOverlay, DescubreMenu
- Spotlight catalog: data-tour anchors on key controls (CFE setup, add contract, monitoring toggles, site IDs, export)
- Analytics events for each spotlight interaction
- E2E smoke: discovery-anchors.spec.ts
- i18n: en.json + es.json messages
- What
- This incremental commit fixes the spotlight anchor placement for 'monitoring' and 'payment_monitoring': moved from `<th>` column headers (ContractsTable) to the per-row MonitorSwitch toggle span (PipelineCell), so the coachmark rings the control you actually act on. E2E test updated to match (now best-effort for per-row anchors).
- Why
- Ringing the column header is confusing UX — the user is directed to look at a label, not the interactive toggle. The fix makes the spotlight highlight where the user should click.
- Areas
- domains/core/src/discovery+414−0domains/cross-domain/src+686−0apps/platform/src/components/discovery+1210−0apps/platform/src/api+289−0apps/platform/src/app+161−33e2e/platform+57−0packages/analytics/src+92−0packages/api/src/schemas+99−0
- Blast
- 58 files, +4732/−63 net-new (nearly all additive). Core surface: new discovery domain + cross-domain coordinator + 5 UI components + API handler. No existing domain entities modified. Two existing components (ContractsTable, PipelineCell) received minor anchor attribute changes.
Findings · 9
correctness1
Comment says 'Recibos vs Pagos' but the prop type is 'recibos' | 'pago' (singular)
apps/platform/src/app/[locale]/(dashboard)/bills/contratos/_components/PipelineCell.tsx:858
The MonitorSwitch receives `kind: 'recibos' | 'pago'` (singular 'pago'). The inline comment reads 'Recibos vs Pagos share this component' — 'Pagos' (plural) doesn't match the actual value. No runtime impact, but could mislead a future reader checking isRecibos against the type.
security1
No security concerns
data-tour values are compile-time string literals; no user input is involved and no trust boundaries are crossed.
conventions1
Inline comment on data-tour span is over-verbose (6 lines for a 2-line WHY)
apps/platform/src/app/[locale]/(dashboard)/bills/contratos/_components/PipelineCell.tsx:851
Project convention: comments only when the WHY is non-obvious; don't explain what the code says. The querySelector first-match behavior is already documented in ContractsTable.tsx's comment block. A single line like '// Spotlight anchor: ring the live toggle per RPU, not the column header' preserves the non-obvious context without restating the code.
tests4
E2E anchor assertions downgraded to best-effort — monitoring anchors now untestable in CI
e2e/platform/discovery-anchors.spec.ts:26
Previously the test required [data-tour="monitoring"] and [data-tour="payment_monitoring"] to be visible whenever the table was present. The new guard (`if (count > 0)`) means the assertion body is silently skipped when no contract rows exist — which is always true in CI with no seed data. A future commit that removes the data-tour attributes entirely would pass green. This is a structural coverage gap for the fix being validated. Resolution: add at least one contract row to the E2E seed fixture, restoring the assertions to mandatory.
isRecibos branch in MonitorSwitch has no unit coverage
apps/platform/src/app/[locale]/(dashboard)/bills/contratos/_components/PipelineCell.tsx:856
The conditional `data-tour={isRecibos ? 'monitoring' : 'payment_monitoring'}` is a logic branch determining which spotlight anchor key is emitted. Neither branch is covered by a unit or component test. An inversion of isRecibos (or future refactor) would silently swap the anchor keys, and with the E2E tests now best-effort, this would go undetected.
No structural check that data-tour keys match the spotlight catalog
apps/platform/src/components/discovery/spotlight-catalog.ts
The keys emitted as data-tour attributes ('monitoring', 'payment_monitoring', 'internal_site_ids', 'export_columns') must match keys registered in the spotlight catalog. There is no type-level satisfies check or unit assertion enforcing this alignment. A key rename in either side creates a silent mismatch — the spotlight coachmark simply doesn't appear, and no test catches it.
export_columns guard status ambiguous — should be mandatory if always present
e2e/platform/discovery-anchors.spec.ts:26
export_columns is folded into the best-effort loop alongside monitoring/payment_monitoring (all three skipped when absent). If export_columns is always present for a given org (like internal_site_ids is always present in a header row), it should be a mandatory assertion rather than best-effort.
improvement2
SPOTLIGHT_ANCHOR_BY_GROUP is a 1-entry map — a direct conditional would be simpler
apps/platform/src/app/[locale]/(dashboard)/bills/contratos/_components/ContractsTable.tsx:192
After removing the recibos and pagos entries, only 'sitio → internal_site_ids' remains. A Partial<Record<…,string>> lookup over one entry adds indirection with no payoff. A direct conditional (`activeGroup === 'sitio' ? 'internal_site_ids' : undefined`) is equally readable and removes the abstraction until a second entry justifies it.
E2E for-loop with count guard is readable but could use a helper
e2e/platform/discovery-anchors.spec.ts:26
Minor: the pattern `for (key of [...]) { if (count > 0) { expect(first()).toBeVisible() } }` is fine but slightly redundant — if count is 0 the assertion is a no-op. Promise.all over the keys with a filter, or a local `expectIfPresent` helper, would read more uniformly. No functional issue on a 3-key list.
History · 36 commits
- c1fe7b7needs attentionincremental3H · 4M · 3L2026-07-27 06:02
- 414db56safeincremental0H · 0M · 2L2026-07-23 00:52
- ea68968needs attentionincremental0H · 4M · 7L2026-07-22 23:01
- 4bdb2c9needs attentionincremental1H · 3M · 5L2026-07-21 23:22
- 6e3f255safeincremental0H · 0M · 0L2026-07-20 16:46
- cc41079blockedincremental5H · 5M · 3L2026-07-20 16:25
- 2451ee0needs attentionincremental0H · 5M · 6L2026-07-20 16:09
- ab7c103needs attentionincremental0H · 1M · 6L2026-07-20 15:52
- 7eff611needs attentionincremental0H · 1M · 2L2026-07-18 00:41
- 246c67csafeincremental0H · 0M · 0L2026-07-17 23:58
- 7521b17safeincremental0H · 0M · 0L2026-07-17 23:35
- 1847ec8needs attentionincremental0H · 2M · 4L2026-07-17 23:28
- 5d3175cneeds attentionincremental3H · 6M · 4L2026-07-17 19:31
- d3f875dneeds attentionincremental1H · 2M · 4L2026-07-17 18:01current
- 47d25faneeds attentionincremental1H · 1M · 2L2026-07-17 00:46
- 440d838needs attentionincremental4H · 9M · 9L2026-07-17 00:27
- 7466f97needs attentionincremental0H · 1M · 5L2026-07-16 23:20
- b686d58needs attentionincremental0H · 2M · 2L2026-07-16 14:24
- 9ea1446blockedincremental2H · 7M · 10L2026-07-16 13:44
- 6f39bb9needs attentionincremental0H · 2M · 7L2026-07-14 21:53
- 2eadf2aneeds attentionincremental0H · 1M · 2L2026-07-14 20:12
- 97bd08fneeds attentionincremental0H · 2M · 5L2026-07-14 19:40
- a48e3ffneeds attentionincremental5H · 8M · 6L2026-07-14 18:22
- 69473b7needs attentionincremental2H · 5M · 3L2026-07-14 01:34
- 92e4235needs attentionincremental2H · 1M · 3L2026-07-14 01:04
- 6be8018safeincremental0H · 0M · 3L2026-07-14 00:15
- 090b4daneeds attentionincremental1H · 3M · 5L2026-07-13 23:50
- fa7683bneeds attentionincremental1H · 4M · 3L2026-07-13 18:55
- d64cd72needs attentionincremental2H · 3M · 2L2026-07-13 16:27
- c1f337bneeds attentionincremental3H · 7M · 14L2026-07-13 13:30
- 46e32b2safeincremental0H · 0M · 3L2026-07-11 02:54
- 3b30949needs attentionincremental0H · 2M · 3L2026-07-11 02:39
- 9f8dbdfneeds attentionincremental3H · 5M · 5L2026-07-11 02:32
- 23191eeneeds attentionincremental5H · 12M · 7L2026-07-11 02:18
- fa9b88fneeds attentionincremental0H · 1M · 2L2026-07-11 01:30
- 6a8bf42blockedfull8H · 11M · 8L2026-07-11 01:08