@visns-studio/visns-components 5.8.10 → 5.8.12
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/package.json +7 -7
- package/src/components/crm/generic/ConfirmationDialog.jsx +97 -0
- package/src/components/crm/generic/GenericQuote.jsx +413 -120
- package/src/components/crm/generic/GenericReport.jsx +153 -17
- package/src/components/crm/generic/SortableQuoteItems.jsx +114 -70
- package/src/components/crm/generic/styles/GenericReport.module.scss +27 -0
|
@@ -8,7 +8,7 @@ import get from 'lodash/get';
|
|
|
8
8
|
import { saveAs } from 'file-saver';
|
|
9
9
|
import { toast } from 'react-toastify';
|
|
10
10
|
import { arrayMoveImmutable } from 'array-move';
|
|
11
|
-
import {
|
|
11
|
+
import { showConfirmDialog } from './ConfirmationDialog';
|
|
12
12
|
|
|
13
13
|
import CustomFetch from '../../crm/Fetch';
|
|
14
14
|
import ActionButtons from './ActionButtons';
|
|
@@ -34,7 +34,12 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
34
34
|
const [formData, setFormData] = useState({});
|
|
35
35
|
|
|
36
36
|
// Modal functions
|
|
37
|
-
const modalOpen = (
|
|
37
|
+
const modalOpen = (
|
|
38
|
+
type,
|
|
39
|
+
id,
|
|
40
|
+
customFormSettings = null,
|
|
41
|
+
itemData = null
|
|
42
|
+
) => {
|
|
38
43
|
// Debug logging to help troubleshoot form settings issues
|
|
39
44
|
console.log('Opening modal with form settings:', {
|
|
40
45
|
type,
|
|
@@ -42,7 +47,7 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
42
47
|
hasCustomSettings: !!customFormSettings,
|
|
43
48
|
customFormSettings,
|
|
44
49
|
defaultForm: form,
|
|
45
|
-
itemData
|
|
50
|
+
itemData,
|
|
46
51
|
});
|
|
47
52
|
|
|
48
53
|
// Use custom form settings if provided, otherwise use the default form
|
|
@@ -58,9 +63,10 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
58
63
|
...formSettings,
|
|
59
64
|
create: {
|
|
60
65
|
title: customFormSettings?.create?.title || 'Create New',
|
|
61
|
-
submitUrl:
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
submitUrl:
|
|
67
|
+
customFormSettings?.create?.submitUrl || '/api/submit',
|
|
68
|
+
method: customFormSettings?.create?.method || 'POST',
|
|
69
|
+
},
|
|
64
70
|
};
|
|
65
71
|
}
|
|
66
72
|
|
|
@@ -68,7 +74,7 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
68
74
|
if (itemData && type === 'update') {
|
|
69
75
|
formSettings = {
|
|
70
76
|
...formSettings,
|
|
71
|
-
initialData: itemData
|
|
77
|
+
initialData: itemData,
|
|
72
78
|
};
|
|
73
79
|
}
|
|
74
80
|
|
|
@@ -93,13 +99,17 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
93
99
|
const newData = { ...data };
|
|
94
100
|
|
|
95
101
|
// Reorder the items in the specified table
|
|
96
|
-
newData[tableId] = arrayMoveImmutable(
|
|
102
|
+
newData[tableId] = arrayMoveImmutable(
|
|
103
|
+
newData[tableId],
|
|
104
|
+
oldIndex,
|
|
105
|
+
newIndex
|
|
106
|
+
);
|
|
97
107
|
|
|
98
108
|
// Update the state immediately for better UX
|
|
99
109
|
setData(newData);
|
|
100
110
|
|
|
101
111
|
// Find the table configuration
|
|
102
|
-
const tableConfig = setting.tables.find(t => t.id === tableId);
|
|
112
|
+
const tableConfig = setting.tables.find((t) => t.id === tableId);
|
|
103
113
|
if (!tableConfig || !tableConfig.urls || !tableConfig.urls.sort) {
|
|
104
114
|
console.error('Sort URL not found for table:', tableId);
|
|
105
115
|
toast.error('Failed to update item order: Missing configuration');
|
|
@@ -110,7 +120,9 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
110
120
|
const { url, key } = tableConfig.urls.sort;
|
|
111
121
|
|
|
112
122
|
// Get CSRF token
|
|
113
|
-
const csrfToken = document
|
|
123
|
+
const csrfToken = document
|
|
124
|
+
.querySelector('meta[name="csrf-token"]')
|
|
125
|
+
?.getAttribute('content');
|
|
114
126
|
|
|
115
127
|
// Show a loading toast
|
|
116
128
|
const toastId = toast.loading('Updating item order...');
|
|
@@ -123,7 +135,7 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
123
135
|
|
|
124
136
|
// Create a request body with just the sort_order key
|
|
125
137
|
const requestBody = {
|
|
126
|
-
[key]: sortOrder
|
|
138
|
+
[key]: sortOrder,
|
|
127
139
|
};
|
|
128
140
|
|
|
129
141
|
// Send individual request for this item
|
|
@@ -134,9 +146,8 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
134
146
|
'X-Requested-With': 'XMLHttpRequest',
|
|
135
147
|
'X-CSRF-TOKEN': csrfToken,
|
|
136
148
|
},
|
|
137
|
-
body: JSON.stringify(requestBody)
|
|
138
|
-
})
|
|
139
|
-
.then(response => {
|
|
149
|
+
body: JSON.stringify(requestBody),
|
|
150
|
+
}).then((response) => {
|
|
140
151
|
if (!response.ok) {
|
|
141
152
|
throw new Error(`Failed to update item ${itemId}`);
|
|
142
153
|
}
|
|
@@ -146,7 +157,7 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
146
157
|
|
|
147
158
|
// Wait for all requests to complete
|
|
148
159
|
Promise.all(updatePromises)
|
|
149
|
-
.then(results => {
|
|
160
|
+
.then((results) => {
|
|
150
161
|
console.log('All items updated successfully:', results);
|
|
151
162
|
|
|
152
163
|
// Update toast
|
|
@@ -158,7 +169,7 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
158
169
|
closeButton: true,
|
|
159
170
|
});
|
|
160
171
|
})
|
|
161
|
-
.catch(error => {
|
|
172
|
+
.catch((error) => {
|
|
162
173
|
console.error('Error updating order:', error);
|
|
163
174
|
|
|
164
175
|
// Update toast
|
|
@@ -179,131 +190,302 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
179
190
|
console.log('Edit item:', { tableId, row, index });
|
|
180
191
|
|
|
181
192
|
// Find the table configuration
|
|
182
|
-
const tableConfig = setting.tables.find(t => t.id === tableId);
|
|
193
|
+
const tableConfig = setting.tables.find((t) => t.id === tableId);
|
|
183
194
|
if (!tableConfig) {
|
|
184
195
|
console.error('Table configuration not found for:', tableId);
|
|
185
196
|
toast.error('Failed to edit item: Missing configuration');
|
|
186
197
|
return;
|
|
187
198
|
}
|
|
188
199
|
|
|
189
|
-
// Find the
|
|
190
|
-
const
|
|
200
|
+
// Find the addItem action to get its form settings
|
|
201
|
+
const addItemAction = actions.find((action) => action.id === 'addItem');
|
|
202
|
+
// Use the addItem action's settings if available, otherwise fall back to table form or default form
|
|
203
|
+
const tableForm = addItemAction?.setting || tableConfig.form || form;
|
|
191
204
|
|
|
192
205
|
// Get the item ID
|
|
193
206
|
const itemId = row[tableConfig.dataId];
|
|
194
207
|
|
|
208
|
+
console.info(row, tableConfig);
|
|
209
|
+
|
|
210
|
+
// Check if we have a valid item ID
|
|
211
|
+
if (!itemId) {
|
|
212
|
+
console.error('No valid item ID found for editing:', row);
|
|
213
|
+
toast.error('Failed to edit item: Missing ID');
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Log the table configuration for debugging
|
|
218
|
+
console.log('Table config for editing:', tableConfig);
|
|
219
|
+
|
|
195
220
|
// If there's a get URL for fetching item details
|
|
196
221
|
if (tableConfig.urls && tableConfig.urls.get) {
|
|
197
222
|
// Get CSRF token
|
|
198
|
-
const csrfToken = document
|
|
223
|
+
const csrfToken = document
|
|
224
|
+
.querySelector('meta[name="csrf-token"]')
|
|
225
|
+
?.getAttribute('content');
|
|
199
226
|
|
|
200
227
|
// Fetch the item details before opening the modal
|
|
201
228
|
// Laravel uses GET for retrieving data
|
|
202
|
-
|
|
229
|
+
// Ensure the URL is properly formatted - it should end with a slash if we're appending an ID
|
|
230
|
+
const baseUrl = tableConfig.urls.get.endsWith('/')
|
|
231
|
+
? tableConfig.urls.get
|
|
232
|
+
: `${tableConfig.urls.get}/`;
|
|
233
|
+
|
|
234
|
+
const fetchUrl = `${baseUrl}${itemId}`;
|
|
235
|
+
console.log('Fetching item details from:', fetchUrl);
|
|
236
|
+
|
|
237
|
+
fetch(fetchUrl, {
|
|
203
238
|
method: 'GET',
|
|
204
239
|
headers: {
|
|
205
240
|
'Content-Type': 'application/json',
|
|
206
241
|
'X-Requested-With': 'XMLHttpRequest',
|
|
207
242
|
'X-CSRF-TOKEN': csrfToken,
|
|
208
|
-
}
|
|
209
|
-
})
|
|
210
|
-
.then(response => {
|
|
211
|
-
if (!response.ok) {
|
|
212
|
-
throw new Error('Failed to fetch item details');
|
|
213
|
-
}
|
|
214
|
-
return response.json();
|
|
243
|
+
},
|
|
215
244
|
})
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
...tableForm,
|
|
222
|
-
update: {
|
|
223
|
-
...tableForm.update,
|
|
224
|
-
submitUrl: tableConfig.urls.update + tableConfig.dataId + '/' + itemId,
|
|
225
|
-
method: 'PUT' // Laravel uses PUT for updates
|
|
245
|
+
.then((response) => {
|
|
246
|
+
if (!response.ok) {
|
|
247
|
+
throw new Error(
|
|
248
|
+
`Failed to fetch item details: ${response.status} ${response.statusText}`
|
|
249
|
+
);
|
|
226
250
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
251
|
+
return response.text().then((text) => {
|
|
252
|
+
try {
|
|
253
|
+
return JSON.parse(text);
|
|
254
|
+
} catch (e) {
|
|
255
|
+
console.error('Invalid JSON response:', text);
|
|
256
|
+
throw new Error(
|
|
257
|
+
'Invalid JSON response from server'
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
})
|
|
262
|
+
.then((itemData) => {
|
|
263
|
+
console.log('Fetched item data:', itemData);
|
|
264
|
+
// Open the modal with the update form type and the fetched data
|
|
265
|
+
// Use the update URL from the table configuration for the form
|
|
266
|
+
// Ensure the URL is properly formatted for the update
|
|
267
|
+
const baseUpdateUrl = tableConfig.urls.update.endsWith('/')
|
|
268
|
+
? tableConfig.urls.update
|
|
269
|
+
: `${tableConfig.urls.update}/`;
|
|
270
|
+
|
|
271
|
+
const updatedFormSettings = {
|
|
272
|
+
...tableForm,
|
|
273
|
+
update: {
|
|
274
|
+
...tableForm.update,
|
|
275
|
+
submitUrl: `${baseUpdateUrl}${itemId}`,
|
|
276
|
+
method: 'PUT', // Laravel uses PUT for updates
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
modalOpen('update', itemId, updatedFormSettings, itemData);
|
|
280
|
+
})
|
|
281
|
+
.catch((error) => {
|
|
282
|
+
console.error('Error fetching item details:', error);
|
|
283
|
+
toast.error(
|
|
284
|
+
'Failed to fetch item details: ' + error.message
|
|
285
|
+
);
|
|
233
286
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
287
|
+
// Fall back to opening the modal with the row data
|
|
288
|
+
console.log('Falling back to row data:', row);
|
|
289
|
+
|
|
290
|
+
// Ensure we have a proper update URL even in the fallback case
|
|
291
|
+
let updateUrl;
|
|
292
|
+
if (tableConfig.urls && tableConfig.urls.update) {
|
|
293
|
+
const baseUpdateUrl = tableConfig.urls.update.endsWith(
|
|
294
|
+
'/'
|
|
295
|
+
)
|
|
296
|
+
? tableConfig.urls.update
|
|
297
|
+
: `${tableConfig.urls.update}/`;
|
|
298
|
+
updateUrl = `${baseUpdateUrl}${itemId}`;
|
|
299
|
+
} else {
|
|
300
|
+
// Default URL if none is provided
|
|
301
|
+
updateUrl = `/ajax/quoteItems/${itemId}`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
console.log('Using fallback update URL:', updateUrl);
|
|
305
|
+
|
|
306
|
+
// Create updated form settings with the proper URL
|
|
307
|
+
const updatedFormSettings = {
|
|
308
|
+
...tableForm,
|
|
309
|
+
update: {
|
|
310
|
+
...tableForm.update,
|
|
311
|
+
submitUrl: updateUrl,
|
|
312
|
+
method: 'PUT', // Laravel uses PUT for updates
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
modalOpen('update', itemId, updatedFormSettings, row);
|
|
317
|
+
});
|
|
237
318
|
} else {
|
|
319
|
+
// If there's no get URL, we still need to construct a proper update URL
|
|
320
|
+
console.log('No get URL found, using row data directly');
|
|
321
|
+
|
|
322
|
+
// Ensure we have a proper update URL
|
|
323
|
+
let updateUrl;
|
|
324
|
+
if (tableConfig.urls && tableConfig.urls.update) {
|
|
325
|
+
const baseUpdateUrl = tableConfig.urls.update.endsWith('/')
|
|
326
|
+
? tableConfig.urls.update
|
|
327
|
+
: `${tableConfig.urls.update}/`;
|
|
328
|
+
updateUrl = `${baseUpdateUrl}${itemId}`;
|
|
329
|
+
} else {
|
|
330
|
+
// Default URL if none is provided
|
|
331
|
+
updateUrl = `/ajax/quoteItems/${itemId}`;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
console.log('Using update URL:', updateUrl);
|
|
335
|
+
|
|
336
|
+
// Create updated form settings with the proper URL
|
|
337
|
+
const updatedFormSettings = {
|
|
338
|
+
...tableForm,
|
|
339
|
+
update: {
|
|
340
|
+
...tableForm.update,
|
|
341
|
+
submitUrl: updateUrl,
|
|
342
|
+
method: 'PUT', // Laravel uses PUT for updates
|
|
343
|
+
},
|
|
344
|
+
};
|
|
345
|
+
|
|
238
346
|
// Open the modal with the update form type
|
|
239
|
-
modalOpen('update', itemId,
|
|
347
|
+
modalOpen('update', itemId, updatedFormSettings, row);
|
|
240
348
|
}
|
|
241
349
|
};
|
|
242
350
|
|
|
243
351
|
const handleDeleteItem = (tableId, row, index) => {
|
|
244
352
|
console.log('Delete item:', { tableId, row, index });
|
|
353
|
+
console.log('Available tables:', setting.tables);
|
|
354
|
+
|
|
355
|
+
// Ensure tableId is a string
|
|
356
|
+
const tableIdStr = String(tableId);
|
|
245
357
|
|
|
246
358
|
// Find the table configuration
|
|
247
|
-
const tableConfig = setting.tables.find(
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
359
|
+
const tableConfig = setting.tables.find(
|
|
360
|
+
(t) => String(t.id) === tableIdStr
|
|
361
|
+
);
|
|
362
|
+
if (!tableConfig) {
|
|
363
|
+
console.error('Table configuration not found for:', tableIdStr);
|
|
364
|
+
toast.error('Failed to delete item: Table configuration not found');
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (!tableConfig.urls || !tableConfig.urls.delete) {
|
|
369
|
+
console.error('Delete URL not found for table:', tableIdStr);
|
|
370
|
+
toast.error(
|
|
371
|
+
'Failed to delete item: Missing delete URL configuration'
|
|
372
|
+
);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Check if dataId is defined in the table configuration
|
|
377
|
+
if (!tableConfig.dataId) {
|
|
378
|
+
console.error(
|
|
379
|
+
'dataId not defined in table configuration:',
|
|
380
|
+
tableConfig
|
|
381
|
+
);
|
|
382
|
+
toast.error('Failed to delete item: Missing dataId configuration');
|
|
251
383
|
return;
|
|
252
384
|
}
|
|
253
385
|
|
|
254
386
|
// Get the item ID
|
|
255
387
|
const itemId = row[tableConfig.dataId];
|
|
256
388
|
|
|
257
|
-
//
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
389
|
+
// Check if itemId exists
|
|
390
|
+
if (!itemId) {
|
|
391
|
+
console.error('Item ID not found in row data:', row);
|
|
392
|
+
toast.error('Failed to delete item: Missing item ID');
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
console.log(
|
|
397
|
+
`Using dataId "${tableConfig.dataId}" to get item ID: ${itemId}`
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
// Define the delete function to be called when confirmed
|
|
401
|
+
const performDelete = () => {
|
|
402
|
+
console.log('Performing delete operation...');
|
|
403
|
+
|
|
404
|
+
// Show a loading toast
|
|
405
|
+
const toastId = toast.loading('Deleting item...');
|
|
406
|
+
|
|
407
|
+
// Get CSRF token
|
|
408
|
+
const csrfToken = document
|
|
409
|
+
.querySelector('meta[name="csrf-token"]')
|
|
410
|
+
?.getAttribute('content');
|
|
411
|
+
|
|
412
|
+
// Laravel requires a special approach for DELETE requests
|
|
413
|
+
// We need to include the _method parameter in the request body
|
|
414
|
+
const formData = new FormData();
|
|
415
|
+
formData.append('_method', 'DELETE');
|
|
416
|
+
|
|
417
|
+
// Ensure the URL is properly formatted for the delete
|
|
418
|
+
const baseDeleteUrl = tableConfig.urls.delete.endsWith('/')
|
|
419
|
+
? tableConfig.urls.delete
|
|
420
|
+
: `${tableConfig.urls.delete}/`;
|
|
421
|
+
|
|
422
|
+
const deleteUrl = `${baseDeleteUrl}${itemId}`;
|
|
423
|
+
console.log('Deleting item from:', deleteUrl);
|
|
424
|
+
|
|
425
|
+
// Send the delete request to the server
|
|
426
|
+
fetch(deleteUrl, {
|
|
427
|
+
method: 'POST', // Actually sending as POST but Laravel will interpret as DELETE
|
|
428
|
+
headers: {
|
|
429
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
430
|
+
'X-CSRF-TOKEN': csrfToken,
|
|
431
|
+
},
|
|
432
|
+
body: formData,
|
|
433
|
+
})
|
|
434
|
+
.then((response) => {
|
|
282
435
|
if (!response.ok) {
|
|
283
436
|
throw new Error('Failed to delete item');
|
|
284
437
|
}
|
|
285
438
|
return response.json();
|
|
286
439
|
})
|
|
287
|
-
.then(responseData => {
|
|
440
|
+
.then((responseData) => {
|
|
288
441
|
console.log('Item deleted successfully:', responseData);
|
|
289
442
|
|
|
290
443
|
// Create a copy of the data
|
|
291
444
|
const newData = { ...data };
|
|
292
445
|
|
|
293
446
|
// Remove the item from the specified table
|
|
294
|
-
newData[tableId] = newData[tableId].filter(
|
|
447
|
+
newData[tableId] = newData[tableId].filter(
|
|
448
|
+
(item) => item[tableConfig.dataId] !== itemId
|
|
449
|
+
);
|
|
295
450
|
|
|
296
451
|
// Update the state
|
|
297
452
|
setData(newData);
|
|
298
453
|
|
|
299
|
-
//
|
|
300
|
-
toast.
|
|
454
|
+
// Update toast
|
|
455
|
+
toast.update(toastId, {
|
|
456
|
+
render: 'Item deleted successfully',
|
|
457
|
+
type: 'success',
|
|
458
|
+
isLoading: false,
|
|
459
|
+
autoClose: 3000,
|
|
460
|
+
closeButton: true,
|
|
461
|
+
});
|
|
301
462
|
})
|
|
302
|
-
.catch(error => {
|
|
463
|
+
.catch((error) => {
|
|
303
464
|
console.error('Error deleting item:', error);
|
|
304
|
-
|
|
465
|
+
|
|
466
|
+
// Update toast
|
|
467
|
+
toast.update(toastId, {
|
|
468
|
+
render: 'Failed to delete item: ' + error.message,
|
|
469
|
+
type: 'error',
|
|
470
|
+
isLoading: false,
|
|
471
|
+
autoClose: 5000,
|
|
472
|
+
closeButton: true,
|
|
473
|
+
});
|
|
305
474
|
});
|
|
306
|
-
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// Show a confirmation dialog
|
|
478
|
+
showConfirmDialog({
|
|
479
|
+
title: 'Delete Item',
|
|
480
|
+
message: 'Are you sure you want to delete this item?',
|
|
481
|
+
confirmLabel: 'Delete',
|
|
482
|
+
cancelLabel: 'Cancel',
|
|
483
|
+
data: row,
|
|
484
|
+
onConfirm: performDelete,
|
|
485
|
+
onCancel: () => {
|
|
486
|
+
console.log('Delete operation cancelled');
|
|
487
|
+
toast.info('Delete operation cancelled');
|
|
488
|
+
},
|
|
307
489
|
});
|
|
308
490
|
};
|
|
309
491
|
|
|
@@ -311,26 +493,40 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
311
493
|
console.log('Add item to table:', tableId);
|
|
312
494
|
|
|
313
495
|
// Find the table configuration
|
|
314
|
-
const tableConfig = setting.tables.find(t => t.id === tableId);
|
|
496
|
+
const tableConfig = setting.tables.find((t) => t.id === tableId);
|
|
315
497
|
if (!tableConfig) {
|
|
316
498
|
console.error('Table configuration not found for:', tableId);
|
|
317
499
|
toast.error('Failed to add item: Missing configuration');
|
|
318
500
|
return;
|
|
319
501
|
}
|
|
320
502
|
|
|
321
|
-
// Find the
|
|
322
|
-
const
|
|
503
|
+
// Find the addItem action to get its form settings
|
|
504
|
+
const addItemAction = actions.find((action) => action.id === 'addItem');
|
|
505
|
+
// Use the addItem action's settings if available, otherwise fall back to table form or default form
|
|
506
|
+
const tableForm = addItemAction?.setting || tableConfig.form || form;
|
|
507
|
+
|
|
508
|
+
// Update the form settings to include the proper submitUrl
|
|
509
|
+
// If there's a submitUrl in the form settings, use it with proper formatting
|
|
510
|
+
let submitUrl;
|
|
511
|
+
if (tableForm.create?.submitUrl) {
|
|
512
|
+
const baseUrl = tableForm.create.submitUrl.endsWith('/')
|
|
513
|
+
? tableForm.create.submitUrl
|
|
514
|
+
: `${tableForm.create.submitUrl}/`;
|
|
515
|
+
submitUrl = `${baseUrl}${routeParams[urlParam]}`;
|
|
516
|
+
} else {
|
|
517
|
+
// Default URL if none is provided
|
|
518
|
+
submitUrl = `/ajax/quoteItems/${routeParams[urlParam]}`;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
console.log('Add item submitUrl:', submitUrl);
|
|
323
522
|
|
|
324
|
-
// Update the form settings to include the dataId in the submitUrl
|
|
325
523
|
const updatedFormSettings = {
|
|
326
524
|
...tableForm,
|
|
327
525
|
create: {
|
|
328
526
|
...tableForm.create,
|
|
329
|
-
submitUrl:
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
method: 'POST' // Laravel uses POST for creating new items
|
|
333
|
-
}
|
|
527
|
+
submitUrl: submitUrl,
|
|
528
|
+
method: 'POST', // Laravel uses POST for creating new items
|
|
529
|
+
},
|
|
334
530
|
};
|
|
335
531
|
|
|
336
532
|
// Open the modal with the create form type and updated form settings
|
|
@@ -423,9 +619,9 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
423
619
|
const requestBody = {
|
|
424
620
|
view: action.view,
|
|
425
621
|
data: {
|
|
426
|
-
|
|
622
|
+
[action.dataKey]: data, // Pass the current quote data
|
|
427
623
|
},
|
|
428
|
-
filename:
|
|
624
|
+
filename: `${action.filenamePrefix}-${data.id}.pdf`,
|
|
429
625
|
paper: action.paper || 'a4',
|
|
430
626
|
orientation: action.orientation || 'portrait',
|
|
431
627
|
download: action.download !== false, // Default to true if not specified
|
|
@@ -495,8 +691,13 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
495
691
|
// Handle other actions based on their id if no specific type is defined
|
|
496
692
|
switch (action.id) {
|
|
497
693
|
case 'addItem':
|
|
498
|
-
// Handle add item action
|
|
694
|
+
// Handle add item action - find the first table and add an item to it
|
|
499
695
|
console.log('Add item clicked');
|
|
696
|
+
if (setting.tables && setting.tables.length > 0) {
|
|
697
|
+
handleAddItem(setting.tables[0].id);
|
|
698
|
+
} else {
|
|
699
|
+
toast.error('No tables found to add item to');
|
|
700
|
+
}
|
|
500
701
|
break;
|
|
501
702
|
case 'editQuote':
|
|
502
703
|
// If no type is specified but id is editQuote, default to opening the edit modal
|
|
@@ -515,14 +716,14 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
515
716
|
|
|
516
717
|
// Default PDF generation with standard settings
|
|
517
718
|
const requestBody = {
|
|
518
|
-
view:
|
|
719
|
+
view: action.view,
|
|
519
720
|
data: {
|
|
520
|
-
|
|
721
|
+
[action.dataKey]: data, // Pass the current quote data
|
|
521
722
|
},
|
|
522
|
-
filename:
|
|
523
|
-
paper: 'a4',
|
|
524
|
-
orientation: 'portrait',
|
|
525
|
-
download: true
|
|
723
|
+
filename: `${action.filenamePrefix}-${data.id}.pdf`,
|
|
724
|
+
paper: action.paper || 'a4',
|
|
725
|
+
orientation: action.orientation || 'portrait',
|
|
726
|
+
download: action.download !== false, // Default to true if not specified
|
|
526
727
|
};
|
|
527
728
|
|
|
528
729
|
// Filename for the downloaded PDF
|
|
@@ -603,7 +804,7 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
603
804
|
<div className={styles.quoteTopSection}>
|
|
604
805
|
<div className={styles.quoteIdentifier}>
|
|
605
806
|
<h1 className={styles.quoteTitle}>
|
|
606
|
-
Quote
|
|
807
|
+
{page?.title || 'Quote'}
|
|
607
808
|
<span className={styles.quoteNumber}>
|
|
608
809
|
#{data.id || ''}
|
|
609
810
|
</span>
|
|
@@ -632,11 +833,21 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
632
833
|
]
|
|
633
834
|
}
|
|
634
835
|
>
|
|
635
|
-
{data.status ?
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
836
|
+
{data.status ? (
|
|
837
|
+
<>
|
|
838
|
+
{data.status.name}
|
|
839
|
+
{data.task?.ticket_id && (
|
|
840
|
+
<span className={styles.ticketId}>
|
|
841
|
+
- #{data.task.ticket_id}
|
|
842
|
+
</span>
|
|
843
|
+
)}
|
|
844
|
+
</>
|
|
845
|
+
) : (
|
|
846
|
+
data.task?.ticket_id && (
|
|
847
|
+
<span className={styles.ticketId}>
|
|
848
|
+
Ticket #{data.task.ticket_id}
|
|
849
|
+
</span>
|
|
850
|
+
)
|
|
640
851
|
)}
|
|
641
852
|
</span>
|
|
642
853
|
</div>
|
|
@@ -662,7 +873,7 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
662
873
|
</div>
|
|
663
874
|
<div className={styles.quoteTitleContainer}>
|
|
664
875
|
<h2 className={styles.quoteBigNumber}>
|
|
665
|
-
Quote #{data.id || ''}
|
|
876
|
+
{page?.title || 'Quote'} #{data.id || ''}
|
|
666
877
|
</h2>
|
|
667
878
|
<div className={styles.quoteDate}>
|
|
668
879
|
<strong>Date:</strong>{' '}
|
|
@@ -759,6 +970,41 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
759
970
|
);
|
|
760
971
|
})()}
|
|
761
972
|
</>
|
|
973
|
+
) : data.supplier ? (
|
|
974
|
+
<>
|
|
975
|
+
{data.supplier.name}
|
|
976
|
+
<br />
|
|
977
|
+
{data.supplier.address1 && (
|
|
978
|
+
<>
|
|
979
|
+
{data.supplier.address1}
|
|
980
|
+
<br />
|
|
981
|
+
</>
|
|
982
|
+
)}
|
|
983
|
+
{data.supplier.address2 && (
|
|
984
|
+
<>
|
|
985
|
+
{data.supplier.address2}
|
|
986
|
+
<br />
|
|
987
|
+
</>
|
|
988
|
+
)}
|
|
989
|
+
{data.supplier.suburb &&
|
|
990
|
+
data.supplier.postcode && (
|
|
991
|
+
<>
|
|
992
|
+
{data.supplier.suburb}
|
|
993
|
+
{data.supplier.state_id
|
|
994
|
+
? `, ${data.supplier.state_id}`
|
|
995
|
+
: ''}
|
|
996
|
+
{', '}
|
|
997
|
+
{data.supplier.postcode}
|
|
998
|
+
<br />
|
|
999
|
+
</>
|
|
1000
|
+
)}
|
|
1001
|
+
{data.supplier.abn && (
|
|
1002
|
+
<>
|
|
1003
|
+
ABN: {data.supplier.abn}
|
|
1004
|
+
<br />
|
|
1005
|
+
</>
|
|
1006
|
+
)}
|
|
1007
|
+
</>
|
|
762
1008
|
) : (
|
|
763
1009
|
''
|
|
764
1010
|
)}
|
|
@@ -767,7 +1013,7 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
767
1013
|
<div className={styles.quoteParty}>
|
|
768
1014
|
<h3 className={styles.partyTitle}>Quote From:</h3>
|
|
769
1015
|
<div className={styles.partyDetails}>
|
|
770
|
-
|
|
1016
|
+
Omnia Global
|
|
771
1017
|
<br />
|
|
772
1018
|
660C Newcastle Street
|
|
773
1019
|
<br />
|
|
@@ -775,9 +1021,9 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
775
1021
|
<br />
|
|
776
1022
|
08 6383 6600
|
|
777
1023
|
<br />
|
|
778
|
-
team@
|
|
1024
|
+
team@omniaglobal.com.au
|
|
779
1025
|
<br />
|
|
780
|
-
|
|
1026
|
+
ACN: 674 383 987
|
|
781
1027
|
</div>
|
|
782
1028
|
</div>
|
|
783
1029
|
</div>
|
|
@@ -843,7 +1089,9 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
843
1089
|
</h2>
|
|
844
1090
|
<button
|
|
845
1091
|
className={styles.addItemButton}
|
|
846
|
-
onClick={() =>
|
|
1092
|
+
onClick={() =>
|
|
1093
|
+
handleAddItem(table.id)
|
|
1094
|
+
}
|
|
847
1095
|
>
|
|
848
1096
|
+ Add Item
|
|
849
1097
|
</button>
|
|
@@ -854,7 +1102,11 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
854
1102
|
>
|
|
855
1103
|
<thead>
|
|
856
1104
|
<tr>
|
|
857
|
-
<th
|
|
1105
|
+
<th
|
|
1106
|
+
className={
|
|
1107
|
+
styles.dragHandleHeader
|
|
1108
|
+
}
|
|
1109
|
+
></th>
|
|
858
1110
|
{table.columns.map((column) => (
|
|
859
1111
|
<th
|
|
860
1112
|
key={`${table.id}-column-${column.id}`}
|
|
@@ -871,22 +1123,62 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
871
1123
|
: column.label}
|
|
872
1124
|
</th>
|
|
873
1125
|
))}
|
|
874
|
-
<th
|
|
1126
|
+
<th
|
|
1127
|
+
className={
|
|
1128
|
+
styles.actionHeader
|
|
1129
|
+
}
|
|
1130
|
+
>
|
|
1131
|
+
Actions
|
|
1132
|
+
</th>
|
|
875
1133
|
</tr>
|
|
876
1134
|
</thead>
|
|
877
1135
|
<SortableTableBody
|
|
878
1136
|
items={data[table.id]}
|
|
879
1137
|
columns={table.columns}
|
|
880
|
-
onSortEnd={({
|
|
881
|
-
|
|
882
|
-
|
|
1138
|
+
onSortEnd={({
|
|
1139
|
+
oldIndex,
|
|
1140
|
+
newIndex,
|
|
1141
|
+
}) =>
|
|
1142
|
+
handleSortEnd(
|
|
1143
|
+
table.id,
|
|
1144
|
+
oldIndex,
|
|
1145
|
+
newIndex
|
|
1146
|
+
)
|
|
1147
|
+
}
|
|
1148
|
+
onEdit={(row, index) => {
|
|
1149
|
+
console.log(
|
|
1150
|
+
'Edit callback with index:',
|
|
1151
|
+
index
|
|
1152
|
+
);
|
|
1153
|
+
handleEditItem(
|
|
1154
|
+
table.id,
|
|
1155
|
+
row,
|
|
1156
|
+
index
|
|
1157
|
+
);
|
|
1158
|
+
}}
|
|
1159
|
+
onDelete={(row, index) => {
|
|
1160
|
+
console.log(
|
|
1161
|
+
'Delete callback from SortableTableBody:',
|
|
1162
|
+
{
|
|
1163
|
+
tableId: table.id,
|
|
1164
|
+
row,
|
|
1165
|
+
index,
|
|
1166
|
+
}
|
|
1167
|
+
);
|
|
1168
|
+
handleDeleteItem(
|
|
1169
|
+
table.id,
|
|
1170
|
+
row,
|
|
1171
|
+
index
|
|
1172
|
+
);
|
|
1173
|
+
}}
|
|
883
1174
|
useDragHandle
|
|
884
1175
|
helperClass="sortableHelper"
|
|
885
1176
|
distance={5}
|
|
886
1177
|
/>
|
|
887
1178
|
<tfoot>
|
|
888
1179
|
<tr className={styles.subtotalRow}>
|
|
889
|
-
<td></td>
|
|
1180
|
+
<td></td>{' '}
|
|
1181
|
+
{/* Empty cell for drag handle */}
|
|
890
1182
|
<td
|
|
891
1183
|
colSpan={
|
|
892
1184
|
table.columns.length - 1
|
|
@@ -904,7 +1196,8 @@ function GenericQuote({ logo, setting, urlParam }) {
|
|
|
904
1196
|
>
|
|
905
1197
|
{formatCurrency(subtotal)}
|
|
906
1198
|
</td>
|
|
907
|
-
<td></td>
|
|
1199
|
+
<td></td>{' '}
|
|
1200
|
+
{/* Empty cell for action buttons */}
|
|
908
1201
|
</tr>
|
|
909
1202
|
</tfoot>
|
|
910
1203
|
</table>
|