init push

This commit is contained in:
2026-07-16 22:16:45 +07:00
commit 8b068bdb10
1021 changed files with 332816 additions and 0 deletions

359
docs/contact-create-flow.md Normal file
View File

@@ -0,0 +1,359 @@
# Contact Create Flow (Frontend Guide)
This document explains the full flow to create a contact, send an automatic invite email, and let the invited user set their first password.
## 1) Prerequisites
1. Admin user is already logged in and has an `access token`.
2. Admin user has already configured a Security PIN.
3. Frontend knows the target `role_id` (fetch from `GET /api/v1/roles/get-all`).
## 2) Endpoints Used
1. `POST /api/v1/auth/pin/verify` to get a PIN `action_token`.
2. `POST /api/v1/contacts/create` to create the contact (auto invite enabled).
3. `POST /api/v1/auth/set-password` to set the first password from invite token.
## 3) Detailed Flow
### Step A - Verify PIN for contact create action
Action ID for contact create:
- `2a59e8b4-5a72-4be3-b8fc-4f49941181c3`
Request:
```json
{
"data": {
"type": "auth_pin_verify",
"attributes": {
"pin": "123456",
"action": "2a59e8b4-5a72-4be3-b8fc-4f49941181c3"
}
}
}
```
Success response:
```json
{
"data": {
"type": "auth_pin_verify",
"attributes": {
"verified": true,
"action_token": "<PIN_ACTION_TOKEN>",
"action_token_ttl_ms": 300000
}
}
}
```
Store `action_token`, then pass it to contact create in header:
- `X-PIN-Verification-Token: <PIN_ACTION_TOKEN>`
### Step B - Create Contact (auto invite)
Endpoint:
- `POST /api/v1/contacts/create`
Required headers:
- `Authorization: Bearer <ACCESS_TOKEN>`
- `X-PIN-Verification-Token: <PIN_ACTION_TOKEN>`
- `Content-Type: application/json`
Minimum required body:
- `data.type = contact_<role>_create`
- `attributes.role_id`
- `attributes.email`
- `attributes.first_name`
- `attributes.last_name`
Allowed create types:
- `contact_pilot_create`
- `contact_doctor_create`
- `contact_air_rescuer_create`
- `contact_technician_create`
- `contact_flight_assistant_create`
- `contact_staff_create`
Rules:
- `data.type` must match the role resolved from `attributes.role_id`.
- Only one role-specific profile object is allowed, and it must match `data.type`.
> Note: backend now **automatically sends an invite email** after successful contact creation.
If PIN token is missing/invalid, API returns `202` with PIN verification required error.
### Step C - User clicks invite link from email
Invite email contains a link like:
- `https://frontend-domain/set-password?token=<INVITE_TOKEN>`
This token already carries user identity (email + user id claims) and is single-use.
### Step D - Frontend submits set password
Endpoint:
- `POST /api/v1/auth/set-password`
Request:
```json
{
"data": {
"type": "auth_set_password",
"attributes": {
"token": "<INVITE_TOKEN>",
"password": "StrongP@ssw0rd123"
}
}
}
```
If successful, user can directly log in using `POST /api/v1/auth/login`.
## 4) Contact Create Payloads by Role
## Pilot
```json
{
"data": {
"type": "contact_pilot_create",
"attributes": {
"role_id": "<ROLE_ID_PILOT>",
"email": "pilot1@example.com",
"username": "pilot1",
"first_name": "Marco",
"last_name": "Melandri",
"mobile_phone": "+628123456789",
"timezone": "Asia/Jakarta",
"is_active": true,
"pilot": {
"pilot_category": "regular",
"short_name": "Marco",
"license_no": "PIL-001",
"license_issued_at": "2025-01-01T00:00:00Z",
"license_expired_at": "2028-01-01T00:00:00Z",
"technician_license_no": "TECH-001",
"has_ifr_qualification": true,
"is_chief_pilot": false,
"is_dul": false,
"total_flight_minutes": 12000,
"responsible_pilot_minutes": 5000,
"second_pilot_minutes": 3000,
"double_command_minutes": 1000,
"flight_instructor_minutes": 500,
"night_flight_minutes": 1200,
"ifr_flight_minutes": 800,
"location": "Jakarta",
"postcode": "12950",
"street_line": "Jl. Sudirman No. 1",
"weight_kg": 72.5,
"photo_file_id": "/uploads/contacts/pilot1.jpg"
}
}
}
}
```
## Doctor
```json
{
"data": {
"type": "contact_doctor_create",
"attributes": {
"role_id": "<ROLE_ID_DOCTOR>",
"email": "doctor1@example.com",
"username": "doctor1",
"first_name": "Alicia",
"last_name": "Pratama",
"mobile_phone": "+628123450001",
"timezone": "Asia/Jakarta",
"is_active": true,
"doctor": {
"short_name": "Dr. Alicia",
"medical_license_no": "MED-DOCTOR-001",
"license_issued_at": "2024-01-01T00:00:00Z",
"license_expired_at": "2029-01-01T00:00:00Z",
"specialization": "Emergency Medicine",
"sub_specialization": "Trauma",
"is_chief_physician": false,
"location": "Jakarta",
"postcode": "12950",
"street_line": "Jl. Jenderal Sudirman No. 10",
"photo_file_id": "/uploads/contacts/doctor1.jpg"
}
}
}
}
```
## Air Rescuer
```json
{
"data": {
"type": "contact_air_rescuer_create",
"attributes": {
"role_id": "<ROLE_ID_AIR_RESCUER>",
"email": "airrescuer1@example.com",
"username": "airrescuer1",
"first_name": "Raka",
"last_name": "Wijaya",
"mobile_phone": "+628123450002",
"timezone": "Asia/Jakarta",
"is_active": true,
"air_rescuer": {
"short_name": "Raka",
"is_chief_physician_in_charge": false,
"responsible_flight_rescuer": false,
"location": "Bandung",
"postcode": "40115",
"street_line": "Jl. Merdeka No. 20",
"photo_file_id": "/uploads/contacts/airrescuer1.jpg"
}
}
}
}
```
## Technician
```json
{
"data": {
"type": "contact_technician_create",
"attributes": {
"role_id": "<ROLE_ID_TECHNICIAN>",
"email": "tech1@example.com",
"username": "tech1",
"first_name": "Bima",
"last_name": "Nugraha",
"mobile_phone": "+628123450003",
"timezone": "Asia/Jakarta",
"is_active": true,
"technician": {
"short_name": "Bima",
"license_no": "TECH-7788",
"is_chief_technician": false,
"is_responsible_complaints": true,
"location": "Surabaya",
"postcode": "60231",
"street_line": "Jl. Diponegoro No. 30",
"photo_file_id": "/uploads/contacts/tech1.jpg"
}
}
}
}
```
## Flight Assistant
```json
{
"data": {
"type": "contact_flight_assistant_create",
"attributes": {
"role_id": "<ROLE_ID_FLIGHT_ASSISTANT>",
"email": "fa1@example.com",
"username": "fa1",
"first_name": "Nadia",
"last_name": "Putri",
"mobile_phone": "+628123450004",
"timezone": "Asia/Jakarta",
"is_active": true,
"flight_assistant": {
"short_name": "Nadia",
"location": "Yogyakarta",
"postcode": "55281",
"street_line": "Jl. Kaliurang No. 15",
"photo_file_id": "/uploads/contacts/fa1.jpg"
}
}
}
}
```
## Staff
```json
{
"data": {
"type": "contact_staff_create",
"attributes": {
"role_id": "<ROLE_ID_STAFF>",
"email": "staff1@example.com",
"username": "staff1",
"first_name": "Dina",
"last_name": "Maharani",
"mobile_phone": "+628123450005",
"timezone": "Asia/Jakarta",
"is_active": true,
"staff": {
"short_name": "Dina",
"role_label": "Operations Staff",
"location": "Semarang",
"postcode": "50132",
"street_line": "Jl. Pandanaran No. 8",
"photo_file_id": "/uploads/contacts/staff1.jpg"
}
}
}
}
```
## 5) Example Create Contact Response
```json
{
"data": {
"type": "contact",
"id": "019ce67e-9289-731d-ae4c-0156902aabbc",
"attributes": {
"role_code": "doctor",
"role_name": "doctor",
"email": "doctor1@example.com",
"first_name": "Alicia",
"last_name": "Pratama",
"is_active": true,
"doctor": {
"short_name": "Dr. Alicia",
"medical_license_no": "MED-DOCTOR-001"
},
"invite_sent": true,
"invite_error": ""
}
}
}
```
If invite sending fails (for example SES/SQS config issue):
- contact create is still successful,
- `invite_sent = false`,
- `invite_error` contains the error message.
## 6) Error Cases Frontend Should Handle
1. `202 PIN verification required` on contact create:
- call `/auth/pin/verify` again (Step A), then retry create with `X-PIN-Verification-Token`.
2. `422 Validation error`:
- check payload fields (invalid role_id, invalid email, etc).
3. `400` on create:
- typically business/constraint conflict.
4. `400` on set-password:
- token invalid/expired/used or password does not meet rules.
## 7) Frontend Integration Summary
1. Admin logs in.
2. Fetch `role_id` from roles list.
3. Verify PIN (`/auth/pin/verify` with contact-create action ID).
4. Create contact (`/contacts/create` + `X-PIN-Verification-Token`).
5. Wait for invited user to open invite email.
6. Set-password page reads `token` from query param.
7. Submit `/auth/set-password` with `{ token, password }`.
8. Redirect user to login page.