soroban-abacus-flashcards/apps/web/src/app/create/calendar/components/CalendarConfigPanel.tsx

320 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import { css } from '../../../../../styled-system/css'
import { AbacusReact, useAbacusConfig } from '@soroban/abacus-react'
import { AbacusDisplayDropdown } from '@/components/AbacusDisplayDropdown'
interface CalendarConfigPanelProps {
month: number
year: number
format: 'monthly' | 'daily'
paperSize: 'us-letter' | 'a4' | 'a3' | 'tabloid'
isGenerating: boolean
onMonthChange: (month: number) => void
onYearChange: (year: number) => void
onFormatChange: (format: 'monthly' | 'daily') => void
onPaperSizeChange: (size: 'us-letter' | 'a4' | 'a3' | 'tabloid') => void
onGenerate: () => void
}
const MONTHS = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
]
export function CalendarConfigPanel({
month,
year,
format,
paperSize,
isGenerating,
onMonthChange,
onYearChange,
onFormatChange,
onPaperSizeChange,
onGenerate,
}: CalendarConfigPanelProps) {
const abacusConfig = useAbacusConfig()
return (
<div
data-component="calendar-config-panel"
className={css({
bg: 'gray.800',
borderRadius: '12px',
padding: '1.5rem',
display: 'flex',
flexDirection: 'column',
gap: '1.5rem',
})}
>
{/* Format Selection */}
<fieldset
data-section="format-selection"
className={css({
border: 'none',
padding: '0',
margin: '0',
})}
>
<legend
className={css({
fontSize: '1.125rem',
fontWeight: '600',
marginBottom: '0.75rem',
color: 'yellow.400',
})}
>
Calendar Format
</legend>
<div
className={css({
display: 'flex',
flexDirection: 'column',
gap: '0.5rem',
})}
>
<label
className={css({
display: 'flex',
alignItems: 'center',
gap: '0.5rem',
cursor: 'pointer',
padding: '0.5rem',
borderRadius: '6px',
_hover: { bg: 'gray.700' },
})}
>
<input
type="radio"
value="monthly"
checked={format === 'monthly'}
onChange={(e) => onFormatChange(e.target.value as 'monthly' | 'daily')}
className={css({
cursor: 'pointer',
})}
/>
<span>Monthly Calendar (one page per month)</span>
</label>
<label
className={css({
display: 'flex',
alignItems: 'center',
gap: '0.5rem',
cursor: 'pointer',
padding: '0.5rem',
borderRadius: '6px',
_hover: { bg: 'gray.700' },
})}
>
<input
type="radio"
value="daily"
checked={format === 'daily'}
onChange={(e) => onFormatChange(e.target.value as 'monthly' | 'daily')}
className={css({
cursor: 'pointer',
})}
/>
<span>Daily Calendar (one page per day)</span>
</label>
</div>
</fieldset>
{/* Date Selection */}
<fieldset
data-section="date-selection"
className={css({
border: 'none',
padding: '0',
margin: '0',
})}
>
<legend
className={css({
fontSize: '1.125rem',
fontWeight: '600',
marginBottom: '0.75rem',
color: 'yellow.400',
})}
>
Date
</legend>
<div
className={css({
display: 'flex',
gap: '0.5rem',
})}
>
<select
data-element="month-select"
value={month}
onChange={(e) => onMonthChange(Number(e.target.value))}
className={css({
flex: '1',
padding: '0.5rem',
borderRadius: '6px',
bg: 'gray.700',
color: 'white',
border: '1px solid',
borderColor: 'gray.600',
cursor: 'pointer',
_hover: { borderColor: 'gray.500' },
})}
>
{MONTHS.map((monthName, index) => (
<option key={monthName} value={index + 1}>
{monthName}
</option>
))}
</select>
<input
type="number"
data-element="year-input"
value={year}
onChange={(e) => onYearChange(Number(e.target.value))}
min={1}
max={9999}
className={css({
width: '100px',
padding: '0.5rem',
borderRadius: '6px',
bg: 'gray.700',
color: 'white',
border: '1px solid',
borderColor: 'gray.600',
_hover: { borderColor: 'gray.500' },
})}
/>
</div>
</fieldset>
{/* Paper Size */}
<fieldset
data-section="paper-size"
className={css({
border: 'none',
padding: '0',
margin: '0',
})}
>
<legend
className={css({
fontSize: '1.125rem',
fontWeight: '600',
marginBottom: '0.75rem',
color: 'yellow.400',
})}
>
Paper Size
</legend>
<select
data-element="paper-size-select"
value={paperSize}
onChange={(e) =>
onPaperSizeChange(e.target.value as 'us-letter' | 'a4' | 'a3' | 'tabloid')
}
className={css({
width: '100%',
padding: '0.5rem',
borderRadius: '6px',
bg: 'gray.700',
color: 'white',
border: '1px solid',
borderColor: 'gray.600',
cursor: 'pointer',
_hover: { borderColor: 'gray.500' },
})}
>
<option value="us-letter">US Letter (8.5" × 11")</option>
<option value="a4">A4 (210mm × 297mm)</option>
<option value="a3">A3 (297mm × 420mm)</option>
<option value="tabloid">Tabloid (11" × 17")</option>
</select>
</fieldset>
{/* Abacus Styling */}
<div
data-section="styling-info"
className={css({
padding: '1rem',
bg: 'gray.700',
borderRadius: '8px',
})}
>
<p
className={css({
fontSize: '0.875rem',
marginBottom: '0.75rem',
color: 'gray.300',
})}
>
Calendar abacus style preview:
</p>
<div
className={css({
display: 'flex',
justifyContent: 'center',
marginBottom: '0.75rem',
})}
>
<AbacusReact
value={12}
columns={2}
customStyles={abacusConfig.customStyles}
scaleFactor={0.5}
showNumbers={false}
/>
</div>
<div
className={css({
display: 'flex',
justifyContent: 'center',
})}
>
<AbacusDisplayDropdown />
</div>
</div>
{/* Generate Button */}
<button
type="button"
data-action="generate-calendar"
onClick={onGenerate}
disabled={isGenerating}
className={css({
padding: '1rem',
bg: 'yellow.500',
color: 'gray.900',
fontWeight: '600',
fontSize: '1.125rem',
borderRadius: '8px',
border: 'none',
cursor: 'pointer',
transition: 'all 0.2s',
_hover: {
bg: 'yellow.400',
},
_disabled: {
bg: 'gray.600',
color: 'gray.400',
cursor: 'not-allowed',
},
})}
>
{isGenerating ? 'Generating PDF...' : 'Generate PDF Calendar'}
</button>
</div>
)
}