Canonical implementations catalog

Golden paths for lists, forms, wizards, and API siblings.

Source: docs/development/DOC-024-canonical-implementations.md

DOC-024: Canonical implementations

Estado

Accepted — 2026-08-23 · Amendment 2026-08-24: gold solo en este repo. IVPrior es muestra para copiar, no dependencia.

Norma: ADR-062. Toda fila de Mirror es un path dentro de IVPrior. Prohibido importar, submoduliar o instruir a un agente a abrir ../IVPrior (u otra máquina) para implementar.

IVPrior se usó una vez (SLICE-001-00) para portar patterns/rules/packages hacia packages/ui, packages/admin-layout y estos docs. Después de ese copy, IVPrior se construye solo.

UI admin — gold en este repo

TipoMirror (IVPrior)Notas
Átomos / data-tablepackages/ui (src/components/ui/button.tsx, input.tsx, textarea.tsx, src/components/data-table/)Port SLICE-001-00; Textarea = mismo chrome que Input, min-h + rows
Page toolbar, shell, wizard shellpackages/admin-layout/src/admin-page-toolbar.tsx · admin-shell.tsx · admin-wizard-shell.tsxPort SLICE-001-00
List page + toolbarapps/web/features/shared/components/AdminCrudListPage.tsxLista simple. Catalogos (Users/Roles/Permissions/Audit) usan AdminEntityTable. Organizations y Plans tienen Card table propia.
Active sessionsapps/web/features/shared/components/ActiveSessionsPage.tsxPlatform and Organization. Device/IP/current badge when the API has them; tenant tokens today expose created/expires only.
Table + AdminMobileListAdminCrudListPage + @ivprior/ui/admin-mobile-listCards <lg, table lg+
Wizard full-pageapps/web/features/tenant-access/components/TenantRolesWizard.tsx2 pasos Details + matrix; Next outline
Auth card (Organization)apps/web/features/tenant-auth/components/OrganizationAuthShell.tsxBackdrop + glowing card; login/signup/invite/forgot/reset/verify
Organization tenant loginapps/web/features/tenant-auth/components/OrganizationLoginPage.tsx/t/{organizationSlug}/login. No global /login (Customer portal not in V1).
Organization signup wizardapps/web/features/tenant-auth/components/OrganizationSignupWizard.tsx3 steps: plan picker + org name/slug, OWNER account, review. Dialog: OrganizationSignupDialog. No Location.
Organization contact (address)apps/web/features/tenant-org/components/OrganizationContactPage.tsxRoute /t/{organizationSlug}/settings. Places autocomplete + map + phone E.164 + support email. Gold atoms in features/shared/address and features/shared/maps.
Tenant admin chromeapps/web/features/tenant-access/components/TenantShell.tsxWorkspace /t/{organizationSlug}/…. Settings + Billing. Support = User Help only (Developer Docs is platform). Header Search = command palette (searchMode="palette"). Bell Inbox + Audit tab (onViewAllLogs). Help ? = resolveContextualHelpUrl. Profile = EntityAvatar + /account/profile.
Organization brandingapps/web/features/tenant-org/components/OrganizationBrandingPage.tsxMirror PlatformBrandingPage: crop logo/favicon, dual colors, workspace preview. No Location table.
Organization dashboardapps/web/features/tenant-org/components/OrganizationDashboardPage.tsxKPI sparklines, Cash Recovered trend, aging donut, top Customers, recent invoices. Demo orgs seed Customer/Invoice.
Clear-on-editapps/web/features/shared/lib/clear-field-errors.tsCopiar a este path en el primer slice UI
FormCloseDialogapps/web/features/shared/components/FormCloseDialog.tsxIdem
Marketing landing /apps/web/features/platform-homepage/components/PlatformMarketingLanding.tsxHeader, site-links, legal footer, store badges
Public plan cardsapps/web/features/platform-homepage/components/PublicPlanCards.tsxCatalog via GET /api/v1/public/plans; empty until published
Platform social links (S-094)apps/web/features/platform-admin/social-links/components/PlatformSocialLinksPage.tsxCatalogo fijo 6 redes; API platform/settings/social-links
Help Center chromeapps/help/features/help-center/components/HelpShell.tsxSidebar colapsable + iconos, ⌘K, locale bandera
Help article bodyapps/help/mdx-components.tsx + HelpArticlePage.tsxReactMarkdown: native <img> for /help/S-xxx/*.svg, HelpCallout from >, breadcrumb + prev/next pager
Developer Docs chromeapps/docs/features/dev-docs/components/DocsShell.tsxSidebar módulos, ⌘K, bandera, catálogo ADR con filtro, artículos + pager
Platform admin chromeapps/web/features/platform-admin/components/PlatformShell.tsxFactory menu: Dashboard, Analytics, Organizations, Plans, Access, Compliance, sessions/branding/social. Header shortcut = Organizations (analog of Tenants; Plans is sidebar-only). Header Search = command palette. Session gate + JWT refresh. Bell Inbox + Audit tab. Help ? = contextual article.
Platform plans listapps/web/features/platform-admin/plans/PlatformPlansListPage.tsxAdminPageToolbar + Add plan + Card table: search, Filter, Export, checkboxes + DataTableBulkBar, ⋮ Edit/Duplicate/Delete. Wizard: PlatformPlanWizard.tsx.
Platform data sources listapps/web/features/platform-admin/data-sources/components/PlatformDataSourcesListPage.tsxMirror Plans list. Modal wizard: PlatformDataSourceFormDialog (Mirror ScheduledJobFormDialog). ⋮ Edit/Deactivate/Delete; bulk Delete. Help S-205.
Subscriber scheduled scansapps/web/features/scanner/components/ScannerSchedulesListPage.tsx (P0 Spec Kit 006)Mirror jobs list. Modal wizard ScannerScheduleFormDialogScheduledJobFormDialog + AdminWizardModalShell. Sin /new ni /{id}. Help S-208.
Platform ticker catalog listapps/web/features/platform-admin/tickers/components/PlatformTickersListPage.tsxMirror Data sources / Analyze list. Modal form (no credentials wizard). Search · Filter · Export. ⋮ Edit/Activate/Deactivate/Delete; bulk Delete. Help S-207. Subscriber watchlist Add = catálogo activo.
Subscriber watchlist listapps/web/features/scanner/components/WatchlistPage.tsxMirror Analyze list. Columns Symbol · Name · Status (name from platform catalog). Add modal = catálogo platform. Checkboxes + bulk + ⋮ Activate/Deactivate/Delete. Analyze combobox = esta lista.
Subscriber paper diary listapps/web/features/scanner/components/PaperDiaryPage.tsxMirror Watchlist / Scanner jobs. Columns Date · Symbol · Name · Type · Summary (signal · IV regime · strategy from snapshot). Search · Filter (type + date) · Export. Checkboxes + bulk delete · ⋮ View (ficha snapshot modal) / Analyze now / Delete. No Add. Help S-204.
Scan job running progressapps/web/features/scanner/components/ScanJobProgressPanel.tsxBarra role=progressbar (chrome LimitMeter) + fase + ticker actual + lista N de M. Processor escribe resultSummary.phase por ticker. No skeleton datatable mientras QUEUED/RUNNING.
Run scan ticker subsetapps/web/features/scanner/components/RunScanDialog.tsxMirror SearchableMultiCombobox (Permission form / Plan scanner sources). Default = watchlist activa; POST params.symbols (API ya filtra vs watchlist). Resultados: miss hint con símbolos (listMissedScanSymbols).
Opportunity ficha copyapps/web/features/scanner/lib/opportunity-copy.tsCopy de ficha (dirección, por qué, estrategia, checklist, disclaimer) en el locale de la UI (next-intl). Motor persiste directionFacts + buckets; jobs viejos con prosa CLI en español se re-renderizan. No mezclar labels EN con cuerpo ES. Estrellas = prioridad relativa; no mostrar el rankingScore numérico.
Analyze context chartapps/web/features/scanner/components/AnalyzeContextChart.tsxFEAT-012. lightweight-charts (Apache, no iframe TradingView). Solo S-202 Analyze sheet. Snapshot as-of; presets 1M·3M·6M·1A; Bollinger diario. MUST NOT usarse fuera de Analyze.
Directional scan result rowsapps/web/features/scanner/components/ScanResultRowPanel.tsxMirror OpportunityResultPanel. SCAN/SCREENER/BACKTEST: ticker + badge de señal + métricas etiquetadas (precio, RSI, motivo SMA i18n). No JSON crudo, no rowKind/sortKey/signalScore sueltos.
Platform organizations listapps/web/features/platform-admin/organizations/PlatformOrganizationsListPage.tsxMirror Plans list. Card table: avatar (EntityIdentityCell), slug <code>, status Badge, Plan chip, Created, Filter status+date, Export, checkboxes + bulk Suspend/Activate, ⋮ Impersonate/Suspend. No Location column.
Platform permissions catalogapps/web/features/platform-admin/access/PlatformPermissionsListPage.tsxAdminEntityTable. Add permission + PermissionFormDialog (Resource/Action comboboxes, Textarea description, status Switch). ⋮ Edit/Delete, bulk delete. 409 if assigned to roles. No Location/orders.
Platform roles catalogapps/web/features/platform-admin/access/PlatformRolesListPage.tsxAdminEntityTable + URL wizard PlatformRoleWizard (Basics + grouped RolePermissionsMatrix). /platform/access/roles/new · /platform/access/roles/{id}. ⋮ Edit/Delete on custom roles. System roles locked.
Platform command paletteapps/web/features/platform-admin/command-palette/PlatformCommandPalette.tsx⌘K / Search icon. Query ≥2 chars searches Organizations; Impersonate → /t/{slug}/dashboard. No orders/locations/menu.
Tenant command paletteapps/web/features/tenant-org/command-palette/TenantCommandPalette.tsx⌘K / Search icon. Nav + actions (dashboard, settings, branding, users, billing). No Location/orders.
Platform auth split layoutapps/web/features/platform-admin/components/PlatformAuthLayout.tsx/platform/login + forgot/reset — hero + form card
Platform branding (S-119)apps/web/features/platform-admin/branding/components/PlatformBrandingPage.tsxLogo/favicon crop, dual ColorPicker, Organization workspace preview
Platform profileapps/web/features/platform-admin/profile/components/PlatformMyProfilePage.tsxHero + details + password
Tenant profileapps/web/features/tenant-access/components/TenantMyProfilePage.tsxTwin of Platform profile. /t/{slug}/account/profile. User.avatarUrl.
Image crop uploadapps/web/features/shared/components/ImageCropUploadField.tsxreact-easy-crop dialog; layouts branding / inline / dropzone. Profile POST /api/v1/platform/me/upload-avatar or /api/v1/me/upload-avatar.
Contextual Help ?apps/web/lib/screen-help-map.tsHeader helpHref = article for the current path. Unmapped /platform/* → S-017; unmapped /t/{slug}/* → S-010. Scanner: S-010 dashboard, S-200 results, S-201 watchlist, S-202 analyze, S-204 diary, S-206 jobs, S-207 tickers.
Admin KPI gridapps/web/features/shared/components/admin-kpi/AdminKpiGrid.tsxCompact density + sparkline; ops home / analytics
Dashboard feed panelapps/web/features/shared/analytics-dashboard/components/DashboardFeedPanel.tsxh-[22rem] + scroll interno. Ops home: orgs/sessions/audit
Entity identity cellapps/web/features/shared/components/EntityIdentityCell.tsxAvatar + name. Organization squircle, Customer circle
Dashboard invoices tableapps/web/features/shared/analytics-dashboard/components/DashboardInvoicesTable.tsxRecent invoices: sort headers, badges, EntityIdentityCell, pagination, AdminMobileList. Harbor + /platform/analytics
Plan mix donutapps/web/features/shared/analytics-dashboard/components/DashboardDistributionChart.tsxRecharts pie; no order-type labels
Platform ops homeapps/web/features/platform-admin/ops-home/components/PlatformOpsHomePage.tsxKPI grid + donut + feeds (orgs/sessions/audit) at /platform
Platform analyticsapps/web/features/platform-admin/dashboard/components/PlatformDashboardPage.tsx/platform/analytics — Cash Recovered trend, aging, AR by Organization, invoices. Not the ops home.
Transactional email layoutapps/api/src/infrastructure/email/templates/transactional-email-layout.tsCard shell; rich header via email-chrome.ts
Transactional email templatesapps/api/src/infrastructure/email/email-template.service.tsReset, invite, verify, welcome — hero + social footer + Help/Docs. Scheduled scan result: subscriberScheduledScanAlert (meta list + disclaimer)
Platform web analytics (GTM)apps/web/features/shared/analytics/PlatformMarketingAnalytics.tsxConsent Mode + banner; GTM off si NEXT_PUBLIC_GTM_ID vacío
Document metadata / SEOapps/web/lib/seo/build-public-page-metadata.ts + robots-policy.ts · Help apps/help/lib/seo/build-help-page-metadata.ts · Docs apps/docs/lib/seo/build-docs-page-metadata.ts · DocumentTitle en AdminPageToolbar · patrón docs/ux/patterns/document-metadata.mdTítulo %s | IVPrior (Help/Docs sufijo propio). Indexables: / /pricing. Paneles /t /platform + auth: noindex.

Hasta que una fila TBD exista: no inventar chrome; componer desde @ivprior/ui / @ivprior/admin-layout (ya copiados al repo) y los patrones en docs/ux/patterns/. El primer ejemplar se registra aquí en el mismo PR.

Patrones: docs/ux/patterns/platform-admin-crud.md · admin-wizard.md · admin-ui-coherence.md.

Qué NO entra en IVPrior (ni como path)

  • Entidad Location, X-Location-Id, courier, QR/menu, marketplace, KDS.
  • Cualquier import o referencia de filesystem a otro producto. Paleta: copiar valores IVPrior en packages/ui/tokens/tokens.css (no remapear a light/negro).

API

TipoMirrorDesde
List + paginationapps/api/src/modules/iam/tenant-access.service.ts (listUsers)Invite/disable + max_users
Dialog confirmapps/web/features/tenant-access/components/TenantUsersListPage.tsx (Invite dialog)
Prometheus scrapeapps/api/src/infrastructure/metrics/metrics.controller.tsADR-028; bearer METRICS_BEARER_TOKEN
JSON loggerapps/api/src/infrastructure/logging/json-logger.service.tscorrelation id + redact PII
Public plan catalogapps/api/src/plans/public-plans.service.tsensureCatalog + published self-serve list
Organization profileapps/api/src/modules/organization/application/organization-profile.service.tsGET/PATCH /api/v1/organization — address, phone E.164, supportEmail
Scanner engine (CLI)apps/scanner-engine/src/scanner_engine/main.py + cli_bridge.pyThin FastAPI over apps/cli. Compose: scanner-engine (make local-up). Host fallback: make engine-dev.
Subscriber scanner auditapps/api/src/modules/scanner/presentation/subscriber-scanner.controller.ts (recordAudit)Mirror AuditService.record (CreateScheduledJobUseCase). Mutations write SCANNER_* with subscriberId so the shell Logs tab is not empty.
Screener CLI → SaaS rowsapps/cli/saas_contract.py (screener_payload) + apps/scanner-engine/src/scanner_engine/main.pySCREENER calls fundamentals.evaluar. 0 hits is a valid miss (mega-caps/ETFs). Copy: emptyScreener, not the generic wait empty.
Backtest CLI → SaaS rowsapps/cli/saas_contract.py (backtest_trade_payload) + apps/cli/backtest.pyBACKTEST calls backtest.run (long-only, no lookahead). Stats strip = trades / win rate / vs buy&hold — no dump of unsupported keys, no IV miss hint.
Bollinger CLI → SaaS rowsapps/cli/saas_contract.py (bollinger_payload) + apps/cli/bollinger.pyBOLLINGER calls collect_tabla (%B, squeeze, trend-filtered signal). One row per ticker, not a trade log. Copy: emptyBollinger, not the IV miss hint.
Scanner job complete notifyapps/api/src/modules/scanner/application/services/scan-job-processor.service.ts (emitFinished)Mirror DispatchSubscriberNotificationUseCase. Inbox+email honor /app/notifications. Actor is not skipped (shouldSkipActor SCANNER_* = false). Deep link /scanner?job= / /scanner/analyze?job= (Mirror PaperDiary ?entry=).

Ops / DX

ConcernCanónicoNotas
Git pre-push (docs/ops-only)scripts/pre-push-gate.sh + .husky/pre-pushSkip pnpm typecheck + pnpm test cuando el push solo toca docs/, memory/, openspec/, specs/, agents/, .specify/, .github/, .cursor/, etc. Cualquier apps/ o packages/ → gate completo. make check = pre-PR.
Session ship (/cierre-session)scripts/sd-ship.sh + make ship--skip-check + --no-wait para slices docs/ops; pre-push ligero acelera el push.

Cómo usar

Mirror: apps/web/features/…/UsersListPage.tsx

Path relativo a IVPrior. Leer ese archivo antes del primer edit. Si aún no hay gold de ese tipo, el slice en curso lo crea aquí y añade la fila.

Schedules y Billing — SLICE-006-06 (2026-09-13)

  • Validación cron API: apps/api/src/modules/scanner/application/utils/scan-schedule-cron.utils.ts; el formulario apps/web/features/scanner/lib/scan-schedule-form.ts refleja ese contrato numérico sin importar servidor. Tests de ambas fronteras cubren la misma matriz de entradas.
  • Billing scanner: apps/web/features/subscriber-admin/billing/components/SaasBillingCurrentPlanCard.tsx / CompactUsageStat; cupos finitos con barra, null/cero sin barra; uso real en aria-valuetext.
  • Modal schedules: ScannerScheduleFormDialog.tsx conserva ScheduledJobFormDialog como Mirror; validación por paso con React Hook Form y errores inline en los FormField existentes.