539 lines
35 KiB
HTML
539 lines
35 KiB
HTML
{{define "title"}}Цены - PriceForge{{end}}
|
||
|
||
{{define "content"}}
|
||
<div class="space-y-4">
|
||
<h1 class="text-2xl font-bold">Управление прайслистами</h1>
|
||
|
||
<div class="bg-white rounded-lg shadow p-4">
|
||
<div class="flex justify-between items-center border-b pb-4 mb-4">
|
||
<div class="flex gap-4">
|
||
<button onclick="loadTab('estimate')" id="btn-estimate" class="text-orange-600 font-medium">Estimate</button>
|
||
<button onclick="loadTab('warehouse')" id="btn-warehouse" class="text-gray-600">Склад</button>
|
||
<button onclick="loadTab('competitor')" id="btn-competitor" class="text-gray-600">Конкуренты</button>
|
||
<button onclick="loadTab('all-configs')" id="btn-all-configs" class="text-gray-600 hidden">Все конфигурации</button>
|
||
</div>
|
||
<button onclick="recalculateAll()" id="btn-recalc" class="px-3 py-1 bg-green-600 text-white text-sm rounded hover:bg-green-700">
|
||
Обновить цены
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Progress bar -->
|
||
<div id="progress-container" class="mb-4 p-4 bg-orange-50 rounded-lg border border-orange-200" style="display:none;">
|
||
<div class="flex justify-between text-sm text-gray-700 mb-2">
|
||
<span id="progress-text" class="font-medium">Обновление цен...</span>
|
||
<span id="progress-percent" class="font-bold">0%</span>
|
||
</div>
|
||
<div class="w-full bg-gray-200 rounded-full h-4">
|
||
<div id="progress-bar" class="bg-orange-600 h-4 rounded-full transition-all duration-300" style="width: 0%"></div>
|
||
</div>
|
||
<div class="text-sm text-gray-600 mt-2">
|
||
<span id="progress-stats"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Search and sort (for LOT/component-settings) -->
|
||
<div id="search-bar" class="mb-4 hidden">
|
||
<div class="flex gap-4 items-center">
|
||
<input type="text" id="search-input" placeholder="Поиск..."
|
||
class="flex-1 px-3 py-2 border rounded"
|
||
onkeyup="debounceSearch()">
|
||
<div class="flex items-center gap-2">
|
||
<span class="text-sm text-gray-500">Сортировка:</span>
|
||
<select id="sort-field" class="px-2 py-1 border rounded text-sm" onchange="changeSort()">
|
||
<option value="lot_name">Артикул</option>
|
||
<option value="category">Категория</option>
|
||
<option value="popularity_score">Популярность</option>
|
||
<option value="estimate_count">Котировок</option>
|
||
<option value="stock_qty">На складе</option>
|
||
</select>
|
||
<button onclick="toggleSortDir()" id="sort-dir-btn" class="px-2 py-1 border rounded text-sm">↑</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="tab-content">
|
||
<div class="text-center py-8 text-gray-500">Загрузка...</div>
|
||
</div>
|
||
|
||
<!-- Pricelists Tab Content (hidden by default) -->
|
||
<div id="pricelists-tab-content" class="hidden">
|
||
|
||
<!-- ESTIMATE: Inline component settings (shown when estimate tab active) -->
|
||
<div id="comp-inline-wrap" class="hidden mb-4">
|
||
<h3 class="text-base font-semibold text-gray-700 mb-3">Настройка компонентов</h3>
|
||
<div class="flex gap-3 items-center mb-3 flex-wrap">
|
||
<input type="text" id="comp-inline-search" placeholder="Поиск по артикулу..."
|
||
class="flex-1 min-w-[200px] px-3 py-2 border rounded text-sm"
|
||
oninput="debounceCompInlineSearch()">
|
||
<div class="flex items-center gap-2">
|
||
<span class="text-sm text-gray-500">Сортировка:</span>
|
||
<select id="comp-inline-sort" class="px-2 py-1 border rounded text-sm" onchange="changeCompInlineSort()">
|
||
<option value="popularity_score">Популярность</option>
|
||
<option value="lot_name">Артикул</option>
|
||
<option value="quote_count">Котировок</option>
|
||
<option value="current_price">Цена</option>
|
||
</select>
|
||
<button onclick="toggleCompInlineSortDir()" id="comp-inline-sort-dir" class="px-2 py-1 border rounded text-sm">↓</button>
|
||
</div>
|
||
</div>
|
||
<div id="comp-inline-content">
|
||
<div class="text-center py-4 text-gray-500 text-sm">Загрузка...</div>
|
||
</div>
|
||
<div id="comp-inline-pagination" class="flex justify-between items-center mt-3 pt-3 border-t hidden">
|
||
<span id="comp-inline-page-info" class="text-sm text-gray-600"></span>
|
||
<div class="flex gap-2">
|
||
<button onclick="compInlinePrevPage()" id="comp-inline-btn-prev" class="px-3 py-1 border rounded text-sm disabled:opacity-50">Назад</button>
|
||
<button onclick="compInlineNextPage()" id="comp-inline-btn-next" class="px-3 py-1 border rounded text-sm disabled:opacity-50">Вперед</button>
|
||
</div>
|
||
</div>
|
||
<div class="border-t-2 border-orange-200 mt-5 mb-1"></div>
|
||
</div>
|
||
|
||
<!-- WAREHOUSE: Stock import section (above pricelists) -->
|
||
<div id="stock-import-wrap" class="hidden border rounded-lg p-4 mb-4">
|
||
<h3 class="text-lg font-semibold mb-3">Импорт stock_log</h3>
|
||
<p class="text-xs text-gray-500 mb-3">В stock_log сохраняются все неотфильтрованные строки. Поле <span class="font-mono">partnumber</span> берется из файла; сопоставление с LOT используется только при расчете warehouse-прайслиста.</p>
|
||
<div class="flex items-center gap-3">
|
||
<input type="file" id="stock-file-input" accept=".mxl,.xlsx" class="block w-full text-sm text-gray-700">
|
||
<button onclick="importStockFile()" class="px-4 py-2 bg-emerald-600 text-white rounded hover:bg-emerald-700">Импортировать</button>
|
||
</div>
|
||
<div class="mt-3">
|
||
<label class="flex items-center gap-2 text-sm text-gray-700 cursor-pointer">
|
||
<input type="checkbox" id="stock-create-pricelist" class="w-4 h-4 text-emerald-600 border-gray-300 rounded focus:ring-emerald-500" checked>
|
||
<span>Создать новый прайслист Warehouse</span>
|
||
</label>
|
||
</div>
|
||
<div id="stock-import-progress" class="hidden mt-4 p-3 bg-orange-50 border border-orange-200 rounded">
|
||
<div class="flex justify-between text-sm mb-1">
|
||
<span id="stock-import-status">Запуск...</span>
|
||
<span id="stock-import-percent">0%</span>
|
||
</div>
|
||
<div class="w-full bg-orange-100 rounded-full h-3">
|
||
<div id="stock-import-bar" class="h-3 bg-orange-600 rounded-full transition-all duration-300" style="width:0%"></div>
|
||
</div>
|
||
<div id="stock-import-stats" class="text-xs text-gray-600 mt-2"></div>
|
||
</div>
|
||
<div id="stock-import-suggestions" class="hidden mt-4 p-3 bg-amber-50 border border-amber-200 rounded">
|
||
<div class="flex items-center justify-between mb-2">
|
||
<div class="text-sm font-medium text-amber-900">Новые партномера без сопоставления</div>
|
||
<div class="flex items-center gap-2">
|
||
<button id="btn-add-all-suggestions" onclick="addAllSuggestions()" class="inline-flex items-center gap-1 px-2 py-1 text-xs rounded border border-orange-200 text-orange-700 hover:bg-orange-50" title="Добавить все">
|
||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>
|
||
<span>Добавить все</span>
|
||
</button>
|
||
<button id="btn-ignore-all-suggestions" onclick="ignoreAllSuggestions()" class="inline-flex items-center gap-1 px-2 py-1 text-xs rounded border border-amber-200 text-amber-700 hover:bg-amber-100" title="Игнорировать все">
|
||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 5.636l-12.728 12.728M5.636 5.636l12.728 12.728"></path></svg>
|
||
<span>Игнорировать все</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="overflow-x-auto">
|
||
<table class="min-w-full divide-y divide-amber-200">
|
||
<thead class="bg-amber-100">
|
||
<tr>
|
||
<th class="px-3 py-2 w-1/3 text-left text-xs font-medium text-amber-800 uppercase">LOT</th>
|
||
<th class="px-3 py-2 w-1/3 text-left text-xs font-medium text-amber-800 uppercase">Partnumber</th>
|
||
<th class="px-3 py-2 text-left text-xs font-medium text-amber-800 uppercase">Описание</th>
|
||
<th class="px-3 py-2 text-right text-xs font-medium text-amber-800 uppercase">Действия</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="stock-import-suggestions-body" class="bg-white divide-y divide-amber-100"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<datalist id="stock-lot-options"></datalist>
|
||
</div>
|
||
|
||
<!-- COMPETITOR: B2B import section (above pricelists) -->
|
||
<div id="comp-b2b-wrap" class="hidden border rounded-lg p-4 mb-4">
|
||
<h3 class="text-lg font-semibold mb-3">Импорт выгрузки B2B</h3>
|
||
<div class="grid grid-cols-2 gap-3 mb-3">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Конкурент <span class="text-red-500">*</span></label>
|
||
<select id="b2b-competitor" class="w-full px-3 py-2 border rounded text-sm focus:outline-none focus:ring-2 focus:ring-orange-500" onchange="onB2BCompetitorChange()">
|
||
<option value="">Выберите конкурента...</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Дата котировки</label>
|
||
<input id="b2b-date" type="date" class="w-full px-3 py-2 border rounded text-sm focus:outline-none focus:ring-2 focus:ring-orange-500">
|
||
</div>
|
||
<div id="b2b-rate-row" class="hidden">
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Курс к USD</label>
|
||
<input id="b2b-rate" type="number" step="0.0001" min="0.0001" placeholder="89.50" class="w-full px-3 py-2 border rounded text-sm focus:outline-none focus:ring-2 focus:ring-orange-500">
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Файл Excel (.xlsx) <span class="text-red-500">*</span></label>
|
||
<input id="b2b-file" type="file" accept=".xlsx,.xls" class="w-full px-3 py-2 border rounded text-sm">
|
||
</div>
|
||
</div>
|
||
<button onclick="submitB2BImport()" id="b2b-submit-btn" class="px-4 py-2 bg-orange-600 text-white rounded hover:bg-orange-700 text-sm">Импортировать</button>
|
||
<div id="b2b-import-progress" class="hidden mt-3 p-3 bg-orange-50 border border-orange-200 rounded">
|
||
<div class="flex justify-between text-sm mb-1">
|
||
<span id="b2b-import-status">Запуск...</span>
|
||
<span id="b2b-import-percent">0%</span>
|
||
</div>
|
||
<div class="w-full bg-orange-100 rounded-full h-3">
|
||
<div id="b2b-import-bar" class="h-3 bg-orange-600 rounded-full transition-all duration-300" style="width:0%"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Pricelist section header -->
|
||
<div class="flex justify-between items-center mb-4">
|
||
<div class="flex items-center gap-3">
|
||
<h2 id="pricelists-title" class="text-xl font-semibold">Estimate</h2>
|
||
</div>
|
||
<div class="flex items-center gap-3">
|
||
<div id="pricelists-create-btn-container"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="bg-white rounded-lg shadow overflow-hidden">
|
||
<table class="min-w-full divide-y divide-gray-200">
|
||
<thead class="bg-gray-50">
|
||
<tr>
|
||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Версия</th>
|
||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Тип прайслиста</th>
|
||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Дата</th>
|
||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Автор</th>
|
||
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase">Позиций</th>
|
||
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase">Исп.</th>
|
||
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase">Статус</th>
|
||
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase">Действия</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="pricelists-body" class="bg-white divide-y divide-gray-200">
|
||
<tr>
|
||
<td colspan="8" class="px-6 py-4 text-center text-gray-500">Загрузка...</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div id="pricelists-pagination" class="flex justify-center space-x-2 mt-4"></div>
|
||
</div>
|
||
|
||
<!-- Sync Status Tab Content (hidden by default) -->
|
||
<div id="sync-status-tab-content" class="hidden">
|
||
<div class="mb-4">
|
||
<h2 class="text-xl font-semibold">Статус синхронизации</h2>
|
||
</div>
|
||
|
||
<div class="bg-white rounded-lg shadow overflow-hidden">
|
||
<table class="min-w-full divide-y divide-gray-200">
|
||
<thead class="bg-gray-50">
|
||
<tr>
|
||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Пользователь</th>
|
||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Версия приложения</th>
|
||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Статус</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="sync-users-status-body" class="bg-white divide-y divide-gray-200">
|
||
<tr>
|
||
<td colspan="3" class="px-6 py-4 text-sm text-gray-500">Загрузка...</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Create Modal -->
|
||
<div id="pricelists-create-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
|
||
<div class="bg-white rounded-lg p-6 max-w-md w-full mx-4">
|
||
<h2 class="text-xl font-bold mb-4">Создать прайслист</h2>
|
||
<p class="text-sm text-gray-600 mb-4">
|
||
Будет создан снимок текущих цен из базы данных.<br>
|
||
Автор прайслиста: <span id="pricelists-db-username" class="font-medium">загрузка...</span>
|
||
</p>
|
||
<form id="pricelists-create-form" class="space-y-4">
|
||
<div id="pricelist-create-progress" class="hidden p-3 bg-orange-50 rounded-lg border border-orange-200">
|
||
<div class="flex justify-between items-center text-sm mb-2">
|
||
<span id="pricelist-create-progress-text" class="font-medium">Подготовка...</span>
|
||
<span id="pricelist-create-progress-percent" class="font-bold">0%</span>
|
||
</div>
|
||
<div class="w-full bg-orange-100 rounded-full h-3 overflow-hidden">
|
||
<div id="pricelist-create-progress-bar" class="bg-orange-600 h-3 rounded-full transition-all duration-300" style="width: 0%"></div>
|
||
</div>
|
||
<div id="pricelist-create-progress-stats" class="text-xs text-gray-600 mt-2"></div>
|
||
</div>
|
||
<div class="flex justify-end space-x-3">
|
||
<button type="button" onclick="closePricelistsCreateModal()"
|
||
class="px-4 py-2 text-gray-700 border border-gray-300 rounded-md hover:bg-gray-50">
|
||
Отмена
|
||
</button>
|
||
<button type="submit"
|
||
class="px-4 py-2 bg-orange-600 text-white rounded-md hover:bg-orange-700">
|
||
Создать
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="pricelist-price-changes-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
|
||
<div class="bg-white rounded-lg p-6 max-w-4xl w-full mx-4 max-h-[85vh] overflow-hidden flex flex-col">
|
||
<div class="flex items-center justify-between mb-4">
|
||
<h2 class="text-xl font-bold">Изменения цен</h2>
|
||
<button type="button" onclick="closePricelistPriceChangesModal()" class="text-gray-500 hover:text-gray-700">Закрыть</button>
|
||
</div>
|
||
<div id="pricelist-price-changes-summary" class="text-sm text-gray-600 mb-4"></div>
|
||
<div id="pricelist-price-changes-content" class="overflow-auto space-y-4"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Create LOT Modal -->
|
||
<div id="create-lot-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
|
||
<div class="bg-white rounded-lg p-6 max-w-md w-full mx-4">
|
||
<h2 class="text-xl font-bold mb-4">Создать новый LOT</h2>
|
||
<form id="create-lot-form" class="space-y-4">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">
|
||
LOT Name (артикул) <span class="text-red-500">*</span>
|
||
</label>
|
||
<input type="text" id="lot-name-input" name="lot_name" required
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-orange-500"
|
||
placeholder="Например: STM32F103C8T6">
|
||
<p class="text-xs text-gray-500 mt-1">Без пробелов, обычно это парт-номер компонента</p>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">
|
||
Описание
|
||
</label>
|
||
<input type="text" id="lot-description-input" name="lot_description"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-orange-500"
|
||
placeholder="Описание компонента">
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">
|
||
Категория
|
||
</label>
|
||
<input type="text" id="lot-category-input" name="category"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-orange-500"
|
||
placeholder="Будет определена автоматически, если не указано">
|
||
<p class="text-xs text-gray-500 mt-1">Например: STM32, ATMEGA, резисторы и т.д.</p>
|
||
</div>
|
||
<div id="create-lot-error" class="hidden p-3 bg-red-50 border border-red-200 rounded text-sm text-red-600"></div>
|
||
<div class="flex justify-end space-x-3">
|
||
<button type="button" onclick="closeCreateLotModal()"
|
||
class="px-4 py-2 text-gray-700 border border-gray-300 rounded-md hover:bg-gray-50">
|
||
Отмена
|
||
</button>
|
||
<button type="submit" id="create-lot-submit-btn"
|
||
class="px-4 py-2 bg-orange-600 text-white rounded-md hover:bg-orange-700">
|
||
Создать
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- LOT Mapping Modal -->
|
||
<div id="create-mapping-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
|
||
<div class="bg-white rounded-lg p-6 max-w-3xl w-full mx-4">
|
||
<div class="flex justify-between items-center mb-4">
|
||
<div>
|
||
<h2 id="mapping-modal-title" class="text-xl font-bold">LOT</h2>
|
||
<p id="mapping-modal-lot-name" class="text-sm text-gray-600 mt-1"></p>
|
||
</div>
|
||
<button onclick="closeCreateMappingModal()" class="text-gray-400 hover:text-gray-600">
|
||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="space-y-4">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Partnumber</label>
|
||
<input type="text" id="mapping-partnumber-input" autocomplete="off"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-orange-500 font-mono"
|
||
placeholder="Добавьте partnumber"
|
||
oninput="onMappingPartnumberChange()">
|
||
<div id="mapping-partnumber-suggestions" class="mt-1 border border-gray-200 rounded-md hidden bg-white"></div>
|
||
<div id="mapping-selected-partnumbers" class="mt-2 flex flex-wrap gap-2"></div>
|
||
<p id="mapping-partnumber-hint" class="text-xs text-gray-500 mt-1">Несопоставленных partnumbers</p>
|
||
<p id="mapping-partnumber-description" class="text-xs text-gray-600 mt-1"></p>
|
||
</div>
|
||
<p id="mapping-lot-description" class="text-xs text-gray-600"></p>
|
||
|
||
<div id="mapping-stats" class="hidden border rounded-lg p-4 bg-gray-50 space-y-3">
|
||
<h3 class="text-sm font-semibold text-gray-700">Статистика</h3>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<div class="text-xs font-medium text-gray-500 uppercase mb-2">Estimate</div>
|
||
<div class="space-y-1 text-sm">
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600">Цена:</span>
|
||
<span id="mapping-estimate-price" class="font-medium">—</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600">Котировок:</span>
|
||
<span id="mapping-estimate-count" class="font-medium">—</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<div class="text-xs font-medium text-gray-500 uppercase mb-2">Склад</div>
|
||
<div class="space-y-1 text-sm">
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600">Цена:</span>
|
||
<span id="mapping-warehouse-price" class="font-medium">—</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600">На складе:</span>
|
||
<span id="mapping-warehouse-qty" class="font-medium">—</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="pt-2 border-t space-y-2">
|
||
<div class="text-xs font-medium text-gray-500 uppercase">График цены</div>
|
||
<div id="mapping-chart-container" class="rounded border bg-white p-2">
|
||
<svg id="mapping-price-chart" class="w-full h-48" viewBox="0 0 700 240" preserveAspectRatio="none"></svg>
|
||
<p id="mapping-chart-empty" class="hidden text-xs text-gray-500 px-2 py-3">Недостаточно данных для графика</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="pt-2 border-t">
|
||
<div class="text-xs text-gray-500">
|
||
Несопоставленных partnumbers: <span id="mapping-unmapped-count" class="font-medium">—</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="create-mapping-error" class="hidden p-3 bg-red-50 border border-red-200 rounded text-sm text-red-600"></div>
|
||
|
||
<div class="flex justify-end space-x-3 pt-2">
|
||
<button type="button" onclick="closeCreateMappingModal()"
|
||
class="px-4 py-2 text-gray-700 border border-gray-300 rounded-md hover:bg-gray-50">
|
||
Отмена
|
||
</button>
|
||
<button type="button" onclick="submitCreateMapping()" id="create-mapping-submit-btn"
|
||
class="px-4 py-2 bg-orange-600 text-white rounded-md hover:bg-orange-700">
|
||
Сохранить сопоставления
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Pagination -->
|
||
<div id="pagination" class="flex justify-between items-center mt-4 pt-4 border-t hidden">
|
||
<span id="page-info" class="text-sm text-gray-600"></span>
|
||
<div class="flex gap-2">
|
||
<button onclick="prevPage()" id="btn-prev" class="px-3 py-1 border rounded text-sm disabled:opacity-50">Назад</button>
|
||
<button onclick="nextPage()" id="btn-next" class="px-3 py-1 border rounded text-sm disabled:opacity-50">Вперед</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Price Settings Modal -->
|
||
<div id="price-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
|
||
<div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4">
|
||
<div class="flex justify-between items-center p-4 border-b">
|
||
<h3 class="text-lg font-semibold">Настройка цены</h3>
|
||
<button onclick="closeModal()" class="text-gray-500 hover:text-gray-700">×</button>
|
||
</div>
|
||
<div class="p-4 space-y-4">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Артикул</label>
|
||
<input type="text" id="modal-lot-name" readonly class="w-full px-3 py-2 border rounded bg-gray-100">
|
||
</div>
|
||
|
||
<div class="flex items-center mb-2">
|
||
<input type="checkbox" id="modal-meta-enabled" class="mr-2" onchange="toggleMetaPrice()">
|
||
<span class="text-sm font-medium text-gray-700">Мета артикул</span>
|
||
</div>
|
||
<div id="meta-price-fields" class="hidden mt-2">
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Источники цен</label>
|
||
<input type="text" id="modal-meta-prices" class="w-full px-3 py-2 border rounded" placeholder="Артикулы через запятую (например: CPU_AMD_9654, MB_INTEL_4.Sapphire_2S)">
|
||
<p class="text-xs text-gray-500 mt-1">Артикулы, чьи цены будут использоваться в расчете. Для автоматического подбора используйте * в конце (например: CPU_AMD_9654*)</p>
|
||
</div>
|
||
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Метод расчёта</label>
|
||
<select id="modal-method" class="w-full px-3 py-2 border rounded" onchange="onMethodChange()">
|
||
<option value="median">Медиана</option>
|
||
<option value="average">Среднее</option>
|
||
<option value="manual">Установить цену вручную</option>
|
||
</select>
|
||
</div>
|
||
<div id="manual-price-field" class="hidden">
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Ручная цена (USD)</label>
|
||
<input type="number" id="modal-manual-price" step="0.01" class="w-full px-3 py-2 border rounded" placeholder="Цена USD">
|
||
<p class="text-xs text-gray-500 mt-1">Ручная цена сохраняется при пересчёте</p>
|
||
</div>
|
||
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Период расчёта</label>
|
||
<select id="modal-period" class="w-full px-3 py-2 border rounded">
|
||
<option value="7">1 неделя</option>
|
||
<option value="30">1 месяц</option>
|
||
<option value="90">1 квартал</option>
|
||
<option value="365">1 год</option>
|
||
<option value="0">Всё время</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Коэффициент корректировки (%)</label>
|
||
<input type="number" id="modal-coefficient" step="1" class="w-full px-3 py-2 border rounded" placeholder="0">
|
||
<p class="text-xs text-gray-500 mt-1">Например: 30 для +30%, -10 для -10%</p>
|
||
</div>
|
||
|
||
<div class="flex items-center pt-2 border-t">
|
||
<input type="checkbox" id="modal-hidden" class="mr-2">
|
||
<span class="text-sm font-medium text-gray-700">Скрыть из конфигуратора</span>
|
||
</div>
|
||
|
||
<div class="bg-gray-50 p-3 rounded space-y-2">
|
||
<div class="text-sm font-medium text-gray-700 mb-2">Расчёт цены</div>
|
||
<div class="grid grid-cols-2 gap-2 text-sm">
|
||
<div class="text-gray-600">Последняя цена:</div>
|
||
<div id="modal-last-price" class="font-medium text-right">—</div>
|
||
<div class="text-gray-600">Медиана (всё время):</div>
|
||
<div id="modal-median-all" class="font-medium text-right">—</div>
|
||
<div class="text-gray-600">Текущая цена:</div>
|
||
<div id="modal-current-price" class="font-medium text-right">—</div>
|
||
<div class="text-gray-600 font-medium text-orange-600">Новая цена:</div>
|
||
<div id="modal-new-price" class="font-bold text-right text-orange-600">—</div>
|
||
</div>
|
||
<div class="text-xs text-gray-500 pt-2 border-t">
|
||
Кол-во котировок: <span id="modal-quote-count">—</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="bg-gray-50 p-3 rounded space-y-2">
|
||
<div class="text-sm font-medium text-gray-700">График цены</div>
|
||
<div id="modal-price-chart" class="w-full">
|
||
<div class="h-44 flex items-center justify-center text-sm text-gray-400">Загрузка...</div>
|
||
</div>
|
||
<div class="flex items-center gap-4 text-xs text-gray-500">
|
||
<span class="inline-flex items-center gap-1">
|
||
<span class="w-3 h-0.5 bg-orange-500"></span>
|
||
Котировки
|
||
</span>
|
||
<span class="inline-flex items-center gap-1">
|
||
<span class="w-3 h-0.5 bg-slate-500"></span>
|
||
Текущая цена
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="flex justify-end gap-2 p-4 border-t">
|
||
<button onclick="closeModal()" class="px-4 py-2 border rounded hover:bg-gray-50">Отмена</button>
|
||
<button onclick="savePrice()" class="px-4 py-2 bg-orange-600 text-white rounded hover:bg-orange-700">Сохранить</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/static/js/admin_pricing.js"></script>
|
||
{{end}}
|
||
|
||
{{template "base" .}}
|