PluginProbe
The Innovative Form Builder – IvyForms / 1.0
The Innovative Form Builder – IvyForms v1.0
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / frontend / src / stores / useNotificationSettingBuilder.ts

useNotificationSettingBuilder.ts in The Innovative Form Builder – IvyForms 1.0, at frontend/src/stores/useNotificationSettingBuilder.ts

417 lines 12.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { defineStore } from 'pinia'
2 import { ref, watch, nextTick } from 'vue'
3 import { useLabels } from '@/composables/useLabels'
4 import IvyMessage from '@/views/_components/message/ivyMessage.ts'
5 import { useApiClient } from '@/composables/useApiClient'
6 import { useUnsavedChangesStore } from '@/stores/useUnsavedChangesStore'
7 import { getErrorMessage } from '@/utils/errorHandling'
8
9 export const useNotificationSettingBuilder = defineStore('notificationSettingBuilder', () => {
10 const allNotifications = ref([])
11 const { getLabel } = useLabels()
12 const tableData = ref([])
13 const paginationMeta = ref({
14 page: 1,
15 perPage: 10,
16 total: 0,
17 })
18 const name = ref<string | null>(getLabel('new_notification'))
19 const sender = ref<string | null>('')
20 const replyTo = ref<string | null>('')
21 const receiver = ref<string | null>('')
22 const enabled = ref(false)
23 const showEmptyFields = ref(false)
24 const subject = ref<string | null>('')
25 const message = ref<string | null>('')
26 const smartLogic = ref(false)
27 const formId = ref<string | null>(null)
28 const id = ref<number | null>(null)
29 const isEditing = ref(false) // Make `isEditing` reactive
30
31 // Use unsaved changes store for state management
32 const unsavedChangesStore = useUnsavedChangesStore()
33
34 // Flag to suppress change tracking during load/reset operations
35 let suppressDirtyTracking = false
36
37 /**
38 * Mark the notification builder as having unsaved changes
39 */
40 const markDirty = () => {
41 if (!suppressDirtyTracking && isEditing.value) {
42 unsavedChangesStore.markDirty('notificationBuilder')
43 }
44 }
45
46 /**
47 * Mark the notification builder as clean (no unsaved changes)
48 */
49 const markClean = () => {
50 unsavedChangesStore.markClean('notificationBuilder')
51 }
52
53 // Watch all tracked state for changes and mark dirty
54 watch([name, sender, replyTo, receiver, enabled, showEmptyFields, subject, message], () =>
55 markDirty(),
56 )
57
58 const { request } = useApiClient()
59
60 // Load existing notification if notification id is provided
61 const loadNotification = async (NotifId: number) => {
62 // Suppress dirty tracking during load
63 suppressDirtyTracking = true
64
65 try {
66 const { data, error, status } = await request(`/notification/${NotifId}`, {
67 method: 'GET',
68 })
69 if (!error && status === 200 && data?.data?.data) {
70 const responseData = data.data.data
71 formId.value = responseData.formId
72 id.value = NotifId
73 name.value = responseData.name || getLabel('new_notification')
74 sender.value = responseData.sender || ''
75 replyTo.value = responseData.replyTo || ''
76 receiver.value = responseData.receiver || ''
77 enabled.value = responseData.enabled || false
78 showEmptyFields.value = Boolean(responseData.showEmptyFields)
79 subject.value = responseData.subject || getLabel('new_notification')
80 message.value = responseData.message || ''
81 smartLogic.value = false
82 isEditing.value = true
83
84 // Start editing and mark clean (just loaded)
85 unsavedChangesStore.startEditing('notificationBuilder')
86 markClean()
87 } else {
88 IvyMessage({
89 message: `${getLabel('failed_to_load_notification')} ${data?.message || error || ''}`,
90 type: 'error',
91 })
92 }
93 } catch (error) {
94 const errorMessage = getErrorMessage(error)
95 IvyMessage({
96 message: `${getLabel('failed_to_load_notification')} ${errorMessage}`,
97 type: 'error',
98 })
99 // Reset editing state on error
100 isEditing.value = false
101 unsavedChangesStore.stopEditing('notificationBuilder')
102 } finally {
103 // Wait for watchers to fire before re-enabling dirty tracking
104 await nextTick()
105 suppressDirtyTracking = false
106 }
107 }
108
109 // Fetch all form notifications
110 const fetchData = async (formId?: number) => {
111 try {
112 const url = formId ? `/notifications/${formId}` : `/notifications`
113 const { data, error } = await request(url, { method: 'GET' })
114 if (data && data.data) {
115 allNotifications.value = data.data.data
116 } else {
117 allNotifications.value = []
118 IvyMessage({
119 message: `${getLabel('unexpected_response_structure')} ${error || data || ''}`,
120 type: 'error',
121 })
122 }
123 } catch (error) {
124 const errorMessage = getErrorMessage(error)
125 IvyMessage({
126 message: `${getLabel('failed_to_fetch_notification')} ${errorMessage}`,
127 type: 'error',
128 })
129 }
130 }
131
132 // Fetch all notifications
133 const fetchAllNotifications = async () => {
134 try {
135 const url = `/notifications`
136 const { data, error } = await request(url, { method: 'GET' })
137 if (data && data.data) {
138 allNotifications.value = data.data.data
139 } else {
140 allNotifications.value = []
141 IvyMessage({
142 message: `${getLabel('unexpected_response_structure')} ${error || data || ''}`,
143 type: 'error',
144 })
145 }
146 } catch (error) {
147 const errorMessage = getErrorMessage(error)
148 IvyMessage({
149 message: `${getLabel('failed_to_fetch_notification')} ${errorMessage}`,
150 type: 'error',
151 })
152 }
153 }
154
155 // Reset notification to default values
156 const resetNotification = (formIdRoute: string) => {
157 // Suppress dirty tracking during reset
158 suppressDirtyTracking = true
159
160 name.value = getLabel('new_notification')
161 sender.value = ''
162 replyTo.value = ''
163 receiver.value = ''
164 enabled.value = true
165 showEmptyFields.value = false
166 subject.value = getLabel('new_notification')
167 message.value = '{{all_data}}'
168 smartLogic.value = false
169 formId.value = formIdRoute
170 id.value = null
171 isEditing.value = false // Reset to false when resetting notification
172
173 // Stop editing and clear dirty flag
174 unsavedChangesStore.stopEditing('notificationBuilder')
175
176 // Re-enable dirty tracking
177 suppressDirtyTracking = false
178 }
179
180 // Create a new notification
181 const createNotification = async (formId) => {
182 const notificationData = {
183 name: name.value || getLabel('new_notification'),
184 sender: sender.value || '',
185 replyTo: replyTo.value || '',
186 receiver: receiver.value || '',
187 enabled: enabled.value || false,
188 showEmptyFields: showEmptyFields.value || false,
189 subject: subject.value || getLabel('new_notification'),
190 message: message.value || '',
191 smartLogic: smartLogic.value || false,
192 formId: formId || null,
193 }
194
195 try {
196 const { data, error, status } = await request('/notification/add/', {
197 method: 'POST',
198 data: notificationData,
199 })
200 if (!error && status === 200) {
201 id.value = data.data.data.id
202 allNotifications.value.push(data.data)
203 IvyMessage({
204 message: getLabel('create_notification'),
205 type: 'success',
206 })
207 markClean()
208 } else {
209 IvyMessage({
210 message: `${getLabel('failed_to_create_notification')}: ${data?.message || error || ''}`,
211 type: 'error',
212 })
213 }
214 } catch (error) {
215 const errorMessage = getErrorMessage(error)
216 IvyMessage({
217 message: `${getLabel('error_creating_notification')} ${errorMessage}`,
218 type: 'error',
219 })
220 }
221 }
222
223 // Update notification data
224 const updateNotification = async (NotifId, dataObj) => {
225 try {
226 const { data, error, status } = await request(`/notification/update/${NotifId}/`, {
227 method: 'POST',
228 data: dataObj,
229 })
230 if (!error && status === 200) {
231 IvyMessage({
232 message: getLabel('update_notification'),
233 type: 'success',
234 })
235 markClean()
236 } else {
237 IvyMessage({
238 message: `${getLabel('failed_to_update_notification')}: ${data?.message || error || ''}`,
239 type: 'error',
240 })
241 }
242 } catch (error) {
243 const errorMessage = getErrorMessage(error)
244 IvyMessage({
245 message: `${getLabel('error_updating_notification')} ${errorMessage}`,
246 type: 'error',
247 })
248 }
249 }
250
251 // Search notifications with pagination and sorting
252 const searchNotifications = async ({
253 page = 1,
254 perPage = 10,
255 search = '',
256 sortBy = '',
257 sortOrder = 'asc',
258 filters = {},
259 formId = null,
260 }: {
261 page?: number
262 perPage?: number
263 search?: string
264 sortBy?: string
265 sortOrder?: string
266 filters?: Record<string, string | number | null>
267 formId?: number | null
268 } = {}) => {
269 try {
270 const mergedFilters = { ...filters }
271 if (formId !== null && formId !== undefined) {
272 mergedFilters.formId = formId
273 }
274 const params: Record<
275 string,
276 string | number | boolean | null | Record<string, string | number | null>
277 > = {
278 page,
279 perPage,
280 search,
281 orderBy: sortBy,
282 order: sortOrder,
283 filters: mergedFilters,
284 }
285
286 const { data, error } = await request('notifications/search', {
287 method: 'GET',
288 params,
289 })
290
291 if (error) {
292 const errorMessage = getErrorMessage(error)
293 IvyMessage({
294 message: `${getLabel('error_fetching_notifications')} ${errorMessage}`,
295 type: 'error',
296 })
297 return
298 }
299
300 const response = data.data
301 tableData.value = normalizeNotificationData(response.data.data || [])
302 paginationMeta.value = response.data.meta || { page: 1, perPage: 10, total: 0 }
303 } catch (error) {
304 const errorMessage = getErrorMessage(error)
305 IvyMessage({
306 message: `${getLabel('error_fetching_notifications')} ${errorMessage}`,
307 type: 'error',
308 })
309 }
310 }
311
312 // Define proper types for notification data
313 interface RawNotificationItem {
314 id?: number
315 name?: string
316 sender?: string
317 receiver?: string
318 subject?: string
319 message?: string
320 enabled?: string | number | boolean
321 formId?: number
322 [key: string]: unknown // For any wrapped notification properties
323 }
324
325 interface NormalizedNotification {
326 id: number
327 name: string
328 sender: string
329 receiver: string
330 subject: string
331 message: string
332 enabled: boolean
333 formId: number
334 }
335
336 // Normalize notification data structure
337 const normalizeNotificationData = (data: RawNotificationItem[]): NormalizedNotification[] => {
338 return data.map((item) => {
339 if (item && typeof item === 'object') {
340 const notificationKey = Object.keys(item).find(
341 (key) => key.includes('notification') || key.includes('Notification'),
342 )
343
344 if (notificationKey && item[notificationKey]) {
345 const notification = item[notificationKey] as RawNotificationItem
346 return {
347 id: notification.id || 0,
348 name: notification.name || '',
349 sender: notification.sender || '',
350 replyTo: notification.replyTo || '',
351 receiver: notification.receiver || '',
352 subject: notification.subject || '',
353 message: notification.message || '',
354 enabled: notification.enabled === '1',
355 formId: notification.formId || 0,
356 }
357 }
358
359 // If it's a direct notification object
360 if (item.id && item.name !== undefined) {
361 return {
362 id: item.id,
363 name: item.name,
364 sender: item.sender || '',
365 replyTo: item.replyTo || '',
366 receiver: item.receiver || '',
367 subject: item.subject || '',
368 message: item.message || '',
369 enabled: item.enabled === '1',
370 formId: item.formId || null,
371 }
372 }
373 }
374
375 // Return a fallback object for items that couldn't be normalized
376 return {
377 id: 0,
378 name: 'Unknown',
379 sender: '',
380 replyTo: '',
381 receiver: '',
382 subject: '',
383 message: '',
384 enabled: false,
385 formId: null,
386 }
387 })
388 }
389
390 return {
391 allNotifications,
392 tableData,
393 paginationMeta,
394 name,
395 sender,
396 replyTo,
397 receiver,
398 enabled,
399 showEmptyFields,
400 subject,
401 message,
402 smartLogic,
403 formId,
404 id,
405 isEditing,
406 markDirty,
407 markClean,
408 fetchData,
409 fetchAllNotifications,
410 resetNotification,
411 createNotification,
412 updateNotification,
413 loadNotification,
414 searchNotifications,
415 }
416 })
417