9696eadeaf
Mitra §A: pre-home (S3a/S3b/AccountInactive) + design system + Bestie Home
...
- Port halo_tokens + halo_theme + HaloButton to mitra_app (rose palette,
Bricolage display, Poppins body, JetBrainsMono).
- Build S3a Input WhatsApp (figma-bestie BestieS3 first half) with
+62 chip, leading-zero/62 normalization, allow '+' in input.
- Build S3b OTP verification (6-digit, 60s resend timer, attempts hint,
Focus(canRequestFocus:false) for maestro inputText compat) with full
error branching (CODE_MISMATCH, OTP_EXPIRED, OTP_USED, ATTEMPTS_EXCEEDED,
WRONG_FLOW, ACCOUNT_INACTIVE).
- Add AccountInactive terminal screen for is_active=false mitras.
- Typed MitraAuthError with Indonesian-first localized messages +
retryAfterSeconds passthrough.
- Rebuild home_screen.dart to match figma BestieHome (greeting + status
card + Ganti Status CTA + Pengingat + 2-tile dark grid).
- Backend: POST /internal/_test/seed-mitra (idempotent) and
PATCH /internal/mitras/:id (display_name update).
- Control center: inline Edit Nama on mitras row + expandable inline log
table under clicked mitra (vs old below-table panel).
- 5 maestro flows ts-mitra-A-01/03/04/05/06 covering invalid input, happy
path, account inactive, phone-format normalization, and the back-to-S3a
regression. All green.
Plan + memory documented in:
- requirement/phase4-mitra-prehome-plan.md
- requirement/flow_mitra.md / flow_mitra.mermaid.md §A
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-19 22:01:28 +08:00
1c9d81d81d
Pricing: migrate from app_config JSON to relational tables
...
Replaces the two `pricing_*_tiers_json` blobs and five `first_session_discount_*`
keys in app_config with dedicated `pricing_tiers` and `pricing_promotions`
tables plus matching `_history` audit tables. UUID PKs, UNIQUE(mode, minutes)
natural-key constraint, optimistic-lock via `updated_at` token returning 409
STALE_WRITE on conflicts. Every mutation writes a history row capturing the
operator (changed_by from request.auth.userId) and change_kind.
CC SettingsPage replaces the JSON-textarea editors with per-row tables —
add / edit / soft-delete / reactivate / reorder, plus a buffered first-session
discount form with the same optimistic-lock contract. `minutes` and `mode` are
read-only on edit since they form the natural key; operators soft-delete and
recreate to change duration.
Stage 5 fixes a latent leak: `client.payment.routes.js` had its own local
`readDiscountConfig` that still read from app_config — would have silently
fallen to hardcoded defaults once the legacy rows were deleted. Now reads from
pricing_promotions via the shared service helper, so CC edits to the first-
session discount affect actual payment pricing on the next request.
Customer-facing GET /api/client/chat/pricing shape unchanged (id values are
now UUIDs instead of "5"/"12"/"60" but lookups happen by (mode, minutes), so
no app changes needed). 27 new backend tests, all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 00:12:11 +08:00
d33d4419ea
Phase 4 Stage 1: backend foundation (additive endpoints + schema)
...
Schema (idempotent migration):
- payment_sessions.is_free_trial -> is_first_session_discount (data copied)
- payment_sessions.mode TEXT NOT NULL DEFAULT 'chat' CHECK (chat|call)
- chat_sessions.topics TEXT[] for ESP picks (info-only)
New endpoints:
- GET /api/client/onboarding-state (drives verif sheet + S6 paywall gate)
- GET /api/client/chat-pricing (rewrite: chat+call groups + first-session
discount block, per-customer eligibility)
- GET /api/shared/auth-providers (env-probed; replaces ENABLE_SOCIAL_AUTH
build flag — frontend cutover lands in stage 2)
- GET /api/client/support-handles (Tanya Admin handles, CC-config-driven)
session_warning WS event fires once at 180s remaining.
app_config seeds (mock pricing tiers, first-session discount, support
handles, payment method order, end-session 2-step toggle).
CC SettingsPage: 3 new sections (first-session discount, pricing tiers
JSON editors, support handles).
15/15 Vitest passing. chat_sessions.is_free_trial also renamed for
consistency (plan only specified payment_sessions; pairing.service.js
read both).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-10 15:56:28 +08:00
d09e50af55
Phase 3.7: paid pairing flow + returning chat + extension flip
...
- Backend: payment_sessions + pairing_failures tables; payment.service.js
and pairing-failure.service.js (new); rewritten pairing.service.js
(payment-gated blast + targeted "Curhat lagi" + cancel + fallback);
rewritten extension.service.js (data-driven auto-approve with offline
safeguard, charge-at-approval); pricing.service.js (extension tiers
without free trial); mitra-status.service.js (countAvailableMitras
cached path); 60s sweeper for stale payment sessions
- Backend routes: client.payment.routes, client.mitra-availability.routes,
internal/failed-pairings.routes; client.chat.routes rewritten for
payment-gated start + /returning + /cancel + /fallback-to-blast;
internal/config.routes adds 4 new keys with Valkey invalidate publish
- client_app: mitra-availability poll, payment screen + notifier, pairing
notifier rewrite (PairingTargetedWaiting + PairingFailed states),
targeted-waiting overlay + bestie-unavailable dialog, "Curhat lagi"
CTA, failed-pairing terminal, extension via payment-session
- mitra_app: PairingRequestType enum, returning-chat 20s countdown
auto-dismiss, extension card "otomatis disetujui" copy
- control_center: 4 new config rows in Settings, Failed Pairings page
(filter + paginate + action menu), sidebar + route registered
- Test infrastructure: Vitest backend (7/7 pass), Playwright CC (4/4
pass), Maestro mobile scaffold (CLI install pending)
- Bugs found via Playwright + fixed: LoginPage labels not associated
with inputs (a11y); backend internal CORS missing PATCH/PUT/DELETE
in allow-methods (silent settings breakage in browsers since Stage 4)
- Docs: phase3.7.md PRD, phase3.7-plan.md, phase3.7-questions.md (Q&A),
phase3.7-testing.md (E2E checklist), phase3.7-test-run-2026-05-03.md
(today's run results)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-03 23:02:49 +08:00
4a796277b8
Phase 3.4: control_center self-managed auth cutover
...
Replaces Firebase Auth with the new JWT + httpOnly-cookie refresh flow.
Smoke-tested end-to-end via curl (login → /me → refresh rotation → logout).
- Remove firebase dep + firebase.js
- New token-bridge decouples api-client from AuthContext and de-dupes
concurrent 401 refreshes
- AuthContext: in-memory access token (useRef), bootstrap via
/internal/auth/refresh, login/logout/refresh methods
- api-client: withCredentials, Bearer attach, auto-retry once on 401
- LoginPage: handle INVALID_CREDENTIALS / ACCOUNT_LOCKED / VALIDATION_ERROR
- Layout: self-service "Ganti password" form
- UsersPage: initial password field on create + per-row admin-forced reset
- .env / .env.example: drop VITE_FIREBASE_* vars
- backend/CLAUDE.md + control_center/CLAUDE.md: describe new auth (were
stale on Firebase)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-04-24 15:32:32 +08:00
780cade3db
Phase 3.3: topic sensitivity + Phase 3.4: auth foundation
...
Phase 3.3 — Session Topic Sensitivity (complete):
- Backend: topic_sensitivity column + session_sensitivity_log, sensitivity service
(flip with one-way-latch + audit), PATCH /api/shared/chat/sessions/:id/topic,
topic carried in pairing + extension WS payloads, CC filter + sensitive stats
+ per-mitra sensitive columns on activity page
- client_app: TopicSelectionBottomSheet before pricing, topic flows through
pairing request, silent WS handler for session_topic_updated
- mitra_app: SensitivityBadge + SensitivityTheme + sensitivityConfigProvider,
overlay badge + yellow accent, chat screen app-bar toggle with configurable
confirmation + latch, extension card shows current flag, history + transcript
yellow theme
- control_center: Sensitivitas Topik settings section, topic filter + column
with inline audit log, sensitive stats dashboard card, mitra activity
sensitive columns with QC flag
Phase 3.4 — Self-Managed Auth (foundation only):
- Migration: auth_sessions + otp_requests tables, social identity columns on
customers, password_hash + lockout on control_center_users, OTP + CC lockout
app_config keys
- New services: password (bcrypt + complexity), token (JWT HS256 + refresh
rotation, session_id claim pre-wires future Valkey revocation),
social-identity (Google + Apple JWKS), OTP (Fazpass stub — real API TBD)
- Constants: AuthProvider + OtpChannel
- Middleware, auth route rewrites, WS auth update, Firebase → FCM isolation
still pending (next chunk); Fazpass docs + Apple Developer setup still
required before E2E testing
Docs:
- requirement/phase3.3.md, phase3.3-plan.md, phase3.3-testing.md
- requirement/phase3.4.md, phase3.4-plan.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-04-24 10:15:12 +08:00
50d31260dc
Fix auth errors, CORS, control center login, and stale session handling
...
- Mitra auth: parse DioException response for proper error messages
(ACCOUNT_NOT_FOUND, ACCOUNT_INACTIVE) instead of generic "OTP invalid"
- Backend: add CORS to internal app (port 3001) for control center
- Control center: fix login race condition (wait for AuthContext verify
before navigating), fix MitraActivityPage fetching paginated data
- Stale session goodbye: both apps detect SESSION_NOT_ACTIVE/409 and
move to complete state instead of retrying endlessly
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-15 20:49:57 +08:00
4c6130aa04
Phase 3.2 WS2: Mitra request activity log + control center page
...
- DB migration: add active_session_count column + mitra_notified index
- Constants: add MISSED to NotificationResponse
- Pairing service: record active_session_count on notification creation,
use MISSED (not IGNORED) when another mitra accepts first
- New mitra-activity.service.js: getMitraActivityLog (paginated),
getMitraActivitySummary (per-mitra aggregates with acceptance rate)
- New mitra-activity.routes.js: GET /internal/mitra-activity/log,
GET /internal/mitra-activity/summary
- Control center: new MitraActivityPage with summary table + detail log,
filters (mitra, date range), color-coded response types, pagination
- Register route in App.jsx, add "Aktivitas Mitra" nav link in Layout
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-09 22:20:52 +08:00
229f889551
Phase 3.1 WS2: FCM fallback Flutter + CC, unread badges, dynamic ping
...
- Control center: add mitra ping config UI (require ping toggle + interval)
- Mitra app StatusNotifier: honor require_ping and ping_interval_seconds
from API; skip heartbeat when ping not required
- Both apps: update notification services for FCM deep-linking
- mitra_app: handle chat_request (open_accept), session_closing
- client_app: handle session_closing, paired
- Unread badge providers:
- mitra_app: UnreadSessions provider (polls active-with-unread, badge
on active sessions button)
- client_app: UnreadCount provider (polls active-with-unread, badge
on _ActiveSessionCard)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-09 14:29:06 +08:00
b4efcf14c2
Phase 3 scaffold: chat engine (WebSocket, FCM, pricing, timer, extension, history)
...
- Backend: WebSocket plugin, chat/pricing/timer/extension/closure/notification services
- Client app: ChatBloc, pricing dialog, chat screen with message status, extension/goodbye flow, history
- Mitra app: MitraChatBloc, ExtensionBloc, chat screen, extension accept/reject, history
- Control center: free trial, extension timeout, early end config toggles
- DB migration: chat_messages, session_closures, session_extensions, customer_transactions tables
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 23:58:11 +08:00
d668112edd
Phase 2 scaffold: mitra online status & pairing logic
...
Add mitra online/offline status with heartbeat-based auto-offline,
customer-mitra pairing via Valkey pub/sub blast, session management,
and control center dashboard with real-time stats.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-05 23:17:49 +08:00
a7a2a32d27
Phase 1 scaffold: auth for all apps
...
- Backend: Fastify with two listeners (public + internal), routes, services, DB migration + seed
- client_app: Flutter with BLoC, all auth screens (welcome, display name, register, OTP, force-register)
- mitra_app: Flutter with BLoC, OTP-only login
- control_center: React + Vite, email/password login, mitra/user management, anonymity settings
- Docs: phase1 plan, API contract, client app mockup
- CLAUDE.md and shared memory for all subprojects
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-05 10:08:42 +08:00