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>
This commit is contained in:
2026-04-09 22:20:52 +08:00
parent b9c4841eb1
commit 4c6130aa04
9 changed files with 312 additions and 4 deletions

View File

@@ -92,9 +92,14 @@ export const createPairingRequest = async (customerId, { duration_minutes, price
// Create notifications for all available mitras
for (const mitra of availableMitras) {
const [{ count: activeCount }] = await sql`
SELECT COUNT(*)::int AS count FROM chat_sessions
WHERE mitra_id = ${mitra.id}
AND status IN (${SessionStatus.ACTIVE}, ${SessionStatus.PENDING_PAYMENT})
`
await sql`
INSERT INTO chat_request_notifications (session_id, mitra_id)
VALUES (${session.id}, ${mitra.id})
INSERT INTO chat_request_notifications (session_id, mitra_id, active_session_count)
VALUES (${session.id}, ${mitra.id}, ${activeCount})
`
// Notify mitra via WebSocket (FCM fallback if offline)
await notifyMitra(mitra.id, {
@@ -139,10 +144,10 @@ export const acceptPairingRequest = async (sessionId, mitraId) => {
WHERE session_id = ${sessionId} AND mitra_id = ${mitraId}
`
// Mark other mitras' notifications as ignored
// Mark other mitras' notifications as missed (another mitra accepted)
await sql`
UPDATE chat_request_notifications
SET response = ${NotificationResponse.IGNORED}, responded_at = NOW()
SET response = ${NotificationResponse.MISSED}, responded_at = NOW()
WHERE session_id = ${sessionId} AND mitra_id != ${mitraId} AND response IS NULL
`