Home›ZX Parenting›ZX Parenting›Dashboard Admin Web
DRAFT

Dashboard Admin Web

JSON

šŸ’” Ide & Konsep

Dashboard Admin Web — ZX Parenting

URL: https://zxparenting.zomet.my.id/admin

Stack: Next.js 15 + Prisma + PostgreSQL

Repo: djvpri/zx_parenting (private)

Deploy: Coolify VPS 103.93.129.94, container pd59y3gb3k6nkqjqvm873uim

Akses

  • Login: email/password atau Google OAuth
  • Role: ADMIN (dipromosikan manual via DB)
  • Non-admin → redirect /login?tab=admin
  • Session: JWT cookie (sesiAdmin())

Halaman Admin

| Route | Fungsi | File |

|-------|--------|------|

| /admin | Dashboard overview: summary cards (anak, ortu, tugas, device online) | admin/page.tsx |

| /admin/anak | CRUD anak (list, tambah, edit, hapus) | admin/anak/page.tsx |

| /admin/tugas | CRUD tugas + assign ke anak | admin/tugas/page.tsx |

| /admin/insights | AI insights per anak (Gemini/rule-based) | admin/insights/page.tsx |

| /admin/error-log | Error log dari app anak + ortu | admin/error-log/page.tsx |

Error Log (v3.6.0+)

#### Admin Page: /admin/error-log

  • Server component, query ActivityLog where tipe='error'
  • Summary cards: Total, 24h, App Anak count, App Ortu count
  • Table: Waktu, Sumber (Anak/Ortu + nama), Screen, Message, Usia (red <24h, amber older)
  • Mockup: /mockup-error-log.html (live)

#### Machine-Readable API: GET /api/error-log

  • Auth: ?token=<CRON_TOKEN> (sama pola /api/cron/jadwal-ai)
  • Params:

- limit — max rows (default 50, max 500)

- since — filter age: 24h, 7d, 30d, all (default 24h)

- platform — filter source: anak, ortu, all (default all)

  • Response:
{
  "summary": { "total": 0, "last24h": 0, "anakCount": 0, "ortuCount": 0 },
  "errors": [
    {
      "id": "cmv...",
      "waktu": "2026-10-13T04:24:48.304Z",
      "ageMs": 3600000,
      "sumber": "anak|ortu",
      "nama": "Budi",
      "screen": "LayarKuisAI",
      "message": "kuisStart fail",
      "extra": { "code": 500 }
    }
  ]
}

{

"summary": { "total": 0, "last24h": 0, "anakCount": 0, "ortuCount": 0 },

"errors": [

{

"id": "cmv...",

"waktu": "2026-10-13T04:24:48.304Z",

"ageMs": 3600000,

"sumber": "anak|ortu",

"nama": "Budi",

"screen": "LayarKuisAI",

"message": "kuisStart fail",

"extra": { "code": 500 }

}

]

}

- **AI agent usage:**
  • AI agent usage:
curl "https://zxparenting.zomet.my.id/api/error-log?token=zx-cron-2026&since=24h&platform=anak"

curl "https://zxparenting.zomet.my.id/api/error-log?token=zx-cron-2026&since=24h&platform=anak"


#### Endpoint Error Report (push dari app)

| App | Endpoint | Auth | Body |
|-----|----------|------|------|
| Anak | `POST /api/agent` | `x-device-token` header | `{"aksi":"lapor-error","detail":{"screen":"...","message":"..."}}` |
| Ortu | `POST /api/ortu/lapor-error` | `Authorization: Bearer <jwt>` | `{"screen":"...","message":"...","extra":{}}` |

#### Schema: `ActivityLog`

#### Endpoint Error Report (push dari app)

| App | Endpoint | Auth | Body |

|-----|----------|------|------|

| Anak | POST /api/agent | x-device-token header | {"aksi":"lapor-error","detail":{"screen":"...","message":"..."}} |

| Ortu | POST /api/ortu/lapor-error | Authorization: Bearer <jwt> | {"screen":"...","message":"...","extra":{}} |

#### Schema: ActivityLog

model ActivityLog {
  id        String   @id @default(cuid())
  anakId    String?  // nullable sejak v3.6.0 (error ortu punya no anakId)
  userId    String?  // NEW v3.6.0 (FK → User.id, nullable)
  tipe      String   // 'error' | 'activity' | 'lapor-error' (rewrite → 'error')
  detail    Json?
  createdAt DateTime @default(now())
  anak      Anak?    @relation(fields: [anakId], references: [id])
  user      User?    @relation(fields: [userId], references: [id])
}

model ActivityLog {

id String @id @default(cuid())

anakId String? // nullable sejak v3.6.0 (error ortu punya no anakId)

userId String? // NEW v3.6.0 (FK → User.id, nullable)

tipe String // 'error' | 'activity' | 'lapor-error' (rewrite → 'error')

detail Json?

createdAt DateTime @default(now())

anak Anak? @relation(fields: [anakId], references: [id])

user User? @relation(fields: [userId], references: [id])

}


### Sidebar Navigation

Sidebar Navigation

Dashboard
ā”œā”€ā”€ Anak
ā”œā”€ā”€ Tugas
ā”œā”€ā”€ Insights
└── Error Log ← NEW v3.6.0

Dashboard

ā”œā”€ā”€ Anak

ā”œā”€ā”€ Tugas

ā”œā”€ā”€ Insights

└── Error Log ← NEW v3.6.0


### API Routes (admin)

| Endpoint | Method | Auth | Fungsi |
|----------|--------|------|--------|
| `/api/admin/metrics` | GET | `sesiAdmin()` | Summary stats dashboard |
| `/api/error-log` | GET | `?token=<CRON_TOKEN>` | Machine-readable error JSON |

### Commits
- `0f53b98` — schema + `/api/ortu/lapor-error` + `/admin/error-log` page
- `2fab773` — `/api/error-log` machine-readable JSON endpoint
- Mockup: `mockup-error-log.html` (committed to repo)

API Routes (admin)

| Endpoint | Method | Auth | Fungsi |

|----------|--------|------|--------|

| /api/admin/metrics | GET | sesiAdmin() | Summary stats dashboard |

| /api/error-log | GET | ?token=<CRON_TOKEN> | Machine-readable error JSON |

Commits

  • 0f53b98 — schema + /api/ortu/lapor-error + /admin/error-log page
  • 2fab773 — /api/error-log machine-readable JSON endpoint
  • Mockup: mockup-error-log.html (committed to repo)