A local web app for a small room-rental business in Austria: guest register, room ledger, occupancy plan, invoices, the monthly tourist-tax report and the quarterly VAT list. Application code only; it ships no data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
63 lines
3.6 KiB
HTML
63 lines
3.6 KiB
HTML
<!doctype html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>{{ t('app_title') }}</title>
|
|
{# a house emoji as favicon: kills the browser's /favicon.ico 404 on every page load #}
|
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🏠</text></svg>">
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
|
<script data-fp="{{ db_fp }}">
|
|
// Re-apply the grid layouts measured on an earlier visit BEFORE the tables parse, so the browser
|
|
// never has to lay out thousands of rows at once (grids.js stores them, keyed by a db fingerprint —
|
|
// any data change re-measures). See freezeLayout() in grids.js.
|
|
(function () {
|
|
try {
|
|
var fp = document.currentScript.dataset.fp;
|
|
window.__gridfp = fp;
|
|
window.__pregrids = {};
|
|
for (var i = localStorage.length - 1; i >= 0; i--) {
|
|
var k = localStorage.key(i);
|
|
if (k.indexOf("grid:") !== 0) continue;
|
|
var v = JSON.parse(localStorage.getItem(k));
|
|
if (v.v !== 5) { localStorage.removeItem(k); continue; } // older stored format: re-measure
|
|
if (v.fp === fp && v.vw === window.innerWidth) {
|
|
var s = document.createElement("style");
|
|
s.textContent = v.css;
|
|
document.head.appendChild(s);
|
|
window.__pregrids[k.slice(5)] = { rowH: v.rowH || 1, widths: v.widths || null };
|
|
} else if (v.fp !== fp) {
|
|
localStorage.removeItem(k);
|
|
}
|
|
}
|
|
} catch (e) { /* no stored layout -> the table measures itself after load */ }
|
|
})();
|
|
</script>
|
|
<script>window.GRID_TEXT = {{ {'search': t('print_search'), 'rows': t('print_rows'), 'rows_one': t('print_rows_one'),
|
|
'date': t('print_date'), 'pin': t('pin_tip'), 'unpin': t('unpin_tip')}|tojson }};</script>
|
|
<script src="{{ url_for('static', filename='grids.js') }}" defer></script>
|
|
</head>
|
|
<body>
|
|
<header class="topbar">
|
|
<div class="brand">{{ t('app_title') }}</div>
|
|
<nav>
|
|
<a href="{{ url_for('home') }}" class="{{ 'active' if request.endpoint == 'home' }}">{{ t('nav_home') }}</a>
|
|
<a href="{{ url_for('register') }}" class="{{ 'active' if request.endpoint in ('register','stay_form') }}">{{ t('nav_register') }}</a>
|
|
<a href="{{ url_for('ledger') }}" class="{{ 'active' if request.endpoint == 'ledger' }}">{{ t('nav_ledger') }}</a>
|
|
<a href="{{ url_for('plan_page') }}" class="{{ 'active' if request.endpoint == 'plan_page' }}">{{ t('nav_plan') }}</a>
|
|
<a href="{{ url_for('invoices') }}" class="{{ 'active' if request.endpoint and request.endpoint.startswith('invoice') }}">{{ t('nav_invoices') }}</a>
|
|
<a href="{{ url_for('clients') }}" class="{{ 'active' if request.endpoint and request.endpoint.startswith('client') }}">{{ t('nav_clients') }}</a>
|
|
<a href="{{ url_for('tvb_page') }}" class="{{ 'active' if request.endpoint == 'tvb_page' }}">{{ t('nav_tvb') }}</a>
|
|
<a href="{{ url_for('vat_page') }}" class="{{ 'active' if request.endpoint == 'vat_page' }}">{{ t('nav_vat') }}</a>
|
|
<a href="{{ url_for('quality_page') }}" class="{{ 'active' if request.endpoint and request.endpoint.startswith('quality') }}">{{ t('nav_quality') }}</a>
|
|
</nav>
|
|
</header>
|
|
<main class="{% block main_class %}{% endblock %}">
|
|
{% with msgs = get_flashed_messages() %}
|
|
{% if msgs %}<div class="flash">{% for m in msgs %}<div>{{ m }}</div>{% endfor %}</div>{% endif %}
|
|
{% endwith %}
|
|
{% block content %}{% endblock %}
|
|
</main>
|
|
</body>
|
|
</html>
|