@visns-studio/visns-components 5.11.5 → 5.11.7
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/README.md +353 -0
- package/package.json +5 -5
- package/src/components/crm/Field.jsx +88 -60
- package/src/components/crm/columns/ColumnRenderers.jsx +19 -0
- package/src/components/crm/generic/GenericDetail.jsx +40 -0
- package/src/components/crm/generic/GenericEditableTable.jsx +3 -3
- package/src/components/crm/generic/GenericQuote.jsx +353 -1
- package/src/components/crm/generic/styles/GenericEditableTable.module.scss +102 -22
- package/src/components/crm/generic/styles/GenericQuote.module.scss +240 -0
- package/src/components/proposal/ProposalTemplatePreview.css +438 -0
- package/src/components/proposal/ProposalTemplatePreview.jsx +337 -0
- package/src/components/proposal/ProposalTemplateSectionManager.css +338 -0
- package/src/components/proposal/ProposalTemplateSectionManager.jsx +473 -0
- package/src/components/proposal/SectionEditor.css +563 -0
- package/src/components/proposal/SectionEditor.jsx +412 -0
- package/src/components/proposal/SectionTypeSelector.css +277 -0
- package/src/components/proposal/SectionTypeSelector.jsx +158 -0
- package/src/components/proposal/VariableInserter.css +392 -0
- package/src/components/proposal/VariableInserter.jsx +260 -0
- package/src/components/proposal/index.js +5 -0
- package/src/components/styles/global.css +179 -1
- package/src/index.js +13 -0
- package/src/utils/urlBuilder.js +40 -0
package/README.md
CHANGED
|
@@ -2350,6 +2350,359 @@ We welcome contributions! Please follow these steps:
|
|
|
2350
2350
|
|
|
2351
2351
|
[MIT License](LICENSE)
|
|
2352
2352
|
|
|
2353
|
+
### Quote Management Components
|
|
2354
|
+
|
|
2355
|
+
#### GenericQuote (Enhanced with Proposal System v5.12.0+)
|
|
2356
|
+
|
|
2357
|
+
The GenericQuote component provides comprehensive quote management with integrated proposal generation capabilities. It supports both traditional quote display and full proposal creation with cover pages, table of contents, and customizable sections.
|
|
2358
|
+
|
|
2359
|
+
```jsx
|
|
2360
|
+
<GenericQuote
|
|
2361
|
+
config={{
|
|
2362
|
+
// Basic quote configuration
|
|
2363
|
+
data: quoteData,
|
|
2364
|
+
currency: "AUD",
|
|
2365
|
+
companyLogo: "/path/to/logo.png",
|
|
2366
|
+
|
|
2367
|
+
// Standard features
|
|
2368
|
+
showLineItems: true,
|
|
2369
|
+
allowEditing: true,
|
|
2370
|
+
showNotes: true,
|
|
2371
|
+
|
|
2372
|
+
// Enhanced proposal features (v5.12.0+)
|
|
2373
|
+
proposalMode: {
|
|
2374
|
+
enabled: true,
|
|
2375
|
+
templateId: 1,
|
|
2376
|
+
brandingProfileId: 2,
|
|
2377
|
+
customSections: [
|
|
2378
|
+
{
|
|
2379
|
+
type: 'overview',
|
|
2380
|
+
title: 'Project Overview',
|
|
2381
|
+
content: '<h1>Executive Summary</h1><p>This proposal outlines...</p>'
|
|
2382
|
+
},
|
|
2383
|
+
{
|
|
2384
|
+
type: 'terms_conditions',
|
|
2385
|
+
title: 'Terms & Conditions',
|
|
2386
|
+
content: '<h3>Payment Terms</h3><p>Net 30 days...</p>'
|
|
2387
|
+
}
|
|
2388
|
+
]
|
|
2389
|
+
}
|
|
2390
|
+
}}
|
|
2391
|
+
userProfile={userProfile}
|
|
2392
|
+
onQuoteSave={(quoteData) => handleQuoteSave(quoteData)}
|
|
2393
|
+
onProposalGenerate={(proposalData) => handleProposalGeneration(proposalData)}
|
|
2394
|
+
/>
|
|
2395
|
+
```
|
|
2396
|
+
|
|
2397
|
+
**New Proposal Features:**
|
|
2398
|
+
|
|
2399
|
+
1. **Proposal Mode Toggle**:
|
|
2400
|
+
- Seamlessly switch between quote view and proposal generation mode
|
|
2401
|
+
- Maintains full backward compatibility with existing quote functionality
|
|
2402
|
+
- Context-aware interface that adapts based on proposal configuration
|
|
2403
|
+
|
|
2404
|
+
2. **Template Management Integration**:
|
|
2405
|
+
- Select from pre-configured proposal templates
|
|
2406
|
+
- Template preview with variable substitution
|
|
2407
|
+
- Custom section creation and ordering
|
|
2408
|
+
- Dynamic content population from quote data
|
|
2409
|
+
|
|
2410
|
+
3. **Branding Profile Support**:
|
|
2411
|
+
- Company logo and branding integration
|
|
2412
|
+
- Custom color schemes and styling
|
|
2413
|
+
- Professional cover page generation
|
|
2414
|
+
- Consistent brand presentation
|
|
2415
|
+
|
|
2416
|
+
4. **Advanced Section Management**:
|
|
2417
|
+
- **Cover Page**: Company branding with proposal title and customer details
|
|
2418
|
+
- **Table of Contents**: Auto-generated from all proposal sections
|
|
2419
|
+
- **Overview**: Executive summary with custom headers (H1, H2, H3) and content
|
|
2420
|
+
- **Quote Items**: Pricing table following original quote structure
|
|
2421
|
+
- **Terms & Conditions**: Static legal terms (rarely changes)
|
|
2422
|
+
- **Payment Terms**: Dynamic payment schedules and due dates
|
|
2423
|
+
- **Review Log**: Version history and change tracking
|
|
2424
|
+
- **Agreement & Signatures**: Static signature section for approvals
|
|
2425
|
+
|
|
2426
|
+
5. **Dynamic Content System**:
|
|
2427
|
+
- Variable replacement throughout all sections
|
|
2428
|
+
- Real-time preview of populated content
|
|
2429
|
+
- Support for both static and dynamic sections
|
|
2430
|
+
- Custom variable definitions per template
|
|
2431
|
+
|
|
2432
|
+
**Configuration Options:**
|
|
2433
|
+
|
|
2434
|
+
```jsx
|
|
2435
|
+
const proposalConfig = {
|
|
2436
|
+
// Enable proposal features
|
|
2437
|
+
enabled: true,
|
|
2438
|
+
|
|
2439
|
+
// Template selection
|
|
2440
|
+
templateId: 1, // Selected proposal template
|
|
2441
|
+
allowTemplateChange: true, // Allow template switching
|
|
2442
|
+
|
|
2443
|
+
// Branding configuration
|
|
2444
|
+
brandingProfileId: 2, // Selected branding profile
|
|
2445
|
+
allowBrandingChange: true, // Allow branding changes
|
|
2446
|
+
|
|
2447
|
+
// Section customization
|
|
2448
|
+
customSections: [
|
|
2449
|
+
{
|
|
2450
|
+
type: 'overview',
|
|
2451
|
+
title: 'Project Overview',
|
|
2452
|
+
content: '<h1>Executive Summary</h1><p>Custom content...</p>',
|
|
2453
|
+
variables: {
|
|
2454
|
+
'project_scope': 'Website development and maintenance',
|
|
2455
|
+
'timeline': '8-12 weeks'
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
],
|
|
2459
|
+
|
|
2460
|
+
// Generation settings
|
|
2461
|
+
autoGenerateTOC: true, // Auto-generate table of contents
|
|
2462
|
+
includePageNumbers: true, // Add page numbers to PDF
|
|
2463
|
+
watermark: false, // Add watermark to pages
|
|
2464
|
+
|
|
2465
|
+
// PDF export options
|
|
2466
|
+
paperSize: 'A4', // Paper size for PDF generation
|
|
2467
|
+
orientation: 'portrait', // Page orientation
|
|
2468
|
+
margins: '40px', // Page margins
|
|
2469
|
+
|
|
2470
|
+
// UI customization
|
|
2471
|
+
showPreview: true, // Show live preview panel
|
|
2472
|
+
previewMode: 'split', // 'split', 'modal', 'tabs'
|
|
2473
|
+
enableSectionReorder: true, // Allow section drag-and-drop
|
|
2474
|
+
showVariableHelper: true, // Show available variables
|
|
2475
|
+
};
|
|
2476
|
+
```
|
|
2477
|
+
|
|
2478
|
+
**Available Variables for Dynamic Content:**
|
|
2479
|
+
|
|
2480
|
+
The proposal system supports these built-in variables that are automatically populated from quote and customer data:
|
|
2481
|
+
|
|
2482
|
+
```javascript
|
|
2483
|
+
// Customer Information
|
|
2484
|
+
{{customer_name}} // Customer company name
|
|
2485
|
+
{{customer_address}} // Customer full address
|
|
2486
|
+
{{customer_contact}} // Primary contact name
|
|
2487
|
+
{{customer_email}} // Primary contact email
|
|
2488
|
+
{{customer_phone}} // Primary contact phone
|
|
2489
|
+
|
|
2490
|
+
// Quote Information
|
|
2491
|
+
{{quote_number}} // Quote/proposal number
|
|
2492
|
+
{{quote_date}} // Quote creation date
|
|
2493
|
+
{{quote_valid_until}} // Quote expiration date
|
|
2494
|
+
{{total_amount}} // Total quote amount (formatted)
|
|
2495
|
+
{{total_ex_gst}} // Total excluding GST
|
|
2496
|
+
{{total_gst}} // GST amount
|
|
2497
|
+
{{currency}} // Currency symbol/code
|
|
2498
|
+
|
|
2499
|
+
// Company Information
|
|
2500
|
+
{{company_name}} // Your company name
|
|
2501
|
+
{{company_address}} // Your company address
|
|
2502
|
+
{{company_phone}} // Your company phone
|
|
2503
|
+
{{company_email}} // Your company email
|
|
2504
|
+
{{company_website}} // Your company website
|
|
2505
|
+
{{company_abn}} // Australian Business Number
|
|
2506
|
+
|
|
2507
|
+
// Project Information
|
|
2508
|
+
{{project_manager}} // Assigned project manager
|
|
2509
|
+
{{project_timeline}} // Estimated timeline
|
|
2510
|
+
{{project_scope}} // Project scope description
|
|
2511
|
+
|
|
2512
|
+
// Date Variables
|
|
2513
|
+
{{current_date}} // Current date
|
|
2514
|
+
{{due_date}} // Payment due date
|
|
2515
|
+
{{start_date}} // Project start date
|
|
2516
|
+
|
|
2517
|
+
// Item-Specific Variables
|
|
2518
|
+
{{items_onceoff}} // One-time purchase items table
|
|
2519
|
+
{{items_monthly}} // Monthly subscription items table
|
|
2520
|
+
{{items_yearly}} // Yearly subscription items table
|
|
2521
|
+
{{line_items_total}} // Total number of line items
|
|
2522
|
+
```
|
|
2523
|
+
|
|
2524
|
+
**Integration with visns-packages Backend:**
|
|
2525
|
+
|
|
2526
|
+
The GenericQuote component seamlessly integrates with the visns-packages proposal system:
|
|
2527
|
+
|
|
2528
|
+
```jsx
|
|
2529
|
+
// Backend integration endpoints
|
|
2530
|
+
const proposalEndpoints = {
|
|
2531
|
+
// Template management
|
|
2532
|
+
templates: '/ajax/proposal-templates',
|
|
2533
|
+
templatePreview: '/ajax/proposal-templates/{id}/preview',
|
|
2534
|
+
|
|
2535
|
+
// Branding profiles
|
|
2536
|
+
brandingProfiles: '/ajax/branding-profiles',
|
|
2537
|
+
brandingCSS: '/ajax/branding-profiles/{id}/css',
|
|
2538
|
+
|
|
2539
|
+
// PDF generation
|
|
2540
|
+
generatePDF: '/ajax/pdf/generate-proposal',
|
|
2541
|
+
previewHTML: '/ajax/pdf/preview-proposal',
|
|
2542
|
+
|
|
2543
|
+
// Variable helpers
|
|
2544
|
+
availableVariables: '/ajax/proposal-templates/variables/available'
|
|
2545
|
+
};
|
|
2546
|
+
```
|
|
2547
|
+
|
|
2548
|
+
**Event Handling:**
|
|
2549
|
+
|
|
2550
|
+
```jsx
|
|
2551
|
+
<GenericQuote
|
|
2552
|
+
config={quoteConfig}
|
|
2553
|
+
// Quote events (existing)
|
|
2554
|
+
onQuoteSave={(data) => console.log('Quote saved:', data)}
|
|
2555
|
+
onLineItemChange={(items) => console.log('Line items updated:', items)}
|
|
2556
|
+
|
|
2557
|
+
// New proposal events
|
|
2558
|
+
onProposalGenerate={(proposalData) => {
|
|
2559
|
+
console.log('Proposal generated:', proposalData);
|
|
2560
|
+
// Handle PDF generation or preview
|
|
2561
|
+
}}
|
|
2562
|
+
onTemplateChange={(templateId) => {
|
|
2563
|
+
console.log('Template changed to:', templateId);
|
|
2564
|
+
// Update template-specific settings
|
|
2565
|
+
}}
|
|
2566
|
+
onBrandingChange={(brandingId) => {
|
|
2567
|
+
console.log('Branding changed to:', brandingId);
|
|
2568
|
+
// Update branding-specific styling
|
|
2569
|
+
}}
|
|
2570
|
+
onSectionUpdate={(sections) => {
|
|
2571
|
+
console.log('Sections updated:', sections);
|
|
2572
|
+
// Handle custom section changes
|
|
2573
|
+
}}
|
|
2574
|
+
onVariableChange={(variables) => {
|
|
2575
|
+
console.log('Variables updated:', variables);
|
|
2576
|
+
// Handle variable substitution updates
|
|
2577
|
+
}}
|
|
2578
|
+
/>
|
|
2579
|
+
```
|
|
2580
|
+
|
|
2581
|
+
**Backward Compatibility:**
|
|
2582
|
+
|
|
2583
|
+
All existing GenericQuote functionality remains unchanged:
|
|
2584
|
+
- Standard quote display and editing
|
|
2585
|
+
- Line item management
|
|
2586
|
+
- Notes and attachments
|
|
2587
|
+
- Export capabilities
|
|
2588
|
+
- Custom actions and workflows
|
|
2589
|
+
|
|
2590
|
+
Proposal features are **opt-in** and activated only when `proposalConfig.enabled: true` is set.
|
|
2591
|
+
|
|
2592
|
+
**Advanced Usage Example:**
|
|
2593
|
+
|
|
2594
|
+
```jsx
|
|
2595
|
+
import React, { useState } from 'react';
|
|
2596
|
+
import { GenericQuote } from '@visns-studio/visns-components';
|
|
2597
|
+
|
|
2598
|
+
const QuoteProposalManager = () => {
|
|
2599
|
+
const [quoteData, setQuoteData] = useState({
|
|
2600
|
+
id: 1,
|
|
2601
|
+
number: 'Q-2024-001',
|
|
2602
|
+
customer: {
|
|
2603
|
+
name: 'Acme Corporation',
|
|
2604
|
+
address: '123 Business St, Sydney NSW 2000',
|
|
2605
|
+
contact: 'John Smith',
|
|
2606
|
+
email: 'john@acme.com'
|
|
2607
|
+
},
|
|
2608
|
+
items: [
|
|
2609
|
+
{
|
|
2610
|
+
description: 'Website Development',
|
|
2611
|
+
quantity: 1,
|
|
2612
|
+
rate: 5000,
|
|
2613
|
+
total: 5000,
|
|
2614
|
+
type: 'onceoff'
|
|
2615
|
+
},
|
|
2616
|
+
{
|
|
2617
|
+
description: 'Monthly Hosting',
|
|
2618
|
+
quantity: 1,
|
|
2619
|
+
rate: 99,
|
|
2620
|
+
total: 99,
|
|
2621
|
+
type: 'monthly'
|
|
2622
|
+
}
|
|
2623
|
+
],
|
|
2624
|
+
total: 5099
|
|
2625
|
+
});
|
|
2626
|
+
|
|
2627
|
+
const [proposalMode, setProposalMode] = useState(false);
|
|
2628
|
+
|
|
2629
|
+
const proposalConfig = {
|
|
2630
|
+
enabled: true,
|
|
2631
|
+
templateId: 1,
|
|
2632
|
+
brandingProfileId: 1,
|
|
2633
|
+
customSections: [
|
|
2634
|
+
{
|
|
2635
|
+
type: 'overview',
|
|
2636
|
+
title: 'Project Overview',
|
|
2637
|
+
content: `
|
|
2638
|
+
<h1>Executive Summary</h1>
|
|
2639
|
+
<p>This proposal outlines our recommended solution for {{customer_name}}.</p>
|
|
2640
|
+
|
|
2641
|
+
<h2>Project Scope</h2>
|
|
2642
|
+
<p>We will develop a modern, responsive website that meets your business requirements.</p>
|
|
2643
|
+
|
|
2644
|
+
<h3>Key Deliverables</h3>
|
|
2645
|
+
<ul>
|
|
2646
|
+
<li>Custom website design and development</li>
|
|
2647
|
+
<li>Content management system</li>
|
|
2648
|
+
<li>Mobile-responsive design</li>
|
|
2649
|
+
<li>SEO optimization</li>
|
|
2650
|
+
</ul>
|
|
2651
|
+
`
|
|
2652
|
+
}
|
|
2653
|
+
],
|
|
2654
|
+
autoGenerateTOC: true,
|
|
2655
|
+
showPreview: true,
|
|
2656
|
+
previewMode: 'split'
|
|
2657
|
+
};
|
|
2658
|
+
|
|
2659
|
+
return (
|
|
2660
|
+
<div className="quote-proposal-manager">
|
|
2661
|
+
<div className="mode-toggle">
|
|
2662
|
+
<button
|
|
2663
|
+
onClick={() => setProposalMode(false)}
|
|
2664
|
+
className={!proposalMode ? 'active' : ''}
|
|
2665
|
+
>
|
|
2666
|
+
Quote View
|
|
2667
|
+
</button>
|
|
2668
|
+
<button
|
|
2669
|
+
onClick={() => setProposalMode(true)}
|
|
2670
|
+
className={proposalMode ? 'active' : ''}
|
|
2671
|
+
>
|
|
2672
|
+
Proposal Builder
|
|
2673
|
+
</button>
|
|
2674
|
+
</div>
|
|
2675
|
+
|
|
2676
|
+
<GenericQuote
|
|
2677
|
+
config={{
|
|
2678
|
+
data: quoteData,
|
|
2679
|
+
currency: "AUD",
|
|
2680
|
+
showLineItems: true,
|
|
2681
|
+
allowEditing: !proposalMode,
|
|
2682
|
+
|
|
2683
|
+
// Enable proposal features based on mode
|
|
2684
|
+
proposalMode: proposalMode ? proposalConfig : { enabled: false }
|
|
2685
|
+
}}
|
|
2686
|
+
userProfile={userProfile}
|
|
2687
|
+
onQuoteSave={(data) => {
|
|
2688
|
+
setQuoteData(data);
|
|
2689
|
+
console.log('Quote updated:', data);
|
|
2690
|
+
}}
|
|
2691
|
+
onProposalGenerate={(proposalData) => {
|
|
2692
|
+
// Generate PDF or handle proposal data
|
|
2693
|
+
console.log('Generating proposal PDF:', proposalData);
|
|
2694
|
+
|
|
2695
|
+
// Example: Download PDF
|
|
2696
|
+
window.open(`/ajax/pdf/generate-proposal?quote_id=${quoteData.id}`, '_blank');
|
|
2697
|
+
}}
|
|
2698
|
+
/>
|
|
2699
|
+
</div>
|
|
2700
|
+
);
|
|
2701
|
+
};
|
|
2702
|
+
|
|
2703
|
+
export default QuoteProposalManager;
|
|
2704
|
+
```
|
|
2705
|
+
|
|
2353
2706
|
## Modal Actions in Components
|
|
2354
2707
|
|
|
2355
2708
|
### Using Modal Actions in GenericQuote Component
|
package/package.json
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"@nivo/pie": "^0.99.0",
|
|
14
14
|
"@tinymce/miniature": "^6.0.0",
|
|
15
15
|
"@tinymce/tinymce-react": "^6.2.1",
|
|
16
|
+
"@uiw/react-color": "^2.6.0",
|
|
16
17
|
"@visns-studio/visns-datagrid-community": "^1.0.14",
|
|
17
18
|
"@visns-studio/visns-datagrid-enterprise": "^1.0.14",
|
|
18
19
|
"@vitejs/plugin-react": "^4.5.2",
|
|
@@ -27,15 +28,14 @@
|
|
|
27
28
|
"html-react-parser": "^5.2.5",
|
|
28
29
|
"lodash": "^4.17.21",
|
|
29
30
|
"lodash.debounce": "^4.0.8",
|
|
30
|
-
"lucide-react": "^0.
|
|
31
|
+
"lucide-react": "^0.518.0",
|
|
31
32
|
"moment": "^2.30.1",
|
|
32
33
|
"motion": "^12.18.1",
|
|
33
34
|
"numeral": "^2.0.6",
|
|
34
35
|
"pluralize": "^8.0.0",
|
|
35
36
|
"qrcode.react": "^4.2.0",
|
|
36
37
|
"quill-image-uploader": "^1.3.0",
|
|
37
|
-
"react-big-calendar": "^1.19.
|
|
38
|
-
"react-color": "^2.19.3",
|
|
38
|
+
"react-big-calendar": "^1.19.4",
|
|
39
39
|
"react-copy-to-clipboard": "^5.1.0",
|
|
40
40
|
"react-datepicker": "^8.4.0",
|
|
41
41
|
"react-dropzone": "^14.3.8",
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
88
88
|
},
|
|
89
89
|
"name": "@visns-studio/visns-components",
|
|
90
|
-
"version": "5.11.
|
|
90
|
+
"version": "5.11.7",
|
|
91
91
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
92
92
|
"main": "src/index.js",
|
|
93
93
|
"files": [
|
|
@@ -108,5 +108,5 @@
|
|
|
108
108
|
"url": "https://github.com/visnsstudio/visns-components/issues"
|
|
109
109
|
},
|
|
110
110
|
"homepage": "https://github.com/visnsstudio/visns-components#readme",
|
|
111
|
-
"packageManager": "yarn@4.9.
|
|
111
|
+
"packageManager": "yarn@4.9.2"
|
|
112
112
|
}
|
|
@@ -7,7 +7,7 @@ import Toggle from 'react-toggle';
|
|
|
7
7
|
import moment from 'moment';
|
|
8
8
|
import parse from 'html-react-parser';
|
|
9
9
|
import Popup from 'reactjs-popup';
|
|
10
|
-
import {
|
|
10
|
+
import { Sketch } from '@uiw/react-color';
|
|
11
11
|
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
|
12
12
|
import { NumericFormat, PatternFormat } from 'react-number-format';
|
|
13
13
|
import { Editor } from '@tinymce/tinymce-react';
|
|
@@ -110,12 +110,13 @@ function Field({
|
|
|
110
110
|
right: '0px',
|
|
111
111
|
bottom: '0px',
|
|
112
112
|
left: '0px',
|
|
113
|
+
zIndex: '999998',
|
|
113
114
|
};
|
|
114
115
|
const popover = {
|
|
115
116
|
position: 'absolute',
|
|
116
|
-
zIndex: '
|
|
117
|
-
left: '
|
|
118
|
-
marginTop: '
|
|
117
|
+
zIndex: '999999',
|
|
118
|
+
left: '0',
|
|
119
|
+
marginTop: '8px',
|
|
119
120
|
};
|
|
120
121
|
|
|
121
122
|
useEffect(() => {
|
|
@@ -456,6 +457,12 @@ function Field({
|
|
|
456
457
|
}
|
|
457
458
|
};
|
|
458
459
|
|
|
460
|
+
// Utility function to detect if a string is a valid hex color
|
|
461
|
+
const isHexColor = (str) => {
|
|
462
|
+
if (!str || typeof str !== 'string') return false;
|
|
463
|
+
return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(str);
|
|
464
|
+
};
|
|
465
|
+
|
|
459
466
|
const renderRelationText = (value) => {
|
|
460
467
|
let text = '';
|
|
461
468
|
|
|
@@ -473,23 +480,56 @@ function Field({
|
|
|
473
480
|
text = processKeys(value);
|
|
474
481
|
}
|
|
475
482
|
|
|
483
|
+
// If the text is a hex color, render it with a color preview
|
|
484
|
+
if (isHexColor(text)) {
|
|
485
|
+
return (
|
|
486
|
+
<div className="relation-color-container">
|
|
487
|
+
<div
|
|
488
|
+
className="relation-color-preview"
|
|
489
|
+
style={{ backgroundColor: text }}
|
|
490
|
+
title={`Color: ${text}`}
|
|
491
|
+
></div>
|
|
492
|
+
<span className="relation-color-text">{text}</span>
|
|
493
|
+
</div>
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
|
|
476
497
|
return text;
|
|
477
498
|
};
|
|
478
499
|
|
|
479
500
|
const renderRelationArrayText = (value) => {
|
|
480
|
-
let text = '';
|
|
481
|
-
|
|
482
501
|
if (value && settings.nameFrom) {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
502
|
+
return (
|
|
503
|
+
<div className="relation-array-container">
|
|
504
|
+
{value.map((item, index) => {
|
|
505
|
+
const itemValue = item[settings.nameFrom];
|
|
506
|
+
|
|
507
|
+
if (isHexColor(itemValue)) {
|
|
508
|
+
return (
|
|
509
|
+
<div key={index} className="relation-color-container inline">
|
|
510
|
+
<div
|
|
511
|
+
className="relation-color-preview"
|
|
512
|
+
style={{ backgroundColor: itemValue }}
|
|
513
|
+
title={`Color: ${itemValue}`}
|
|
514
|
+
></div>
|
|
515
|
+
<span className="relation-color-text">{itemValue}</span>
|
|
516
|
+
{index < value.length - 1 && <span className="separator">, </span>}
|
|
517
|
+
</div>
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return (
|
|
522
|
+
<span key={index}>
|
|
523
|
+
{itemValue}
|
|
524
|
+
{index < value.length - 1 && ', '}
|
|
525
|
+
</span>
|
|
526
|
+
);
|
|
527
|
+
})}
|
|
528
|
+
</div>
|
|
529
|
+
);
|
|
490
530
|
}
|
|
491
531
|
|
|
492
|
-
return
|
|
532
|
+
return '';
|
|
493
533
|
};
|
|
494
534
|
|
|
495
535
|
const renderInput = () => {
|
|
@@ -624,52 +664,41 @@ function Field({
|
|
|
624
664
|
);
|
|
625
665
|
case 'colour':
|
|
626
666
|
return (
|
|
627
|
-
<div className="cpicker">
|
|
628
|
-
<div
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
inputValue && inputValue !== 'null'
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
667
|
+
<div className="cpicker-inline">
|
|
668
|
+
<div className="cpicker-preview-container">
|
|
669
|
+
<div
|
|
670
|
+
className="cpicker__box"
|
|
671
|
+
data-color={inputValue && inputValue !== 'null' ? inputValue : 'No colour selected'}
|
|
672
|
+
style={
|
|
673
|
+
inputValue && inputValue !== 'null'
|
|
674
|
+
? { backgroundColor: inputValue }
|
|
675
|
+
: {}
|
|
676
|
+
}
|
|
677
|
+
title={inputValue && inputValue !== 'null' ? `Current colour: ${inputValue}` : 'No colour selected'}
|
|
678
|
+
></div>
|
|
679
|
+
<span className="cpicker-value">
|
|
680
|
+
{inputValue && inputValue !== 'null' ? inputValue : 'No colour selected'}
|
|
681
|
+
</span>
|
|
682
|
+
</div>
|
|
683
|
+
<div className="cpicker-container">
|
|
684
|
+
<Sketch
|
|
685
|
+
color={
|
|
686
|
+
inputValue && inputValue !== 'null'
|
|
687
|
+
? inputValue
|
|
688
|
+
: '#000000'
|
|
689
|
+
}
|
|
690
|
+
onChange={(color) => {
|
|
691
|
+
onChangeColour(
|
|
692
|
+
{ target: { value: color.hex } },
|
|
693
|
+
{ hex: color.hex },
|
|
694
|
+
settings.id
|
|
643
695
|
);
|
|
644
696
|
}}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
<div
|
|
651
|
-
style={cover}
|
|
652
|
-
onClick={() => {
|
|
653
|
-
setColorPickerShow(false);
|
|
654
|
-
}}
|
|
655
|
-
/>
|
|
656
|
-
|
|
657
|
-
<BlockPicker
|
|
658
|
-
color={
|
|
659
|
-
inputValue && inputValue !== 'null'
|
|
660
|
-
? inputValue
|
|
661
|
-
: ''
|
|
662
|
-
}
|
|
663
|
-
onChangeComplete={(color, event) => {
|
|
664
|
-
onChangeColour(
|
|
665
|
-
event,
|
|
666
|
-
color,
|
|
667
|
-
settings.id
|
|
668
|
-
);
|
|
669
|
-
}}
|
|
670
|
-
/>
|
|
671
|
-
</div>
|
|
672
|
-
) : null}
|
|
697
|
+
style={{
|
|
698
|
+
boxShadow: 'none',
|
|
699
|
+
border: 'none'
|
|
700
|
+
}}
|
|
701
|
+
/>
|
|
673
702
|
</div>
|
|
674
703
|
</div>
|
|
675
704
|
);
|
|
@@ -1981,13 +2010,12 @@ function Field({
|
|
|
1981
2010
|
'media',
|
|
1982
2011
|
'table',
|
|
1983
2012
|
'code',
|
|
1984
|
-
'hr',
|
|
1985
2013
|
],
|
|
1986
2014
|
toolbar:
|
|
1987
2015
|
'insertTextDropdown | undo redo | blocks | ' +
|
|
1988
2016
|
'bold italic forecolor | alignleft aligncenter ' +
|
|
1989
2017
|
'alignright alignjustify | bullist numlist outdent indent | ' +
|
|
1990
|
-
'
|
|
2018
|
+
'removeformat | table | code',
|
|
1991
2019
|
content_style:
|
|
1992
2020
|
'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }',
|
|
1993
2021
|
setup: (editor) => {
|
|
@@ -767,6 +767,25 @@ export const renderRelationColumn = ({
|
|
|
767
767
|
typeof value === 'string' ||
|
|
768
768
|
typeof value === 'number'
|
|
769
769
|
) {
|
|
770
|
+
// Check if the value is a hex color and render with color preview
|
|
771
|
+
const isHexColor = (str) => {
|
|
772
|
+
if (!str || typeof str !== 'string') return false;
|
|
773
|
+
return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(str);
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
if (isHexColor(value)) {
|
|
777
|
+
return (
|
|
778
|
+
<div className="relation-color-container">
|
|
779
|
+
<div
|
|
780
|
+
className="relation-color-preview"
|
|
781
|
+
style={{ backgroundColor: value }}
|
|
782
|
+
title={`Color: ${value}`}
|
|
783
|
+
></div>
|
|
784
|
+
<span className="relation-color-text">{value}</span>
|
|
785
|
+
</div>
|
|
786
|
+
);
|
|
787
|
+
}
|
|
788
|
+
|
|
770
789
|
return (
|
|
771
790
|
<CellWithTooltip
|
|
772
791
|
value={value}
|