From 56584cc8ffdbac00f75ab9dfce88962e6e6646be Mon Sep 17 00:00:00 2001 From: Macbook Date: Thu, 11 Dec 2025 11:39:50 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=BE=D0=B8=D1=81=D0=BA=20=D0=B8=D0=BD?= =?UTF-8?q?=D1=81=D1=82=D1=80=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=20=D0=BF=D0=BE=D0=BF=D0=BE=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B8=20=D1=81=D0=BA=D0=BB=D0=B0=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__pycache__/toolkit.cpython-313.pyc | Bin 4492 -> 4413 bytes api/routers/toolkit.py | 5 +- api/static/js/index.js | 1033 +++++++++++------ .../__pycache__/actions.cpython-313.pyc | Bin 26353 -> 26312 bytes 4 files changed, 677 insertions(+), 361 deletions(-) diff --git a/api/routers/__pycache__/toolkit.cpython-313.pyc b/api/routers/__pycache__/toolkit.cpython-313.pyc index d8e472bd565d0fc1b9be796592ff6d5c558b2c9c..419d40a0700443f913b3bb92980a547bd59ec9d3 100644 GIT binary patch delta 758 zcmaiwO=uHA6vt;Kv)kPyyV1aOaFarN z3toyKUPcds2kEH@9wd0MAha}KPnue>A&4gr7B7K%EjSbG!BZd1Z~pJ`e{VjX=pVe& zp(I%Wjr_``)WloqH@skA0Cb>%=O6}qKn!)E42*+m$Xx$N%rfw=It|G%YLzo6fsQTH zm^`#MsxdB(WYCX}of;s^=w{nV(|#e(NuFY-MTg=y05Z0?Kri6qnn?yn;S|}x?Km#h zOF3>8fXT;Xjq8QX;^SxN4JS7U=7(T~T;@H}I{}Qt>l1bMkPP2RQ>K1*IVbdetopKtWglAW&HB0b~)6hKV? zE#CFMT}M~Js3@)bingn0uUvad=f2|KRs8FTJtb6#e8<++=%Z*^O_c8>%Zd52vhWq( z|5-c~f{>##8g(*ieb09Q)m`(EBdZ%u5|^#*w%hQ+t!8LA@K(!}08O?9XsCEwY-PFw z8clfnoRjH6)o|mI$WvW%sKyyw@`T4|^0D1dxuZhHraIkL!&`i6;}Gm6&!k(hn;b|1 zI8IK>SNR})oH;;dWS6&_$w7vT6b4;6k)i-LJ9h@ZgkxriypV%XC!b{B(0S$?VHjl? zV>nBZ<0v#1CYx8a&!^JqyX@a*v&&|fmPU&GjyJHK{cJrq2bc;m)H{UX2idu$baK?Z gMzAyD4#38W>iT1$`=JG(DOf{P*6xv{Q^&0BFJc&_KL7v# delta 736 zcmYk4Uq}=|9LHyNXV$&F+iP$AcZk?}XQXi+=ifSwv$F8k!~S^QgZfbEopO2I;@%1Q zQqoHxK@USCxBncUU%y0Pq71JOptV0&y-1M`!GEwf{sR$orw=CjePzLZrDNl!AG(gzD&88cI0(J!IEaxPx0JxT5||&}P{K3%PT{TC3M&sp zAeTOf&Bo^NGwG4^#I@-zK`TNvi+wQKj#m607PqR=Fe(ZHjf*Zd8bQUHZa)>PIKbEf8QX1!C7myHIWY&D z$pv`;hR9>N5k|?Xd_K}f&t`;hTE_ z*0H~AYkNCp2a{|EmU#N6k(|n;B4!_Xqv+lsEZbHq4tV p.toolbox_id === toolboxInfo.id) + .forEach(p => placementMap[p.toolkit_id] = p.placement); + + // lower -> category + const categoriesByLower = Object.fromEntries(categories.map(c => [c.title.toLowerCase().trim(), c])); + + const toolkitsMap = Object.fromEntries(toolkits.map(t => [t.id, t])); + + const toolkitsByCategory = {}; + toolkits.forEach(t => { + if (!toolkitsByCategory[t.category_id]) toolkitsByCategory[t.category_id] = []; + toolkitsByCategory[t.category_id].push(t); + }); + + // lower title -> toolkit within category + const toolkitsLowerByCategory = {}; + Object.entries(toolkitsByCategory).forEach(([catId, list]) => { + const map = {}; + list.forEach(t => map[t.title.toLowerCase().trim()] = t); + toolkitsLowerByCategory[catId] = map; + }); + + // ============================== + // Вспомогательные функции + // ============================== + const normalize = s => (s || '').toLowerCase().trim(); + + // exact match by lower title + const findCategoryByExact = title => { + if (!title) return null; + return categoriesByLower[title.toLowerCase().trim()] || null; + }; + + const findToolkitByExact = (categoryId, title) => { + if (!categoryId || !title) return null; + const map = toolkitsLowerByCategory[categoryId] || {}; + return map[title.toLowerCase().trim()] || null; + }; + + // format cost + const formatCost = value => { + if (typeof value !== 'number') value = parseFloat(value) || 0; + return formatPrice(value); + }; + + // debounce + function debounce(fn, delay = 200) { + let t; + return function (...args) { + clearTimeout(t); + t = setTimeout(() => fn.apply(this, args), delay); + }; } - // Создаем модальное окно + // Inject minimal styles for autocomplete dropdown (only once) + (function injectStyles() { + if (document.getElementById('fillToolboxAutocompleteStyles')) return; + const style = document.createElement('style'); + style.id = 'fillToolboxAutocompleteStyles'; + style.textContent = ` + .ft-autocomplete { position: relative; } + .ft-autocomplete-list { + position: absolute; + z-index: 2000; + left: 0; + right: 0; + max-height: 220px; + overflow: auto; + background: white; + border: 1px solid #dee2e6; + border-radius: 0.25rem; + box-shadow: 0 .25rem .5rem rgba(0,0,0,.08); + } + .ft-autocomplete-item { padding: .375rem .5rem; cursor: pointer; } + .ft-autocomplete-item:hover, .ft-autocomplete-item.active { background: #7abb92ff; } + .ft-autocomplete-empty { padding: .375rem .5rem; color: #6c757d; } + `; + document.head.appendChild(style); + })(); + + // create suggestions list element for an input (returns container and helper functions) + function createAutocomplete(forInput) { + // wrapper ft-autocomplete should exist around input + let wrapper = forInput.closest('.ft-autocomplete'); + if (!wrapper) { + wrapper = document.createElement('div'); + wrapper.className = 'ft-autocomplete'; + forInput.parentNode.insertBefore(wrapper, forInput); + wrapper.appendChild(forInput); + } + + let list = wrapper.querySelector('.ft-autocomplete-list'); + if (!list) { + list = document.createElement('div'); + list.className = 'ft-autocomplete-list'; + list.style.display = 'none'; + wrapper.appendChild(list); + } + + function show(items) { + list.innerHTML = ''; + if (!items || items.length === 0) { + const empty = document.createElement('div'); + empty.className = 'ft-autocomplete-empty'; + empty.textContent = 'Ничего не найдено'; + list.appendChild(empty); + list.style.display = 'block'; + return; + } + items.forEach(it => { + const div = document.createElement('div'); + div.className = 'ft-autocomplete-item'; + div.textContent = it.title; + div.dataset.valueId = it.id; + div.addEventListener('mousedown', (e) => { + // mousedown чтобы сработало до blur + e.preventDefault(); + if (typeof wrapper._onSelect === 'function') wrapper._onSelect(it); + hide(); + }); + list.appendChild(div); + }); + list.style.display = 'block'; + } + + function hide() { + list.innerHTML = ''; + list.style.display = 'none'; + } + + function onSelect(fn) { wrapper._onSelect = fn; } + + return { wrapper, list, show, hide, onSelect }; + } + + // ============================== + // Создание строки таблицы + // ============================== + function createRow(rowIndex = 0) { + const rowId = `row-${Date.now()}-${rowIndex}`; + return ` + + +
+ +
+ + + +
+ +
+ + + + + + + + + + + `; + } + + // ============================== + // Создаём модалку + // ============================== modal = document.createElement('div'); modal.className = 'modal fade'; modal.id = 'fillToolboxModal'; @@ -1073,11 +1247,8 @@ async function fillToolbox(toolboxInfo) {