hazo_chat 2.0.16 → 2.1.1

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.
Files changed (32) hide show
  1. package/README.md +60 -8
  2. package/SETUP_CHECKLIST.md +266 -10
  3. package/dist/api/index.d.ts +17 -2
  4. package/dist/api/index.d.ts.map +1 -1
  5. package/dist/api/index.js +16 -1
  6. package/dist/api/index.js.map +1 -1
  7. package/dist/api/messages.d.ts +52 -1
  8. package/dist/api/messages.d.ts.map +1 -1
  9. package/dist/api/messages.js +380 -22
  10. package/dist/api/messages.js.map +1 -1
  11. package/dist/api/types.d.ts +25 -0
  12. package/dist/api/types.d.ts.map +1 -1
  13. package/dist/components/hazo_chat/hazo_chat.d.ts.map +1 -1
  14. package/dist/components/hazo_chat/hazo_chat.js +18 -10
  15. package/dist/components/hazo_chat/hazo_chat.js.map +1 -1
  16. package/dist/components/hazo_chat/hazo_chat_header.d.ts.map +1 -1
  17. package/dist/components/hazo_chat/hazo_chat_header.js +22 -1
  18. package/dist/components/hazo_chat/hazo_chat_header.js.map +1 -1
  19. package/dist/components/hazo_chat/hazo_chat_messages.d.ts +5 -4
  20. package/dist/components/hazo_chat/hazo_chat_messages.d.ts.map +1 -1
  21. package/dist/components/hazo_chat/hazo_chat_messages.js +153 -7
  22. package/dist/components/hazo_chat/hazo_chat_messages.js.map +1 -1
  23. package/dist/components/ui/chat_bubble.d.ts.map +1 -1
  24. package/dist/components/ui/chat_bubble.js +28 -5
  25. package/dist/components/ui/chat_bubble.js.map +1 -1
  26. package/dist/hooks/use_chat_messages.d.ts +3 -3
  27. package/dist/hooks/use_chat_messages.d.ts.map +1 -1
  28. package/dist/hooks/use_chat_messages.js +259 -136
  29. package/dist/hooks/use_chat_messages.js.map +1 -1
  30. package/dist/types/index.d.ts +108 -2
  31. package/dist/types/index.d.ts.map +1 -1
  32. package/package.json +1 -1
package/README.md CHANGED
@@ -12,7 +12,7 @@ A full-featured React chat component library for 1-1 communication with document
12
12
  - 📄 **Document Viewer** - Built-in PDF and image viewer with expand/collapse toggle, download, and open in new tab actions
13
13
  - 👤 **User Profiles** - Avatar display and user information
14
14
  - 🔄 **Infinite Scroll** - Cursor-based pagination for message history
15
- - ✅ **Read Receipts** - Track message read status
15
+ - ✅ **Read Receipts** - Automatic mark-as-read when messages become visible using Intersection Observer
16
16
  - 🗑️ **Soft Delete** - Delete messages with undo capability
17
17
  - 🎨 **Customizable** - TailwindCSS-based theming
18
18
  - 🚀 **API-First** - No server-side dependencies in client components
@@ -242,19 +242,28 @@ This section defines the visual design standards and component behavior specific
242
242
 
243
243
  **Message Timestamp Display:**
244
244
  - Location: Chat bubble footer (`ChatBubble`)
245
- - Only timestamp is displayed (no status icons for sent/unread messages)
246
245
  - Font size: `text-xs`
247
246
  - Color: `text-muted-foreground`
248
- - Format: 24-hour format (e.g., "10:37 AM", "15:51")
247
+ - Time format: 24-hour format (e.g., "10:37", "15:51")
248
+ - Date prefix: Messages before today show date in `dd/MMM` format (e.g., "02/Dec 10:37")
249
249
  - Timezone: Respects `timezone` prop (default: "GMT+10")
250
250
 
251
- **Read Receipt Indicator:**
251
+ **Message Status Indicators (Sender's Messages Only):**
252
252
  - Location: Chat bubble footer, after timestamp
253
- - Icon: `IoCheckmarkDoneSharp` (from `react-icons/io5`)
254
- - Size: `h-4 w-4` (16px × 16px)
255
- - Color: `text-green-500`
256
- - Display condition: Only shown when `read_at` is not null AND message is from sender
257
253
  - Position: After timestamp with `gap-1` spacing
254
+ - Size: `h-4 w-4` (16px × 16px)
255
+
256
+ **Sent Indicator (Grey Single Check):**
257
+ - Icon: `IoCheckmark` (from `react-icons/io5`)
258
+ - Color: `text-muted-foreground` (grey)
259
+ - Display condition: Shown when message is sent but `read_at` is null
260
+ - Meaning: Message delivered but not yet read by recipient
261
+
262
+ **Read Receipt (Green Double Check):**
263
+ - Icon: `IoCheckmarkDoneSharp` (from `react-icons/io5`)
264
+ - Color: `text-green-500` (green)
265
+ - Display condition: Only shown when `read_at` is not null
266
+ - Meaning: Message has been read by recipient
258
267
 
259
268
  #### Component Behavior
260
269
 
@@ -293,6 +302,20 @@ This section defines the visual design standards and component behavior specific
293
302
  - Indicator: Chevron icon (`IoChevronDown` when collapsed, `IoChevronUp` when expanded)
294
303
  - Default state: Collapsed when no references, expanded when references exist
295
304
 
305
+ **Automatic Mark-as-Read:**
306
+ - Detection: Uses Intersection Observer API to detect when messages become visible
307
+ - Trigger threshold: Messages marked as read when 50% visible in the ScrollArea viewport
308
+ - Scope: Only marks messages where the current user is the receiver (not the sender)
309
+ - State tracking: Prevents duplicate marking using in-memory Set
310
+ - API endpoint: Requires `PATCH /api/hazo_chat/messages/[id]/read` route
311
+ - Visual indicator: Green double-checkmark (IoCheckmarkDoneSharp) appears after timestamp
312
+ - Automatic: No user action required - messages are marked as read automatically when scrolled into view
313
+
314
+ **Note:** The mark-as-read functionality requires:
315
+ 1. The `HazoChat` component (includes all hooks and logic)
316
+ 2. The API route for marking messages as read (see API Routes Setup below)
317
+ 3. Messages must be received by the current user (messages sent by current user are not marked)
318
+
296
319
  #### Layout Standards
297
320
 
298
321
  **Chat Input Area:**
@@ -476,6 +499,7 @@ hazo_chat requires these API endpoints:
476
499
  |----------|--------|-------------|
477
500
  | `/api/hazo_chat/messages` | GET | Fetch chat messages |
478
501
  | `/api/hazo_chat/messages` | POST | Send a new message |
502
+ | `/api/hazo_chat/messages/[id]/read` | PATCH | Mark a message as read (automatic) |
479
503
  | `/api/hazo_chat/unread_count` | GET | Get unread message counts by reference_id (optional) |
480
504
  | `/api/hazo_auth/me` | GET | Get current authenticated user |
481
505
  | `/api/hazo_auth/profiles` | POST | Fetch user profiles by IDs |
@@ -501,6 +525,34 @@ const { GET, POST } = createMessagesHandler({
501
525
  export { GET, POST };
502
526
  ```
503
527
 
528
+ ```typescript
529
+ // app/api/hazo_chat/messages/[id]/read/route.ts
530
+ import { NextRequest } from 'next/server';
531
+ import { createMarkAsReadHandler } from 'hazo_chat/api';
532
+ import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
533
+
534
+ export const dynamic = 'force-dynamic';
535
+
536
+ const { PATCH } = createMarkAsReadHandler({
537
+ getHazoConnect: () => getHazoConnectSingleton(),
538
+ // Optional: custom authentication
539
+ getUserIdFromRequest: async (request) => {
540
+ // Return user ID from your auth system
541
+ return request.cookies.get('user_id')?.value || null;
542
+ }
543
+ });
544
+
545
+ // Wrapper to handle Next.js App Router params
546
+ async function handlePATCH(
547
+ request: NextRequest,
548
+ context: { params: { id: string } | Promise<{ id: string }> }
549
+ ) {
550
+ return PATCH(request, context);
551
+ }
552
+
553
+ export { handlePATCH as PATCH };
554
+ ```
555
+
504
556
  ### Custom Implementation
505
557
 
506
558
  If you need more control, implement the endpoints manually. See [SETUP_CHECKLIST.md](./SETUP_CHECKLIST.md) for detailed examples.
@@ -56,12 +56,141 @@ npm install react react-dom next
56
56
 
57
57
  ## 3. Database Setup
58
58
 
59
- ### Step 3.1: Create hazo_chat Table
59
+ ### Step 3.1: PostgreSQL Setup (Recommended)
60
60
 
61
- Run this SQL to create the chat messages table:
61
+ For PostgreSQL databases, follow these steps to create the schema with UUID types, enums, and proper defaults.
62
+
63
+ #### Step 3.1.1: Enable UUID Extension
64
+
65
+ First, enable the UUID extension for generating UUIDs:
66
+
67
+ ```sql
68
+ -- Enable UUID extension (required for gen_random_uuid())
69
+ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
70
+ -- OR for PostgreSQL 13+, use pgcrypto extension
71
+ CREATE EXTENSION IF NOT EXISTS "pgcrypto";
72
+ ```
73
+
74
+ #### Step 3.1.2: Create Enum Types (Optional but Recommended)
75
+
76
+ Create enum types for reference types to ensure data integrity:
77
+
78
+ ```sql
79
+ -- Create enum for reference types
80
+ CREATE TYPE hazo_enum_chat_type AS ENUM ('chat', 'field', 'project', 'support', 'general');
81
+
82
+ -- Note: If you need to add more enum values later, use:
83
+ -- ALTER TYPE hazo_chat_reference_type ADD VALUE 'new_type';
84
+ ```
85
+
86
+ #### Step 3.1.3: Create hazo_chat Table
87
+
88
+ Create the chat messages table with UUID types and proper defaults:
89
+
90
+ ```sql
91
+ -- hazo_chat table for storing chat messages (PostgreSQL)
92
+ CREATE TABLE IF NOT EXISTS hazo_chat (
93
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
94
+ reference_id UUID NOT NULL,
95
+ reference_type hazo_enum_chat_type DEFAULT 'chat' NOT NULL,
96
+ sender_user_id UUID NOT NULL,
97
+ receiver_user_id UUID NOT NULL,
98
+ message_text TEXT,
99
+ reference_list JSONB,
100
+ read_at TIMESTAMPTZ,
101
+ deleted_at TIMESTAMPTZ,
102
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
103
+ changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
104
+ );
105
+
106
+ -- Performance indexes
107
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_reference_id ON hazo_chat(reference_id);
108
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_sender ON hazo_chat(sender_user_id);
109
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_receiver ON hazo_chat(receiver_user_id);
110
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_created ON hazo_chat(created_at DESC);
111
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_reference_type ON hazo_chat(reference_type);
112
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_read_at ON hazo_chat(read_at) WHERE read_at IS NOT NULL;
113
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_deleted_at ON hazo_chat(deleted_at) WHERE deleted_at IS NOT NULL;
114
+
115
+ -- Composite index for common query pattern
116
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_receiver_reference ON hazo_chat(receiver_user_id, reference_id);
117
+ ```
118
+
119
+ **Alternative: Without Enum Type (More Flexible)**
120
+
121
+ If you prefer flexibility over strict enum validation:
62
122
 
63
123
  ```sql
64
- -- hazo_chat table for storing chat messages
124
+ -- hazo_chat table with TEXT reference_type (more flexible)
125
+ CREATE TABLE IF NOT EXISTS hazo_chat (
126
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
127
+ reference_id UUID NOT NULL,
128
+ reference_type TEXT DEFAULT 'chat' NOT NULL,
129
+ sender_user_id UUID NOT NULL,
130
+ receiver_user_id UUID NOT NULL,
131
+ message_text TEXT,
132
+ reference_list JSONB,
133
+ read_at TIMESTAMPTZ,
134
+ deleted_at TIMESTAMPTZ,
135
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
136
+ changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
137
+ );
138
+
139
+ -- Performance indexes (same as above)
140
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_reference_id ON hazo_chat(reference_id);
141
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_sender ON hazo_chat(sender_user_id);
142
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_receiver ON hazo_chat(receiver_user_id);
143
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_created ON hazo_chat(created_at DESC);
144
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_reference_type ON hazo_chat(reference_type);
145
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_read_at ON hazo_chat(read_at) WHERE read_at IS NOT NULL;
146
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_deleted_at ON hazo_chat(deleted_at) WHERE deleted_at IS NOT NULL;
147
+ CREATE INDEX IF NOT EXISTS idx_hazo_chat_receiver_reference ON hazo_chat(receiver_user_id, reference_id);
148
+ ```
149
+
150
+ #### Step 3.1.4: Ensure Users Table Exists (PostgreSQL)
151
+
152
+ You need a users table with at least these fields:
153
+
154
+ ```sql
155
+ -- Users table for PostgreSQL
156
+ CREATE TABLE IF NOT EXISTS hazo_users (
157
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
158
+ email_address VARCHAR(255) UNIQUE NOT NULL,
159
+ name VARCHAR(255),
160
+ profile_picture_url TEXT,
161
+ is_active BOOLEAN DEFAULT TRUE NOT NULL,
162
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
163
+ changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
164
+ );
165
+
166
+ -- Indexes for users table
167
+ CREATE INDEX IF NOT EXISTS idx_hazo_users_email ON hazo_users(email_address);
168
+ CREATE INDEX IF NOT EXISTS idx_hazo_users_active ON hazo_users(is_active) WHERE is_active = TRUE;
169
+ ```
170
+
171
+ #### Step 3.1.5: Add Foreign Key Constraints (Optional)
172
+
173
+ Add foreign key constraints for referential integrity:
174
+
175
+ ```sql
176
+ -- Add foreign key constraints (optional but recommended)
177
+ ALTER TABLE hazo_chat
178
+ ADD CONSTRAINT fk_hazo_chat_sender
179
+ FOREIGN KEY (sender_user_id)
180
+ REFERENCES hazo_users(id)
181
+ ON DELETE RESTRICT,
182
+ ADD CONSTRAINT fk_hazo_chat_receiver
183
+ FOREIGN KEY (receiver_user_id)
184
+ REFERENCES hazo_users(id)
185
+ ON DELETE RESTRICT;
186
+ ```
187
+
188
+ ### Step 3.2: SQLite Setup (Alternative)
189
+
190
+ For SQLite databases (development/testing), use this simplified schema:
191
+
192
+ ```sql
193
+ -- hazo_chat table for storing chat messages (SQLite)
65
194
  CREATE TABLE IF NOT EXISTS hazo_chat (
66
195
  id TEXT PRIMARY KEY,
67
196
  reference_id TEXT NOT NULL,
@@ -72,8 +201,8 @@ CREATE TABLE IF NOT EXISTS hazo_chat (
72
201
  reference_list TEXT,
73
202
  read_at TEXT,
74
203
  deleted_at TEXT,
75
- created_at TEXT NOT NULL,
76
- changed_at TEXT NOT NULL
204
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
205
+ changed_at TEXT NOT NULL DEFAULT (datetime('now'))
77
206
  );
78
207
 
79
208
  -- Performance indexes
@@ -83,9 +212,7 @@ CREATE INDEX IF NOT EXISTS idx_hazo_chat_receiver ON hazo_chat(receiver_user_id)
83
212
  CREATE INDEX IF NOT EXISTS idx_hazo_chat_created ON hazo_chat(created_at DESC);
84
213
  ```
85
214
 
86
- ### Step 3.2: Ensure Users Table Exists
87
-
88
- You need a users table with at least these fields:
215
+ **SQLite Users Table:**
89
216
 
90
217
  ```sql
91
218
  CREATE TABLE IF NOT EXISTS hazo_users (
@@ -94,12 +221,91 @@ CREATE TABLE IF NOT EXISTS hazo_users (
94
221
  name TEXT,
95
222
  profile_picture_url TEXT,
96
223
  is_active INTEGER DEFAULT 1,
97
- created_at TEXT NOT NULL,
98
- changed_at TEXT NOT NULL
224
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
225
+ changed_at TEXT NOT NULL DEFAULT (datetime('now'))
99
226
  );
100
227
  ```
101
228
 
229
+ ### Step 3.3: Key Differences Between PostgreSQL and SQLite
230
+
231
+ | Feature | PostgreSQL | SQLite |
232
+ |---------|-----------|--------|
233
+ | **ID Type** | `UUID` with `gen_random_uuid()` default | `TEXT` (manual UUID generation) |
234
+ | **Boolean** | `BOOLEAN` type with `TRUE`/`FALSE` | `INTEGER` with `0`/`1` |
235
+ | **Timestamp** | `TIMESTAMPTZ` with `NOW()` default | `TEXT` with `datetime('now')` |
236
+ | **JSON** | `JSONB` type (binary JSON) | `TEXT` (JSON string) |
237
+ | **Enum** | Native `ENUM` type available | Not supported (use TEXT) |
238
+ | **Extensions** | Requires UUID extension | No extensions needed |
239
+
240
+ #### Step 3.3.1: Verify PostgreSQL Setup
241
+
242
+ Run these queries to verify your PostgreSQL setup:
243
+
244
+ ```sql
245
+ -- Verify UUID extension is enabled
246
+ SELECT extname, extversion FROM pg_extension WHERE extname IN ('uuid-ossp', 'pgcrypto');
247
+
248
+ -- Verify enum type exists (if using enum)
249
+ SELECT typname FROM pg_type WHERE typname = 'hazo_chat_reference_type';
250
+
251
+ -- Verify table structure
252
+ SELECT
253
+ column_name,
254
+ data_type,
255
+ column_default,
256
+ is_nullable
257
+ FROM information_schema.columns
258
+ WHERE table_name = 'hazo_chat'
259
+ ORDER BY ordinal_position;
260
+
261
+ -- Verify indexes
262
+ SELECT indexname, indexdef
263
+ FROM pg_indexes
264
+ WHERE tablename = 'hazo_chat';
265
+
266
+ -- Test UUID generation
267
+ SELECT gen_random_uuid() AS test_uuid;
268
+
269
+ -- Test table insert with defaults (replace UUIDs with valid user IDs)
270
+ INSERT INTO hazo_chat (
271
+ reference_id,
272
+ sender_user_id,
273
+ receiver_user_id,
274
+ message_text
275
+ ) VALUES (
276
+ gen_random_uuid(),
277
+ (SELECT id FROM hazo_users LIMIT 1), -- Use existing user ID
278
+ (SELECT id FROM hazo_users LIMIT 1), -- Use existing user ID
279
+ 'Test message'
280
+ );
281
+
282
+ -- Verify auto-generated values
283
+ SELECT
284
+ id,
285
+ created_at,
286
+ changed_at,
287
+ reference_type
288
+ FROM hazo_chat
289
+ WHERE message_text = 'Test message'
290
+ LIMIT 1;
291
+
292
+ -- Clean up test data
293
+ DELETE FROM hazo_chat WHERE message_text = 'Test message';
294
+ ```
295
+
102
296
  ### Verification
297
+
298
+ **PostgreSQL:**
299
+ - [ ] UUID extension enabled (`uuid-ossp` or `pgcrypto`)
300
+ - [ ] Enum type created (if using enum approach)
301
+ - [ ] `hazo_chat` table exists with UUID columns
302
+ - [ ] All indexes created successfully
303
+ - [ ] Users table exists with UUID primary key
304
+ - [ ] Can query: `SELECT * FROM hazo_chat LIMIT 1`
305
+ - [ ] UUID default generation works: `INSERT INTO hazo_chat (...) VALUES (...)` generates UUID automatically
306
+ - [ ] Timestamp defaults work: `created_at` and `changed_at` auto-populate
307
+
308
+ **SQLite:**
103
309
  - [ ] `hazo_chat` table exists in database
104
310
  - [ ] Users table exists with at least one user
105
311
  - [ ] Can query: `SELECT * FROM hazo_chat LIMIT 1`
@@ -132,6 +338,41 @@ const { GET, POST } = createMessagesHandler({
132
338
  export { GET, POST };
133
339
  ```
134
340
 
341
+ ### Step 4.1.5: Mark as Read API (Automatic Read Receipts)
342
+
343
+ **File: `src/app/api/hazo_chat/messages/[id]/read/route.ts`**
344
+
345
+ ```typescript
346
+ /**
347
+ * API route to mark a chat message as read
348
+ * Uses the exportable handler from hazo_chat package
349
+ * Supports PATCH for marking messages as read
350
+ */
351
+
352
+ export const dynamic = 'force-dynamic';
353
+
354
+ import { NextRequest } from 'next/server';
355
+ import { createMarkAsReadHandler } from 'hazo_chat/api';
356
+ import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
357
+
358
+ // Create handler using the exportable factory from hazo_chat
359
+ const { PATCH: patchHandler } = createMarkAsReadHandler({
360
+ getHazoConnect: () => getHazoConnectSingleton()
361
+ });
362
+
363
+ // Wrapper to handle Next.js App Router params
364
+ async function PATCH(
365
+ request: NextRequest,
366
+ context: { params: { id: string } | Promise<{ id: string }> }
367
+ ) {
368
+ return patchHandler(request, context);
369
+ }
370
+
371
+ export { PATCH };
372
+ ```
373
+
374
+ **Note:** This endpoint is called automatically by the `HazoChat` component when messages become visible in the viewport. It uses the Intersection Observer API to detect visibility and marks messages as read when they are at least 50% visible.
375
+
135
376
  ### Step 4.2: Auth Me API
136
377
 
137
378
  **File: `src/app/api/hazo_auth/me/route.ts`**
@@ -327,6 +568,7 @@ export async function GET(request: NextRequest) {
327
568
  | Endpoint | Method | File | Purpose |
328
569
  |----------|--------|------|---------|
329
570
  | `/api/hazo_chat/messages` | GET, POST | `api/hazo_chat/messages/route.ts` | Message CRUD |
571
+ | `/api/hazo_chat/messages/[id]/read` | PATCH | `api/hazo_chat/messages/[id]/read/route.ts` | Mark message as read (automatic) |
330
572
  | `/api/hazo_chat/unread_count` | GET | `api/hazo_chat/unread_count/route.ts` | Get unread counts (optional) |
331
573
  | `/api/hazo_auth/me` | GET | `api/hazo_auth/me/route.ts` | Get current user |
332
574
  | `/api/hazo_auth/profiles` | POST | `api/hazo_auth/profiles/route.ts` | Get user profiles |
@@ -336,6 +578,7 @@ export async function GET(request: NextRequest) {
336
578
  - [ ] `GET /api/hazo_auth/me` returns user data when logged in
337
579
  - [ ] `POST /api/hazo_auth/profiles` returns profiles for given IDs
338
580
  - [ ] `GET /api/hazo_chat/messages?receiver_user_id=xxx` works
581
+ - [ ] `PATCH /api/hazo_chat/messages/[message-id]/read` marks message as read
339
582
  - [ ] `GET /api/hazo_chat/unread_count?receiver_user_id=xxx` returns unread counts (if implemented)
340
583
 
341
584
  ---
@@ -880,6 +1123,19 @@ For detailed specifications, see the [UI Design Standards](#ui-design-standards)
880
1123
  - [ ] No TypeScript errors
881
1124
 
882
1125
  ### Database Verification
1126
+
1127
+ **PostgreSQL:**
1128
+ - [ ] UUID extension enabled (check with: `SELECT * FROM pg_extension WHERE extname = 'uuid-ossp' OR extname = 'pgcrypto';`)
1129
+ - [ ] Enum type created (if using): `SELECT typname FROM pg_type WHERE typname = 'hazo_chat_reference_type';`
1130
+ - [ ] `hazo_chat` table exists with correct column types
1131
+ - [ ] All indexes created (check with: `SELECT indexname FROM pg_indexes WHERE tablename = 'hazo_chat';`)
1132
+ - [ ] UUID default generation works: Test insert without providing ID
1133
+ - [ ] Timestamp defaults work: Test insert without providing timestamps
1134
+ - [ ] Boolean columns accept TRUE/FALSE values
1135
+ - [ ] Can insert and query messages
1136
+ - [ ] Foreign key constraints work (if enabled)
1137
+
1138
+ **SQLite:**
883
1139
  - [ ] `hazo_chat` table exists
884
1140
  - [ ] Users table exists with test users
885
1141
  - [ ] Can insert and query messages
@@ -18,9 +18,24 @@
18
18
  *
19
19
  * export { GET, POST };
20
20
  * ```
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // app/api/hazo_chat/messages/[id]/route.ts
25
+ * import { createDeleteHandler } from 'hazo_chat/api';
26
+ * import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
27
+ *
28
+ * export const dynamic = 'force-dynamic';
29
+ *
30
+ * const { DELETE } = createDeleteHandler({
31
+ * getHazoConnect: () => getHazoConnectSingleton()
32
+ * });
33
+ *
34
+ * export { DELETE };
35
+ * ```
21
36
  */
22
- export { createMessagesHandler } from './messages.js';
37
+ export { createMessagesHandler, createMarkAsReadHandler, createDeleteHandler, } from './messages.js';
23
38
  export { createUnreadCountFunction } from './unread_count.js';
24
- export type { MessagesHandlerOptions, ChatMessageInput, ChatMessageRecord } from './types.js';
39
+ export type { MessagesHandlerOptions, ChatMessageInput, ChatMessageRecord, ApiErrorResponse, ApiSuccessResponse, PaginationMeta, } from './types.js';
25
40
  export type { UnreadCountFunctionOptions, UnreadCountResult } from './unread_count.js';
26
41
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAG9D,YAAY,EACV,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,0BAA0B,EAC1B,iBAAiB,EAClB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAG9D,YAAY,EACV,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/api/index.js CHANGED
@@ -18,8 +18,23 @@
18
18
  *
19
19
  * export { GET, POST };
20
20
  * ```
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // app/api/hazo_chat/messages/[id]/route.ts
25
+ * import { createDeleteHandler } from 'hazo_chat/api';
26
+ * import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
27
+ *
28
+ * export const dynamic = 'force-dynamic';
29
+ *
30
+ * const { DELETE } = createDeleteHandler({
31
+ * getHazoConnect: () => getHazoConnectSingleton()
32
+ * });
33
+ *
34
+ * export { DELETE };
35
+ * ```
21
36
  */
22
37
  // Export handler factories
23
- export { createMessagesHandler } from './messages.js';
38
+ export { createMessagesHandler, createMarkAsReadHandler, createDeleteHandler, } from './messages.js';
24
39
  export { createUnreadCountFunction } from './unread_count.js';
25
40
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,2BAA2B;AAC3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,2BAA2B;AAC3B,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Messages API Handler Factory
3
3
  *
4
- * Creates GET and POST handlers for the /api/hazo_chat/messages endpoint.
4
+ * Creates GET, POST, and DELETE handlers for the /api/hazo_chat/messages endpoint.
5
5
  * These handlers should be used in a Next.js API route.
6
6
  *
7
7
  * @example
@@ -31,4 +31,55 @@ export declare function createMessagesHandler(options: MessagesHandlerOptions):
31
31
  GET: (request: NextRequest) => Promise<NextResponse>;
32
32
  POST: (request: NextRequest) => Promise<NextResponse>;
33
33
  };
34
+ /**
35
+ * Creates a PATCH handler for marking a message as read
36
+ *
37
+ * This handler should be used in a Next.js API route like:
38
+ * /api/hazo_chat/messages/[id]/read/route.ts
39
+ *
40
+ * @param options - Configuration options
41
+ * @returns PATCH handler function
42
+ */
43
+ export declare function createMarkAsReadHandler(options: MessagesHandlerOptions): {
44
+ PATCH: (request: NextRequest, context: {
45
+ params: {
46
+ id: string;
47
+ } | Promise<{
48
+ id: string;
49
+ }>;
50
+ }) => Promise<NextResponse>;
51
+ };
52
+ /**
53
+ * Creates a DELETE handler for soft-deleting a message
54
+ *
55
+ * This handler should be used in a Next.js API route like:
56
+ * /api/hazo_chat/messages/[id]/route.ts
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * // app/api/hazo_chat/messages/[id]/route.ts
61
+ * import { createDeleteHandler } from 'hazo_chat/api';
62
+ * import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
63
+ *
64
+ * export const dynamic = 'force-dynamic';
65
+ *
66
+ * const { DELETE } = createDeleteHandler({
67
+ * getHazoConnect: () => getHazoConnectSingleton()
68
+ * });
69
+ *
70
+ * export { DELETE };
71
+ * ```
72
+ *
73
+ * @param options - Configuration options
74
+ * @returns DELETE handler function
75
+ */
76
+ export declare function createDeleteHandler(options: MessagesHandlerOptions): {
77
+ DELETE: (request: NextRequest, context: {
78
+ params: {
79
+ id: string;
80
+ } | Promise<{
81
+ id: string;
82
+ }>;
83
+ }) => Promise<NextResponse>;
84
+ };
34
85
  //# sourceMappingURL=messages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/api/messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIxD,OAAO,KAAK,EAAE,sBAAsB,EAAuC,MAAM,YAAY,CAAC;AAmB9F;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB;mBAWvC,WAAW,KAAG,OAAO,CAAC,YAAY,CAAC;oBA+FlC,WAAW,KAAG,OAAO,CAAC,YAAY,CAAC;EA0GjE"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/api/messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIxD,OAAO,KAAK,EAAE,sBAAsB,EAA6E,MAAM,YAAY,CAAC;AAkEpI;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB;mBAcvC,WAAW,KAAG,OAAO,CAAC,YAAY,CAAC;oBAoKlC,WAAW,KAAG,OAAO,CAAC,YAAY,CAAC;EAqIjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,sBAAsB;qBAY1D,WAAW,WACX;QAAE,MAAM,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,KAC5D,OAAO,CAAC,YAAY,CAAC;EA8HzB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,sBAAsB;sBAatD,WAAW,WACX;QAAE,MAAM,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,KAC5D,OAAO,CAAC,YAAY,CAAC;EAwHzB"}