Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Post-MVP rails sweep: viewer-zone formatting (34 files) and typed refusals (7 actions, 30 routes) in one bucket

未关闭
#1,663 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
5/5
预计耗时
一周以上
新手友好度
42/100
Issue 类型
重构
描述清晰度
描述清楚
活跃度
活跃

调研方向

从 lib/time/viewer-zone.ts、lib/errors/refusal.ts,以及 tests/time/viewer-zone.test.ts 和 tests/errors/refusal-rails.test.ts 中现有的 rails 开始。审查列出的 dashboard/components 文件和 actions/routes,在应用既定模式之前验证每个候选项。运行两个 pin tests 和指定的 grep checks;完成意味着两轮扫描满足所有已声明的条件,且不添加逐文件测试。

由索引模型根据 Issue 内容生成。

描述

enhancement

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.tsx
  • app/dashboard/consultant/[consultantId]/(features)/documents/DocumentsTab.tsx
  • app/dashboard/consultant/[consultantId]/(features)/earnings/EarningsSummaryPanel.tsx
  • app/dashboard/consultant/[consultantId]/(features)/recordings/components/RecordingCard.tsx
  • app/dashboard/consultee/[consulteeId]/(features)/home/HomeTab.tsx
  • app/dashboard/organization/[orgId]/appointments/AppointmentsPageClient.tsx
  • app/dashboard/organization/[orgId]/documents/DocumentsClient.tsx
  • app/dashboard/organization/[orgId]/recordings/RecordingsClient.tsx
  • app/dashboard/organization/[orgId]/reimbursements/page.tsx
  • app/dashboard/staff/[staffId]/(features)/home/HomePageClient.tsx
  • app/explore/experts/[consultantId]/components/SubscriptionPricingToggle.tsx
  • app/form/onboarding/components/experience/CertificationsSection.tsx
  • app/form/onboarding/components/experience/WorkExperienceSection.tsx
  • AppointmentList.tsx
  • AppointmentRow.tsx
  • components/admin/WaitlistManagement.tsx
  • components/appointments/AppointmentCalendar.tsx
  • components/appointments/AppointmentRow.tsx
  • components/appointments/AppointmentSheet.tsx
  • components/appointments/DayGroupHeader.tsx
  • components/appointments/detail/AppointmentDetailClient.tsx
  • components/appointments/detail/AppointmentDocumentsList.tsx
  • components/appointments/detail/RescheduleProposalCard.tsx
  • components/appointments/HeldSlotBadge.tsx
  • components/appointments/NextUpHero.tsx
  • components/appointments/SessionTimeline.tsx
  • components/chat/CustomMessage.tsx
  • components/dashboard/shared/DocumentsPage.tsx
  • components/scheduling/SessionReleasePicker.tsx
  • components/scheduling/UnifiedCalendar.tsx
  • DayGroupHeader.tsx
  • EarningsSummaryPanel.tsx
  • NextUpHero.tsx
  • page.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.ts
  • actions/stream/chat/stream.action.ts
  • actions/stream/chat/user.action.ts
  • actions/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.ts
  • app/api/admin/disputes/[disputeId]/route.ts
  • app/api/admin/erasure-requests/[id]/process/route.ts
  • app/api/admin/maintenance/route.ts
  • app/api/admin/payments/[paymentId]/route.ts
  • app/api/admin/payouts/[id]/route.ts
  • app/api/admin/reconcile-ledgers/route.ts
  • app/api/admin/tds/route.ts
  • app/api/admin/users/[userId]/route.ts
  • app/api/admin/verification/[verificationId]/route.ts
  • app/api/admin/waitlist/broadcast/route.ts
  • app/api/announcements/[id]/route.ts
  • app/api/announcements/route.ts
  • app/api/appointments/[appointmentId]/cancel/preview/route.ts
  • app/api/appointments/[appointmentId]/cancel/route.ts
  • app/api/appointments/[appointmentId]/documents/[documentId]/download/route.ts
  • app/api/appointments/[appointmentId]/documents/[documentId]/route.ts
  • app/api/appointments/[appointmentId]/documents/consultant/route.ts
  • app/api/appointments/[appointmentId]/documents/route.ts
  • app/api/appointments/[appointmentId]/reschedule/route.ts
  • app/api/appointments/[appointmentId]/support/route.ts
  • app/api/bookings/classes/[classId]/route.ts
  • app/api/bookings/classes/[classId]/validate/route.ts
  • app/api/bookings/classes/crud-with-plan/[classId]/route.ts
  • app/api/bookings/classes/crud-with-plan/route.ts
  • app/api/bookings/consultations/[consultationId]/route.ts
  • app/api/bookings/consultations/[consultationId]/validate/route.ts
  • app/api/bookings/consultations/route.ts
  • app/api/bookings/subscriptions/[subscriptionId]/route.ts
  • app/api/bookings/subscriptions/[subscriptionId]/validate/route.ts

Done when

  • No format( / toLocale* on an absolute instant outside lib/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.

主要语言
TypeScript
星标
4
派生
4
平均合并
8 小时 53 分钟
30 天内合并 PR
232

环境准备

我们还没有检查这个项目的环境配置文件。先看它的 README,通用步骤见我们的新手贡献指南。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

Practitionist/familiarise_web 的其他 Issue

查看 Practitionist/familiarise_web 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。