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
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Files API Handler Factory
3
+ *
4
+ * Creates handlers for file upload and download operations.
5
+ * Used for filesystem storage mode.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // app/api/hazo_notes/files/upload/route.ts
10
+ * import { createFilesHandler } from 'hazo_notes/api';
11
+ *
12
+ * export const dynamic = 'force-dynamic';
13
+ *
14
+ * const { POST } = createFilesHandler({
15
+ * getHazoConnect: () => getHazoConnectSingleton(),
16
+ * getUserIdFromRequest: async (req) => userId,
17
+ * file_storage_mode: 'filesystem',
18
+ * file_storage_path: '/uploads/notes',
19
+ * max_file_size_mb: 10,
20
+ * allowed_file_types: ['pdf', 'png', 'jpg', 'jpeg', 'gif'],
21
+ * });
22
+ *
23
+ * export { POST };
24
+ * ```
25
+ */
26
+ import { NextResponse } from 'next/server';
27
+ import { get_mime_type, is_allowed_file_type, generate_file_no, } from '../utils/file_utils.js';
28
+ // ============================================================================
29
+ // Constants
30
+ // ============================================================================
31
+ const DEFAULT_MAX_FILE_SIZE_MB = 10;
32
+ const DEFAULT_ALLOWED_TYPES = ['pdf', 'png', 'jpg', 'jpeg', 'gif', 'doc', 'docx'];
33
+ // ============================================================================
34
+ // Helper Functions
35
+ // ============================================================================
36
+ /** No-op logger */
37
+ const noopLogger = {
38
+ error: () => { },
39
+ warn: () => { },
40
+ info: () => { },
41
+ debug: () => { },
42
+ };
43
+ /** Create error response */
44
+ function createErrorResponse(error, status) {
45
+ return NextResponse.json({ success: false, error }, { status });
46
+ }
47
+ /**
48
+ * Creates file upload and download handlers
49
+ *
50
+ * @param options - Configuration options
51
+ * @returns Object with POST (upload) and GET (download) handlers
52
+ */
53
+ export function createFilesHandler(options) {
54
+ const { getHazoConnect, getLogger, getUserIdFromRequest, file_storage_mode = 'jsonb', file_storage_path = '/uploads/notes', max_file_size_mb = DEFAULT_MAX_FILE_SIZE_MB, allowed_file_types = DEFAULT_ALLOWED_TYPES, } = options;
55
+ const logger = getLogger?.() || noopLogger;
56
+ const max_bytes = max_file_size_mb * 1024 * 1024;
57
+ /**
58
+ * POST handler - Upload a file
59
+ *
60
+ * Accepts multipart/form-data with:
61
+ * - file: The file to upload
62
+ * - ref_id: Reference ID for the notes
63
+ * - embed_type: 'embed' or 'attachment'
64
+ */
65
+ async function POST(request) {
66
+ try {
67
+ // Check authentication
68
+ const userId = getUserIdFromRequest
69
+ ? await getUserIdFromRequest(request)
70
+ : null;
71
+ if (!userId) {
72
+ return createErrorResponse('Unauthorized', 401);
73
+ }
74
+ // Parse form data
75
+ const formData = await request.formData();
76
+ const file = formData.get('file');
77
+ const ref_id = formData.get('ref_id');
78
+ const embed_type = formData.get('embed_type') || 'attachment';
79
+ if (!file) {
80
+ return createErrorResponse('No file provided', 400);
81
+ }
82
+ if (!ref_id) {
83
+ return createErrorResponse('ref_id is required', 400);
84
+ }
85
+ // Validate file type
86
+ if (!is_allowed_file_type(file.name, allowed_file_types)) {
87
+ return createErrorResponse(`File type not allowed. Allowed types: ${allowed_file_types.join(', ')}`, 400);
88
+ }
89
+ // Validate file size
90
+ if (file.size > max_bytes) {
91
+ return createErrorResponse(`File size exceeds maximum of ${max_file_size_mb} MB`, 400);
92
+ }
93
+ logger.debug('[hazo_notes/files] Upload request', {
94
+ filename: file.name,
95
+ size: file.size,
96
+ ref_id,
97
+ embed_type,
98
+ storage_mode: file_storage_mode,
99
+ });
100
+ // Generate file number
101
+ const file_no = generate_file_no([]);
102
+ let filedata;
103
+ if (file_storage_mode === 'filesystem') {
104
+ // Filesystem storage - save to disk and store path
105
+ try {
106
+ const fs = await import('fs/promises');
107
+ const path = await import('path');
108
+ // Create directory if it doesn't exist
109
+ const upload_dir = path.join(process.cwd(), file_storage_path, ref_id);
110
+ await fs.mkdir(upload_dir, { recursive: true });
111
+ // Save file
112
+ const safe_filename = `${file_no}_${file.name.replace(/[^a-zA-Z0-9._-]/g, '_')}`;
113
+ const file_path = path.join(upload_dir, safe_filename);
114
+ const buffer = Buffer.from(await file.arrayBuffer());
115
+ await fs.writeFile(file_path, buffer);
116
+ // Store relative path
117
+ filedata = `${file_storage_path}/${ref_id}/${safe_filename}`;
118
+ logger.debug('[hazo_notes/files] File saved to filesystem', { path: filedata });
119
+ }
120
+ catch (error) {
121
+ logger.error('[hazo_notes/files] Filesystem write error', {
122
+ error: error instanceof Error ? error.message : String(error),
123
+ });
124
+ return createErrorResponse('Failed to save file', 500);
125
+ }
126
+ }
127
+ else {
128
+ // JSONB storage - convert to base64
129
+ const buffer = await file.arrayBuffer();
130
+ filedata = Buffer.from(buffer).toString('base64');
131
+ }
132
+ const note_file = {
133
+ file_no,
134
+ embed_type,
135
+ filename: file.name,
136
+ filedata,
137
+ mime_type: get_mime_type(file.name),
138
+ file_size: file.size,
139
+ };
140
+ return NextResponse.json({
141
+ success: true,
142
+ file: note_file,
143
+ });
144
+ }
145
+ catch (error) {
146
+ logger.error('[hazo_notes/files] Upload error', {
147
+ error: error instanceof Error ? error.message : String(error),
148
+ });
149
+ return createErrorResponse('Failed to upload file', 500);
150
+ }
151
+ }
152
+ /**
153
+ * GET handler - Download a file (for filesystem storage mode)
154
+ *
155
+ * Query params:
156
+ * - ref_id: Reference ID
157
+ * - file_no: File number
158
+ */
159
+ async function GET(request, context) {
160
+ try {
161
+ if (file_storage_mode !== 'filesystem') {
162
+ return createErrorResponse('File download only available for filesystem storage mode', 400);
163
+ }
164
+ const url = new URL(request.url);
165
+ const ref_id = url.searchParams.get('ref_id');
166
+ const file_no = url.searchParams.get('file_no');
167
+ if (!ref_id || !file_no) {
168
+ return createErrorResponse('ref_id and file_no are required', 400);
169
+ }
170
+ // Get notes to find the file
171
+ const hazoConnect = await getHazoConnect();
172
+ const result = await hazoConnect.rawQuery(`/hazo_notes?ref_id=eq.${ref_id}`);
173
+ if (!result || result.length === 0) {
174
+ return createErrorResponse('Notes not found', 404);
175
+ }
176
+ // Find the file in notes
177
+ const notes = result[0].note || [];
178
+ let file_info = null;
179
+ for (const note of notes) {
180
+ if (note.note_files) {
181
+ const found = note.note_files.find((f) => f.file_no === file_no);
182
+ if (found) {
183
+ file_info = found;
184
+ break;
185
+ }
186
+ }
187
+ }
188
+ if (!file_info) {
189
+ return createErrorResponse('File not found', 404);
190
+ }
191
+ // Read file from filesystem
192
+ const fs = await import('fs/promises');
193
+ const path = await import('path');
194
+ const file_path = path.join(process.cwd(), file_info.filedata);
195
+ const buffer = await fs.readFile(file_path);
196
+ return new NextResponse(buffer, {
197
+ headers: {
198
+ 'Content-Type': file_info.mime_type || 'application/octet-stream',
199
+ 'Content-Disposition': `attachment; filename="${file_info.filename}"`,
200
+ 'Content-Length': String(buffer.length),
201
+ },
202
+ });
203
+ }
204
+ catch (error) {
205
+ logger.error('[hazo_notes/files] Download error', {
206
+ error: error instanceof Error ? error.message : String(error),
207
+ });
208
+ return createErrorResponse('Failed to download file', 500);
209
+ }
210
+ }
211
+ return { GET, POST };
212
+ }
213
+ //# sourceMappingURL=create_files_handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create_files_handler.js","sourceRoot":"","sources":["../../src/api/create_files_handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Notes API Handler Factory
3
+ *
4
+ * Creates GET and POST handlers for the /api/hazo_notes/[ref_id] endpoint.
5
+ * These handlers should be used in a Next.js API route.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // app/api/hazo_notes/[ref_id]/route.ts
10
+ * import { createNotesHandler } from 'hazo_notes/api';
11
+ * import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
12
+ *
13
+ * export const dynamic = 'force-dynamic';
14
+ *
15
+ * const { GET, POST } = createNotesHandler({
16
+ * getHazoConnect: () => getHazoConnectSingleton(),
17
+ * getUserIdFromRequest: async (req) => {
18
+ * // Your auth logic here
19
+ * return userId;
20
+ * },
21
+ * getUserProfile: async (userId) => {
22
+ * // Your profile lookup logic here
23
+ * return { id: userId, name: 'User', email: 'user@example.com' };
24
+ * }
25
+ * });
26
+ *
27
+ * export { GET, POST };
28
+ * ```
29
+ */
30
+ import { NextResponse } from 'next/server';
31
+ import type { CreateNotesHandlerOptions, NotesApiResponse, AddNoteApiResponse } from '../types/index.js';
32
+ /**
33
+ * Creates GET and POST handlers for notes
34
+ *
35
+ * @param options - Configuration options
36
+ * @returns Object with GET and POST handlers
37
+ */
38
+ export declare function createNotesHandler(options: CreateNotesHandlerOptions): {
39
+ GET: (request: Request, context: {
40
+ params: Promise<{
41
+ ref_id: string;
42
+ }>;
43
+ }) => Promise<NextResponse<NotesApiResponse>>;
44
+ POST: (request: Request, context: {
45
+ params: Promise<{
46
+ ref_id: string;
47
+ }>;
48
+ }) => Promise<NextResponse<AddNoteApiResponse>>;
49
+ };
50
+ //# sourceMappingURL=create_notes_handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create_notes_handler.d.ts","sourceRoot":"","sources":["../../src/api/create_notes_handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EACV,yBAAyB,EAIzB,gBAAgB,EAChB,kBAAkB,EAEnB,MAAM,mBAAmB,CAAC;AA+C3B;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB;mBAWxD,OAAO,WACP;QAAE,MAAM,EAAE,OAAO,CAAC;YAAE,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,KAC/C,OAAO,CAAC,YAAY,CAAC,gBAAgB,CAAC,CAAC;oBAoE/B,OAAO,WACP;QAAE,MAAM,EAAE,OAAO,CAAC;YAAE,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,KAC/C,OAAO,CAAC,YAAY,CAAC,kBAAkB,CAAC,CAAC;EAkJ7C"}
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Notes API Handler Factory
3
+ *
4
+ * Creates GET and POST handlers for the /api/hazo_notes/[ref_id] endpoint.
5
+ * These handlers should be used in a Next.js API route.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // app/api/hazo_notes/[ref_id]/route.ts
10
+ * import { createNotesHandler } from 'hazo_notes/api';
11
+ * import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
12
+ *
13
+ * export const dynamic = 'force-dynamic';
14
+ *
15
+ * const { GET, POST } = createNotesHandler({
16
+ * getHazoConnect: () => getHazoConnectSingleton(),
17
+ * getUserIdFromRequest: async (req) => {
18
+ * // Your auth logic here
19
+ * return userId;
20
+ * },
21
+ * getUserProfile: async (userId) => {
22
+ * // Your profile lookup logic here
23
+ * return { id: userId, name: 'User', email: 'user@example.com' };
24
+ * }
25
+ * });
26
+ *
27
+ * export { GET, POST };
28
+ * ```
29
+ */
30
+ import { NextResponse } from 'next/server';
31
+ // ============================================================================
32
+ // Constants
33
+ // ============================================================================
34
+ /** Maximum note text length in characters */
35
+ const MAX_NOTE_TEXT_LENGTH = 10000;
36
+ /** Maximum files per note */
37
+ const DEFAULT_MAX_FILES_PER_NOTE = 5;
38
+ // ============================================================================
39
+ // Helper Functions
40
+ // ============================================================================
41
+ /** Generate a UUID v4 */
42
+ function generateUUID() {
43
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
44
+ const r = (Math.random() * 16) | 0;
45
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
46
+ return v.toString(16);
47
+ });
48
+ }
49
+ /** Create a standardized error response */
50
+ function createErrorResponse(error, status) {
51
+ return NextResponse.json({
52
+ success: false,
53
+ error,
54
+ }, { status });
55
+ }
56
+ /** No-op logger */
57
+ const noopLogger = {
58
+ error: () => { },
59
+ warn: () => { },
60
+ info: () => { },
61
+ debug: () => { },
62
+ };
63
+ /**
64
+ * Creates GET and POST handlers for notes
65
+ *
66
+ * @param options - Configuration options
67
+ * @returns Object with GET and POST handlers
68
+ */
69
+ export function createNotesHandler(options) {
70
+ const { getHazoConnect, getLogger, getUserIdFromRequest, getUserProfile } = options;
71
+ const logger = getLogger?.() || noopLogger;
72
+ /**
73
+ * GET handler - Fetch notes for a ref_id
74
+ *
75
+ * @param request - The incoming request
76
+ * @param context - Route context with params
77
+ */
78
+ async function GET(request, context) {
79
+ try {
80
+ const { ref_id } = await context.params;
81
+ if (!ref_id) {
82
+ return createErrorResponse('ref_id is required', 400);
83
+ }
84
+ logger.debug('[hazo_notes] GET request', { ref_id });
85
+ const hazoConnect = await getHazoConnect();
86
+ // Query the hazo_notes table using PostgREST-style query
87
+ const result = await hazoConnect.rawQuery(`/hazo_notes?ref_id=eq.${ref_id}`);
88
+ if (!result || result.length === 0) {
89
+ return NextResponse.json({
90
+ success: true,
91
+ notes: [],
92
+ note_count: 0,
93
+ });
94
+ }
95
+ const row = result[0];
96
+ const notes_db = Array.isArray(row.note) ? row.note : [];
97
+ // Enrich notes with user profiles if getUserProfile is provided
98
+ let notes;
99
+ if (getUserProfile) {
100
+ notes = await Promise.all(notes_db.map(async (note) => {
101
+ const profile = await getUserProfile(note.userid).catch(() => null);
102
+ return {
103
+ ...note,
104
+ user_name: profile?.name || 'Unknown User',
105
+ user_email: profile?.email || '',
106
+ user_avatar: profile?.profile_image,
107
+ };
108
+ }));
109
+ }
110
+ else {
111
+ notes = notes_db.map((note) => ({
112
+ ...note,
113
+ user_name: 'Unknown User',
114
+ user_email: '',
115
+ }));
116
+ }
117
+ return NextResponse.json({
118
+ success: true,
119
+ notes,
120
+ note_count: row.note_count,
121
+ });
122
+ }
123
+ catch (error) {
124
+ logger.error('[hazo_notes] GET error', {
125
+ error: error instanceof Error ? error.message : String(error),
126
+ });
127
+ return createErrorResponse('Failed to fetch notes', 500);
128
+ }
129
+ }
130
+ /**
131
+ * POST handler - Add a new note
132
+ *
133
+ * @param request - The incoming request
134
+ * @param context - Route context with params
135
+ */
136
+ async function POST(request, context) {
137
+ try {
138
+ const { ref_id } = await context.params;
139
+ if (!ref_id) {
140
+ return NextResponse.json({ success: false, error: 'ref_id is required' }, { status: 400 });
141
+ }
142
+ // Get user ID from request
143
+ const userId = getUserIdFromRequest
144
+ ? await getUserIdFromRequest(request)
145
+ : null;
146
+ if (!userId) {
147
+ return NextResponse.json({ success: false, error: 'Unauthorized - user not authenticated' }, { status: 401 });
148
+ }
149
+ // Parse request body
150
+ const body = await request.json();
151
+ const { note_text, note_files } = body;
152
+ // Validate note_text
153
+ if (!note_text || typeof note_text !== 'string') {
154
+ return NextResponse.json({ success: false, error: 'note_text is required' }, { status: 400 });
155
+ }
156
+ if (note_text.length > MAX_NOTE_TEXT_LENGTH) {
157
+ return NextResponse.json({
158
+ success: false,
159
+ error: `note_text exceeds maximum length of ${MAX_NOTE_TEXT_LENGTH} characters`,
160
+ }, { status: 400 });
161
+ }
162
+ // Validate note_files if provided
163
+ if (note_files) {
164
+ if (!Array.isArray(note_files)) {
165
+ return NextResponse.json({ success: false, error: 'note_files must be an array' }, { status: 400 });
166
+ }
167
+ if (note_files.length > DEFAULT_MAX_FILES_PER_NOTE) {
168
+ return NextResponse.json({
169
+ success: false,
170
+ error: `Maximum ${DEFAULT_MAX_FILES_PER_NOTE} files per note allowed`,
171
+ }, { status: 400 });
172
+ }
173
+ }
174
+ logger.debug('[hazo_notes] POST request', { ref_id, userId });
175
+ const hazoConnect = await getHazoConnect();
176
+ // Create new note entry
177
+ const new_note = {
178
+ userid: userId,
179
+ created_at: new Date().toISOString(),
180
+ note_text: note_text.trim(),
181
+ note_files: note_files || undefined,
182
+ };
183
+ // Check if row exists for this ref_id
184
+ const existing = await hazoConnect.rawQuery(`/hazo_notes?ref_id=eq.${ref_id}`);
185
+ let updated_count;
186
+ if (!existing || existing.length === 0) {
187
+ // Create new row with first note
188
+ const new_id = generateUUID();
189
+ await hazoConnect.rawQuery('/hazo_notes', {
190
+ method: 'POST',
191
+ body: JSON.stringify({
192
+ id: new_id,
193
+ ref_id,
194
+ note: [new_note],
195
+ note_count: 1,
196
+ }),
197
+ headers: { 'Content-Type': 'application/json' },
198
+ });
199
+ updated_count = 1;
200
+ }
201
+ else {
202
+ // Append to existing notes array
203
+ const row = existing[0];
204
+ const existing_notes = Array.isArray(row.note) ? row.note : [];
205
+ const updated_notes = [...existing_notes, new_note];
206
+ updated_count = updated_notes.length;
207
+ await hazoConnect.rawQuery(`/hazo_notes?id=eq.${row.id}`, {
208
+ method: 'PATCH',
209
+ body: JSON.stringify({
210
+ note: updated_notes,
211
+ note_count: updated_count,
212
+ changed_at: new Date().toISOString(),
213
+ }),
214
+ headers: { 'Content-Type': 'application/json' },
215
+ });
216
+ }
217
+ // Get user profile for response
218
+ const profile = getUserProfile
219
+ ? await getUserProfile(userId).catch(() => null)
220
+ : null;
221
+ const enriched_note = {
222
+ ...new_note,
223
+ user_name: profile?.name || 'Unknown User',
224
+ user_email: profile?.email || '',
225
+ user_avatar: profile?.profile_image,
226
+ };
227
+ return NextResponse.json({
228
+ success: true,
229
+ note: enriched_note,
230
+ note_count: updated_count,
231
+ });
232
+ }
233
+ catch (error) {
234
+ logger.error('[hazo_notes] POST error', {
235
+ error: error instanceof Error ? error.message : String(error),
236
+ });
237
+ return NextResponse.json({ success: false, error: 'Failed to add note' }, { status: 500 });
238
+ }
239
+ }
240
+ return { GET, POST };
241
+ }
242
+ //# sourceMappingURL=create_notes_handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create_notes_handler.js","sourceRoot":"","sources":["../../src/api/create_notes_handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * API handlers for hazo_notes
3
+ *
4
+ * Factory functions for creating Next.js API route handlers
5
+ */
6
+ export { createNotesHandler } from './create_notes_handler.js';
7
+ export { createFilesHandler } from './create_files_handler.js';
8
+ export type { CreateNotesHandlerOptions, CreateFilesHandlerOptions, NotesApiResponse, AddNoteApiResponse, FileUploadApiResponse, } from '../types/index.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAG/D,YAAY,EACV,yBAAyB,EACzB,yBAAyB,EACzB,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * API handlers for hazo_notes
3
+ *
4
+ * Factory functions for creating Next.js API route handlers
5
+ */
6
+ export { createNotesHandler } from './create_notes_handler.js';
7
+ export { createFilesHandler } from './create_files_handler.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { HazoNotesEntryProps } from '../types/index.js';
2
+ /**
3
+ * Single note entry display component
4
+ */
5
+ export declare function HazoNotesEntry({ note, ProfileStampComponent }: HazoNotesEntryProps): import("react/jsx-runtime").JSX.Element;
6
+ //# sourceMappingURL=hazo_notes_entry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hazo_notes_entry.d.ts","sourceRoot":"","sources":["../../src/components/hazo_notes_entry.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AA+C7D;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,qBAAqB,EAAE,EAAE,mBAAmB,2CAmElF"}
@@ -0,0 +1,69 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { render_note_with_files } from './hazo_notes_file_preview.js';
4
+ /**
5
+ * Format timestamp for display
6
+ */
7
+ function format_timestamp(iso_timestamp) {
8
+ try {
9
+ const date = new Date(iso_timestamp);
10
+ return date.toLocaleString('en-US', {
11
+ month: 'short',
12
+ day: 'numeric',
13
+ hour: '2-digit',
14
+ minute: '2-digit',
15
+ });
16
+ }
17
+ catch {
18
+ return iso_timestamp;
19
+ }
20
+ }
21
+ /**
22
+ * Get initials from name for avatar
23
+ */
24
+ function get_initials(name) {
25
+ const parts = name.trim().split(/\s+/);
26
+ if (parts.length >= 2) {
27
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
28
+ }
29
+ return name.substring(0, 2).toUpperCase();
30
+ }
31
+ /**
32
+ * Generate consistent hex color from name for avatar background
33
+ */
34
+ function get_avatar_color(name) {
35
+ const colors = [
36
+ '#ef4444', '#f97316', '#d97706', '#22c55e',
37
+ '#14b8a6', '#3b82f6', '#6366f1', '#a855f7',
38
+ '#ec4899', '#f43f5e',
39
+ ];
40
+ let hash = 0;
41
+ for (let i = 0; i < name.length; i++) {
42
+ hash = name.charCodeAt(i) + ((hash << 5) - hash);
43
+ }
44
+ return colors[Math.abs(hash) % colors.length];
45
+ }
46
+ /**
47
+ * Single note entry display component
48
+ */
49
+ export function HazoNotesEntry({ note, ProfileStampComponent }) {
50
+ const { user_name, user_avatar, created_at, note_text, note_files } = note;
51
+ // Render avatar
52
+ const render_avatar = () => {
53
+ if (ProfileStampComponent) {
54
+ const custom_fields = [{ label: 'Posted', value: format_timestamp(created_at) }];
55
+ return (_jsx(ProfileStampComponent, { size: "sm", show_name: false, show_email: false, custom_fields: custom_fields }));
56
+ }
57
+ // Fallback to image or initials
58
+ if (user_avatar) {
59
+ return (_jsx("img", { src: user_avatar, alt: user_name, className: "w-7 h-7 rounded-full object-cover flex-shrink-0", title: user_name }));
60
+ }
61
+ // Initials avatar
62
+ return (_jsx("span", { className: "w-7 h-7 rounded-full inline-flex items-center justify-center text-xs font-bold flex-shrink-0", style: {
63
+ backgroundColor: get_avatar_color(user_name),
64
+ color: '#ffffff',
65
+ }, title: user_name, children: get_initials(user_name) }));
66
+ };
67
+ return (_jsxs("div", { className: "cls_hazo_notes_entry p-3", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [render_avatar(), !ProfileStampComponent && (_jsx("span", { className: "text-xs text-yellow-700 flex-shrink-0", children: format_timestamp(created_at) }))] }), _jsx("div", { className: "cls_hazo_notes_entry_content ml-9 overflow-hidden", children: _jsx("div", { className: "w-full min-h-[40px] rounded-md border border-yellow-400 px-3 py-2 text-sm text-yellow-900 overflow-hidden", style: { backgroundColor: '#d4c896' }, children: render_note_with_files(note_text, note_files) }) })] }));
68
+ }
69
+ //# sourceMappingURL=hazo_notes_entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hazo_notes_entry.js","sourceRoot":"","sources":["../../src/components/hazo_notes_entry.tsx"],"names":[],"mappings":"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"}