diff --git a/mockup.html b/mockup.html index 53a80f7..35eab86 100644 --- a/mockup.html +++ b/mockup.html @@ -185,9 +185,129 @@ body{font-family:var(--font-ui);background:var(--surf);color:var(--ink);line-hei +/* ── GANTT ── */ +.gantt-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;margin-top:4px;padding-bottom:16px} +.gantt{min-width:580px;font-family:var(--font-ui)} +.gantt-hdr{display:grid;grid-template-columns:170px 1fr;align-items:end;margin-bottom:6px;padding-bottom:8px;border-bottom:1.5px solid var(--line)} +.gantt-hdr-lbl{font-size:11px;font-weight:700;color:var(--mut);text-transform:uppercase;letter-spacing:.5px} +.gantt-dates{position:relative;height:22px} +.gantt-tick{position:absolute;transform:translateX(-50%);font-size:10px;color:var(--mut);font-weight:500;white-space:nowrap} +.gantt-today-hdr{position:absolute;transform:translateX(-50%);font-size:10px;font-weight:800;color:var(--bg);white-space:nowrap;top:-2px} +.gantt-row{display:grid;grid-template-columns:170px 1fr;align-items:center;margin-bottom:8px;cursor:pointer} +.gantt-row:hover .gantt-lbl{color:var(--bg)} +.gantt-lbl{padding-right:12px;overflow:hidden} +.gantt-lbl-name{font-size:13px;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.gantt-lbl-sub{font-size:11px;color:var(--mut);white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.gantt-track{position:relative;height:32px;background:var(--surf);border-radius:6px;overflow:visible} +.gantt-bar{position:absolute;height:100%;border-radius:6px;display:flex;align-items:center;padding:0 10px;font-size:11px;font-weight:700;color:#fff;white-space:nowrap;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,.18);transition:filter .15s} +.gantt-bar:hover{filter:brightness(1.1)} +.gantt-bar.g-overdue{background:linear-gradient(90deg,#7f1d1d,#dc2626)} +.gantt-bar.g-urgent{background:linear-gradient(90deg,#9f1239,#e11d48)} +.gantt-bar.g-warn{background:linear-gradient(90deg,#92400e,#d97706)} +.gantt-bar.g-ok{background:linear-gradient(90deg,#1e3a8a,#3b82f6)} +.gantt-bar.g-done{background:linear-gradient(90deg,#14532d,#16a34a);opacity:.7} +.gantt-today-line{position:absolute;top:-4px;bottom:-4px;width:2px;background:var(--bg);border-radius:1px;z-index:10;pointer-events:none} +.gantt-today-line::after{content:'';position:absolute;top:0;left:50%;transform:translateX(-50%);width:8px;height:8px;background:var(--bg);border-radius:50%;margin-top:-3px} +.gantt-legend{display:flex;gap:14px;flex-wrap:wrap;margin-top:16px;padding-top:12px;border-top:1px solid var(--line)} +.gantt-leg-item{display:flex;align-items:center;gap:5px;font-size:11px;color:var(--mut)} +.gantt-leg-dot{width:10px;height:10px;border-radius:3px;flex-shrink:0} + +/* ── GANTT ── */ +(function() { + // Дата-хелперы + function d(str) { return new Date(str + 'T00:00:00'); } + function addDays(dt, n) { var r = new Date(dt); r.setDate(r.getDate()+n); return r; } + function daysBetween(a, b) { return Math.round((b-a)/86400000); } + function fmt(dt) { + var m = ['янв','фев','мар','апр','май','июн','июл','авг','сен','окт','ноя','дек']; + return dt.getDate() + ' ' + m[dt.getMonth()]; + } + + var CASES = [ + { ico:'🍽️', name:'Кухня — агентский', sub:'Протокол разногласий · ст.22 ЗоЗПП', start:'2025-05-19', end:'2025-05-26', cls:'g-urgent', go:"tab('case')" }, + { ico:'💼', name:'Трудовой договор', sub:'Допсоглашение (испыт. срок)', start:'2025-05-21', end:'2025-05-30', cls:'g-warn', go:"tab('case')" }, + { ico:'🏠', name:'ДДУ (новая редакция)',sub:'Сверка версий с застройщиком', start:'2025-05-19', end:'2025-06-05', cls:'g-ok', go:"tab('case')" }, + { ico:'📄', name:'Квартира · ДДУ', sub:'Передача квартиры · 214-ФЗ', start:'2025-05-27', end:'2025-06-22', cls:'g-ok', go:"toast('📅 Открываю дело')" }, + ]; + + function render() { + var root = document.getElementById('gantt-root'); + if (!root) return; + + var today = new Date(); today.setHours(0,0,0,0); + var rangeStart = d('2025-05-17'); + var rangeEnd = d('2025-06-25'); + var totalDays = daysBetween(rangeStart, rangeEnd); + + function pct(dt) { return Math.max(0, Math.min(100, daysBetween(rangeStart, dt) / totalDays * 100)); } + + // Шапка с датами + var tickDates = []; + var cur = new Date(rangeStart); + while (cur <= rangeEnd) { + tickDates.push(new Date(cur)); + cur.setDate(cur.getDate() + 7); + } + + var ticksHTML = tickDates.map(function(dt) { + var p = pct(dt); + return '' + fmt(dt) + ''; + }).join(''); + + var todayPct = pct(today); + var todayHdrHTML = 'сегодня ▼'; + + var hdrHTML = '

