Skip to content

Commit d69cb98

Browse files
authored
Here's a concise commit message: (#715)
``` feat: Add enhanced notification system with queue management and error handling - Implement NotificationManager with queue, concurrency control, and stats - Add network status monitoring in NaiveProvider - Create notification demo component for testing - Improve error handling with better messages and retry actions - Add i18n error messages - Update notification placement and behavior in UI ```
1 parent 7c26dfe commit d69cb98

6 files changed

Lines changed: 605 additions & 34 deletions

File tree

web/src/components/common/NaiveProvider/index.vue

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import { defineComponent, h } from 'vue'
2+
import { defineComponent, h, onMounted, onUnmounted } from 'vue'
33
import {
44
NDialogProvider,
55
NLoadingBarProvider,
@@ -10,14 +10,48 @@ import {
1010
useMessage,
1111
useNotification,
1212
} from 'naive-ui'
13+
import { notificationManager } from '@/utils/notificationManager'
1314
1415
function registerNaiveTools() {
1516
window.$loadingBar = useLoadingBar()
1617
window.$dialog = useDialog()
1718
window.$message = useMessage()
1819
window.$notification = useNotification()
20+
21+
// Initialize notification manager
22+
notificationManager.setMessageInstance(window.$message)
1923
}
2024
25+
// Handle online/offline status
26+
function handleNetworkStatus() {
27+
if (!navigator.onLine) {
28+
window.$message?.error('You are offline. Please check your internet connection.', {
29+
duration: 0,
30+
closable: true,
31+
action: {
32+
text: 'Retry',
33+
onClick: () => window.location.reload()
34+
}
35+
})
36+
}
37+
}
38+
39+
onMounted(() => {
40+
window.addEventListener('online', () => {
41+
window.$message?.success('You are back online!', { duration: 3000 })
42+
})
43+
44+
window.addEventListener('offline', handleNetworkStatus)
45+
46+
// Check initial network status
47+
handleNetworkStatus()
48+
})
49+
50+
onUnmounted(() => {
51+
window.removeEventListener('online', () => {})
52+
window.removeEventListener('offline', handleNetworkStatus)
53+
})
54+
2155
const NaiveProviderContent = defineComponent({
2256
name: 'NaiveProviderContent',
2357
setup() {
@@ -32,12 +66,17 @@ const NaiveProviderContent = defineComponent({
3266
<template>
3367
<NLoadingBarProvider>
3468
<NDialogProvider>
35-
<NNotificationProvider>
36-
<NMessageProvider placement="top-right" >
37-
<slot />
38-
<NaiveProviderContent />
39-
</NMessageProvider>
40-
</NNotificationProvider>
69+
<NNotificationProvider :max="5" :placement="'top-right'">
70+
<NMessageProvider
71+
:placement="'top-right'"
72+
:max="3"
73+
:duration="5000"
74+
:closable="true"
75+
>
76+
<slot />
77+
<NaiveProviderContent />
78+
</NMessageProvider>
79+
</NNotificationProvider>
4180
</NDialogProvider>
4281
</NLoadingBarProvider>
4382
</template>
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
<template>
2+
<div class="notification-demo">
3+
<n-card title="Notification System Demo">
4+
<n-space vertical>
5+
<div class="demo-section">
6+
<h3>Basic Notifications</h3>
7+
<n-space>
8+
<n-button @click="showSuccess" type="success">Success</n-button>
9+
<n-button @click="showError" type="error">Error</n-button>
10+
<n-button @click="showWarning" type="warning">Warning</n-button>
11+
<n-button @click="showInfo" type="info">Info</n-button>
12+
</n-space>
13+
</div>
14+
15+
<div class="demo-section">
16+
<h3>Persistent Notifications</h3>
17+
<n-space>
18+
<n-button @click="showPersistentError" type="error">Persistent Error</n-button>
19+
<n-button @click="showPersistentWarning" type="warning">Persistent Warning</n-button>
20+
</n-space>
21+
</div>
22+
23+
<div class="demo-section">
24+
<h3>Action Notifications</h3>
25+
<n-space>
26+
<n-button @click="showActionError" type="error">Error with Action</n-button>
27+
<n-button @click="showActionSuccess" type="success">Success with Action</n-button>
28+
</n-space>
29+
</div>
30+
31+
<div class="demo-section">
32+
<h3>Batch Notifications</h3>
33+
<n-space>
34+
<n-button @click="showBatch" type="primary">Show Batch (5)</n-button>
35+
<n-button @click="clearAll" type="default">Clear All</n-button>
36+
</n-space>
37+
</div>
38+
39+
<div class="demo-section">
40+
<h3>Network Status</h3>
41+
<n-space>
42+
<n-button @click="simulateOffline" type="warning">Simulate Offline</n-button>
43+
<n-button @click="simulateOnline" type="success">Simulate Online</n-button>
44+
</n-space>
45+
</div>
46+
47+
<div class="demo-section">
48+
<h3>Error Simulation</h3>
49+
<n-space>
50+
<n-button @click="simulateNetworkError" type="error">Network Error</n-button>
51+
<n-button @click="simulateTimeout" type="warning">Timeout Error</n-button>
52+
<n-button @click="simulateAuthError" type="error">Auth Error</n-button>
53+
<n-button @click="simulateServerError" type="error">Server Error</n-button>
54+
</n-space>
55+
</div>
56+
57+
<div class="demo-section">
58+
<h3>Notification Stats</h3>
59+
<n-space vertical>
60+
<div>Queued: {{ stats.queued }}</div>
61+
<div>Active: {{ stats.active }}</div>
62+
<div>Max Concurrent: {{ stats.maxConcurrent }}</div>
63+
</n-space>
64+
</div>
65+
</n-space>
66+
</n-card>
67+
</div>
68+
</template>
69+
70+
<script setup lang="ts">
71+
import { useNotification } from '@/utils/notificationManager'
72+
import { useErrorHandling } from '@/views/chat/composables/useErrorHandling'
73+
import { ref } from 'vue'
74+
75+
const notification = useNotification()
76+
const { handleApiError } = useErrorHandling()
77+
78+
const stats = ref(notification.stats)
79+
80+
// Basic notifications
81+
function showSuccess() {
82+
notification.success('Operation completed successfully!')
83+
}
84+
85+
function showError() {
86+
notification.error('Something went wrong!')
87+
}
88+
89+
function showWarning() {
90+
notification.warning('Please be careful with this action.')
91+
}
92+
93+
function showInfo() {
94+
notification.info('Here is some information for you.')
95+
}
96+
97+
// Persistent notifications
98+
function showPersistentError() {
99+
notification.persistent('This is a persistent error message. It will stay until you close it.', 'error', {
100+
text: 'Retry',
101+
onClick: () => notification.success('Retried successfully!')
102+
})
103+
}
104+
105+
function showPersistentWarning() {
106+
notification.persistent('This is a persistent warning message.', 'warning', {
107+
text: 'Dismiss',
108+
onClick: () => notification.info('Warning dismissed')
109+
})
110+
}
111+
112+
// Action notifications
113+
function showActionError() {
114+
notification.error('Failed to save changes', {
115+
duration: 8000,
116+
action: {
117+
text: 'Retry',
118+
onClick: () => notification.success('Changes saved successfully!')
119+
}
120+
})
121+
}
122+
123+
function showActionSuccess() {
124+
notification.success('File uploaded successfully!', {
125+
action: {
126+
text: 'View File',
127+
onClick: () => notification.info('Opening file...')
128+
}
129+
})
130+
}
131+
132+
// Batch notifications
133+
function showBatch() {
134+
const messages = [
135+
{ type: 'success', text: 'Item 1 created' },
136+
{ type: 'error', text: 'Item 2 failed' },
137+
{ type: 'warning', text: 'Item 3 has warnings' },
138+
{ type: 'info', text: 'Item 4 processed' },
139+
{ type: 'success', text: 'Item 5 completed' }
140+
]
141+
142+
messages.forEach((msg, index) => {
143+
setTimeout(() => {
144+
notification[msg.type](msg.text)
145+
}, index * 500)
146+
})
147+
}
148+
149+
// Clear all notifications
150+
function clearAll() {
151+
notification.clear()
152+
}
153+
154+
// Network status simulation
155+
function simulateOffline() {
156+
notification.persistent('You are offline. Please check your internet connection.', 'error', {
157+
text: 'Retry',
158+
onClick: () => notification.success('Connection restored!')
159+
})
160+
}
161+
162+
function simulateOnline() {
163+
notification.success('You are back online!')
164+
}
165+
166+
// Error simulation
167+
function simulateNetworkError() {
168+
const error = new Error('Network Error')
169+
error.name = 'Network Error'
170+
error.message = 'Failed to connect to server'
171+
handleApiError(error, 'demo')
172+
}
173+
174+
function simulateTimeout() {
175+
const error = new Error('Timeout Error')
176+
error.name = 'Timeout Error'
177+
error.message = 'Request timed out after 30 seconds'
178+
handleApiError(error, 'demo')
179+
}
180+
181+
function simulateAuthError() {
182+
const error = new Error('Auth Error')
183+
error.name = 'Auth Error'
184+
error.message = 'Authentication failed'
185+
error.response = {
186+
status: 401,
187+
data: { message: 'Invalid credentials' }
188+
}
189+
handleApiError(error, 'demo')
190+
}
191+
192+
function simulateServerError() {
193+
const error = new Error('Server Error')
194+
error.name = 'Server Error'
195+
error.message = 'Internal server error'
196+
error.response = {
197+
status: 500,
198+
data: { message: 'Something went wrong on our end' }
199+
}
200+
handleApiError(error, 'demo')
201+
}
202+
</script>
203+
204+
<style scoped>
205+
.notification-demo {
206+
padding: 20px;
207+
max-width: 800px;
208+
margin: 0 auto;
209+
}
210+
211+
.demo-section {
212+
margin-bottom: 24px;
213+
padding: 16px;
214+
border: 1px solid #e5e7eb;
215+
border-radius: 8px;
216+
background: #f9fafb;
217+
}
218+
219+
.demo-section h3 {
220+
margin-top: 0;
221+
margin-bottom: 12px;
222+
color: #374151;
223+
font-size: 16px;
224+
font-weight: 600;
225+
}
226+
</style>

web/src/locales/en-US.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,16 @@
225225
"MODEL_006": "Failed to get a response from the model",
226226
"RESOURCE_EXHAUSTED": "Resource exhausted",
227227
"NotAdmin": "Non-administrators are prohibited from accessing",
228+
"unauthorized": "Session expired. Please login again.",
229+
"forbidden": "Access denied. You don't have permission for this action.",
230+
"notFound": "The requested resource was not found.",
231+
"serverError": "Server error. Our team has been notified and is working on a fix.",
232+
"timeout": "Request timed out. Please check your connection and try again.",
233+
"network": "Network connection error. Please check your internet connection.",
234+
"validation": "Please check your input and try again.",
235+
"rateLimit": "Too many requests. Please wait a moment before trying again.",
236+
"connection": "Connection interrupted. Please check your connection and try again.",
237+
"unknown": "An unexpected error occurred. Please try again.",
228238
"NotAuthorized": "Please log in first",
229239
"VALD_001": "Invalid Request",
230240
"VALD_004": "Invalid email or password",

web/src/utils/errorHandler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { logger } from './logger'
22
import { useAuthStore } from '@/store'
3+
import { showNotification, showErrorNotification, showWarningNotification, showSuccessNotification, showPersistentNotification } from './notificationManager'
34

45
export interface ApiError {
56
status: number

0 commit comments

Comments
 (0)