User Chat Interface¶
Relevant source files * src/router.js * views/soporte.ejs
Purpose and Scope¶
This document describes the user-facing chat interface in the support chat system. This interface is displayed to users with the user role when they access the support page. It provides a simple, single-conversation chat experience where users communicate with administrators.
For information about the admin-facing multi-conversation interface, see Admin Chat Interface. For the overall support chat system architecture including WebSocket handling, see Support Chat System.
Sources: views/soporte.ejs L1-L191
Route and Access Control¶
The user chat interface is served by the /soporte route, which requires authentication via the verifyToken middleware. The route handler renders the soporte.ejs template with user information extracted from the JWT token.
| Aspect | Details |
|---|---|
| Route Path | GET /soporte |
| Middleware | verifyToken (authentication required) |
| View Template | views/soporte.ejs |
| Data Passed | User object with username and role properties |
The route implementation at src/router.js L220-L227
extracts the username and role from the decoded JWT token (req.user) and passes them to the view for rendering.
Sources: src/router.js L220-L227
HTML Structure¶
The user chat interface consists of two main elements rendered when user.role !== 'admin':
flowchart TD
Container["Container div.container.mt-4"]
Header["Header section<br>Shows username and role"]
ChatArea["Chat display area<br>div#chat<br>400px height, scrollable"]
Form["Message form<br>form#formulario"]
Input["Text input<br>input#mensaje"]
Button["Submit button<br>btn-primary"]
Container --> Header
Container --> ChatArea
Container --> Form
Form --> Input
Form --> Button
Chat Display Area¶
The chat area is a bordered, scrollable div that displays message history and incoming messages:
- Element ID:
chat - Styling: Border, rounded corners, padding, 400px fixed height with
overflow-y: auto - Location: views/soporte.ejs L27
Message Input Form¶
The form allows users to compose and send messages:
- Element ID:
formulario - Input Field ID:
mensaje - Layout: Bootstrap grid (10 columns for input, 2 for button)
- Location: views/soporte.ejs L28-L32
Sources: views/soporte.ejs L25-L33
Client-Side JavaScript Components¶
Socket.IO Connection Initialization¶
The user interface establishes a WebSocket connection using Socket.IO immediately upon page load:
flowchart TD
PageLoad["Page loads<br>soporte.ejs"]
LoadLib["Load /socket.io/socket.io.js"]
InitSocket["Execute: const socket = io()"]
Connected["WebSocket connection established<br>JWT from cookie used for auth"]
PageLoad --> LoadLib
LoadLib --> InitSocket
InitSocket --> Connected
The Socket.IO client library is loaded from the server-provided endpoint at views/soporte.ejs L37
and the connection is established at views/soporte.ejs L39
Sources: views/soporte.ejs L37-L42
Message Display Function¶
The agregarMensaje function renders individual messages in the chat area:
Function Signature: agregarMensaje(de, mensaje, fecha = null)
| Parameter | Type | Description |
|---|---|---|
de |
string | Username of message sender |
mensaje |
string | Message content |
fecha |
string/Date | Optional timestamp (ISO format) |
Behavior:
- Creates a
<p>element - Formats sender name in bold
- Appends formatted timestamp if provided (via
formatearFecha) - Appends message to
chatdiv - Auto-scrolls chat to bottom
The timestamp formatting function formatearFecha converts ISO date strings to DD/MM/YYYY HH:MM format for readability.
Sources: views/soporte.ejs L44-L60
Loading Message History¶
Upon page load, the user interface fetches historical messages from the /api/mensajes/mios endpoint:
sequenceDiagram
participant Browser
participant (soporte.ejs user view)
participant GET /api/mensajes/mios
participant verifyToken
participant mensajes table
participant div
Browser->>GET /api/mensajes/mios: GET /api/mensajes/mios
GET /api/mensajes/mios->>GET /api/mensajes/mios: JWT in cookie
GET /api/mensajes/mios->>mensajes table: Extract username from JWT
mensajes table-->>GET /api/mensajes/mios: SELECT * WHERE
GET /api/mensajes/mios-->>Browser: de_usuario=? OR para_usuario=?
Browser->>Browser: Message rows
Browser->>Browser: JSON array of messages
Browser->>div: Clear chat.innerHTML
API Endpoint Details:
- Route:
GET /api/mensajes/miossrc/router.js L256-L280 - Authentication: Requires
verifyTokenmiddleware - Response: JSON array of message objects with
de_usuario,para_usuario,mensaje,fecha - Query Logic: Returns all messages where user is either sender or recipient, ordered by date ascending
Client Implementation at views/soporte.ejs L166-L171
:
- Fetch from endpoint
- Parse JSON response
- Clear existing chat content
- Iterate through messages and call
agregarMensajefor each
Sources: views/soporte.ejs L166-L171
Receiving Real-time Messages¶
The user interface listens for the mensaje_recibido WebSocket event to receive messages in real-time:
flowchart TD
ServerEmit["Socket.IO Server<br>Emits 'mensaje_recibido'"]
EventHandler["socket.on('mensaje_recibido', handler)"]
ExtractData["Extract: de, mensaje<br>from event payload"]
CreateTimestamp["Create new Date() timestamp"]
CallDisplay["Call agregarMensaje(de, mensaje, fecha)"]
UpdateUI["Message appears in chat"]
ServerEmit --> EventHandler
EventHandler --> ExtractData
ExtractData --> CreateTimestamp
CreateTimestamp --> CallDisplay
CallDisplay --> UpdateUI
Event Handler at views/soporte.ejs L174-L177
:
- Event Name:
mensaje_recibido - Payload Structure:
{ de, mensaje } - Action: Calls
agregarMensajewith sender username, message content, and current timestamp
Sources: views/soporte.ejs L174-L177
Sending Messages¶
Users send messages by submitting the form, which emits a mensaje_privado event to the server:
sequenceDiagram
participant User
participant form
participant input
participant Socket.IO Client
participant Socket.IO Server
participant mensajes table
participant div
User->>form: Submit form
form->>form: e.preventDefault()
form->>input: Get value.trim()
form->>form: Validate not empty
form->>Socket.IO Client: emit('mensaje_privado',
Socket.IO Client->>Socket.IO Server: {para: 'admin', mensaje})
Socket.IO Server->>mensajes table: WebSocket message
Socket.IO Server->>Socket.IO Server: INSERT message
form->>div: Route to admin rooms
form->>input: agregarMensaje('Tú', mensaje, new Date())
Form Submit Handler at views/soporte.ejs L180-L187
:
| Step | Action | Code Location |
|---|---|---|
| 1 | Prevent default form submission | e.preventDefault() |
| 2 | Extract and trim message | inputMensaje.value.trim() |
| 3 | Validate non-empty | Early return if empty |
| 4 | Emit Socket.IO event | socket.emit("mensaje_privado", {para: "admin", mensaje}) |
| 5 | Display locally as "Tú" | agregarMensaje("Tú", mensaje, new Date()) |
| 6 | Clear input field | inputMensaje.value = '' |
Key Implementation Detail: The recipient is hardcoded to "admin" at views/soporte.ejs L184
This ensures all user messages are routed to the admin room, regardless of which specific admin responds. The server-side socket handler (documented in Socket Handler) manages the actual message routing and persistence.
Sources: views/soporte.ejs L180-L187
Complete User Interaction Flow¶
flowchart TD
Start["User accesses /soporte"]
Auth["verifyToken middleware<br>validates JWT"]
Render["Render soporte.ejs<br>user.role !== 'admin'"]
LoadScript["Load Socket.IO client library"]
ConnectWS["Establish WebSocket connection<br>const socket = io()"]
FetchHistory["fetch('/api/mensajes/mios')"]
DisplayHistory["Display historical messages<br>agregarMensaje() for each"]
ListenEvents["Listen for 'mensaje_recibido'"]
Ready["User can send/receive messages"]
UserTypes["User types message"]
Submit["Submit form"]
EmitEvent["socket.emit('mensaje_privado',<br>{para: 'admin', mensaje})"]
LocalDisplay["Display in local chat as 'Tú'"]
IncomingMsg["Admin sends message"]
ReceiveEvent["socket.on('mensaje_recibido')"]
DisplayIncoming["agregarMensaje(de, mensaje, fecha)"]
Start --> Auth
Auth --> Render
Render --> LoadScript
LoadScript --> ConnectWS
ConnectWS --> FetchHistory
FetchHistory --> DisplayHistory
DisplayHistory --> ListenEvents
ListenEvents --> Ready
Ready --> UserTypes
UserTypes --> Submit
Submit --> EmitEvent
EmitEvent --> LocalDisplay
Ready --> IncomingMsg
IncomingMsg --> ReceiveEvent
ReceiveEvent --> DisplayIncoming
Sources: views/soporte.ejs L164-L188
Code Entity Mapping¶
Key Variables and Elements¶
| JavaScript Variable | HTML Element ID | Purpose |
|---|---|---|
socket |
N/A | Socket.IO client instance |
chat |
#chat |
Message display container |
inputMensaje |
#mensaje |
Message text input field |
formulario |
#formulario |
Message submission form |
Sources: views/soporte.ejs L39-L42
Event Handlers¶
| Event Type | Element | Handler Function | Location |
|---|---|---|---|
submit |
#formulario |
Anonymous function | views/soporte.ejs L180-L187 |
mensaje_recibido |
socket (WebSocket) |
Anonymous function | views/soporte.ejs L174-L177 |
Sources: views/soporte.ejs L174-L177
Helper Functions¶
| Function Name | Parameters | Return Type | Purpose |
|---|---|---|---|
formatearFecha |
isoString |
string |
Converts ISO timestamp to DD/MM/YYYY HH:MM |
agregarMensaje |
de, mensaje, fecha=null |
void |
Appends formatted message to chat |
Sources: views/soporte.ejs L44-L60
Differences from Admin Interface¶
The user interface is significantly simpler than the admin interface:
| Feature | User Interface | Admin Interface |
|---|---|---|
| User List | Not present | Shows all users with conversations |
| Conversation Switching | Not applicable (single conversation) | Can switch between user conversations |
| Recipient Selection | Hardcoded to "admin" | Selected from active conversation |
| Notifications | Not implemented | Visual indicators for new messages |
| Message History Endpoint | /api/mensajes/mios |
/api/mensajes?con=username |
| Conversation State | Stateless | Maintains conversaciones object |
Sources: views/soporte.ejs L25-L33