Files
smoothschedule/frontend/src/puck/components/layout/Columns.tsx
poduck 29bcb27e76 Add Puck site builder with preview and draft functionality
Frontend:
- Add comprehensive Puck component library (Layout, Content, Booking, Contact)
- Add Services component with usePublicServices hook integration
- Add 150+ icons to IconList component organized by category
- Add preview modal with viewport toggles (desktop/tablet/mobile)
- Add draft save/discard functionality with localStorage persistence
- Add draft status indicator in PageEditor toolbar
- Fix useSites hooks to use correct API URLs (/pages/{id}/)

Backend:
- Add SiteConfig model for theme, header, footer configuration
- Add Page SEO fields (meta_title, meta_description, og_image, etc.)
- Add puck_data validation for component structure
- Add create_missing_sites management command
- Fix PageViewSet to use EntitlementService for permissions
- Add comprehensive tests for site builder functionality

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 01:32:11 -05:00

98 lines
2.7 KiB
TypeScript

import React from 'react';
import type { ComponentConfig } from '@measured/puck';
import { DropZone } from '@measured/puck';
import type { ColumnsProps } from '../../types';
const COLUMN_CONFIGS = {
'2': { count: 2, classes: 'grid-cols-1 md:grid-cols-2' },
'3': { count: 3, classes: 'grid-cols-1 md:grid-cols-3' },
'4': { count: 4, classes: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4' },
'2-1': { count: 2, classes: 'grid-cols-1 md:grid-cols-3', colSpans: ['md:col-span-2', 'md:col-span-1'] },
'1-2': { count: 2, classes: 'grid-cols-1 md:grid-cols-3', colSpans: ['md:col-span-1', 'md:col-span-2'] },
};
const GAP_CLASSES = {
none: 'gap-0',
small: 'gap-4',
medium: 'gap-6',
large: 'gap-8',
};
const ALIGN_CLASSES = {
top: 'items-start',
center: 'items-center',
bottom: 'items-end',
stretch: 'items-stretch',
};
export const Columns: ComponentConfig<ColumnsProps> = {
label: 'Columns',
fields: {
columns: {
type: 'select',
options: [
{ label: '2 Columns', value: '2' },
{ label: '3 Columns', value: '3' },
{ label: '4 Columns', value: '4' },
{ label: '2:1 Ratio', value: '2-1' },
{ label: '1:2 Ratio', value: '1-2' },
],
},
gap: {
type: 'select',
options: [
{ label: 'None', value: 'none' },
{ label: 'Small', value: 'small' },
{ label: 'Medium', value: 'medium' },
{ label: 'Large', value: 'large' },
],
},
verticalAlign: {
type: 'select',
options: [
{ label: 'Top', value: 'top' },
{ label: 'Center', value: 'center' },
{ label: 'Bottom', value: 'bottom' },
{ label: 'Stretch', value: 'stretch' },
],
},
stackOnMobile: {
type: 'radio',
label: 'Stack on Mobile',
options: [
{ label: 'Yes', value: true },
{ label: 'No', value: false },
],
},
},
defaultProps: {
columns: '2',
gap: 'medium',
verticalAlign: 'top',
stackOnMobile: true,
},
render: ({ columns, gap, verticalAlign, stackOnMobile }) => {
const config = COLUMN_CONFIGS[columns] || COLUMN_CONFIGS['2'];
const gapClass = GAP_CLASSES[gap] || GAP_CLASSES.medium;
const alignClass = ALIGN_CLASSES[verticalAlign] || ALIGN_CLASSES.top;
// Generate column elements
const columnElements = Array.from({ length: config.count }).map((_, index) => {
const colSpan = config.colSpans?.[index] || '';
return (
<div key={index} className={colSpan}>
<DropZone zone={`column-${index}`} />
</div>
);
});
return (
<div className={`grid ${config.classes} ${gapClass} ${alignClass}`}>
{columnElements}
</div>
);
},
};
export default Columns;