@classic-homes/notifications 0.1.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 ADDED
@@ -0,0 +1,277 @@
1
+ # @classic-homes/notifications
2
+
3
+ Framework-agnostic notifications core with Svelte bindings for the Classic Theme design system.
4
+
5
+ ## Features
6
+
7
+ - Persistent notifications from API with pagination
8
+ - Mark as read/unread functionality
9
+ - Batch operations (mark all read, delete all)
10
+ - Svelte reactive stores for notification state
11
+ - Derived stores for unread count and loading state
12
+ - TypeScript-first with full type safety
13
+ - Integrates with `@classic-homes/auth` for API authentication
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @classic-homes/notifications
19
+ ```
20
+
21
+ ## Prerequisites
22
+
23
+ This package requires `@classic-homes/auth` to be initialized first, as it uses the shared HTTP client and authentication headers.
24
+
25
+ ```typescript
26
+ import { initAuth } from '@classic-homes/auth';
27
+
28
+ initAuth({
29
+ baseUrl: 'https://api.example.com',
30
+ // ... other config
31
+ });
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ### 1. Fetch Notifications
37
+
38
+ ```typescript
39
+ import { notificationStore } from '@classic-homes/notifications/svelte';
40
+
41
+ // Fetch initial notifications
42
+ await notificationStore.fetch();
43
+
44
+ // With pagination
45
+ await notificationStore.fetch({ page: 1, limit: 20 });
46
+
47
+ // Filter by type or read status
48
+ await notificationStore.fetch({ type: 'info', unreadOnly: true });
49
+ ```
50
+
51
+ ### 2. Use in Svelte Components
52
+
53
+ ```svelte
54
+ <script lang="ts">
55
+ import {
56
+ notificationStore,
57
+ notifications,
58
+ unreadCount,
59
+ isLoadingNotifications,
60
+ } from '@classic-homes/notifications/svelte';
61
+
62
+ // Load notifications on mount
63
+ $effect(() => {
64
+ notificationStore.fetch();
65
+ });
66
+ </script>
67
+
68
+ {#if $isLoadingNotifications}
69
+ <Spinner />
70
+ {:else}
71
+ <p>You have {$unreadCount} unread notifications</p>
72
+
73
+ {#each $notifications as notification}
74
+ <div class:unread={!notification.read}>
75
+ <h4>{notification.title}</h4>
76
+ <p>{notification.message}</p>
77
+ <button onclick={() => notificationStore.markRead(notification.id)}> Mark as read </button>
78
+ </div>
79
+ {/each}
80
+ {/if}
81
+ ```
82
+
83
+ ## API Reference
84
+
85
+ ### Core Exports
86
+
87
+ ```typescript
88
+ import {
89
+ // API
90
+ notificationsApi,
91
+
92
+ // Service
93
+ notificationService,
94
+ NotificationService,
95
+
96
+ // Types
97
+ type Notification,
98
+ type NotificationType,
99
+ type NotificationPriority,
100
+ type NotificationPreferences,
101
+ } from '@classic-homes/notifications';
102
+ ```
103
+
104
+ ### Svelte Exports
105
+
106
+ ```typescript
107
+ import {
108
+ // Main store
109
+ notificationStore,
110
+
111
+ // Derived stores
112
+ notifications, // Notification[]
113
+ unreadCount, // number
114
+ isLoadingNotifications, // boolean
115
+
116
+ // Types
117
+ type Notification,
118
+ type NotificationType,
119
+ type NotificationPriority,
120
+ } from '@classic-homes/notifications/svelte';
121
+ ```
122
+
123
+ ## Notification Store Methods
124
+
125
+ ### Fetching
126
+
127
+ ```typescript
128
+ // Fetch notifications with optional filters
129
+ await notificationStore.fetch({
130
+ page?: number,
131
+ limit?: number,
132
+ type?: NotificationType,
133
+ unreadOnly?: boolean,
134
+ });
135
+
136
+ // Refresh current page
137
+ await notificationStore.refresh();
138
+
139
+ // Load more (next page)
140
+ await notificationStore.loadMore();
141
+ ```
142
+
143
+ ### Marking as Read
144
+
145
+ ```typescript
146
+ // Mark single notification as read
147
+ await notificationStore.markRead(notificationId);
148
+
149
+ // Mark multiple notifications as read
150
+ await notificationStore.markManyRead([id1, id2, id3]);
151
+
152
+ // Mark all notifications as read
153
+ await notificationStore.markAllRead();
154
+ ```
155
+
156
+ ### Deleting
157
+
158
+ ```typescript
159
+ // Dismiss single notification
160
+ await notificationStore.dismiss(notificationId);
161
+
162
+ // Delete all notifications
163
+ await notificationStore.deleteAll();
164
+ ```
165
+
166
+ ### State Management
167
+
168
+ ```typescript
169
+ // Clear error state
170
+ notificationStore.clearError();
171
+
172
+ // Reset store to initial state
173
+ notificationStore.reset();
174
+ ```
175
+
176
+ ## Notification Types
177
+
178
+ ```typescript
179
+ interface Notification {
180
+ id: string;
181
+ type: 'info' | 'success' | 'warning' | 'error';
182
+ priority: 'low' | 'normal' | 'high' | 'urgent';
183
+ title: string;
184
+ message: string;
185
+ read: boolean;
186
+ readAt?: string;
187
+ actionUrl?: string;
188
+ actionLabel?: string;
189
+ metadata?: Record<string, unknown>;
190
+ createdAt: string;
191
+ expiresAt?: string;
192
+ }
193
+ ```
194
+
195
+ ## Using with Toast Notifications
196
+
197
+ For client-side only toast notifications (not persisted), use `toastStore` from `@classic-homes/theme-svelte`:
198
+
199
+ ```svelte
200
+ <script lang="ts">
201
+ import { toastStore } from '@classic-homes/theme-svelte';
202
+
203
+ function showSuccess() {
204
+ toastStore.success('Operation completed!');
205
+ }
206
+
207
+ function showError() {
208
+ toastStore.error('Something went wrong');
209
+ }
210
+ </script>
211
+ ```
212
+
213
+ ## Store State
214
+
215
+ ```typescript
216
+ interface NotificationState {
217
+ notifications: Notification[];
218
+ unreadCount: number;
219
+ isLoading: boolean;
220
+ error: string | null;
221
+ pagination: {
222
+ page: number;
223
+ limit: number;
224
+ total: number;
225
+ totalPages: number;
226
+ };
227
+ }
228
+ ```
229
+
230
+ ## Example: Notification Bell
231
+
232
+ ```svelte
233
+ <script lang="ts">
234
+ import {
235
+ notificationStore,
236
+ notifications,
237
+ unreadCount,
238
+ } from '@classic-homes/notifications/svelte';
239
+ import { Badge, Button, DropdownMenu } from '@classic-homes/theme-svelte';
240
+
241
+ let isOpen = $state(false);
242
+
243
+ $effect(() => {
244
+ if (isOpen) {
245
+ notificationStore.fetch({ limit: 5 });
246
+ }
247
+ });
248
+ </script>
249
+
250
+ <DropdownMenu bind:open={isOpen}>
251
+ <Button slot="trigger" variant="ghost">
252
+ <BellIcon />
253
+ {#if $unreadCount > 0}
254
+ <Badge variant="destructive">{$unreadCount}</Badge>
255
+ {/if}
256
+ </Button>
257
+
258
+ <div slot="content">
259
+ {#each $notifications as notification}
260
+ <div class="notification-item">
261
+ <span>{notification.title}</span>
262
+ {#if !notification.read}
263
+ <button onclick={() => notificationStore.markRead(notification.id)}> Mark read </button>
264
+ {/if}
265
+ </div>
266
+ {/each}
267
+
268
+ {#if $unreadCount > 0}
269
+ <button onclick={() => notificationStore.markAllRead()}> Mark all as read </button>
270
+ {/if}
271
+ </div>
272
+ </DropdownMenu>
273
+ ```
274
+
275
+ ## License
276
+
277
+ MIT
@@ -0,0 +1,163 @@
1
+ import { d as NotificationListParams, c as NotificationsListResponse, b as Notification, k as ChannelsResponse, N as NotificationType, P as PreferencesResponse, U as UpdatePreferenceData, l as UpdatePreferenceResponse, B as BatchPreferenceUpdate, j as BulkPreferenceResponse, D as DeletePreferenceResponse, T as TestNotificationParams, m as TestNotificationResponse, e as NotificationChannel, g as NotificationPreference } from '../types-BSztr7EQ.js';
2
+ export { C as ChannelType, i as NotificationError, f as NotificationFrequency, h as NotificationPreferenceResult, a as NotificationPriority } from '../types-BSztr7EQ.js';
3
+
4
+ /**
5
+ * Notifications API
6
+ *
7
+ * API client methods for notification endpoints.
8
+ * Uses the auth package's HTTP client for making requests.
9
+ */
10
+
11
+ declare const notificationsApi: {
12
+ /**
13
+ * List notifications with optional filters.
14
+ */
15
+ list(params?: NotificationListParams, customFetch?: typeof fetch): Promise<NotificationsListResponse>;
16
+ /**
17
+ * Get a single notification by ID.
18
+ */
19
+ get(id: string, customFetch?: typeof fetch): Promise<Notification>;
20
+ /**
21
+ * Mark a notification as read.
22
+ */
23
+ markRead(id: string): Promise<void>;
24
+ /**
25
+ * Mark multiple notifications as read.
26
+ */
27
+ markManyRead(ids: string[]): Promise<void>;
28
+ /**
29
+ * Mark all notifications as read.
30
+ */
31
+ markAllRead(): Promise<void>;
32
+ /**
33
+ * Dismiss (delete) a notification.
34
+ */
35
+ dismiss(id: string): Promise<void>;
36
+ /**
37
+ * Delete multiple notifications in bulk.
38
+ */
39
+ bulkDelete(ids: string[]): Promise<void>;
40
+ /**
41
+ * Delete all notifications.
42
+ */
43
+ deleteAll(): Promise<void>;
44
+ /**
45
+ * Get available notification channels.
46
+ */
47
+ getChannels(customFetch?: typeof fetch): Promise<ChannelsResponse>;
48
+ /**
49
+ * Get notification preferences.
50
+ */
51
+ getPreferences(params?: {
52
+ channelId?: string;
53
+ notificationType?: NotificationType;
54
+ }, customFetch?: typeof fetch): Promise<PreferencesResponse>;
55
+ /**
56
+ * Update a notification preference.
57
+ */
58
+ updatePreference(preference: UpdatePreferenceData): Promise<UpdatePreferenceResponse>;
59
+ /**
60
+ * Batch update notification preferences.
61
+ */
62
+ batchUpdatePreferences(preferences: BatchPreferenceUpdate[]): Promise<BulkPreferenceResponse>;
63
+ /**
64
+ * Delete a notification preference.
65
+ */
66
+ deletePreference(subscriptionId: string): Promise<DeletePreferenceResponse>;
67
+ /**
68
+ * Generate test notifications.
69
+ */
70
+ generateTestNotifications(params?: TestNotificationParams): Promise<TestNotificationResponse>;
71
+ };
72
+
73
+ /**
74
+ * Notification Service
75
+ *
76
+ * Business logic layer for notification operations.
77
+ * Wraps notificationsApi calls and provides a clean interface for components.
78
+ */
79
+
80
+ /**
81
+ * NotificationService
82
+ *
83
+ * Provides a clean interface for notification operations.
84
+ * Can be instantiated for testing or used via the singleton export.
85
+ */
86
+ declare class NotificationService {
87
+ /**
88
+ * List notifications with optional filters.
89
+ */
90
+ list(params?: NotificationListParams, customFetch?: typeof fetch): Promise<NotificationsListResponse>;
91
+ /**
92
+ * Get a single notification by ID.
93
+ */
94
+ get(id: string, customFetch?: typeof fetch): Promise<Notification>;
95
+ /**
96
+ * Mark a notification as read.
97
+ */
98
+ markRead(id: string): Promise<void>;
99
+ /**
100
+ * Mark multiple notifications as read.
101
+ */
102
+ markManyRead(ids: string[]): Promise<void>;
103
+ /**
104
+ * Mark all notifications as read.
105
+ */
106
+ markAllRead(): Promise<void>;
107
+ /**
108
+ * Dismiss (delete) a notification.
109
+ */
110
+ dismiss(id: string): Promise<void>;
111
+ /**
112
+ * Delete multiple notifications in bulk.
113
+ */
114
+ bulkDelete(ids: string[]): Promise<void>;
115
+ /**
116
+ * Delete all notifications.
117
+ */
118
+ deleteAll(): Promise<void>;
119
+ /**
120
+ * Get available notification channels.
121
+ */
122
+ getChannels(customFetch?: typeof fetch): Promise<{
123
+ success: boolean;
124
+ channels: NotificationChannel[];
125
+ }>;
126
+ /**
127
+ * Get notification preferences.
128
+ */
129
+ getPreferences(params?: {
130
+ channelId?: string;
131
+ notificationType?: NotificationType;
132
+ }, customFetch?: typeof fetch): Promise<{
133
+ success: boolean;
134
+ preferences: NotificationPreference[];
135
+ }>;
136
+ /**
137
+ * Update a notification preference.
138
+ */
139
+ updatePreference(preference: UpdatePreferenceData): Promise<{
140
+ success: boolean;
141
+ message: string;
142
+ subscriptionId: string;
143
+ }>;
144
+ /**
145
+ * Batch update notification preferences.
146
+ */
147
+ batchUpdatePreferences(preferences: BatchPreferenceUpdate[]): Promise<BulkPreferenceResponse>;
148
+ /**
149
+ * Delete a notification preference.
150
+ */
151
+ deletePreference(subscriptionId: string): Promise<{
152
+ success: boolean;
153
+ message: string;
154
+ }>;
155
+ /**
156
+ * Generate test notifications (dev/admin only).
157
+ */
158
+ generateTestNotifications(params?: TestNotificationParams): Promise<TestNotificationResponse>;
159
+ }
160
+ /** Singleton instance of NotificationService */
161
+ declare const notificationService: NotificationService;
162
+
163
+ export { BatchPreferenceUpdate, BulkPreferenceResponse, ChannelsResponse, DeletePreferenceResponse, Notification, NotificationChannel, NotificationListParams, NotificationPreference, NotificationService, NotificationType, NotificationsListResponse, PreferencesResponse, TestNotificationParams, TestNotificationResponse, UpdatePreferenceData, UpdatePreferenceResponse, notificationService, notificationsApi };
@@ -0,0 +1,258 @@
1
+ import { api } from '@classic-homes/auth/core';
2
+
3
+ // src/core/api.ts
4
+ var notificationsApi = {
5
+ // ============================================================================
6
+ // Notifications CRUD
7
+ // ============================================================================
8
+ /**
9
+ * List notifications with optional filters.
10
+ */
11
+ async list(params, customFetch) {
12
+ const queryParams = new URLSearchParams();
13
+ const page = params?.page || 1;
14
+ const limit = params?.limit || 20;
15
+ const offset = (page - 1) * limit;
16
+ queryParams.append("limit", limit.toString());
17
+ queryParams.append("offset", offset.toString());
18
+ if (params?.unreadOnly) {
19
+ queryParams.append("read", "false");
20
+ } else if (params?.readOnly) {
21
+ queryParams.append("read", "true");
22
+ }
23
+ if (params?.type) {
24
+ queryParams.append("type", params.type);
25
+ }
26
+ const response = await api.get(
27
+ `/notifications?${queryParams.toString()}`,
28
+ true,
29
+ customFetch
30
+ );
31
+ return response;
32
+ },
33
+ /**
34
+ * Get a single notification by ID.
35
+ */
36
+ async get(id, customFetch) {
37
+ const response = await api.get(
38
+ `/notifications/${id}`,
39
+ true,
40
+ customFetch
41
+ );
42
+ return response.notification;
43
+ },
44
+ /**
45
+ * Mark a notification as read.
46
+ */
47
+ async markRead(id) {
48
+ await api.patch(`/notifications/${id}/read`, {}, true);
49
+ },
50
+ /**
51
+ * Mark multiple notifications as read.
52
+ */
53
+ async markManyRead(ids) {
54
+ await api.post("/notifications/mark-read", { ids }, true);
55
+ },
56
+ /**
57
+ * Mark all notifications as read.
58
+ */
59
+ async markAllRead() {
60
+ await api.post("/notifications/mark-read", { all: true }, true);
61
+ },
62
+ /**
63
+ * Dismiss (delete) a notification.
64
+ */
65
+ async dismiss(id) {
66
+ await api.delete(`/notifications/${id}`, true);
67
+ },
68
+ /**
69
+ * Delete multiple notifications in bulk.
70
+ */
71
+ async bulkDelete(ids) {
72
+ await api.post("/notifications/bulk-delete", { ids }, true);
73
+ },
74
+ /**
75
+ * Delete all notifications.
76
+ */
77
+ async deleteAll() {
78
+ await api.post("/notifications/bulk-delete", { all: true }, true);
79
+ },
80
+ // ============================================================================
81
+ // Notification Channels
82
+ // ============================================================================
83
+ /**
84
+ * Get available notification channels.
85
+ */
86
+ async getChannels(customFetch) {
87
+ const response = await api.get("/notifications/channels", true, customFetch);
88
+ return response;
89
+ },
90
+ // ============================================================================
91
+ // Notification Preferences
92
+ // ============================================================================
93
+ /**
94
+ * Get notification preferences.
95
+ */
96
+ async getPreferences(params, customFetch) {
97
+ const queryParams = new URLSearchParams();
98
+ if (params?.channelId) queryParams.append("channelId", params.channelId);
99
+ if (params?.notificationType) queryParams.append("notificationType", params.notificationType);
100
+ const response = await api.get(
101
+ `/notifications/preferences?${queryParams.toString()}`,
102
+ true,
103
+ customFetch
104
+ );
105
+ return response;
106
+ },
107
+ /**
108
+ * Update a notification preference.
109
+ */
110
+ async updatePreference(preference) {
111
+ const response = await api.put(
112
+ "/notifications/preferences",
113
+ preference,
114
+ true
115
+ );
116
+ return response;
117
+ },
118
+ /**
119
+ * Batch update notification preferences.
120
+ */
121
+ async batchUpdatePreferences(preferences) {
122
+ const response = await api.post(
123
+ "/notifications/preferences/batch",
124
+ { preferences },
125
+ true
126
+ );
127
+ const data = response;
128
+ return data.data || data;
129
+ },
130
+ /**
131
+ * Delete a notification preference.
132
+ */
133
+ async deletePreference(subscriptionId) {
134
+ const response = await api.delete(
135
+ `/notifications/preferences/${subscriptionId}`,
136
+ true
137
+ );
138
+ return response;
139
+ },
140
+ // ============================================================================
141
+ // Test Notifications (Dev/Admin)
142
+ // ============================================================================
143
+ /**
144
+ * Generate test notifications.
145
+ */
146
+ async generateTestNotifications(params) {
147
+ const response = await api.post(
148
+ "/notifications/test",
149
+ params || {},
150
+ true
151
+ );
152
+ const data = response;
153
+ return data.data ?? data;
154
+ }
155
+ };
156
+
157
+ // src/core/service.ts
158
+ var NotificationService = class {
159
+ // ============================================================================
160
+ // Notifications CRUD
161
+ // ============================================================================
162
+ /**
163
+ * List notifications with optional filters.
164
+ */
165
+ async list(params, customFetch) {
166
+ return await notificationsApi.list(params, customFetch);
167
+ }
168
+ /**
169
+ * Get a single notification by ID.
170
+ */
171
+ async get(id, customFetch) {
172
+ return await notificationsApi.get(id, customFetch);
173
+ }
174
+ /**
175
+ * Mark a notification as read.
176
+ */
177
+ async markRead(id) {
178
+ await notificationsApi.markRead(id);
179
+ }
180
+ /**
181
+ * Mark multiple notifications as read.
182
+ */
183
+ async markManyRead(ids) {
184
+ await notificationsApi.markManyRead(ids);
185
+ }
186
+ /**
187
+ * Mark all notifications as read.
188
+ */
189
+ async markAllRead() {
190
+ await notificationsApi.markAllRead();
191
+ }
192
+ /**
193
+ * Dismiss (delete) a notification.
194
+ */
195
+ async dismiss(id) {
196
+ await notificationsApi.dismiss(id);
197
+ }
198
+ /**
199
+ * Delete multiple notifications in bulk.
200
+ */
201
+ async bulkDelete(ids) {
202
+ await notificationsApi.bulkDelete(ids);
203
+ }
204
+ /**
205
+ * Delete all notifications.
206
+ */
207
+ async deleteAll() {
208
+ await notificationsApi.deleteAll();
209
+ }
210
+ // ============================================================================
211
+ // Notification Channels
212
+ // ============================================================================
213
+ /**
214
+ * Get available notification channels.
215
+ */
216
+ async getChannels(customFetch) {
217
+ return await notificationsApi.getChannels(customFetch);
218
+ }
219
+ // ============================================================================
220
+ // Notification Preferences
221
+ // ============================================================================
222
+ /**
223
+ * Get notification preferences.
224
+ */
225
+ async getPreferences(params, customFetch) {
226
+ return await notificationsApi.getPreferences(params, customFetch);
227
+ }
228
+ /**
229
+ * Update a notification preference.
230
+ */
231
+ async updatePreference(preference) {
232
+ return await notificationsApi.updatePreference(preference);
233
+ }
234
+ /**
235
+ * Batch update notification preferences.
236
+ */
237
+ async batchUpdatePreferences(preferences) {
238
+ return await notificationsApi.batchUpdatePreferences(preferences);
239
+ }
240
+ /**
241
+ * Delete a notification preference.
242
+ */
243
+ async deletePreference(subscriptionId) {
244
+ return await notificationsApi.deletePreference(subscriptionId);
245
+ }
246
+ // ============================================================================
247
+ // Test Notifications
248
+ // ============================================================================
249
+ /**
250
+ * Generate test notifications (dev/admin only).
251
+ */
252
+ async generateTestNotifications(params) {
253
+ return await notificationsApi.generateTestNotifications(params);
254
+ }
255
+ };
256
+ var notificationService = new NotificationService();
257
+
258
+ export { NotificationService, notificationService, notificationsApi };
@@ -0,0 +1,3 @@
1
+ export { NotificationService, notificationService, notificationsApi } from './core/index.js';
2
+ export { B as BatchPreferenceUpdate, j as BulkPreferenceResponse, C as ChannelType, k as ChannelsResponse, D as DeletePreferenceResponse, b as Notification, e as NotificationChannel, i as NotificationError, f as NotificationFrequency, d as NotificationListParams, g as NotificationPreference, h as NotificationPreferenceResult, a as NotificationPriority, N as NotificationType, c as NotificationsListResponse, P as PreferencesResponse, T as TestNotificationParams, m as TestNotificationResponse, U as UpdatePreferenceData, l as UpdatePreferenceResponse } from './types-BSztr7EQ.js';
3
+ export { isLoadingNotifications, notificationStore, notifications, unreadCount } from './svelte/index.js';