@visns-studio/visns-components 5.13.9 → 5.13.11

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.
@@ -0,0 +1,412 @@
1
+ /* Header Configuration Editor Styles */
2
+ .header-config-editor {
3
+ background: var(--tertiary-color, white);
4
+ border-radius: 8px;
5
+ padding: 1.5rem;
6
+ border: 1px solid var(--paragraph-color, #e2e8f0);
7
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
8
+ }
9
+
10
+ .header-config-header {
11
+ margin-bottom: 1.5rem;
12
+ }
13
+
14
+ .header-config-title {
15
+ display: flex;
16
+ align-items: center;
17
+ gap: 0.5rem;
18
+ margin-bottom: 0.5rem;
19
+ }
20
+
21
+ .header-config-title h3 {
22
+ margin: 0;
23
+ font-size: 1.25rem;
24
+ font-weight: 600;
25
+ color: var(--header-color, #1f2937);
26
+ }
27
+
28
+ .header-config-title svg {
29
+ color: var(--primary-color, #3cbf7d);
30
+ }
31
+
32
+ .header-config-description {
33
+ color: var(--paragraph-color, #6b7280);
34
+ margin: 0;
35
+ font-size: 0.875rem;
36
+ }
37
+
38
+ .header-config-content {
39
+ display: flex;
40
+ flex-direction: column;
41
+ gap: 1.5rem;
42
+ }
43
+
44
+ .header-config-section {
45
+ border-bottom: 1px solid var(--paragraph-color, #f3f4f6);
46
+ padding-bottom: 1.5rem;
47
+ }
48
+
49
+ .header-config-section:last-child {
50
+ border-bottom: none;
51
+ padding-bottom: 0;
52
+ }
53
+
54
+ .header-config-section h4 {
55
+ margin: 0 0 1rem 0;
56
+ font-size: 1rem;
57
+ font-weight: 600;
58
+ color: var(--header-color, #374151);
59
+ }
60
+
61
+ /* Main Toggle Switch */
62
+ .header-config-toggle-main {
63
+ display: flex;
64
+ align-items: center;
65
+ gap: 1rem;
66
+ }
67
+
68
+ .header-config-switch {
69
+ position: relative;
70
+ display: inline-block;
71
+ width: 60px;
72
+ height: 34px;
73
+ }
74
+
75
+ .header-config-switch input {
76
+ opacity: 0;
77
+ width: 0;
78
+ height: 0;
79
+ }
80
+
81
+ .slider {
82
+ position: absolute;
83
+ cursor: pointer;
84
+ top: 0;
85
+ left: 0;
86
+ right: 0;
87
+ bottom: 0;
88
+ background-color: #ccc;
89
+ transition: 0.4s;
90
+ border-radius: 34px;
91
+ }
92
+
93
+ .slider:before {
94
+ position: absolute;
95
+ content: "";
96
+ height: 26px;
97
+ width: 26px;
98
+ left: 4px;
99
+ bottom: 4px;
100
+ background-color: white;
101
+ transition: 0.4s;
102
+ border-radius: 50%;
103
+ }
104
+
105
+ input:checked + .slider {
106
+ background-color: var(--primary-color, #3cbf7d);
107
+ }
108
+
109
+ input:focus + .slider {
110
+ box-shadow: 0 0 1px var(--primary-color, #3cbf7d);
111
+ }
112
+
113
+ input:checked + .slider:before {
114
+ transform: translateX(26px);
115
+ }
116
+
117
+ .header-config-toggle-info {
118
+ flex: 1;
119
+ }
120
+
121
+ .header-config-toggle-info strong {
122
+ display: block;
123
+ font-size: 1rem;
124
+ color: var(--header-color, #1f2937);
125
+ margin-bottom: 0.25rem;
126
+ }
127
+
128
+ .header-config-toggle-info p {
129
+ margin: 0;
130
+ font-size: 0.875rem;
131
+ color: var(--paragraph-color, #6b7280);
132
+ }
133
+
134
+ /* Header Options */
135
+ .header-config-options {
136
+ display: flex;
137
+ flex-direction: column;
138
+ gap: 1rem;
139
+ }
140
+
141
+ .header-config-option {
142
+ display: flex;
143
+ align-items: center;
144
+ gap: 0.75rem;
145
+ padding: 0.75rem;
146
+ border: 1px solid var(--paragraph-color, #e5e7eb);
147
+ border-radius: 6px;
148
+ transition: all 0.2s;
149
+ }
150
+
151
+ .header-config-option:hover {
152
+ background: var(--bg-color, #f9fafb);
153
+ border-color: var(--paragraph-color, #d1d5db);
154
+ }
155
+
156
+ /* Custom Checkbox */
157
+ .header-config-checkbox {
158
+ position: relative;
159
+ display: block;
160
+ cursor: pointer;
161
+ padding-left: 35px;
162
+ margin-bottom: 0;
163
+ line-height: 1.5;
164
+ }
165
+
166
+ .header-config-checkbox input {
167
+ position: absolute;
168
+ opacity: 0;
169
+ cursor: pointer;
170
+ height: 0;
171
+ width: 0;
172
+ }
173
+
174
+ .checkmark {
175
+ position: absolute;
176
+ top: 0;
177
+ left: 0;
178
+ height: 20px;
179
+ width: 20px;
180
+ background-color: #eee;
181
+ border-radius: 4px;
182
+ border: 1px solid var(--paragraph-color, #d1d5db);
183
+ }
184
+
185
+ .header-config-checkbox:hover input ~ .checkmark {
186
+ background-color: #ccc;
187
+ }
188
+
189
+ .header-config-checkbox input:checked ~ .checkmark {
190
+ background-color: var(--primary-color, #3cbf7d);
191
+ border-color: var(--primary-color, #3cbf7d);
192
+ }
193
+
194
+ .checkmark:after {
195
+ content: "";
196
+ position: absolute;
197
+ display: none;
198
+ }
199
+
200
+ .header-config-checkbox input:checked ~ .checkmark:after {
201
+ display: block;
202
+ }
203
+
204
+ .header-config-checkbox .checkmark:after {
205
+ left: 6px;
206
+ top: 2px;
207
+ width: 5px;
208
+ height: 10px;
209
+ border: solid white;
210
+ border-width: 0 3px 3px 0;
211
+ transform: rotate(45deg);
212
+ }
213
+
214
+ .header-config-option-info {
215
+ flex: 1;
216
+ }
217
+
218
+ .header-config-option-info strong {
219
+ display: block;
220
+ font-size: 0.875rem;
221
+ color: var(--header-color, #374151);
222
+ margin-bottom: 0.25rem;
223
+ }
224
+
225
+ .header-config-option-info p {
226
+ margin: 0;
227
+ font-size: 0.75rem;
228
+ color: var(--paragraph-color, #6b7280);
229
+ }
230
+
231
+ /* Header Preview */
232
+ .header-config-preview {
233
+ border: 1px solid var(--paragraph-color, #e5e7eb);
234
+ border-radius: 6px;
235
+ padding: 1rem;
236
+ background: var(--bg-color, #f9fafb);
237
+ }
238
+
239
+ .preview-header {
240
+ display: flex;
241
+ align-items: center;
242
+ justify-content: space-between;
243
+ padding: 15px 0;
244
+ border-bottom: 2px solid var(--primary-color, #3cbf7d);
245
+ }
246
+
247
+ .preview-header-logo {
248
+ flex-shrink: 0;
249
+ margin-right: 15px;
250
+ }
251
+
252
+ .preview-header-logo img {
253
+ max-height: 40px;
254
+ width: auto;
255
+ }
256
+
257
+ .preview-logo-placeholder {
258
+ width: 40px;
259
+ height: 40px;
260
+ background-color: var(--primary-color, #3cbf7d);
261
+ color: white;
262
+ border-radius: 6px;
263
+ display: flex;
264
+ align-items: center;
265
+ justify-content: center;
266
+ font-weight: bold;
267
+ font-size: 12px;
268
+ }
269
+
270
+ .preview-header-content {
271
+ flex: 1;
272
+ text-align: right;
273
+ }
274
+
275
+ .preview-company-name {
276
+ font-size: 16px;
277
+ font-weight: bold;
278
+ color: var(--primary-color, #3cbf7d);
279
+ margin-bottom: 4px;
280
+ }
281
+
282
+ .preview-company-info {
283
+ font-size: 11px;
284
+ color: #666;
285
+ line-height: 1.3;
286
+ }
287
+
288
+ .preview-company-info div {
289
+ margin-bottom: 1px;
290
+ }
291
+
292
+ .preview-label {
293
+ font-weight: 600;
294
+ color: var(--paragraph-color, #64748b);
295
+ display: inline-block;
296
+ width: 50px;
297
+ font-size: 10px;
298
+ }
299
+
300
+ /* Branding Profile Info */
301
+ .header-config-branding-info {
302
+ background: var(--bg-color, #f0f9ff);
303
+ border: 1px solid var(--primary-color, #bfdbfe);
304
+ border-radius: 6px;
305
+ padding: 1rem;
306
+ }
307
+
308
+ .header-config-branding-info p {
309
+ margin: 0 0 0.5rem 0;
310
+ font-size: 0.875rem;
311
+ color: var(--paragraph-color, #374151);
312
+ }
313
+
314
+ .header-config-branding-info p:last-child {
315
+ margin-bottom: 0;
316
+ }
317
+
318
+ .header-config-note {
319
+ font-size: 0.75rem !important;
320
+ color: var(--paragraph-color, #6b7280) !important;
321
+ font-style: italic;
322
+ margin-top: 0.5rem !important;
323
+ }
324
+
325
+ /* Actions */
326
+ .header-config-actions {
327
+ display: flex;
328
+ align-items: center;
329
+ justify-content: flex-end;
330
+ gap: 0.75rem;
331
+ margin-top: 1.5rem;
332
+ padding-top: 1.5rem;
333
+ border-top: 1px solid var(--paragraph-color, #e5e7eb);
334
+ }
335
+
336
+ .header-config-btn {
337
+ display: inline-flex;
338
+ align-items: center;
339
+ gap: 0.5rem;
340
+ padding: 0.75rem 1.25rem;
341
+ font-size: 0.875rem;
342
+ font-weight: 600;
343
+ border-radius: 6px;
344
+ cursor: pointer;
345
+ transition: all 0.2s;
346
+ border: none;
347
+ text-decoration: none;
348
+ }
349
+
350
+ .header-config-btn.secondary {
351
+ background: var(--tertiary-color, white);
352
+ color: var(--paragraph-color, #6b7280);
353
+ border: 1px solid var(--paragraph-color, #d1d5db);
354
+ }
355
+
356
+ .header-config-btn.secondary:hover {
357
+ background: var(--bg-color, #f9fafb);
358
+ border-color: var(--paragraph-color, #9ca3af);
359
+ }
360
+
361
+ .header-config-btn.primary {
362
+ background: var(--primary-color, #3cbf7d);
363
+ color: white;
364
+ border: 1px solid var(--primary-color, #3cbf7d);
365
+ box-shadow: 0 2px 4px rgba(60, 191, 125, 0.2);
366
+ }
367
+
368
+ .header-config-btn.primary:hover {
369
+ background: var(--header-color, #1b3933);
370
+ border-color: var(--header-color, #1b3933);
371
+ box-shadow: 0 4px 8px rgba(27, 57, 51, 0.3);
372
+ transform: translateY(-1px);
373
+ }
374
+
375
+ /* Responsive Design */
376
+ @media (max-width: 768px) {
377
+ .header-config-editor {
378
+ padding: 1rem;
379
+ }
380
+
381
+ .header-config-toggle-main {
382
+ flex-direction: column;
383
+ align-items: flex-start;
384
+ gap: 0.75rem;
385
+ }
386
+
387
+ .header-config-option {
388
+ flex-direction: column;
389
+ align-items: flex-start;
390
+ gap: 0.5rem;
391
+ }
392
+
393
+ .preview-header {
394
+ flex-direction: column;
395
+ align-items: flex-start;
396
+ gap: 1rem;
397
+ }
398
+
399
+ .preview-header-content {
400
+ text-align: left;
401
+ }
402
+
403
+ .header-config-actions {
404
+ flex-direction: column-reverse;
405
+ gap: 0.5rem;
406
+ }
407
+
408
+ .header-config-btn {
409
+ width: 100%;
410
+ justify-content: center;
411
+ }
412
+ }
@@ -0,0 +1,219 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Settings, Eye, EyeOff, Save, X } from 'lucide-react';
3
+ import './HeaderConfigEditor.css';
4
+
5
+ const HeaderConfigEditor = ({
6
+ config,
7
+ onSave,
8
+ onCancel,
9
+ brandingProfile
10
+ }) => {
11
+ const [headerConfig, setHeaderConfig] = useState({
12
+ enabled: false,
13
+ show_address: true,
14
+ show_website: true,
15
+ show_phone: true,
16
+ show_abn: true,
17
+ ...config
18
+ });
19
+
20
+ useEffect(() => {
21
+ if (config) {
22
+ setHeaderConfig({ ...headerConfig, ...config });
23
+ }
24
+ }, [config]);
25
+
26
+ const handleToggle = (field) => {
27
+ setHeaderConfig(prev => ({
28
+ ...prev,
29
+ [field]: !prev[field]
30
+ }));
31
+ };
32
+
33
+ const handleSave = () => {
34
+ onSave(headerConfig);
35
+ };
36
+
37
+ const companyInfo = brandingProfile?.company_info || {};
38
+ const hasLogo = brandingProfile?.logo_url;
39
+
40
+ return (
41
+ <div className="header-config-editor">
42
+ <div className="header-config-header">
43
+ <div className="header-config-title">
44
+ <Settings size={20} />
45
+ <h3>Proposal Header Configuration</h3>
46
+ </div>
47
+ <p className="header-config-description">
48
+ Configure the header that appears at the top of your proposals
49
+ </p>
50
+ </div>
51
+
52
+ <div className="header-config-content">
53
+ {/* Main Toggle */}
54
+ <div className="header-config-section">
55
+ <div className="header-config-toggle-main">
56
+ <label className="header-config-switch">
57
+ <input
58
+ type="checkbox"
59
+ checked={headerConfig.enabled}
60
+ onChange={() => handleToggle('enabled')}
61
+ />
62
+ <span className="slider"></span>
63
+ </label>
64
+ <div className="header-config-toggle-info">
65
+ <strong>Enable Proposal Header</strong>
66
+ <p>Show company information at the top of each proposal</p>
67
+ </div>
68
+ </div>
69
+ </div>
70
+
71
+ {/* Header Options */}
72
+ {headerConfig.enabled && (
73
+ <>
74
+ <div className="header-config-section">
75
+ <h4>Header Content</h4>
76
+ <div className="header-config-options">
77
+ <div className="header-config-option">
78
+ <label className="header-config-checkbox">
79
+ <input
80
+ type="checkbox"
81
+ checked={headerConfig.show_address}
82
+ onChange={() => handleToggle('show_address')}
83
+ />
84
+ <span className="checkmark"></span>
85
+ </label>
86
+ <div className="header-config-option-info">
87
+ <strong>Show Address</strong>
88
+ <p>{companyInfo.address || 'No address set in branding profile'}</p>
89
+ </div>
90
+ </div>
91
+
92
+ <div className="header-config-option">
93
+ <label className="header-config-checkbox">
94
+ <input
95
+ type="checkbox"
96
+ checked={headerConfig.show_website}
97
+ onChange={() => handleToggle('show_website')}
98
+ />
99
+ <span className="checkmark"></span>
100
+ </label>
101
+ <div className="header-config-option-info">
102
+ <strong>Show Website</strong>
103
+ <p>{companyInfo.website || 'No website set in branding profile'}</p>
104
+ </div>
105
+ </div>
106
+
107
+ <div className="header-config-option">
108
+ <label className="header-config-checkbox">
109
+ <input
110
+ type="checkbox"
111
+ checked={headerConfig.show_phone}
112
+ onChange={() => handleToggle('show_phone')}
113
+ />
114
+ <span className="checkmark"></span>
115
+ </label>
116
+ <div className="header-config-option-info">
117
+ <strong>Show Phone</strong>
118
+ <p>{companyInfo.phone || 'No phone set in branding profile'}</p>
119
+ </div>
120
+ </div>
121
+
122
+ <div className="header-config-option">
123
+ <label className="header-config-checkbox">
124
+ <input
125
+ type="checkbox"
126
+ checked={headerConfig.show_abn}
127
+ onChange={() => handleToggle('show_abn')}
128
+ />
129
+ <span className="checkmark"></span>
130
+ </label>
131
+ <div className="header-config-option-info">
132
+ <strong>Show ABN</strong>
133
+ <p>{companyInfo.abn || 'No ABN set in branding profile'}</p>
134
+ </div>
135
+ </div>
136
+ </div>
137
+ </div>
138
+
139
+ {/* Header Preview */}
140
+ <div className="header-config-section">
141
+ <h4>Header Preview</h4>
142
+ <div className="header-config-preview">
143
+ <div className="preview-header">
144
+ <div className="preview-header-logo">
145
+ {hasLogo ? (
146
+ <img src={brandingProfile.logo_url} alt="Logo" />
147
+ ) : (
148
+ <div className="preview-logo-placeholder">
149
+ {brandingProfile?.company_name?.substring(0, 3).toUpperCase() || 'LOG'}
150
+ </div>
151
+ )}
152
+ </div>
153
+ <div className="preview-header-content">
154
+ <div className="preview-company-name">
155
+ {brandingProfile?.company_name || 'Company Name'}
156
+ </div>
157
+ <div className="preview-company-info">
158
+ {headerConfig.show_address && companyInfo.address && (
159
+ <div>{companyInfo.address}</div>
160
+ )}
161
+ {headerConfig.show_website && companyInfo.website && (
162
+ <div><span className="preview-label">Website</span>{companyInfo.website}</div>
163
+ )}
164
+ {headerConfig.show_phone && companyInfo.phone && (
165
+ <div><span className="preview-label">Phone</span>{companyInfo.phone}</div>
166
+ )}
167
+ {headerConfig.show_abn && companyInfo.abn && (
168
+ <div><span className="preview-label">ABN</span>{companyInfo.abn}</div>
169
+ )}
170
+ </div>
171
+ </div>
172
+ </div>
173
+ </div>
174
+ </div>
175
+ </>
176
+ )}
177
+
178
+ {/* Branding Profile Info */}
179
+ <div className="header-config-section">
180
+ <h4>Branding Profile Information</h4>
181
+ <div className="header-config-branding-info">
182
+ <p>
183
+ <strong>Current Profile:</strong> {brandingProfile?.name || 'Default'}
184
+ </p>
185
+ <p>
186
+ <strong>Company:</strong> {brandingProfile?.company_name || 'Not set'}
187
+ </p>
188
+ <p>
189
+ <strong>Logo:</strong> {hasLogo ? 'Set' : 'Not set'}
190
+ </p>
191
+ <p className="header-config-note">
192
+ Header content is pulled from the branding profile. To update company information,
193
+ edit the branding profile settings.
194
+ </p>
195
+ </div>
196
+ </div>
197
+ </div>
198
+
199
+ <div className="header-config-actions">
200
+ <button
201
+ onClick={onCancel}
202
+ className="header-config-btn secondary"
203
+ >
204
+ <X size={16} />
205
+ Cancel
206
+ </button>
207
+ <button
208
+ onClick={handleSave}
209
+ className="header-config-btn primary"
210
+ >
211
+ <Save size={16} />
212
+ Save Header Settings
213
+ </button>
214
+ </div>
215
+ </div>
216
+ );
217
+ };
218
+
219
+ export default HeaderConfigEditor;