- Add global SWR config with exponential backoff retry (swr-config.ts) - Add ErrorBoundary component for React crash recovery (error-boundary.tsx) - Standardize ErrorState onRetry on all 6 dashboard views (dashboard, media, messages, moderation, recordings, voice) - Fix useAction: expose resetError + onError callback - Refactor useSpeakers to SWR-backed state (was local useState) for consistent cache/revalidate semantics with other hooks - Remove polling in useReview (15s refreshInterval); replace with useReviewWsSync subscribing to WS moderation_action events - Extract hashUserId to lib/hash.ts (de-dup with ambient-canvas) - WS context: add reconnect/error toast feedback via onStatusChange - WS connection: expose reconnectAttemptCount getter All typecheck + lint clean, Next 16 build passes.
5.5 KiB
5.5 KiB
FE Optimasi: Error Handling, State Refactor, Polish
Scope: services/frontend/src — Next.js 16, React 19, SWR, oRPC-over-WS.
Branch: feat/fe-error-handling-state (buat baru dari main).
User: MythEclipse — bahasa Indonesia/Inggris campur, "gas" = eksekusi langsung.
1. Masalah yang teridentifikasi
1.1 Error handling tidak seragam
ErrorState(states.tsx:126) sudah ada denganonRetry?, tapi 80% pemanggilan tidak passonRetry→ user stuck, harus refresh manual.- Pola
if (error && !data) return <ErrorState />berulang di 5+ view, setiap satu hardcode logic-nya. useAction(use-action.ts) seterrordi state tapi komponen pakaitry/catchlokal juga — dua error states (hookaction.error+ lokalcatch (e)).
1.2 State duplication / double source of truth
useReview(use-messages.ts:129) pakairefreshInterval: 15_000(polling) padahal WS sudah broadcastmoderation_actionreal-time. Polling + WS = double-fetch + race condition.page.tsxdanview.tsxduplikat stateguildId/channelId/query— logic selection ada di 2 tempat.- Chatbot pakai local
failedstate +setFailed(text)untuk retry, tapiuseActionsudah adaerrorfield yang tidak dipakai. useSpeakers(use-voice.ts:50) pakai localuseStateuntuk speakers, bukan SWR → tidak konsisten dengan pola SWR yang dipakai semua hook lain.
1.3 WebSocket error/user feedback lemah
WsConnection.onerror(connection.ts:78) cumasetStatus("error")— tidak ada info ke user "reconnecting… (attempt N)".- Tidak ada broadcast error ke komponen (tidak ada
ws_errorhandler).
2. Rencana perubahan
2.1 Standardisasi ErrorBoundary + Retry (FE-only)
File: src/components/shared/states.tsx
- Tambahkan komponen
ErrorBoundary(React error boundary untuk crash React, bukan fetch error). - Refactor semua
<ErrorState error={error} />→<ErrorState error={error} onRetry={retryFn} />. - Tambahkan helper
withSWRatau pattern: tiap view pakaierror+mutate/refetchdari SWR dan pass ke ErrorState.
File: src/app/(dashboard)/*/view.tsx (6 files)
- Setiap
ErrorStatedapatonRetryyang memanggilmutate/refetch. DashboardView:onRetry={() => void mutate(["dashboard-stats"])}— tapi SWR keys tersebar. Solusi: exportmutatevia custom hook atau pakaiuseSWRconfigonErrorRetryglobal.- Keputusan: gunakan pola global SWR config (
src/lib/swr-config.ts) denganonErrorRetrybackoff, dan tiap view passonRetryexplicit ke ErrorState.
2.2 Hapus polling useReview, ganti WS-driven
File: src/hooks/use-messages.ts
- Hapus
refreshInterval: 15_000dariuseReview. - Tambahkan
useReviewWsSync(ws)— subscribe kemoderation_actionWS event, mutate key["messages-review", channelId]. - Backend sudah broadcast
moderation_actionvia WS (redis-channels.ts:130). Review messages yang di-flag akan dapatmoderation_actionevent. Bisa pakai ini.
File: src/app/(dashboard)/messages/view.tsx
- Tambahkan
useReviewWsSync(ws)call.
2.3 Konsistensi state: useSpeakers → SWR
File: src/hooks/use-voice.ts
- Refactor
useSpeakersagar pakai SWR key["voice-speakers"]+ initialData dariuseVoiceStatus. Ini memungkinkan revalidate + cache sharing. - Tapi
voice_statedanvoice_active_useradalah WS events — perlu persist ke SWR cache viamutate. Refactor:useSpeakerssubscribe WS + mutate SWR key.
2.4 Chatbot: gunakan useAction error state, hapus duplikat failed
File: src/components/chatbot/chatbot.tsx
- Hapus
const [failed, setFailed]— sebalihoikan keuseActionreturnerror. - Tapi
useActionmutateAsyncthrow — perlu catch. Refactor: pakaimutate(fire-and-forget) +isPending+error. - Note: chatbot pakai
sendyang butuh pemuatan history — tetap pakai local state untuk msgs tapi gunakanaction.erroruntuk display.
2.5 WS error feedback
File: src/lib/ws/connection.ts
onerroremit kode/status ke status listeners.- Tambahkan method
getReconnectAttempt()atau expose via status change.
File: src/lib/ws/context.tsx
- Subscribe
onStatusChangediWsProvider, toast "Reconnecting… (attempt N)" ketika statuserror/connecting.
3. Verification
npx tsc --noEmit— compile OKnpx biome check src/— lint OKnpm run build— build OK (Next 16 SSG/SSR)- Manual: refresh halaman, pastikan ErrorState muncul dengan tombol Retry yang bisa diklik.
4. Files yang disentuh
src/components/shared/states.tsx # ErrorBoundary + helper
src/lib/swr-config.ts # global SWR config (baru)
src/hooks/use-messages.ts # useReviewWsSync, hapus polling
src/hooks/use-voice.ts # useSpeakers → SWR
src/hooks/use-action.ts # expose resetError
src/lib/ws/connection.ts # reconnect info
src/lib/ws/context.tsx # WS error toast
src/app/(dashboard)/messages/view.tsx # useReviewWsSync
src/app/(dashboard)/messages/page.tsx # SSR error passthrough
src/app/(dashboard)/moderation/view.tsx # onRetry
src/app/(dashboard)/dashboard/view.tsx # onRetry
src/app/(dashboard)/voice/view.tsx # onRetry
src/app/(dashboard)/media/view.tsx # onRetry
src/app/(dashboard)/recordings/view.tsx # onRetry
src/components/chatbot/chatbot.tsx # pakai useAction error
5. Out of scope (BE)
- Regex heuristic di moderation.repository.ts (scam domain extraction) — ini BE task, catat tapi jangan sentuh kecuali diminta.