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

@@ -0,0 +1,33 @@
import { authenticate, requirePermission } from '../../plugins/auth.js'
import { getCcUserByFirebaseUid } from '../../services/cc-user.service.js'
import { getMitraActivityLog, getMitraActivitySummary } from '../../services/mitra-activity.service.js'
const attachCcUser = async (request, reply) => {
const user = await getCcUserByFirebaseUid(request.firebaseUser.uid)
if (!user) return reply.code(403).send({
success: false,
error: { code: 'FORBIDDEN', message: 'Not a control center user' },
})
request.ccUser = user
}
export const mitraActivityRoutes = async (app) => {
app.get('/log', {
preHandler: [authenticate, attachCcUser, requirePermission('mitra', 'read')],
}, async (request, reply) => {
const { mitra_id, date_from, date_to, page = 1, limit = 20 } = request.query
const result = await getMitraActivityLog({
mitra_id, date_from, date_to,
page: Number(page), limit: Number(limit),
})
return reply.send({ success: true, data: result })
})
app.get('/summary', {
preHandler: [authenticate, attachCcUser, requirePermission('mitra', 'read')],
}, async (request, reply) => {
const { mitra_id, date_from, date_to } = request.query
const result = await getMitraActivitySummary({ mitra_id, date_from, date_to })
return reply.send({ success: true, data: result })
})
}