@visns-studio/visns-components 5.13.8 → 5.13.10

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.
@@ -1,9 +1,10 @@
1
1
  import React, { useState, useEffect } from 'react';
2
- import { Plus, Move, Eye, EyeOff, Trash2 } from 'lucide-react';
2
+ import { Plus, Move, Eye, EyeOff, Trash2, Settings } from 'lucide-react';
3
3
  import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
4
4
  import { arrayMoveImmutable } from 'array-move';
5
5
  import SectionEditor from './SectionEditor';
6
6
  import SectionTypeSelector from './SectionTypeSelector';
7
+ import HeaderConfigEditor from './HeaderConfigEditor';
7
8
  import { toast } from 'react-toastify';
8
9
  import { buildUrl, defaultProposalApiEndpoints } from '../../utils/urlBuilder';
9
10
  import './ProposalTemplateSectionManager.css';
@@ -112,6 +113,7 @@ const ProposalTemplateSectionManager = ({
112
113
  const [isLoading, setIsLoading] = useState(false);
113
114
  const [editingSection, setEditingSection] = useState(null);
114
115
  const [showSectionTypeSelector, setShowSectionTypeSelector] = useState(false);
116
+ const [showHeaderConfig, setShowHeaderConfig] = useState(false);
115
117
  const [availableVariables, setAvailableVariables] = useState([]);
116
118
 
117
119
  // Helper function to replace template variables in URLs with templateId included
@@ -239,6 +241,64 @@ const ProposalTemplateSectionManager = ({
239
241
  setEditingSection(section);
240
242
  };
241
243
 
244
+ const handleSaveHeaderConfig = async (headerConfig) => {
245
+ try {
246
+ setIsLoading(true);
247
+
248
+ // Prepare the updated template data
249
+ const updatedTemplateData = {
250
+ ...templateData,
251
+ styling: {
252
+ ...templateData?.styling,
253
+ header: headerConfig
254
+ }
255
+ };
256
+
257
+ // Make API call to save the template styling
258
+ const requestPayload = {
259
+ name: templateData.name,
260
+ description: templateData.description,
261
+ styling: updatedTemplateData.styling,
262
+ variables: templateData.variables || [],
263
+ branding_profile_id: templateData.branding_profile_id
264
+ };
265
+
266
+ console.log('Sending header config request:', requestPayload);
267
+
268
+ const response = await fetch(buildUrlWithTemplate(apiEndpoints.templateData || '/ajax/proposalTemplates/{templateId}'), {
269
+ method: 'PUT',
270
+ headers: {
271
+ 'Content-Type': 'application/json',
272
+ 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')
273
+ },
274
+ body: JSON.stringify(requestPayload)
275
+ });
276
+
277
+ if (!response.ok) {
278
+ throw new Error(`HTTP error! status: ${response.status}`);
279
+ }
280
+
281
+ const data = await response.json();
282
+
283
+ // Check if the response indicates success (either explicit success flag or 200 status)
284
+ if (data.success !== false) {
285
+ // Update local state if onChange callback is provided
286
+ if (onChange) {
287
+ onChange(updatedTemplateData);
288
+ }
289
+ setShowHeaderConfig(false);
290
+ toast.success('Header configuration saved');
291
+ } else {
292
+ throw new Error(data.message || 'Failed to save header configuration');
293
+ }
294
+ } catch (error) {
295
+ console.error('Error saving header configuration:', error);
296
+ toast.error('Failed to save header configuration');
297
+ } finally {
298
+ setIsLoading(false);
299
+ }
300
+ };
301
+
242
302
  const handleSaveSection = async (sectionData) => {
243
303
  try {
244
304
  // Helper function to deeply clean objects and remove circular references
@@ -430,13 +490,23 @@ const ProposalTemplateSectionManager = ({
430
490
  Drag and drop to reorder sections. Use variables like {`{{customer_name}}`} for dynamic content.
431
491
  </p>
432
492
  </div>
433
- <button
434
- onClick={() => setShowSectionTypeSelector(true)}
435
- className="add-section-btn"
436
- >
437
- <Plus className="w-4 h-4" />
438
- Add Section
439
- </button>
493
+ <div className="add-section-actions">
494
+ <button
495
+ onClick={() => setShowHeaderConfig(true)}
496
+ className="add-section-btn secondary"
497
+ title="Configure proposal header"
498
+ >
499
+ <Settings className="w-4 h-4" />
500
+ Header Settings
501
+ </button>
502
+ <button
503
+ onClick={() => setShowSectionTypeSelector(true)}
504
+ className="add-section-btn"
505
+ >
506
+ <Plus className="w-4 h-4" />
507
+ Add Section
508
+ </button>
509
+ </div>
440
510
  </div>
441
511
 
442
512
  {sections.length === 0 ? (
@@ -483,6 +553,15 @@ const ProposalTemplateSectionManager = ({
483
553
  />
484
554
  )}
485
555
 
556
+ {showHeaderConfig && (
557
+ <HeaderConfigEditor
558
+ config={templateData?.styling?.header}
559
+ brandingProfile={templateData?.brandingProfile}
560
+ onSave={handleSaveHeaderConfig}
561
+ onCancel={() => setShowHeaderConfig(false)}
562
+ />
563
+ )}
564
+
486
565
  {editingSection && (
487
566
  <SectionEditor
488
567
  section={editingSection}
@@ -17,7 +17,7 @@
17
17
  }
18
18
 
19
19
  .section-editor-content {
20
- background: white;
20
+ background: var(--tertiary-color, white);
21
21
  border-radius: 8px;
22
22
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
23
23
  max-width: 1200px;
@@ -44,8 +44,8 @@
44
44
  align-items: center;
45
45
  justify-content: space-between;
46
46
  padding: 1.5rem;
47
- border-bottom: 1px solid #e2e8f0;
48
- background: #1b3933;
47
+ border-bottom: 1px solid var(--paragraph-color, #e2e8f0);
48
+ background: var(--primary-color, #1b3933);
49
49
  color: white;
50
50
  border-radius: 8px 8px 0 0;
51
51
  }
@@ -84,7 +84,7 @@
84
84
  }
85
85
 
86
86
  .section-editor-preview-toggle.active {
87
- background: #3cbf7d;
87
+ background: var(--primary-color, #3cbf7d);
88
88
  color: white;
89
89
  }
90
90
 
@@ -109,7 +109,7 @@
109
109
  }
110
110
 
111
111
  .section-editor-close-btn:hover {
112
- background: #3cbf7d;
112
+ background: var(--primary-color, #3cbf7d);
113
113
  color: white;
114
114
  }
115
115
 
@@ -132,7 +132,7 @@
132
132
  .section-editor-form-label {
133
133
  font-size: 0.875rem;
134
134
  font-weight: 600;
135
- color: #374151;
135
+ color: var(--paragraph-color, #374151);
136
136
  margin-bottom: 0.5rem;
137
137
  }
138
138
 
@@ -154,12 +154,12 @@
154
154
  height: 1rem;
155
155
  border-radius: 4px;
156
156
  border: 2px solid #d1d5db;
157
- accent-color: #3cbf7d;
157
+ accent-color: var(--primary-color, #3cbf7d);
158
158
  }
159
159
 
160
160
  .section-editor-checkbox label {
161
161
  font-size: 0.875rem;
162
- color: #374151;
162
+ color: var(--paragraph-color, #374151);
163
163
  cursor: pointer;
164
164
  }
165
165
 
@@ -177,7 +177,7 @@
177
177
  .section-editor-content-label {
178
178
  font-size: 0.875rem;
179
179
  font-weight: 600;
180
- color: #374151;
180
+ color: var(--paragraph-color, #374151);
181
181
  }
182
182
 
183
183
  .section-editor-content-actions {
@@ -193,12 +193,12 @@
193
193
  padding: 0.5rem 0.75rem;
194
194
  font-size: 0.8rem;
195
195
  font-weight: 500;
196
- border: 1px solid #d1d5db;
196
+ border: 1px solid var(--paragraph-color, #d1d5db);
197
197
  border-radius: 6px;
198
198
  cursor: pointer;
199
199
  transition: all 0.2s ease-in-out;
200
- background: white;
201
- color: #374151;
200
+ background: var(--tertiary-color, white);
201
+ color: var(--paragraph-color, #374151);
202
202
  text-decoration: none;
203
203
  user-select: none;
204
204
  position: relative;
@@ -206,13 +206,13 @@
206
206
  }
207
207
 
208
208
  .section-editor-action-btn:hover {
209
- background: #f9fafb;
210
- border-color: #9ca3af;
209
+ background: var(--bg-color, #f9fafb);
210
+ border-color: var(--paragraph-color, #9ca3af);
211
211
  }
212
212
 
213
213
  .section-editor-action-btn.primary {
214
- background: linear-gradient(135deg, #3cbf7d 0%, #2da967 100%);
215
- border-color: #3cbf7d;
214
+ background: linear-gradient(135deg, var(--primary-color, #3cbf7d) 0%, var(--primary-color, #2da967) 100%);
215
+ border-color: var(--primary-color, #3cbf7d);
216
216
  color: white;
217
217
  box-shadow: 0 2px 4px rgba(60, 191, 125, 0.2);
218
218
  font-size: 0.8rem;
@@ -221,8 +221,8 @@
221
221
  }
222
222
 
223
223
  .section-editor-action-btn.primary:hover {
224
- background: linear-gradient(135deg, #2da967 0%, #1b3933 100%);
225
- border-color: #1b3933;
224
+ background: linear-gradient(135deg, var(--primary-color, #2da967) 0%, var(--header-color, #1b3933) 100%);
225
+ border-color: var(--header-color, #1b3933);
226
226
  box-shadow: 0 4px 8px rgba(27, 57, 51, 0.3);
227
227
  transform: translateY(-1px);
228
228
  }
@@ -233,8 +233,8 @@
233
233
  }
234
234
 
235
235
  .section-editor-preview {
236
- background: #f8fafc;
237
- border: 1px solid #e2e8f0;
236
+ background: var(--bg-color, #f8fafc);
237
+ border: 1px solid var(--paragraph-color, #e2e8f0);
238
238
  border-radius: 5px;
239
239
  padding: 1rem;
240
240
  min-height: 400px;
@@ -244,7 +244,7 @@
244
244
  max-width: none;
245
245
  font-size: 0.875rem;
246
246
  line-height: 1.6;
247
- color: #374151;
247
+ color: var(--paragraph-color, #374151);
248
248
  }
249
249
 
250
250
  /* Compact HTML content styling for section editor preview */
@@ -258,14 +258,14 @@
258
258
  line-height: 1.2 !important;
259
259
  margin: 0 0 0.5rem 0 !important;
260
260
  font-weight: 600;
261
- color: #1f2937 !important;
261
+ color: var(--header-color, #1f2937) !important;
262
262
  }
263
263
 
264
264
  .section-editor-preview .prose p {
265
265
  font-size: 0.875rem !important;
266
266
  line-height: 1.4 !important;
267
267
  margin: 0 0 0.5rem 0 !important;
268
- color: #374151 !important;
268
+ color: var(--paragraph-color, #374151) !important;
269
269
  }
270
270
 
271
271
  .section-editor-preview .prose ul,
@@ -274,7 +274,7 @@
274
274
  line-height: 1.4 !important;
275
275
  margin: 0 0 0.5rem 1rem !important;
276
276
  padding: 0;
277
- color: #374151 !important;
277
+ color: var(--paragraph-color, #374151) !important;
278
278
  }
279
279
 
280
280
  .section-editor-preview .prose li {
@@ -295,14 +295,14 @@
295
295
  line-height: 1.2 !important;
296
296
  margin: 0 0 0.5rem 0 !important;
297
297
  font-weight: 600;
298
- color: #1f2937 !important;
298
+ color: var(--header-color, #1f2937) !important;
299
299
  }
300
300
 
301
301
  .section-editor-preview p {
302
302
  font-size: 0.875rem !important;
303
303
  line-height: 1.4 !important;
304
304
  margin: 0 0 0.5rem 0 !important;
305
- color: #374151 !important;
305
+ color: var(--paragraph-color, #374151) !important;
306
306
  }
307
307
 
308
308
  .section-editor-preview ul,
@@ -311,7 +311,7 @@
311
311
  line-height: 1.4 !important;
312
312
  margin: 0 0 0.5rem 1rem !important;
313
313
  padding: 0;
314
- color: #374151 !important;
314
+ color: var(--paragraph-color, #374151) !important;
315
315
  }
316
316
 
317
317
  .section-editor-preview li {
@@ -322,8 +322,8 @@
322
322
  }
323
323
 
324
324
  .section-editor-styling-panel {
325
- background: #f8fafc;
326
- border: 1px solid #e2e8f0;
325
+ background: var(--bg-color, #f8fafc);
326
+ border: 1px solid var(--paragraph-color, #e2e8f0);
327
327
  border-radius: 5px;
328
328
  padding: 1rem;
329
329
  margin-bottom: 1.5rem;
@@ -332,7 +332,7 @@
332
332
  .section-editor-styling-title {
333
333
  font-size: 0.875rem;
334
334
  font-weight: 600;
335
- color: #374151;
335
+ color: var(--paragraph-color, #374151);
336
336
  margin-bottom: 0.75rem;
337
337
  }
338
338
 
@@ -350,7 +350,7 @@
350
350
 
351
351
  .section-editor-styling-input {
352
352
  padding: 0.5rem;
353
- border: 1px solid #d1d5db;
353
+ border: 1px solid var(--paragraph-color, #d1d5db);
354
354
  border-radius: 4px;
355
355
  font-size: 0.875rem;
356
356
  width: 100%;
@@ -359,8 +359,8 @@
359
359
  .section-editor-styling-input:focus {
360
360
  outline: none;
361
361
  ring: 2px;
362
- ring-color: #3cbf7d;
363
- border-color: #3cbf7d;
362
+ ring-color: var(--primary-color, #3cbf7d);
363
+ border-color: var(--primary-color, #3cbf7d);
364
364
  }
365
365
 
366
366
  .section-editor-dynamic-notice {
@@ -368,8 +368,8 @@
368
368
  }
369
369
 
370
370
  .section-editor-dynamic-card {
371
- background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
372
- border: 1px solid #bae6fd;
371
+ background: linear-gradient(135deg, var(--bg-color, #f0f9ff) 0%, var(--bg-color, #e0f2fe) 100%);
372
+ border: 1px solid var(--primary-color, #bae6fd);
373
373
  border-radius: 8px;
374
374
  padding: 1.5rem;
375
375
  display: flex;
@@ -379,7 +379,7 @@
379
379
  }
380
380
 
381
381
  .section-editor-dynamic-icon {
382
- background: #0ea5e9;
382
+ background: var(--primary-color, #0ea5e9);
383
383
  color: white;
384
384
  padding: 0.75rem;
385
385
  border-radius: 8px;
@@ -397,13 +397,13 @@
397
397
  .section-editor-dynamic-title {
398
398
  font-size: 1rem;
399
399
  font-weight: 600;
400
- color: #0c4a6e;
400
+ color: var(--primary-color, #0c4a6e);
401
401
  margin: 0 0 0.5rem 0;
402
402
  }
403
403
 
404
404
  .section-editor-dynamic-description {
405
405
  font-size: 0.875rem;
406
- color: #0369a1;
406
+ color: var(--primary-color, #0369a1);
407
407
  line-height: 1.5;
408
408
  margin: 0 0 1rem 0;
409
409
  }
@@ -415,8 +415,8 @@
415
415
  }
416
416
 
417
417
  .section-editor-variables-summary {
418
- background: #eff6ff;
419
- border: 1px solid #bfdbfe;
418
+ background: var(--bg-color, #eff6ff);
419
+ border: 1px solid var(--primary-color, #bfdbfe);
420
420
  border-radius: 5px;
421
421
  padding: 1rem;
422
422
  margin-bottom: 1.5rem;
@@ -425,7 +425,7 @@
425
425
  .section-editor-variables-title {
426
426
  font-size: 0.875rem;
427
427
  font-weight: 600;
428
- color: #1e40af;
428
+ color: var(--primary-color, #1e40af);
429
429
  margin-bottom: 0.5rem;
430
430
  }
431
431
 
@@ -439,10 +439,10 @@
439
439
  padding: 0.25rem 0.5rem;
440
440
  font-size: 0.75rem;
441
441
  font-weight: 500;
442
- background: #dbeafe;
443
- color: #1e40af;
442
+ background: var(--primary-color, #3cbf7d);
443
+ color: var(--tertiary-color, white);
444
444
  border-radius: 12px;
445
- border: 1px solid #bfdbfe;
445
+ border: 1px solid var(--header-color, #2da967);
446
446
  }
447
447
 
448
448
  .section-editor-footer {
@@ -451,8 +451,8 @@
451
451
  justify-content: flex-end;
452
452
  gap: 0.75rem;
453
453
  padding: 1rem 1.5rem;
454
- border-top: 1px solid #e2e8f0;
455
- background: #f8fafc;
454
+ border-top: 1px solid var(--paragraph-color, #e2e8f0);
455
+ background: var(--bg-color, #f8fafc);
456
456
  border-radius: 0 0 8px 8px;
457
457
  }
458
458
 
@@ -470,25 +470,25 @@
470
470
  }
471
471
 
472
472
  .section-editor-btn.secondary {
473
- background: white;
474
- color: #6b7280;
475
- border: 1px solid #d1d5db;
473
+ background: var(--tertiary-color, white);
474
+ color: var(--paragraph-color, #6b7280);
475
+ border: 1px solid var(--paragraph-color, #d1d5db);
476
476
  }
477
477
 
478
478
  .section-editor-btn.secondary:hover {
479
- background: #f9fafb;
480
- border-color: #9ca3af;
479
+ background: var(--bg-color, #f9fafb);
480
+ border-color: var(--paragraph-color, #9ca3af);
481
481
  }
482
482
 
483
483
  .section-editor-btn.primary {
484
- background: #3cbf7d;
484
+ background: var(--primary-color, #3cbf7d);
485
485
  color: white;
486
486
  border: 1px solid #3cbf7d;
487
487
  }
488
488
 
489
489
  .section-editor-btn.primary:hover {
490
- background: #1b3933;
491
- border-color: #1b3933;
490
+ background: var(--header-color, #1b3933);
491
+ border-color: var(--header-color, #1b3933);
492
492
  }
493
493
 
494
494
  /* TinyMCE Editor Overrides */
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
2
2
  import { X, Save, Tag, Settings, Eye } from 'lucide-react';
3
3
  import Field from '../Field';
4
4
  import VariableInserter from './VariableInserter';
5
+ import AgreementSignatureEditor from './AgreementSignatureEditor';
5
6
  import './SectionEditor.css';
6
7
 
7
8
  const SectionEditor = ({ section, availableVariables = [], onSave, onCancel }) => {
@@ -141,10 +142,11 @@ const SectionEditor = ({ section, availableVariables = [], onSave, onCancel }) =
141
142
  const finalFormData = {
142
143
  ...formData,
143
144
  content: currentContent,
144
- variables: extractedVariables
145
+ variables: extractedVariables,
146
+ // Preserve template data for agreement signature sections
147
+ ...(formData.template && { template: formData.template })
145
148
  };
146
149
 
147
- console.log('Final form data being saved:', finalFormData);
148
150
  onSave(finalFormData);
149
151
  };
150
152
 
@@ -267,7 +269,31 @@ const SectionEditor = ({ section, availableVariables = [], onSave, onCancel }) =
267
269
  </div>
268
270
  </div>
269
271
 
270
- {/* Content Editor or Preview - Only show if not dynamic */}
272
+ {/* Agreement Signature Template Editor */}
273
+ {formData.section_type === 'agreement_signature' ? (
274
+ <AgreementSignatureEditor
275
+ section={section}
276
+ templateId={section?.template_id}
277
+ onSave={(updatedSection) => {
278
+ setFormData({
279
+ ...formData,
280
+ content: updatedSection.content,
281
+ template: updatedSection.template
282
+ });
283
+ }}
284
+ onTemplateChange={(updatedSection) => {
285
+ setFormData({
286
+ ...formData,
287
+ content: updatedSection.content,
288
+ template: updatedSection.template
289
+ });
290
+ }}
291
+ onCancel={onCancel}
292
+ hideFooter={true}
293
+ />
294
+ ) : (
295
+ <>
296
+ {/* Content Editor or Preview - Only show if not dynamic */}
271
297
  {!formData.is_dynamic ? (
272
298
  <div className="section-editor-content-section">
273
299
  <div className="section-editor-content-header">
@@ -411,6 +437,8 @@ const SectionEditor = ({ section, availableVariables = [], onSave, onCancel }) =
411
437
  </div>
412
438
  </div>
413
439
  )}
440
+ </>
441
+ )}
414
442
  </div>
415
443
 
416
444
  {/* Footer */}