feat(web): add routine preview timeline in editor
- Visual horizontal bar chart showing step durations proportionally - Color-coded segments with golden-angle hue distribution - Time markers (start/end) and step legend - Updates live as steps are added, removed, or reordered
This commit is contained in:
parent
d3b55a2135
commit
c5c800077c
220
web/public/sw.js
220
web/public/sw.js
File diff suppressed because one or more lines are too long
@ -255,6 +255,86 @@ export function RoutineEditor({
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Timeline Preview */}
|
||||
{steps.length > 0 && totalMinutes > 0 && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--cm-text-secondary)' }}>
|
||||
Timeline Preview
|
||||
</label>
|
||||
<div
|
||||
className="rounded-xl border p-3"
|
||||
style={{ backgroundColor: 'var(--cm-surface-card)', borderColor: 'var(--cm-border)' }}
|
||||
>
|
||||
{/* Bar segments */}
|
||||
<div className="flex rounded-lg overflow-hidden h-8 mb-2">
|
||||
{steps.map((step, idx) => {
|
||||
const pct = totalMinutes > 0 ? (step.durationMinutes / totalMinutes) * 100 : 0;
|
||||
const hue = (idx * 137) % 360; // golden-angle distribution for distinct colors
|
||||
return (
|
||||
<div
|
||||
key={step.id}
|
||||
className="flex items-center justify-center text-[10px] font-medium truncate px-1"
|
||||
style={{
|
||||
width: `${Math.max(pct, 2)}%`,
|
||||
backgroundColor: `hsl(${hue}, 60%, 45%)`,
|
||||
color: '#fff',
|
||||
borderRight: idx < steps.length - 1 ? '1px solid var(--cm-bg-elevated)' : undefined,
|
||||
}}
|
||||
title={`${step.label || `Step ${idx + 1}`}: ${step.durationMinutes}m`}
|
||||
>
|
||||
{pct > 10 ? (step.label || `#${idx + 1}`) : ''}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{/* Time markers */}
|
||||
<div className="flex justify-between text-[10px]" style={{ color: 'var(--cm-text-tertiary)' }}>
|
||||
<span>0:00</span>
|
||||
{(() => {
|
||||
let cumulative = 0;
|
||||
return steps.slice(0, -1).map((step, idx) => {
|
||||
cumulative += step.durationMinutes;
|
||||
const pct = (cumulative / totalMinutes) * 100;
|
||||
const h = Math.floor(cumulative / 60);
|
||||
const m = cumulative % 60;
|
||||
return (
|
||||
<span
|
||||
key={step.id}
|
||||
className="absolute"
|
||||
style={{ left: `${pct}%`, transform: 'translateX(-50%)' }}
|
||||
>
|
||||
{h > 0 ? `${h}:${String(m).padStart(2, '0')}` : `${m}m`}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
})()}
|
||||
<span>
|
||||
{Math.floor(totalMinutes / 60) > 0
|
||||
? `${Math.floor(totalMinutes / 60)}:${String(totalMinutes % 60).padStart(2, '0')}`
|
||||
: `${totalMinutes}m`}
|
||||
</span>
|
||||
</div>
|
||||
{/* Step legend */}
|
||||
<div className="flex flex-wrap gap-x-3 gap-y-1 mt-2">
|
||||
{steps.map((step, idx) => {
|
||||
const hue = (idx * 137) % 360;
|
||||
return (
|
||||
<div key={step.id} className="flex items-center gap-1">
|
||||
<div
|
||||
className="w-2 h-2 rounded-sm"
|
||||
style={{ backgroundColor: `hsl(${hue}, 60%, 45%)` }}
|
||||
/>
|
||||
<span className="text-[10px]" style={{ color: 'var(--cm-text-tertiary)' }}>
|
||||
{step.label || `Step ${idx + 1}`} ({step.durationMinutes}m)
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Save as template toggle */}
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
|
||||
Loading…
Reference in New Issue
Block a user