From 2467cc5fce900064bf91ff9457db3bc9d012cf2d Mon Sep 17 00:00:00 2001 From: dv Date: Thu, 19 Mar 2026 13:00:20 +0300 Subject: [PATCH] fix vibe 3 1 --- docker-compose.yml | 2 +- public/css/app.css | 40 ++++++++++++++++++++++++-- public/js/ui.js | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 2ea2817..c38da8e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: ports: - "${APP_PORT:-3000}:3000" volumes: - - ./data:/data + - /opt/intra_monitor/data:/data environment: NODE_ENV: production PORT: 3000 diff --git a/public/css/app.css b/public/css/app.css index cdb461a..9ba5f0b 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -1368,9 +1368,36 @@ html.dark .stats-card { background:var(--surface); } padding:6px; } .adv-filter-panel.open{display:block;} +.adv-filter-actions{ + display:flex; + gap:6px; + padding:2px 2px 8px; + margin:0 0 4px; + border-bottom:1px solid var(--border); +} +.adv-filter-action-btn{ + flex:1; + border:1px solid var(--border); + background:var(--surface2); + color:var(--text2); + border-radius:6px; + font-family:inherit; + font-size:11px; + padding:4px 6px; + cursor:pointer; + transition:border-color .15s,color .15s,background .15s; +} +.adv-filter-action-btn:hover:not(:disabled){ + border-color:var(--accent); + color:var(--accent); +} +.adv-filter-action-btn:disabled{ + opacity:.5; + cursor:not-allowed; +} .adv-filter-item{ display:flex; - align-items:flex-start; + align-items:center; gap:8px; padding:6px 7px; border-radius:6px; @@ -1381,11 +1408,18 @@ html.dark .stats-card { background:var(--surface); } } .adv-filter-item:hover{background:var(--surface2);} .adv-filter-item input{ - margin-top:2px; + margin-top:0; accent-color:var(--accent); cursor:pointer; } -.adv-filter-item span{ +.adv-filter-dot{ + width:8px; + height:8px; + border-radius:50%; + flex-shrink:0; + box-shadow:0 0 0 1px rgba(0,0,0,.08); +} +.adv-filter-text{ flex:1; min-width:0; line-height:1.35; diff --git a/public/js/ui.js b/public/js/ui.js index 5fabfb7..37b9735 100644 --- a/public/js/ui.js +++ b/public/js/ui.js @@ -90,6 +90,44 @@ function updateAdvancedFilterLabel(selectId){ lbl.textContent=`${cfg.shortLabel}: ${selected.length}`; } +function getIntraStatusFilterColor(label){ + const lc=String(label||'').toLowerCase(); + if(lc.includes('открыт')||lc.includes('нов')||lc.includes('назнач')) return 'var(--blue)'; + if(lc.includes('работ')||lc.includes('ожид')||lc.includes('progress')) return 'var(--orange)'; + if(lc.includes('выполн')||lc.includes('закрыт')||lc.includes('реш')) return 'var(--green)'; + if(lc.includes('hold')||lc.includes('пауза')||lc.includes('приост')) return '#faad14'; + return 'var(--text3)'; +} + +function getAdvancedFilterOptionColor(selectId,opt){ + const value=String(opt?.value||''); + const label=String(opt?.textContent||''); + if(selectId==='filter-who-select'){ + if(value==='internal') return 'var(--green)'; + if(value==='external') return 'var(--red)'; + return 'var(--text3)'; + } + if(selectId==='filter-internal-select'){ + const m=MARKS_CONFIG.find((x)=>String(x.key)===value); + return m?.colorDot||m?.chipDotColor||m?.ispColor||'var(--text3)'; + } + if(selectId==='filter-intra-select') return getIntraStatusFilterColor(label); + return avatarColor(label||value); +} + +function setAdvancedFilterSelection(selectId,selected){ + const sel=document.getElementById(selectId); + if(!sel) return; + [...sel.options].forEach((opt)=>{ + if(opt.disabled) return; + if(String(opt.value).trim()==='') return; + opt.selected=!!selected; + }); + updateAdvancedFilterLabel(selectId); + buildAdvancedFilterPanel(selectId); + renderTable(); +} + function buildAdvancedFilterPanel(selectId){ const cfg=ADV_FILTER_CONFIG[selectId]; if(!cfg) return; @@ -106,6 +144,34 @@ function buildAdvancedFilterPanel(selectId){ return; } + const selectedCount=opts.filter((o)=>o.selected).length; + const actions=document.createElement('div'); + actions.className='adv-filter-actions'; + + const btnAll=document.createElement('button'); + btnAll.type='button'; + btnAll.className='adv-filter-action-btn'; + btnAll.textContent='Выбрать все'; + btnAll.disabled=selectedCount===opts.length; + btnAll.addEventListener('click',(e)=>{ + e.stopPropagation(); + setAdvancedFilterSelection(selectId,true); + }); + + const btnNone=document.createElement('button'); + btnNone.type='button'; + btnNone.className='adv-filter-action-btn'; + btnNone.textContent='Снять все'; + btnNone.disabled=selectedCount===0; + btnNone.addEventListener('click',(e)=>{ + e.stopPropagation(); + setAdvancedFilterSelection(selectId,false); + }); + + actions.appendChild(btnAll); + actions.appendChild(btnNone); + panel.appendChild(actions); + const frag=document.createDocumentFragment(); opts.forEach((opt)=>{ const item=document.createElement('label'); @@ -120,10 +186,16 @@ function buildAdvancedFilterPanel(selectId){ renderTable(); }); + const dot=document.createElement('span'); + dot.className='adv-filter-dot'; + dot.style.background=getAdvancedFilterOptionColor(selectId,opt); + const text=document.createElement('span'); + text.className='adv-filter-text'; text.textContent=opt.textContent; item.appendChild(cb); + item.appendChild(dot); item.appendChild(text); frag.appendChild(item); });