Commit 120a50c9 authored by M. Hafidz 's avatar M. Hafidz

.

parent 7488e8f9
......@@ -7,71 +7,7 @@
Diagram ini menunjukkan arsitektur keseluruhan sistem Ortax Omnichannel dari perspektif high-level, mencakup semua layer dari pengguna hingga database.
```mermaid
flowchart TB
subgraph USERS["PENGGUNA"]
AGENT["Agent / Staff<br/>Role: member"]
ADMIN["Admin / Owner<br/>Role: admin, owner"]
CUSTOMER["Customer<br/>External User"]
end
subgraph CHANNELS["EXTERNAL CHANNELS<br/>(Platform API)"]
WA["WhatsApp Business API<br/>(Meta Graph API)"]
IG["Instagram Graph API<br/>(Meta Graph API)"]
TG["Telegram Bot API<br/>(Telegram)"]
FB["Facebook Messenger<br/>(Meta Graph API)"]
TT["TikTok<br/>(Planned)"]
end
subgraph FE["FRONTEND APPLICATION<br/>React 19 + Vite + MUI v7"]
direction TB
FE_AUTH["Auth Module<br/>sign-in, sign-up, forgot/reset password"]
FE_CHAT["Chat Module<br/>82 files - conversations, messages, real-time"]
FE_CAMP["Campaign Module<br/>33 files - templates, recipients, execution"]
FE_TICKET["Ticket Module<br/>25 files - CRUD, lifecycle, attachments"]
FE_SETTINGS["Settings Module<br/>20+ pages - admin panels"]
end
subgraph BE["BACKEND APPLICATION<br/>Hono + Drizzle ORM + Bun"]
direction TB
BE_API["API Gateway<br/>/api/v1"]
BE_AUTH_ENGINE["Better-Auth Engine<br/>session, RBAC, API keys"]
BE_SERVICES["47 Service Modules<br/>business logic layer"]
BE_WEBHOOK["Webhook Receivers<br/>WhatsApp, Instagram, Telegram"]
BE_QUEUE["RabbitMQ Worker<br/>distributed processing"]
end
subgraph REALTIME["REAL-TIME LAYER"]
PUSHER["Pusher<br/>private-room, private-channel, private-post"]
SOCKET["Socket.IO<br/>new-message, new-conversation"]
end
subgraph DB_LAYER["DATABASE & STORAGE"]
PG["PostgreSQL<br/>schema: omnichannel<br/>30+ tables"]
UPLOAD["File Storage<br/>Local / S3 / GCS / Azure"]
end
USERS -->|"Browser<br/>HTTPS"| FE
CUSTOMER -->|"Send Message"| CHANNELS
CHANNELS -->|"Webhook POST"| BE_WEBHOOK
FE -->|"REST API<br/>/api/v1/*"| BE_API
BE_API --> BE_AUTH_ENGINE
BE_API --> BE_SERVICES
BE_SERVICES --> PG
BE_SERVICES --> UPLOAD
BE_WEBHOOK --> BE_QUEUE
BE_QUEUE --> BE_SERVICES
BE_SERVICES -->|"Trigger Events"| REALTIME
REALTIME -->|"Push to Client"| FE
CHANNELS -->|"API Response"| BE_SERVICES
style USERS fill:#E8F5E9,stroke:#4CAF50,color:#1B5E20
style CHANNELS fill:#FFF3E0,stroke:#FF9800,color:#E65100
style FE fill:#E3F2FD,stroke:#2196F3,color:#0D47A1
style BE fill:#FCE4EC,stroke:#E91E63,color:#880E4F
style REALTIME fill:#F3E5F5,stroke:#9C27B0,color:#4A148C
style DB_LAYER fill:#E0F7FA,stroke:#00BCD4,color:#006064
```
![](./images/1.1.png)
---
......@@ -79,50 +15,7 @@ flowchart TB
Diagram ini menunjukkan alur perjalanan pengguna dari pertama kali membuka aplikasi hingga menggunakan fitur-fitur utama.
```mermaid
flowchart TD
START([User Opens App]) --> HAS_SESSION{Has Active<br/>Session Cookie?}
HAS_SESSION -->|"Yes"| AUTH_CHECK{Session Valid?<br/>better-auth verify}
HAS_SESSION -->|"No"| LOGIN["Login Page<br/>/auth/sign-in"]
AUTH_CHECK -->|"Valid"| HAS_WORKSPACE{Has Active<br/>Workspace?}
AUTH_CHECK -->|"Invalid / Expired"| LOGIN
LOGIN --> SIGNIN_PAGE["Sign-In Page<br/>(BetterAuth.SignInPage)"]
SIGNIN_PAGE --> INPUT_CREDS["Input Email & Password"]
INPUT_CREDS --> SUBMIT["POST /api/v1/auth/sign-in<br/>better-auth client"]
SUBMIT --> VALID{Credentials Valid?}
VALID -->|"No"| SHOW_ERR["Show Error Message<br/>'Invalid email or password'"]
SHOW_ERR --> SIGNIN_PAGE
VALID -->|"Yes"| SET_COOKIE["Session Cookie Set<br/>User Context Loaded"]
SET_COOKIE --> HAS_WORKSPACE
HAS_WORKSPACE -->|"No"| CREATE_WS["Create Workspace<br/>/dashboard/workspace/create"]
HAS_WORKSPACE -->|"Yes"| DASHBOARD["Dashboard<br/>Redirect to /dashboard/chat"]
CREATE_WS --> WS_FORM["Workspace Creation Form<br/>Input: workspace name"]
WS_FORM --> WS_API["authClient.organization.create()<br/>+ setActive(organization)"]
WS_API --> DASHBOARD
DASHBOARD --> SIDEBAR{{"Sidebar Navigation<br/>MainLayout"}}
SIDEBAR -->|"Chat"| CHAT["/dashboard/chat<br/>Real-time Conversations"]
SIDEBAR -->|"Social Media"| SOCIAL["/dashboard/social-media<br/>Instagram Posts"]
SIDEBAR -->|"Campaign"| CAMP["/dashboard/campaign<br/>Marketing Campaigns"]
SIDEBAR -->|"Contact"| CONTACT["/dashboard/contact<br/>Contact Management"]
SIDEBAR -->|"Tickets"| TICKET["/dashboard/ticket<br/>Issue Tracking"]
SIDEBAR -->|"Settings"| SETTINGS["/dashboard/settings<br/>Configuration Panel"]
CHAT --> CHAT_FLOW["Chat Flow<br/>(see FE Chat Detail)"]
CAMP --> CAMP_FLOW["Campaign Flow<br/>(see FE Campaign Detail)"]
TICKET --> TICKET_FLOW["Ticket Flow<br/>(see FE Ticket Detail)"]
SETTINGS --> SETTINGS_FLOW["Settings Flow<br/>(see FE Settings Detail)"]
style START fill:#C8E6C9,stroke:#4CAF50
style DASHBOARD fill:#BBDEFB,stroke:#2196F3
style SHOW_ERR fill:#FFCDD2,stroke:#F44336
```
![](./images/1.2.png)
---
......@@ -130,151 +23,16 @@ flowchart TD
Struktur navigasi lengkap yang tersedia di sidebar dashboard.
```mermaid
flowchart LR
subgraph NAV["Dashboard Navigation (Sidebar)"]
direction TB
NAV_MGMT["Management"]
NAV_CHAT["Chat<br/>/dashboard/chat"]
NAV_SOCIAL["Sosial Media<br/>/dashboard/social-media"]
NAV_CAMP["Campaign<br/>/dashboard/campaign"]
NAV_CONTACT["Contact<br/>/dashboard/contact"]
NAV_TICKET["Tickets<br/>/dashboard/ticket"]
NAV_SETTINGS["Settings ▸<br/>/dashboard/settings"]
end
NAV_MGMT --> NAV_CHAT
NAV_MGMT --> NAV_SOCIAL
NAV_MGMT --> NAV_CAMP
NAV_MGMT --> NAV_CONTACT
NAV_MGMT --> NAV_TICKET
NAV_MGMT --> NAV_SETTINGS
NAV_SETTINGS --> SET_TAG["Tags<br/>[admin, owner]"]
NAV_SETTINGS --> SET_USER["Users<br/>[admin, owner]"]
NAV_SETTINGS --> SET_CH["Channels<br/>[admin, owner]"]
NAV_SETTINGS --> SET_WS["Workspaces<br/>[owner only]"]
NAV_SETTINGS --> SET_ACC["My Account<br/>[all roles]"]
NAV_SETTINGS --> SET_OH["Office Hour & Auto Response<br/>[admin, owner]"]
NAV_SETTINGS --> SET_QR["Quick Reply<br/>[admin, owner]"]
NAV_SETTINGS --> SET_API["API Keys<br/>[admin, owner]"]
NAV_SETTINGS --> SET_RC["Category Resolve<br/>[admin, owner]"]
style NAV fill:#F5F5F5,stroke:#9E9E9E
style NAV_SETTINGS fill:#E8EAF6,stroke:#3F51B5
```
![](./images/1.3.png)
---
## 1.4 High-Level Data Flow (Aliran Data Keseluruhan)
```mermaid
flowchart LR
subgraph INPUT["Input Sources"]
USER_ACTION["User Actions<br/>(Click, Type, Submit)"]
WEBHOOK_IN["Channel Webhooks<br/>(WA, IG, TG)"]
end
subgraph PROCESSING["Processing Layer"]
FE_REACT["React App<br/>State Management (SWR)"]
BE_HONO["Hono Server<br/>Route → Service → Repository"]
MQ["RabbitMQ<br/>Work Queue"]
end
subgraph STORAGE["Storage Layer"]
PG["PostgreSQL<br/>30+ Tables"]
FILES["File Storage<br/>Uploads"]
CACHE["SWR Cache<br/>Client-side"]
end
subgraph OUTPUT["Output Channels"]
UI["UI Rendering<br/>React Components"]
PUSH_OUT["Pusher + Socket.IO<br/>Real-time Events"]
PLATFORM_OUT["Platform APIs<br/>Send Messages"]
EMAIL_OUT["Email<br/>Nodemailer"]
end
USER_ACTION --> FE_REACT
WEBHOOK_IN --> BE_HONO
WEBHOOK_IN --> MQ
MQ --> BE_HONO
FE_REACT -->|"HTTP Request"| BE_HONO
BE_HONO --> PG
BE_HONO --> FILES
FE_REACT --> CACHE
BE_HONO --> PUSH_OUT
BE_HONO --> PLATFORM_OUT
BE_HONO --> EMAIL_OUT
PUSH_OUT --> FE_REACT
FE_REACT --> UI
style INPUT fill:#E8F5E9,stroke:#4CAF50
style PROCESSING fill:#E3F2FD,stroke:#2196F3
style STORAGE fill:#FFF3E0,stroke:#FF9800
style OUTPUT fill:#F3E5F5,stroke:#9C27B0
```
![](./images/1.4.png)
---
## 1.5 Multi-Channel Message Flow
```mermaid
flowchart TD
subgraph INCOMING["Pesan Masuk"]
direction LR
C1["WhatsApp Customer"]
C2["Instagram User"]
C3["Telegram User"]
end
subgraph WEBHOOKS["Webhook Processing"]
WH_WA["/api/webhook/whatsapp"]
WH_IG["/api/webhook/instagram"]
WH_TG["/api/webhook/telegram"]
end
subgraph QUEUE["Message Queue"]
RABBIT["RabbitMQ<br/>omnichannel_work_queue"]
end
subgraph BACKEND["Backend Processing"]
PARSE["Parse & Normalize Message"]
FIND_CONTACT["Find or Create Contact"]
FIND_CONV["Find or Create Conversation"]
SAVE_MSG["Save to conversation_messages"]
CHECK_OH{"Within<br/>Office Hours?"}
AUTO_RESP["Send Auto Response<br/>(if configured)"]
NOTIFY["Pusher + Socket.IO<br/>Notify Agents"]
end
subgraph AGENT_UI["Agent Interface"]
FE_RECEIVE["Real-time Update<br/>via Pusher/Socket.IO"]
SWR_UPDATE["SWR Revalidation<br/>Update Conversation List"]
SHOW_MSG["Display New Message<br/>in Chat UI"]
end
C1 -->|"Message"| WH_WA
C2 -->|"DM/Comment"| WH_IG
C3 -->|"Message"| WH_TG
WH_WA -->|"Publish"| RABBIT
WH_IG -->|"Direct"| PARSE
WH_TG -->|"Direct"| PARSE
RABBIT -->|"Consume"| PARSE
PARSE --> FIND_CONTACT --> FIND_CONV --> SAVE_MSG
SAVE_MSG --> CHECK_OH
CHECK_OH -->|"No"| AUTO_RESP
CHECK_OH -->|"Yes"| NOTIFY
AUTO_RESP --> NOTIFY
NOTIFY --> FE_RECEIVE --> SWR_UPDATE --> SHOW_MSG
style INCOMING fill:#E8F5E9,stroke:#4CAF50
style WEBHOOKS fill:#FFF3E0,stroke:#FF9800
style QUEUE fill:#FCE4EC,stroke:#E91E63
style BACKEND fill:#E3F2FD,stroke:#2196F3
style AGENT_UI fill:#F3E5F5,stroke:#9C27B0
```
![](./images/1.5.png)
......@@ -7,51 +7,7 @@
Diagram ini menunjukkan mapping lengkap antara Frontend modules, API endpoints, dan Backend services untuk setiap fitur utama.
```mermaid
flowchart LR
subgraph FE["FRONTEND (React 19)"]
direction TB
FE1["🔐 Auth Pages<br/>sign-in, sign-up,<br/>forgot/reset password"]
FE2["💬 Chat Pages<br/>82 files<br/>conversations, messages"]
FE3["📢 Campaign Pages<br/>33 files<br/>templates, recipients"]
FE4["🎫 Ticket Pages<br/>25 files<br/>CRUD, lifecycle"]
FE5["⚙️ Settings Pages<br/>20+ pages<br/>admin configuration"]
end
subgraph API["API LAYER (Hono /api/v1)"]
direction TB
API1["/auth/*<br/>sign-in, sign-up,<br/>forget-password,<br/>change-password,<br/>admin/*, organization/*"]
API2["/u/conversations<br/>/u/conversation-messages<br/>/u/channels<br/>/u/contacts<br/>/u/notes"]
API3["/u/campaigns<br/>/u/campaign-recipient-lists<br/>/u/campaign-recipient-list-items<br/>/u/templates"]
API4["/u/tickets<br/>/u/ticket-history"]
API5["/u/workspaces<br/>/u/workspace-members<br/>/u/workspace-api-keys<br/>/u/channels<br/>/u/tags<br/>/u/user<br/>/u/role-permissions<br/>/u/message-templates<br/>/u/office-hour-*<br/>/u/conversation-resolve-category"]
end
subgraph BE["BACKEND SERVICES"]
direction TB
BS1["better-auth<br/>session, RBAC,<br/>API keys"]
BS2["conversation<br/>conversation-message<br/>channel<br/>contact<br/>note<br/>tag"]
BS3["campaign<br/>campaign-recipient-list<br/>template<br/>whatsapp-template"]
BS4["ticket<br/>ticket-history<br/>ticket-attachment"]
BS5["workspace<br/>workspace-members<br/>workspace-api-key<br/>role-permission<br/>channel<br/>tag<br/>message-template<br/>office-hour-schedule<br/>office-hour-response<br/>conv-resolve-category"]
end
FE1 -->|"POST /auth/sign-in<br/>POST /auth/forget-password<br/>POST /auth/change-password<br/>POST /auth/admin/*<br/>POST /auth/organization/*"| API1
FE2 -->|"GET /conversations<br/>POST /conversation-messages<br/>PATCH /conversations/:id/status<br/>POST /conversations/:id/read<br/>GET /conversation-messages<br/>POST /conversation-messages/template<br/>POST /conversation-messages/upload-media"| API2
FE3 -->|"CRUD /campaigns<br/>POST /campaigns/:id/execute<br/>CRUD /templates<br/>POST /templates/:id/sync<br/>CRUD /campaign-recipient-lists<br/>POST /templates/upload-media"| API3
FE4 -->|"CRUD /tickets<br/>GET /ticket-history/ticket/:id<br/>POST /tickets/upload-media<br/>DELETE /tickets/remove-media/:id"| API4
FE5 -->|"CRUD /workspaces<br/>CRUD /workspace-members<br/>CRUD /channels<br/>CRUD /tags<br/>PUT /user/update-user<br/>POST /auth/change-password<br/>CRUD /role-permissions<br/>CRUD /message-templates<br/>CRUD /office-hour-*<br/>CRUD /conversation-resolve-category<br/>CRUD /workspace-api-keys"| API5
API1 --> BS1
API2 --> BS2
API3 --> BS3
API4 --> BS4
API5 --> BS5
style FE fill:#E3F2FD,stroke:#1976D2
style API fill:#FFF8E1,stroke:#F9A825
style BE fill:#FCE4EC,stroke:#C2185B
```
![](./images/2.1.png)
---
......@@ -59,62 +15,7 @@ flowchart LR
Alur komunikasi real-time dari webhook masuk hingga update UI di browser agent.
```mermaid
flowchart TD
subgraph EXTERNAL["EXTERNAL CHANNELS"]
WA_MSG["📱 WhatsApp<br/>Message from Customer"]
IG_MSG["📸 Instagram<br/>DM / Comment"]
TG_MSG["✈️ Telegram<br/>Message from User"]
end
subgraph BE_RECEIVE["BACKEND - Webhook Receivers"]
WH_WA["POST /api/webhook/whatsapp"]
WH_IG["POST /api/webhook/instagram"]
WH_TG["POST /api/webhook/telegram"]
end
subgraph BE_PROCESS["BACKEND - Processing"]
RABBIT["RabbitMQ Queue<br/>omnichannel_work_queue"]
PROCESSOR["Message Processor Service"]
DB_WRITE["PostgreSQL<br/>INSERT conversations<br/>INSERT conversation_messages"]
PUSHER_S["Pusher Server<br/>Trigger Events"]
SOCKET_S["Socket.IO Server<br/>Emit Events"]
end
subgraph FE_RECEIVE["FRONTEND - Real-time Clients"]
PUSHER_C["Pusher Client<br/>use-pusher-chat.ts<br/>Subscribe: private-room:*<br/>private-channel:*"]
SOCKET_C["Socket.IO Client<br/>use-socket-io.ts<br/>Listen: new-message<br/>new-conversation"]
SWR_CACHE["SWR Cache<br/>Automatic Revalidation<br/>mutate() on events"]
UI["UI Components<br/>ChatNav, ChatMessageList<br/>Auto Update"]
end
WA_MSG -->|"HTTP POST"| WH_WA
IG_MSG -->|"HTTP POST"| WH_IG
TG_MSG -->|"HTTP POST"| WH_TG
WH_WA -->|"Publish to Queue"| RABBIT
WH_IG -->|"Direct Process"| PROCESSOR
WH_TG -->|"Direct Process"| PROCESSOR
RABBIT -->|"Consume"| PROCESSOR
PROCESSOR --> DB_WRITE
PROCESSOR --> PUSHER_S
PROCESSOR --> SOCKET_S
PUSHER_S -->|"private-room:{convId}<br/>Event: new-message"| PUSHER_C
PUSHER_S -->|"private-channel:{chId}<br/>Event: new-conversation"| PUSHER_C
SOCKET_S -->|"Event: new-message"| SOCKET_C
SOCKET_S -->|"Event: conversation-updated"| SOCKET_C
PUSHER_C --> SWR_CACHE
SOCKET_C --> SWR_CACHE
SWR_CACHE --> UI
style EXTERNAL fill:#E8F5E9,stroke:#4CAF50
style BE_RECEIVE fill:#FFF3E0,stroke:#FF9800
style BE_PROCESS fill:#FCE4EC,stroke:#E91E63
style FE_RECEIVE fill:#E3F2FD,stroke:#2196F3
```
![](./images/2.2.png)
---
......@@ -122,34 +23,7 @@ flowchart TD
Tabel event real-time yang digunakan dalam sistem.
```mermaid
flowchart TD
subgraph EVENTS["Real-Time Events"]
direction TB
E1["new-message<br/>Pesan baru masuk/terkirim"]
E2["new-conversation<br/>Conversation baru dibuat"]
E3["message-updated<br/>Status pesan berubah<br/>(SENT → DELIVERED → READ)"]
E4["conversation-updated<br/>Status/assignee conversation berubah"]
E5["new-post-comment<br/>Komentar baru di Instagram post"]
E6["post-updated<br/>Instagram post diupdate"]
end
subgraph CHANNELS["Pusher Channels"]
C1["private-room:{conversationId}<br/>Event: new-message, message-updated"]
C2["private-channel:{channelId}<br/>Event: new-conversation, conversation-updated"]
C3["private-post:{postId}<br/>Event: new-post-comment, post-updated"]
end
E1 --> C1
E2 --> C2
E3 --> C1
E4 --> C2
E5 --> C3
E6 --> C3
style EVENTS fill:#F3E5F5,stroke:#9C27B0
style CHANNELS fill:#E8EAF6,stroke:#3F51B5
```
![](./images/2.3.png)
---
......@@ -157,79 +31,13 @@ flowchart TD
Alur integrasi autentikasi antara FE dan BE.
```mermaid
flowchart TD
subgraph FE_AUTH["FRONTEND - Auth Context"]
BA_CLIENT["better-auth client<br/>/src/lib/better-auth.ts<br/>Base URL: /api/v1/auth"]
AUTH_PROVIDER["AuthProvider<br/>/src/auth/context/better-auth/"]
AUTH_GUARD["AuthGuard<br/>GuestGuard<br/>WorkspaceGuard<br/>RouteRoleGuard"]
end
subgraph API_AUTH["API ENDPOINTS"]
SIGN_IN["POST /api/v1/auth/sign-in/email-password"]
SIGN_UP["POST /api/v1/auth/sign-up/email-password"]
FORGOT["POST /api/v1/auth/forget-password"]
RESET["POST /api/v1/auth/reset-password"]
CHANGE["POST /api/v1/auth/change-password"]
SESSION["GET /api/v1/auth/get-session"]
ADMIN_API["POST /api/v1/auth/admin/*"]
ORG_API["POST /api/v1/auth/organization/*"]
end
subgraph BE_AUTH["BACKEND - better-auth Engine"]
BA_HANDLER["better-auth handler<br/>/src/services/better-auth/"]
SESSION_MW["requireAuth Middleware<br/>Session verification"]
PERM_MW["requirePermission Middleware<br/>RBAC check"]
APIKEY_MW["requireWorkspaceApiKey Middleware<br/>x-api-key verification"]
end
BA_CLIENT -->|"HTTP Request"| API_AUTH
AUTH_PROVIDER --> BA_CLIENT
AUTH_GUARD --> AUTH_PROVIDER
API_AUTH --> BA_HANDLER
BA_HANDLER -->|"Set Session Cookie"| BA_CLIENT
SESSION_MW -->|"Verify Session"| BA_HANDLER
PERM_MW -->|"Check Role + Permission"| BA_HANDLER
APIKEY_MW -->|"Verify API Key"| BA_HANDLER
style FE_AUTH fill:#E3F2FD,stroke:#1976D2
style API_AUTH fill:#FFF8E1,stroke:#F9A825
style BE_AUTH fill:#FCE4EC,stroke:#C2185B
```
![](./images/2.4.png)
---
## 2.5 File Upload Integration Flow
```mermaid
flowchart TD
FE_UPLOAD["Frontend<br/>File Upload Component<br/>(multipart/form-data)"] --> ROUTE_CHECK{Upload Type?}
ROUTE_CHECK -->|"Chat Media"| CHAT_API["POST /api/v1/u/conversation-messages/upload-media<br/>body: {file, mediaType, conversationId}"]
ROUTE_CHECK -->|"Ticket Media"| TICKET_API["POST /api/v1/u/tickets/upload-media<br/>body: {file, ticketId?}"]
ROUTE_CHECK -->|"Template Media"| TEMPL_API["POST /api/v1/u/templates/upload-media<br/>body: {file, mediaType, channelId}<br/>(Meta Resumable API)"]
ROUTE_CHECK -->|"Channel Icon"| ICON_API["POST /api/v1/u/channels/:id/upload-icon<br/>body: {file}"]
ROUTE_CHECK -->|"Profile Photo"| PROF_API["PUT /api/v1/u/user/update-user<br/>body: {photo}"]
CHAT_API --> FILE_SERVICE["File Storage Service"]
TICKET_API --> FILE_SERVICE
TEMPL_API --> META_API["Meta Graph API<br/>Resumable Upload"]
ICON_API --> FILE_SERVICE
PROF_API --> FILE_SERVICE
FILE_SERVICE --> STORAGE_CHECK{Storage Type?}
STORAGE_CHECK -->|"LOCAL"| LOCAL["Local Filesystem<br/>/uploads/*"]
STORAGE_CHECK -->|"S3"| S3["AWS S3"]
STORAGE_CHECK -->|"GCS"| GCS["Google Cloud Storage"]
STORAGE_CHECK -->|"AZURE"| AZURE["Azure Blob Storage"]
META_API --> META_STORE["Meta Servers"]
style FE_UPLOAD fill:#E3F2FD,stroke:#1976D2
style STORAGE_CHECK fill:#FFF8E1,stroke:#F9A825
```
![](./images/2.5.png)
---
......@@ -237,31 +45,4 @@ flowchart TD
Diagram ini menunjukkan urutan middleware yang dilalui setiap request dari FE ke BE.
```mermaid
flowchart TD
REQUEST["Incoming HTTP Request"] --> CORS["① CORS Middleware<br/>cors.config.ts<br/>Allowed origins validation"]
CORS --> TIMING["② Timing Middleware<br/>Response time header"]
TIMING --> REQ_ID["③ Request ID<br/>Unique request identifier"]
REQ_ID --> LOGGER["④ Logger Middleware<br/>Request logging"]
LOGGER --> BODY_LIMIT["⑤ Body Limit Middleware<br/>5MB default<br/>100MB WhatsApp uploads"]
BODY_LIMIT --> TIMEOUT["⑥ Timeout Middleware<br/>5min default<br/>15min uploads"]
TIMEOUT --> ROUTE_MATCH{Route Match?}
ROUTE_MATCH -->|"/api/v1/auth/*"| AUTH_ROUTE["better-auth handler<br/>(self-handled)"]
ROUTE_MATCH -->|"/api/webhook/*"| WEBHOOK_ROUTE["Webhook Routes<br/>(WhatsApp CORS only)"]
ROUTE_MATCH -->|"/api/v1/u/*"| PROTECTED["Protected Route Chain"]
ROUTE_MATCH -->|"/uploads/*"| UPLOAD_ROUTE["File Storage Route<br/>(+ upload timeout)"]
ROUTE_MATCH -->|"/api/v1/swagger"| SWAGGER["Swagger UI"]
PROTECTED --> AUTH_MW["⑦ requireAuth<br/>Session verification"]
AUTH_MW --> PERM_MW["⑧ requirePermission<br/>RBAC: resource:action"]
PERM_MW --> HANDLER["Route Handler<br/>→ Service → Repository → DB"]
AUTH_ROUTE --> HANDLER2["better-auth internal handler"]
WEBHOOK_ROUTE --> HANDLER3["Webhook processor"]
style REQUEST fill:#C8E6C9,stroke:#4CAF50
style HANDLER fill:#BBDEFB,stroke:#2196F3
style HANDLER2 fill:#BBDEFB,stroke:#2196F3
style HANDLER3 fill:#BBDEFB,stroke:#2196F3
```
![](./images/2.6.png)
......@@ -7,57 +7,7 @@
Diagram lengkap struktur routing Frontend dari root hingga setiap halaman.
```mermaid
flowchart TD
ROOT["/ (Root Route)<br/>src/app.tsx"] --> MAIN_LAYOUT["MainLayout<br/>(Public Pages)"]
ROOT --> AUTH_LAYOUT["AuthSplitLayout<br/>(Auth Pages)"]
ROOT --> SIMPLE_LAYOUT["SimpleLayout<br/>(Minimal Pages)"]
ROOT --> DASH_LAYOUT["DashboardLayout<br/>(Protected Pages)"]
subgraph PUBLIC["Public Routes"]
MAIN_LAYOUT --> HOME["/ — HomePage"]
MAIN_LAYOUT --> ABOUT["/about-us"]
MAIN_LAYOUT --> PRIVACY["/privacy-policy"]
MAIN_LAYOUT --> TERMS["/terms-of-service"]
SIMPLE_LAYOUT --> MAINT["/maintenance"]
end
subgraph AUTH_ROUTES["Auth Routes (GuestGuard)"]
AUTH_LAYOUT --> GUEST["GuestGuard<br/>(Block if logged in)"]
GUEST --> SIGNIN["/auth/sign-in<br/>BetterAuth.SignInPage"]
GUEST --> FORGOT["/auth/forgot-password<br/>BetterAuth.ForgotPasswordPage"]
GUEST --> RESET["/auth/reset-password<br/>BetterAuth.ResetPasswordPage"]
GUEST --> SIGNUP["/auth/sign-up<br/>(Hidden - Invite Only)"]
GUEST --> ACCEPT_INV["/accept-invitation/:id<br/>(+ ReCAPTCHA)"]
end
subgraph ERROR_ROUTES["Error Routes"]
ROOT --> E403["/error/403 — Forbidden"]
ROOT --> E404["/error/404 — Not Found"]
ROOT --> E500["/error/500 — Server Error"]
end
subgraph DASH["Dashboard Routes (AuthGuard + WorkspaceGuard)"]
DASH_LAYOUT --> AUTH_G["AuthGuard<br/>Check Session"]
AUTH_G --> WS_G["WorkspaceGuard<br/>Check Active Workspace"]
WS_G --> DASH_NAV{{"Dashboard Navigation"}}
DASH_NAV --> CHAT_R["/dashboard/chat"]
DASH_NAV --> SOCIAL["/dashboard/social-media"]
DASH_NAV --> WS_CREATE["/dashboard/workspace/create"]
DASH_NAV --> TAG_NAV["/dashboard/tag<br/>/new /:id/edit"]
DASH_NAV --> CONTACT["/dashboard/contact"]
DASH_NAV --> CAMP_NAV["/dashboard/campaign/*"]
DASH_NAV --> TICK_NAV["/dashboard/ticket/*"]
DASH_NAV --> SET_NAV["/dashboard/settings/*"]
end
style ROOT fill:#C8E6C9,stroke:#4CAF50
style DASH fill:#E3F2FD,stroke:#1976D2
style AUTH_ROUTES fill:#FFF3E0,stroke:#FF9800
style PUBLIC fill:#F3E5F5,stroke:#9C27B0
```
![](./images/3.1.png)
---
......@@ -65,56 +15,7 @@ flowchart TD
Alur autentikasi lengkap dari halaman login hingga masuk dashboard.
```mermaid
flowchart TD
START([User Opens App]) --> VISIT{"Current URL?"}
VISIT -->|"/auth/*"| GUEST_CHECK{GuestGuard<br/>Is Logged In?}
VISIT -->|"/dashboard/*"| AUTH_CHECK{AuthGuard<br/>Has Session?}
VISIT -->|Other| PUBLIC_PAGE["Show Public Page"]
GUEST_CHECK -->|Yes| REDIRECT_DASH["Redirect to<br/>/dashboard/chat"]
GUEST_CHECK -->|No| AUTH_PAGE["Show Auth Page"]
AUTH_CHECK -->|No| REDIRECT_LOGIN["Redirect to<br/>/auth/sign-in"]
AUTH_CHECK -->|Yes| WS_CHECK{WorkspaceGuard<br/>Has Workspace?}
WS_CHECK -->|No| REDIRECT_WS["Redirect to<br/>/dashboard/workspace/create"]
WS_CHECK -->|Yes| ROLE_CHECK{RouteRoleGuard<br/>Has Permission?}
ROLE_CHECK -->|No| REDIRECT_403["Redirect to<br/>/error/403"]
ROLE_CHECK -->|Yes| SHOW_PAGE["Show Dashboard Page"]
subgraph SIGNIN_FLOW["Sign-In Process"]
AUTH_PAGE --> SIGNIN["BetterAuth.SignInPage"]
SIGNIN --> INPUT["Input Email + Password"]
INPUT --> CLICK["Click Sign In"]
CLICK --> API_CALL["authClient.signIn.email()<br/>POST /api/v1/auth/sign-in/email-password"]
API_CALL --> SUCCESS{Success?}
SUCCESS -->|No| ERR_MSG["Show Error<br/>'Invalid credentials'"]
SUCCESS -->|Yes| SESSION_SET["Session Cookie Set<br/>AuthProvider Context Updated"]
SESSION_SET --> REDIRECT_DASH
ERR_MSG --> INPUT
end
subgraph INVITE_FLOW["Invitation Flow"]
ACCEPT_INV["/accept-invitation/:id"] --> CAPTCHA["ReCAPTCHA Verification"]
CAPTCHA --> VERIFY{Captcha Valid?}
VERIFY -->|No| CAPTCHA
VERIFY -->|Yes| ACCEPT_API["authClient.organization<br/>.acceptInvitation(id)"]
ACCEPT_API --> SUCCESS_INV{Success?}
SUCCESS_INV -->|No| INV_ERR["Show Error"]
SUCCESS_INV -->|Yes| SET_ACTIVE["SetActive Organization"]
SET_ACTIVE --> REDIRECT_DASH
INV_ERR --> CAPTCHA
end
style START fill:#C8E6C9,stroke:#4CAF50
style REDIRECT_LOGIN fill:#FFCDD2,stroke:#F44336
style REDIRECT_403 fill:#FFCDD2,stroke:#F44336
style SHOW_PAGE fill:#BBDEFB,stroke:#2196F3
style SIGNIN fill:#FFF8E1,stroke:#F9A825
```
![](./images/3.2.png)
---
......@@ -128,95 +29,7 @@ Modul Chat adalah modul terbesar (82 files). Diagram ini menunjukkan alur lengka
> - **Infinite Scroll**: Hook `use-message-list-infinite-scroll.ts` dan `use-message-list-scroll-persistence.ts` masih di-import di `chat-message-list.tsx`, tapi **tidak pernah ter-trigger** karena `hasMore` selalu `false` (karena response pagination dummy). Hook `use-infinite-scroll.ts` dan `use-messages-scroll.ts` adalah **dead code** (tidak di-import di mana pun).
> - **Auto-Scroll**: Hook `use-message-list-initial-load.ts` tetap aktif untuk auto-scroll to bottom saat initial load.
```mermaid
flowchart TD
CHAT_PAGE["Chat Page<br/>/dashboard/chat"] --> INIT["Initialize ChatContext"]
INIT --> LOAD_CHANNELS["Load Channels<br/>GET /channels/with-unread-count"]
INIT --> LOAD_COUNTS["Load Chat Counts<br/>GET /conversations/total-counts-chat"]
INIT --> CONNECT_RT["Connect Real-time<br/>Pusher + Socket.IO"]
LOAD_CHANNELS --> RENDER_LAYOUT["Render ChatLayout<br/>(3-Column Layout)"]
LOAD_COUNTS --> RENDER_LAYOUT
CONNECT_RT --> RENDER_LAYOUT
subgraph LEFT_PANEL["LEFT PANEL — ChatNav"]
RENDER_LAYOUT --> CH_NAV["chat-nav.tsx"]
CH_NAV --> CH_FOOTER["chat-channel-list-footer.tsx<br/>Channel filter tabs"]
CH_FOOTER --> CH_SEARCH["chat-nav-search-results.tsx<br/>Search conversations"]
CH_SEARCH --> CH_ITEM["chat-nav-item.tsx<br/>Conversation list item"]
CH_ITEM --> CH_LIST["Conversation List (Full GET)<br/>use-conversation-pagination.ts<br/>⚠️ BE route terima page/limit<br/>tapi repository tidak pakai"]
end
subgraph FILTERS["Conversation Filters"]
FILTER_GROUP["Filter Options:"] --> F1["Channel Filter<br/>(WhatsApp, Instagram, etc.)"]
FILTER_GROUP --> F2["Status Filter<br/>All / My / Unassigned / Assigned / Resolved"]
FILTER_GROUP --> F3["Tag Filter"]
FILTER_GROUP --> F4["Date Range Filter"]
FILTER_GROUP --> F5["24h Window Filter<br/>(WhatsApp only)"]
FILTER_GROUP --> F6["First Response Filter"]
end
CH_LIST --> FILTERS
subgraph CENTER_PANEL["CENTER PANEL — ChatRoom"]
CH_LIST -->|"Select Conversation"| LOAD_MSG["Load All Messages (Full GET)<br/>GET /conversation-messages<br/>use-message-list-initial-load.ts<br/>⚠️ BE limit/offset di-comment out<br/>→ returns all messages"]
LOAD_MSG --> MSG_LIST["chat-message-list.tsx<br/>Message list<br/>(auto-scroll to bottom on load)"]
MSG_LIST --> MSG_ITEM["chat-message-item.tsx<br/>Individual message bubble"]
MSG_ITEM --> MSG_TYPES["Message Types:<br/>TEXT, IMAGE, VIDEO, AUDIO,<br/>DOCUMENT, TEMPLATE, SYSTEM,<br/>INTERNAL_NOTE"]
MSG_TYPES --> DATE_SEP["chat-date-separator.tsx<br/>Date separators between messages"]
end
subgraph INPUT_AREA["MESSAGE INPUT"]
MSG_LIST --> MSG_INPUT["chat-message-input.tsx"]
MSG_INPUT --> TEXT_INPUT["bubble-editor.tsx<br/>(TipTap Rich Editor)"]
MSG_INPUT --> FILE_BTN["File Upload<br/>use-file-upload.ts"]
MSG_INPUT --> EMOJI_BTN["Emoji Picker<br/>use-emoji-picker.ts"]
MSG_INPUT --> QUICK_BTN["Quick Message<br/>quick-message-overlay.tsx<br/>use-quick-message.ts"]
MSG_INPUT --> TEMPL_BTN["WhatsApp Template<br/>chat-template-dialog.tsx<br/>use-template-dialog.ts"]
MSG_INPUT --> REPLY_BTN["Reply to Message"]
MSG_INPUT --> SEND_BTN["Send Button"]
end
subgraph SEND_FLOW["SEND MESSAGE FLOW"]
TEXT_INPUT --> SEND_BTN
FILE_BTN --> UPLOAD["use-file-upload.ts<br/>POST /upload-media"]
UPLOAD --> SEND_BTN
QUICK_BTN --> INSERT_QM["Insert Quick Message Text"]
TEMPL_BTN --> SEND_TEMPL["POST /conversation-messages/template"]
SEND_BTN -->|"POST /conversation-messages<br/>(JSON or Multipart)"| API_SEND
INSERT_QM --> TEXT_INPUT
end
subgraph RT_UPDATE["REAL-TIME UPDATE"]
API_SEND -->|"Message Sent"| PUSHER_RECV["use-pusher-chat.ts<br/>Listen: new-message"]
PUSHER_RECV --> SWR_MUTATE["SWR mutate()<br/>Revalidate messages cache"]
SWR_MUTATE --> UI_UPDATE["Update Message List<br/>(Optimistic UI)"]
end
subgraph RIGHT_PANEL["RIGHT PANEL — Details Sidebar"]
RENDER_LAYOUT --> INFO_TAB["Information Tab<br/>chat-room-information.tsx<br/>Contact details, tags, channels"]
RENDER_LAYOUT --> NOTE_TAB["Notes Tab<br/>note-list, note-item,<br/>note-detail, note-form<br/>CRUD: POST /notes"]
RENDER_LAYOUT --> TICKET_TAB["Ticket Tab<br/>ticket-list, ticket-item,<br/>ticket-detail, ticket-form<br/>Linked tickets to conversation"]
end
subgraph HEADER_ACTIONS["HEADER ACTIONS"]
RENDER_LAYOUT --> CH_HEADER["chat-header-details.tsx"]
CH_HEADER --> ASSIGN["Assign Conversation<br/>chat-assign-dialog.tsx<br/>PATCH /conversations/:id/status"]
CH_HEADER --> RESOLVE["Resolve Conversation<br/>chat-resolve-dialog.tsx<br/>PATCH /conversations/:id/status"]
CH_HEADER --> HISTORY["View History<br/>chat-room-history.tsx<br/>GET /conversations/:id/history"]
CH_HEADER --> GET_NEW["Get New Chat<br/>POST /conversations/get-new-chat<br/>(Claim next unassigned)"]
CH_HEADER --> SEARCH_MSG["Search Messages<br/>chat-search-dialog.tsx<br/>GET /conversation-messages/search"]
CH_HEADER --> BULK_RESOLVE["Bulk Resolve<br/>bulk-resolve-modal.tsx<br/>POST /conversations/bulk-resolve"]
end
style CHAT_PAGE fill:#E3F2FD,stroke:#1976D2
style LEFT_PANEL fill:#E8F5E9,stroke:#4CAF50
style CENTER_PANEL fill:#FFF3E0,stroke:#FF9800
style RIGHT_PANEL fill:#F3E5F5,stroke:#9C27B0
style SEND_FLOW fill:#FFEBEE,stroke:#F44336
style RT_UPDATE fill:#E0F7FA,stroke:#00BCD4
```
![](./images/3.3.png)
---
......@@ -224,88 +37,7 @@ flowchart TD
Alur lengkap modul Campaign dari pembuatan template hingga eksekusi.
```mermaid
flowchart TD
CAMP_PAGE["Campaign Page<br/>/dashboard/campaign"] --> CAMP_LAYOUT["campaign-layout.tsx<br/>(Tab Navigation)"]
CAMP_LAYOUT --> TAB_CAMPAIGNS["Tab: Campaigns"]
CAMP_LAYOUT --> TAB_TEMPLATES["Tab: Templates"]
CAMP_LAYOUT --> TAB_RECIPIENTS["Tab: Recipient Lists"]
subgraph CAMPAIGNS_TAB["CAMPAIGNS MANAGEMENT"]
TAB_CAMPAIGNS --> CAMP_LIST["campaign-view.tsx<br/>Campaign List DataTable"]
CAMP_LIST --> CAMP_TOOLBAR["campaign-table-toolbar.tsx<br/>Filters + Search"]
CAMP_LIST --> CAMP_ROW["campaign-table-row.tsx<br/>Campaign Row"]
CAMP_ROW --> CAMP_ACTIONS["Campaign Actions"]
CAMP_ACTIONS --> VIEW_DETAIL["View Detail<br/>/dashboard/campaign/:id<br/>campaign-detail-view.tsx"]
CAMP_ACTIONS --> EDIT["Edit Campaign<br/>/dashboard/campaign/:id/edit<br/>campaign-create-view.tsx"]
CAMP_ACTIONS --> EXECUTE["Execute Campaign<br/>POST /campaigns/:id/execute"]
CAMP_ACTIONS --> DELETE["Delete Campaign<br/>DELETE /campaigns/:id"]
CAMP_LIST --> CREATE_BTN["+ New Campaign"]
CREATE_BTN --> CREATE_FORM["campaign-create-view.tsx<br/>campaign-create-form.tsx"]
CREATE_FORM --> FORM_STEP1["Step 1: Campaign Name<br/>& Description"]
FORM_STEP1 --> FORM_STEP2["Step 2: Select Channel<br/>(WhatsApp channel only)"]
FORM_STEP2 --> FORM_STEP3["Step 3: Select Template<br/>(from approved templates)"]
FORM_STEP3 --> FORM_STEP4["Step 4: Select Recipient List<br/>(from recipient lists)"]
FORM_STEP4 --> SAVE_DRAFT["Save as DRAFT<br/>POST /campaigns"]
end
subgraph TEMPLATES_TAB["TEMPLATE MANAGEMENT"]
TAB_TEMPLATES --> TEMPL_LIST["campaign-templates-view.tsx<br/>Template List"]
TEMPL_LIST --> TEMPL_TOOLBAR["template-table-toolbar.tsx"]
TEMPL_LIST --> TEMPL_ROW["template-table-row.tsx"]
TEMPL_ROW --> TEMPL_ACTIONS["Template Actions"]
TEMPL_ACTIONS --> VIEW_TEMPL["View Detail<br/>campaign-templates-detail.tsx"]
TEMPL_ACTIONS --> EDIT_TEMPL["Edit Template<br/>campaign-template-edit.tsx"]
TEMPL_ACTIONS --> SYNC_TEMPL["Sync to WhatsApp<br/>POST /templates/:id/sync"]
TEMPL_ACTIONS --> DEL_TEMPL["Delete Template<br/>DELETE /templates/:id"]
TEMPL_LIST --> CREATE_TEMPL["+ New Template"]
CREATE_TEMPL --> TEMPL_CREATE["campaign-template-create.tsx"]
TEMPL_CREATE --> TEMPL_FORM["template-form-fields.tsx<br/>template-form-schema.ts"]
TEMPL_FORM --> TEMPL_HEADER["Header Component<br/>(Text / Image / Video)"]
TEMPL_FORM --> TEMPL_BODY["Body Component<br/>(Text + Variables {{1}}, {{2}}...)"]
TEMPL_FORM --> TEMPL_FOOTER["Footer Component<br/>(Text)"]
TEMPL_FORM --> TEMPL_BUTTONS["Buttons Component<br/>(Quick Reply / URL / Phone)"]
TEMPL_BODY --> UPLOAD_MEDIA["Upload Header Media<br/>POST /templates/upload-media<br/>(Meta Resumable API)"]
TEMPL_FORM --> SAVE_TEMPL["Create Template<br/>POST /templates"]
SAVE_TEMPL --> SYNC_WA["Sync to WhatsApp<br/>POST /templates/:id/sync"]
end
subgraph RECIPIENTS_TAB["RECIPIENT LIST MANAGEMENT"]
TAB_RECIPIENTS --> RL_LIST["campaign-recipient-lists-view.tsx"]
RL_LIST --> RL_TOOLBAR["recipient-list-table-toolbar.tsx"]
RL_LIST --> RL_ROW["recipient-list-table-row.tsx"]
RL_ROW --> RL_ACTIONS["List Actions"]
RL_ACTIONS --> VIEW_RL["View List Detail<br/>campaign-recipient-list-detail.tsx"]
RL_ACTIONS --> EDIT_RL["Edit List"]
RL_ACTIONS --> DEL_RL["Delete List<br/>DELETE /campaign-recipient-lists/:id"]
RL_LIST --> CREATE_RL["+ New Recipient List"]
CREATE_RL --> WIZARD["recipient-list-create-wizard.tsx<br/>(Step-by-step Wizard)"]
WIZARD --> W_STEP1["Step 1: Name & Description<br/>+ Variable Definitions<br/>(e.g. {{nama}}, {{no_pesanan}})"]
W_STEP1 --> W_STEP2["Step 2: Select Contacts<br/>recipient-list-select-contacts-dialog.tsx<br/>(From contact database)"]
W_STEP2 --> W_STEP3["Step 3: Input Variable Values<br/>recipient-list-input-variables-dialog.tsx<br/>(Per contact: nama=John, no_pesanan=123)"]
W_STEP3 --> SAVE_RL["Save Recipient List<br/>POST /campaign-recipient-lists"]
VIEW_RL --> ITEMS_LIST["List Items<br/>campaign-recipient-list-item-table-row.tsx"]
ITEMS_LIST --> ADD_CONTACTS["Add More Contacts<br/>recipient-list-add-contacts-dialog.tsx<br/>POST /campaign-recipient-lists/:id/contacts"]
ITEMS_LIST --> RM_CONTACTS["Remove Contacts<br/>DELETE /campaign-recipient-lists/:id/contacts"]
end
subgraph EXECUTION_FLOW["CAMPAIGN EXECUTION"]
EXECUTE --> EXEC_API["POST /campaigns/:id/execute"]
EXEC_API --> BE_PROCESS["Backend Processing<br/>(See BE Campaign Flow)"]
BE_PROCESS --> DELIVERY_WEBHOOK["WhatsApp Delivery Webhooks<br/>→ Update per-recipient status"]
DELIVERY_WEBHOOK --> VIEW_STATS["View Campaign Stats<br/>sent / delivered / read / failed"]
end
style CAMPAIGNS_TAB fill:#E8F5E9,stroke:#4CAF50
style TEMPLATES_TAB fill:#FFF3E0,stroke:#FF9800
style RECIPIENTS_TAB fill:#F3E5F5,stroke:#9C27B0
style EXECUTION_FLOW fill:#FFEBEE,stroke:#F44336
```
![](./images/3.4.png)
---
......@@ -313,75 +45,7 @@ flowchart TD
Alur lengkap modul Ticket dari pembuatan hingga lifecycle management.
```mermaid
flowchart TD
TICKET_PAGE["Ticket Page<br/>/dashboard/ticket"] --> TICKET_LIST["ticket-list-view.tsx<br/>Ticket DataTable"]
TICKET_LIST --> TOOLBAR["ticket-table-toolbar.tsx<br/>Status Filter | Assignee Filter<br/>Contact Search"]
TICKET_LIST --> TABLE["Ticket Table Columns:<br/>Number, Title, Status,<br/>Assignee, Created, Updated"]
TABLE --> CREATE_BTN["+ New Ticket"]
TABLE --> SELECT_ROW["Select Ticket → Detail View"]
TABLE --> BATCH_ACTIONS["Batch Actions<br/>(Checkbox Selection)"]
subgraph CREATE_FLOW["CREATE TICKET"]
CREATE_BTN --> CREATE_VIEW["ticket-create-view.tsx<br/>(Standalone)"]
CREATE_VIEW --> CREATE_FORM["ticket-create-form.tsx"]
CREATE_FORM --> FORM_TITLE["Ticket Title *"]
CREATE_FORM --> FORM_DESC["Description *"]
CREATE_FORM --> FORM_CONV["Link Conversation<br/>(optional - select from list)"]
CREATE_FORM --> FORM_CONTACT["Link Contact<br/>(optional - select from list)"]
CREATE_FORM --> FORM_ATTACH["Attachments<br/>POST /tickets/upload-media"]
CREATE_FORM --> FORM_TEMPL["Use Template<br/>/dashboard/ticket/templates"]
FORM_ATTACH --> SUBMIT["POST /tickets<br/>→ Status: TODO"]
FORM_TITLE --> SUBMIT
FORM_DESC --> SUBMIT
end
subgraph DETAIL_FLOW["TICKET DETAIL VIEW"]
SELECT_ROW --> DETAIL["ticket-detail-view.tsx<br/>/dashboard/ticket/:id"]
DETAIL --> INFO["Ticket Info<br/>Number, Title, Description,<br/>Status, Assignee, Dates"]
DETAIL --> ATTACHMENTS["ticket-attachment-preview.tsx<br/>File attachments"]
DETAIL --> HISTORY["ticket-history-timeline.tsx<br/>Status change history"]
DETAIL --> ACTION_BUTTONS["Status Action Buttons"]
end
subgraph ACTIONS["TICKET LIFECYCLE ACTIONS"]
ACTION_BUTTONS --> ASSIGN["Assign to Agent<br/>ticket-assign-dialog.tsx<br/>PUT /tickets/:id<br/>assignedTo, assignedBy"]
ACTION_BUTTONS --> START["Start Progress<br/>PUT /tickets/:id<br/>status: IN_PROGRESS"]
ACTION_BUTTONS --> RESOLVE["Resolve<br/>ticket-resolve-dialog.tsx<br/>PUT /tickets/:id<br/>status: RESOLVED + resolvedNote"]
ACTION_BUTTONS --> DONE["Mark Done<br/>ticket-done-dialog.tsx<br/>PUT /tickets/:id<br/>status: DONE"]
ACTION_BUTTONS --> REJECT["Reject<br/>ticket-reject-dialog.tsx<br/>PUT /tickets/:id<br/>status: REJECTED + rejectReason"]
ACTION_BUTTONS --> EDIT["Edit Ticket<br/>ticket-edit-view.tsx<br/>PUT /tickets/:id"]
end
subgraph BATCH_OP["BATCH OPERATIONS"]
BATCH_ACTIONS --> BATCH_ASSIGN["Batch Assign<br/>ticket-batch-assign-dialog.tsx<br/>Loop PUT /tickets/:id"]
BATCH_ACTIONS --> BATCH_STATUS["Batch Update Status<br/>→ IN_PROGRESS<br/>Loop PUT /tickets/:id"]
end
subgraph TICKET_TEMPLATES["TICKET TEMPLATES"]
TICKET_PAGE --> TEMPL_NAV["/dashboard/ticket/templates"]
TEMPL_NAV --> TEMPL_LIST_T["ticket-template-list-view.tsx"]
TEMPL_LIST_T --> TEMPL_CREATE_T["+ New Ticket Template<br/>ticket-template-create-view.tsx"]
TEMPL_CREATE_T --> TEMPL_FORM_T["ticket-template-form-fields.tsx"]
TEMPL_FORM_T --> TEMPL_TYPE["Template Type:<br/>TICKET_START / TICKET_END"]
TEMPL_FORM_T --> SAVE_TEMPL_T["POST /templates/ticket"]
end
subgraph EMBEDDED["TICKET FROM CHAT"]
CHAT_PANEL["Chat Room<br/>Right Panel → Ticket Tab"] --> TICKET_TAB["chat-room-ticket.tsx"]
TICKET_TAB --> TICKET_FORM_CHAT["ticket-form.tsx<br/>(Inline Create)"]
TICKET_FORM_CHAT --> CREATE_FROM_CHAT["Create Ticket<br/>(auto-links current conversation)"]
TICKET_TAB --> TICKET_LIST_CHAT["ticket-list.tsx<br/>(Linked tickets list)"]
end
style CREATE_FLOW fill:#E8F5E9,stroke:#4CAF50
style DETAIL_FLOW fill:#FFF3E0,stroke:#FF9800
style ACTIONS fill:#FFEBEE,stroke:#F44336
style TICKET_TEMPLATES fill:#E3F2FD,stroke:#1976D2
style EMBEDDED fill:#F3E5F5,stroke:#9C27B0
```
![](./images/3.5.png)
---
......@@ -389,87 +53,7 @@ flowchart TD
Alur lengkap halaman Settings beserta role guard pada setiap sub-menu.
```mermaid
flowchart TD
SETTINGS_ENTRY["/dashboard/settings"] --> REDIRECT["Redirect to<br/>/dashboard/settings/user/list"]
SETTINGS_ENTRY --> SETTINGS_NAV{{"Settings Navigation"}}
subgraph USER_MGMT["USER MANAGEMENT [admin, owner]"]
SETTINGS_NAV --> SET_USER["/settings/user/list<br/>SettingsUserListPage"]
SETTINGS_NAV --> SET_USER_NEW["/settings/user/new<br/>UserCreatePage"]
SETTINGS_NAV --> SET_USER_EDIT["/settings/user/:id/edit<br/>UserEditPage"]
SETTINGS_NAV --> SET_ROLE_PERM["/settings/user/role-permission<br/>SettingsUserRolePermissionPage"]
SET_USER --> USER_TABLE["User List Table<br/>GET /auth/admin/list-users"]
USER_TABLE --> USER_ACTIONS["User Actions"]
USER_ACTIONS --> CREATE_USER["Create User<br/>POST /auth/admin/create-user"]
USER_ACTIONS --> EDIT_USER["Edit User<br/>POST /auth/admin/update-user"]
USER_ACTIONS --> SET_ROLE["Set Role<br/>POST /auth/admin/set-role"]
USER_ACTIONS --> BAN_USER["Ban User"]
USER_ACTIONS --> UNBAN["Unban User<br/>POST /auth/admin/unban-user"]
USER_ACTIONS --> INVITE["Invite Member<br/>POST /auth/organization/invite-member"]
end
subgraph CHANNEL_MGMT["CHANNEL MANAGEMENT [admin, owner]"]
SETTINGS_NAV --> SET_CH["/settings/channel<br/>ChannelPage"]
SETTINGS_NAV --> SET_CH_NEW["/settings/channel/new<br/>ChannelCreatePage"]
SETTINGS_NAV --> SET_CH_EDIT["/settings/channel/:id/edit<br/>ChannelEditPage"]
SET_CH --> CH_LIST["Channel List<br/>GET /channels"]
CH_LIST --> CH_ACTIONS["Channel Actions"]
CH_ACTIONS --> CONNECT_WA["Connect WhatsApp<br/>POST /channels/whatsapp<br/>(Embedded Signup Flow)"]
CH_ACTIONS --> CONNECT_IG["Connect Instagram<br/>POST /channels/instagram<br/>(Business Login Flow)"]
CH_ACTIONS --> EDIT_CH["Edit Channel<br/>PUT /channels/:id"]
CH_ACTIONS --> GEN_WEBHOOK["Generate Webhook Token<br/>POST /channels/:id/generate-webhook"]
CH_ACTIONS --> DEL_CH["Delete Channel<br/>DELETE /channels/:id"]
end
subgraph WS_MGMT["WORKSPACE MANAGEMENT [owner only]"]
SETTINGS_NAV --> SET_WS["/settings/workspace<br/>WorkspacePage"]
SETTINGS_NAV --> SET_WS_NEW["/settings/workspace/new<br/>WorkspaceCreatePage"]
SETTINGS_NAV --> SET_WS_EDIT["/settings/workspace/:id/edit<br/>WorkspaceEditPage"]
SET_WS --> WS_LIST["Workspace List<br/>authClient.organization.list()"]
WS_LIST --> WS_ACTIONS["Workspace Actions"]
WS_ACTIONS --> CREATE_WS["Create Workspace<br/>authClient.organization.create()"]
WS_ACTIONS --> EDIT_WS["Edit Workspace<br/>authClient.organization.update()"]
WS_ACTIONS --> DEL_WS["Delete Workspace<br/>authClient.organization.delete()"]
WS_ACTIONS --> SESSION_CFG["Session Time Config<br/>PUT /workspaces/:id/session-time<br/>Options: 5min, 30min, 1hour, 24hour, never"]
end
subgraph ACCOUNT["MY ACCOUNT [all roles]"]
SETTINGS_NAV --> SET_ACCOUNT["/settings/account<br/>AccountGeneralPage"]
SETTINGS_NAV --> SET_PWD["/settings/account/change-password<br/>AccountChangePasswordPage"]
SET_ACCOUNT --> PROFILE["Profile Settings<br/>PUT /user/update-user<br/>(username, fullname, phone,<br/>photo, image_url, status)"]
SET_PWD --> CHANGE_PWD["Change Password<br/>POST /auth/change-password<br/>(currentPassword, newPassword,<br/>revokeOtherSessions)"]
end
subgraph OTHER_SETTINGS["OTHER SETTINGS [admin, owner]"]
SETTINGS_NAV --> SET_TAG["/settings/tag<br/>Tag Management<br/>CRUD /tags"]
SETTINGS_NAV --> SET_OH["/settings/office-hours<br/>Office Hours & Auto Response<br/>CRUD /office-hour-*"]
SETTINGS_NAV --> SET_QM["/settings/quick-message<br/>Quick Reply Templates<br/>CRUD /message-templates"]
SETTINGS_NAV --> SET_API["/settings/api-keys<br/>API Key Management<br/>CRUD /workspace-api-keys"]
SETTINGS_NAV --> SET_RC["/settings/resolve-category<br/>Resolve Categories<br/>CRUD /conversation-resolve-category"]
end
subgraph OFFICE_HOURS_DETAIL["OFFICE HOURS DETAIL"]
SET_OH --> OH_SCHEDULE["Schedule Configuration<br/>Per Channel"]
OH_SCHEDULE --> OH_DAYS["Days: Mon-Sun<br/>Start Time, End Time<br/>Timezone"]
OH_SCHEDULE --> OH_STATUS["useOfficeHoursStatus hook<br/>Auto-check every minute<br/>→ Online / Offline"]
SET_OH --> OH_RESPONSE["Auto Response Configuration<br/>Per Channel"]
OH_RESPONSE --> OH_DURING["During Office Hours<br/>Auto-response message"]
OH_RESPONSE --> OH_OUTSIDE["Outside Office Hours<br/>Auto-response message"]
end
style USER_MGMT fill:#E8F5E9,stroke:#4CAF50
style CHANNEL_MGMT fill:#FFF3E0,stroke:#FF9800
style WS_MGMT fill:#FFEBEE,stroke:#F44336
style ACCOUNT fill:#E3F2FD,stroke:#1976D2
style OTHER_SETTINGS fill:#F3E5F5,stroke:#9C27B0
```
![](./images/3.6.png)
---
......@@ -477,36 +61,4 @@ flowchart TD
Diagram yang menunjukkan pola data fetching menggunakan SWR di seluruh aplikasi.
```mermaid
flowchart TD
COMPONENT["React Component<br/>(Page / Section)"] --> HOOK["Custom Hook<br/>(use-conversations,<br/>use-messages, etc.)"]
HOOK --> SWR_HOOK["useSWR()<br/>SWR Hook"]
SWR_HOOK --> FETCHER["Axios Fetcher<br/>/src/lib/fetcher.ts"]
FETCHER --> API["API Request<br/>GET /api/v1/u/*"]
API --> RESPONSE{"Response?"}
RESPONSE -->|200 OK| CACHE["SWR Cache<br/>(Key: URL + Params)"]
RESPONSE -->|Error| ERROR_HANDLER["Error Handling"]
ERROR_HANDLER --> SHOW_TOAST["Snackbar Toast<br/>Error Message"]
CACHE --> RENDER["Render Component<br/>with Cached Data"]
RENDER --> IDLE{"Data Stale?"}
IDLE -->|Yes| REVALIDATE["Auto Revalidation<br/>(revalidateOnFocus,<br/>revalidateOnReconnect)"]
IDLE -->|No| WAIT["Wait for Event"]
REVALIDATE --> FETCHER
subgraph RT_EVENTS["Real-Time Triggered Revalidation"]
PUSHER_EV["Pusher Event Received"] --> MUTATE["SWR mutate()<br/>Invalidate specific key"]
SOCKET_EV["Socket.IO Event"] --> MUTATE
MUTATE --> FETCHER
end
subgraph MANUAL["Manual Mutations"]
OPTIMISTIC["Optimistic Update"] --> LOCAL_UPDATE["Update local cache first"]
MANUAL_CALL["Manual API Call<br/>(POST/PUT/DELETE)"] --> MANUAL_MUTATE["mutate() after success"]
MANUAL_CALL -->|"Error"| ROLLBACK["Rollback optimistic update"]
LOCAL_UPDATE --> MANUAL_MUTATE
end
```
![](./images/3.7.png)
......@@ -7,63 +7,7 @@
Arsitektur umum Backend menggunakan pattern **Repository → Service → Route** (Layered Architecture).
```mermaid
flowchart TD
ENTRY["index.ts<br/>Bun HTTP Server<br/>Port: 8000"] --> APP_SVC["app.service.ts<br/>createApp()"]
APP_SVC --> INIT_PUSH["Initialize Pusher"]
APP_SVC --> INIT_RMQ["Initialize RabbitMQ"]
APP_SVC --> DEV_INIT{"Development?"}
DEV_INIT -->|Yes| DB_INIT["Initialize DB<br/>Test Connection"]
subgraph GLOBAL_MW["Global Middleware Stack"]
GM1["CORS (cors.config.ts)<br/>Allowed origins validation"]
GM2["Timing (response time header)"]
GM3["Request ID (unique per request)"]
GM4["Logger (request logging)"]
GM5["Body Limit (5MB default)"]
GM6["Timeout (5min default)"]
GM1 --> GM2 --> GM3 --> GM4 --> GM5 --> GM6
end
APP_SVC --> GLOBAL_MW
subgraph ROUTES["Route Registration (v1.ts)"]
R_AUTH["/api/v1/auth/*<br/>better-auth handler"]
R_WEBHOOK["/api/webhook/*<br/>Webhook receivers"]
R_PROTECTED["/api/v1/u/*<br/>requireAuth middleware"]
R_UPLOADS["/api/uploads<br/>File storage"]
R_SWAGGER["/api/v1/swagger<br/>OpenAPI docs"]
end
GLOBAL_MW --> ROUTES
subgraph AUTH_MW_LAYER["Auth Middleware Chain"]
AM1["requireAuth<br/>(Session verification)"]
AM2["optionalAuth<br/>(No block if missing)"]
AM3["requirePermission<br/>(RBAC check)"]
AM4["requireWorkspaceApiKey<br/>(x-api-key header)"]
AM5["requireAuthOrWorkspaceApiKey<br/>(Either session or API key)"]
end
R_PROTECTED --> AUTH_MW_LAYER
AUTH_MW_LAYER --> SERVICE["Service Layer<br/>(47 Modules)"]
SERVICE --> REPO["Repository Layer<br/>(Drizzle ORM queries)"]
REPO --> DB["PostgreSQL<br/>schema: omnichannel<br/>30+ tables"]
SERVICE --> NOTIFY["Real-time Notification<br/>notifyHybrid()<br/>Socket.IO + Pusher"]
SERVICE --> EXT_API["External APIs<br/>WhatsApp / Instagram / Telegram"]
R_WEBHOOK --> RABBIT["RabbitMQ Publisher"]
RABBIT --> WORKER["Worker Consumer"]
WORKER --> SERVICE
style ENTRY fill:#C8E6C9,stroke:#4CAF50
style GLOBAL_MW fill:#FFF3E0,stroke:#FF9800
style AUTH_MW_LAYER fill:#FFEBEE,stroke:#F44336
style SERVICE fill:#E3F2FD,stroke:#1976D2
style REPO fill:#F3E5F5,stroke:#9C27B0
```
![](./images/4.1.png)
---
......@@ -71,72 +15,7 @@ flowchart TD
Alur lengkap autentikasi dan authorization di Backend.
```mermaid
flowchart TD
REQUEST["Incoming Request"] --> ROUTE_TYPE{Route Type?}
subgraph PUBLIC_ROUTES["Public Routes (No Auth)"]
ROUTE_TYPE -->|"/api/v1/swagger"| SWAGGER["Swagger UI"]
ROUTE_TYPE -->|"/api/webhook/whatsapp<br/>(GET verify)"| WH_VERIFY["Webhook Verification"]
end
subgraph BETTER_AUTH["Better-Auth Self-Handled"]
ROUTE_TYPE -->|"/api/v1/auth/*"| BA_HANDLER["better-auth handler"]
BA_HANDLER --> BA_SIGNIN["sign-in/email-password"]
BA_HANDLER --> BA_SIGNUP["sign-up/email-password"]
BA_HANDLER --> BA_FORGET["forget-password"]
BA_HANDLER --> BA_RESET["reset-password"]
BA_HANDLER --> BA_CHANGE["change-password"]
BA_HANDLER --> BA_SESSION["get-session"]
BA_HANDLER --> BA_ADMIN["admin/list-users"]
BA_HANDLER --> BA_CREATE["admin/create-user"]
BA_HANDLER --> BA_UPDATE["admin/update-user"]
BA_HANDLER --> BA_SET_ROLE["admin/set-role"]
BA_HANDLER --> BA_UNBAN["admin/unban-user"]
BA_HANDLER --> BA_ORG_LIST["organization/list-members"]
BA_HANDLER --> BA_ORG_INVITE["organization/invite-member"]
BA_HANDLER --> BA_ORG_UPDATE["organization/update-member-role"]
BA_HANDLER --> BA_ORG_REMOVE["organization/remove-member"]
BA_HANDLER --> BA_ORG_ACCEPT["organization/accept-invitation"]
BA_HANDLER --> BA_APIKEY["api-key/create"]
end
subgraph PROTECTED["Protected Routes (Auth Required)"]
ROUTE_TYPE -->|"/api/v1/u/*"| AUTH_CHECK
AUTH_CHECK["Which Auth Method?"]
AUTH_CHECK -->|"Default"| REQUIRE_AUTH["requireAuth middleware"]
AUTH_CHECK -->|"Tickets"| EITHER["requireAuthOrWorkspaceApiKey"]
AUTH_CHECK -->|"Optional"| OPTIONAL["optionalAuth"]
REQUIRE_AUTH --> SESSION_CHECK{Valid Session<br/>Cookie?}
SESSION_CHECK -->|No| ERR_401["401 Unauthorized<br/>response.util.ts"]
SESSION_CHECK -->|Yes| SET_CTX["Set c.set('user', user)<br/>c.set('session', session)"]
EITHER --> CHECK_EITHER{Session OR<br/>API Key?}
CHECK_EITHER -->|Neither| ERR_401
CHECK_EITHER -->|Session| SET_CTX
CHECK_EITHER -->|API Key| CHECK_API{Valid API Key?<br/>x-api-key header}
CHECK_API -->|No| ERR_401
CHECK_API -->|Yes| SET_API_CTX["Set user + workspace<br/>from key metadata"]
SET_CTX --> RBAC_CHECK
SET_API_CTX --> RBAC_CHECK
RBAC_CHECK["requirePermission({resource: action})"]
RBAC_CHECK --> CHECK_ROLE{User has<br/>permission?}
CHECK_ROLE -->|No| ERR_403["403 Forbidden"]
CHECK_ROLE -->|Yes| HANDLER["Route Handler"]
end
HANDLER --> SVC["Service → Repository → DB"]
SVC --> RESP["Response<br/>response.util.ts<br/>success / error / paginated"]
SVC --> NOTIFY["notifyHybrid()<br/>Pusher + Socket.IO"]
style PUBLIC_ROUTES fill:#E8F5E9,stroke:#4CAF50
style BETTER_AUTH fill:#FFF3E0,stroke:#FF9800
style PROTECTED fill:#FFEBEE,stroke:#F44336
```
![](./images/4.2.png)
---
......@@ -144,91 +23,7 @@ flowchart TD
Alur lengkap mesin chat Backend, baik inbound maupun outbound.
```mermaid
flowchart TD
subgraph INBOUND["INBOUND MESSAGE FLOW"]
WEBHOOK["Webhook Received"] --> PARSE["Parse Platform Data"]
PARSE --> EXTRACT["Extract:<br/>sender_phone, content, type,<br/>channel_code, raw_id"]
EXTRACT --> PROFANITY{"Profanity<br/>Filter?"}
PROFANITY -->|Flagged| MARK_FLAGGED["Mark as flagged"]
PROFANITY -->|Clean| FIND_CONTACT
MARK_FLAGGED --> FIND_CONTACT{Contact<br/>Exists?<br/>MATCH by phone + channel_id}
FIND_CONTACT -->|No| CREATE_CONTACT["Create Contact<br/>INSERT contacts<br/>contact_name=phone<br/>contact_phone=phone"]
FIND_CONTACT -->|Yes| GET_CONTACT["SELECT contact<br/>WHERE phone + channel_id"]
CREATE_CONTACT --> FIND_CONV
GET_CONTACT --> FIND_CONV
FIND_CONV{Active Conversation?<br/>MATCH by contact_id<br/>+ channel_id<br/>+ status NOT CLOSED/RESOLVED}
FIND_CONV -->|No| CREATE_CONV["INSERT conversations<br/>status: UNASSIGNED<br/>unread_counts: 1"]
FIND_CONV -->|Yes Chain| UPDATE_CHAIN["Link previous_conversation_id"]
FIND_CONV -->|Yes Active| UPDATE_CONV
UPDATE_CHAIN --> UPDATE_CONV["UPDATE conversations<br/>last_message, last_message_at<br/>unread_counts++<br/>conversation_24h_window<br/>(WhatsApp only)"]
UPDATE_CONV --> SAVE_MSG["INSERT conversation_messages<br/>from=customer, to=system<br/>type based on content"]
SAVE_MSG --> SAVE_ATTACH{"Has<br/>Attachments?"}
SAVE_ATTACH -->|Yes| SAVE_ATT["INSERT message_attachments<br/>Download media from<br/>platform API"]
SAVE_ATTACH -->|No| CHECK_OH
SAVE_ATT --> CHECK_OH
CHECK_OH{"Within<br/>Office Hours?<br/>office-hours-schedule"}
CHECK_OH -->|No| AUTO_RESP["Send Auto Response<br/>office-hours-auto-responses<br/>→ Platform Send API"]
CHECK_OH -->|Yes| NOTIFY
AUTO_RESP --> NOTIFY["notifyHybrid()"]
NOTIFY --> P1["Pusher: private-room:{convId}<br/>Event: new-message"]
NOTIFY --> P2["Pusher: private-channel:{chId}<br/>Event: new-conversation<br/>(if new)"]
NOTIFY --> S1["Socket.IO: emit new-message"]
end
subgraph OUTBOUND["OUTBOUND MESSAGE FLOW"]
SEND_API["POST /conversation-messages<br/>(Agent sends message)"] --> VALIDATE["Validate with Zod<br/>conversation-message.validation.ts"]
VALIDATE --> CHECK_TYPE{Message Type?}
CHECK_TYPE -->|"TEXT"| PREPARE_TEXT["Prepare text content"]
CHECK_TYPE -->|"IMAGE/VIDEO/AUDIO/DOC"| UPLOAD_MEDIA["Check media<br/>Already uploaded or new"]
CHECK_TYPE -->|"TEMPLATE"| PREPARE_TEMPL["Prepare template<br/>components + variables"]
PREPARE_TEXT --> PROFANITY_OUT{"Profanity?"}
UPLOAD_MEDIA --> PROFANITY_OUT
PREPARE_TEMPL --> SKIP_PROFANE
PROFANITY_OUT -->|Flagged| REJECT["400 Bad Request<br/>'Message contains inappropriate content'"]
PROFANITY_OUT -->|Clean| CHECK_WINDOW
SKIP_PROFANE --> CHECK_WINDOW{"WhatsApp 24h<br/>Window Active?<br/>(if WhatsApp channel)"}
CHECK_WINDOW -->|Expired| ERR_WINDOW["Error:<br/>'24-hour messaging window expired'"]
CHECK_WINDOW -->|Active or Non-WA| SEND_PLATFORM
SEND_PLATFORM["Send to Platform API"]
SEND_PLATFORM --> WA_API["WhatsApp API<br/>POST /messages"]
SEND_PLATFORM --> IG_API["Instagram API<br/>POST /messages"]
SEND_PLATFORM --> TG_API["Telegram API<br/>sendMessage"]
WA_API --> SAVE_OUT["INSERT conversation_messages<br/>from=agent, to=customer<br/>status: SENT"]
IG_API --> SAVE_OUT
TG_API --> SAVE_OUT
SAVE_OUT --> UPDATE_CONV_OUT["UPDATE conversations<br/>last_message, last_message_at"]
UPDATE_CONV_OUT --> NOTIFY_OUT["notifyHybrid()<br/>private-room:{convId}<br/>Event: new-message"]
end
subgraph DELIVERY["DELIVERY STATUS WEBHOOK"]
DELIVERY_WB["Platform Delivery Webhook"] --> FIND_MSG{"Find Message<br/>by platform_msg_id"}
FIND_MSG --> UPDATE_STATUS["UPDATE conversation_messages<br/>status: DELIVERED / READ / FAILED"]
UPDATE_STATUS --> NOTIFY_DELIVERY["notifyHybrid()<br/>Event: message-updated"]
end
style INBOUND fill:#E8F5E9,stroke:#4CAF50
style OUTBOUND fill:#FFF3E0,stroke:#FF9800
style DELIVERY fill:#E3F2FD,stroke:#1976D2
```
![](./images/4.3.png)
---
......@@ -266,67 +61,7 @@ stateDiagram-v2
Alur lengkap eksekusi campaign di Backend.
```mermaid
flowchart TD
TRIGGER["POST /campaigns/:id/execute"] --> LOAD["Load Campaign<br/>+ Template + Channel + Recipient List"]
LOAD --> STATUS_CHECK{Campaign Status?}
STATUS_CHECK -->|"DRAFT"| SET_RUNNING["UPDATE campaigns<br/>status: RUNNING<br/>started_at: NOW()"]
STATUS_CHECK -->|"SCHEDULED"| CHECK_SCHEDULE{Scheduled time<br/>reached?}
STATUS_CHECK -->|"RUNNING"| ALREADY["Return: Already running"]
STATUS_CHECK -->|"COMPLETED/CANCELLED"| ERR["Return Error"]
CHECK_SCHEDULE -->|"Not yet"| SCHEDULE_WAIT["Wait for cron/scheduler"]
CHECK_SCHEDULE -->|"Yes"| SET_RUNNING
SET_RUNNING --> LOAD_TEMPLATE["Load Template<br/>templates table<br/>template_components (JSON)"]
LOAD_TEMPLATE --> LOAD_LIST["Load Recipient List Items<br/>campaign_recipient_list_items<br/>+ contacts"]
LOAD_LIST --> INIT_COUNTS["Reset campaign counts:<br/>total_recipients, sent, delivered,<br/>read, failed = 0"]
INIT_COUNTS --> LOOP_START["Start Loop"]
subgraph LOOP["PER RECIPIENT LOOP"]
LOOP_START --> GET_NEXT{Next<br/>Recipient?}
GET_NEXT -->|No| COMPLETE
GET_NEXT -->|Yes| RENDER["Render Template<br/>Replace {{1}}, {{2}}...<br/>with recipient variables"]
RENDER --> FIND_CONV{Has Active<br/>Conversation?<br/>contact_id + channel_id}
FIND_CONV -->|No| CREATE_CONV["INSERT conversations<br/>status: UNASSIGNED"]
FIND_CONV -->|Yes| USE_CONV["Use existing conversation"]
CREATE_CONV --> SEND_WA
USE_CONV --> SEND_WA
SEND_WA["Send via WhatsApp API<br/>POST /{phone_id}/messages<br/>template type message"]
SEND_WA --> WA_RESPONSE{WhatsApp<br/>API Response?}
WA_RESPONSE -->|Success| RECORD_SENT["INSERT campaign_recipients<br/>status: SENT<br/>conversation_id, message_id"]
WA_RESPONSE -->|Rate Limit| WAIT_RETRY["Wait + Retry<br/>(Exponential backoff)"]
WA_RESPONSE -->|Error| RECORD_FAIL["INSERT campaign_recipients<br/>status: FAILED"]
RECORD_SENT --> INC_SENT["UPDATE campaigns<br/>sent++"]
RECORD_FAIL --> INC_FAIL["UPDATE campaigns<br/>failed++"]
INC_SENT --> NEXT["Continue Loop"]
INC_FAIL --> NEXT
WAIT_RETRY --> SEND_WA
NEXT --> GET_NEXT
end
COMPLETE["All Recipients Processed"] --> SET_COMPLETE["UPDATE campaigns<br/>status: COMPLETED<br/>completed_at: NOW()"]
subgraph DELIVERY["DELIVERY WEBHOOKS"]
WA_DELIVERY["WhatsApp Delivery Callback"] --> FIND_RECIP{Find campaign_recipient<br/>by conversation_message_id}
FIND_RECIP --> UPDATE_RECIP["UPDATE campaign_recipients<br/>status: DELIVERED / READ / FAILED"]
UPDATE_RECIP --> UPDATE_CAMP_COUNTS["UPDATE campaigns<br/>delivered++ / read++ / failed++"]
UPDATE_CAMP_COUNTS --> NOTIFY_STATS["Optional: Push notification<br/>to campaign dashboard"]
end
style LOOP fill:#E8F5E9,stroke:#4CAF50
style DELIVERY fill:#FFF3E0,stroke:#FF9800
```
![](./images/4.5.png)
---
......@@ -334,60 +69,7 @@ flowchart TD
Alur lengkap lifecycle ticket di Backend.
```mermaid
flowchart TD
CREATE["POST /tickets<br/>Create Ticket"] --> VALIDATE_T["Validate with Zod<br/>ticket.validation.ts"]
VALIDATE_T --> INSERT_TICKET["INSERT tickets<br/>ticket_number: auto-generated<br/>status: TODO"]
INSERT_TICKET --> INSERT_ATTACH{"Has<br/>Attachments?"}
INSERT_ATTACH -->|Yes| INSERT_ATT["INSERT ticket_attachments<br/>(link to ticket_id)"]
INSERT_ATTACH -->|No| DONE_CREATE
INSERT_ATT --> DONE_CREATE["Ticket Created<br/>201 Created Response"]
subgraph LIFECYCLE["TICKET LIFECYCLE"]
DONE_CREATE --> TODO_ST["Status: TODO"]
TODO_ST -->|"Assign to Agent<br/>PUT /tickets/:id<br/>assignedTo, assignedBy"| IN_PROGRESS["Status: IN_PROGRESS"]
TODO_ST -->|"Soft Delete<br/>DELETE /tickets/:id"| DELETED["Status: DELETED<br/>(deleted_at IS NOT NULL)"]
IN_PROGRESS -->|"Reassign<br/>assignedTo changed"| IN_PROGRESS
IN_PROGRESS -->|"Resolve<br/>status: RESOLVED<br/>resolvedBy, resolvedAt<br/>resolvedNote"| RESOLVED["Status: RESOLVED"]
IN_PROGRESS -->|"Reject<br/>status: REJECTED<br/>rejectedBy, rejectedAt<br/>rejectReason"| REJECTED["Status: REJECTED"]
RESOLVED -->|"Reopen<br/>status back to<br/>IN_PROGRESS"| IN_PROGRESS
RESOLVED -->|"Mark Done<br/>status: DONE<br/>closedBy, closedAt"| DONE["Status: DONE"]
REJECTED --> END_STATE["Terminal State"]
DONE --> END_STATE
DELETED --> END_STATE
end
subgraph HISTORY["STATUS HISTORY TRACKING"]
STATUS_CHANGE["Any Status Change"] --> RECORD_HIST["INSERT ticket_status_history<br/>ticket_id, old_status, new_status<br/>updated_by, assigned_to<br/>resolved_by, rejected_by"]
RECORD_HIST --> NOTIFY_TICKET["notifyHybrid()<br/>(if real-time needed)"]
end
LIFECYCLE --> HISTORY
subgraph BATCH["BATCH OPERATIONS"]
BATCH_ASSIGN["Batch Assign<br/>(FE loops PUT calls)"] --> LOOP_PUT1["PUT /tickets/:id<br/>assignedTo: userId<br/>× N tickets"]
BATCH_STATUS["Batch Update Status<br/>(FE loops PUT calls)"] --> LOOP_PUT2["PUT /tickets/:id<br/>status: IN_PROGRESS<br/>× N tickets"]
end
subgraph FROM_CHAT["TICKET FROM CONVERSATION"]
CHAT_LINK["conversation_id provided"] --> VERIFY_CONV{"Conversation<br/>Exists?"}
VERIFY_CONV -->|Yes| LINK_CONV["Set ticket.conversation_id"]
VERIFY_CONV -->|No| ERR_CONV["Error: Conversation not found"]
CONTACT_LINK["contact_id provided"] --> VERIFY_CONTACT{"Contact<br/>Exists?"}
VERIFY_CONTACT -->|Yes| LINK_CONTACT["Set ticket.contact_id"]
VERIFY_CONTACT -->|No| ERR_CONTACT["Error: Contact not found"]
end
style LIFECYCLE fill:#E8F5E9,stroke:#4CAF50
style HISTORY fill:#FFF3E0,stroke:#FF9800
style BATCH fill:#E3F2FD,stroke:#1976D2
style FROM_CHAT fill:#F3E5F5,stroke:#9C27B0
```
![](./images/4.6.png)
---
......@@ -395,76 +77,7 @@ flowchart TD
Arsitektur pemrosesan webhook dari external channels.
```mermaid
flowchart TD
subgraph INCOMING["WEBHOOK RECEIVERS"]
WH_WA_GET["GET /api/webhook/whatsapp<br/>Verification<br/>(hub.mode, hub.verify_token,<br/>hub.challenge)"]
WH_WA_POST["POST /api/webhook/whatsapp<br/>Receive messages<br/>(WhatsApp payload)"]
WH_IG["POST /api/webhook/instagram<br/>Receive DMs + Comments<br/>(Instagram payload)"]
WH_TG["POST /api/webhook/telegram<br/>Receive messages<br/>(Telegram payload)"]
end
subgraph WHATSAPP_FLOW["WHATSAPP FLOW"]
WH_WA_GET --> VERIFY{Verify Token<br/>Matches?}
VERIFY -->|Yes| RETURN_CHALLENGE["Return hub.challenge<br/>200 OK"]
VERIFY -->|No| ERR_403["403 Forbidden"]
WH_WA_POST --> VERIFY_WA_HUB{"Hub Mode = 'subscribe'?"}
%% Perbaikan Baris 15 & 16: Dipisah ke baris baru
VERIFY_WA_HUB -->|Yes| RETURN_200["200 OK No processing"]
VERIFY_WA_HUB -->|No| PARSE_WA["Parse WhatsApp Payload<br/>Extract: entry.changes<br/>messages, statuses"]
PARSE_WA --> MSG_TYPE{Message Type?}
MSG_TYPE -->|"messages"| TO_QUEUE["Publish to RabbitMQ<br/>Queue: omnichannel_work_queue<br/>Route: whatsapp"]
MSG_TYPE -->|"statuses"| PROCESS_STATUS["Process Delivery Status<br/>UPDATE conversation_messages<br/>status: SENT → DELIVERED → READ"]
TO_QUEUE --> RABBIT_MQ["RabbitMQ<br/>amqplib connection"]
end
subgraph RABBIT_WORKER["RABBITMQ WORKER"]
RABBIT_MQ --> CONSUME["Consume from Queue"]
CONSUME --> ACK{"Process<br/>Success?"}
ACK -->|Yes| ACK_MSG["ack message"]
ACK -->|No| NACK["nack + requeue<br/>(or dead-letter)"]
NACK --> CONSUME
end
subgraph DIRECT_PROCESSING["DIRECT PROCESSING<br/>(Instagram, Telegram)"]
WH_IG --> PARSE_IG["Parse Instagram Payload"]
PARSE_IG --> IG_TYPE{Event Type?}
%% Perbaikan: Menghapus karakter asteris (*) yang bisa mengganggu parser
IG_TYPE -->|messages| INSTA_DM["Process Instagram DM<br/>→ conversation engine"]
IG_TYPE -->|comments| INSTA_COMMENT["Process Comment<br/>→ conversation engine<br/>context: POST_COMMENT"]
WH_TG --> PARSE_TG["Parse Telegram Payload"]
PARSE_TG --> TG_PROCESS["Process Telegram Message<br/>→ conversation engine"]
end
subgraph SHARED_PROCESSOR["SHARED MESSAGE PROCESSOR"]
TO_QUEUE --> PROCESSOR["Message Processor Service"]
INSTA_DM --> PROCESSOR
INSTA_COMMENT --> PROCESSOR
TG_PROCESS --> PROCESSOR
PROCESSOR --> CONTACT_RESOLVE["Resolve/Create Contact"]
PROCESSOR --> CONV_RESOLVE["Resolve/Create Conversation"]
PROCESSOR --> SAVE_MESSAGE["Save Message + Attachments"]
PROCESSOR --> CHECK_AUTO_RESP["Check Auto Response"]
PROCESSOR --> NOTIFY_RT["Real-time Notification"]
end
PROCESS_STATUS --> DB_UPDATE["Direct DB Update"]
DB_UPDATE --> DB[(PostgreSQL)]
SHARED_PROCESSOR --> DB
style INCOMING fill:#E8F5E9,stroke:#4CAF50
style WHATSAPP_FLOW fill:#FFF3E0,stroke:#FF9800
style RABBIT_WORKER fill:#FFEBEE,stroke:#F44336
style DIRECT_PROCESSING fill:#E3F2FD,stroke:#1976D2
style SHARED_PROCESSOR fill:#F3E5F5,stroke:#9C27B0
```
![](./images/4.7.png)
---
......@@ -472,76 +85,11 @@ flowchart TD
Pattern yang digunakan di setiap service module Backend.
```mermaid
flowchart TD
subgraph MODULE["Service Module Structure"]
direction TB
ROUTE["*.route.ts<br/>───────────────<br/>Define Hono routes<br/>Apply middleware<br/>Call service functions<br/>Return responses"]
SVC["*.service.ts<br/>───────────────<br/>Business logic<br/>Orchestrate repos<br/>Handle errors<br/>Trigger notifications"]
REPO["*.repository.ts<br/>───────────────<br/>Drizzle ORM queries<br/>INSERT / SELECT<br/>UPDATE / DELETE<br/>Transactions"]
VALID["*.validation.ts<br/>───────────────<br/>Zod schemas<br/>Input validation<br/>Output parsing"]
ERR["*.error.ts<br/>───────────────<br/>Custom error classes<br/>Error codes<br/>Error messages"]
TYPE["*.type.ts<br/>───────────────<br/>TypeScript types<br/>Interfaces<br/>Enums"]
end
ROUTE -->|"Validate input"| VALID
ROUTE -->|"Call business logic"| SVC
SVC -->|"Query database"| REPO
SVC -->|"Throw errors"| ERR
REPO -->|"Read types"| TYPE
ROUTE -->|"Read types"| TYPE
SVC -->|"Read types"| TYPE
subgraph EXAMPLE["Example: conversation-message"]
EX_ROUTE["conversation-message.route.ts<br/>GET / → listMessages()<br/>POST / → sendMessage()<br/>POST /template → sendTemplate()"]
EX_SVC["conversation-message.service.ts<br/>sendMessage():<br/> 1. Validate profanity<br/> 2. Check 24h window<br/> 3. Send to platform API<br/> 4. Save to DB<br/> 5. Notify real-time"]
EX_REPO["conversation-message.repository.ts<br/>findById()<br/>findByConversationId()<br/>create()<br/>updateStatus()"]
EX_VALID["conversation-message.validation.ts<br/>sendMessageSchema<br/>sendTemplateSchema<br/>listMessagesSchema"]
end
EX_ROUTE --> EX_SVC
EX_SVC --> EX_REPO
EX_ROUTE --> EX_VALID
style MODULE fill:#F5F5F5,stroke:#9E9E9E
style EXAMPLE fill:#E3F2FD,stroke:#1976D2
```
![](./images/4.8.png)
---
## 4.9 BE Error Handling Flow
```mermaid
flowchart TD
REQUEST["Request Handler"] --> TRY["try { ... }"]
TRY --> HANDLER["Business Logic"]
HANDLER --> SUCCESS{Success?}
SUCCESS -->|Yes| RETURN_OK["response.util.success(data)<br/>200 OK"]
SUCCESS -->|No| ERROR_CATCH["catch (error)"]
ERROR_CATCH --> ERROR_TYPE{Error Type?}
ERROR_TYPE -->|"AppError"| APP_ERR["Custom App Error<br/>status + message + code"]
ERROR_TYPE -->|"HTTPException"| HTTP_ERR["Hono HTTP Exception"]
ERROR_TYPE -->|"DrizzleQueryError"| DB_ERR["Database Query Error"]
ERROR_TYPE -->|"PostgresError"| PG_ERR["PostgreSQL Error<br/>(unique violation, fk, etc.)"]
ERROR_TYPE -->|"ZodError"| ZOD_ERR["Validation Error<br/>Input format invalid"]
ERROR_TYPE -->|"Unknown"| UNKNOWN_ERR["Unknown Error"]
APP_ERR --> MAP_STATUS["Map to HTTP Status"]
HTTP_ERR --> MAP_STATUS
DB_ERR --> LOG_ERR["Log Error<br/>console.error"]
PG_ERR --> LOG_ERR
ZOD_ERR --> MAP_400["400 Bad Request<br/>Validation error details"]
UNKNOWN_ERR --> LOG_ERR
LOG_ERR --> MAP_500["500 Internal Server Error"]
MAP_STATUS --> RESPONSE["Return Error Response<br/>response.util.error()"]
MAP_400 --> RESPONSE
MAP_500 --> RESPONSE
style REQUEST fill:#C8E6C9,stroke:#4CAF50
style ERROR_CATCH fill:#FFCDD2,stroke:#F44336
style RESPONSE fill:#BBDEFB,stroke:#2196F3
```
![](./images/4.9.png)
# ORTAX OMNICHANNEL APPLICATION
# COMPREHENSIVE FLOWCHART & ARCHITECTURE REPORT
---
| **Dokumen** | Laporan Analisis Arsitektur & Flowchart |
|---|---|
| **Versi Aplikasi** | Backend v0.5.7 · Frontend v0.4.6 |
| **Tanggal Laporan** | 21 April 2026 |
| **Disusun oleh** | Tim Analisis Arsitektur |
| **Status** | Final |
---
## DAFTAR ISI
| # | Dokumen | File | Deskripsi |
|---|---------|------|-----------|
| 1 | Executive Summary | `00-EXECUTIVE-SUMMARY.md` | Ringkasan eksekutif (file ini) |
| 2 | Arsitektur Umum Aplikasi | `01-OVERALL-ARCHITECTURE.md` | System architecture, user journey, navigasi |
| 3 | Integrasi FE-BE | `02-INTEGRATION-FLOW.md` | API mapping, real-time flow, database ERD |
| 4 | Frontend Flowcharts | `03-FE-FLOWCHARTS.md` | Routing, Auth, Chat, Campaign, Ticket, Settings |
| 5 | Backend Flowcharts | `04-BE-FLOWCHARTS.md` | Service architecture, Auth RBAC, Chat engine, Campaign engine, Ticket lifecycle |
| 6 | Ringkasan & Tabel | `05-SUMMARY-TABLES.md` | Role matrix, endpoint catalog, tech stack |
---
## RINGKASAN EKSEKUTIF
### Tentang Aplikasi
**Ortax Omnichannel** adalah platform komunikasi pelanggan multi-channel yang memungkinkan bisnis mengelola percakapan dari WhatsApp, Instagram, Telegram, dan channel lainnya dalam satu dashboard terpusat.
### 5 Fitur Utama
| # | Fitur | Deskripsi |
|---|-------|-----------|
| 1 | **Authentication (Auth)** | Login/register via Better-Auth, session management, RBAC (owner/admin/member), forgot/reset password |
| 2 | **Chat** | Real-time conversation management, multi-channel messaging (WhatsApp/Instagram/Telegram), message templates, file attachments, internal notes, conversation assignment & resolution |
| 3 | **Campaign** | WhatsApp marketing campaigns dengan template messages, recipient list management, variable personalization, delivery tracking |
| 4 | **Ticket** | Issue tracking linked to conversations/contacts, full lifecycle (TODO → IN_PROGRESS → RESOLVED → DONE), assignment, batch operations |
| 5 | **Settings** | User management, channel configuration, workspace management, office hours & auto-response, quick reply templates, API keys, tags, resolve categories, role & permission management |
### Technology Stack
| Layer | Technology |
|-------|-----------|
| **Frontend** | React 19, TypeScript 5.9, Vite 7, MUI v7, SWR, Axios |
| **Backend** | Hono v4, Bun runtime, Drizzle ORM, TypeScript |
| **Database** | PostgreSQL (schema: omnichannel, 30+ tables) |
| **Auth** | better-auth v1.3 |
| **Real-time** | Pusher + Socket.IO (hybrid) |
| **Message Queue** | RabbitMQ (webhook processing) |
| **Validation** | Zod (FE + BE) |
| **External APIs** | Meta WhatsApp Business API, Instagram Graph API, Telegram Bot API |
### Arsitektur Utama
- **Frontend**: React SPA dengan route guards (AuthGuard, WorkspaceGuard, GuestGuard, RouteRoleGuard)
- **Backend**: Hono HTTP server dengan Repository-Service-Route pattern (47 service modules)
- **Real-time**: Hybrid Pusher + Socket.IO untuk notifikasi real-time
- **Multi-tenancy**: Workspace-based dengan RBAC permission system
- **Webhook Queue**: RabbitMQ untuk distributed webhook processing
- **Soft Delete**: Semua entity menggunakan soft delete pattern
### Statistik Kode
| Metrik | FE | BE |
|--------|-----|-----|
| Total Service Modules | - | 47 modules |
| Chat Section Files | 82 files | ~8 services |
| Campaign Section Files | 33 files | ~5 services |
| Ticket Section Files | 25 files | ~3 services |
| API Endpoints | ~80+ action functions | ~60+ route endpoints |
| Database Tables | - | 30+ tables |
| Real-time Channels | 3 (Pusher/Socket.IO/WS) | 3 (Pusher/Socket.IO/RabbitMQ) |
---
> **Catatan**: Semua diagram flowchart menggunakan sintaks **Mermaid** yang dapat dirender di GitHub, GitLab, Notion, VS Code (dengan extension), atau [Mermaid Live Editor](https://mermaid.live).
# BAGIAN 1: ARSITEKTUR UMUM APLIKASI
## (Overall Application Architecture)
---
## 1.1 System Architecture Overview
Diagram ini menunjukkan arsitektur keseluruhan sistem Ortax Omnichannel dari perspektif high-level, mencakup semua layer dari pengguna hingga database.
```mermaid
flowchart TB
subgraph USERS["PENGGUNA"]
AGENT["Agent / Staff<br/>Role: member"]
ADMIN["Admin / Owner<br/>Role: admin, owner"]
CUSTOMER["Customer<br/>External User"]
end
subgraph CHANNELS["EXTERNAL CHANNELS<br/>(Platform API)"]
WA["WhatsApp Business API<br/>(Meta Graph API)"]
IG["Instagram Graph API<br/>(Meta Graph API)"]
TG["Telegram Bot API<br/>(Telegram)"]
FB["Facebook Messenger<br/>(Meta Graph API)"]
TT["TikTok<br/>(Planned)"]
end
subgraph FE["FRONTEND APPLICATION<br/>React 19 + Vite + MUI v7"]
direction TB
FE_AUTH["Auth Module<br/>sign-in, sign-up, forgot/reset password"]
FE_CHAT["Chat Module<br/>82 files - conversations, messages, real-time"]
FE_CAMP["Campaign Module<br/>33 files - templates, recipients, execution"]
FE_TICKET["Ticket Module<br/>25 files - CRUD, lifecycle, attachments"]
FE_SETTINGS["Settings Module<br/>20+ pages - admin panels"]
end
subgraph BE["BACKEND APPLICATION<br/>Hono + Drizzle ORM + Bun"]
direction TB
BE_API["API Gateway<br/>/api/v1"]
BE_AUTH_ENGINE["Better-Auth Engine<br/>session, RBAC, API keys"]
BE_SERVICES["47 Service Modules<br/>business logic layer"]
BE_WEBHOOK["Webhook Receivers<br/>WhatsApp, Instagram, Telegram"]
BE_QUEUE["RabbitMQ Worker<br/>distributed processing"]
end
subgraph REALTIME["REAL-TIME LAYER"]
PUSHER["Pusher<br/>private-room, private-channel, private-post"]
SOCKET["Socket.IO<br/>new-message, new-conversation"]
end
subgraph DB_LAYER["DATABASE & STORAGE"]
PG["PostgreSQL<br/>schema: omnichannel<br/>30+ tables"]
UPLOAD["File Storage<br/>Local / S3 / GCS / Azure"]
end
USERS -->|"Browser<br/>HTTPS"| FE
CUSTOMER -->|"Send Message"| CHANNELS
CHANNELS -->|"Webhook POST"| BE_WEBHOOK
FE -->|"REST API<br/>/api/v1/*"| BE_API
BE_API --> BE_AUTH_ENGINE
BE_API --> BE_SERVICES
BE_SERVICES --> PG
BE_SERVICES --> UPLOAD
BE_WEBHOOK --> BE_QUEUE
BE_QUEUE --> BE_SERVICES
BE_SERVICES -->|"Trigger Events"| REALTIME
REALTIME -->|"Push to Client"| FE
CHANNELS -->|"API Response"| BE_SERVICES
style USERS fill:#E8F5E9,stroke:#4CAF50,color:#1B5E20
style CHANNELS fill:#FFF3E0,stroke:#FF9800,color:#E65100
style FE fill:#E3F2FD,stroke:#2196F3,color:#0D47A1
style BE fill:#FCE4EC,stroke:#E91E63,color:#880E4F
style REALTIME fill:#F3E5F5,stroke:#9C27B0,color:#4A148C
style DB_LAYER fill:#E0F7FA,stroke:#00BCD4,color:#006064
```
---
## 1.2 User Journey Flowchart (Alur Pengguna Umum)
Diagram ini menunjukkan alur perjalanan pengguna dari pertama kali membuka aplikasi hingga menggunakan fitur-fitur utama.
```mermaid
flowchart TD
START([User Opens App]) --> HAS_SESSION{Has Active<br/>Session Cookie?}
HAS_SESSION -->|"Yes"| AUTH_CHECK{Session Valid?<br/>better-auth verify}
HAS_SESSION -->|"No"| LOGIN["Login Page<br/>/auth/sign-in"]
AUTH_CHECK -->|"Valid"| HAS_WORKSPACE{Has Active<br/>Workspace?}
AUTH_CHECK -->|"Invalid / Expired"| LOGIN
LOGIN --> SIGNIN_PAGE["Sign-In Page<br/>(BetterAuth.SignInPage)"]
SIGNIN_PAGE --> INPUT_CREDS["Input Email & Password"]
INPUT_CREDS --> SUBMIT["POST /api/v1/auth/sign-in<br/>better-auth client"]
SUBMIT --> VALID{Credentials Valid?}
VALID -->|"No"| SHOW_ERR["Show Error Message<br/>'Invalid email or password'"]
SHOW_ERR --> SIGNIN_PAGE
VALID -->|"Yes"| SET_COOKIE["Session Cookie Set<br/>User Context Loaded"]
SET_COOKIE --> HAS_WORKSPACE
HAS_WORKSPACE -->|"No"| CREATE_WS["Create Workspace<br/>/dashboard/workspace/create"]
HAS_WORKSPACE -->|"Yes"| DASHBOARD["Dashboard<br/>Redirect to /dashboard/chat"]
CREATE_WS --> WS_FORM["Workspace Creation Form<br/>Input: workspace name"]
WS_FORM --> WS_API["authClient.organization.create()<br/>+ setActive(organization)"]
WS_API --> DASHBOARD
DASHBOARD --> SIDEBAR{{"Sidebar Navigation<br/>MainLayout"}}
SIDEBAR -->|"Chat"| CHAT["/dashboard/chat<br/>Real-time Conversations"]
SIDEBAR -->|"Social Media"| SOCIAL["/dashboard/social-media<br/>Instagram Posts"]
SIDEBAR -->|"Campaign"| CAMP["/dashboard/campaign<br/>Marketing Campaigns"]
SIDEBAR -->|"Contact"| CONTACT["/dashboard/contact<br/>Contact Management"]
SIDEBAR -->|"Tickets"| TICKET["/dashboard/ticket<br/>Issue Tracking"]
SIDEBAR -->|"Settings"| SETTINGS["/dashboard/settings<br/>Configuration Panel"]
CHAT --> CHAT_FLOW["Chat Flow<br/>(see FE Chat Detail)"]
CAMP --> CAMP_FLOW["Campaign Flow<br/>(see FE Campaign Detail)"]
TICKET --> TICKET_FLOW["Ticket Flow<br/>(see FE Ticket Detail)"]
SETTINGS --> SETTINGS_FLOW["Settings Flow<br/>(see FE Settings Detail)"]
style START fill:#C8E6C9,stroke:#4CAF50
style DASHBOARD fill:#BBDEFB,stroke:#2196F3
style SHOW_ERR fill:#FFCDD2,stroke:#F44336
```
---
## 1.3 Application Navigation Structure
Struktur navigasi lengkap yang tersedia di sidebar dashboard.
```mermaid
flowchart LR
subgraph NAV["Dashboard Navigation (Sidebar)"]
direction TB
NAV_MGMT["Management"]
NAV_CHAT["Chat<br/>/dashboard/chat"]
NAV_SOCIAL["Sosial Media<br/>/dashboard/social-media"]
NAV_CAMP["Campaign<br/>/dashboard/campaign"]
NAV_CONTACT["Contact<br/>/dashboard/contact"]
NAV_TICKET["Tickets<br/>/dashboard/ticket"]
NAV_SETTINGS["Settings ▸<br/>/dashboard/settings"]
end
NAV_MGMT --> NAV_CHAT
NAV_MGMT --> NAV_SOCIAL
NAV_MGMT --> NAV_CAMP
NAV_MGMT --> NAV_CONTACT
NAV_MGMT --> NAV_TICKET
NAV_MGMT --> NAV_SETTINGS
NAV_SETTINGS --> SET_TAG["Tags<br/>[admin, owner]"]
NAV_SETTINGS --> SET_USER["Users<br/>[admin, owner]"]
NAV_SETTINGS --> SET_CH["Channels<br/>[admin, owner]"]
NAV_SETTINGS --> SET_WS["Workspaces<br/>[owner only]"]
NAV_SETTINGS --> SET_ACC["My Account<br/>[all roles]"]
NAV_SETTINGS --> SET_OH["Office Hour & Auto Response<br/>[admin, owner]"]
NAV_SETTINGS --> SET_QR["Quick Reply<br/>[admin, owner]"]
NAV_SETTINGS --> SET_API["API Keys<br/>[admin, owner]"]
NAV_SETTINGS --> SET_RC["Category Resolve<br/>[admin, owner]"]
style NAV fill:#F5F5F5,stroke:#9E9E9E
style NAV_SETTINGS fill:#E8EAF6,stroke:#3F51B5
```
---
## 1.4 High-Level Data Flow (Aliran Data Keseluruhan)
```mermaid
flowchart LR
subgraph INPUT["Input Sources"]
USER_ACTION["User Actions<br/>(Click, Type, Submit)"]
WEBHOOK_IN["Channel Webhooks<br/>(WA, IG, TG)"]
end
subgraph PROCESSING["Processing Layer"]
FE_REACT["React App<br/>State Management (SWR)"]
BE_HONO["Hono Server<br/>Route → Service → Repository"]
MQ["RabbitMQ<br/>Work Queue"]
end
subgraph STORAGE["Storage Layer"]
PG["PostgreSQL<br/>30+ Tables"]
FILES["File Storage<br/>Uploads"]
CACHE["SWR Cache<br/>Client-side"]
end
subgraph OUTPUT["Output Channels"]
UI["UI Rendering<br/>React Components"]
PUSH_OUT["Pusher + Socket.IO<br/>Real-time Events"]
PLATFORM_OUT["Platform APIs<br/>Send Messages"]
EMAIL_OUT["Email<br/>Nodemailer"]
end
USER_ACTION --> FE_REACT
WEBHOOK_IN --> BE_HONO
WEBHOOK_IN --> MQ
MQ --> BE_HONO
FE_REACT -->|"HTTP Request"| BE_HONO
BE_HONO --> PG
BE_HONO --> FILES
FE_REACT --> CACHE
BE_HONO --> PUSH_OUT
BE_HONO --> PLATFORM_OUT
BE_HONO --> EMAIL_OUT
PUSH_OUT --> FE_REACT
FE_REACT --> UI
style INPUT fill:#E8F5E9,stroke:#4CAF50
style PROCESSING fill:#E3F2FD,stroke:#2196F3
style STORAGE fill:#FFF3E0,stroke:#FF9800
style OUTPUT fill:#F3E5F5,stroke:#9C27B0
```
---
## 1.5 Multi-Channel Message Flow
```mermaid
flowchart TD
subgraph INCOMING["Pesan Masuk"]
direction LR
C1["WhatsApp Customer"]
C2["Instagram User"]
C3["Telegram User"]
end
subgraph WEBHOOKS["Webhook Processing"]
WH_WA["/api/webhook/whatsapp"]
WH_IG["/api/webhook/instagram"]
WH_TG["/api/webhook/telegram"]
end
subgraph QUEUE["Message Queue"]
RABBIT["RabbitMQ<br/>omnichannel_work_queue"]
end
subgraph BACKEND["Backend Processing"]
PARSE["Parse & Normalize Message"]
FIND_CONTACT["Find or Create Contact"]
FIND_CONV["Find or Create Conversation"]
SAVE_MSG["Save to conversation_messages"]
CHECK_OH{"Within<br/>Office Hours?"}
AUTO_RESP["Send Auto Response<br/>(if configured)"]
NOTIFY["Pusher + Socket.IO<br/>Notify Agents"]
end
subgraph AGENT_UI["Agent Interface"]
FE_RECEIVE["Real-time Update<br/>via Pusher/Socket.IO"]
SWR_UPDATE["SWR Revalidation<br/>Update Conversation List"]
SHOW_MSG["Display New Message<br/>in Chat UI"]
end
C1 -->|"Message"| WH_WA
C2 -->|"DM/Comment"| WH_IG
C3 -->|"Message"| WH_TG
WH_WA -->|"Publish"| RABBIT
WH_IG -->|"Direct"| PARSE
WH_TG -->|"Direct"| PARSE
RABBIT -->|"Consume"| PARSE
PARSE --> FIND_CONTACT --> FIND_CONV --> SAVE_MSG
SAVE_MSG --> CHECK_OH
CHECK_OH -->|"No"| AUTO_RESP
CHECK_OH -->|"Yes"| NOTIFY
AUTO_RESP --> NOTIFY
NOTIFY --> FE_RECEIVE --> SWR_UPDATE --> SHOW_MSG
style INCOMING fill:#E8F5E9,stroke:#4CAF50
style WEBHOOKS fill:#FFF3E0,stroke:#FF9800
style QUEUE fill:#FCE4EC,stroke:#E91E63
style BACKEND fill:#E3F2FD,stroke:#2196F3
style AGENT_UI fill:#F3E5F5,stroke:#9C27B0
```
# BAGIAN 2: INTEGRASI FE-BE
## (Frontend-Backend Integration Flow)
---
## 2.1 API Integration Map — 5 Fitur Utama
Diagram ini menunjukkan mapping lengkap antara Frontend modules, API endpoints, dan Backend services untuk setiap fitur utama.
```mermaid
flowchart LR
subgraph FE["FRONTEND (React 19)"]
direction TB
FE1["🔐 Auth Pages<br/>sign-in, sign-up,<br/>forgot/reset password"]
FE2["💬 Chat Pages<br/>82 files<br/>conversations, messages"]
FE3["📢 Campaign Pages<br/>33 files<br/>templates, recipients"]
FE4["🎫 Ticket Pages<br/>25 files<br/>CRUD, lifecycle"]
FE5["⚙️ Settings Pages<br/>20+ pages<br/>admin configuration"]
end
subgraph API["API LAYER (Hono /api/v1)"]
direction TB
API1["/auth/*<br/>sign-in, sign-up,<br/>forget-password,<br/>change-password,<br/>admin/*, organization/*"]
API2["/u/conversations<br/>/u/conversation-messages<br/>/u/channels<br/>/u/contacts<br/>/u/notes"]
API3["/u/campaigns<br/>/u/campaign-recipient-lists<br/>/u/campaign-recipient-list-items<br/>/u/templates"]
API4["/u/tickets<br/>/u/ticket-history"]
API5["/u/workspaces<br/>/u/workspace-members<br/>/u/workspace-api-keys<br/>/u/channels<br/>/u/tags<br/>/u/user<br/>/u/role-permissions<br/>/u/message-templates<br/>/u/office-hour-*<br/>/u/conversation-resolve-category"]
end
subgraph BE["BACKEND SERVICES"]
direction TB
BS1["better-auth<br/>session, RBAC,<br/>API keys"]
BS2["conversation<br/>conversation-message<br/>channel<br/>contact<br/>note<br/>tag"]
BS3["campaign<br/>campaign-recipient-list<br/>template<br/>whatsapp-template"]
BS4["ticket<br/>ticket-history<br/>ticket-attachment"]
BS5["workspace<br/>workspace-members<br/>workspace-api-key<br/>role-permission<br/>channel<br/>tag<br/>message-template<br/>office-hour-schedule<br/>office-hour-response<br/>conv-resolve-category"]
end
FE1 -->|"POST /auth/sign-in<br/>POST /auth/forget-password<br/>POST /auth/change-password<br/>POST /auth/admin/*<br/>POST /auth/organization/*"| API1
FE2 -->|"GET /conversations<br/>POST /conversation-messages<br/>PATCH /conversations/:id/status<br/>POST /conversations/:id/read<br/>GET /conversation-messages<br/>POST /conversation-messages/template<br/>POST /conversation-messages/upload-media"| API2
FE3 -->|"CRUD /campaigns<br/>POST /campaigns/:id/execute<br/>CRUD /templates<br/>POST /templates/:id/sync<br/>CRUD /campaign-recipient-lists<br/>POST /templates/upload-media"| API3
FE4 -->|"CRUD /tickets<br/>GET /ticket-history/ticket/:id<br/>POST /tickets/upload-media<br/>DELETE /tickets/remove-media/:id"| API4
FE5 -->|"CRUD /workspaces<br/>CRUD /workspace-members<br/>CRUD /channels<br/>CRUD /tags<br/>PUT /user/update-user<br/>POST /auth/change-password<br/>CRUD /role-permissions<br/>CRUD /message-templates<br/>CRUD /office-hour-*<br/>CRUD /conversation-resolve-category<br/>CRUD /workspace-api-keys"| API5
API1 --> BS1
API2 --> BS2
API3 --> BS3
API4 --> BS4
API5 --> BS5
style FE fill:#E3F2FD,stroke:#1976D2
style API fill:#FFF8E1,stroke:#F9A825
style BE fill:#FCE4EC,stroke:#C2185B
```
---
## 2.2 Real-Time Integration Flow
Alur komunikasi real-time dari webhook masuk hingga update UI di browser agent.
```mermaid
flowchart TD
subgraph EXTERNAL["EXTERNAL CHANNELS"]
WA_MSG["📱 WhatsApp<br/>Message from Customer"]
IG_MSG["📸 Instagram<br/>DM / Comment"]
TG_MSG["✈️ Telegram<br/>Message from User"]
end
subgraph BE_RECEIVE["BACKEND - Webhook Receivers"]
WH_WA["POST /api/webhook/whatsapp"]
WH_IG["POST /api/webhook/instagram"]
WH_TG["POST /api/webhook/telegram"]
end
subgraph BE_PROCESS["BACKEND - Processing"]
RABBIT["RabbitMQ Queue<br/>omnichannel_work_queue"]
PROCESSOR["Message Processor Service"]
DB_WRITE["PostgreSQL<br/>INSERT conversations<br/>INSERT conversation_messages"]
PUSHER_S["Pusher Server<br/>Trigger Events"]
SOCKET_S["Socket.IO Server<br/>Emit Events"]
end
subgraph FE_RECEIVE["FRONTEND - Real-time Clients"]
PUSHER_C["Pusher Client<br/>use-pusher-chat.ts<br/>Subscribe: private-room:*<br/>private-channel:*"]
SOCKET_C["Socket.IO Client<br/>use-socket-io.ts<br/>Listen: new-message<br/>new-conversation"]
SWR_CACHE["SWR Cache<br/>Automatic Revalidation<br/>mutate() on events"]
UI["UI Components<br/>ChatNav, ChatMessageList<br/>Auto Update"]
end
WA_MSG -->|"HTTP POST"| WH_WA
IG_MSG -->|"HTTP POST"| WH_IG
TG_MSG -->|"HTTP POST"| WH_TG
WH_WA -->|"Publish to Queue"| RABBIT
WH_IG -->|"Direct Process"| PROCESSOR
WH_TG -->|"Direct Process"| PROCESSOR
RABBIT -->|"Consume"| PROCESSOR
PROCESSOR --> DB_WRITE
PROCESSOR --> PUSHER_S
PROCESSOR --> SOCKET_S
PUSHER_S -->|"private-room:{convId}<br/>Event: new-message"| PUSHER_C
PUSHER_S -->|"private-channel:{chId}<br/>Event: new-conversation"| PUSHER_C
SOCKET_S -->|"Event: new-message"| SOCKET_C
SOCKET_S -->|"Event: conversation-updated"| SOCKET_C
PUSHER_C --> SWR_CACHE
SOCKET_C --> SWR_CACHE
SWR_CACHE --> UI
style EXTERNAL fill:#E8F5E9,stroke:#4CAF50
style BE_RECEIVE fill:#FFF3E0,stroke:#FF9800
style BE_PROCESS fill:#FCE4EC,stroke:#E91E63
style FE_RECEIVE fill:#E3F2FD,stroke:#2196F3
```
---
## 2.3 Real-Time Event Types
Tabel event real-time yang digunakan dalam sistem.
```mermaid
flowchart TD
subgraph EVENTS["Real-Time Events"]
direction TB
E1["new-message<br/>Pesan baru masuk/terkirim"]
E2["new-conversation<br/>Conversation baru dibuat"]
E3["message-updated<br/>Status pesan berubah<br/>(SENT → DELIVERED → READ)"]
E4["conversation-updated<br/>Status/assignee conversation berubah"]
E5["new-post-comment<br/>Komentar baru di Instagram post"]
E6["post-updated<br/>Instagram post diupdate"]
end
subgraph CHANNELS["Pusher Channels"]
C1["private-room:{conversationId}<br/>Event: new-message, message-updated"]
C2["private-channel:{channelId}<br/>Event: new-conversation, conversation-updated"]
C3["private-post:{postId}<br/>Event: new-post-comment, post-updated"]
end
E1 --> C1
E2 --> C2
E3 --> C1
E4 --> C2
E5 --> C3
E6 --> C3
style EVENTS fill:#F3E5F5,stroke:#9C27B0
style CHANNELS fill:#E8EAF6,stroke:#3F51B5
```
---
## 2.4 Authentication Integration Flow
Alur integrasi autentikasi antara FE dan BE.
```mermaid
flowchart TD
subgraph FE_AUTH["FRONTEND - Auth Context"]
BA_CLIENT["better-auth client<br/>/src/lib/better-auth.ts<br/>Base URL: /api/v1/auth"]
AUTH_PROVIDER["AuthProvider<br/>/src/auth/context/better-auth/"]
AUTH_GUARD["AuthGuard<br/>GuestGuard<br/>WorkspaceGuard<br/>RouteRoleGuard"]
end
subgraph API_AUTH["API ENDPOINTS"]
SIGN_IN["POST /api/v1/auth/sign-in/email-password"]
SIGN_UP["POST /api/v1/auth/sign-up/email-password"]
FORGOT["POST /api/v1/auth/forget-password"]
RESET["POST /api/v1/auth/reset-password"]
CHANGE["POST /api/v1/auth/change-password"]
SESSION["GET /api/v1/auth/get-session"]
ADMIN_API["POST /api/v1/auth/admin/*"]
ORG_API["POST /api/v1/auth/organization/*"]
end
subgraph BE_AUTH["BACKEND - better-auth Engine"]
BA_HANDLER["better-auth handler<br/>/src/services/better-auth/"]
SESSION_MW["requireAuth Middleware<br/>Session verification"]
PERM_MW["requirePermission Middleware<br/>RBAC check"]
APIKEY_MW["requireWorkspaceApiKey Middleware<br/>x-api-key verification"]
end
BA_CLIENT -->|"HTTP Request"| API_AUTH
AUTH_PROVIDER --> BA_CLIENT
AUTH_GUARD --> AUTH_PROVIDER
API_AUTH --> BA_HANDLER
BA_HANDLER -->|"Set Session Cookie"| BA_CLIENT
SESSION_MW -->|"Verify Session"| BA_HANDLER
PERM_MW -->|"Check Role + Permission"| BA_HANDLER
APIKEY_MW -->|"Verify API Key"| BA_HANDLER
style FE_AUTH fill:#E3F2FD,stroke:#1976D2
style API_AUTH fill:#FFF8E1,stroke:#F9A825
style BE_AUTH fill:#FCE4EC,stroke:#C2185B
```
---
## 2.5 File Upload Integration Flow
```mermaid
flowchart TD
FE_UPLOAD["Frontend<br/>File Upload Component<br/>(multipart/form-data)"] --> ROUTE_CHECK{Upload Type?}
ROUTE_CHECK -->|"Chat Media"| CHAT_API["POST /api/v1/u/conversation-messages/upload-media<br/>body: {file, mediaType, conversationId}"]
ROUTE_CHECK -->|"Ticket Media"| TICKET_API["POST /api/v1/u/tickets/upload-media<br/>body: {file, ticketId?}"]
ROUTE_CHECK -->|"Template Media"| TEMPL_API["POST /api/v1/u/templates/upload-media<br/>body: {file, mediaType, channelId}<br/>(Meta Resumable API)"]
ROUTE_CHECK -->|"Channel Icon"| ICON_API["POST /api/v1/u/channels/:id/upload-icon<br/>body: {file}"]
ROUTE_CHECK -->|"Profile Photo"| PROF_API["PUT /api/v1/u/user/update-user<br/>body: {photo}"]
CHAT_API --> FILE_SERVICE["File Storage Service"]
TICKET_API --> FILE_SERVICE
TEMPL_API --> META_API["Meta Graph API<br/>Resumable Upload"]
ICON_API --> FILE_SERVICE
PROF_API --> FILE_SERVICE
FILE_SERVICE --> STORAGE_CHECK{Storage Type?}
STORAGE_CHECK -->|"LOCAL"| LOCAL["Local Filesystem<br/>/uploads/*"]
STORAGE_CHECK -->|"S3"| S3["AWS S3"]
STORAGE_CHECK -->|"GCS"| GCS["Google Cloud Storage"]
STORAGE_CHECK -->|"AZURE"| AZURE["Azure Blob Storage"]
META_API --> META_STORE["Meta Servers"]
style FE_UPLOAD fill:#E3F2FD,stroke:#1976D2
style STORAGE_CHECK fill:#FFF8E1,stroke:#F9A825
```
---
## 2.6 Middleware Stack (Request Lifecycle)
Diagram ini menunjukkan urutan middleware yang dilalui setiap request dari FE ke BE.
```mermaid
flowchart TD
REQUEST["Incoming HTTP Request"] --> CORS["① CORS Middleware<br/>cors.config.ts<br/>Allowed origins validation"]
CORS --> TIMING["② Timing Middleware<br/>Response time header"]
TIMING --> REQ_ID["③ Request ID<br/>Unique request identifier"]
REQ_ID --> LOGGER["④ Logger Middleware<br/>Request logging"]
LOGGER --> BODY_LIMIT["⑤ Body Limit Middleware<br/>5MB default<br/>100MB WhatsApp uploads"]
BODY_LIMIT --> TIMEOUT["⑥ Timeout Middleware<br/>5min default<br/>15min uploads"]
TIMEOUT --> ROUTE_MATCH{Route Match?}
ROUTE_MATCH -->|"/api/v1/auth/*"| AUTH_ROUTE["better-auth handler<br/>(self-handled)"]
ROUTE_MATCH -->|"/api/webhook/*"| WEBHOOK_ROUTE["Webhook Routes<br/>(WhatsApp CORS only)"]
ROUTE_MATCH -->|"/api/v1/u/*"| PROTECTED["Protected Route Chain"]
ROUTE_MATCH -->|"/uploads/*"| UPLOAD_ROUTE["File Storage Route<br/>(+ upload timeout)"]
ROUTE_MATCH -->|"/api/v1/swagger"| SWAGGER["Swagger UI"]
PROTECTED --> AUTH_MW["⑦ requireAuth<br/>Session verification"]
AUTH_MW --> PERM_MW["⑧ requirePermission<br/>RBAC: resource:action"]
PERM_MW --> HANDLER["Route Handler<br/>→ Service → Repository → DB"]
AUTH_ROUTE --> HANDLER2["better-auth internal handler"]
WEBHOOK_ROUTE --> HANDLER3["Webhook processor"]
style REQUEST fill:#C8E6C9,stroke:#4CAF50
style HANDLER fill:#BBDEFB,stroke:#2196F3
style HANDLER2 fill:#BBDEFB,stroke:#2196F3
style HANDLER3 fill:#BBDEFB,stroke:#2196F3
```
# BAGIAN 3: FLOWCHART FRONTEND (FE)
## Detailed Frontend Application Flowcharts
---
## 3.1 FE General Routing Architecture
Diagram lengkap struktur routing Frontend dari root hingga setiap halaman.
```mermaid
flowchart TD
ROOT["/ (Root Route)<br/>src/app.tsx"] --> MAIN_LAYOUT["MainLayout<br/>(Public Pages)"]
ROOT --> AUTH_LAYOUT["AuthSplitLayout<br/>(Auth Pages)"]
ROOT --> SIMPLE_LAYOUT["SimpleLayout<br/>(Minimal Pages)"]
ROOT --> DASH_LAYOUT["DashboardLayout<br/>(Protected Pages)"]
subgraph PUBLIC["Public Routes"]
MAIN_LAYOUT --> HOME["/ — HomePage"]
MAIN_LAYOUT --> ABOUT["/about-us"]
MAIN_LAYOUT --> PRIVACY["/privacy-policy"]
MAIN_LAYOUT --> TERMS["/terms-of-service"]
SIMPLE_LAYOUT --> MAINT["/maintenance"]
end
subgraph AUTH_ROUTES["Auth Routes (GuestGuard)"]
AUTH_LAYOUT --> GUEST["GuestGuard<br/>(Block if logged in)"]
GUEST --> SIGNIN["/auth/sign-in<br/>BetterAuth.SignInPage"]
GUEST --> FORGOT["/auth/forgot-password<br/>BetterAuth.ForgotPasswordPage"]
GUEST --> RESET["/auth/reset-password<br/>BetterAuth.ResetPasswordPage"]
GUEST --> SIGNUP["/auth/sign-up<br/>(Hidden - Invite Only)"]
GUEST --> ACCEPT_INV["/accept-invitation/:id<br/>(+ ReCAPTCHA)"]
end
subgraph ERROR_ROUTES["Error Routes"]
ROOT --> E403["/error/403 — Forbidden"]
ROOT --> E404["/error/404 — Not Found"]
ROOT --> E500["/error/500 — Server Error"]
end
subgraph DASH["Dashboard Routes (AuthGuard + WorkspaceGuard)"]
DASH_LAYOUT --> AUTH_G["AuthGuard<br/>Check Session"]
AUTH_G --> WS_G["WorkspaceGuard<br/>Check Active Workspace"]
WS_G --> DASH_NAV{{"Dashboard Navigation"}}
DASH_NAV --> CHAT_R["/dashboard/chat"]
DASH_NAV --> SOCIAL["/dashboard/social-media"]
DASH_NAV --> WS_CREATE["/dashboard/workspace/create"]
DASH_NAV --> TAG_NAV["/dashboard/tag<br/>/new /:id/edit"]
DASH_NAV --> CONTACT["/dashboard/contact"]
DASH_NAV --> CAMP_NAV["/dashboard/campaign/*"]
DASH_NAV --> TICK_NAV["/dashboard/ticket/*"]
DASH_NAV --> SET_NAV["/dashboard/settings/*"]
end
style ROOT fill:#C8E6C9,stroke:#4CAF50
style DASH fill:#E3F2FD,stroke:#1976D2
style AUTH_ROUTES fill:#FFF3E0,stroke:#FF9800
style PUBLIC fill:#F3E5F5,stroke:#9C27B0
```
---
## 3.2 FE Auth Flow (Detailed)
Alur autentikasi lengkap dari halaman login hingga masuk dashboard.
```mermaid
flowchart TD
START([User Opens App]) --> VISIT{"Current URL?"}
VISIT -->|"/auth/*"| GUEST_CHECK{GuestGuard<br/>Is Logged In?}
VISIT -->|"/dashboard/*"| AUTH_CHECK{AuthGuard<br/>Has Session?}
VISIT -->|Other| PUBLIC_PAGE["Show Public Page"]
GUEST_CHECK -->|Yes| REDIRECT_DASH["Redirect to<br/>/dashboard/chat"]
GUEST_CHECK -->|No| AUTH_PAGE["Show Auth Page"]
AUTH_CHECK -->|No| REDIRECT_LOGIN["Redirect to<br/>/auth/sign-in"]
AUTH_CHECK -->|Yes| WS_CHECK{WorkspaceGuard<br/>Has Workspace?}
WS_CHECK -->|No| REDIRECT_WS["Redirect to<br/>/dashboard/workspace/create"]
WS_CHECK -->|Yes| ROLE_CHECK{RouteRoleGuard<br/>Has Permission?}
ROLE_CHECK -->|No| REDIRECT_403["Redirect to<br/>/error/403"]
ROLE_CHECK -->|Yes| SHOW_PAGE["Show Dashboard Page"]
subgraph SIGNIN_FLOW["Sign-In Process"]
AUTH_PAGE --> SIGNIN["BetterAuth.SignInPage"]
SIGNIN --> INPUT["Input Email + Password"]
INPUT --> CLICK["Click Sign In"]
CLICK --> API_CALL["authClient.signIn.email()<br/>POST /api/v1/auth/sign-in/email-password"]
API_CALL --> SUCCESS{Success?}
SUCCESS -->|No| ERR_MSG["Show Error<br/>'Invalid credentials'"]
SUCCESS -->|Yes| SESSION_SET["Session Cookie Set<br/>AuthProvider Context Updated"]
SESSION_SET --> REDIRECT_DASH
ERR_MSG --> INPUT
end
subgraph INVITE_FLOW["Invitation Flow"]
ACCEPT_INV["/accept-invitation/:id"] --> CAPTCHA["ReCAPTCHA Verification"]
CAPTCHA --> VERIFY{Captcha Valid?}
VERIFY -->|No| CAPTCHA
VERIFY -->|Yes| ACCEPT_API["authClient.organization<br/>.acceptInvitation(id)"]
ACCEPT_API --> SUCCESS_INV{Success?}
SUCCESS_INV -->|No| INV_ERR["Show Error"]
SUCCESS_INV -->|Yes| SET_ACTIVE["SetActive Organization"]
SET_ACTIVE --> REDIRECT_DASH
INV_ERR --> CAPTCHA
end
style START fill:#C8E6C9,stroke:#4CAF50
style REDIRECT_LOGIN fill:#FFCDD2,stroke:#F44336
style REDIRECT_403 fill:#FFCDD2,stroke:#F44336
style SHOW_PAGE fill:#BBDEFB,stroke:#2196F3
style SIGNIN fill:#FFF8E1,stroke:#F9A825
```
---
## 3.3 FE Chat Module Flow (Detailed)
Modul Chat adalah modul terbesar (82 files). Diagram ini menunjukkan alur lengkap.
> **Catatan Penting (Kondisi Aktual):**
> - **Conversations**: Di-fetch **Full GET**. BE route menerima parameter `page`/`limit` dan memanggil `calculatePagination()`, namun repository **tidak menggunakan** `.limit()`/`.offset()` di query DB. Semua conversations di-return sekaligus.
> - **Messages**: Di-fetch **Full GET**. Parameter `page`/`limit` di route, service, dan repository BE **sudah di-comment out**. Response mengembalikan `page:0, limit:0, totalPages:0` (dummy). Semua messages dalam conversation chain di-return sekaligus.
> - **Infinite Scroll**: Hook `use-message-list-infinite-scroll.ts` dan `use-message-list-scroll-persistence.ts` masih di-import di `chat-message-list.tsx`, tapi **tidak pernah ter-trigger** karena `hasMore` selalu `false` (karena response pagination dummy). Hook `use-infinite-scroll.ts` dan `use-messages-scroll.ts` adalah **dead code** (tidak di-import di mana pun).
> - **Auto-Scroll**: Hook `use-message-list-initial-load.ts` tetap aktif untuk auto-scroll to bottom saat initial load.
```mermaid
flowchart TD
CHAT_PAGE["Chat Page<br/>/dashboard/chat"] --> INIT["Initialize ChatContext"]
INIT --> LOAD_CHANNELS["Load Channels<br/>GET /channels/with-unread-count"]
INIT --> LOAD_COUNTS["Load Chat Counts<br/>GET /conversations/total-counts-chat"]
INIT --> CONNECT_RT["Connect Real-time<br/>Pusher + Socket.IO"]
LOAD_CHANNELS --> RENDER_LAYOUT["Render ChatLayout<br/>(3-Column Layout)"]
LOAD_COUNTS --> RENDER_LAYOUT
CONNECT_RT --> RENDER_LAYOUT
subgraph LEFT_PANEL["LEFT PANEL — ChatNav"]
RENDER_LAYOUT --> CH_NAV["chat-nav.tsx"]
CH_NAV --> CH_FOOTER["chat-channel-list-footer.tsx<br/>Channel filter tabs"]
CH_FOOTER --> CH_SEARCH["chat-nav-search-results.tsx<br/>Search conversations"]
CH_SEARCH --> CH_ITEM["chat-nav-item.tsx<br/>Conversation list item"]
CH_ITEM --> CH_LIST["Conversation List (Full GET)<br/>use-conversation-pagination.ts<br/>⚠️ BE route terima page/limit<br/>tapi repository tidak pakai"]
end
subgraph FILTERS["Conversation Filters"]
FILTER_GROUP["Filter Options:"] --> F1["Channel Filter<br/>(WhatsApp, Instagram, etc.)"]
FILTER_GROUP --> F2["Status Filter<br/>All / My / Unassigned / Assigned / Resolved"]
FILTER_GROUP --> F3["Tag Filter"]
FILTER_GROUP --> F4["Date Range Filter"]
FILTER_GROUP --> F5["24h Window Filter<br/>(WhatsApp only)"]
FILTER_GROUP --> F6["First Response Filter"]
end
CH_LIST --> FILTERS
subgraph CENTER_PANEL["CENTER PANEL — ChatRoom"]
CH_LIST -->|"Select Conversation"| LOAD_MSG["Load All Messages (Full GET)<br/>GET /conversation-messages<br/>use-message-list-initial-load.ts<br/>⚠️ BE limit/offset di-comment out<br/>→ returns all messages"]
LOAD_MSG --> MSG_LIST["chat-message-list.tsx<br/>Message list<br/>(auto-scroll to bottom on load)"]
MSG_LIST --> MSG_ITEM["chat-message-item.tsx<br/>Individual message bubble"]
MSG_ITEM --> MSG_TYPES["Message Types:<br/>TEXT, IMAGE, VIDEO, AUDIO,<br/>DOCUMENT, TEMPLATE, SYSTEM,<br/>INTERNAL_NOTE"]
MSG_TYPES --> DATE_SEP["chat-date-separator.tsx<br/>Date separators between messages"]
end
subgraph INPUT_AREA["MESSAGE INPUT"]
MSG_LIST --> MSG_INPUT["chat-message-input.tsx"]
MSG_INPUT --> TEXT_INPUT["bubble-editor.tsx<br/>(TipTap Rich Editor)"]
MSG_INPUT --> FILE_BTN["File Upload<br/>use-file-upload.ts"]
MSG_INPUT --> EMOJI_BTN["Emoji Picker<br/>use-emoji-picker.ts"]
MSG_INPUT --> QUICK_BTN["Quick Message<br/>quick-message-overlay.tsx<br/>use-quick-message.ts"]
MSG_INPUT --> TEMPL_BTN["WhatsApp Template<br/>chat-template-dialog.tsx<br/>use-template-dialog.ts"]
MSG_INPUT --> REPLY_BTN["Reply to Message"]
MSG_INPUT --> SEND_BTN["Send Button"]
end
subgraph SEND_FLOW["SEND MESSAGE FLOW"]
TEXT_INPUT --> SEND_BTN
FILE_BTN --> UPLOAD["use-file-upload.ts<br/>POST /upload-media"]
UPLOAD --> SEND_BTN
QUICK_BTN --> INSERT_QM["Insert Quick Message Text"]
TEMPL_BTN --> SEND_TEMPL["POST /conversation-messages/template"]
SEND_BTN -->|"POST /conversation-messages<br/>(JSON or Multipart)"| API_SEND
INSERT_QM --> TEXT_INPUT
end
subgraph RT_UPDATE["REAL-TIME UPDATE"]
API_SEND -->|"Message Sent"| PUSHER_RECV["use-pusher-chat.ts<br/>Listen: new-message"]
PUSHER_RECV --> SWR_MUTATE["SWR mutate()<br/>Revalidate messages cache"]
SWR_MUTATE --> UI_UPDATE["Update Message List<br/>(Optimistic UI)"]
end
subgraph RIGHT_PANEL["RIGHT PANEL — Details Sidebar"]
RENDER_LAYOUT --> INFO_TAB["Information Tab<br/>chat-room-information.tsx<br/>Contact details, tags, channels"]
RENDER_LAYOUT --> NOTE_TAB["Notes Tab<br/>note-list, note-item,<br/>note-detail, note-form<br/>CRUD: POST /notes"]
RENDER_LAYOUT --> TICKET_TAB["Ticket Tab<br/>ticket-list, ticket-item,<br/>ticket-detail, ticket-form<br/>Linked tickets to conversation"]
end
subgraph HEADER_ACTIONS["HEADER ACTIONS"]
RENDER_LAYOUT --> CH_HEADER["chat-header-details.tsx"]
CH_HEADER --> ASSIGN["Assign Conversation<br/>chat-assign-dialog.tsx<br/>PATCH /conversations/:id/status"]
CH_HEADER --> RESOLVE["Resolve Conversation<br/>chat-resolve-dialog.tsx<br/>PATCH /conversations/:id/status"]
CH_HEADER --> HISTORY["View History<br/>chat-room-history.tsx<br/>GET /conversations/:id/history"]
CH_HEADER --> GET_NEW["Get New Chat<br/>POST /conversations/get-new-chat<br/>(Claim next unassigned)"]
CH_HEADER --> SEARCH_MSG["Search Messages<br/>chat-search-dialog.tsx<br/>GET /conversation-messages/search"]
CH_HEADER --> BULK_RESOLVE["Bulk Resolve<br/>bulk-resolve-modal.tsx<br/>POST /conversations/bulk-resolve"]
end
style CHAT_PAGE fill:#E3F2FD,stroke:#1976D2
style LEFT_PANEL fill:#E8F5E9,stroke:#4CAF50
style CENTER_PANEL fill:#FFF3E0,stroke:#FF9800
style RIGHT_PANEL fill:#F3E5F5,stroke:#9C27B0
style SEND_FLOW fill:#FFEBEE,stroke:#F44336
style RT_UPDATE fill:#E0F7FA,stroke:#00BCD4
```
---
## 3.4 FE Campaign Module Flow (Detailed)
Alur lengkap modul Campaign dari pembuatan template hingga eksekusi.
```mermaid
flowchart TD
CAMP_PAGE["Campaign Page<br/>/dashboard/campaign"] --> CAMP_LAYOUT["campaign-layout.tsx<br/>(Tab Navigation)"]
CAMP_LAYOUT --> TAB_CAMPAIGNS["Tab: Campaigns"]
CAMP_LAYOUT --> TAB_TEMPLATES["Tab: Templates"]
CAMP_LAYOUT --> TAB_RECIPIENTS["Tab: Recipient Lists"]
subgraph CAMPAIGNS_TAB["CAMPAIGNS MANAGEMENT"]
TAB_CAMPAIGNS --> CAMP_LIST["campaign-view.tsx<br/>Campaign List DataTable"]
CAMP_LIST --> CAMP_TOOLBAR["campaign-table-toolbar.tsx<br/>Filters + Search"]
CAMP_LIST --> CAMP_ROW["campaign-table-row.tsx<br/>Campaign Row"]
CAMP_ROW --> CAMP_ACTIONS["Campaign Actions"]
CAMP_ACTIONS --> VIEW_DETAIL["View Detail<br/>/dashboard/campaign/:id<br/>campaign-detail-view.tsx"]
CAMP_ACTIONS --> EDIT["Edit Campaign<br/>/dashboard/campaign/:id/edit<br/>campaign-create-view.tsx"]
CAMP_ACTIONS --> EXECUTE["Execute Campaign<br/>POST /campaigns/:id/execute"]
CAMP_ACTIONS --> DELETE["Delete Campaign<br/>DELETE /campaigns/:id"]
CAMP_LIST --> CREATE_BTN["+ New Campaign"]
CREATE_BTN --> CREATE_FORM["campaign-create-view.tsx<br/>campaign-create-form.tsx"]
CREATE_FORM --> FORM_STEP1["Step 1: Campaign Name<br/>& Description"]
FORM_STEP1 --> FORM_STEP2["Step 2: Select Channel<br/>(WhatsApp channel only)"]
FORM_STEP2 --> FORM_STEP3["Step 3: Select Template<br/>(from approved templates)"]
FORM_STEP3 --> FORM_STEP4["Step 4: Select Recipient List<br/>(from recipient lists)"]
FORM_STEP4 --> SAVE_DRAFT["Save as DRAFT<br/>POST /campaigns"]
end
subgraph TEMPLATES_TAB["TEMPLATE MANAGEMENT"]
TAB_TEMPLATES --> TEMPL_LIST["campaign-templates-view.tsx<br/>Template List"]
TEMPL_LIST --> TEMPL_TOOLBAR["template-table-toolbar.tsx"]
TEMPL_LIST --> TEMPL_ROW["template-table-row.tsx"]
TEMPL_ROW --> TEMPL_ACTIONS["Template Actions"]
TEMPL_ACTIONS --> VIEW_TEMPL["View Detail<br/>campaign-templates-detail.tsx"]
TEMPL_ACTIONS --> EDIT_TEMPL["Edit Template<br/>campaign-template-edit.tsx"]
TEMPL_ACTIONS --> SYNC_TEMPL["Sync to WhatsApp<br/>POST /templates/:id/sync"]
TEMPL_ACTIONS --> DEL_TEMPL["Delete Template<br/>DELETE /templates/:id"]
TEMPL_LIST --> CREATE_TEMPL["+ New Template"]
CREATE_TEMPL --> TEMPL_CREATE["campaign-template-create.tsx"]
TEMPL_CREATE --> TEMPL_FORM["template-form-fields.tsx<br/>template-form-schema.ts"]
TEMPL_FORM --> TEMPL_HEADER["Header Component<br/>(Text / Image / Video)"]
TEMPL_FORM --> TEMPL_BODY["Body Component<br/>(Text + Variables {{1}}, {{2}}...)"]
TEMPL_FORM --> TEMPL_FOOTER["Footer Component<br/>(Text)"]
TEMPL_FORM --> TEMPL_BUTTONS["Buttons Component<br/>(Quick Reply / URL / Phone)"]
TEMPL_BODY --> UPLOAD_MEDIA["Upload Header Media<br/>POST /templates/upload-media<br/>(Meta Resumable API)"]
TEMPL_FORM --> SAVE_TEMPL["Create Template<br/>POST /templates"]
SAVE_TEMPL --> SYNC_WA["Sync to WhatsApp<br/>POST /templates/:id/sync"]
end
subgraph RECIPIENTS_TAB["RECIPIENT LIST MANAGEMENT"]
TAB_RECIPIENTS --> RL_LIST["campaign-recipient-lists-view.tsx"]
RL_LIST --> RL_TOOLBAR["recipient-list-table-toolbar.tsx"]
RL_LIST --> RL_ROW["recipient-list-table-row.tsx"]
RL_ROW --> RL_ACTIONS["List Actions"]
RL_ACTIONS --> VIEW_RL["View List Detail<br/>campaign-recipient-list-detail.tsx"]
RL_ACTIONS --> EDIT_RL["Edit List"]
RL_ACTIONS --> DEL_RL["Delete List<br/>DELETE /campaign-recipient-lists/:id"]
RL_LIST --> CREATE_RL["+ New Recipient List"]
CREATE_RL --> WIZARD["recipient-list-create-wizard.tsx<br/>(Step-by-step Wizard)"]
WIZARD --> W_STEP1["Step 1: Name & Description<br/>+ Variable Definitions<br/>(e.g. {{nama}}, {{no_pesanan}})"]
W_STEP1 --> W_STEP2["Step 2: Select Contacts<br/>recipient-list-select-contacts-dialog.tsx<br/>(From contact database)"]
W_STEP2 --> W_STEP3["Step 3: Input Variable Values<br/>recipient-list-input-variables-dialog.tsx<br/>(Per contact: nama=John, no_pesanan=123)"]
W_STEP3 --> SAVE_RL["Save Recipient List<br/>POST /campaign-recipient-lists"]
VIEW_RL --> ITEMS_LIST["List Items<br/>campaign-recipient-list-item-table-row.tsx"]
ITEMS_LIST --> ADD_CONTACTS["Add More Contacts<br/>recipient-list-add-contacts-dialog.tsx<br/>POST /campaign-recipient-lists/:id/contacts"]
ITEMS_LIST --> RM_CONTACTS["Remove Contacts<br/>DELETE /campaign-recipient-lists/:id/contacts"]
end
subgraph EXECUTION_FLOW["CAMPAIGN EXECUTION"]
EXECUTE --> EXEC_API["POST /campaigns/:id/execute"]
EXEC_API --> BE_PROCESS["Backend Processing<br/>(See BE Campaign Flow)"]
BE_PROCESS --> DELIVERY_WEBHOOK["WhatsApp Delivery Webhooks<br/>→ Update per-recipient status"]
DELIVERY_WEBHOOK --> VIEW_STATS["View Campaign Stats<br/>sent / delivered / read / failed"]
end
style CAMPAIGNS_TAB fill:#E8F5E9,stroke:#4CAF50
style TEMPLATES_TAB fill:#FFF3E0,stroke:#FF9800
style RECIPIENTS_TAB fill:#F3E5F5,stroke:#9C27B0
style EXECUTION_FLOW fill:#FFEBEE,stroke:#F44336
```
---
## 3.5 FE Ticket Module Flow (Detailed)
Alur lengkap modul Ticket dari pembuatan hingga lifecycle management.
```mermaid
flowchart TD
TICKET_PAGE["Ticket Page<br/>/dashboard/ticket"] --> TICKET_LIST["ticket-list-view.tsx<br/>Ticket DataTable"]
TICKET_LIST --> TOOLBAR["ticket-table-toolbar.tsx<br/>Status Filter | Assignee Filter<br/>Contact Search"]
TICKET_LIST --> TABLE["Ticket Table Columns:<br/>Number, Title, Status,<br/>Assignee, Created, Updated"]
TABLE --> CREATE_BTN["+ New Ticket"]
TABLE --> SELECT_ROW["Select Ticket → Detail View"]
TABLE --> BATCH_ACTIONS["Batch Actions<br/>(Checkbox Selection)"]
subgraph CREATE_FLOW["CREATE TICKET"]
CREATE_BTN --> CREATE_VIEW["ticket-create-view.tsx<br/>(Standalone)"]
CREATE_VIEW --> CREATE_FORM["ticket-create-form.tsx"]
CREATE_FORM --> FORM_TITLE["Ticket Title *"]
CREATE_FORM --> FORM_DESC["Description *"]
CREATE_FORM --> FORM_CONV["Link Conversation<br/>(optional - select from list)"]
CREATE_FORM --> FORM_CONTACT["Link Contact<br/>(optional - select from list)"]
CREATE_FORM --> FORM_ATTACH["Attachments<br/>POST /tickets/upload-media"]
CREATE_FORM --> FORM_TEMPL["Use Template<br/>/dashboard/ticket/templates"]
FORM_ATTACH --> SUBMIT["POST /tickets<br/>→ Status: TODO"]
FORM_TITLE --> SUBMIT
FORM_DESC --> SUBMIT
end
subgraph DETAIL_FLOW["TICKET DETAIL VIEW"]
SELECT_ROW --> DETAIL["ticket-detail-view.tsx<br/>/dashboard/ticket/:id"]
DETAIL --> INFO["Ticket Info<br/>Number, Title, Description,<br/>Status, Assignee, Dates"]
DETAIL --> ATTACHMENTS["ticket-attachment-preview.tsx<br/>File attachments"]
DETAIL --> HISTORY["ticket-history-timeline.tsx<br/>Status change history"]
DETAIL --> ACTION_BUTTONS["Status Action Buttons"]
end
subgraph ACTIONS["TICKET LIFECYCLE ACTIONS"]
ACTION_BUTTONS --> ASSIGN["Assign to Agent<br/>ticket-assign-dialog.tsx<br/>PUT /tickets/:id<br/>assignedTo, assignedBy"]
ACTION_BUTTONS --> START["Start Progress<br/>PUT /tickets/:id<br/>status: IN_PROGRESS"]
ACTION_BUTTONS --> RESOLVE["Resolve<br/>ticket-resolve-dialog.tsx<br/>PUT /tickets/:id<br/>status: RESOLVED + resolvedNote"]
ACTION_BUTTONS --> DONE["Mark Done<br/>ticket-done-dialog.tsx<br/>PUT /tickets/:id<br/>status: DONE"]
ACTION_BUTTONS --> REJECT["Reject<br/>ticket-reject-dialog.tsx<br/>PUT /tickets/:id<br/>status: REJECTED + rejectReason"]
ACTION_BUTTONS --> EDIT["Edit Ticket<br/>ticket-edit-view.tsx<br/>PUT /tickets/:id"]
end
subgraph BATCH_OP["BATCH OPERATIONS"]
BATCH_ACTIONS --> BATCH_ASSIGN["Batch Assign<br/>ticket-batch-assign-dialog.tsx<br/>Loop PUT /tickets/:id"]
BATCH_ACTIONS --> BATCH_STATUS["Batch Update Status<br/>→ IN_PROGRESS<br/>Loop PUT /tickets/:id"]
end
subgraph TICKET_TEMPLATES["TICKET TEMPLATES"]
TICKET_PAGE --> TEMPL_NAV["/dashboard/ticket/templates"]
TEMPL_NAV --> TEMPL_LIST_T["ticket-template-list-view.tsx"]
TEMPL_LIST_T --> TEMPL_CREATE_T["+ New Ticket Template<br/>ticket-template-create-view.tsx"]
TEMPL_CREATE_T --> TEMPL_FORM_T["ticket-template-form-fields.tsx"]
TEMPL_FORM_T --> TEMPL_TYPE["Template Type:<br/>TICKET_START / TICKET_END"]
TEMPL_FORM_T --> SAVE_TEMPL_T["POST /templates/ticket"]
end
subgraph EMBEDDED["TICKET FROM CHAT"]
CHAT_PANEL["Chat Room<br/>Right Panel → Ticket Tab"] --> TICKET_TAB["chat-room-ticket.tsx"]
TICKET_TAB --> TICKET_FORM_CHAT["ticket-form.tsx<br/>(Inline Create)"]
TICKET_FORM_CHAT --> CREATE_FROM_CHAT["Create Ticket<br/>(auto-links current conversation)"]
TICKET_TAB --> TICKET_LIST_CHAT["ticket-list.tsx<br/>(Linked tickets list)"]
end
style CREATE_FLOW fill:#E8F5E9,stroke:#4CAF50
style DETAIL_FLOW fill:#FFF3E0,stroke:#FF9800
style ACTIONS fill:#FFEBEE,stroke:#F44336
style TICKET_TEMPLATES fill:#E3F2FD,stroke:#1976D2
style EMBEDDED fill:#F3E5F5,stroke:#9C27B0
```
---
## 3.6 FE Settings Module Flow (Detailed)
Alur lengkap halaman Settings beserta role guard pada setiap sub-menu.
```mermaid
flowchart TD
SETTINGS_ENTRY["/dashboard/settings"] --> REDIRECT["Redirect to<br/>/dashboard/settings/user/list"]
SETTINGS_ENTRY --> SETTINGS_NAV{{"Settings Navigation"}}
subgraph USER_MGMT["USER MANAGEMENT [admin, owner]"]
SETTINGS_NAV --> SET_USER["/settings/user/list<br/>SettingsUserListPage"]
SETTINGS_NAV --> SET_USER_NEW["/settings/user/new<br/>UserCreatePage"]
SETTINGS_NAV --> SET_USER_EDIT["/settings/user/:id/edit<br/>UserEditPage"]
SETTINGS_NAV --> SET_ROLE_PERM["/settings/user/role-permission<br/>SettingsUserRolePermissionPage"]
SET_USER --> USER_TABLE["User List Table<br/>GET /auth/admin/list-users"]
USER_TABLE --> USER_ACTIONS["User Actions"]
USER_ACTIONS --> CREATE_USER["Create User<br/>POST /auth/admin/create-user"]
USER_ACTIONS --> EDIT_USER["Edit User<br/>POST /auth/admin/update-user"]
USER_ACTIONS --> SET_ROLE["Set Role<br/>POST /auth/admin/set-role"]
USER_ACTIONS --> BAN_USER["Ban User"]
USER_ACTIONS --> UNBAN["Unban User<br/>POST /auth/admin/unban-user"]
USER_ACTIONS --> INVITE["Invite Member<br/>POST /auth/organization/invite-member"]
end
subgraph CHANNEL_MGMT["CHANNEL MANAGEMENT [admin, owner]"]
SETTINGS_NAV --> SET_CH["/settings/channel<br/>ChannelPage"]
SETTINGS_NAV --> SET_CH_NEW["/settings/channel/new<br/>ChannelCreatePage"]
SETTINGS_NAV --> SET_CH_EDIT["/settings/channel/:id/edit<br/>ChannelEditPage"]
SET_CH --> CH_LIST["Channel List<br/>GET /channels"]
CH_LIST --> CH_ACTIONS["Channel Actions"]
CH_ACTIONS --> CONNECT_WA["Connect WhatsApp<br/>POST /channels/whatsapp<br/>(Embedded Signup Flow)"]
CH_ACTIONS --> CONNECT_IG["Connect Instagram<br/>POST /channels/instagram<br/>(Business Login Flow)"]
CH_ACTIONS --> EDIT_CH["Edit Channel<br/>PUT /channels/:id"]
CH_ACTIONS --> GEN_WEBHOOK["Generate Webhook Token<br/>POST /channels/:id/generate-webhook"]
CH_ACTIONS --> DEL_CH["Delete Channel<br/>DELETE /channels/:id"]
end
subgraph WS_MGMT["WORKSPACE MANAGEMENT [owner only]"]
SETTINGS_NAV --> SET_WS["/settings/workspace<br/>WorkspacePage"]
SETTINGS_NAV --> SET_WS_NEW["/settings/workspace/new<br/>WorkspaceCreatePage"]
SETTINGS_NAV --> SET_WS_EDIT["/settings/workspace/:id/edit<br/>WorkspaceEditPage"]
SET_WS --> WS_LIST["Workspace List<br/>authClient.organization.list()"]
WS_LIST --> WS_ACTIONS["Workspace Actions"]
WS_ACTIONS --> CREATE_WS["Create Workspace<br/>authClient.organization.create()"]
WS_ACTIONS --> EDIT_WS["Edit Workspace<br/>authClient.organization.update()"]
WS_ACTIONS --> DEL_WS["Delete Workspace<br/>authClient.organization.delete()"]
WS_ACTIONS --> SESSION_CFG["Session Time Config<br/>PUT /workspaces/:id/session-time<br/>Options: 5min, 30min, 1hour, 24hour, never"]
end
subgraph ACCOUNT["MY ACCOUNT [all roles]"]
SETTINGS_NAV --> SET_ACCOUNT["/settings/account<br/>AccountGeneralPage"]
SETTINGS_NAV --> SET_PWD["/settings/account/change-password<br/>AccountChangePasswordPage"]
SET_ACCOUNT --> PROFILE["Profile Settings<br/>PUT /user/update-user<br/>(username, fullname, phone,<br/>photo, image_url, status)"]
SET_PWD --> CHANGE_PWD["Change Password<br/>POST /auth/change-password<br/>(currentPassword, newPassword,<br/>revokeOtherSessions)"]
end
subgraph OTHER_SETTINGS["OTHER SETTINGS [admin, owner]"]
SETTINGS_NAV --> SET_TAG["/settings/tag<br/>Tag Management<br/>CRUD /tags"]
SETTINGS_NAV --> SET_OH["/settings/office-hours<br/>Office Hours & Auto Response<br/>CRUD /office-hour-*"]
SETTINGS_NAV --> SET_QM["/settings/quick-message<br/>Quick Reply Templates<br/>CRUD /message-templates"]
SETTINGS_NAV --> SET_API["/settings/api-keys<br/>API Key Management<br/>CRUD /workspace-api-keys"]
SETTINGS_NAV --> SET_RC["/settings/resolve-category<br/>Resolve Categories<br/>CRUD /conversation-resolve-category"]
end
subgraph OFFICE_HOURS_DETAIL["OFFICE HOURS DETAIL"]
SET_OH --> OH_SCHEDULE["Schedule Configuration<br/>Per Channel"]
OH_SCHEDULE --> OH_DAYS["Days: Mon-Sun<br/>Start Time, End Time<br/>Timezone"]
OH_SCHEDULE --> OH_STATUS["useOfficeHoursStatus hook<br/>Auto-check every minute<br/>→ Online / Offline"]
SET_OH --> OH_RESPONSE["Auto Response Configuration<br/>Per Channel"]
OH_RESPONSE --> OH_DURING["During Office Hours<br/>Auto-response message"]
OH_RESPONSE --> OH_OUTSIDE["Outside Office Hours<br/>Auto-response message"]
end
style USER_MGMT fill:#E8F5E9,stroke:#4CAF50
style CHANNEL_MGMT fill:#FFF3E0,stroke:#FF9800
style WS_MGMT fill:#FFEBEE,stroke:#F44336
style ACCOUNT fill:#E3F2FD,stroke:#1976D2
style OTHER_SETTINGS fill:#F3E5F5,stroke:#9C27B0
```
---
## 3.7 FE Data Fetching Architecture (SWR Pattern)
Diagram yang menunjukkan pola data fetching menggunakan SWR di seluruh aplikasi.
```mermaid
flowchart TD
COMPONENT["React Component<br/>(Page / Section)"] --> HOOK["Custom Hook<br/>(use-conversations,<br/>use-messages, etc.)"]
HOOK --> SWR_HOOK["useSWR()<br/>SWR Hook"]
SWR_HOOK --> FETCHER["Axios Fetcher<br/>/src/lib/fetcher.ts"]
FETCHER --> API["API Request<br/>GET /api/v1/u/*"]
API --> RESPONSE{"Response?"}
RESPONSE -->|200 OK| CACHE["SWR Cache<br/>(Key: URL + Params)"]
RESPONSE -->|Error| ERROR_HANDLER["Error Handling"]
ERROR_HANDLER --> SHOW_TOAST["Snackbar Toast<br/>Error Message"]
CACHE --> RENDER["Render Component<br/>with Cached Data"]
RENDER --> IDLE{"Data Stale?"}
IDLE -->|Yes| REVALIDATE["Auto Revalidation<br/>(revalidateOnFocus,<br/>revalidateOnReconnect)"]
IDLE -->|No| WAIT["Wait for Event"]
REVALIDATE --> FETCHER
subgraph RT_EVENTS["Real-Time Triggered Revalidation"]
PUSHER_EV["Pusher Event Received"] --> MUTATE["SWR mutate()<br/>Invalidate specific key"]
SOCKET_EV["Socket.IO Event"] --> MUTATE
MUTATE --> FETCHER
end
subgraph MANUAL["Manual Mutations"]
OPTIMISTIC["Optimistic Update"] --> LOCAL_UPDATE["Update local cache first"]
MANUAL_CALL["Manual API Call<br/>(POST/PUT/DELETE)"] --> MANUAL_MUTATE["mutate() after success"]
MANUAL_CALL -->|"Error"| ROLLBACK["Rollback optimistic update"]
LOCAL_UPDATE --> MANUAL_MUTATE
end
```
# BAGIAN 4: FLOWCHART BACKEND (BE)
## Detailed Backend Application Flowcharts
---
## 4.1 BE General Service Architecture
Arsitektur umum Backend menggunakan pattern **Repository → Service → Route** (Layered Architecture).
```mermaid
flowchart TD
ENTRY["index.ts<br/>Bun HTTP Server<br/>Port: 8000"] --> APP_SVC["app.service.ts<br/>createApp()"]
APP_SVC --> INIT_PUSH["Initialize Pusher"]
APP_SVC --> INIT_RMQ["Initialize RabbitMQ"]
APP_SVC --> DEV_INIT{"Development?"}
DEV_INIT -->|Yes| DB_INIT["Initialize DB<br/>Test Connection"]
subgraph GLOBAL_MW["Global Middleware Stack"]
GM1["CORS (cors.config.ts)<br/>Allowed origins validation"]
GM2["Timing (response time header)"]
GM3["Request ID (unique per request)"]
GM4["Logger (request logging)"]
GM5["Body Limit (5MB default)"]
GM6["Timeout (5min default)"]
GM1 --> GM2 --> GM3 --> GM4 --> GM5 --> GM6
end
APP_SVC --> GLOBAL_MW
subgraph ROUTES["Route Registration (v1.ts)"]
R_AUTH["/api/v1/auth/*<br/>better-auth handler"]
R_WEBHOOK["/api/webhook/*<br/>Webhook receivers"]
R_PROTECTED["/api/v1/u/*<br/>requireAuth middleware"]
R_UPLOADS["/api/uploads<br/>File storage"]
R_SWAGGER["/api/v1/swagger<br/>OpenAPI docs"]
end
GLOBAL_MW --> ROUTES
subgraph AUTH_MW_LAYER["Auth Middleware Chain"]
AM1["requireAuth<br/>(Session verification)"]
AM2["optionalAuth<br/>(No block if missing)"]
AM3["requirePermission<br/>(RBAC check)"]
AM4["requireWorkspaceApiKey<br/>(x-api-key header)"]
AM5["requireAuthOrWorkspaceApiKey<br/>(Either session or API key)"]
end
R_PROTECTED --> AUTH_MW_LAYER
AUTH_MW_LAYER --> SERVICE["Service Layer<br/>(47 Modules)"]
SERVICE --> REPO["Repository Layer<br/>(Drizzle ORM queries)"]
REPO --> DB["PostgreSQL<br/>schema: omnichannel<br/>30+ tables"]
SERVICE --> NOTIFY["Real-time Notification<br/>notifyHybrid()<br/>Socket.IO + Pusher"]
SERVICE --> EXT_API["External APIs<br/>WhatsApp / Instagram / Telegram"]
R_WEBHOOK --> RABBIT["RabbitMQ Publisher"]
RABBIT --> WORKER["Worker Consumer"]
WORKER --> SERVICE
style ENTRY fill:#C8E6C9,stroke:#4CAF50
style GLOBAL_MW fill:#FFF3E0,stroke:#FF9800
style AUTH_MW_LAYER fill:#FFEBEE,stroke:#F44336
style SERVICE fill:#E3F2FD,stroke:#1976D2
style REPO fill:#F3E5F5,stroke:#9C27B0
```
---
## 4.2 BE Auth & RBAC Flow (Detailed)
Alur lengkap autentikasi dan authorization di Backend.
```mermaid
flowchart TD
REQUEST["Incoming Request"] --> ROUTE_TYPE{Route Type?}
subgraph PUBLIC_ROUTES["Public Routes (No Auth)"]
ROUTE_TYPE -->|"/api/v1/swagger"| SWAGGER["Swagger UI"]
ROUTE_TYPE -->|"/api/webhook/whatsapp<br/>(GET verify)"| WH_VERIFY["Webhook Verification"]
end
subgraph BETTER_AUTH["Better-Auth Self-Handled"]
ROUTE_TYPE -->|"/api/v1/auth/*"| BA_HANDLER["better-auth handler"]
BA_HANDLER --> BA_SIGNIN["sign-in/email-password"]
BA_HANDLER --> BA_SIGNUP["sign-up/email-password"]
BA_HANDLER --> BA_FORGET["forget-password"]
BA_HANDLER --> BA_RESET["reset-password"]
BA_HANDLER --> BA_CHANGE["change-password"]
BA_HANDLER --> BA_SESSION["get-session"]
BA_HANDLER --> BA_ADMIN["admin/list-users"]
BA_HANDLER --> BA_CREATE["admin/create-user"]
BA_HANDLER --> BA_UPDATE["admin/update-user"]
BA_HANDLER --> BA_SET_ROLE["admin/set-role"]
BA_HANDLER --> BA_UNBAN["admin/unban-user"]
BA_HANDLER --> BA_ORG_LIST["organization/list-members"]
BA_HANDLER --> BA_ORG_INVITE["organization/invite-member"]
BA_HANDLER --> BA_ORG_UPDATE["organization/update-member-role"]
BA_HANDLER --> BA_ORG_REMOVE["organization/remove-member"]
BA_HANDLER --> BA_ORG_ACCEPT["organization/accept-invitation"]
BA_HANDLER --> BA_APIKEY["api-key/create"]
end
subgraph PROTECTED["Protected Routes (Auth Required)"]
ROUTE_TYPE -->|"/api/v1/u/*"| AUTH_CHECK
AUTH_CHECK["Which Auth Method?"]
AUTH_CHECK -->|"Default"| REQUIRE_AUTH["requireAuth middleware"]
AUTH_CHECK -->|"Tickets"| EITHER["requireAuthOrWorkspaceApiKey"]
AUTH_CHECK -->|"Optional"| OPTIONAL["optionalAuth"]
REQUIRE_AUTH --> SESSION_CHECK{Valid Session<br/>Cookie?}
SESSION_CHECK -->|No| ERR_401["401 Unauthorized<br/>response.util.ts"]
SESSION_CHECK -->|Yes| SET_CTX["Set c.set('user', user)<br/>c.set('session', session)"]
EITHER --> CHECK_EITHER{Session OR<br/>API Key?}
CHECK_EITHER -->|Neither| ERR_401
CHECK_EITHER -->|Session| SET_CTX
CHECK_EITHER -->|API Key| CHECK_API{Valid API Key?<br/>x-api-key header}
CHECK_API -->|No| ERR_401
CHECK_API -->|Yes| SET_API_CTX["Set user + workspace<br/>from key metadata"]
SET_CTX --> RBAC_CHECK
SET_API_CTX --> RBAC_CHECK
RBAC_CHECK["requirePermission({resource: action})"]
RBAC_CHECK --> CHECK_ROLE{User has<br/>permission?}
CHECK_ROLE -->|No| ERR_403["403 Forbidden"]
CHECK_ROLE -->|Yes| HANDLER["Route Handler"]
end
HANDLER --> SVC["Service → Repository → DB"]
SVC --> RESP["Response<br/>response.util.ts<br/>success / error / paginated"]
SVC --> NOTIFY["notifyHybrid()<br/>Pusher + Socket.IO"]
style PUBLIC_ROUTES fill:#E8F5E9,stroke:#4CAF50
style BETTER_AUTH fill:#FFF3E0,stroke:#FF9800
style PROTECTED fill:#FFEBEE,stroke:#F44336
```
---
## 4.3 BE Conversation & Message Flow (Chat Engine)
Alur lengkap mesin chat Backend, baik inbound maupun outbound.
```mermaid
flowchart TD
subgraph INBOUND["INBOUND MESSAGE FLOW"]
WEBHOOK["Webhook Received"] --> PARSE["Parse Platform Data"]
PARSE --> EXTRACT["Extract:<br/>sender_phone, content, type,<br/>channel_code, raw_id"]
EXTRACT --> PROFANITY{"Profanity<br/>Filter?"}
PROFANITY -->|Flagged| MARK_FLAGGED["Mark as flagged"]
PROFANITY -->|Clean| FIND_CONTACT
MARK_FLAGGED --> FIND_CONTACT{Contact<br/>Exists?<br/>MATCH by phone + channel_id}
FIND_CONTACT -->|No| CREATE_CONTACT["Create Contact<br/>INSERT contacts<br/>contact_name=phone<br/>contact_phone=phone"]
FIND_CONTACT -->|Yes| GET_CONTACT["SELECT contact<br/>WHERE phone + channel_id"]
CREATE_CONTACT --> FIND_CONV
GET_CONTACT --> FIND_CONV
FIND_CONV{Active Conversation?<br/>MATCH by contact_id<br/>+ channel_id<br/>+ status NOT CLOSED/RESOLVED}
FIND_CONV -->|No| CREATE_CONV["INSERT conversations<br/>status: UNASSIGNED<br/>unread_counts: 1"]
FIND_CONV -->|Yes Chain| UPDATE_CHAIN["Link previous_conversation_id"]
FIND_CONV -->|Yes Active| UPDATE_CONV
UPDATE_CHAIN --> UPDATE_CONV["UPDATE conversations<br/>last_message, last_message_at<br/>unread_counts++<br/>conversation_24h_window<br/>(WhatsApp only)"]
UPDATE_CONV --> SAVE_MSG["INSERT conversation_messages<br/>from=customer, to=system<br/>type based on content"]
SAVE_MSG --> SAVE_ATTACH{"Has<br/>Attachments?"}
SAVE_ATTACH -->|Yes| SAVE_ATT["INSERT message_attachments<br/>Download media from<br/>platform API"]
SAVE_ATTACH -->|No| CHECK_OH
SAVE_ATT --> CHECK_OH
CHECK_OH{"Within<br/>Office Hours?<br/>office-hours-schedule"}
CHECK_OH -->|No| AUTO_RESP["Send Auto Response<br/>office-hours-auto-responses<br/>→ Platform Send API"]
CHECK_OH -->|Yes| NOTIFY
AUTO_RESP --> NOTIFY["notifyHybrid()"]
NOTIFY --> P1["Pusher: private-room:{convId}<br/>Event: new-message"]
NOTIFY --> P2["Pusher: private-channel:{chId}<br/>Event: new-conversation<br/>(if new)"]
NOTIFY --> S1["Socket.IO: emit new-message"]
end
subgraph OUTBOUND["OUTBOUND MESSAGE FLOW"]
SEND_API["POST /conversation-messages<br/>(Agent sends message)"] --> VALIDATE["Validate with Zod<br/>conversation-message.validation.ts"]
VALIDATE --> CHECK_TYPE{Message Type?}
CHECK_TYPE -->|"TEXT"| PREPARE_TEXT["Prepare text content"]
CHECK_TYPE -->|"IMAGE/VIDEO/AUDIO/DOC"| UPLOAD_MEDIA["Check media<br/>Already uploaded or new"]
CHECK_TYPE -->|"TEMPLATE"| PREPARE_TEMPL["Prepare template<br/>components + variables"]
PREPARE_TEXT --> PROFANITY_OUT{"Profanity?"}
UPLOAD_MEDIA --> PROFANITY_OUT
PREPARE_TEMPL --> SKIP_PROFANE
PROFANITY_OUT -->|Flagged| REJECT["400 Bad Request<br/>'Message contains inappropriate content'"]
PROFANITY_OUT -->|Clean| CHECK_WINDOW
SKIP_PROFANE --> CHECK_WINDOW{"WhatsApp 24h<br/>Window Active?<br/>(if WhatsApp channel)"}
CHECK_WINDOW -->|Expired| ERR_WINDOW["Error:<br/>'24-hour messaging window expired'"]
CHECK_WINDOW -->|Active or Non-WA| SEND_PLATFORM
SEND_PLATFORM["Send to Platform API"]
SEND_PLATFORM --> WA_API["WhatsApp API<br/>POST /messages"]
SEND_PLATFORM --> IG_API["Instagram API<br/>POST /messages"]
SEND_PLATFORM --> TG_API["Telegram API<br/>sendMessage"]
WA_API --> SAVE_OUT["INSERT conversation_messages<br/>from=agent, to=customer<br/>status: SENT"]
IG_API --> SAVE_OUT
TG_API --> SAVE_OUT
SAVE_OUT --> UPDATE_CONV_OUT["UPDATE conversations<br/>last_message, last_message_at"]
UPDATE_CONV_OUT --> NOTIFY_OUT["notifyHybrid()<br/>private-room:{convId}<br/>Event: new-message"]
end
subgraph DELIVERY["DELIVERY STATUS WEBHOOK"]
DELIVERY_WB["Platform Delivery Webhook"] --> FIND_MSG{"Find Message<br/>by platform_msg_id"}
FIND_MSG --> UPDATE_STATUS["UPDATE conversation_messages<br/>status: DELIVERED / READ / FAILED"]
UPDATE_STATUS --> NOTIFY_DELIVERY["notifyHybrid()<br/>Event: message-updated"]
end
style INBOUND fill:#E8F5E9,stroke:#4CAF50
style OUTBOUND fill:#FFF3E0,stroke:#FF9800
style DELIVERY fill:#E3F2FD,stroke:#1976D2
```
---
## 4.4 BE Conversation Status Flow
Alur perubahan status conversation.
```mermaid
stateDiagram-v2
[*] --> UNASSIGNED: Customer sends first message
UNASSIGNED --> ASSIGNED: Agent claims chat<br/>(POST /get-new-chat)
UNASSIGNED --> ASSIGNED: Admin assigns agent<br/>(PATCH /status: assignedTo)
ASSIGNED --> UNASSIGNED: Agent unassigns<br/>(PATCH /status: assignedTo=null)
ASSIGNED --> RESOLVED: Agent resolves<br/>(PATCH /status + resolveCategoryIds)
ASSIGNED --> ASSIGNED: Agent sends message<br/>(message flow continues)
RESOLVED --> ASSIGNED: Customer replies<br/>(new message in chain)
RESOLVED --> CLOSED: Admin closes<br/>(PATCH /status: CLOSED)
CLOSED --> ASSIGNED: Customer replies<br/>(new conversation created,<br/>previous_conversation_id linked)
UNASSIGNED: Unread count tracked
ASSIGNED: first_response_at set
ASSIGNED: first_response_by set
RESOLVED: resolved_at set
RESOLVED: resolved_by set
RESOLVED: resolve categories linked
CLOSED: conversation ended
```
---
## 4.5 BE Campaign Execution Flow (Detailed)
Alur lengkap eksekusi campaign di Backend.
```mermaid
flowchart TD
TRIGGER["POST /campaigns/:id/execute"] --> LOAD["Load Campaign<br/>+ Template + Channel + Recipient List"]
LOAD --> STATUS_CHECK{Campaign Status?}
STATUS_CHECK -->|"DRAFT"| SET_RUNNING["UPDATE campaigns<br/>status: RUNNING<br/>started_at: NOW()"]
STATUS_CHECK -->|"SCHEDULED"| CHECK_SCHEDULE{Scheduled time<br/>reached?}
STATUS_CHECK -->|"RUNNING"| ALREADY["Return: Already running"]
STATUS_CHECK -->|"COMPLETED/CANCELLED"| ERR["Return Error"]
CHECK_SCHEDULE -->|"Not yet"| SCHEDULE_WAIT["Wait for cron/scheduler"]
CHECK_SCHEDULE -->|"Yes"| SET_RUNNING
SET_RUNNING --> LOAD_TEMPLATE["Load Template<br/>templates table<br/>template_components (JSON)"]
LOAD_TEMPLATE --> LOAD_LIST["Load Recipient List Items<br/>campaign_recipient_list_items<br/>+ contacts"]
LOAD_LIST --> INIT_COUNTS["Reset campaign counts:<br/>total_recipients, sent, delivered,<br/>read, failed = 0"]
INIT_COUNTS --> LOOP_START["Start Loop"]
subgraph LOOP["PER RECIPIENT LOOP"]
LOOP_START --> GET_NEXT{Next<br/>Recipient?}
GET_NEXT -->|No| COMPLETE
GET_NEXT -->|Yes| RENDER["Render Template<br/>Replace {{1}}, {{2}}...<br/>with recipient variables"]
RENDER --> FIND_CONV{Has Active<br/>Conversation?<br/>contact_id + channel_id}
FIND_CONV -->|No| CREATE_CONV["INSERT conversations<br/>status: UNASSIGNED"]
FIND_CONV -->|Yes| USE_CONV["Use existing conversation"]
CREATE_CONV --> SEND_WA
USE_CONV --> SEND_WA
SEND_WA["Send via WhatsApp API<br/>POST /{phone_id}/messages<br/>template type message"]
SEND_WA --> WA_RESPONSE{WhatsApp<br/>API Response?}
WA_RESPONSE -->|Success| RECORD_SENT["INSERT campaign_recipients<br/>status: SENT<br/>conversation_id, message_id"]
WA_RESPONSE -->|Rate Limit| WAIT_RETRY["Wait + Retry<br/>(Exponential backoff)"]
WA_RESPONSE -->|Error| RECORD_FAIL["INSERT campaign_recipients<br/>status: FAILED"]
RECORD_SENT --> INC_SENT["UPDATE campaigns<br/>sent++"]
RECORD_FAIL --> INC_FAIL["UPDATE campaigns<br/>failed++"]
INC_SENT --> NEXT["Continue Loop"]
INC_FAIL --> NEXT
WAIT_RETRY --> SEND_WA
NEXT --> GET_NEXT
end
COMPLETE["All Recipients Processed"] --> SET_COMPLETE["UPDATE campaigns<br/>status: COMPLETED<br/>completed_at: NOW()"]
subgraph DELIVERY["DELIVERY WEBHOOKS"]
WA_DELIVERY["WhatsApp Delivery Callback"] --> FIND_RECIP{Find campaign_recipient<br/>by conversation_message_id}
FIND_RECIP --> UPDATE_RECIP["UPDATE campaign_recipients<br/>status: DELIVERED / READ / FAILED"]
UPDATE_RECIP --> UPDATE_CAMP_COUNTS["UPDATE campaigns<br/>delivered++ / read++ / failed++"]
UPDATE_CAMP_COUNTS --> NOTIFY_STATS["Optional: Push notification<br/>to campaign dashboard"]
end
style LOOP fill:#E8F5E9,stroke:#4CAF50
style DELIVERY fill:#FFF3E0,stroke:#FF9800
```
---
## 4.6 BE Ticket Lifecycle Flow (Detailed)
Alur lengkap lifecycle ticket di Backend.
```mermaid
flowchart TD
CREATE["POST /tickets<br/>Create Ticket"] --> VALIDATE_T["Validate with Zod<br/>ticket.validation.ts"]
VALIDATE_T --> INSERT_TICKET["INSERT tickets<br/>ticket_number: auto-generated<br/>status: TODO"]
INSERT_TICKET --> INSERT_ATTACH{"Has<br/>Attachments?"}
INSERT_ATTACH -->|Yes| INSERT_ATT["INSERT ticket_attachments<br/>(link to ticket_id)"]
INSERT_ATTACH -->|No| DONE_CREATE
INSERT_ATT --> DONE_CREATE["Ticket Created<br/>201 Created Response"]
subgraph LIFECYCLE["TICKET LIFECYCLE"]
DONE_CREATE --> TODO_ST["Status: TODO"]
TODO_ST -->|"Assign to Agent<br/>PUT /tickets/:id<br/>assignedTo, assignedBy"| IN_PROGRESS["Status: IN_PROGRESS"]
TODO_ST -->|"Soft Delete<br/>DELETE /tickets/:id"| DELETED["Status: DELETED<br/>(deleted_at IS NOT NULL)"]
IN_PROGRESS -->|"Reassign<br/>assignedTo changed"| IN_PROGRESS
IN_PROGRESS -->|"Resolve<br/>status: RESOLVED<br/>resolvedBy, resolvedAt<br/>resolvedNote"| RESOLVED["Status: RESOLVED"]
IN_PROGRESS -->|"Reject<br/>status: REJECTED<br/>rejectedBy, rejectedAt<br/>rejectReason"| REJECTED["Status: REJECTED"]
RESOLVED -->|"Reopen<br/>status back to<br/>IN_PROGRESS"| IN_PROGRESS
RESOLVED -->|"Mark Done<br/>status: DONE<br/>closedBy, closedAt"| DONE["Status: DONE"]
REJECTED --> END_STATE["Terminal State"]
DONE --> END_STATE
DELETED --> END_STATE
end
subgraph HISTORY["STATUS HISTORY TRACKING"]
STATUS_CHANGE["Any Status Change"] --> RECORD_HIST["INSERT ticket_status_history<br/>ticket_id, old_status, new_status<br/>updated_by, assigned_to<br/>resolved_by, rejected_by"]
RECORD_HIST --> NOTIFY_TICKET["notifyHybrid()<br/>(if real-time needed)"]
end
LIFECYCLE --> HISTORY
subgraph BATCH["BATCH OPERATIONS"]
BATCH_ASSIGN["Batch Assign<br/>(FE loops PUT calls)"] --> LOOP_PUT1["PUT /tickets/:id<br/>assignedTo: userId<br/>× N tickets"]
BATCH_STATUS["Batch Update Status<br/>(FE loops PUT calls)"] --> LOOP_PUT2["PUT /tickets/:id<br/>status: IN_PROGRESS<br/>× N tickets"]
end
subgraph FROM_CHAT["TICKET FROM CONVERSATION"]
CHAT_LINK["conversation_id provided"] --> VERIFY_CONV{"Conversation<br/>Exists?"}
VERIFY_CONV -->|Yes| LINK_CONV["Set ticket.conversation_id"]
VERIFY_CONV -->|No| ERR_CONV["Error: Conversation not found"]
CONTACT_LINK["contact_id provided"] --> VERIFY_CONTACT{"Contact<br/>Exists?"}
VERIFY_CONTACT -->|Yes| LINK_CONTACT["Set ticket.contact_id"]
VERIFY_CONTACT -->|No| ERR_CONTACT["Error: Contact not found"]
end
style LIFECYCLE fill:#E8F5E9,stroke:#4CAF50
style HISTORY fill:#FFF3E0,stroke:#FF9800
style BATCH fill:#E3F2FD,stroke:#1976D2
style FROM_CHAT fill:#F3E5F5,stroke:#9C27B0
```
---
## 4.7 BE Webhook Processing Architecture
Arsitektur pemrosesan webhook dari external channels.
```mermaid
flowchart TD
subgraph INCOMING["WEBHOOK RECEIVERS"]
WH_WA_GET["GET /api/webhook/whatsapp<br/>Verification<br/>(hub.mode, hub.verify_token,<br/>hub.challenge)"]
WH_WA_POST["POST /api/webhook/whatsapp<br/>Receive messages<br/>(WhatsApp payload)"]
WH_IG["POST /api/webhook/instagram<br/>Receive DMs + Comments<br/>(Instagram payload)"]
WH_TG["POST /api/webhook/telegram<br/>Receive messages<br/>(Telegram payload)"]
end
subgraph WHATSAPP_FLOW["WHATSAPP FLOW"]
WH_WA_GET --> VERIFY{Verify Token<br/>Matches?}
VERIFY -->|Yes| RETURN_CHALLENGE["Return hub.challenge<br/>200 OK"]
VERIFY -->|No| ERR_403["403 Forbidden"]
WH_WA_POST --> VERIFY_WA_HUB{"Hub Mode = 'subscribe'?"}
%% Perbaikan Baris 15 & 16: Dipisah ke baris baru
VERIFY_WA_HUB -->|Yes| RETURN_200["200 OK No processing"]
VERIFY_WA_HUB -->|No| PARSE_WA["Parse WhatsApp Payload<br/>Extract: entry.changes<br/>messages, statuses"]
PARSE_WA --> MSG_TYPE{Message Type?}
MSG_TYPE -->|"messages"| TO_QUEUE["Publish to RabbitMQ<br/>Queue: omnichannel_work_queue<br/>Route: whatsapp"]
MSG_TYPE -->|"statuses"| PROCESS_STATUS["Process Delivery Status<br/>UPDATE conversation_messages<br/>status: SENT → DELIVERED → READ"]
TO_QUEUE --> RABBIT_MQ["RabbitMQ<br/>amqplib connection"]
end
subgraph RABBIT_WORKER["RABBITMQ WORKER"]
RABBIT_MQ --> CONSUME["Consume from Queue"]
CONSUME --> ACK{"Process<br/>Success?"}
ACK -->|Yes| ACK_MSG["ack message"]
ACK -->|No| NACK["nack + requeue<br/>(or dead-letter)"]
NACK --> CONSUME
end
subgraph DIRECT_PROCESSING["DIRECT PROCESSING<br/>(Instagram, Telegram)"]
WH_IG --> PARSE_IG["Parse Instagram Payload"]
PARSE_IG --> IG_TYPE{Event Type?}
%% Perbaikan: Menghapus karakter asteris (*) yang bisa mengganggu parser
IG_TYPE -->|messages| INSTA_DM["Process Instagram DM<br/>→ conversation engine"]
IG_TYPE -->|comments| INSTA_COMMENT["Process Comment<br/>→ conversation engine<br/>context: POST_COMMENT"]
WH_TG --> PARSE_TG["Parse Telegram Payload"]
PARSE_TG --> TG_PROCESS["Process Telegram Message<br/>→ conversation engine"]
end
subgraph SHARED_PROCESSOR["SHARED MESSAGE PROCESSOR"]
TO_QUEUE --> PROCESSOR["Message Processor Service"]
INSTA_DM --> PROCESSOR
INSTA_COMMENT --> PROCESSOR
TG_PROCESS --> PROCESSOR
PROCESSOR --> CONTACT_RESOLVE["Resolve/Create Contact"]
PROCESSOR --> CONV_RESOLVE["Resolve/Create Conversation"]
PROCESSOR --> SAVE_MESSAGE["Save Message + Attachments"]
PROCESSOR --> CHECK_AUTO_RESP["Check Auto Response"]
PROCESSOR --> NOTIFY_RT["Real-time Notification"]
end
PROCESS_STATUS --> DB_UPDATE["Direct DB Update"]
DB_UPDATE --> DB[(PostgreSQL)]
SHARED_PROCESSOR --> DB
style INCOMING fill:#E8F5E9,stroke:#4CAF50
style WHATSAPP_FLOW fill:#FFF3E0,stroke:#FF9800
style RABBIT_WORKER fill:#FFEBEE,stroke:#F44336
style DIRECT_PROCESSING fill:#E3F2FD,stroke:#1976D2
style SHARED_PROCESSOR fill:#F3E5F5,stroke:#9C27B0
```
---
## 4.8 BE Service Module Pattern
Pattern yang digunakan di setiap service module Backend.
```mermaid
flowchart TD
subgraph MODULE["Service Module Structure"]
direction TB
ROUTE["*.route.ts<br/>───────────────<br/>Define Hono routes<br/>Apply middleware<br/>Call service functions<br/>Return responses"]
SVC["*.service.ts<br/>───────────────<br/>Business logic<br/>Orchestrate repos<br/>Handle errors<br/>Trigger notifications"]
REPO["*.repository.ts<br/>───────────────<br/>Drizzle ORM queries<br/>INSERT / SELECT<br/>UPDATE / DELETE<br/>Transactions"]
VALID["*.validation.ts<br/>───────────────<br/>Zod schemas<br/>Input validation<br/>Output parsing"]
ERR["*.error.ts<br/>───────────────<br/>Custom error classes<br/>Error codes<br/>Error messages"]
TYPE["*.type.ts<br/>───────────────<br/>TypeScript types<br/>Interfaces<br/>Enums"]
end
ROUTE -->|"Validate input"| VALID
ROUTE -->|"Call business logic"| SVC
SVC -->|"Query database"| REPO
SVC -->|"Throw errors"| ERR
REPO -->|"Read types"| TYPE
ROUTE -->|"Read types"| TYPE
SVC -->|"Read types"| TYPE
subgraph EXAMPLE["Example: conversation-message"]
EX_ROUTE["conversation-message.route.ts<br/>GET / → listMessages()<br/>POST / → sendMessage()<br/>POST /template → sendTemplate()"]
EX_SVC["conversation-message.service.ts<br/>sendMessage():<br/> 1. Validate profanity<br/> 2. Check 24h window<br/> 3. Send to platform API<br/> 4. Save to DB<br/> 5. Notify real-time"]
EX_REPO["conversation-message.repository.ts<br/>findById()<br/>findByConversationId()<br/>create()<br/>updateStatus()"]
EX_VALID["conversation-message.validation.ts<br/>sendMessageSchema<br/>sendTemplateSchema<br/>listMessagesSchema"]
end
EX_ROUTE --> EX_SVC
EX_SVC --> EX_REPO
EX_ROUTE --> EX_VALID
style MODULE fill:#F5F5F5,stroke:#9E9E9E
style EXAMPLE fill:#E3F2FD,stroke:#1976D2
```
---
## 4.9 BE Error Handling Flow
```mermaid
flowchart TD
REQUEST["Request Handler"] --> TRY["try { ... }"]
TRY --> HANDLER["Business Logic"]
HANDLER --> SUCCESS{Success?}
SUCCESS -->|Yes| RETURN_OK["response.util.success(data)<br/>200 OK"]
SUCCESS -->|No| ERROR_CATCH["catch (error)"]
ERROR_CATCH --> ERROR_TYPE{Error Type?}
ERROR_TYPE -->|"AppError"| APP_ERR["Custom App Error<br/>status + message + code"]
ERROR_TYPE -->|"HTTPException"| HTTP_ERR["Hono HTTP Exception"]
ERROR_TYPE -->|"DrizzleQueryError"| DB_ERR["Database Query Error"]
ERROR_TYPE -->|"PostgresError"| PG_ERR["PostgreSQL Error<br/>(unique violation, fk, etc.)"]
ERROR_TYPE -->|"ZodError"| ZOD_ERR["Validation Error<br/>Input format invalid"]
ERROR_TYPE -->|"Unknown"| UNKNOWN_ERR["Unknown Error"]
APP_ERR --> MAP_STATUS["Map to HTTP Status"]
HTTP_ERR --> MAP_STATUS
DB_ERR --> LOG_ERR["Log Error<br/>console.error"]
PG_ERR --> LOG_ERR
ZOD_ERR --> MAP_400["400 Bad Request<br/>Validation error details"]
UNKNOWN_ERR --> LOG_ERR
LOG_ERR --> MAP_500["500 Internal Server Error"]
MAP_STATUS --> RESPONSE["Return Error Response<br/>response.util.error()"]
MAP_400 --> RESPONSE
MAP_500 --> RESPONSE
style REQUEST fill:#C8E6C9,stroke:#4CAF50
style ERROR_CATCH fill:#FFCDD2,stroke:#F44336
style RESPONSE fill:#BBDEFB,stroke:#2196F3
```
# BAGIAN 5: RINGKASAN & TABEL REFERENSI
## Summary Tables & Database Schema
---
## 5.1 Ringkasan 5 Fitur Utama
| # | Fitur | Deskripsi Singkat | Kompleksitas |
|---|-------|-------------------|-------------|
| 1 | **Auth** | Autentikasi user dengan Better-Auth, session management, RBAC 3 role (owner/admin/member), forgot/reset password, API key authentication | Tinggi |
| 2 | **Chat** | Real-time omnichannel messaging (WhatsApp/Instagram/Telegram), conversation management (assign/resolve), message templates, file attachments, internal notes, office hours auto-response, profanity filter | Sangat Tinggi |
| 3 | **Campaign** | WhatsApp marketing campaign builder, template management (Meta Resumable API), recipient list dengan variable personalization, batch execution, delivery tracking | Tinggi |
| 4 | **Ticket** | Issue tracking terhubung dengan conversation/contact, full lifecycle (TODO → IN_PROGRESS → RESOLVED → DONE/REJECTED), attachment, status history, batch operations | Sedang |
| 5 | **Settings** | Konfigurasi workspace: user management, channel connection (WhatsApp Embedded Signup, Instagram Business Login), office hours, quick reply, API keys, tags, resolve categories, role permissions | Tinggi |
---
## 5.2 Technology Stack Comparison
| Layer | Frontend (FE) | Backend (BE) |
|-------|--------------|--------------|
| **Runtime** | Browser + Vite Dev Server | Bun (HTTP Server) |
| **Framework** | React 19.2 | Hono 4.10 |
| **Language** | TypeScript 5.9 | TypeScript |
| **UI Library** | MUI v7.3, Emotion | — |
| **State/Data** | SWR 2.3, React Context | — |
| **HTTP Client** | Axios 1.11 | Hono built-in |
| **Validation** | Zod 4.0 + @hookform/resolvers | Zod 4.1 + drizzle-zod |
| **Forms** | React Hook Form 7.62 | — |
| **ORM** | — | Drizzle ORM 0.44 |
| **Database** | — | PostgreSQL (pg driver) |
| **Auth** | better-auth client 1.3 | better-auth server 1.3 |
| **Real-time** | Pusher-js 8.4, Socket.io-client 4.8, react-use-websocket 4.13 | Pusher 5.2, Socket.IO 4.8 |
| **Message Queue** | — | RabbitMQ (amqplib 0.10) |
| **Routing** | react-router v7.8 | Hono router |
| **Rich Text** | TipTap v3, Quill 2.0 | — |
| **Charts** | ApexCharts 5.3 | — |
| **i18n** | i18next 25.3 | — |
| **Animations** | Framer Motion 12.23 | — |
| **PDF** | @react-pdf/renderer 4.3 | — |
| **Package Manager** | Yarn 4.12 | Bun |
| **Build Tool** | Vite 7.1 | Bun |
| **Process Manager** | PM2 (pm2.config.cjs) | PM2 (pm2.config.js) |
| **Container** | Docker | Docker |
| **CI/CD** | GitLab CI | GitLab CI |
---
## 5.3 Role-Based Access Control Matrix
| Menu / Fitur | owner | admin | member | Endpoint Pattern |
|---|:---:|:---:|:---:|---|
| **Login / Register** | ✓ | ✓ | ✓ | `/api/v1/auth/*` |
| **Dashboard — Chat** | ✓ | ✓ | ✓ | `/api/v1/u/conversations/*` |
| **Dashboard — Social Media** | ✓ | ✓ | ✓ | `/api/v1/u/posts/*` |
| **Dashboard — Campaign** | ✓ | ✓ | ✓ | `/api/v1/u/campaigns/*` |
| **Dashboard — Contact** | ✓ | ✓ | ✓ | `/api/v1/u/contacts/*` |
| **Dashboard — Tickets** | ✓ | ✓ | ✓ | `/api/v1/u/tickets/*` |
| **Settings — User Management** | ✓ | ✓ | ✗ | `/api/v1/auth/admin/*` |
| **Settings — Role & Permission** | ✓ | ✓ | ✗ | `/api/v1/u/role-permissions/*` |
| **Settings — Channel Management** | ✓ | ✓ | ✗ | `/api/v1/u/channels/*` |
| **Settings — Workspace Management** | ✓ | ✗ | ✗ | `/api/v1/u/workspaces/*` |
| **Settings — Workspace Members** | ✓ | ✓ | ✗ | `/api/v1/u/workspace-members/*` |
| **Settings — Tags** | ✓ | ✓ | ✗ | `/api/v1/u/tags/*` |
| **Settings — Office Hours** | ✓ | ✓ | ✗ | `/api/v1/u/office-hour-*` |
| **Settings — Quick Reply** | ✓ | ✓ | ✗ | `/api/v1/u/message-templates/*` |
| **Settings — API Keys** | ✓ | ✓ | ✗ | `/api/v1/u/workspace-api-keys/*` |
| **Settings — Resolve Category** | ✓ | ✓ | ✗ | `/api/v1/u/conversation-resolve-category/*` |
| **Settings — My Account** | ✓ | ✓ | ✓ | `/api/v1/u/user/*` |
| **Settings — Change Password** | ✓ | ✓ | ✓ | `/api/v1/auth/change-password` |
---
## 5.4 Complete API Endpoint Catalog
### Auth Endpoints (`/api/v1/auth/*`)
| Method | Endpoint | Auth | Deskripsi |
|--------|----------|------|-----------|
| POST | `/auth/sign-in/email-password` | None | Login |
| POST | `/auth/sign-up/email-password` | None | Register (disabled) |
| POST | `/auth/forget-password` | None | Lupa password |
| POST | `/auth/reset-password` | None | Reset password |
| POST | `/auth/change-password` | Session | Ganti password |
| GET | `/auth/get-session` | Session | Cek session |
| GET | `/auth/admin/list-users` | Session + Admin | List semua user |
| GET | `/auth/admin/get-user` | Session + Admin | Detail user |
| POST | `/auth/admin/create-user` | Session + Admin | Buat user |
| POST | `/auth/admin/update-user` | Session + Admin | Update user |
| POST | `/auth/admin/set-role` | Session + Admin | Set role user |
| POST | `/auth/admin/unban-user` | Session + Admin | Unban user |
| GET | `/auth/organization/list-members` | Session | List member org |
| POST | `/auth/organization/invite-member` | Session + Admin | Invite member |
| POST | `/auth/organization/update-member-role` | Session + Admin | Update role member |
| POST | `/auth/organization/remove-member` | Session + Admin | Hapus member |
| POST | `/auth/organization/accept-invitation` | Session | Terima invitation |
| POST | `/auth/api-key/create` | Session + Admin | Buat API key |
### Channel Endpoints (`/api/v1/u/channels`)
| Method | Endpoint | Permission | Deskripsi |
|--------|----------|-----------|-----------|
| GET | `/channels` | channel:read | List channels |
| GET | `/channels/with-unread-count` | channel:read | Channels + unread count |
| GET | `/channels/:id` | channel:read | Detail channel |
| POST | `/channels` | channel:create | Buat channel |
| PUT | `/channels/:id` | channel:update | Update channel |
| DELETE | `/channels/:id` | channel:delete | Hapus channel |
| POST | `/channels/:id/generate-webhook` | channel:update | Generate webhook token |
| POST | `/channels/whatsapp` | channel:create | Connect WhatsApp |
| GET | `/channels/whatsapp/get-access-token` | channel:read | Get WA access token |
| POST | `/channels/instagram` | channel:create | Connect Instagram |
| POST | `/channels/:id/upload-icon` | channel:update | Upload icon |
### Conversation Endpoints (`/api/v1/u/conversations`)
| Method | Endpoint | Permission | Deskripsi |
|--------|----------|-----------|-----------|
| GET | `/conversations` | conversation:read | List conversations |
| GET | `/conversations/total-counts-chat` | conversation:read | Total counts |
| GET | `/conversations/:id` | conversation:read | Detail conversation |
| GET | `/conversations/:id/history` | conversation:read | History chain |
| PATCH | `/conversations/:id/status` | conversation:update_status | Update status |
| PUT | `/conversations/:id/history` | — | Update resolve categories |
| POST | `/conversations/:id/read` | conversation:read | Mark as read |
| POST | `/conversations/get-new-chat` | conversation:update_status | Claim next chat |
| POST | `/conversations/bulk-resolve` | — | Bulk resolve |
### Message Endpoints (`/api/v1/u/conversation-messages`)
| Method | Endpoint | Permission | Deskripsi |
|--------|----------|-----------|-----------|
| GET | `/conversation-messages` | message:read | List messages |
| POST | `/conversation-messages` | message:create | Kirim pesan |
| POST | `/conversation-messages/template` | message:create | Kirim template |
| PUT | `/conversation-messages/:id/internal-note` | message:internal_note_update | Update note |
| DELETE | `/conversation-messages/:id/internal-note` | message:internal_note_update | Hapus note |
| POST | `/conversation-messages/upload-media` | message:create | Upload media |
| DELETE | `/conversation-messages/:id` | message:create | Hapus pesan |
### Campaign Endpoints (`/api/v1/u/campaigns`)
| Method | Endpoint | Permission | Deskripsi |
|--------|----------|-----------|-----------|
| GET | `/campaigns` | — | List campaigns |
| GET | `/campaigns/:id` | — | Detail campaign |
| POST | `/campaigns` | — | Buat campaign |
| PUT | `/campaigns/:id` | — | Update campaign |
| DELETE | `/campaigns/:id` | — | Hapus campaign |
| POST | `/campaigns/:id/execute` | — | Eksekusi campaign |
### Template Endpoints (`/api/v1/u/templates`)
| Method | Endpoint | Permission | Deskripsi |
|--------|----------|-----------|-----------|
| GET | `/templates` | template:read | List templates |
| GET | `/templates/:id` | template:read | Detail template |
| POST | `/templates` | template:create | Buat template |
| POST | `/templates/ticket` | template:create | Buat ticket template |
| PUT | `/templates/:id` | template:update | Update template |
| DELETE | `/templates/:id` | template:delete | Hapus template |
| POST | `/templates/upload-media` | template:create | Upload media (Meta API) |
| POST | `/templates/:id/sync` | template:create | Sync ke WhatsApp |
### Ticket Endpoints (`/api/v1/u/tickets`)
| Method | Endpoint | Permission | Deskripsi |
|--------|----------|-----------|-----------|
| GET | `/tickets` | ticket:read | List tickets |
| GET | `/tickets/:id` | ticket:read | Detail ticket |
| POST | `/tickets` | ticket:create | Buat ticket |
| PUT | `/tickets/:id` | ticket:update | Update ticket |
| DELETE | `/tickets/:id` | ticket:delete | Hapus ticket |
| POST | `/tickets/upload-media` | ticket:create | Upload media |
| DELETE | `/tickets/remove-media/:id` | — | Hapus media |
### Recipient List Endpoints
| Method | Endpoint | Deskripsi |
|--------|----------|-----------|
| GET | `/campaign-recipient-lists` | List recipient lists |
| GET | `/campaign-recipient-lists/:id` | Detail recipient list |
| POST | `/campaign-recipient-lists` | Buat recipient list |
| PUT | `/campaign-recipient-lists/:id` | Update recipient list |
| DELETE | `/campaign-recipient-lists/:id` | Hapus recipient list |
| GET | `/campaign-recipient-list-items` | List items |
| POST | `/campaign-recipient-lists/:id/contacts` | Tambah kontak |
| DELETE | `/campaign-recipient-lists/:id/contacts` | Hapus kontak |
---
## 5.5 Database Schema — Entity Relationship Diagram
```mermaid
erDiagram
users {
uuid user_id PK
varchar user_email "NOT NULL, INDEXED"
varchar user_username "NOT NULL, INDEXED"
varchar user_fullname
varchar user_phone
boolean user_is_email_verified "default: false"
varchar user_image_url
varchar user_role
boolean user_is_banned "default: false"
text user_ban_reason
timestamp user_ban_expires
varchar user_status "OFFLINE / ONLINE"
timestamp created_at
timestamp updated_at
timestamp deleted_at
uuid created_by "FK → users"
uuid updated_by "FK → users"
uuid deleted_by "FK → users"
}
workspaces {
uuid workspace_id PK
varchar workspace_name "NOT NULL"
varchar workspace_slug "UNIQUE"
varchar workspace_logo
integer session_time "default: 604800 (7 days)"
timestamp created_at
timestamp updated_at
timestamp deleted_at
uuid created_by "FK → users"
uuid updated_by "FK → users"
uuid deleted_by "FK → users"
}
workspace_members {
uuid workspace_member_id PK
uuid workspace_id FK "FK → workspaces, CASCADE"
uuid user_id FK "FK → users, CASCADE"
text workspace_member_role "default: member"
varchar workspace_member_status "ACTIVE / INACTIVE / SUSPENDED"
boolean is_developer "default: false"
boolean is_devops "default: false"
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
workspace_invitations {
uuid workspace_invitation_id PK
uuid workspace_id FK "FK → workspaces"
varchar workspace_invitation_email
varchar workspace_invitation_role
varchar workspace_invitation_status "default: pending"
boolean is_developer
boolean is_devops
timestamp expires_at
}
sessions {
uuid session_id PK
varchar session_token "UNIQUE"
uuid session_active_workspace_id
uuid user_id FK "FK → users, CASCADE"
timestamp expires_at
}
api_keys {
uuid id PK
text name
text start
text prefix
text key
uuid user_id FK "FK → users"
text permissions "JSON string"
jsonb metadata "workspaceId, appName"
boolean rate_limit_enabled
integer rate_limit_time_window
integer rate_limit_max
}
channels {
uuid channel_id PK
varchar channel_name "NOT NULL"
varchar channel_code "WHATSAPP / INSTAGRAM / TELEGRAM / etc."
varchar channel_icon
varchar channel_platform_id
varchar channel_waba_id
text channel_api_token
varchar channel_api_webhook_token
varchar channel_status "ACTIVE / INACTIVE / MAINTENANCE"
jsonb metadata
jsonb channel_instagram_permission
uuid workspace_id FK "FK → workspaces"
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
member_channels {
uuid member_channel_id PK
uuid workspace_member_id FK "FK → workspace_members"
uuid channel_id FK "FK → channels"
timestamp created_at
}
contacts {
uuid contact_id PK
varchar contact_name "NOT NULL"
varchar contact_phone "NOT NULL"
varchar contact_username
varchar contact_email
varchar contact_location
boolean contact_is_testing
uuid channel_id FK "FK → channels"
uuid workspace_id FK "FK → workspaces"
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
tags {
uuid tag_id PK
varchar tag_name "UNIQUE"
varchar tag_color "default: #3B82F6"
uuid workspace_id FK "FK → workspaces"
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
contact_tags {
uuid contact_tag_id PK
uuid contact_id FK "FK → contacts"
uuid tag_id FK "FK → tags"
timestamp created_at
}
conversations {
uuid conversation_id PK
uuid conversation_from
uuid conversation_to
varchar conversation_status "UNASSIGNED / ASSIGNED / RESOLVED / CLOSED"
text conversation_last_message
timestamp conversation_last_message_at
boolean conversation_is_testing
timestamp conversation_first_response_at
uuid conversation_first_response_by
timestamp conversation_24h_window_start
timestamp conversation_24h_window_end
uuid previous_conversation_id FK "self-ref (chain)"
uuid assigned_to FK "FK → users"
uuid assigned_by FK "FK → users"
timestamp assigned_at
timestamp resolved_at
uuid resolved_by FK "FK → users"
text resolved_description
uuid channel_id FK "FK → channels"
uuid contact_id FK "FK → contacts"
varchar conversation_raw_id
uuid read_by FK "FK → users"
integer unread_counts "default: 0"
varchar conversation_context "DM / POST_COMMENT / STORY_COMMENT"
uuid instagram_post_id FK "FK → instagram_posts"
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
conversation_messages {
uuid conversation_message_id PK
uuid conversation_id FK "FK → conversations"
uuid conversation_message_from
uuid conversation_message_to
text conversation_message_content
integer conversation_message_type_id FK "FK → conversation_message_types"
boolean conversation_message_is_system "default: false"
integer conversation_message_system_type_id FK "FK → conversation_message_system_types"
varchar conversation_message_status "SENT / DELIVERED / READ / FAILED"
uuid replied_to_message_id FK "self-ref (reply)"
jsonb conversation_message_template_components
boolean is_answered
uuid answered_by
timestamp answered_at
uuid created_by FK "FK → users"
timestamp created_at
timestamp updated_at
}
message_attachments {
uuid attachment_id PK
uuid conversation_message_id FK "FK → conversation_messages, CASCADE"
varchar attachment_file_name
bigint file_size
varchar mime_type
varchar file_url
varchar attachment_storage_type "LOCAL / S3 / GCS / AZURE"
jsonb attachment_metadata
timestamp expired_at
timestamp created_at
}
conversation_message_reactions {
uuid conversation_message_reaction_id PK
uuid conversation_message_id FK "FK → conversation_messages"
varchar reaction_type
varchar emoji
varchar sender_id
timestamp created_at
}
tickets {
uuid ticket_id PK
uuid conversation_id FK "FK → conversations"
varchar ticket_title "NOT NULL"
text ticket_description "NOT NULL"
text ticket_error_info
varchar ticket_status "TODO / IN_PROGRESS / RESOLVED / DONE / REJECTED / DELETED"
varchar ticket_number
uuid assigned_to FK "FK → users"
uuid assigned_by FK "FK → users"
timestamp assigned_at
uuid resolved_by FK "FK → users"
timestamp resolved_at
text resolved_note
uuid rejected_by FK "FK → users"
timestamp rejected_at
text reject_reason
uuid closed_by FK "FK → users"
timestamp closed_at
text reassign_note
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
ticket_attachments {
uuid ticket_attachment_id PK
uuid ticket_id FK "FK → tickets"
varchar attachment_file_name
bigint file_size
varchar mime_type
varchar file_url
timestamp created_at
}
ticket_status_history {
bigint ticket_status_history_id PK
uuid ticket_id FK "FK → tickets"
varchar old_status
varchar new_status
uuid updated_by FK "FK → users"
uuid assigned_to FK "FK → users"
uuid resolved_by FK "FK → users"
uuid rejected_by FK "FK → users"
timestamp created_at
}
templates {
uuid template_id PK
varchar template_name
varchar template_language
varchar template_category "MARKETING / UTILITY / AUTHENTICATION"
varchar template_status "APPROVED / REJECTED / PENDING / PAUSED"
varchar template_whatsapp_id
jsonb template_components
varchar template_quality_score "GREEN / YELLOW / RED / UNKNOWN"
uuid channel_id FK "FK → channels"
boolean template_is_used_in_campaign
boolean template_is_ticket "default: false"
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
campaigns {
uuid campaign_id PK
varchar campaign_name
text campaign_description
varchar campaign_status "DRAFT / SCHEDULED / RUNNING / COMPLETED / CANCELLED"
uuid template_id FK "FK → templates"
timestamp campaign_scheduled_at
timestamp campaign_started_at
timestamp campaign_completed_at
integer campaign_total_recipients
integer campaign_sent_count
integer campaign_failed_count
integer campaign_delivered_count
integer campaign_read_count
uuid channel_id FK "FK → channels"
uuid workspace_id FK "FK → workspaces"
uuid campaign_recipient_list_id FK "FK → campaign_recipient_lists"
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
campaign_recipient_lists {
uuid campaign_recipient_list_id PK
varchar campaign_recipient_list_name
text campaign_recipient_list_description
jsonb campaign_recipient_list_variables
uuid workspace_id FK "FK → workspaces"
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
campaign_recipient_list_items {
uuid campaign_recipient_list_item_id PK
uuid campaign_recipient_list_id FK "FK → campaign_recipient_lists"
uuid contact_id FK "FK → contacts"
jsonb variable_values
timestamp created_at
}
campaign_recipients {
uuid campaign_recipient_id PK
uuid campaign_id FK "FK → campaigns"
uuid contact_id FK "FK → contacts"
uuid conversation_id FK "FK → conversations"
uuid conversation_message_id FK "FK → conversation_messages"
varchar status "PENDING / SENT / DELIVERED / READ / FAILED"
timestamp created_at
timestamp updated_at
}
message_templates {
serial message_template_id PK
varchar message_template_name
text message_template_content
varchar message_template_shortcut
uuid workspace_id FK "FK → workspaces"
uuid channel_id FK "FK → channels"
jsonb message_template_variables
boolean message_template_is_global
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
notes {
uuid note_id PK
varchar note_title
text note_description
uuid workspace_id FK "FK → workspaces"
uuid contact_id FK "FK → contacts"
uuid created_by FK "FK → users"
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
conversation_resolve_category {
uuid resolve_category_id PK
varchar resolve_category_name
varchar resolve_category_color
text resolve_category_description
uuid workspace_id FK "FK → workspaces"
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
conversation_resolve_categories {
uuid conversation_resolve_category_id PK
uuid conversation_id FK "FK → conversations"
uuid resolve_category_id FK "FK → conversation_resolve_category"
timestamp created_at
}
office_hours_schedule {
uuid office_hours_schedule_id PK
uuid workspace_id FK "FK → workspaces"
integer day_of_week "0-6"
time start_time
time end_time
varchar timezone
boolean is_active "default: true"
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
channel_office_hours_schedule {
uuid channel_office_hours_schedule_id PK
uuid channel_id FK "FK → channels"
integer day_of_week "0-6"
time start_time
time end_time
varchar timezone
boolean is_active "default: true"
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
office_hours_auto_responses {
uuid office_hours_auto_response_id PK
uuid workspace_id FK "FK → workspaces"
text during_office_hours_message
text outside_office_hours_message
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
channel_office_hours_auto_responses {
uuid channel_office_hours_auto_response_id PK
uuid channel_id FK "FK → channels"
text during_office_hours_message
text outside_office_hours_message
timestamp created_at
timestamp updated_at
timestamp deleted_at
}
users ||--o{ sessions : "has"
users ||--o{ workspace_members : "belongs_to"
users ||--o{ api_keys : "owns"
users ||--o{ workspace_invitations : "invited by"
workspaces ||--o{ workspace_members : "contains"
workspaces ||--o{ channels : "has"
workspaces ||--o{ tags : "has"
workspaces ||--o{ office_hours_schedule : "has"
workspaces ||--o{ office_hours_auto_responses : "has"
workspaces ||--o{ campaigns : "has"
workspaces ||--o{ message_templates : "has"
workspaces ||--o{ conversation_resolve_category : "has"
workspaces ||--o{ campaign_recipient_lists : "has"
workspaces ||--o{ notes : "has"
channels ||--o{ contacts : "receives"
channels ||--o{ conversations : "has"
channels ||--o{ templates : "has"
channels ||--o{ campaigns : "uses"
channels ||--o{ member_channels : "has"
channels ||--o{ channel_office_hours_schedule : "has"
channels ||--o{ channel_office_hours_auto_responses : "has"
contacts ||--o{ conversations : "initiates"
contacts ||--o{ contact_tags : "has"
contacts ||--o{ campaign_recipient_list_items : "included in"
contacts ||--o{ notes : "has"
tags ||--o{ contact_tags : "applied to"
conversations ||--o{ conversation_messages : "contains"
conversations ||--o{ tickets : "generates"
conversations ||--o{ conversation_resolve_categories : "categorized"
conversations ||--o| conversations : "chained"
conversation_messages ||--o{ message_attachments : "has"
conversation_messages ||--o{ conversation_message_reactions : "has"
conversation_messages ||--o| conversation_messages : "replies to"
tickets ||--o{ ticket_attachments : "has"
tickets ||--o{ ticket_status_history : "tracks"
templates ||--o{ campaigns : "used by"
campaigns ||--o{ campaign_recipients : "sends to"
campaign_recipient_lists ||--o{ campaign_recipient_list_items : "contains"
conversation_resolve_category ||--o{ conversation_resolve_categories : "links"
workspace_members ||--o{ member_channels : "has access"
```
---
## 5.6 Statistik Final
| Metrik | Jumlah |
|--------|--------|
| **Total API Endpoints** | ~60+ |
| **Total Service Modules (BE)** | 47 |
| **Total Database Tables** | 30+ |
| **Total FE Component Files** | ~200+ |
| **Total FE Hooks** | ~30+ |
| **Total FE Pages** | ~40+ |
| **Chat Module Files (FE)** | 82 |
| **Campaign Module Files (FE)** | 33 |
| **Ticket Module Files (FE)** | 25 |
| **Settings Pages** | 20+ |
| **Auth Methods Supported** | 6 (better-auth active, 5备用) |
| **External Channel Integrations** | 3 active (WA, IG, TG) |
| **Real-time Mechanisms** | 3 (Pusher, Socket.IO, WebSocket) |
| **User Roles** | 3 (owner, admin, member) |
| **Permission Resources** | ~10 (channel, contact, conversation, message, ticket, template, campaign, tag, note, workspace) |
| **Conversation Statuses** | 4 (UNASSIGNED, ASSIGNED, RESOLVED, CLOSED) |
| **Ticket Statuses** | 6 (TODO, IN_PROGRESS, RESOLVED, DONE, REJECTED, DELETED) |
| **Campaign Statuses** | 5 (DRAFT, SCHEDULED, RUNNING, COMPLETED, CANCELLED) |
---
## 5.7 Daftar File Report
| # | File | Isi |
|---|------|-----|
| 1 | `00-EXECUTIVE-SUMMARY.md` | Ringkasan eksekutif, daftar isi, statistik |
| 2 | `01-OVERALL-ARCHITECTURE.md` | Arsitektur sistem, user journey, navigasi, data flow, multi-channel message flow |
| 3 | `02-INTEGRATION-FLOW.md` | API integration map, real-time flow, auth integration, file upload flow, middleware stack |
| 4 | `03-FE-FLOWCHARTS.md` | FE routing, auth flow, chat flow (82 files detail), campaign flow, ticket flow, settings flow, SWR data fetching pattern |
| 5 | `04-BE-FLOWCHARTS.md` | BE service architecture, auth RBAC, chat engine, conversation status, campaign execution, ticket lifecycle, webhook processing, service module pattern, error handling |
| 6 | `05-SUMMARY-TABLES.md` | Ringkasan fitur, tech stack, RBAC matrix, API catalog, database ERD, statistik final |
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment