Files
smoothschedule/frontend/src/puck/components/contact/BusinessHours.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

103 lines
3.3 KiB
TypeScript

import React from 'react';
import type { ComponentConfig } from '@measured/puck';
import type { BusinessHoursProps } from '../../types';
import { Clock, CheckCircle, XCircle } from 'lucide-react';
const DEFAULT_HOURS = [
{ day: 'Monday', hours: '9:00 AM - 5:00 PM', isOpen: true },
{ day: 'Tuesday', hours: '9:00 AM - 5:00 PM', isOpen: true },
{ day: 'Wednesday', hours: '9:00 AM - 5:00 PM', isOpen: true },
{ day: 'Thursday', hours: '9:00 AM - 5:00 PM', isOpen: true },
{ day: 'Friday', hours: '9:00 AM - 5:00 PM', isOpen: true },
{ day: 'Saturday', hours: '10:00 AM - 2:00 PM', isOpen: true },
{ day: 'Sunday', hours: 'Closed', isOpen: false },
];
export const BusinessHours: ComponentConfig<BusinessHoursProps> = {
label: 'Business Hours',
fields: {
showCurrent: {
type: 'radio',
label: 'Highlight Current Day',
options: [
{ label: 'Yes', value: true },
{ label: 'No', value: false },
],
},
title: {
type: 'text',
label: 'Title',
},
},
defaultProps: {
showCurrent: true,
title: 'Business Hours',
},
render: ({ showCurrent, title }) => {
const today = new Date().toLocaleDateString('en-US', { weekday: 'long' });
return (
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6">
{title && (
<div className="flex items-center gap-3 mb-6">
<Clock className="w-6 h-6 text-indigo-600 dark:text-indigo-400" />
<h3 className="text-xl font-semibold text-gray-900 dark:text-white">
{title}
</h3>
</div>
)}
<div className="space-y-3">
{DEFAULT_HOURS.map(({ day, hours, isOpen }) => {
const isToday = showCurrent && day === today;
return (
<div
key={day}
className={`flex items-center justify-between py-2 px-3 rounded-lg ${
isToday
? 'bg-indigo-50 dark:bg-indigo-900/20'
: ''
}`}
>
<div className="flex items-center gap-2">
{isOpen ? (
<CheckCircle className="w-4 h-4 text-green-500" />
) : (
<XCircle className="w-4 h-4 text-red-400" />
)}
<span
className={`font-medium ${
isToday
? 'text-indigo-600 dark:text-indigo-400'
: 'text-gray-700 dark:text-gray-300'
}`}
>
{day}
{isToday && (
<span className="ml-2 text-xs bg-indigo-600 text-white px-2 py-0.5 rounded-full">
Today
</span>
)}
</span>
</div>
<span
className={`${
isOpen
? 'text-gray-600 dark:text-gray-400'
: 'text-red-500 dark:text-red-400'
}`}
>
{hours}
</span>
</div>
);
})}
</div>
</div>
);
},
};
export default BusinessHours;