@bailierich/booking-components 2.0.0

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.
Files changed (83) hide show
  1. package/README.md +319 -0
  2. package/TENANT_DATA_INTEGRATION.md +402 -0
  3. package/TENANT_SETUP.md +316 -0
  4. package/components/BookingFlow/BookingFlow.tsx +790 -0
  5. package/components/BookingFlow/index.ts +5 -0
  6. package/components/BookingFlow/steps/AddonsSelection.tsx +118 -0
  7. package/components/BookingFlow/steps/Confirmation.tsx +185 -0
  8. package/components/BookingFlow/steps/ContactForm.tsx +292 -0
  9. package/components/BookingFlow/steps/CycleAwareDateSelection.tsx +277 -0
  10. package/components/BookingFlow/steps/DateSelection.tsx +473 -0
  11. package/components/BookingFlow/steps/ServiceSelection.tsx +315 -0
  12. package/components/BookingFlow/steps/TimeSelection.tsx +230 -0
  13. package/components/BookingFlow/steps/index.ts +10 -0
  14. package/components/BottomSheet/index.tsx +120 -0
  15. package/components/Forms/FormBlock.tsx +283 -0
  16. package/components/Forms/FormField.tsx +385 -0
  17. package/components/Forms/FormRenderer.tsx +216 -0
  18. package/components/Forms/FormValidation.ts +122 -0
  19. package/components/Forms/index.ts +4 -0
  20. package/components/HoldTimer/HoldTimer.tsx +266 -0
  21. package/components/HoldTimer/index.ts +2 -0
  22. package/components/SectionRenderer.tsx +558 -0
  23. package/components/Sections/About.tsx +145 -0
  24. package/components/Sections/BeforeAfter.tsx +81 -0
  25. package/components/Sections/BookingSection.tsx +76 -0
  26. package/components/Sections/Contact.tsx +103 -0
  27. package/components/Sections/FAQSection.tsx +239 -0
  28. package/components/Sections/FeatureContent.tsx +113 -0
  29. package/components/Sections/FeaturedLink.tsx +103 -0
  30. package/components/Sections/FixedInfoCard.tsx +189 -0
  31. package/components/Sections/Gallery.tsx +83 -0
  32. package/components/Sections/Header.tsx +78 -0
  33. package/components/Sections/Hero.tsx +178 -0
  34. package/components/Sections/ImageSection.tsx +147 -0
  35. package/components/Sections/InstagramFeed.tsx +38 -0
  36. package/components/Sections/LinkList.tsx +76 -0
  37. package/components/Sections/LocationMap.tsx +202 -0
  38. package/components/Sections/Logo.tsx +61 -0
  39. package/components/Sections/MinimalFooter.tsx +78 -0
  40. package/components/Sections/MinimalHeader.tsx +81 -0
  41. package/components/Sections/MinimalNavigation.tsx +63 -0
  42. package/components/Sections/Navbar.tsx +258 -0
  43. package/components/Sections/PricingTable.tsx +106 -0
  44. package/components/Sections/ScrollingTextDivider.tsx +138 -0
  45. package/components/Sections/ScrollingTextDivider.tsx.bak +138 -0
  46. package/components/Sections/ServicesPreview.tsx +129 -0
  47. package/components/Sections/SocialBar.tsx +177 -0
  48. package/components/Sections/Team.tsx +80 -0
  49. package/components/Sections/Testimonials.tsx +92 -0
  50. package/components/Sections/TextSection.tsx +116 -0
  51. package/components/Sections/VideoSection.tsx +178 -0
  52. package/components/Sections/index.ts +57 -0
  53. package/components/index.ts +21 -0
  54. package/dist/index-DAai7Glf.d.mts +474 -0
  55. package/dist/index-DAai7Glf.d.ts +474 -0
  56. package/dist/index.d.mts +1075 -0
  57. package/dist/index.d.ts +1075 -0
  58. package/dist/index.js +22 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/index.mjs +22 -0
  61. package/dist/index.mjs.map +1 -0
  62. package/dist/styles/index.d.mts +1 -0
  63. package/dist/styles/index.d.ts +1 -0
  64. package/dist/styles/index.js +2 -0
  65. package/dist/styles/index.js.map +1 -0
  66. package/dist/styles/index.mjs +2 -0
  67. package/dist/styles/index.mjs.map +1 -0
  68. package/docs/API.md +849 -0
  69. package/docs/CALLBACKS.md +760 -0
  70. package/docs/COMPLETE_SESSION_SUMMARY.md +404 -0
  71. package/docs/DATA_SHAPES.md +684 -0
  72. package/docs/MIGRATION.md +662 -0
  73. package/docs/PAYMENT_INTEGRATION.md +766 -0
  74. package/docs/SESSION_SUMMARY.md +185 -0
  75. package/docs/STYLING.md +735 -0
  76. package/index.ts +4 -0
  77. package/lib/storage.ts +239 -0
  78. package/package.json +59 -0
  79. package/styles/animations.ts +210 -0
  80. package/styles/index.ts +1 -0
  81. package/tsconfig.json +32 -0
  82. package/tsup.config.ts +13 -0
  83. package/types/index.ts +369 -0
@@ -0,0 +1,81 @@
1
+ 'use client';
2
+
3
+ export interface Comparison {
4
+ id: string | number;
5
+ title?: string;
6
+ beforeImage?: string;
7
+ afterImage?: string;
8
+ }
9
+
10
+ export interface BeforeAfterProps {
11
+ title?: string;
12
+ comparisons?: Comparison[];
13
+ colors: {
14
+ primary: string;
15
+ text: string;
16
+ };
17
+ typography: {
18
+ headingFont?: string;
19
+ };
20
+ enableInlineEditing?: boolean;
21
+ sectionId?: string;
22
+ }
23
+
24
+ export function BeforeAfter({
25
+ title,
26
+ comparisons = [],
27
+ colors,
28
+ typography,
29
+ enableInlineEditing = false,
30
+ sectionId = ''
31
+ }: BeforeAfterProps) {
32
+ const mockComparisons = comparisons.length > 0 ? comparisons : [
33
+ { id: 1, title: 'Hair Transformation', beforeImage: '', afterImage: '' }
34
+ ];
35
+
36
+ return (
37
+ <section className="py-16 px-6">
38
+ <div className="max-w-7xl mx-auto">
39
+ <h2
40
+ className="text-3xl md:text-4xl font-bold text-center mb-12"
41
+ style={{ fontFamily: typography.headingFont, color: colors.text }}
42
+ {...(enableInlineEditing && {
43
+ 'data-editable': true,
44
+ 'data-section-id': sectionId,
45
+ 'data-field-path': 'settings.title'
46
+ })}
47
+ >
48
+ {title || 'Results'}
49
+ </h2>
50
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
51
+ {mockComparisons.map((comparison, index) => (
52
+ <div key={comparison.id || index} className="space-y-4">
53
+ {comparison.title && (
54
+ <h3
55
+ className="text-xl font-semibold text-center"
56
+ style={{ color: colors.text }}
57
+ >
58
+ {comparison.title}
59
+ </h3>
60
+ )}
61
+ <div className="relative aspect-[4/3] rounded-lg overflow-hidden">
62
+ <div
63
+ className="absolute inset-0"
64
+ style={{ backgroundColor: colors.primary, opacity: 0.1 }}
65
+ />
66
+ <div className="absolute top-4 left-4 bg-black/50 text-white px-3 py-1 rounded text-sm">
67
+ Before
68
+ </div>
69
+ <div className="absolute top-4 right-4 bg-black/50 text-white px-3 py-1 rounded text-sm">
70
+ After
71
+ </div>
72
+ </div>
73
+ </div>
74
+ ))}
75
+ </div>
76
+ </div>
77
+ </section>
78
+ );
79
+ }
80
+
81
+ export default BeforeAfter;
@@ -0,0 +1,76 @@
1
+ 'use client';
2
+
3
+ export interface BookingConfig {
4
+ theme?: {
5
+ colors?: {
6
+ primary: string;
7
+ secondary: string;
8
+ text: string;
9
+ bookingText?: string;
10
+ };
11
+ typography?: {
12
+ headingFont?: string;
13
+ bodyFont?: string;
14
+ bodySize?: string;
15
+ };
16
+ };
17
+ bookingSettings?: any; // Full booking flow configuration
18
+ }
19
+
20
+ export interface BookingSectionProps {
21
+ config: BookingConfig;
22
+ colors: {
23
+ primary: string;
24
+ secondary: string;
25
+ text: string;
26
+ bookingText?: string;
27
+ };
28
+ // Optional: If you want to pass a custom BookingFlow component
29
+ BookingFlowComponent?: React.ComponentType<any>;
30
+ enableInlineEditing?: boolean;
31
+ sectionId?: string;
32
+ }
33
+
34
+ export function BookingSection({
35
+ config,
36
+ colors,
37
+ BookingFlowComponent,
38
+ enableInlineEditing = false,
39
+ sectionId = ''
40
+ }: BookingSectionProps) {
41
+ // This is a wrapper component that can render any booking flow
42
+ // If a custom component is provided, use it; otherwise show a placeholder
43
+
44
+ if (BookingFlowComponent) {
45
+ return (
46
+ <div className="container mx-auto px-4 py-16">
47
+ <BookingFlowComponent config={config} colors={colors} />
48
+ </div>
49
+ );
50
+ }
51
+
52
+ // Placeholder when no BookingFlow component is provided
53
+ return (
54
+ <div className="container mx-auto px-4 py-16">
55
+ <div className="max-w-4xl mx-auto text-center">
56
+ <div
57
+ className="p-12 rounded-2xl border-2 border-dashed"
58
+ style={{ borderColor: colors.primary, opacity: 0.5 }}
59
+ >
60
+ <div className="text-6xl mb-4">📅</div>
61
+ <h3
62
+ className="text-2xl font-bold mb-3"
63
+ style={{ color: colors.text }}
64
+ >
65
+ Booking Flow
66
+ </h3>
67
+ <p className="text-base opacity-70" style={{ color: colors.text }}>
68
+ Pass a BookingFlowComponent prop to render your custom booking flow here.
69
+ </p>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ );
74
+ }
75
+
76
+ export default BookingSection;
@@ -0,0 +1,103 @@
1
+ 'use client';
2
+
3
+ export interface ContactProps {
4
+ title?: string;
5
+ email?: string;
6
+ phone?: string;
7
+ address?: string;
8
+ showForm?: boolean;
9
+ colors: {
10
+ primary: string;
11
+ text: string;
12
+ };
13
+ typography: {
14
+ headingFont?: string;
15
+ };
16
+ enableInlineEditing?: boolean;
17
+ sectionId?: string;
18
+ }
19
+
20
+ export function Contact({
21
+ title,
22
+ email = 'contact@example.com',
23
+ phone = '(555) 123-4567',
24
+ address = '123 Main Street, City, State 12345',
25
+ showForm = true,
26
+ colors,
27
+ typography,
28
+ enableInlineEditing = false,
29
+ sectionId = ''
30
+ }: ContactProps) {
31
+ return (
32
+ <section className="py-16 px-6">
33
+ <div className="max-w-7xl mx-auto">
34
+ <h2
35
+ className="text-3xl md:text-4xl font-bold text-center mb-12"
36
+ style={{ fontFamily: typography.headingFont, color: colors.text }}
37
+ {...(enableInlineEditing && {
38
+ 'data-editable': true,
39
+ 'data-section-id': sectionId,
40
+ 'data-field-path': 'settings.title'
41
+ })}
42
+ >
43
+ {title || 'Get In Touch'}
44
+ </h2>
45
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
46
+ <div className="space-y-6">
47
+ <div className="flex items-center gap-4">
48
+ <svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" style={{ color: colors.primary }}>
49
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
50
+ </svg>
51
+ <span style={{ color: colors.text }}>{email}</span>
52
+ </div>
53
+ <div className="flex items-center gap-4">
54
+ <svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" style={{ color: colors.primary }}>
55
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
56
+ </svg>
57
+ <span style={{ color: colors.text }}>{phone}</span>
58
+ </div>
59
+ <div className="flex items-start gap-4">
60
+ <svg className="w-6 h-6 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" style={{ color: colors.primary }}>
61
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
62
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
63
+ </svg>
64
+ <span style={{ color: colors.text }}>{address}</span>
65
+ </div>
66
+ </div>
67
+
68
+ {showForm && (
69
+ <form className="space-y-4">
70
+ <input
71
+ type="text"
72
+ placeholder="Name"
73
+ className="w-full px-4 py-3 rounded-lg bg-white/10 backdrop-blur-sm border border-white/20"
74
+ style={{ color: colors.text }}
75
+ />
76
+ <input
77
+ type="email"
78
+ placeholder="Email"
79
+ className="w-full px-4 py-3 rounded-lg bg-white/10 backdrop-blur-sm border border-white/20"
80
+ style={{ color: colors.text }}
81
+ />
82
+ <textarea
83
+ placeholder="Message"
84
+ rows={4}
85
+ className="w-full px-4 py-3 rounded-lg bg-white/10 backdrop-blur-sm border border-white/20"
86
+ style={{ color: colors.text }}
87
+ />
88
+ <button
89
+ type="submit"
90
+ className="w-full px-6 py-3 rounded-lg font-semibold transition-all hover:scale-105"
91
+ style={{ backgroundColor: colors.primary, color: '#FFFFFF' }}
92
+ >
93
+ Send Message
94
+ </button>
95
+ </form>
96
+ )}
97
+ </div>
98
+ </div>
99
+ </section>
100
+ );
101
+ }
102
+
103
+ export default Contact;
@@ -0,0 +1,239 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+
5
+ export interface FAQItem {
6
+ question: string;
7
+ answer: string;
8
+ }
9
+
10
+ export interface FAQSectionProps {
11
+ title?: string;
12
+ faqs: FAQItem[];
13
+ layout?: 'accordion' | 'stacked';
14
+ maxWidth?: 'full' | 'large' | 'medium' | 'small';
15
+ colors: {
16
+ primary: string;
17
+ text: string;
18
+ };
19
+ typography?: {
20
+ headingFont?: string;
21
+ bodyFont?: string;
22
+ };
23
+ enableInlineEditing?: boolean;
24
+ sectionId?: string;
25
+ }
26
+
27
+ function FAQAccordionItem({
28
+ faq,
29
+ index,
30
+ colors,
31
+ typography,
32
+ enableInlineEditing,
33
+ sectionId
34
+ }: {
35
+ faq: FAQItem;
36
+ index: number;
37
+ colors: any;
38
+ typography: any;
39
+ enableInlineEditing: boolean;
40
+ sectionId: string;
41
+ }) {
42
+ const [isOpen, setIsOpen] = useState(false);
43
+
44
+ return (
45
+ <div
46
+ className="border-b border-opacity-10"
47
+ style={{ borderColor: colors.text }}
48
+ >
49
+ <button
50
+ onClick={() => setIsOpen(!isOpen)}
51
+ className="w-full py-4 px-6 flex items-center justify-between text-left hover:bg-opacity-5 transition-colors"
52
+ style={{
53
+ backgroundColor: isOpen ? `${colors.primary}10` : 'transparent'
54
+ }}
55
+ >
56
+ <span
57
+ className="font-semibold text-lg pr-4"
58
+ style={{
59
+ color: colors.text,
60
+ fontFamily: typography?.headingFont || 'inherit'
61
+ }}
62
+ {...(enableInlineEditing && {
63
+ 'data-editable': true,
64
+ 'data-section-id': sectionId,
65
+ 'data-field-path': `settings.faqs.${index}.question`
66
+ })}
67
+ >
68
+ {faq.question || 'Add question...'}
69
+ </span>
70
+ <svg
71
+ className="w-5 h-5 flex-shrink-0 transition-transform"
72
+ style={{
73
+ color: colors.primary,
74
+ transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)'
75
+ }}
76
+ fill="none"
77
+ stroke="currentColor"
78
+ viewBox="0 0 24 24"
79
+ >
80
+ <path
81
+ strokeLinecap="round"
82
+ strokeLinejoin="round"
83
+ strokeWidth={2}
84
+ d="M19 9l-7 7-7-7"
85
+ />
86
+ </svg>
87
+ </button>
88
+ {isOpen && (
89
+ <div
90
+ className="px-6 pb-4 pt-2"
91
+ style={{
92
+ color: colors.text,
93
+ opacity: 0.8,
94
+ fontFamily: typography?.bodyFont || 'inherit'
95
+ }}
96
+ {...(enableInlineEditing && {
97
+ 'data-editable': true,
98
+ 'data-section-id': sectionId,
99
+ 'data-field-path': `settings.faqs.${index}.answer`
100
+ })}
101
+ >
102
+ {faq.answer || 'Add answer...'}
103
+ </div>
104
+ )}
105
+ </div>
106
+ );
107
+ }
108
+
109
+ export function FAQSection({
110
+ title = 'Frequently Asked Questions',
111
+ faqs = [],
112
+ layout = 'accordion',
113
+ maxWidth = 'large',
114
+ colors,
115
+ typography,
116
+ enableInlineEditing = false,
117
+ sectionId = ''
118
+ }: FAQSectionProps) {
119
+ // Max width classes
120
+ const maxWidthMap = {
121
+ full: 'max-w-full',
122
+ large: 'max-w-4xl',
123
+ medium: 'max-w-3xl',
124
+ small: 'max-w-2xl'
125
+ };
126
+
127
+ return (
128
+ <div className="container mx-auto px-4">
129
+ <div className={`${maxWidthMap[maxWidth]} mx-auto`}>
130
+ {/* Title */}
131
+ {title && (
132
+ <h2
133
+ className="text-3xl font-bold text-center mb-8"
134
+ style={{
135
+ color: colors.text,
136
+ fontFamily: typography?.headingFont || 'inherit'
137
+ }}
138
+ {...(enableInlineEditing && {
139
+ 'data-editable': true,
140
+ 'data-section-id': sectionId,
141
+ 'data-field-path': 'settings.title'
142
+ })}
143
+ >
144
+ {title}
145
+ </h2>
146
+ )}
147
+
148
+ {/* FAQ Items */}
149
+ {faqs.length > 0 ? (
150
+ <div
151
+ className="rounded-lg overflow-hidden"
152
+ style={{
153
+ border: `1px solid ${colors.text}20`,
154
+ backgroundColor: `${colors.primary}05`
155
+ }}
156
+ >
157
+ {faqs.map((faq, index) => (
158
+ layout === 'accordion' ? (
159
+ <FAQAccordionItem
160
+ key={index}
161
+ faq={faq}
162
+ index={index}
163
+ colors={colors}
164
+ typography={typography}
165
+ enableInlineEditing={enableInlineEditing}
166
+ sectionId={sectionId}
167
+ />
168
+ ) : (
169
+ // Stacked layout (always expanded)
170
+ <div
171
+ key={index}
172
+ className="border-b border-opacity-10 last:border-b-0 p-6"
173
+ style={{ borderColor: colors.text }}
174
+ >
175
+ <h3
176
+ className="font-semibold text-lg mb-2"
177
+ style={{
178
+ color: colors.text,
179
+ fontFamily: typography?.headingFont || 'inherit'
180
+ }}
181
+ {...(enableInlineEditing && {
182
+ 'data-editable': true,
183
+ 'data-section-id': sectionId,
184
+ 'data-field-path': `settings.faqs.${index}.question`
185
+ })}
186
+ >
187
+ {faq.question || 'Add question...'}
188
+ </h3>
189
+ <p
190
+ style={{
191
+ color: colors.text,
192
+ opacity: 0.8,
193
+ fontFamily: typography?.bodyFont || 'inherit'
194
+ }}
195
+ {...(enableInlineEditing && {
196
+ 'data-editable': true,
197
+ 'data-section-id': sectionId,
198
+ 'data-field-path': `settings.faqs.${index}.answer`
199
+ })}
200
+ >
201
+ {faq.answer || 'Add answer...'}
202
+ </p>
203
+ </div>
204
+ )
205
+ ))}
206
+ </div>
207
+ ) : (
208
+ // Empty state
209
+ <div
210
+ className="p-12 rounded-lg border-2 border-dashed text-center"
211
+ style={{
212
+ borderColor: `${colors.primary}30`,
213
+ backgroundColor: `${colors.primary}05`
214
+ }}
215
+ >
216
+ <svg
217
+ className="w-12 h-12 mx-auto mb-4"
218
+ fill="none"
219
+ stroke={colors.primary}
220
+ viewBox="0 0 24 24"
221
+ >
222
+ <path
223
+ strokeLinecap="round"
224
+ strokeLinejoin="round"
225
+ strokeWidth={2}
226
+ d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
227
+ />
228
+ </svg>
229
+ <p style={{ color: colors.text, opacity: 0.6 }}>
230
+ Add FAQ items to get started
231
+ </p>
232
+ </div>
233
+ )}
234
+ </div>
235
+ </div>
236
+ );
237
+ }
238
+
239
+ export default FAQSection;
@@ -0,0 +1,113 @@
1
+ 'use client';
2
+
3
+ export interface FeatureContentProps {
4
+ headline?: string;
5
+ description?: string;
6
+ image?: string;
7
+ buttonText?: string;
8
+ buttonUrl?: string;
9
+ layout?: 'image-left' | 'image-right' | 'image-top';
10
+ showButton?: boolean;
11
+ colors: {
12
+ primary: string;
13
+ text: string;
14
+ };
15
+ typography: {
16
+ headingFont?: string;
17
+ };
18
+ enableInlineEditing?: boolean;
19
+ sectionId?: string;
20
+ }
21
+
22
+ export function FeatureContent({
23
+ headline,
24
+ description,
25
+ image,
26
+ buttonText,
27
+ buttonUrl,
28
+ layout = 'image-right',
29
+ showButton = true,
30
+ colors,
31
+ typography,
32
+ enableInlineEditing = false,
33
+ sectionId = ''
34
+ }: FeatureContentProps) {
35
+ const imageSection = (
36
+ <div className="w-full h-full aspect-video rounded-lg overflow-hidden bg-gray-100">
37
+ {image ? (
38
+ <img
39
+ src={image}
40
+ alt={headline || 'Feature image'}
41
+ className="w-full h-full object-cover"
42
+ />
43
+ ) : (
44
+ <div
45
+ className="w-full h-full flex items-center justify-center"
46
+ style={{ backgroundColor: colors.primary, opacity: 0.2 }}
47
+ >
48
+ <span className="text-sm opacity-50">Add image</span>
49
+ </div>
50
+ )}
51
+ </div>
52
+ );
53
+
54
+ const contentSection = (
55
+ <div className="space-y-4">
56
+ <h2
57
+ className="text-3xl font-bold"
58
+ style={{
59
+ fontFamily: typography.headingFont,
60
+ color: colors.text
61
+ }}
62
+ {...(enableInlineEditing && {
63
+ 'data-editable': true,
64
+ 'data-section-id': sectionId,
65
+ 'data-field-path': 'settings.headline'
66
+ })}
67
+ >
68
+ {headline || 'Feature Headline'}
69
+ </h2>
70
+ <p
71
+ className="text-lg opacity-80"
72
+ style={{ color: colors.text }}
73
+ {...(enableInlineEditing && {
74
+ 'data-editable': true,
75
+ 'data-section-id': sectionId,
76
+ 'data-field-path': 'settings.description'
77
+ })}
78
+ >
79
+ {description || 'Feature description goes here...'}
80
+ </p>
81
+ {showButton && (
82
+ <a
83
+ href={buttonUrl || '#'}
84
+ className="inline-block px-6 py-3 rounded-lg font-semibold transition-all hover:scale-105"
85
+ style={{
86
+ backgroundColor: colors.primary,
87
+ color: '#FFFFFF'
88
+ }}
89
+ {...(enableInlineEditing && {
90
+ 'data-editable': true,
91
+ 'data-section-id': sectionId,
92
+ 'data-field-path': 'settings.buttonText'
93
+ })}
94
+ >
95
+ {buttonText || 'Learn More'}
96
+ </a>
97
+ )}
98
+ </div>
99
+ );
100
+
101
+ return (
102
+ <div className="container mx-auto px-4 py-16">
103
+ <div className={`grid md:grid-cols-2 gap-12 items-center ${layout === 'image-top' ? 'grid-cols-1' : ''}`}>
104
+ {layout === 'image-left' && imageSection}
105
+ {contentSection}
106
+ {layout === 'image-right' && imageSection}
107
+ {layout === 'image-top' && imageSection}
108
+ </div>
109
+ </div>
110
+ );
111
+ }
112
+
113
+ export default FeatureContent;