wacrm/src/types/index.ts
Arnas Donauskas 5243188d0c
feat(automations): add 'First Message from Contact' trigger
new_contact_created only fires when the webhook auto-creates the
contact row, so it misses the case of manually-added / imported
contacts sending their first-ever message.

first_inbound_message fires whenever the incoming message is the
contact's first customer-sent message (checked by counting prior
customer-sender messages in the conversation before insert), which
covers both auto-created and pre-existing contacts.

- Added first_inbound_message to AutomationTriggerType.
- New pill color + dropdown entry labeled "First Message from Contact".
- Webhook now counts prior customer messages before inserting the
  incoming one; dispatches first_inbound_message when the count is 0.
- new_contact_created still fires for brand-new contacts; they're
  independent triggers with overlapping coverage, so users pick.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 20:47:13 +03:00

375 lines
8.3 KiB
TypeScript

export interface Profile {
id: string;
user_id: string;
full_name: string;
email: string;
avatar_url?: string;
role: string;
created_at: string;
}
export interface Contact {
id: string;
user_id: string;
phone: string;
name?: string;
email?: string;
company?: string;
avatar_url?: string;
created_at: string;
updated_at: string;
}
export interface Tag {
id: string;
user_id: string;
name: string;
color: string;
created_at: string;
}
export interface ContactTag {
id: string;
contact_id: string;
tag_id: string;
}
export interface CustomField {
id: string;
user_id: string;
field_name: string;
field_type: string;
field_options?: Record<string, unknown>;
created_at: string;
}
export interface ContactCustomValue {
id: string;
contact_id: string;
custom_field_id: string;
value?: string;
}
export interface ContactNote {
id: string;
contact_id: string;
user_id: string;
note_text: string;
created_at: string;
}
export type ConversationStatus = 'open' | 'pending' | 'closed';
export interface Conversation {
id: string;
user_id: string;
contact_id: string;
status: ConversationStatus;
assigned_agent_id?: string;
last_message_text?: string;
last_message_at?: string;
unread_count: number;
created_at: string;
updated_at: string;
contact?: Contact;
}
export type SenderType = 'customer' | 'agent' | 'bot';
export type ContentType = 'text' | 'image' | 'document' | 'audio' | 'video' | 'location' | 'template';
export type MessageStatus = 'sending' | 'sent' | 'delivered' | 'read' | 'failed';
export interface Message {
id: string;
conversation_id: string;
sender_type: SenderType;
sender_id?: string;
content_type: ContentType;
content_text?: string;
media_url?: string;
template_name?: string;
message_id?: string;
status: MessageStatus;
created_at: string;
}
export interface WhatsAppConfig {
id: string;
user_id: string;
phone_number_id: string;
waba_id?: string;
access_token: string;
verify_token?: string;
status: 'connected' | 'disconnected';
connected_at?: string;
}
export interface MessageTemplate {
id: string;
user_id: string;
name: string;
category: 'Marketing' | 'Utility' | 'Authentication';
language?: string;
header_type?: 'text' | 'image' | 'video' | 'document';
header_content?: string;
body_text: string;
footer_text?: string;
buttons?: Record<string, unknown>[];
status?: 'Draft' | 'Pending' | 'Approved' | 'Rejected';
created_at: string;
}
export interface Pipeline {
id: string;
user_id: string;
name: string;
created_at: string;
}
export interface PipelineStage {
id: string;
pipeline_id: string;
name: string;
position: number;
color: string;
created_at: string;
}
export type DealStatus = 'open' | 'won' | 'lost';
export interface Deal {
id: string;
user_id: string;
pipeline_id: string;
stage_id: string;
/**
* Nullable after migration 004 — becomes NULL when the referenced
* contact is deleted (ON DELETE SET NULL). History preserved.
*/
contact_id: string | null;
conversation_id?: string;
assigned_to?: string;
title: string;
value: number;
currency?: string;
notes?: string;
expected_close_date?: string;
status?: DealStatus;
created_at: string;
updated_at?: string;
contact?: Contact;
stage?: PipelineStage;
assignee?: Profile;
}
export type BroadcastStatus = 'draft' | 'scheduled' | 'sending' | 'sent' | 'failed';
export type RecipientStatus = 'pending' | 'sent' | 'delivered' | 'read' | 'replied' | 'failed';
export interface Broadcast {
id: string;
user_id: string;
name: string;
template_name: string;
template_language: string;
template_variables?: Record<string, unknown>;
audience_filter?: Record<string, unknown>;
scheduled_at?: string;
status: BroadcastStatus;
total_recipients: number;
sent_count: number;
delivered_count: number;
read_count: number;
replied_count: number;
failed_count: number;
created_at: string;
}
export interface BroadcastRecipient {
id: string;
broadcast_id: string;
/**
* Nullable after migration 004 — becomes NULL when the referenced
* contact is deleted (ON DELETE SET NULL). History preserved; the
* UI renders "Unknown" for orphaned rows.
*/
contact_id: string | null;
status: RecipientStatus;
sent_at?: string;
delivered_at?: string;
read_at?: string;
replied_at?: string;
error_message?: string;
/**
* Meta's message id, persisted when the broadcast send succeeds so
* the webhook can mirror status updates back onto the recipient row.
* Added in migration 003.
*/
whatsapp_message_id?: string;
created_at: string;
contact?: Contact;
}
// ============================================================
// Automations (migration 006)
// ============================================================
export type AutomationTriggerType =
| 'new_message_received'
| 'first_inbound_message'
| 'keyword_match'
| 'new_contact_created'
| 'conversation_assigned'
| 'tag_added'
| 'time_based';
export type AutomationStepType =
| 'send_message'
| 'send_template'
| 'add_tag'
| 'remove_tag'
| 'assign_conversation'
| 'update_contact_field'
| 'create_deal'
| 'wait'
| 'condition'
| 'send_webhook'
| 'close_conversation';
export type AutomationLogStatus = 'success' | 'partial' | 'failed';
export interface KeywordMatchTriggerConfig {
keywords: string[];
match_type: 'exact' | 'contains';
case_sensitive?: boolean;
}
export interface TagTriggerConfig {
tag_id: string;
}
export interface TimeBasedTriggerConfig {
/** Cron expression or simple HH:mm string; engine can accept either. */
schedule: string;
timezone?: string;
}
export type AutomationTriggerConfig =
| Record<string, never>
| KeywordMatchTriggerConfig
| TagTriggerConfig
| TimeBasedTriggerConfig
| Record<string, unknown>;
export interface SendMessageStepConfig {
text: string;
}
export interface SendTemplateStepConfig {
template_name: string;
language?: string;
variables?: Record<string, string>;
}
export interface TagStepConfig {
tag_id: string;
}
export interface AssignConversationStepConfig {
mode: 'specific' | 'round_robin';
agent_id?: string;
}
export interface UpdateContactFieldStepConfig {
field: string;
value: string;
}
export interface CreateDealStepConfig {
pipeline_id: string;
stage_id: string;
title: string;
value?: number;
}
export interface WaitStepConfig {
amount: number;
unit: 'minutes' | 'hours' | 'days';
}
export type ConditionSubject =
| 'contact_field'
| 'tag_presence'
| 'message_content'
| 'time_of_day';
export interface ConditionStepConfig {
subject: ConditionSubject;
/** e.g. field name, tag id, substring, or "HH:mm-HH:mm" depending on subject */
operand?: string;
/** For contact_field equals / message_content contains — comparison value */
value?: string;
}
export interface SendWebhookStepConfig {
url: string;
headers?: Record<string, string>;
body_template?: string;
}
export type AutomationStepConfig =
| SendMessageStepConfig
| SendTemplateStepConfig
| TagStepConfig
| AssignConversationStepConfig
| UpdateContactFieldStepConfig
| CreateDealStepConfig
| WaitStepConfig
| ConditionStepConfig
| SendWebhookStepConfig
| Record<string, never>
| Record<string, unknown>;
export interface Automation {
id: string;
user_id: string;
name: string;
description?: string;
trigger_type: AutomationTriggerType;
trigger_config: AutomationTriggerConfig;
is_active: boolean;
execution_count: number;
last_executed_at?: string | null;
created_at: string;
updated_at: string;
}
export interface AutomationStep {
id: string;
automation_id: string;
parent_step_id?: string | null;
branch?: 'yes' | 'no' | null;
step_type: AutomationStepType;
step_config: AutomationStepConfig;
position: number;
created_at: string;
}
export interface AutomationLogStepResult {
step_id: string;
step_type: AutomationStepType;
status: 'success' | 'skipped' | 'failed';
detail?: string;
}
export interface AutomationLog {
id: string;
automation_id: string;
user_id: string;
contact_id: string | null;
trigger_event: string;
steps_executed: AutomationLogStepResult[];
status: AutomationLogStatus;
error_message?: string | null;
created_at: string;
contact?: Contact;
}