Post-MVP rails sweep: viewer-zone formatting (34 files) and typed refusals (7 actions, 30 routes) in one bucket
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Aptitud para principiantes
- 42/100
- Tipo de issue
- Refactorización
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- next.js, react, typescript
- Área
- api, backend, frontend, full-stack, observability
Línea de trabajo
Empieza por lib/time/viewer-zone.ts, lib/errors/refusal.ts y los rails existentes en tests/time/viewer-zone.test.ts y tests/errors/refusal-rails.test.ts. Audita los archivos de dashboard/components y las actions/routes indicados, verificando cada candidato antes de aplicar los patrones establecidos. Ejecuta ambas pin tests y las comprobaciones grep especificadas; se considera terminado cuando los dos barridos cumplen todas las condiciones indicadas sin añadir tests por archivo.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Why one issue
Two rails landed on 2026-09-15 and each was applied only to the sites that were visibly failing, on purpose (the Stream MVP is the priority): the typed Refusal (#1655, lib/errors/refusal.ts, docs/errors/01-refusals.md) and the viewer-zone formatting rail (#1657, lib/time/viewer-zone.ts, docs/booking/06-booking-lifecycle.md "Times are rendered in the viewer's zone"). This issue is the single bucket for finishing both sweeps after the MVP, so neither turns into a trickle of one-file PRs. Both are mechanical against an existing rail; both are Sonnet-tier work; both should ship as ONE PR each (or one combined), not per file.
Sweep A — bare date formatting on absolute instants (hydration + wrong-zone risk)
Every file below formats a Date with date-fns format(), toLocaleDateString, or a local-zone getter. On the server that is UTC and in the browser it is the user's zone, so any of them that server-renders can throw React #418 and re-render, and any of them shows the wrong wall-clock time to a user whose zone is not the runtime's. Replace with formatInViewerZone / formatForViewer and thread the zone from getViewerZone() (RSC) or useViewerZone() (client-only), exactly as AppointmentRow/NextUpHero do on #1657. The relative labels (getProximityLabel, CountdownBadge, DayGroupHeader's isToday, AppointmentList.dayKey) are the same bug at day boundaries and belong here too.
app/dashboard/consultant/[consultantId]/(features)/analytics/AnalyticsPageClient.tsxapp/dashboard/consultant/[consultantId]/(features)/documents/DocumentsTab.tsxapp/dashboard/consultant/[consultantId]/(features)/earnings/EarningsSummaryPanel.tsxapp/dashboard/consultant/[consultantId]/(features)/recordings/components/RecordingCard.tsxapp/dashboard/consultee/[consulteeId]/(features)/home/HomeTab.tsxapp/dashboard/organization/[orgId]/appointments/AppointmentsPageClient.tsxapp/dashboard/organization/[orgId]/documents/DocumentsClient.tsxapp/dashboard/organization/[orgId]/recordings/RecordingsClient.tsxapp/dashboard/organization/[orgId]/reimbursements/page.tsxapp/dashboard/staff/[staffId]/(features)/home/HomePageClient.tsxapp/explore/experts/[consultantId]/components/SubscriptionPricingToggle.tsxapp/form/onboarding/components/experience/CertificationsSection.tsxapp/form/onboarding/components/experience/WorkExperienceSection.tsxAppointmentList.tsxAppointmentRow.tsxcomponents/admin/WaitlistManagement.tsxcomponents/appointments/AppointmentCalendar.tsxcomponents/appointments/AppointmentRow.tsxcomponents/appointments/AppointmentSheet.tsxcomponents/appointments/DayGroupHeader.tsxcomponents/appointments/detail/AppointmentDetailClient.tsxcomponents/appointments/detail/AppointmentDocumentsList.tsxcomponents/appointments/detail/RescheduleProposalCard.tsxcomponents/appointments/HeldSlotBadge.tsxcomponents/appointments/NextUpHero.tsxcomponents/appointments/SessionTimeline.tsxcomponents/chat/CustomMessage.tsxcomponents/dashboard/shared/DocumentsPage.tsxcomponents/scheduling/SessionReleasePicker.tsxcomponents/scheduling/UnifiedCalendar.tsxDayGroupHeader.tsxEarningsSummaryPanel.tsxNextUpHero.tsxpage.tsx
Sweep B — refusals still thrown as plain Errors
Server actions that throw new Error("Unauthorized …") are captured by Next's onRequestError; routes that answer a hand-built 4xx while also calling Sentry.captureException produce an error-level issue for a modelled "no". Convert each to a Refusal answered through apiError (routes) or returned as an ActionResult (actions), with a userMessage for the toast and a devMessage for the log — the shape #1655 established. Verify each candidate against the code before converting; some 4xx sites are genuine faults and should stay captured.
Server actions (count of refusal-shaped throws):
actions/stream/chat/event-channel.action.tsactions/stream/chat/stream.action.tsactions/stream/chat/user.action.tsactions/stream/chat/member.action.ts
Route handlers that both capture to Sentry and answer a 4xx (candidates, not verdicts):
app/api/admin/compliance/tds-return/route.tsapp/api/admin/disputes/[disputeId]/route.tsapp/api/admin/erasure-requests/[id]/process/route.tsapp/api/admin/maintenance/route.tsapp/api/admin/payments/[paymentId]/route.tsapp/api/admin/payouts/[id]/route.tsapp/api/admin/reconcile-ledgers/route.tsapp/api/admin/tds/route.tsapp/api/admin/users/[userId]/route.tsapp/api/admin/verification/[verificationId]/route.tsapp/api/admin/waitlist/broadcast/route.tsapp/api/announcements/[id]/route.tsapp/api/announcements/route.tsapp/api/appointments/[appointmentId]/cancel/preview/route.tsapp/api/appointments/[appointmentId]/cancel/route.tsapp/api/appointments/[appointmentId]/documents/[documentId]/download/route.tsapp/api/appointments/[appointmentId]/documents/[documentId]/route.tsapp/api/appointments/[appointmentId]/documents/consultant/route.tsapp/api/appointments/[appointmentId]/documents/route.tsapp/api/appointments/[appointmentId]/reschedule/route.tsapp/api/appointments/[appointmentId]/support/route.tsapp/api/bookings/classes/[classId]/route.tsapp/api/bookings/classes/[classId]/validate/route.tsapp/api/bookings/classes/crud-with-plan/[classId]/route.tsapp/api/bookings/classes/crud-with-plan/route.tsapp/api/bookings/consultations/[consultationId]/route.tsapp/api/bookings/consultations/[consultationId]/validate/route.tsapp/api/bookings/consultations/route.tsapp/api/bookings/subscriptions/[subscriptionId]/route.tsapp/api/bookings/subscriptions/[subscriptionId]/validate/route.ts
Done when
- No
format(/toLocale*on an absolute instant outsidelib/time/;grep -rln 'from "date-fns"' --include=*.tsx components app | xargs grep -l "format("returns only files that format durations or already pass a zone. - No server action throws a refusal-shaped
Error; no route answers a 4xx for a modelled refusal while also capturing it. - One pin per rail already exists (
__tests__/time/viewer-zone.test.ts,__tests__/errors/refusal-rails.test.ts); do not add per-file tests.
Part of the post-MVP hardening; see #1645 (the sweeps-treat-permanent-as-transient lesson), #1655, #1657.
- Lenguaje dominante
- TypeScript
- Estrellas
- 4
- Forks
- 4
- Merge medio
- 9 h 48 min
- PR fusionados (30 d)
- 230
Preparar el entorno
Aún no hemos revisado los archivos de configuración de este proyecto. Empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de Practitionist/familiarise_web
-
infrastructure launch: scale production
Dificultad 2/5 Medio día Aptitud para principiantes 72/100
Practitionist/familiarise_web#1772 ·
Los mantenedores suelen responder en 1 día
-
reliability tech-debt
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
Practitionist/familiarise_web#1630 ·
Los mantenedores suelen responder en 1 día
-
moderation
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Practitionist/familiarise_web#1604 ·
Los mantenedores suelen responder en 1 día
-
Enterprise launch: post-mvp notifications tech-debt
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
Practitionist/familiarise_web#1452 ·
Los mantenedores suelen responder en 1 día
-
bug dashboard Enterprise launch: post-mvp
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
Practitionist/familiarise_web#1445 ·
Los mantenedores suelen responder en 1 día
Todos los issues de Practitionist/familiarise_web
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
rohitg00/agentmemory#1428 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
boxlite-ai/boxlite#1729 ·
Los mantenedores suelen responder en 1 día
-
Detect Deno tasks from deno.jsonAbiertodetectors enhancement good first issue
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
SM260845/readme-gen#1 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
angular/angularfire#3774 ·
Los mantenedores suelen responder en 2 días