PluginProbe
The Innovative Form Builder – IvyForms / 0.9
The Innovative Form Builder – IvyForms v0.9
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 0.9, at frontend/src/stores/useNotificationSettingBuilder.ts

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