hazo_notes 1.0.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.
Files changed (97) hide show
  1. package/README.md +661 -0
  2. package/SETUP_CHECKLIST.md +453 -0
  3. package/dist/api/create_files_handler.d.ts +42 -0
  4. package/dist/api/create_files_handler.d.ts.map +1 -0
  5. package/dist/api/create_files_handler.js +213 -0
  6. package/dist/api/create_files_handler.js.map +1 -0
  7. package/dist/api/create_notes_handler.d.ts +50 -0
  8. package/dist/api/create_notes_handler.d.ts.map +1 -0
  9. package/dist/api/create_notes_handler.js +242 -0
  10. package/dist/api/create_notes_handler.js.map +1 -0
  11. package/dist/api/index.d.ts +9 -0
  12. package/dist/api/index.d.ts.map +1 -0
  13. package/dist/api/index.js +8 -0
  14. package/dist/api/index.js.map +1 -0
  15. package/dist/components/hazo_notes_entry.d.ts +6 -0
  16. package/dist/components/hazo_notes_entry.d.ts.map +1 -0
  17. package/dist/components/hazo_notes_entry.js +69 -0
  18. package/dist/components/hazo_notes_entry.js.map +1 -0
  19. package/dist/components/hazo_notes_file_preview.d.ts +16 -0
  20. package/dist/components/hazo_notes_file_preview.d.ts.map +1 -0
  21. package/dist/components/hazo_notes_file_preview.js +77 -0
  22. package/dist/components/hazo_notes_file_preview.js.map +1 -0
  23. package/dist/components/hazo_notes_icon.d.ts +6 -0
  24. package/dist/components/hazo_notes_icon.d.ts.map +1 -0
  25. package/dist/components/hazo_notes_icon.js +208 -0
  26. package/dist/components/hazo_notes_icon.js.map +1 -0
  27. package/dist/components/hazo_notes_panel.d.ts +6 -0
  28. package/dist/components/hazo_notes_panel.d.ts.map +1 -0
  29. package/dist/components/hazo_notes_panel.js +197 -0
  30. package/dist/components/hazo_notes_panel.js.map +1 -0
  31. package/dist/components/index.d.ts +8 -0
  32. package/dist/components/index.d.ts.map +1 -0
  33. package/dist/components/index.js +8 -0
  34. package/dist/components/index.js.map +1 -0
  35. package/dist/hooks/index.d.ts +8 -0
  36. package/dist/hooks/index.d.ts.map +1 -0
  37. package/dist/hooks/index.js +6 -0
  38. package/dist/hooks/index.js.map +1 -0
  39. package/dist/hooks/use_notes.d.ts +46 -0
  40. package/dist/hooks/use_notes.d.ts.map +1 -0
  41. package/dist/hooks/use_notes.js +146 -0
  42. package/dist/hooks/use_notes.js.map +1 -0
  43. package/dist/hooks/use_notes_file_upload.d.ts +52 -0
  44. package/dist/hooks/use_notes_file_upload.d.ts.map +1 -0
  45. package/dist/hooks/use_notes_file_upload.js +125 -0
  46. package/dist/hooks/use_notes_file_upload.js.map +1 -0
  47. package/dist/index.client.d.ts +16 -0
  48. package/dist/index.client.d.ts.map +1 -0
  49. package/dist/index.client.js +18 -0
  50. package/dist/index.client.js.map +1 -0
  51. package/dist/index.d.ts +13 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +15 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/lib/config.d.ts +31 -0
  56. package/dist/lib/config.d.ts.map +1 -0
  57. package/dist/lib/config.js +123 -0
  58. package/dist/lib/config.js.map +1 -0
  59. package/dist/lib/index.d.ts +6 -0
  60. package/dist/lib/index.d.ts.map +1 -0
  61. package/dist/lib/index.js +6 -0
  62. package/dist/lib/index.js.map +1 -0
  63. package/dist/logger/context.d.ts +49 -0
  64. package/dist/logger/context.d.ts.map +1 -0
  65. package/dist/logger/context.js +45 -0
  66. package/dist/logger/context.js.map +1 -0
  67. package/dist/logger/index.d.ts +9 -0
  68. package/dist/logger/index.d.ts.map +1 -0
  69. package/dist/logger/index.js +7 -0
  70. package/dist/logger/index.js.map +1 -0
  71. package/dist/logger/server.d.ts +27 -0
  72. package/dist/logger/server.d.ts.map +1 -0
  73. package/dist/logger/server.js +36 -0
  74. package/dist/logger/server.js.map +1 -0
  75. package/dist/logger/types.d.ts +20 -0
  76. package/dist/logger/types.d.ts.map +1 -0
  77. package/dist/logger/types.js +15 -0
  78. package/dist/logger/types.js.map +1 -0
  79. package/dist/types/index.d.ts +267 -0
  80. package/dist/types/index.d.ts.map +1 -0
  81. package/dist/types/index.js +5 -0
  82. package/dist/types/index.js.map +1 -0
  83. package/dist/utils/cn.d.ts +16 -0
  84. package/dist/utils/cn.d.ts.map +1 -0
  85. package/dist/utils/cn.js +19 -0
  86. package/dist/utils/cn.js.map +1 -0
  87. package/dist/utils/file_utils.d.ts +51 -0
  88. package/dist/utils/file_utils.d.ts.map +1 -0
  89. package/dist/utils/file_utils.js +128 -0
  90. package/dist/utils/file_utils.js.map +1 -0
  91. package/dist/utils/index.d.ts +6 -0
  92. package/dist/utils/index.d.ts.map +1 -0
  93. package/dist/utils/index.js +6 -0
  94. package/dist/utils/index.js.map +1 -0
  95. package/migrations/001_create_hazo_notes_table.sql +77 -0
  96. package/package.json +119 -0
  97. package/templates/config/hazo_notes_config.ini +43 -0
package/README.md ADDED
@@ -0,0 +1,661 @@
1
+ # hazo_notes
2
+
3
+ Database-backed notes system with file attachment support for Next.js applications in the hazo ecosystem.
4
+
5
+ ## Features
6
+
7
+ - **Database-backed persistence** - Notes stored in PostgreSQL or SQLite via hazo_connect
8
+ - **File attachments** - Support for images, PDFs, and documents with embed/attach modes
9
+ - **Flexible UI styles** - Choose between popover or slide panel presentation
10
+ - **Smart save modes** - Explicit save/cancel buttons or auto-save on blur
11
+ - **INI-based configuration** - Simple config file for all settings
12
+ - **Full TypeScript support** - Complete type definitions included
13
+ - **Controlled and uncontrolled modes** - Works with parent state or manages its own
14
+ - **Paste-to-embed images** - Paste images directly into notes
15
+ - **User attribution** - Automatic user profiles with avatars (optional)
16
+ - **File reference syntax** - Inline file references with `<<embed:XXXX>>` and `<<attach:XXXX>>`
17
+
18
+ ## Prerequisites
19
+
20
+ Before installing hazo_notes, ensure you have:
21
+
22
+ - Next.js 14+ with React 18+
23
+ - Tailwind CSS configured
24
+ - **hazo_connect** installed and configured (for database access)
25
+ - PostgreSQL or SQLite database
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ npm install hazo_notes
31
+ ```
32
+
33
+ ### Peer Dependencies
34
+
35
+ Install these based on your needs:
36
+
37
+ ```bash
38
+ # Required for UI components
39
+ npm install @radix-ui/react-popover @radix-ui/react-dialog react-icons
40
+
41
+ # Recommended for full functionality
42
+ npm install hazo_connect hazo_auth hazo_logs
43
+ ```
44
+
45
+ ## Quick Start
46
+
47
+ ### 1. Add the Component
48
+
49
+ ```tsx
50
+ import { HazoNotesIcon } from 'hazo_notes';
51
+
52
+ function MyComponent() {
53
+ return (
54
+ <div className="flex items-center gap-2">
55
+ <h2>Customer Information</h2>
56
+ <HazoNotesIcon
57
+ ref_id="customer-info-section"
58
+ label="Customer Information"
59
+ />
60
+ </div>
61
+ );
62
+ }
63
+ ```
64
+
65
+ ### 2. Create API Route
66
+
67
+ Create `app/api/hazo_notes/[ref_id]/route.ts`:
68
+
69
+ ```typescript
70
+ import { createNotesHandler } from 'hazo_notes/api';
71
+ import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
72
+ // Import your auth and user lookup functions
73
+ import { getSession } from '@/lib/auth'; // Replace with your auth
74
+ import { getUserById } from '@/lib/users'; // Replace with your user lookup
75
+
76
+ export const dynamic = 'force-dynamic';
77
+
78
+ const { GET, POST } = createNotesHandler({
79
+ getHazoConnect: () => getHazoConnectSingleton(),
80
+ getUserIdFromRequest: async (req) => {
81
+ // IMPORTANT: Replace with your authentication logic
82
+ const session = await getSession(req);
83
+ return session?.user?.id || null;
84
+ },
85
+ getUserProfile: async (userId) => {
86
+ // IMPORTANT: Replace with your user profile lookup
87
+ const user = await getUserById(userId);
88
+ return {
89
+ id: userId,
90
+ name: user?.name || 'Unknown User',
91
+ email: user?.email || '',
92
+ profile_image: user?.avatar,
93
+ };
94
+ },
95
+ });
96
+
97
+ export { GET, POST };
98
+ ```
99
+
100
+ ### 3. Set Up Database
101
+
102
+ Run the migration:
103
+
104
+ **PostgreSQL:**
105
+ ```sql
106
+ CREATE TABLE hazo_notes (
107
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
108
+ ref_id UUID NOT NULL,
109
+ note JSONB NOT NULL DEFAULT '[]'::jsonb,
110
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
111
+ changed_at TIMESTAMPTZ,
112
+ note_count INTEGER NOT NULL DEFAULT 0
113
+ );
114
+
115
+ CREATE INDEX idx_hazo_notes_ref_id ON hazo_notes(ref_id);
116
+ ```
117
+
118
+ **SQLite:**
119
+ ```sql
120
+ CREATE TABLE IF NOT EXISTS hazo_notes (
121
+ id TEXT PRIMARY KEY,
122
+ ref_id TEXT NOT NULL,
123
+ note TEXT NOT NULL DEFAULT '[]',
124
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
125
+ changed_at TEXT,
126
+ note_count INTEGER NOT NULL DEFAULT 0
127
+ );
128
+
129
+ CREATE INDEX IF NOT EXISTS idx_hazo_notes_ref_id ON hazo_notes(ref_id);
130
+ ```
131
+
132
+ ### 4. Configure (Optional)
133
+
134
+ Copy the config template:
135
+
136
+ ```bash
137
+ mkdir -p config
138
+ cp node_modules/hazo_notes/templates/config/hazo_notes_config.ini config/
139
+ ```
140
+
141
+ Edit `config/hazo_notes_config.ini` to customize behavior.
142
+
143
+ For detailed setup instructions, see [SETUP_CHECKLIST.md](./SETUP_CHECKLIST.md).
144
+
145
+ ## Usage Examples
146
+
147
+ ### Basic Usage
148
+
149
+ ```tsx
150
+ import { HazoNotesIcon } from 'hazo_notes';
151
+
152
+ export default function FormPage() {
153
+ return (
154
+ <div className="space-y-4">
155
+ <div className="flex items-center justify-between">
156
+ <label>Annual Income</label>
157
+ <HazoNotesIcon
158
+ ref_id="income-field"
159
+ label="Annual Income"
160
+ />
161
+ </div>
162
+ <input type="number" name="income" />
163
+ </div>
164
+ );
165
+ }
166
+ ```
167
+
168
+ **Result**: Click the notes icon to add contextual notes about this field.
169
+
170
+ ### With File Attachments
171
+
172
+ ```tsx
173
+ <HazoNotesIcon
174
+ ref_id="contract-review"
175
+ label="Contract Review"
176
+ enable_files={true}
177
+ max_files_per_note={5}
178
+ allowed_file_types={['pdf', 'docx', 'png', 'jpg']}
179
+ max_file_size_mb={10}
180
+ />
181
+ ```
182
+
183
+ **Features**:
184
+ - Upload files via file picker or paste images
185
+ - Files referenced in note text with `<<embed:0001>>` or `<<attach:0001>>`
186
+ - Images display inline, other files show as download links
187
+
188
+ ### Slide Panel Style
189
+
190
+ ```tsx
191
+ <HazoNotesIcon
192
+ ref_id="detailed-notes"
193
+ label="Detailed Notes"
194
+ panel_style="slide_panel"
195
+ />
196
+ ```
197
+
198
+ **Result**: Notes open in a slide-out panel instead of a popover.
199
+
200
+ ### Auto-Save Mode
201
+
202
+ ```tsx
203
+ <HazoNotesIcon
204
+ ref_id="quick-notes"
205
+ label="Quick Notes"
206
+ save_mode="auto"
207
+ />
208
+ ```
209
+
210
+ **Result**: Notes save automatically when panel closes (no save/cancel buttons).
211
+
212
+ ### Custom Styling
213
+
214
+ ```tsx
215
+ <HazoNotesIcon
216
+ ref_id="styled-notes"
217
+ label="Styled Notes"
218
+ background_color="bg-blue-50"
219
+ className="ml-2"
220
+ />
221
+ ```
222
+
223
+ ### Controlled Mode
224
+
225
+ ```tsx
226
+ 'use client';
227
+
228
+ import { useState } from 'react';
229
+ import { HazoNotesIcon } from 'hazo_notes';
230
+ import type { NoteEntry } from 'hazo_notes/types';
231
+
232
+ export default function ControlledExample() {
233
+ const [notes, setNotes] = useState<NoteEntry[]>([]);
234
+
235
+ return (
236
+ <HazoNotesIcon
237
+ ref_id="controlled-notes"
238
+ label="Controlled Notes"
239
+ notes={notes}
240
+ on_notes_change={setNotes}
241
+ />
242
+ );
243
+ }
244
+ ```
245
+
246
+ **Use Case**: Sync notes with parent component state or external state management.
247
+
248
+ ## Configuration
249
+
250
+ Configuration options in `config/hazo_notes_config.ini`:
251
+
252
+ ```ini
253
+ [ui]
254
+ # Background color for notes panel (Tailwind CSS class)
255
+ background_color = bg-yellow-100
256
+
257
+ # Panel presentation style: popover | slide_panel
258
+ panel_style = popover
259
+
260
+ # Save behavior: explicit | auto
261
+ save_mode = explicit
262
+
263
+ [storage]
264
+ # File storage mode: jsonb | filesystem
265
+ file_storage_mode = jsonb
266
+
267
+ # Path for filesystem storage (only used when file_storage_mode = filesystem)
268
+ file_storage_path = /uploads/notes
269
+
270
+ [files]
271
+ # Maximum file size in MB
272
+ max_file_size_mb = 10
273
+
274
+ # Allowed file types (comma-separated extensions, no dots)
275
+ allowed_file_types = pdf,png,jpg,jpeg,gif,doc,docx
276
+
277
+ # Maximum files per single note entry
278
+ max_files_per_note = 5
279
+
280
+ [logging]
281
+ # Log file path (relative to application root)
282
+ logfile = logs/hazo_notes.log
283
+ ```
284
+
285
+ ## Component API
286
+
287
+ ### HazoNotesIcon Props
288
+
289
+ ```typescript
290
+ interface HazoNotesIconProps {
291
+ // Required
292
+ ref_id: string; // Unique identifier for this notes instance
293
+
294
+ // Display
295
+ label?: string; // Panel header label
296
+ has_notes?: boolean; // Show indicator when notes exist
297
+ note_count?: number; // Display count badge
298
+
299
+ // Controlled mode
300
+ notes?: NoteEntry[]; // Controlled notes array
301
+ on_notes_change?: (notes: NoteEntry[]) => void;
302
+
303
+ // User context
304
+ current_user?: NoteUserInfo; // User info (auto-fetched if not provided)
305
+
306
+ // Configuration overrides
307
+ panel_style?: 'popover' | 'slide_panel';
308
+ save_mode?: 'explicit' | 'auto';
309
+ background_color?: string; // Tailwind class
310
+
311
+ // File options
312
+ enable_files?: boolean; // Enable file attachments
313
+ max_files_per_note?: number;
314
+ allowed_file_types?: string[];
315
+ max_file_size_mb?: number;
316
+
317
+ // Callbacks
318
+ on_open?: () => void;
319
+ on_close?: () => void;
320
+
321
+ // Styling
322
+ disabled?: boolean;
323
+ className?: string;
324
+ }
325
+ ```
326
+
327
+ ## Hooks API
328
+
329
+ ### use_notes
330
+
331
+ Manages notes state and API interactions.
332
+
333
+ ```typescript
334
+ import { use_notes } from 'hazo_notes/hooks';
335
+
336
+ function MyComponent({ refId }: { refId: string }) {
337
+ const {
338
+ notes,
339
+ note_count,
340
+ loading,
341
+ error,
342
+ add_note,
343
+ refresh,
344
+ } = use_notes(refId);
345
+
346
+ const handleAddNote = async () => {
347
+ const success = await add_note('This is my note');
348
+ if (success) {
349
+ console.log('Note added!');
350
+ }
351
+ };
352
+
353
+ if (loading) return <div>Loading notes...</div>;
354
+ if (error) return <div>Error: {error}</div>;
355
+
356
+ return (
357
+ <div>
358
+ <p>{note_count} notes</p>
359
+ <button onClick={handleAddNote}>Add Note</button>
360
+ </div>
361
+ );
362
+ }
363
+ ```
364
+
365
+ ### use_notes_file_upload
366
+
367
+ Handles file uploads and validation.
368
+
369
+ ```typescript
370
+ import { use_notes_file_upload } from 'hazo_notes/hooks';
371
+
372
+ function FileUploadExample() {
373
+ const {
374
+ pending_files,
375
+ upload_file,
376
+ remove_file,
377
+ uploading,
378
+ error,
379
+ } = use_notes_file_upload({
380
+ ref_id: 'my-notes',
381
+ max_file_size_mb: 5,
382
+ });
383
+
384
+ const handleFileSelect = async (e: React.ChangeEvent<HTMLInputElement>) => {
385
+ const file = e.target.files?.[0];
386
+ if (file) {
387
+ const uploaded = await upload_file(file, 'attachment');
388
+ if (uploaded) {
389
+ console.log('File uploaded:', uploaded.filename);
390
+ }
391
+ }
392
+ };
393
+
394
+ return (
395
+ <div>
396
+ <input type="file" onChange={handleFileSelect} disabled={uploading} />
397
+ {pending_files.map(f => (
398
+ <div key={f.file_no}>
399
+ {f.filename}
400
+ <button onClick={() => remove_file(f.file_no)}>Remove</button>
401
+ </div>
402
+ ))}
403
+ </div>
404
+ );
405
+ }
406
+ ```
407
+
408
+ ## File Attachments
409
+
410
+ Notes support inline file references in text:
411
+
412
+ ### Embed Mode (Images Inline)
413
+
414
+ ```
415
+ Check out this screenshot:
416
+ <<embed:0001>>
417
+ ```
418
+
419
+ **Result**: Image displays directly in the note.
420
+
421
+ ### Attach Mode (Download Link)
422
+
423
+ ```
424
+ Download the full report:
425
+ <<attach:0001>>
426
+ ```
427
+
428
+ **Result**: Shows as a clickable download link with file icon.
429
+
430
+ ### Paste to Embed
431
+
432
+ Users can paste images directly into the note textarea - they're automatically uploaded and referenced with `<<embed:XXXX>>` syntax.
433
+
434
+ ## File Storage Modes
435
+
436
+ ### JSONB Mode (Default)
437
+
438
+ ```ini
439
+ [storage]
440
+ file_storage_mode = jsonb
441
+ ```
442
+
443
+ - Files stored as Base64 in database
444
+ - Simpler setup (no file API needed)
445
+ - Good for small files (< 1MB)
446
+ - Works out of the box
447
+
448
+ ### Filesystem Mode
449
+
450
+ ```ini
451
+ [storage]
452
+ file_storage_mode = filesystem
453
+ file_storage_path = /uploads/notes
454
+ ```
455
+
456
+ - Files stored on server filesystem
457
+ - Better for large files
458
+ - Requires file upload API route
459
+
460
+ Create `app/api/hazo_notes/files/upload/route.ts`:
461
+
462
+ ```typescript
463
+ import { createFilesHandler } from 'hazo_notes/api';
464
+ import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
465
+
466
+ export const dynamic = 'force-dynamic';
467
+
468
+ const { POST } = createFilesHandler({
469
+ getHazoConnect: () => getHazoConnectSingleton(),
470
+ getUserIdFromRequest: async (req) => {
471
+ const session = await getSession(req);
472
+ return session?.user?.id || null;
473
+ },
474
+ file_storage_mode: 'filesystem',
475
+ file_storage_path: '/uploads/notes',
476
+ max_file_size_mb: 10,
477
+ allowed_file_types: ['pdf', 'png', 'jpg', 'jpeg', 'gif'],
478
+ });
479
+
480
+ export { POST };
481
+ ```
482
+
483
+ ## Logger Integration (Optional)
484
+
485
+ ### Client-Side
486
+
487
+ ```tsx
488
+ // app/providers.tsx
489
+ 'use client';
490
+
491
+ import { LoggerProvider } from 'hazo_notes';
492
+ import { createClientLogger } from 'hazo_logs/ui';
493
+
494
+ const logger = createClientLogger({ packageName: 'my_app' });
495
+
496
+ export function Providers({ children }: { children: React.ReactNode }) {
497
+ return (
498
+ <LoggerProvider logger={logger}>
499
+ {children}
500
+ </LoggerProvider>
501
+ );
502
+ }
503
+ ```
504
+
505
+ ### Server-Side
506
+
507
+ ```typescript
508
+ // lib/logger-setup.ts
509
+ import { set_server_logger } from 'hazo_notes/lib';
510
+ import { createLogger } from 'hazo_logs';
511
+
512
+ export function initializeLogger() {
513
+ set_server_logger(createLogger('hazo_notes'));
514
+ }
515
+ ```
516
+
517
+ ## TypeScript Types
518
+
519
+ All types are exported from `hazo_notes/types`:
520
+
521
+ ```typescript
522
+ import type {
523
+ NoteEntry,
524
+ NoteFile,
525
+ NoteUserInfo,
526
+ HazoNotesIconProps,
527
+ HazoNotesPanelProps,
528
+ } from 'hazo_notes/types';
529
+ ```
530
+
531
+ ## Database Schema
532
+
533
+ The `hazo_notes` table stores all notes:
534
+
535
+ ```sql
536
+ CREATE TABLE hazo_notes (
537
+ id UUID PRIMARY KEY,
538
+ ref_id UUID NOT NULL, -- Links to parent entity
539
+ note JSONB NOT NULL DEFAULT '[]', -- Array of note entries
540
+ created_at TIMESTAMPTZ NOT NULL,
541
+ changed_at TIMESTAMPTZ,
542
+ note_count INTEGER NOT NULL DEFAULT 0
543
+ );
544
+ ```
545
+
546
+ Each note entry in the JSONB array:
547
+
548
+ ```typescript
549
+ {
550
+ userid: "user-uuid",
551
+ created_at: "2026-01-07T12:30:00.000Z",
552
+ note_text: "This is the note content",
553
+ note_files: [
554
+ {
555
+ file_no: "0001",
556
+ embed_type: "embed",
557
+ filename: "screenshot.png",
558
+ filedata: "base64_data_or_file_path",
559
+ mime_type: "image/png",
560
+ file_size: 12345
561
+ }
562
+ ]
563
+ }
564
+ ```
565
+
566
+ ## Troubleshooting
567
+
568
+ ### Notes icon doesn't open panel
569
+
570
+ **Problem**: Icon renders but clicking does nothing.
571
+
572
+ **Solution**: Install required UI components:
573
+ ```bash
574
+ npm install @radix-ui/react-popover @radix-ui/react-dialog
575
+ ```
576
+
577
+ ### User shows as "Unknown User"
578
+
579
+ **Problem**: Notes display but no user names.
580
+
581
+ **Solution**: Implement `getUserProfile` in your API handler:
582
+ ```typescript
583
+ getUserProfile: async (userId) => {
584
+ const user = await fetchUserFromDatabase(userId);
585
+ return {
586
+ id: userId,
587
+ name: user.name,
588
+ email: user.email,
589
+ profile_image: user.avatar,
590
+ };
591
+ }
592
+ ```
593
+
594
+ ### Notes don't persist
595
+
596
+ **Problem**: Notes disappear after refresh.
597
+
598
+ **Solution**:
599
+ 1. Verify database table exists (run migration)
600
+ 2. Check `ref_id` is consistent
601
+ 3. Verify API route is working: `curl http://localhost:3000/api/hazo_notes/test-id`
602
+
603
+ ### File upload fails
604
+
605
+ **Problem**: Can't upload files.
606
+
607
+ **Solution**:
608
+ - For JSONB mode: Should work out of the box
609
+ - For filesystem mode: Create the files upload API route (see File Storage Modes above)
610
+
611
+ ### Authentication errors
612
+
613
+ **Problem**: "Unauthorized" when adding notes.
614
+
615
+ **Solution**: Implement `getUserIdFromRequest` to return authenticated user ID:
616
+ ```typescript
617
+ getUserIdFromRequest: async (req) => {
618
+ const session = await getSession(req);
619
+ if (!session?.user?.id) return null;
620
+ return session.user.id;
621
+ }
622
+ ```
623
+
624
+ ## Examples
625
+
626
+ See the `test-app/` directory for complete working examples:
627
+
628
+ - **Basic notes**: Simple note creation and display
629
+ - **Popover style**: Notes in a popover
630
+ - **Slide panel style**: Notes in a slide-out panel
631
+ - **With files**: File attachment demonstrations
632
+ - **Auto-save**: Auto-save mode example
633
+ - **Multiple instances**: Multiple independent notes on one page
634
+ - **Controlled mode**: Parent state integration
635
+
636
+ Run the test app:
637
+
638
+ ```bash
639
+ npm run dev:test-app
640
+ # Open http://localhost:3002
641
+ ```
642
+
643
+ ## Contributing
644
+
645
+ See [SETUP_CHECKLIST.md](./SETUP_CHECKLIST.md) for development setup instructions.
646
+
647
+ ## License
648
+
649
+ MIT
650
+
651
+ ## Related Packages
652
+
653
+ - [hazo_connect](https://github.com/pub12/hazo_connect) - Database connection abstraction
654
+ - [hazo_auth](https://github.com/pub12/hazo_auth) - Authentication and user management
655
+ - [hazo_logs](https://github.com/pub12/hazo_logs) - Structured logging
656
+ - [hazo_config](https://github.com/pub12/hazo_config) - INI configuration management
657
+
658
+ ## Support
659
+
660
+ - Issues: https://github.com/pub12/hazo_notes/issues
661
+ - Documentation: https://github.com/pub12/hazo_notes#readme