Merge pull request #511 from Tria-plc/freight/feat/chat-app

chat app support to the passenger with websocket and anonymous
This commit is contained in:
Nathnael Wondisha
2026-07-07 17:16:10 +03:00
committed by GitHub
28 changed files with 2387 additions and 102 deletions

View File

@@ -1,5 +1,7 @@
import type { BaseEntity } from "../common";
export * from "./support-chat";
export enum TicketStatus {
Reserved = "RESERVED",
Confirmed = "CONFIRMED",

View File

@@ -0,0 +1,103 @@
/**
* Shared contracts for the passenger in-app customer-support chat.
*
* Unlike freight (company-scoped), a passenger conversation ("ticket") belongs to a
* single **individual passenger** (keyed by their IAM user id). Backoffice agents
* work a shared inbox (no assignment). Messages are text-only for the MVP.
*
* These are the wire (JSON) shapes — dates are ISO strings — plus the frozen
* Socket.IO event/namespace constants shared by the gateway (emitter) and both
* passenger web apps (subscribers).
*/
/** Lifecycle of a support conversation. Mirrors the Prisma `SupportConversationStatus`. */
export enum PassengerSupportStatus {
OPEN = "OPEN",
RESOLVED = "RESOLVED",
CLOSED = "CLOSED",
}
/** Author of a message. The Prisma `SupportSender` also has `BOT` (legacy, unused here). */
export enum PassengerSupportSender {
USER = "USER",
AGENT = "AGENT",
}
/** A single chat message on the wire. */
export interface PassengerSupportMessageDto {
id: string;
conversationId: string;
sender: PassengerSupportSender;
/** Display name of the author, best-effort. */
authorName?: string | null;
text: string;
createdAt: string;
}
/** A conversation ("ticket") on the wire, with denormalized last-message fields. */
export interface PassengerSupportConversationDto {
id: string;
/** Set for authenticated passengers; null for guest conversations. */
userId?: string | null;
/** Set for guest (unauthenticated) conversations. */
guestId?: string | null;
guestEmail?: string | null;
guestPhone?: string | null;
passengerId?: string | null;
/** Display name — passenger's name for authed, guest's name for guests. */
passengerName?: string | null;
subject?: string | null;
status: PassengerSupportStatus;
assignedAgentId?: string | null;
lastMessageAt?: string | null;
lastMessagePreview?: string | null;
lastMessageSender?: PassengerSupportSender | null;
/** Unread count for the caller's side (messages from the other sender after their cursor). */
unreadCount: number;
createdAt: string;
updatedAt: string;
}
/** Customer opens a new ticket: subject + first message. */
export interface CreatePassengerSupportConversationDto {
subject: string;
initialMessage: string;
}
/** Guest (unauthenticated) opens a ticket: identity + contact captured up front. */
export interface CreateGuestSupportConversationDto {
guestId: string;
name: string;
email: string;
phone?: string;
subject: string;
initialMessage: string;
}
/** Post a message into an existing conversation. */
export interface SendPassengerSupportMessageDto {
text: string;
}
/** Paginated list envelope for the conversations list endpoints. */
export interface PassengerSupportConversationListResult {
items: PassengerSupportConversationDto[];
count: number;
/** Total unread conversations for the caller's side (badge source). */
unreadCount: number;
}
/** Socket.io event names pushed server → client on the passenger support namespace. */
export const PASSENGER_SUPPORT_WS_EVENTS = {
MESSAGE_NEW: "passenger-support:message-new",
CONVERSATION_UPDATED: "passenger-support:conversation-updated",
} as const;
/** Socket.io namespace the passenger support gateway listens on. */
export const PASSENGER_SUPPORT_WS_NAMESPACE = "passenger-support-chat";
/** Payload for {@link PASSENGER_SUPPORT_WS_EVENTS.MESSAGE_NEW}. */
export interface PassengerSupportMessageEvent {
conversation: PassengerSupportConversationDto;
message: PassengerSupportMessageDto;
}