i18n: перевод страницы dislocation (ru/uk/en)

Вкладки аптеки/офис, метрики, фильтры, таблицы, пагинация, карта и её
popup/JS-строки помечены {% trans %} (в т.ч. внутри <script>, с обходом
кавычек) и переведены на uk/en. 51 строка.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-25 16:54:20 +03:00
parent 87cb9de005
commit 92527cc52a
5 changed files with 514 additions and 128 deletions
+64 -62
View File
@@ -1,19 +1,21 @@
{% load common_tags %}
{% load i18n %}
{% trans "адрес не указан" as no_addr_short %}{% trans "Должность не указана" as no_post %}{% trans "Адрес неизвестен" as no_addr %}
<div class="page-content">
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:1.5rem;">
<div>
<h1 style="font-size:1.25rem; font-weight:500; color:var(--color-foreground);">Дислокація</h1>
<p style="font-size:0.875rem; color:var(--color-muted-foreground); margin-top:0.25rem;">{{ pharmacies|length }} аптек · {{ office_employees_count }} співробітників офісу/складу · персонал по кожній локації</p>
<h1 style="font-size:1.25rem; font-weight:500; color:var(--color-foreground);">{% trans "Дислокация" %}</h1>
<p style="font-size:0.875rem; color:var(--color-muted-foreground); margin-top:0.25rem;">{{ pharmacies|length }} {% trans "аптек" %} · {{ office_employees_count }} {% trans "сотрудников офиса/склада" %} · {% trans "персонал по каждой локации" %}</p>
</div>
</div>
<!-- Вкладки -->
<div style="display:flex; gap:0.25rem; background:var(--color-muted); border-radius:var(--radius); padding:0.25rem; width:fit-content; margin-bottom:1.5rem;">
<button type="button" class="dislo-tab-btn dislo-tab-active" data-tab="pharmacy" onclick="disloTabSwitch('pharmacy')"
style="padding:0.375rem 1rem; font-size:0.8125rem; font-weight:500; border:none; border-radius:calc(var(--radius) - 2px); cursor:pointer; background:var(--color-card); color:var(--color-card-foreground); box-shadow:0 1px 2px rgb(0 0 0/0.08);">Аптеки</button>
style="padding:0.375rem 1rem; font-size:0.8125rem; font-weight:500; border:none; border-radius:calc(var(--radius) - 2px); cursor:pointer; background:var(--color-card); color:var(--color-card-foreground); box-shadow:0 1px 2px rgb(0 0 0/0.08);">{% trans "Аптеки" %}</button>
<button type="button" class="dislo-tab-btn" data-tab="office" onclick="disloTabSwitch('office')"
style="padding:0.375rem 1rem; font-size:0.8125rem; font-weight:500; border:none; border-radius:calc(var(--radius) - 2px); cursor:pointer; background:transparent; color:var(--color-muted-foreground);">Офіс / Склад</button>
style="padding:0.375rem 1rem; font-size:0.8125rem; font-weight:500; border:none; border-radius:calc(var(--radius) - 2px); cursor:pointer; background:transparent; color:var(--color-muted-foreground);">{% trans "Офис / Склад" %}</button>
</div>
<!-- ══ ВКЛАДКА: АПТЕКИ ══ -->
@@ -22,31 +24,31 @@
<!-- Метрики -->
<div style="display:grid; grid-template-columns:repeat(4, minmax(0,1fr)); gap:0.75rem; margin-bottom:1.5rem;">
<div class="stat-card" style="padding:1rem 1.25rem;">
<div style="font-size:0.6875rem; color:var(--color-muted-foreground); text-transform:uppercase; letter-spacing:0.05em; margin-bottom:0.375rem;">Всього аптек</div>
<div style="font-size:0.6875rem; color:var(--color-muted-foreground); text-transform:uppercase; letter-spacing:0.05em; margin-bottom:0.375rem;">{% trans "Всего аптек" %}</div>
<div style="font-size:1.5rem; font-weight:500; color:var(--color-foreground);">{{ pharmacies|length }}</div>
<div style="font-size:0.75rem; color:var(--color-success); margin-top:0.25rem;">{{ active_count }} активних</div>
<div style="font-size:0.75rem; color:var(--color-success); margin-top:0.25rem;">{{ active_count }} {% trans "активных" %}</div>
</div>
<div class="stat-card" style="padding:1rem 1.25rem;">
<div style="font-size:0.6875rem; color:var(--color-muted-foreground); text-transform:uppercase; letter-spacing:0.05em; margin-bottom:0.375rem;">Організацій</div>
<div style="font-size:0.6875rem; color:var(--color-muted-foreground); text-transform:uppercase; letter-spacing:0.05em; margin-bottom:0.375rem;">{% trans "Организаций" %}</div>
<div style="font-size:1.5rem; font-weight:500; color:var(--color-foreground);">{{ organizations_count }}</div>
<div style="font-size:0.75rem; color:var(--color-muted-foreground); margin-top:0.25rem;">юридичних осіб</div>
<div style="font-size:0.75rem; color:var(--color-muted-foreground); margin-top:0.25rem;">{% trans "юридических лиц" %}</div>
</div>
<div class="stat-card" style="padding:1rem 1.25rem;">
<div style="font-size:0.6875rem; color:var(--color-muted-foreground); text-transform:uppercase; letter-spacing:0.05em; margin-bottom:0.375rem;">Без завідувача</div>
<div style="font-size:0.6875rem; color:var(--color-muted-foreground); text-transform:uppercase; letter-spacing:0.05em; margin-bottom:0.375rem;">{% trans "Без заведующего" %}</div>
<div style="font-size:1.5rem; font-weight:500; color:var(--color-foreground);">{{ pharmacies_no_head }}</div>
{% if pharmacies_no_head %}
<div style="font-size:0.75rem; color:var(--color-warning); margin-top:0.25rem;">потребують призначення</div>
<div style="font-size:0.75rem; color:var(--color-warning); margin-top:0.25rem;">{% trans "требуют назначения" %}</div>
{% else %}
<div style="font-size:0.75rem; color:var(--color-success); margin-top:0.25rem;">всі аптеки із завідувачем</div>
<div style="font-size:0.75rem; color:var(--color-success); margin-top:0.25rem;">{% trans "все аптеки с заведующим" %}</div>
{% endif %}
</div>
<div class="stat-card" style="padding:1rem 1.25rem;">
<div style="font-size:0.6875rem; color:var(--color-muted-foreground); text-transform:uppercase; letter-spacing:0.05em; margin-bottom:0.375rem;">Без персоналу</div>
<div style="font-size:0.6875rem; color:var(--color-muted-foreground); text-transform:uppercase; letter-spacing:0.05em; margin-bottom:0.375rem;">{% trans "Без персонала" %}</div>
<div style="font-size:1.5rem; font-weight:500; color:var(--color-foreground);">{{ pharmacies_empty }}</div>
{% if pharmacies_empty %}
<div style="font-size:0.75rem; color:var(--color-destructive); margin-top:0.25rem;">аптек без співробітників</div>
<div style="font-size:0.75rem; color:var(--color-destructive); margin-top:0.25rem;">{% trans "аптек без сотрудников" %}</div>
{% else %}
<div style="font-size:0.75rem; color:var(--color-success); margin-top:0.25rem;">всі укомплектовані</div>
<div style="font-size:0.75rem; color:var(--color-success); margin-top:0.25rem;">{% trans "все укомплектованы" %}</div>
{% endif %}
</div>
</div>
@@ -59,11 +61,11 @@
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:var(--color-muted-foreground); flex-shrink:0;">
<circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/>
</svg>
<input type="text" id="dislo-search" placeholder="Пошук аптеки..." oninput="disloFilter()"
<input type="text" id="dislo-search" placeholder="{% trans 'Поиск аптеки...' %}" oninput="disloFilter()"
class="input" style="border:none; background:transparent; padding:0; box-shadow:none; font-size:0.8125rem;">
</div>
<select class="input" id="dislo-filter-enterprise" onchange="disloEnterpriseChange()" style="width:auto; font-size:0.8125rem; padding:0.375rem 0.625rem; max-width:15rem;">
<option value="">Всі підприємства</option>
<option value="">{% trans "Все предприятия" %}</option>
{% for enterprise in enterprise_list %}
<option value="{{ enterprise.grouper|escape }}">{{ enterprise.grouper }}</option>
{% endfor %}
@@ -71,34 +73,34 @@
<div class="filter-tree" style="position:relative; min-width:14rem;">
<button id="dislo-filter-region-button" type="button" class="input" onclick="disloToggleRegionTree()"
style="width:100%; text-align:left; font-size:0.78rem; padding:0.3rem 0.55rem; display:flex; justify-content:space-between; align-items:center; gap:0.4rem; border:1px solid var(--color-border); background:var(--color-muted); color:var(--color-foreground);">
<span id="dislo-filter-region-label">Всі міста/регіони</span>
<span id="dislo-filter-region-label">{% trans "Все города/регионы" %}</span>
<span style="font-size:0.75rem; color:var(--color-muted-foreground);">▾</span>
</button>
<div id="dislo-region-tree-panel" style="position:absolute; top:100%; left:0; right:0; z-index:20; max-height:240px; overflow:auto; background:var(--color-card); border:1px solid var(--color-border); border-radius:0.75rem; margin-top:0.25rem; box-shadow:0 12px 24px rgb(0 0 0 / 8%); display:none;">
<div style="padding:0.55rem 0.65rem; cursor:pointer; border-bottom:1px solid rgba(0,0,0,.06); font-size:0.8rem;"
onclick="disloSelectRegionNode('', 'Всі міста/регіони')">
Всі міста/регіони
onclick="disloSelectRegionNode('', &quot;{% trans 'Все города/регионы' %}&quot;)">
{% trans "Все города/регионы" %}
</div>
<div id="dislo-region-tree-root"></div>
</div>
<input type="hidden" id="dislo-filter-region" value="">
</div>
<select class="input" id="dislo-filter-status" onchange="disloFilter()" style="width:auto; font-size:0.8125rem; padding:0.375rem 0.625rem;">
<option value="">Всі статуси</option>
<option value="active">Активні</option>
<option value="inactive">Неактивні</option>
<option value="">{% trans "Все статусы" %}</option>
<option value="active">{% trans "Активные" %}</option>
<option value="inactive">{% trans "Неактивные" %}</option>
</select>
</div>
<div style="display:flex; gap:2px; background:var(--color-muted); border-radius:var(--radius); padding:3px; flex-shrink:0;">
<button id="dislo-btn-table" onclick="disloView('table')"
style="display:flex;align-items:center;gap:0.375rem;padding:0.3125rem 0.75rem;border-radius:calc(var(--radius) - 2px);font-size:0.8125rem;font-weight:500;border:none;cursor:pointer;transition:background 150ms;background:var(--color-card);color:var(--color-foreground);">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 3h18v18H3zM3 9h18M3 15h18M9 3v18"/></svg>
Таблиця
{% trans "Таблица" %}
</button>
<button id="dislo-btn-map" onclick="disloView('map')"
style="display:flex;align-items:center;gap:0.375rem;padding:0.3125rem 0.75rem;border-radius:calc(var(--radius) - 2px);font-size:0.8125rem;font-weight:500;border:none;cursor:pointer;transition:background 150ms;background:transparent;color:var(--color-muted-foreground);">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14.106 5.553a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619v12.764a1 1 0 0 1-.553.894l-4.553 2.277a2 2 0 0 1-1.788 0l-4.212-2.106a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0z"/><path d="M15 5.764v15"/><path d="M9 3.236v15"/></svg>
Карта
{% trans "Карта" %}
</button>
</div>
</div>
@@ -111,13 +113,13 @@
<thead>
<tr style="border-bottom:1px solid var(--color-border);">
<th style="width:2rem;"></th>
<th class="dislo-sort-th" onclick="disloSortBy('pharmacy','number')" style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;cursor:pointer;user-select:none;white-space:nowrap;">Аптека <span class="dislo-sort-icon" data-key="pharmacy" style="opacity:0.35;">⇅</span></th>
<th class="dislo-sort-th" onclick="disloSortBy('org','string')" style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;cursor:pointer;user-select:none;white-space:nowrap;">Організація <span class="dislo-sort-icon" data-key="org" style="opacity:0.35;">⇅</span></th>
<th class="dislo-sort-th" onclick="disloSortBy('head','string')" style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;cursor:pointer;user-select:none;white-space:nowrap;">Завідуючий <span class="dislo-sort-icon" data-key="head" style="opacity:0.35;">⇅</span></th>
<th class="dislo-sort-th" onclick="disloSortBy('staff','number')" style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;cursor:pointer;user-select:none;white-space:nowrap;">Персонал <span class="dislo-sort-icon" data-key="staff" style="opacity:0.35;">⇅</span></th>
<th class="dislo-sort-th" onclick="disloSortBy('phone','string')" style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;cursor:pointer;user-select:none;white-space:nowrap;">Телефон <span class="dislo-sort-icon" data-key="phone" style="opacity:0.35;">⇅</span></th>
<th class="dislo-sort-th" onclick="disloSortBy('hours','string')" style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;cursor:pointer;user-select:none;white-space:nowrap;">Режим роботи <span class="dislo-sort-icon" data-key="hours" style="opacity:0.35;">⇅</span></th>
<th class="dislo-sort-th" onclick="disloSortBy('status','number')" style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;cursor:pointer;user-select:none;white-space:nowrap;">Статус <span class="dislo-sort-icon" data-key="status" style="opacity:0.35;">⇅</span></th>
<th class="dislo-sort-th" onclick="disloSortBy('pharmacy','number')" style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;cursor:pointer;user-select:none;white-space:nowrap;">{% trans "Аптека" %} <span class="dislo-sort-icon" data-key="pharmacy" style="opacity:0.35;">⇅</span></th>
<th class="dislo-sort-th" onclick="disloSortBy('org','string')" style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;cursor:pointer;user-select:none;white-space:nowrap;">{% trans "Организация" %} <span class="dislo-sort-icon" data-key="org" style="opacity:0.35;">⇅</span></th>
<th class="dislo-sort-th" onclick="disloSortBy('head','string')" style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;cursor:pointer;user-select:none;white-space:nowrap;">{% trans "Заведующий" %} <span class="dislo-sort-icon" data-key="head" style="opacity:0.35;">⇅</span></th>
<th class="dislo-sort-th" onclick="disloSortBy('staff','number')" style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;cursor:pointer;user-select:none;white-space:nowrap;">{% trans "Персонал" %} <span class="dislo-sort-icon" data-key="staff" style="opacity:0.35;">⇅</span></th>
<th class="dislo-sort-th" onclick="disloSortBy('phone','string')" style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;cursor:pointer;user-select:none;white-space:nowrap;">{% trans "Телефон" %} <span class="dislo-sort-icon" data-key="phone" style="opacity:0.35;">⇅</span></th>
<th class="dislo-sort-th" onclick="disloSortBy('hours','string')" style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;cursor:pointer;user-select:none;white-space:nowrap;">{% trans "Режим работы" %} <span class="dislo-sort-icon" data-key="hours" style="opacity:0.35;">⇅</span></th>
<th class="dislo-sort-th" onclick="disloSortBy('status','number')" style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;cursor:pointer;user-select:none;white-space:nowrap;">{% trans "Статус" %} <span class="dislo-sort-icon" data-key="status" style="opacity:0.35;">⇅</span></th>
</tr>
</thead>
<tbody>
@@ -147,25 +149,25 @@
<td style="padding:0.75rem 1rem; color:var(--color-foreground);">
<a href="/pharmacies/{{ pharmacy.id }}/" hx-get="/pharmacies/{{ pharmacy.id }}/" hx-target="#main-content" hx-push-url="/pharmacies/{{ pharmacy.id }}/" hx-indicator="#loader"
onclick="event.stopPropagation();" style="font-weight:600; color:var(--color-foreground); text-decoration:none;">№ {{ pharmacy.pharmacy_number }}</a>
<div style="font-size:0.75rem; color:var(--color-muted-foreground); margin-top:0.125rem; max-width:220px; overflow:hidden; text-overflow:ellipsis;">{{ pharmacy.address|default:"адресу не вказано" }}</div>
<div style="font-size:0.75rem; color:var(--color-muted-foreground); margin-top:0.125rem; max-width:220px; overflow:hidden; text-overflow:ellipsis;">{{ pharmacy.address|default:no_addr_short }}</div>
</td>
<td style="padding:0.75rem 1rem; color:var(--color-foreground);">{{ pharmacy.organization.name|default:"—" }}</td>
<td style="padding:0.75rem 1rem; color:var(--color-foreground);">
{% if pharmacy.head_pharmacist %}
{{ pharmacy.head_pharmacist.first_name }} {{ pharmacy.head_pharmacist.last_name|default:'' }}
{% else %}
<span style="color:var(--color-warning); font-size:0.8125rem;">не призначений</span>
<span style="color:var(--color-warning); font-size:0.8125rem;">{% trans "не назначен" %}</span>
{% endif %}
</td>
<td style="padding:0.75rem 1rem;">
<div style="display:flex; align-items:center; gap:0.5rem;">
<span style="color:var(--color-foreground); font-weight:500;">{{ pharmacy.staff_count }}</span>
{% if pharmacy.staff_status == 'danger' %}
<span class="badge badge-danger">Нестача</span>
<span class="badge badge-danger">{% trans "Нехватка" %}</span>
{% elif pharmacy.staff_status == 'warning' %}
<span class="badge badge-warning">Увага</span>
<span class="badge badge-warning">{% trans "Внимание" %}</span>
{% else %}
<span class="badge badge-success">Норма</span>
<span class="badge badge-success">{% trans "Норма" %}</span>
{% endif %}
</div>
</td>
@@ -175,9 +177,9 @@
<td style="padding:0.75rem 1rem; color:var(--color-foreground); font-size:0.8125rem;">{{ pharmacy.working_hours }}</td>
<td style="padding:0.75rem 1rem;">
{% if pharmacy.is_active %}
<span class="badge badge-success">Активна</span>
<span class="badge badge-success">{% trans "Активна" %}</span>
{% else %}
<span class="badge badge-danger">Неактивна</span>
<span class="badge badge-danger">{% trans "Неактивна" %}</span>
{% endif %}
</td>
</tr>
@@ -197,29 +199,29 @@
{% endif %}
<div style="min-width:0;">
<div style="font-size:0.8125rem; font-weight:500; color:var(--color-card-foreground); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">{{ employee.last_name }} {{ employee.first_name }}</div>
<div style="font-size:0.75rem; color:var(--color-muted-foreground); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">{{ employee.post.name|default:"Посада не вказана" }}{% if employee.phone %} · {{ employee.phone }}{% endif %}</div>
<div style="font-size:0.75rem; color:var(--color-muted-foreground); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">{{ employee.post.name|default:no_post }}{% if employee.phone %} · {{ employee.phone }}{% endif %}</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div style="padding:0.5rem 0; color:var(--color-muted-foreground); font-size:0.8125rem;">У цій аптеці поки немає активних співробітників</div>
<div style="padding:0.5rem 0; color:var(--color-muted-foreground); font-size:0.8125rem;">{% trans "В этой аптеке пока нет активных сотрудников" %}</div>
{% endif %}
</td>
</tr>
{% empty %}
<tr>
<td colspan="8" style="padding:2rem; text-align:center; color:var(--color-muted-foreground);">Аптеки не знайдені</td>
<td colspan="8" style="padding:2rem; text-align:center; color:var(--color-muted-foreground);">{% trans "Аптеки не найдены" %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div style="padding:0.875rem 1.5rem;border-top:1px solid var(--color-border);display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:0.75rem;">
<span id="dislo-page-info" style="font-size:0.8125rem;color:var(--color-muted-foreground);">Показано 0–0 з 0</span>
<span id="dislo-page-info" style="font-size:0.8125rem;color:var(--color-muted-foreground);">{% trans "Показано 0–0 из 0" %}</span>
<div style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap;">
<div style="display:flex;align-items:center;gap:0.375rem;">
<span style="font-size:0.8125rem;color:var(--color-muted-foreground);white-space:nowrap;">На сторінці:</span>
<span style="font-size:0.8125rem;color:var(--color-muted-foreground);white-space:nowrap;">{% trans "На странице:" %}</span>
<select id="dislo-page-size" class="input" style="font-size:0.8125rem;padding:0.3rem 0.5rem;height:auto;width:auto;" onchange="disloSetPageSize(this.value)">
<option value="10">10</option>
<option value="15">15</option>
@@ -228,9 +230,9 @@
</select>
</div>
<div style="display:flex;align-items:center;gap:0.375rem;">
<button type="button" id="dislo-prev-btn" class="btn btn-secondary" style="padding:0.375rem 0.75rem;font-size:0.8125rem;" onclick="disloGoToPage(disloPage - 1)">← Попередня</button>
<span id="dislo-page-label" style="font-size:0.8125rem;color:var(--color-muted-foreground);white-space:nowrap;">Стор. 1 з 1</span>
<button type="button" id="dislo-next-btn" class="btn btn-secondary" style="padding:0.375rem 0.75rem;font-size:0.8125rem;" onclick="disloGoToPage(disloPage + 1)">Наступна →</button>
<button type="button" id="dislo-prev-btn" class="btn btn-secondary" style="padding:0.375rem 0.75rem;font-size:0.8125rem;" onclick="disloGoToPage(disloPage - 1)">← {% trans "Предыдущая" %}</button>
<span id="dislo-page-label" style="font-size:0.8125rem;color:var(--color-muted-foreground);white-space:nowrap;">{% trans "Стр. 1 из 1" %}</span>
<button type="button" id="dislo-next-btn" class="btn btn-secondary" style="padding:0.375rem 0.75rem;font-size:0.8125rem;" onclick="disloGoToPage(disloPage + 1)">{% trans "Следующая" %} →</button>
</div>
</div>
</div>
@@ -241,7 +243,7 @@
<div id="dislo-view-map" style="display:none;">
{% if pharmacies_no_location %}
<div style="font-size:0.8125rem; color:var(--color-warning); margin-bottom:0.75rem;">
⚠ {{ pharmacies_no_location }} апт. без вказаної адреси не показані на карті (немає координат)
⚠ {{ pharmacies_no_location }} {% trans "апт. без указанного адреса не показаны на карте (нет координат)" %}
</div>
{% endif %}
<div style="display:grid;grid-template-columns:1fr 300px;gap:1rem;">
@@ -250,7 +252,7 @@
</div>
<div style="background:var(--color-card);border-radius:0.75rem;border:1px solid var(--color-border);max-height:520px;overflow-y:auto;">
<div style="padding:0.875rem 1rem;border-bottom:1px solid var(--color-border);font-size:0.8125rem;font-weight:600;color:var(--color-foreground);position:sticky;top:0;background:var(--color-card);z-index:1;">
Список аптек
{% trans "Список аптек" %}
</div>
<div style="padding:0.75rem 0.75rem;">
{% for pharmacy in pharmacies %}
@@ -283,7 +285,7 @@
<div id="dislo-panel-office" class="dislo-panel" style="display:none;">
<div style="margin-bottom:1rem;">
<span style="font-size:0.875rem; color:var(--color-muted-foreground);">{{ office_employees_count }} співробітників офісу та складу, згруповано по відділах</span>
<span style="font-size:0.875rem; color:var(--color-muted-foreground);">{{ office_employees_count }} {% trans "сотрудников офиса и склада, сгруппировано по отделам" %}</span>
</div>
<div style="background:var(--color-card); border-radius:0.75rem; border:1px solid var(--color-border); box-shadow:0 1px 2px rgb(0 0 0/0.05); overflow:hidden;">
@@ -291,10 +293,10 @@
<table style="width:100%; border-collapse:collapse; font-size:0.875rem;">
<thead>
<tr style="border-bottom:1px solid var(--color-border);">
<th style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem; width:8rem;">Внутр. номер</th>
<th style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">Посада</th>
<th style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">П.І.Б.</th>
<th style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">Корпоративний телефон</th>
<th style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem; width:8rem;">{% trans "Внутр. номер" %}</th>
<th style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">{% trans "Должность" %}</th>
<th style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">{% trans "Ф.И.О." %}</th>
<th style="padding:0.75rem 1rem;text-align:left;color:var(--color-muted-foreground);font-weight:600;font-size:0.8125rem;">{% trans "Корпоративный телефон" %}</th>
</tr>
</thead>
<tbody>
@@ -315,7 +317,7 @@
{% if office_no_department %}
<tr>
<td colspan="4" style="padding:0.5rem 1rem; background:color-mix(in srgb, var(--color-warning) 18%, var(--color-card)); font-weight:600; color:var(--color-foreground);">Без відділу</td>
<td colspan="4" style="padding:0.5rem 1rem; background:color-mix(in srgb, var(--color-warning) 18%, var(--color-card)); font-weight:600; color:var(--color-foreground);">{% trans "Без отдела" %}</td>
</tr>
{% for employee in office_no_department %}
<tr class="tr-hover" style="border-bottom:1px solid var(--color-border); cursor:pointer;"
@@ -330,7 +332,7 @@
{% if not office_departments and not office_no_department %}
<tr>
<td colspan="4" style="padding:2rem; text-align:center; color:var(--color-muted-foreground);">Співробітників офісу/складу не знайдено</td>
<td colspan="4" style="padding:2rem; text-align:center; color:var(--color-muted-foreground);">{% trans "Сотрудников офиса/склада не найдено" %}</td>
</tr>
{% endif %}
</tbody>
@@ -355,7 +357,7 @@ const disloData = [
{
id: {{ pharmacy.id }},
name: "{{ pharmacy.organization.name|escapejs|default:'' }} #{{ pharmacy.pharmacy_number|escapejs|default:'0' }}",
address: "{{ pharmacy.address|escapejs|default:'Адреса невідома' }}",
address: "{{ pharmacy.address|escapejs|default:no_addr }}",
head: "{{ pharmacy.head_pharmacist.first_name|escapejs|default:'' }} {{ pharmacy.head_pharmacist.last_name|escapejs|default:'' }}".trim() || "—",
phone: "{{ pharmacy.phone|escapejs|default:'-' }}",
hours: "{{ pharmacy.working_hours|escapejs|default:'-' }}",
@@ -432,7 +434,7 @@ function disloEnterpriseChange() {
const allowedIds = disloGetAllowedRegionIdsByEnterprise();
if (selectedRegion && !allowedIds.has(selectedRegion)) {
regionInput.value = '';
document.getElementById('dislo-filter-region-label').textContent = 'Всі міста/регіони';
document.getElementById('dislo-filter-region-label').textContent = '{% trans "Все города/регионы" %}';
}
disloRenderRegionTree();
disloFilter();
@@ -449,7 +451,7 @@ function disloCloseRegionTree() {
function disloSelectRegionNode(regionId, label) {
document.getElementById('dislo-filter-region').value = regionId || '';
document.getElementById('dislo-filter-region-label').textContent = label || 'Всі міста/регіони';
document.getElementById('dislo-filter-region-label').textContent = label || '{% trans "Все города/регионы" %}';
disloCloseRegionTree();
disloFilter();
}
@@ -552,11 +554,11 @@ function disloCreateMarker(pharmacy) {
<small>🧑 ${pharmacy.head || '-'}</small><br/>
<small>☎️ ${pharmacy.phone}</small><br/>
<small>🕐 ${pharmacy.hours}</small><br/>
<small>👥 Персонал: ${pharmacy.staff}</small><br/>
<small>👥 {% trans "Персонал" %}: ${pharmacy.staff}</small><br/>
<span style="display:inline-block;margin-top:4px;padding:2px 6px;border-radius:3px;background:${markerColor};color:white;font-size:11px;">
${pharmacy.active ? 'Активна' : 'Неактивна'}
${pharmacy.active ? '{% trans "Активна" %}' : '{% trans "Неактивна" %}'}
</span><br/>
<a href="/pharmacies/${pharmacy.id}/" style="display:inline-block;margin-top:6px;font-size:11px;">Детальніше →</a>
<a href="/pharmacies/${pharmacy.id}/" style="display:inline-block;margin-top:6px;font-size:11px;">{% trans "Подробнее" %} →</a>
</div>
`);
marker.on('click', function() {
@@ -715,7 +717,7 @@ function disloUpdatePaginationBar(total, start, end, page, totalPages) {
const label = document.getElementById('dislo-page-label');
const prevBtn = document.getElementById('dislo-prev-btn');
const nextBtn = document.getElementById('dislo-next-btn');
if (info) info.textContent = total ? `Показано ${start + 1}–${end} з ${total}` : 'Немає записів';
if (info) info.textContent = total ? `{% trans "Показано" %} ${start + 1}–${end} {% trans "из" %} ${total}` : '{% trans "Нет записей" %}';
if (label) label.textContent = `Стор. ${page} з ${totalPages}`;
if (prevBtn) prevBtn.disabled = page <= 1;
if (nextBtn) nextBtn.disabled = page >= totalPages;