/* eslint-disable */ /* timing-print.jsx — печатный A4-документ + библиотека + сводка. */ (function () { const R = window.React; const { useState } = R; const D = window.TimingData; /* ============ Библиотека блоков (drag-источник) ============ */ function Library({ dragInfo, onAdd, onCollapse }) { const groups = D.TYPE_ORDER.map(ty => ({ ty, label: D.TYPES[ty].label, color: D.TYPES[ty].color, blocks: D.LIBRARY.filter(b => b.y === ty), })); return (
Библиотека блоков
{onCollapse && ( )}
Перетащите в тайминг или нажмите, чтобы добавить в конец
{groups.map(g => (
{g.label}
{g.blocks.map((b, i) => (
{ dragInfo.current = { type: 'lib', block: b }; }} onDragEnd={() => { dragInfo.current = null; }} onClick={() => onAdd(b)} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 10px', background: 'var(--color-surface)', border: '1px solid var(--color-line-subtle)', borderRadius: 'var(--radius-card)', cursor: 'grab', userSelect: 'none', }}> {b.t} {b.d}м
))}
))}
); } /* ============ Сводка / инспектор ============ */ function Summary({ doc, phase, computed, span, onMeta, onPhaseStart, days }) { const m = doc.meta; const rows = computed.filter(c => c.kind === 'row'); const hasDays = days && days.length > 1; const byType = {}; rows.forEach(r => { byType[r.type] = (byType[r.type] || 0) + r.dur; }); const totalDur = rows.reduce((s, r) => s + r.dur, 0) || 1; const overlaps = rows.filter(r => r._overlap > 0); const phaseStart = phase === 'prep' ? doc.prepStart : doc.eventStart; const field = (label, key) => (
onMeta(key, e.target.value)} style={{ border: '1px solid var(--color-line-subtle)', background: 'var(--color-canvas)', borderRadius: 'var(--radius-input)', padding: '6px 8px', fontSize: 12.5, color: 'var(--color-ink-900)', outline: 'none', }} />
); return (
{/* Totals */} {hasDays ? (
Подготовка и монтаж
{days.length} {days.length < 5 ? 'дня' : 'дней'}
{D.dur(days.reduce((s, d) => s + d.dur, 0))} работы · {rows.length} блоков
) : (
{phase === 'prep' ? 'Подготовка и монтаж' : 'Мероприятие'}
{D.fmt(span.start)} {D.fmt(span.end)}
{D.dur(span.total)} · {rows.length} блоков
)} {/* Phase start */}
Старт {phase === 'prep' ? 'подготовки' : 'мероприятия'}
якорь для авторасчёта
onPhaseStart(D.parse(e.target.value))} style={{ width: 64, border: '1px solid var(--color-line-subtle)', background: 'var(--color-canvas)', borderRadius: 'var(--radius-input)', padding: '6px 8px', fontFamily: 'var(--font-mono)', fontSize: 15, fontWeight: 600, textAlign: 'center', color: 'var(--color-ink-900)', fontVariantNumeric: 'tabular-nums', outline: 'none', }} />
{hasDays && (
Дни подготовки
{days.map((d, i) => (
{i + 1}
{d.label}
{D.fmt(d.start)}–{D.fmt(d.end)}
{D.dur(d.dur)}
))}
)} {/* Type breakdown */}
Распределение времени
{D.TYPE_ORDER.filter(ty => byType[ty]).map(ty => (
{D.TYPES[ty].label} {D.dur(byType[ty])}
))} {!rows.length &&
Нет блоков
}
{/* Warnings */} {overlaps.length > 0 && (
Накладки по времени
{overlaps.map(o => (
«{o.title || 'Блок'}» начинается на {D.dur(o._overlap)} раньше окончания предыдущего
))}
)} {/* Event meta */}
Шапка документа
{field('Мероприятие', 'event')} {field('Дата', 'date')} {field('Площадка', 'venue')}
{field('Масштаб', 'guests')} {field('Код', 'code')}
{field('Менеджер (PM)', 'pm')}
); } /* ============ Сводка для сетки мероприятия ============ */ function GridSummary({ doc, grid, span, onMeta }) { const m = doc.meta; const field = (label, key) => (
onMeta(key, e.target.value)} style={{ border: '1px solid var(--color-line-subtle)', background: 'var(--color-canvas)', borderRadius: 'var(--radius-input)', padding: '6px 8px', fontSize: 12.5, color: 'var(--color-ink-900)', outline: 'none', }} />
); return (
Мероприятие
{D.fmt(span.start)} {D.fmt(span.end)}
{D.dur(span.total)} · {grid.slots.length} слотов · {grid.cards.length} карточек
Колонки (треки)
{grid.tracks.map(t => { const n = grid.cards.filter(c => c.trackId === t.id).length; return (
{t.name} {n}
); })} {!grid.tracks.length &&
Нет колонок
}
Как пользоваться
Карточки тащите между ячейками. Блоки из библиотеки можно бросать прямо в ячейку. «+» в ячейке — новая карточка.
Шапка документа
{field('Мероприятие', 'event')} {field('Дата', 'date')} {field('Площадка', 'venue')}
{field('Масштаб', 'guests')} {field('Код', 'code')}
{field('Менеджер (PM)', 'pm')}
); } /* ============ Печатный документ A4 ============ */ function PrintTiming({ title, range, items, phaseStart }) { const computed = D.computeTimes(items, phaseStart); let dayNum = 0; return (
{title} {range}
{computed.map(it => { if (it.kind === 'day') { dayNum++; return (
День {dayNum} {it.date && {it.date}} старт {D.fmt(it.start)}
); } if (it.kind === 'section') { return
{it.title}
; } const t = D.TYPES[it.type]; return (
{D.fmt(it._start)}–{D.fmt(it._end)} {D.dur(it.dur)}
{t.label} {it.title}
{it.who && {it.who}} {it.zone && {it.zone}} {it.note && {it.note}}
); })}
); } /* ============ Печатная сетка мероприятия ============ */ function PrintGrid({ title, range, grid }) { const slots = grid.slots.slice().sort((a, b) => a.time - b.time); const cols = '46px repeat(' + grid.tracks.length + ', 1fr)'; return (
{title} {range}
Время
{grid.tracks.map(t => (
{t.name}
))} {slots.map((slot, i) => { const next = slots[i + 1]; const delta = next ? next.time - slot.time : 0; return (
{D.fmt(slot.time)} {delta > 0 && +{D.dur(delta)}}
{grid.tracks.map(t => { const cards = grid.cards.filter(c => c.trackId === t.id && c.slotId === slot.id); return (
{cards.map(c => (
{c.title}
{(c.dur || c.who) &&
{[c.dur ? D.dur(c.dur) : null, c.who || null].filter(Boolean).join(' · ')}
}
))}
); })}
); })}
); } function PrintSheet({ tag, m, today, pageStyle, first, children }) { return (
EZ
EZ‑C
EZS Events
{tag}
{m.code || '—'} · напечатано {today}

{m.event || 'Мероприятие'}

{[m.date, m.venue, m.guests].filter(Boolean).join(' · ')} {m.pm && PM: {m.pm}}
{children}
); } function PrintDoc({ doc, pageStyle }) { const m = doc.meta; const pDays = D.dayBlocks(doc.prep, doc.prepStart); const hasDays = doc.prep.some(it => it.kind === 'day'); const prepSpan = D.phaseSpan(D.computeTimes(doc.prep, doc.prepStart), doc.prepStart); const evSpan = D.gridSpan(doc.eventGrid); const today = new Date().toLocaleDateString('ru-RU', { day: '2-digit', month: 'short', year: 'numeric' }); const prepRange = hasDays ? pDays.length + ' дн. · ' + D.dur(pDays.reduce((s, d) => s + d.dur, 0)) + ' работы' : D.fmt(prepSpan.start) + '–' + D.fmt(prepSpan.end) + ' · ' + D.dur(prepSpan.total); return (
Типы работ: {D.TYPE_ORDER.map(ty => ( {D.TYPES[ty].label} ))}
); } window.TimingPrint = { Library, Summary, GridSummary, PrintDoc }; })();