mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-11-03 09:12:19 +01:00
Stage 5a translated the contacts pages but the browser diff was only run against /contacts/existing, so the other three kept most of their page headers, action buttons and confirmation modals in English: page titles, "Add Contact", "Pending Contacts", Refresh, Filters, Approve/Ignore and both batch-confirmation dialogs, plus Advanced Filters, Preview Cleanup and the cleanup confirmation on /contacts/manage. Most of the keys they needed already existed from 5a - the template sites were simply never wired to them. Three spinners in the Settings modal (Regions, Analyzer, Observer lists) were missed in 8b for the opposite reason: their containers are excluded from the stage-8 browser check as JS-populated, but the spinner inside is server-rendered, so excluding the container hid it. Root cause of both, and the reason for the new tool: a browser diff only sees what is visible on the page it is pointed at. Add i18n-template-diff.py, which fetches each entry point's HTML in en and pl and compares text nodes directly. No browser, no per-page script, and it covers closed modals and every page in one run - it found all of the above in a single pass. It cannot see anything JavaScript writes, so it complements the Playwright scripts rather than replacing them. Every server-rendered template in the project is now translated. The whole-project run reports 11 identical strings, all correct: the device name, version strings, the language names (which are supposed to match), "1 hop" (identical in Polish) and the glossary terms Hash, HB, Repeater. Catalog: 760 -> 781 keys, pl at 100%. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
129 lines
6.5 KiB
HTML
129 lines
6.5 KiB
HTML
{% extends "contacts_base.html" %}
|
|
|
|
{% block title %}{{ t('contacts.add.title') }} - mc-webui{% endblock %}
|
|
|
|
{% block extra_head %}
|
|
<!-- html5-qrcode for QR scanning -->
|
|
<script src="https://unpkg.com/html5-qrcode@2.3.8/html5-qrcode.min.js"></script>
|
|
{% endblock %}
|
|
|
|
{% block page_content %}
|
|
<div id="addPageContent" class="p-3">
|
|
<!-- Page Header -->
|
|
<div class="mb-3">
|
|
<h4 class="mb-2">
|
|
<i class="bi bi-person-plus"></i> {{ t('contacts.add.title') }}
|
|
</h4>
|
|
</div>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="d-flex gap-2 mb-3">
|
|
<button class="btn btn-outline-secondary btn-sm" onclick="navigateTo('/contacts/manage');">
|
|
<i class="bi bi-arrow-left"></i> {{ t('common.back') }}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Input Mode Tabs -->
|
|
<ul class="nav nav-tabs mb-3" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" id="tab-uri" data-bs-toggle="tab" data-bs-target="#pane-uri" type="button" role="tab">
|
|
<i class="bi bi-link-45deg"></i> URI
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="tab-qr" data-bs-toggle="tab" data-bs-target="#pane-qr" type="button" role="tab">
|
|
<i class="bi bi-qr-code-scan"></i> {{ t('channels.qr_alt') }}
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="tab-manual" data-bs-toggle="tab" data-bs-target="#pane-manual" type="button" role="tab">
|
|
<i class="bi bi-pencil"></i> {{ t('contacts.add.tab_manual') }}
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="tab-content">
|
|
<!-- URI Paste Tab -->
|
|
<div class="tab-pane fade show active" id="pane-uri" role="tabpanel">
|
|
<div class="mb-3">
|
|
<label for="uriInput" class="form-label">{{ t('contacts.add.uri_label') }}</label>
|
|
<textarea class="form-control font-monospace" id="uriInput" rows="3"
|
|
placeholder="{{ t('contacts.add.uri_ph') }}"></textarea>
|
|
<small class="form-text text-muted">{{ t('contacts.add.uri_hint') }}</small>
|
|
</div>
|
|
<!-- URI Preview -->
|
|
<div id="uriPreview" class="alert alert-info d-none mb-3">
|
|
<strong>{{ t('contacts.add.preview') }}</strong>
|
|
<div><span class="text-muted">{{ t('contacts.add.name') }}</span> <span id="uriPreviewName"></span></div>
|
|
<div><span class="text-muted">{{ t('contacts.add.key') }}</span> <span id="uriPreviewKey" class="font-monospace small" style="word-break: break-all;"></span></div>
|
|
<div><span class="text-muted">{{ t('contacts.add.type_short') }}</span> <span id="uriPreviewType"></span></div>
|
|
</div>
|
|
<button class="btn btn-success" id="addFromUriBtn" disabled>
|
|
<i class="bi bi-plus-circle"></i> {{ t('contacts.add.title') }}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- QR Code Tab -->
|
|
<div class="tab-pane fade" id="pane-qr" role="tabpanel">
|
|
<!-- Camera Scanner -->
|
|
<div id="qrScannerContainer" class="mb-3">
|
|
<div id="qrReader" style="width: 100%; max-width: 500px;"></div>
|
|
<div id="qrCameraButtons" class="d-flex gap-2 mt-2">
|
|
<button class="btn btn-primary btn-sm" id="startCameraBtn">
|
|
<i class="bi bi-camera-video"></i> {{ t('contacts.add.start_camera') }}
|
|
</button>
|
|
<button class="btn btn-outline-secondary btn-sm d-none" id="stopCameraBtn">
|
|
<i class="bi bi-stop-circle"></i> {{ t('contacts.add.stop_camera') }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!-- File Upload Fallback -->
|
|
<div class="mb-3">
|
|
<label for="qrFileInput" class="form-label">{{ t('contacts.add.qr_label') }}</label>
|
|
<input type="file" class="form-control" id="qrFileInput" accept="image/*">
|
|
</div>
|
|
<!-- QR Result -->
|
|
<div id="qrResult" class="alert alert-success d-none mb-3">
|
|
<strong>{{ t('contacts.add.scanned') }}</strong>
|
|
<div><span class="text-muted">{{ t('contacts.add.name') }}</span> <span id="qrResultName"></span></div>
|
|
<div><span class="text-muted">{{ t('contacts.add.key') }}</span> <span id="qrResultKey" class="font-monospace small" style="word-break: break-all;"></span></div>
|
|
<div><span class="text-muted">{{ t('contacts.add.type_short') }}</span> <span id="qrResultType"></span></div>
|
|
</div>
|
|
<div id="qrError" class="alert alert-danger d-none mb-3"></div>
|
|
<button class="btn btn-success d-none" id="addFromQrBtn">
|
|
<i class="bi bi-plus-circle"></i> {{ t('contacts.add.title') }}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Manual Entry Tab -->
|
|
<div class="tab-pane fade" id="pane-manual" role="tabpanel">
|
|
<div class="mb-3">
|
|
<label for="manualName" class="form-label">{{ t('contacts.add.name') }}</label>
|
|
<input type="text" class="form-control" id="manualName" placeholder="{{ t('contacts.add.name_ph') }}" maxlength="32">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="manualKey" class="form-label">{{ t('contacts.add.pubkey') }}</label>
|
|
<input type="text" class="form-control font-monospace" id="manualKey"
|
|
placeholder="{{ t('contacts.add.pubkey_ph') }}" maxlength="64" pattern="[0-9a-fA-F]{64}">
|
|
<small class="form-text text-muted" id="manualKeyCount">{{ t('contacts.add.key_count', n=0) }}</small>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="manualType" class="form-label">{{ t('contacts.add.type') }}</label>
|
|
<select class="form-select" id="manualType">
|
|
<option value="1" selected>COM (Companion)</option>
|
|
<option value="2">REP (Repeater)</option>
|
|
<option value="3">ROOM (Room Server)</option>
|
|
<option value="4">SENS (Sensor)</option>
|
|
</select>
|
|
</div>
|
|
<button class="btn btn-success" id="addManualBtn" disabled>
|
|
<i class="bi bi-plus-circle"></i> {{ t('contacts.add.title') }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Status Messages -->
|
|
<div id="addStatus" class="mt-3 d-none"></div>
|
|
</div>
|
|
{% endblock %}
|