hazo_chat 2.1.1 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,11 +1,18 @@
1
1
  # hazo_chat
2
2
 
3
- A full-featured React chat component library for 1-1 communication with document sharing, file attachments, and real-time messaging capabilities.
3
+ A full-featured React chat component library for group-based communication with document sharing, file attachments, and real-time messaging capabilities.
4
4
 
5
- **Version 2.0** - Now with API-first architecture! No server-side dependencies in client components.
5
+ **Version 3.1** - Generic schema supporting multiple chat patterns: support (client-to-staff), peer (1:1), and group conversations.
6
+
7
+ **Version 3.0** - Introduced group-based chat architecture. Multiple users can participate in a single chat group, perfect for support staff rotating on client sessions.
8
+
9
+ **Version 2.0** introduced API-first architecture with no server-side dependencies in client components.
6
10
 
7
11
  ## Features
8
12
 
13
+ - 👥 **Group-Based Chat** - Multiple users can participate in a single chat group
14
+ - 🏗️ **Multiple Chat Patterns** - Support for support (client-to-staff), peer (1:1), and group conversations
15
+ - 🔄 **Role-Based Access** - Support for 'client', 'staff', 'owner', 'admin', and 'member' roles within groups
9
16
  - 📱 **Responsive Design** - Works on desktop and mobile with adaptive layout
10
17
  - 💬 **Real-time Messaging** - Polling or manual refresh modes for message updates with optimistic UI
11
18
  - 📎 **File Attachments** - Support for documents and images with preview
@@ -478,7 +485,7 @@ export default function ChatPage() {
478
485
  return (
479
486
  <div className="h-screen">
480
487
  <HazoChat
481
- receiver_user_id="recipient-uuid"
488
+ chat_group_id="group-uuid"
482
489
  reference_id="conversation-123"
483
490
  reference_type="support"
484
491
  title="Chat with Support"
@@ -497,13 +504,15 @@ hazo_chat requires these API endpoints:
497
504
 
498
505
  | Endpoint | Method | Description |
499
506
  |----------|--------|-------------|
500
- | `/api/hazo_chat/messages` | GET | Fetch chat messages |
501
- | `/api/hazo_chat/messages` | POST | Send a new message |
507
+ | `/api/hazo_chat/messages` | GET | Fetch chat messages (requires `chat_group_id` param) |
508
+ | `/api/hazo_chat/messages` | POST | Send a new message (requires `chat_group_id` in body) |
502
509
  | `/api/hazo_chat/messages/[id]/read` | PATCH | Mark a message as read (automatic) |
503
- | `/api/hazo_chat/unread_count` | GET | Get unread message counts by reference_id (optional) |
510
+ | `/api/hazo_chat/unread_count` | GET | Get unread message counts by chat_group_id (optional) |
504
511
  | `/api/hazo_auth/me` | GET | Get current authenticated user |
505
512
  | `/api/hazo_auth/profiles` | POST | Fetch user profiles by IDs |
506
513
 
514
+ **Breaking Change (v3.0):** Messages API now uses `chat_group_id` instead of `receiver_user_id`. All group members can see and send messages.
515
+
507
516
  ### Using Exportable Handlers (Recommended)
508
517
 
509
518
  ```typescript
@@ -563,9 +572,11 @@ hazo_chat provides server-side library functions that can be used in API routes,
563
572
 
564
573
  ### hazo_chat_get_unread_count
565
574
 
566
- Get unread message counts grouped by reference_id for a receiver user.
575
+ Get unread message counts grouped by chat_group_id for a user.
567
576
 
568
- **Purpose:** Returns an array of reference IDs with the count of unread messages (where `read_at` is `null`) for a given receiver user ID. Useful for displaying unread message badges or notifications.
577
+ **Purpose:** Returns an array of chat group IDs with the count of unread messages (where `read_at` is `null`) for a given user ID. Useful for displaying unread message badges or notifications.
578
+
579
+ **Breaking Change (v3.0):** Now groups by `chat_group_id` instead of `reference_id`. Accepts optional `chat_group_ids` filter.
569
580
 
570
581
  **Usage:**
571
582
 
@@ -579,11 +590,13 @@ const hazo_chat_get_unread_count = createUnreadCountFunction({
579
590
  });
580
591
 
581
592
  // Use the function
582
- const unreadCounts = await hazo_chat_get_unread_count('receiver-user-id-123');
593
+ const unreadCounts = await hazo_chat_get_unread_count({
594
+ user_id: 'user-id-123',
595
+ chat_group_ids: ['group-1', 'group-2'] // Optional: filter by specific groups
596
+ });
583
597
  // Returns: [
584
- // { reference_id: 'ref-1', count: 5 },
585
- // { reference_id: 'ref-2', count: 3 },
586
- // { reference_id: '', count: 1 } // Empty reference_id for general messages
598
+ // { chat_group_id: 'group-1', count: 5 },
599
+ // { chat_group_id: 'group-2', count: 3 }
587
600
  // ]
588
601
  ```
589
602
 
@@ -591,14 +604,16 @@ const unreadCounts = await hazo_chat_get_unread_count('receiver-user-id-123');
591
604
 
592
605
  ```typescript
593
606
  interface UnreadCountResult {
594
- reference_id: string; // The reference ID (empty string for messages without reference)
595
- count: number; // Number of unread messages for this reference
607
+ chat_group_id: string; // The chat group ID
608
+ count: number; // Number of unread messages in this group
596
609
  }
597
610
  ```
598
611
 
599
612
  **Function Behavior:**
600
613
  - Only counts messages where `read_at` is `null` and `deleted_at` is `null`
601
- - Groups results by `reference_id`
614
+ - Only counts messages in groups where the user is a member
615
+ - Groups results by `chat_group_id`
616
+ - Optionally filters by specific `chat_group_ids` if provided
602
617
  - Sorts results by count (descending - most unread first)
603
618
  - Returns empty array if no unread messages found
604
619
  - Returns empty array on errors (doesn't throw)
@@ -620,33 +635,41 @@ const hazo_chat_get_unread_count = createUnreadCountFunction({
620
635
  export async function GET(request: NextRequest) {
621
636
  try {
622
637
  const { searchParams } = new URL(request.url);
623
- const receiver_user_id = searchParams.get('receiver_user_id');
638
+ const user_id = searchParams.get('user_id');
639
+ const chat_group_ids_param = searchParams.get('chat_group_ids');
624
640
 
625
- if (!receiver_user_id) {
641
+ if (!user_id) {
626
642
  return NextResponse.json(
627
- {
628
- success: false,
629
- error: 'receiver_user_id is required',
643
+ {
644
+ success: false,
645
+ error: 'user_id is required',
630
646
  unread_counts: []
631
647
  },
632
648
  { status: 400 }
633
649
  );
634
650
  }
635
651
 
636
- const unread_counts = await hazo_chat_get_unread_count(receiver_user_id);
652
+ const chat_group_ids = chat_group_ids_param
653
+ ? chat_group_ids_param.split(',')
654
+ : undefined;
655
+
656
+ const unread_counts = await hazo_chat_get_unread_count({
657
+ user_id,
658
+ chat_group_ids
659
+ });
637
660
 
638
661
  return NextResponse.json({
639
662
  success: true,
640
- receiver_user_id,
663
+ user_id,
641
664
  unread_counts,
642
- total_references: unread_counts.length,
665
+ total_groups: unread_counts.length,
643
666
  total_unread: unread_counts.reduce((sum, item) => sum + item.count, 0)
644
667
  });
645
668
  } catch (error) {
646
669
  const error_message = error instanceof Error ? error.message : 'Unknown error';
647
670
  return NextResponse.json(
648
- {
649
- success: false,
671
+ {
672
+ success: false,
650
673
  error: error_message,
651
674
  unread_counts: []
652
675
  },
@@ -714,7 +737,7 @@ export async function getUnreadCounts(receiver_user_id: string) {
714
737
 
715
738
  | Prop | Type | Required | Default | Description |
716
739
  |------|------|----------|---------|-------------|
717
- | `receiver_user_id` | `string` | ✅ | - | UUID of the chat recipient |
740
+ | `chat_group_id` | `string` | ✅ | - | UUID of the chat group (CHANGED from `receiver_user_id` in v3.0) |
718
741
  | `reference_id` | `string` | ❌ | - | Reference ID for chat context grouping |
719
742
  | `reference_type` | `string` | ❌ | `'chat'` | Type of reference |
720
743
  | `api_base_url` | `string` | ❌ | `'/api/hazo_chat'` | Base URL for API endpoints |
@@ -735,7 +758,7 @@ export async function getUnreadCounts(receiver_user_id: string) {
735
758
 
736
759
  ```tsx
737
760
  <HazoChat
738
- receiver_user_id="user-123"
761
+ chat_group_id="group-123"
739
762
  reference_id="project-456"
740
763
  reference_type="project_chat"
741
764
  api_base_url="/api/hazo_chat"
@@ -803,7 +826,7 @@ To make all chat bubbles fully round (instead of the default style with a tail):
803
826
 
804
827
  ```tsx
805
828
  <HazoChat
806
- receiver_user_id="user-123"
829
+ chat_group_id="group-123"
807
830
  reference_id="project-456"
808
831
  show_sidebar_toggle={false} // Hide hamburger menu
809
832
  show_delete_button={false} // Hide delete buttons
@@ -954,7 +977,7 @@ const {
954
977
  mark_as_read, // (message_id) => Promise<void>
955
978
  refresh, // () => void - Reload messages
956
979
  } = useChatMessages({
957
- receiver_user_id: 'user-456',
980
+ chat_group_id: 'group-456', // CHANGED from receiver_user_id in v3.0
958
981
  reference_id: 'chat-123',
959
982
  reference_type: 'direct',
960
983
  api_base_url: '/api/hazo_chat',
@@ -1017,7 +1040,7 @@ interface ChatMessage {
1017
1040
  reference_id: string;
1018
1041
  reference_type: string;
1019
1042
  sender_user_id: string;
1020
- receiver_user_id: string;
1043
+ chat_group_id: string; // CHANGED from receiver_user_id in v3.0
1021
1044
  message_text: string | null;
1022
1045
  reference_list: ChatReferenceItem[] | null;
1023
1046
  read_at: string | null;
@@ -1025,7 +1048,7 @@ interface ChatMessage {
1025
1048
  created_at: string;
1026
1049
  changed_at: string;
1027
1050
  sender_profile?: HazoUserProfile;
1028
- receiver_profile?: HazoUserProfile;
1051
+ // receiver_profile removed in v3.0
1029
1052
  is_sender: boolean;
1030
1053
  send_status?: 'sending' | 'sent' | 'failed';
1031
1054
  }
@@ -1059,32 +1082,204 @@ interface ChatReferenceItem {
1059
1082
 
1060
1083
  **Note on MIME Type:** The `mime_type` property is optional. If not provided, the component will automatically infer the MIME type from the file extension (e.g., `.jpg` → `image/jpeg`, `.pdf` → `application/pdf`). This ensures document preview works even when `mime_type` is not explicitly set. Supported file extensions for inference include: `pdf`, `png`, `jpg`, `jpeg`, `gif`, `webp`, `txt`, `doc`, `docx`.
1061
1084
 
1085
+ ### ChatGroup (NEW in v3.0, UPDATED in v3.1)
1086
+
1087
+ ```typescript
1088
+ interface ChatGroup {
1089
+ id: string;
1090
+ client_user_id: string | null | undefined; // NULLABLE in v3.1 (only required for 'support' groups)
1091
+ group_type: ChatGroupType; // NEW in v3.1
1092
+ name?: string | null;
1093
+ created_at: string;
1094
+ changed_at: string;
1095
+ }
1096
+ ```
1097
+
1098
+ ### ChatGroupType (NEW in v3.1)
1099
+
1100
+ ```typescript
1101
+ type ChatGroupType = 'support' | 'peer' | 'group';
1102
+ ```
1103
+
1104
+ **Group Type Definitions:**
1105
+ - `'support'`: Client-to-staff support conversation (requires `client_user_id`)
1106
+ - `'peer'`: Peer-to-peer direct message between two users
1107
+ - `'group'`: Multi-user group conversation
1108
+
1109
+ ### ChatGroupUser (NEW in v3.0, UPDATED in v3.1)
1110
+
1111
+ ```typescript
1112
+ interface ChatGroupUser {
1113
+ chat_group_id: string;
1114
+ user_id: string;
1115
+ role: ChatGroupUserRole; // EXPANDED in v3.1
1116
+ created_at: string;
1117
+ changed_at: string;
1118
+ }
1119
+ ```
1120
+
1121
+ ### ChatGroupUserRole (NEW in v3.0, EXPANDED in v3.1)
1122
+
1123
+ ```typescript
1124
+ // v3.0
1125
+ type ChatGroupUserRole = 'client' | 'staff';
1126
+
1127
+ // v3.1 (expanded)
1128
+ type ChatGroupUserRole = 'client' | 'staff' | 'owner' | 'admin' | 'member';
1129
+ ```
1130
+
1131
+ **Role Definitions:**
1132
+ - `'client'`: Customer/end-user in support scenarios
1133
+ - `'staff'`: Support personnel in support scenarios
1134
+ - `'owner'`: Creator/owner of peer or group chats
1135
+ - `'admin'`: Delegated administrator in group chats
1136
+ - `'member'`: Standard participant in peer or group chats
1137
+
1138
+ ### ChatGroupWithMembers (NEW in v3.0, UPDATED in v3.1)
1139
+
1140
+ ```typescript
1141
+ interface ChatGroupWithMembers extends ChatGroup {
1142
+ members: (ChatGroupUser & { profile?: HazoUserProfile })[];
1143
+ owner_profile?: HazoUserProfile; // NEW in v3.1 - profile of the group owner
1144
+ }
1145
+ ```
1146
+
1062
1147
  ## Database Schema
1063
1148
 
1064
- ### hazo_chat Table
1149
+ **Breaking Changes in v3.0:** The database schema has been updated to support group-based chat. Migration required.
1150
+
1151
+ **New in v3.1:** Generic schema supporting multiple chat patterns - support, peer, and group conversations.
1152
+
1153
+ ### PostgreSQL Enum Types (RECOMMENDED)
1154
+
1155
+ For PostgreSQL installations, create these custom enum types for type safety and consistency:
1156
+
1157
+ ```sql
1158
+ -- Reference type for chat contexts
1159
+ CREATE TYPE hazo_enum_chat_type AS ENUM ('chat', 'field', 'project', 'support', 'general');
1160
+
1161
+ -- Group type for conversation patterns (v3.1)
1162
+ CREATE TYPE hazo_enum_group_type AS ENUM ('support', 'peer', 'group');
1163
+
1164
+ -- Membership roles (v3.1)
1165
+ CREATE TYPE hazo_enum_group_role AS ENUM ('client', 'staff', 'owner', 'admin', 'member');
1166
+ ```
1167
+
1168
+ ### Group Types
1169
+
1170
+ hazo_chat supports three distinct group types to accommodate different conversation patterns:
1171
+
1172
+ | Type | Description | Use Case | `client_user_id` | Typical Roles |
1173
+ |------|-------------|----------|------------------|---------------|
1174
+ | **support** | Client-to-staff support conversation | Customer support, helpdesk | Required (identifies the client) | 'client', 'staff' |
1175
+ | **peer** | Peer-to-peer direct message (1:1) | Direct messaging between equals | Not used (null) | 'owner', 'member' |
1176
+ | **group** | Multi-user group conversation | Team collaboration, channels | Not used (null) | 'owner', 'admin', 'member' |
1177
+
1178
+ ### Role Types
1179
+
1180
+ The `role` field in `hazo_chat_group_users` defines each member's permissions and relationship to the group:
1181
+
1182
+ | Role | Description | Used In | Capabilities |
1183
+ |------|-------------|---------|--------------|
1184
+ | **client** | Customer/end-user receiving support | support groups | Send messages, view messages |
1185
+ | **staff** | Support personnel helping clients | support groups | Send messages, view messages, assist client |
1186
+ | **owner** | Creator/owner of the conversation | peer, group | Full control, can add/remove members |
1187
+ | **admin** | Delegated administrator | group | Can manage members, moderate content |
1188
+ | **member** | Standard participant | peer, group | Send messages, view messages |
1189
+
1190
+ ### hazo_chat_group Table (UPDATED in v3.1)
1191
+
1192
+ ```sql
1193
+ -- PostgreSQL with enums (RECOMMENDED)
1194
+ CREATE TABLE hazo_chat_group (
1195
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
1196
+ client_user_id UUID REFERENCES hazo_users(id), -- NULLABLE in v3.1
1197
+ group_type hazo_enum_group_type NOT NULL DEFAULT 'support', -- NEW in v3.1
1198
+ name VARCHAR(255),
1199
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1200
+ changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
1201
+ );
1202
+
1203
+ -- PostgreSQL without enums (alternative)
1204
+ CREATE TABLE hazo_chat_group (
1205
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
1206
+ client_user_id UUID REFERENCES hazo_users(id), -- NULLABLE in v3.1
1207
+ group_type VARCHAR(20) NOT NULL DEFAULT 'support' CHECK (group_type IN ('support', 'peer', 'group')), -- NEW in v3.1
1208
+ name VARCHAR(255),
1209
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1210
+ changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
1211
+ );
1212
+
1213
+ CREATE INDEX idx_hazo_chat_group_client ON hazo_chat_group(client_user_id);
1214
+ CREATE INDEX idx_hazo_chat_group_type ON hazo_chat_group(group_type); -- NEW in v3.1
1215
+ ```
1216
+
1217
+ **Column Changes in v3.1:**
1218
+ - `client_user_id`: Changed from `NOT NULL` to nullable - only required for 'support' type groups
1219
+ - `group_type`: NEW field - defines the conversation pattern ('support', 'peer', 'group')
1220
+
1221
+ ### hazo_chat_group_users Table (UPDATED in v3.1)
1222
+
1223
+ ```sql
1224
+ -- PostgreSQL with enums (RECOMMENDED)
1225
+ CREATE TABLE hazo_chat_group_users (
1226
+ chat_group_id UUID NOT NULL REFERENCES hazo_chat_group(id) ON DELETE CASCADE,
1227
+ user_id UUID NOT NULL REFERENCES hazo_users(id) ON DELETE CASCADE,
1228
+ role hazo_enum_group_role NOT NULL, -- EXPANDED in v3.1
1229
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1230
+ changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1231
+ PRIMARY KEY (chat_group_id, user_id)
1232
+ );
1233
+
1234
+ -- PostgreSQL without enums (alternative)
1235
+ CREATE TABLE hazo_chat_group_users (
1236
+ chat_group_id UUID NOT NULL REFERENCES hazo_chat_group(id) ON DELETE CASCADE,
1237
+ user_id UUID NOT NULL REFERENCES hazo_users(id) ON DELETE CASCADE,
1238
+ role VARCHAR(20) NOT NULL CHECK (role IN ('client', 'staff', 'owner', 'admin', 'member')), -- EXPANDED in v3.1
1239
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1240
+ changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1241
+ PRIMARY KEY (chat_group_id, user_id)
1242
+ );
1243
+
1244
+ CREATE INDEX idx_hazo_chat_group_users_user ON hazo_chat_group_users(user_id);
1245
+ CREATE INDEX idx_hazo_chat_group_users_group ON hazo_chat_group_users(chat_group_id);
1246
+ CREATE INDEX idx_hazo_chat_group_users_role ON hazo_chat_group_users(role); -- NEW in v3.1
1247
+ ```
1248
+
1249
+ **Column Changes in v3.1:**
1250
+ - `role`: EXPANDED from ('client', 'staff') to ('client', 'staff', 'owner', 'admin', 'member')
1251
+
1252
+ ### hazo_chat Table (MODIFIED in v3.0)
1065
1253
 
1066
1254
  ```sql
1255
+ -- PostgreSQL
1067
1256
  CREATE TABLE hazo_chat (
1068
- id TEXT PRIMARY KEY,
1069
- reference_id TEXT NOT NULL,
1257
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
1258
+ reference_id UUID NOT NULL,
1070
1259
  reference_type TEXT DEFAULT 'chat',
1071
- sender_user_id TEXT NOT NULL,
1072
- receiver_user_id TEXT NOT NULL,
1260
+ sender_user_id UUID NOT NULL REFERENCES hazo_users(id),
1261
+ chat_group_id UUID NOT NULL REFERENCES hazo_chat_group(id), -- CHANGED from receiver_user_id
1073
1262
  message_text TEXT,
1074
- reference_list TEXT, -- JSON array of ChatReferenceItem
1075
- read_at TEXT,
1076
- deleted_at TEXT,
1077
- created_at TEXT NOT NULL,
1078
- changed_at TEXT NOT NULL
1263
+ reference_list JSONB, -- JSON array of ChatReferenceItem
1264
+ read_at TIMESTAMPTZ,
1265
+ deleted_at TIMESTAMPTZ,
1266
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1267
+ changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
1079
1268
  );
1080
1269
 
1081
1270
  -- Indexes for performance
1082
1271
  CREATE INDEX idx_hazo_chat_reference_id ON hazo_chat(reference_id);
1083
1272
  CREATE INDEX idx_hazo_chat_sender ON hazo_chat(sender_user_id);
1084
- CREATE INDEX idx_hazo_chat_receiver ON hazo_chat(receiver_user_id);
1085
- CREATE INDEX idx_hazo_chat_created ON hazo_chat(created_at);
1273
+ CREATE INDEX idx_hazo_chat_group ON hazo_chat(chat_group_id); -- CHANGED from receiver index
1274
+ CREATE INDEX idx_hazo_chat_created ON hazo_chat(created_at DESC);
1086
1275
  ```
1087
1276
 
1277
+ **Migration Notes:**
1278
+ - Rename column: `receiver_user_id` → `chat_group_id`
1279
+ - Create new tables: `hazo_chat_group`, `hazo_chat_group_users`
1280
+ - Migrate existing 1-1 chats to groups with both users as members
1281
+ - Update foreign key constraints
1282
+
1088
1283
  ## UI Behavior & Responsive Design
1089
1284
 
1090
1285
  ### Hamburger Menu Button
@@ -1148,56 +1343,261 @@ max_file_size_mb = 10
1148
1343
  allowed_types = pdf,png,jpg,jpeg,gif,txt,doc,docx
1149
1344
  ```
1150
1345
 
1151
- ## Migration from v1.x
1346
+ ## Migration from v3.0 to v3.1
1347
+
1348
+ **Version 3.1** introduces schema changes to support multiple chat patterns (support, peer, group) while maintaining backward compatibility.
1349
+
1350
+ ### What Changed in v3.1
1351
+
1352
+ | v3.0 | v3.1 |
1353
+ |------|------|
1354
+ | Fixed support pattern only | Three patterns: support, peer, group |
1355
+ | `client_user_id` always required | `client_user_id` nullable (only for support groups) |
1356
+ | 2 roles: 'client', 'staff' | 5 roles: 'client', 'staff', 'owner', 'admin', 'member' |
1357
+ | No `group_type` field | NEW `group_type` field ('support', 'peer', 'group') |
1358
+
1359
+ ### Migration Steps (v3.0 to v3.1)
1360
+
1361
+ **Important:** v3.1 is backward compatible with v3.0 schema. Existing support-type groups continue to work without changes.
1362
+
1363
+ 1. **Create PostgreSQL Enum Types (Optional but Recommended):**
1364
+
1365
+ ```sql
1366
+ -- Reference type for chat contexts
1367
+ CREATE TYPE hazo_enum_chat_type AS ENUM ('chat', 'field', 'project', 'support', 'general');
1368
+
1369
+ -- Group type for conversation patterns
1370
+ CREATE TYPE hazo_enum_group_type AS ENUM ('support', 'peer', 'group');
1371
+
1372
+ -- Membership roles
1373
+ CREATE TYPE hazo_enum_group_role AS ENUM ('client', 'staff', 'owner', 'admin', 'member');
1374
+ ```
1375
+
1376
+ 2. **Add group_type Column to hazo_chat_group:**
1377
+
1378
+ ```sql
1379
+ -- Add group_type column (defaults to 'support' for backward compatibility)
1380
+ ALTER TABLE hazo_chat_group
1381
+ ADD COLUMN group_type VARCHAR(20) NOT NULL DEFAULT 'support'
1382
+ CHECK (group_type IN ('support', 'peer', 'group'));
1383
+
1384
+ -- Or with enum type (if you created the enum):
1385
+ ALTER TABLE hazo_chat_group
1386
+ ADD COLUMN group_type hazo_enum_group_type NOT NULL DEFAULT 'support';
1387
+
1388
+ -- Add index for group_type
1389
+ CREATE INDEX idx_hazo_chat_group_type ON hazo_chat_group(group_type);
1390
+ ```
1391
+
1392
+ 3. **Make client_user_id Nullable:**
1393
+
1394
+ ```sql
1395
+ -- Remove NOT NULL constraint from client_user_id
1396
+ ALTER TABLE hazo_chat_group
1397
+ ALTER COLUMN client_user_id DROP NOT NULL;
1398
+ ```
1399
+
1400
+ 4. **Update Role Constraint in hazo_chat_group_users:**
1401
+
1402
+ ```sql
1403
+ -- Drop old constraint
1404
+ ALTER TABLE hazo_chat_group_users
1405
+ DROP CONSTRAINT IF EXISTS hazo_chat_group_users_role_check;
1406
+
1407
+ -- Add new constraint with expanded roles
1408
+ ALTER TABLE hazo_chat_group_users
1409
+ ADD CONSTRAINT hazo_chat_group_users_role_check
1410
+ CHECK (role IN ('client', 'staff', 'owner', 'admin', 'member'));
1411
+
1412
+ -- Or with enum type (if you created the enum):
1413
+ ALTER TABLE hazo_chat_group_users
1414
+ ALTER COLUMN role TYPE hazo_enum_group_role
1415
+ USING role::hazo_enum_group_role;
1416
+
1417
+ -- Add index for role
1418
+ CREATE INDEX idx_hazo_chat_group_users_role ON hazo_chat_group_users(role);
1419
+ ```
1420
+
1421
+ 5. **Verify Existing Data:**
1422
+
1423
+ ```sql
1424
+ -- All existing groups should have group_type = 'support'
1425
+ SELECT group_type, COUNT(*)
1426
+ FROM hazo_chat_group
1427
+ GROUP BY group_type;
1428
+
1429
+ -- All existing members should have role IN ('client', 'staff')
1430
+ SELECT role, COUNT(*)
1431
+ FROM hazo_chat_group_users
1432
+ GROUP BY role;
1433
+ ```
1434
+
1435
+ ### Creating New Group Types
1436
+
1437
+ After migration, you can create peer and group conversations:
1438
+
1439
+ ```typescript
1440
+ // Support group (v3.0 style - still works)
1441
+ await db.insert_with_result('hazo_chat_group', {
1442
+ client_user_id: 'client-uuid',
1443
+ group_type: 'support',
1444
+ name: 'Customer Support'
1445
+ });
1446
+
1447
+ // Peer-to-peer chat (v3.1)
1448
+ await db.insert_with_result('hazo_chat_group', {
1449
+ client_user_id: null, // Not used for peer chats
1450
+ group_type: 'peer',
1451
+ name: 'Alice & Bob'
1452
+ });
1453
+
1454
+ // Group conversation (v3.1)
1455
+ await db.insert_with_result('hazo_chat_group', {
1456
+ client_user_id: null, // Not used for group chats
1457
+ group_type: 'group',
1458
+ name: 'Project Team'
1459
+ });
1460
+ ```
1461
+
1462
+ ### No Code Changes Required
1463
+
1464
+ The v3.1 schema changes are transparent to the component API. Your existing code continues to work:
1465
+
1466
+ ```tsx
1467
+ // This works for all group types (support, peer, group)
1468
+ <HazoChat
1469
+ chat_group_id="group-123"
1470
+ reference_id="chat-456"
1471
+ reference_type="support"
1472
+ />
1473
+ ```
1474
+
1475
+ ### TypeScript Type Updates
1476
+
1477
+ If you use TypeScript types from hazo_chat:
1478
+
1479
+ ```typescript
1480
+ // v3.1 types (automatically available after updating package)
1481
+ import type {
1482
+ ChatGroup, // client_user_id is now optional
1483
+ ChatGroupType, // 'support' | 'peer' | 'group'
1484
+ ChatGroupUserRole // 'client' | 'staff' | 'owner' | 'admin' | 'member'
1485
+ } from 'hazo_chat';
1486
+
1487
+ const group: ChatGroup = {
1488
+ id: 'group-123',
1489
+ client_user_id: null, // ✅ Now optional
1490
+ group_type: 'peer', // ✅ New field
1491
+ name: 'Chat',
1492
+ created_at: new Date().toISOString(),
1493
+ changed_at: new Date().toISOString()
1494
+ };
1495
+ ```
1496
+
1497
+ ## Migration from v2.x to v3.0
1152
1498
 
1153
- Version 2.0 introduces breaking changes for a simpler, more reliable architecture.
1499
+ **Version 3.0** introduces breaking changes to support group-based chat architecture.
1154
1500
 
1155
1501
  ### What Changed
1156
1502
 
1157
- | v1.x | v2.x |
1503
+ | v2.x | v3.0 |
1158
1504
  |------|------|
1159
- | Pass `hazo_connect` prop | Not needed - uses API calls |
1160
- | Pass `hazo_auth` prop | Not needed - uses API calls |
1161
- | Pass `document_save_location` prop | Not needed |
1162
- | Direct database access | API-based data access |
1505
+ | `receiver_user_id` prop | `chat_group_id` prop |
1506
+ | 1-1 chat only | Group-based chat with multiple participants |
1507
+ | `receiver_user_id` in API calls | `chat_group_id` in API calls |
1508
+ | Single `hazo_chat` table | Three tables: `hazo_chat_group`, `hazo_chat_group_users`, `hazo_chat` (modified) |
1509
+ | Unread count by `reference_id` | Unread count by `chat_group_id` |
1510
+ | `ChatMessage.receiver_profile` field | Field removed |
1163
1511
 
1164
1512
  ### Migration Steps
1165
1513
 
1166
- 1. **Remove adapter props:**
1514
+ 1. **Database Migration:**
1515
+
1516
+ ```sql
1517
+ -- Step 1: Create new tables
1518
+ CREATE TABLE hazo_chat_group (...); -- See Database Schema section
1519
+ CREATE TABLE hazo_chat_group_users (...);
1520
+
1521
+ -- Step 2: Migrate existing data (example)
1522
+ -- For each unique sender-receiver pair, create a chat group
1523
+ INSERT INTO hazo_chat_group (id, client_user_id, name)
1524
+ SELECT
1525
+ gen_random_uuid(),
1526
+ receiver_user_id,
1527
+ 'Migrated Chat ' || sender_user_id
1528
+ FROM hazo_chat
1529
+ GROUP BY sender_user_id, receiver_user_id;
1530
+
1531
+ -- Step 3: Rename column
1532
+ ALTER TABLE hazo_chat RENAME COLUMN receiver_user_id TO chat_group_id;
1533
+
1534
+ -- Step 4: Update foreign keys
1535
+ ALTER TABLE hazo_chat
1536
+ ADD CONSTRAINT fk_chat_group
1537
+ FOREIGN KEY (chat_group_id)
1538
+ REFERENCES hazo_chat_group(id);
1539
+ ```
1540
+
1541
+ 2. **Update Component Props:**
1167
1542
 
1168
1543
  ```tsx
1169
- // Before (v1.x)
1544
+ // Before (v2.x)
1170
1545
  <HazoChat
1171
- hazo_connect={adapter}
1172
- hazo_auth={authService}
1173
- document_save_location="/uploads"
1174
- receiver_user_id="..."
1546
+ receiver_user_id="user-123"
1547
+ reference_id="chat-456"
1175
1548
  />
1176
1549
 
1177
- // After (v2.x)
1550
+ // After (v3.0)
1178
1551
  <HazoChat
1179
- receiver_user_id="..."
1552
+ chat_group_id="group-123"
1553
+ reference_id="chat-456"
1180
1554
  />
1181
1555
  ```
1182
1556
 
1183
- 2. **Create API routes** (see [API Routes Setup](#api-routes-setup))
1557
+ 3. **Update API Calls:**
1558
+
1559
+ ```typescript
1560
+ // Before (v2.x)
1561
+ const response = await fetch(
1562
+ `/api/hazo_chat/messages?receiver_user_id=${userId}`
1563
+ );
1564
+
1565
+ // After (v3.0)
1566
+ const response = await fetch(
1567
+ `/api/hazo_chat/messages?chat_group_id=${groupId}`
1568
+ );
1569
+ ```
1184
1570
 
1185
- 3. **Update imports:**
1571
+ 4. **Update Type References:**
1186
1572
 
1187
1573
  ```typescript
1188
- // The API handler factory is new
1189
- import { createMessagesHandler } from 'hazo_chat/api';
1574
+ // Remove receiver_profile usage
1575
+ // message.receiver_profile removed
1576
+
1577
+ // Update CreateMessagePayload
1578
+ // receiver_user_id → chat_group_id
1190
1579
  ```
1191
1580
 
1192
1581
  ### Why the Change?
1193
1582
 
1194
- The v1.x architecture required passing server-side adapters (`hazo_connect`, `hazo_auth`) to client components. This caused issues:
1583
+ The v2.x architecture supported only 1-1 communication. This was limiting for scenarios where:
1584
+
1585
+ - Multiple support staff need to rotate on a single client chat
1586
+ - Team collaboration is required within a chat context
1587
+ - Client needs to see all staff responses in one unified thread
1588
+
1589
+ The v3.0 group-based architecture solves these by:
1590
+
1591
+ - Supporting multiple users in a single chat group
1592
+ - Role-based access (client vs staff)
1593
+ - Unified message history for all group members
1594
+ - Better scalability for team-based support scenarios
1595
+
1596
+ ## Migration from v1.x to v2.0
1195
1597
 
1196
- - "Module not found: Can't resolve 'fs'" errors
1197
- - Hydration mismatches
1198
- - Complex webpack configuration
1598
+ Version 2.0 introduced API-first architecture (no server-side dependencies in client components).
1199
1599
 
1200
- The v2.x API-first architecture solves these by keeping all server code in API routes.
1600
+ See [v2.0 release notes](https://github.com/pub12/hazo_chat/releases/tag/v2.0.0) for details.
1201
1601
 
1202
1602
  ## Development
1203
1603