Files
datecalc/templates/index.html

792 lines
32 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% macro format_date(date_str) %}
{%- if date_str and '-' in date_str and date_str|length == 10 -%}
{{ date_str[8:10] }}.{{ date_str[5:7] }}.{{ date_str[0:4] }}
{%- else -%}
{{ date_str }}
{%- endif -%}
{% endmacro %}
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Elpatrons Datumsrechner Open Source Kalender- und Datumsberechnungen</title>
<meta name="description" content="Elpatrons Datumsrechner: Open Source Web-App für Kalender- und Datumsberechnungen. Tage, Werktage, Wochen, Monate, Kalenderwochen, Wochentage und mehr berechnen barrierefrei, werbefrei, trackingfrei, kostenlos.">
<meta name="keywords" content="Datum, Kalender, Datumsrechner, Werktage, Tage zählen, Kalenderwoche, Wochentag, Open Source, Python, Flask, barrierefreiheit, barrierefrei, kostenlos, werbefrei, trackingfrei, cookiefrei">
<meta property="og:title" content="Elpatrons Datumsrechner Open Source Kalender- und Datumsberechnungen">
<meta property="og:description" content="Open Source Web-App für Kalender- und Datumsberechnungen. Werbefrei, trackingfrei, kostenlos.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://codeberg.org/elpatron/datecalc">
<meta property="og:image" content="/static/logo.svg">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="HandheldFriendly" content="true">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="format-detection" content="telephone=no">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="application-name" content="Elpatrons Datumsrechner">
<meta name="msapplication-TileColor" content="#2563eb">
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
<style>
:root {
--primary: #2563eb;
--primary-dark: #1e40af;
--background: #f8fafc;
--surface: #fff;
--border: #e5e7eb;
--text: #1e293b;
--shadow: 0 2px 8px rgba(30,41,59,0.07);
}
body {
background: var(--background);
color: var(--text);
font-family: 'Segoe UI', Arial, sans-serif;
margin: 0;
padding: 0;
}
.container {
max-width: 480px;
margin: 3em auto;
background: var(--surface);
border-radius: 16px;
box-shadow: var(--shadow);
padding: 2.5em 2em 2em 2em;
border: 1px solid var(--border);
position: relative;
}
.help-button-container {
position: absolute;
top: 2.5em;
right: 2em;
z-index: 10;
}
.help-button {
width: 2.2em;
height: 2.2em;
border-radius: 50%;
background: rgba(37, 99, 235, 0.1);
color: var(--primary);
border: 1px solid var(--border);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 1em;
font-weight: 500;
transition: all 0.2s;
min-width: 44px;
min-height: 44px;
}
.help-button:hover {
background: rgba(37, 99, 235, 0.2);
border-color: var(--primary);
}
.help-button:focus {
outline: 3px solid #facc15;
outline-offset: 2px;
box-shadow: 0 0 0 4px #1e293b;
}
.help-tooltip {
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
background: var(--text);
color: white;
padding: 0.5em 0.8em;
border-radius: 6px;
font-size: 0.85em;
white-space: nowrap;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s, visibility 0.2s;
margin-top: 0.5em;
z-index: 20;
}
.help-tooltip::before {
content: '';
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
border: 5px solid transparent;
border-bottom-color: var(--text);
}
.help-button:hover + .help-tooltip,
.help-button:focus + .help-tooltip {
opacity: 1;
visibility: visible;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: none;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-overlay.active {
display: flex;
}
.modal-content {
background: var(--surface);
border-radius: 12px;
padding: 2em;
max-width: 90%;
max-height: 90%;
overflow-y: auto;
position: relative;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
margin: 0 auto;
left: 50%;
transform: translateX(-50%);
}
.modal-close {
position: absolute;
top: 1em;
right: 1em;
background: none;
border: none;
font-size: 1.5em;
cursor: pointer;
color: var(--text);
padding: 0.5em;
border-radius: 50%;
transition: background 0.2s;
width: 2.5em;
height: 2.5em;
display: flex;
align-items: center;
justify-content: center;
min-width: 44px;
min-height: 44px;
}
.modal-close:hover {
background: var(--border);
}
.modal-close:focus {
outline: 3px solid #facc15;
outline-offset: 2px;
}
.modal-content h1 {
margin-top: 0;
color: var(--primary-dark);
}
.modal-content h2 {
color: var(--primary-dark);
margin-top: 1.5em;
margin-bottom: 0.5em;
}
.modal-content p {
line-height: 1.6;
margin-bottom: 1em;
}
.modal-content ul {
margin-bottom: 1em;
padding-left: 1.5em;
}
.modal-content li {
margin-bottom: 0.5em;
}
h1 {
text-align: center;
margin-bottom: 2em;
font-size: 2.1em;
letter-spacing: 0.01em;
}
form {
margin-bottom: 2.2em;
padding-bottom: 1.2em;
border-bottom: 1px solid var(--border);
}
form:last-of-type {
border-bottom: none;
margin-bottom: 0;
}
h2 {
font-size: 1.15em;
margin-bottom: 0.7em;
color: var(--primary-dark);
}
label {
display: block;
margin-bottom: 0.7em;
font-weight: 500;
}
.date-row {
display: flex;
align-items: center;
gap: 0.5em;
margin-top: 0.2em;
}
.date-calc-row {
display: flex;
align-items: center;
gap: 0.5em;
margin-top: 0.2em;
}
input[type="date"] {
padding: 0.45em 0.7em;
border: 1px solid var(--border);
border-radius: 6px;
font-size: 1em;
background: #f1f5f9;
color: var(--text);
}
.today-btn {
padding: 0.35em 0.9em;
background: var(--primary-dark);
color: #fff;
border: none;
border-radius: 6px;
font-size: 0.95em;
font-weight: 500;
cursor: pointer;
transition: background 0.2s;
}
.today-btn:hover {
background: var(--primary);
}
button, .accordion-header {
background: var(--primary);
color: #fff;
border: none;
border-radius: 6px;
font-size: 1em;
font-weight: 600;
cursor: pointer;
box-shadow: 0 1px 3px rgba(30,41,59,0.05);
transition: background 0.2s;
}
/* Berechnungs-Buttons vergrößern */
button[type="submit"] {
font-size: 1.1em;
padding: 0.8em 1.5em;
min-width: 140px;
margin-top: 1.2em;
}
button:hover, .accordion-header:hover {
background: var(--primary-dark);
}
button:focus, .accordion-header:focus {
outline: 3px solid #facc15;
outline-offset: 2px;
box-shadow: 0 0 0 4px #1e293b;
z-index: 2;
}
.result {
margin-top: 1em;
font-weight: bold;
background: #e0e7ff;
color: #1e293b;
border-radius: 6px;
padding: 0.7em 1em;
box-shadow: 0 1px 2px rgba(30,41,59,0.04);
border: 2px solid #2563eb;
}
.accordion {
border-radius: 12px;
overflow: hidden;
box-shadow: var(--shadow);
background: var(--surface);
margin-bottom: 2em;
/* Layout-Shift-Prävention */
min-height: 200px;
contain: layout style paint;
}
.accordion-item + .accordion-item {
border-top: 1px solid var(--border);
margin-top: 0.5em;
}
.accordion-header {
background: var(--primary-dark);
color: #fff;
cursor: pointer;
padding: 1em 1.2em;
font-size: 1.1em;
font-weight: 600;
border: none;
outline: none;
width: 100%;
text-align: left;
transition: background 0.2s;
}
.accordion-header.active, .accordion-header:hover {
background: var(--primary);
}
.accordion-content {
display: none;
padding: 1.2em 1.2em 1em 1.2em;
background: var(--surface);
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out, padding 0.3s ease-out;
opacity: 0;
}
.accordion-content.active {
display: block;
max-height: 500px;
opacity: 1;
transition: max-height 0.3s ease-in, padding 0.3s ease-in, opacity 0.3s ease-in;
}
.header-tage {
background: #2563eb;
color: #fff;
}
.header-tage.active, .header-tage:hover {
background: #1e40af;
color: #fff;
}
.header-werktage {
background: #059669;
color: #fff;
}
.header-werktage.active, .header-werktage:hover {
background: #047857;
color: #fff;
}
.header-wochentag {
background: #f59e42;
color: #1e293b;
}
.header-wochentag.active, .header-wochentag:hover {
background: #d97706;
color: #fff;
}
.header-plusminus-tage {
background: #a21caf;
color: #fff;
}
.header-plusminus-tage.active, .header-plusminus-tage:hover {
background: #701a75;
color: #fff;
}
.header-plusminus-werktage {
background: #0ea5e9;
color: #fff;
}
.header-plusminus-werktage.active, .header-plusminus-werktage:hover {
background: #0369a1;
color: #fff;
}
.header-plusminus-wochenmonate {
background: #f43f5e;
color: #fff;
}
.header-plusminus-wochenmonate.active, .header-plusminus-wochenmonate:hover {
background: #be123c;
color: #fff;
}
.header-kw {
background: #a78bfa;
color: #1e293b;
}
.header-kw.active, .header-kw:hover {
background: #7c3aed;
color: #fff;
}
.header-kw-datum {
background: #facc15;
color: #1e293b;
}
.header-kw-datum.active, .header-kw-datum:hover {
background: #ca8a04;
color: #fff;
}
.header-plusminus {
background: #be123c;
color: #fff;
}
.header-plusminus.active, .header-plusminus:hover {
background: #7f1d1d;
color: #fff;
}
@media (max-width: 600px) {
.container {
margin: 1em;
padding: 1.2em 0.7em 1em 0.7em;
}
h1 {
font-size: 1.3em;
}
.help-button-container {
top: 1.5em;
right: 1.2em;
}
.help-button {
width: 2em;
height: 2em;
font-size: 0.9em;
min-width: 48px;
min-height: 48px;
}
.help-tooltip {
font-size: 0.8em;
padding: 0.4em 0.6em;
}
.modal-content {
padding: 1.5em;
margin: 1em;
}
}
/* Touch-Target Optimierungen für Footer-Links */
footer a {
display: inline-block;
padding: 0.5em 0.8em;
margin: 0.3em 0.2em;
min-height: 44px;
min-width: 44px;
line-height: 1.4;
text-decoration: none;
border-radius: 6px;
transition: background-color 0.2s, color 0.2s;
position: relative;
}
footer a:hover {
background-color: rgba(37, 99, 235, 0.1);
text-decoration: underline;
}
footer a:focus {
outline: 3px solid #facc15;
outline-offset: 2px;
background-color: rgba(37, 99, 235, 0.1);
}
/* Zusätzlicher Abstand zwischen Footer-Links */
footer br + a {
margin-top: 0.5em;
}
/* Responsive Anpassungen für Footer */
@media (max-width: 600px) {
footer a {
padding: 0.6em 1em;
margin: 0.4em 0.3em;
min-height: 48px;
min-width: 48px;
}
}
</style>
<link rel="manifest" href="/static/manifest.json">
<meta name="theme-color" content="#2563eb">
<script>
function setToday(id) {
const today = new Date().toISOString().split('T')[0];
document.getElementById(id).value = today;
}
function openAccordion(idx) {
const headers = document.querySelectorAll('.accordion-header');
const panels = document.querySelectorAll('.accordion-content');
headers.forEach((btn, i) => {
btn.classList.toggle('active', i === idx);
btn.setAttribute('aria-expanded', i === idx ? 'true' : 'false');
});
panels.forEach((el, i) => {
el.classList.toggle('active', i === idx);
});
}
function showHelp() {
const modal = document.getElementById('helpModal');
modal.classList.add('active');
document.body.style.overflow = 'hidden';
// Fokus auf den Schließen-Button setzen
setTimeout(() => {
document.querySelector('.modal-close').focus();
}, 100);
}
function hideHelp() {
const modal = document.getElementById('helpModal');
modal.classList.remove('active');
document.body.style.overflow = '';
// Fokus zurück auf den Hilfe-Button setzen
document.querySelector('.help-button').focus();
}
document.addEventListener('DOMContentLoaded', function() {
// Sofortige Aktivierung der ersten Accordion-Sektion um Layout-Shifts zu vermeiden
const activeIdx = parseInt("{{ active_idx|default(0) }}");
const headers = document.querySelectorAll('.accordion-header');
const panels = document.querySelectorAll('.accordion-content');
// Sofort den aktiven Zustand setzen
headers.forEach((btn, i) => {
btn.classList.toggle('active', i === activeIdx);
btn.setAttribute('aria-expanded', i === activeIdx ? 'true' : 'false');
});
panels.forEach((el, i) => {
el.classList.toggle('active', i === activeIdx);
});
// Tastatursteuerung für Accordion
headers.forEach((header, idx) => {
header.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
openAccordion(idx);
headers[idx].focus();
} else if (e.key === 'ArrowDown') {
e.preventDefault();
const next = (idx + 1) % headers.length;
headers[next].focus();
} else if (e.key === 'ArrowUp') {
e.preventDefault();
const prev = (idx - 1 + headers.length) % headers.length;
headers[prev].focus();
}
});
});
// ESC-Taste zum Schließen des Modals
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
hideHelp();
}
});
// Klick außerhalb des Modals zum Schließen
document.getElementById('helpModal').addEventListener('click', function(e) {
if (e.target === this) {
hideHelp();
}
});
});
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/static/service-worker.js');
});
}
</script>
</head>
<body>
<div class="container">
<div class="help-button-container">
<button type="button" class="help-button" onclick="showHelp()" aria-label="Hilfe anzeigen" title="Hilfe anzeigen" aria-describedby="help-tooltip">?</button>
<div id="help-tooltip" class="help-tooltip" role="tooltip">Öffnet ein Hilfefenster mit Informationen über den Datumsrechner</div>
</div>
<div style="text-align:center; margin-bottom:1.2em;">
<div style="font-size:1.1em; font-style:italic; color:#64748b;">Elpatrons</div>
<h1 style="margin:0;">Datumsrechner</h1>
<div style="font-size:0.9em; color:#353535; margin-top:0.3em;">
Eine <em>freie</em> Web-App: barriere<em>frei</em>, werbe<em>frei</em>, tracking<em>frei</em>, lizenz<em>frei</em> und kosten<em>frei</em>.
</div>
</div>
<div class="accordion">
<div class="accordion-item">
<button type="button" class="accordion-header header-tage active" id="accordion-header-0" aria-expanded="true" aria-controls="accordion-panel-0" role="button" tabindex="0" onclick="openAccordion(0)">
<span style="vertical-align:middle;display:inline-block;width:1.5em;">
<!-- Kalender mit Doppelpfeil -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="16" rx="4" fill="#fff" stroke="#2563eb" stroke-width="2"/><rect x="3" y="5" width="18" height="4" rx="2" fill="#2563eb"/><rect x="6" y="2" width="2" height="4" rx="1" fill="#2563eb"/><rect x="16" y="2" width="2" height="4" rx="1" fill="#2563eb"/><path d="M8 15h8M8 15l2-2M8 15l2 2M16 15l-2-2M16 15l-2 2" stroke="#2563eb" stroke-width="1.5" stroke-linecap="round"/></svg>
</span>
Anzahl der Tage/Werktage zwischen zwei Daten
</button>
<div class="accordion-content active" id="accordion-panel-0" role="region" aria-labelledby="accordion-header-0">
<form method="post">
<label for="start1">Startdatum:<br>
<span class="date-row">
<input type="date" name="start1" id="start1">
<button type="button" class="today-btn" onclick="setToday('start1')">Heute</button>
</span>
</label>
<label for="end1">Enddatum:<br>
<span class="date-row">
<input type="date" name="end1" id="end1">
<button type="button" class="today-btn" onclick="setToday('end1')">Heute</button>
</span>
</label>
<fieldset style="display:flex;align-items:center;gap:0.5em;margin-top:0.7em; border:none; padding:0;">
<legend class="sr-only">Optionen</legend>
<input type="checkbox" name="werktage" id="werktage" {% if request.form.get('werktage') %}checked{% endif %} aria-checked="{{ 'true' if request.form.get('werktage') else 'false' }}">
<label for="werktage" style="margin:0;">Nur Werktage</label>
</fieldset>
<button name="action" value="tage_werktage" type="submit">Berechnen</button>
</form>
{% if tage is not none %}
<div class="result" aria-live="polite">
{% if request.form.get('werktage') %}
Anzahl der Werktage zwischen <b>{{ format_date(request.form.get('start1', '')) }}</b> und <b>{{ format_date(request.form.get('end1', '')) }}</b>: {{ tage }}
{% else %}
Anzahl der Tage zwischen <b>{{ format_date(request.form.get('start1', '')) }}</b> und <b>{{ format_date(request.form.get('end1', '')) }}</b>: {{ tage }}
{% endif %}
</div>
{% endif %}
</div>
</div>
<div class="accordion-item">
<button type="button" class="accordion-header header-wochentag" id="accordion-header-1" aria-expanded="false" aria-controls="accordion-panel-1" role="button" tabindex="0" onclick="openAccordion(1)">
<span style="vertical-align:middle;display:inline-block;width:1.5em;">
<!-- Kalender mit W -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="16" rx="4" fill="#fff" stroke="#2563eb" stroke-width="2"/><rect x="3" y="5" width="18" height="4" rx="2" fill="#2563eb"/><rect x="6" y="2" width="2" height="4" rx="1" fill="#2563eb"/><rect x="16" y="2" width="2" height="4" rx="1" fill="#2563eb"/><text x="12" y="17" text-anchor="middle" font-size="12" font-family="Segoe UI, Arial, sans-serif" fill="#2563eb" font-weight="bold">W</text></svg>
</span>
Wochentag eines Datums
</button>
<div class="accordion-content" id="accordion-panel-1" role="region" aria-labelledby="accordion-header-1">
<form method="post">
<label for="datum3">Datum:<br>
<span class="date-row">
<input type="date" name="datum3" id="datum3">
<button type="button" class="today-btn" onclick="setToday('datum3')">Heute</button>
</span>
</label>
<button name="action" value="wochentag" type="submit">Anzeigen</button>
</form>
{% if wochentag is not none %}
<div class="result" aria-live="polite">Wochentag von <b>{{ format_date(request.form.get('datum3', '')) }}</b>: {{ wochentag }}</div>
{% endif %}
</div>
</div>
<div class="accordion-item">
<button type="button" class="accordion-header header-kw-datum" id="accordion-header-2" aria-expanded="false" aria-controls="accordion-panel-2" role="button" tabindex="0" onclick="openAccordion(2)">
<span style="vertical-align:middle;display:inline-block;width:1.5em;">
<!-- Kalender mit # -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="16" rx="4" fill="#fff" stroke="#2563eb" stroke-width="2"/><rect x="3" y="5" width="18" height="4" rx="2" fill="#2563eb"/><rect x="6" y="2" width="2" height="4" rx="1" fill="#2563eb"/><rect x="16" y="2" width="2" height="4" rx="1" fill="#2563eb"/><text x="12" y="17" text-anchor="middle" font-size="13" font-family="Segoe UI, Arial, sans-serif" fill="#2563eb" font-weight="bold">#</text></svg>
</span>
Kalenderwoche eines Datums
</button>
<div class="accordion-content" id="accordion-panel-2" role="region" aria-labelledby="accordion-header-2">
<form method="post">
<label for="datum6">Datum:<br>
<span class="date-row">
<input type="date" name="datum6" id="datum6">
<button type="button" class="today-btn" onclick="setToday('datum6')">Heute</button>
</span>
</label>
<button name="action" value="kw_berechnen" type="submit">Kalenderwoche berechnen</button>
</form>
{% if kw_berechnen is not none %}
<div class="result" aria-live="polite">Kalenderwoche von <b>{{ format_date(request.form.get('datum6', '')) }}</b>: {{ kw_berechnen }}</div>
{% endif %}
</div>
</div>
<div class="accordion-item">
<button type="button" class="accordion-header header-kw" id="accordion-header-3" aria-expanded="false" aria-controls="accordion-panel-3" role="button" tabindex="0" onclick="openAccordion(3)">
<span style="vertical-align:middle;display:inline-block;width:1.5em;">
<!-- Kalender mit Pfeil nach außen -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="16" rx="4" fill="#fff" stroke="#2563eb" stroke-width="2"/><rect x="3" y="5" width="18" height="4" rx="2" fill="#2563eb"/><rect x="6" y="2" width="2" height="4" rx="1" fill="#2563eb"/><rect x="16" y="2" width="2" height="4" rx="1" fill="#2563eb"/><path d="M7 17l5-5 5 5" stroke="#2563eb" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><text x="12" y="12" text-anchor="middle" font-size="8" font-family="Segoe UI, Arial, sans-serif" fill="#2563eb" font-weight="bold">KW</text></svg>
</span>
Start-/Enddatum zu Kalenderwoche
</button>
<div class="accordion-content" id="accordion-panel-3" role="region" aria-labelledby="accordion-header-3">
<form method="post">
<label for="jahr7">Jahr:<br>
<input type="number" name="jahr7" id="jahr7" min="1900" max="2100" style="width: 7em;">
</label>
<label for="kw7">Kalenderwoche:<br>
<input type="number" name="kw7" id="kw7" min="1" max="53" style="width: 5em;">
</label>
<button name="action" value="kw_datum" type="submit">Start-/Enddatum berechnen</button>
</form>
{% if kw_datum is not none %}
<div class="result" aria-live="polite">Start-/Enddatum der KW <b>{{ request.form.get('kw7', '') }}</b> im Jahr <b>{{ request.form.get('jahr7', '') }}</b>: {{ kw_datum }}</div>
{% endif %}
</div>
</div>
<div class="accordion-item">
<button type="button" class="accordion-header header-plusminus" id="accordion-header-4" aria-expanded="false" aria-controls="accordion-panel-4" role="button" tabindex="0" onclick="openAccordion(4)">
<span style="vertical-align:middle;display:inline-block;width:1.5em;">
<!-- Kalender mit ± -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="16" rx="4" fill="#fff" stroke="#2563eb" stroke-width="2"/><rect x="3" y="5" width="18" height="4" rx="2" fill="#2563eb"/><rect x="6" y="2" width="2" height="4" rx="1" fill="#2563eb"/><rect x="16" y="2" width="2" height="4" rx="1" fill="#2563eb"/><text x="12" y="17" text-anchor="middle" font-size="16" font-family="Segoe UI, Arial, sans-serif" fill="#2563eb" font-weight="bold">±</text></svg>
</span>
Datum plus/minus X Tage/Wochen/Monate
</button>
<div class="accordion-content" id="accordion-panel-4" role="region" aria-labelledby="accordion-header-4">
<form method="post">
<label for="datum_pm">Datum:<br>
<span class="date-row">
<input type="date" name="datum_pm" id="datum_pm">
<button type="button" class="today-btn" onclick="setToday('datum_pm')">Heute</button>
</span>
</label>
<label for="anzahl_pm">Anzahl:<br>
<input type="number" name="anzahl_pm" id="anzahl_pm" style="width: 6em;">
</label>
<fieldset class="date-calc-row" style="border:none; padding:0;">
<legend class="sr-only">Rechenrichtung</legend>
<label for="richtung_pm_add"><input type="radio" name="richtung_pm" id="richtung_pm_add" value="add" checked> addieren</label>
<label for="richtung_pm_sub"><input type="radio" name="richtung_pm" id="richtung_pm_sub" value="sub"> subtrahieren</label>
</fieldset>
<fieldset style="display:flex; align-items:center; gap:0.5em; margin-top:0.7em; border:none; padding:0;">
<legend class="sr-only">Einheit und Werktage</legend>
<label for="einheit_pm" style="margin:0;">Einheit:
<select name="einheit_pm" id="einheit_pm">
<option value="tage">Tage</option>
<option value="wochen">Wochen</option>
<option value="monate">Monate</option>
</select>
</label>
<input type="checkbox" name="werktage_pm" id="werktage_pm">
<label for="werktage_pm" style="margin:0;">Nur Werktage</label>
</fieldset>
<button name="action" value="plusminus" type="submit">Berechnen</button>
</form>
{% if plusminus_result is not none %}
<div class="result" aria-live="polite">{{ plusminus_result }}</div>
{% endif %}
</div>
</div>
</div>
</div>
<!-- Help Modal Overlay -->
<div id="helpModal" class="modal-overlay" role="dialog" aria-labelledby="help-title" aria-describedby="help-content">
<div class="modal-content">
<button type="button" class="modal-close" onclick="hideHelp()" aria-label="Hilfe schließen">&times;</button>
<h1 id="help-title">Was ist Elpatrons Datumsrechner?</h1>
<p>Der Datumsrechner kann verschiedene Datumsberechnungen durchführen:</p>
<ul>
<li>Anzahl der Tage zwischen zwei Daten</li>
<li>Anzahl der Werktage zwischen zwei Daten</li>
<li>Anzeige des Wochentags eines Datums</li>
<li>Datum plus/minus X Tage</li>
<li>Datum plus/minus X Werktage</li>
<li>Datum plus/minus X Wochen/Monate</li>
<li>Kalenderwoche zu Datum</li>
<li>Start-/Enddatum einer Kalenderwoche eines Jahres</li>
</ul>
<h2>Online Datumsrechner gibt es bereits in einer Vielzahl, warum also noch einer?</h2>
<p>Aus zwei Gründen:</p>
<ul>
<li>Finde mal einen Datumsrechner, der nicht vollkommen verseucht mit Werbung, Tracking und Cookies ist!</li>
<li>Das hat mich so geärgert, dass ich meinen eigenen programmiert habe.
<ul>
<li>Genau genommen nicht ich selbst. Diese App wurde zum überwiegenden Teil von KI nach meinen Anweisungen entwickelt (Vibe Coding).</li>
</ul>
</li>
</ul>
<h2>Was du noch wissen solltest</h2>
<ul>
<li>Ich habe versucht, die App möglichst barrierefrei zu gestalten, um Menschen mit Einschränkungen die Benutzung zu erleichtern.</li>
<li>Diese App schnüffelt dir nicht hinterher, sammelt keine persönlichen Daten und geht dir auch sonst (hoffentlich!) in keiner Weise auf die Nerven.</li>
<li>Den Quellcode dieser App habe ich auf <a href="https://codeberg.org/elpatron/datecalc" target="_blank">Codeberg</a> veröffentlicht, du kannst ihn einsehen, verändern oder damit deinen eigenen kleinen Datumsrechner betreiben.</li>
<li>Die App läuft auf meinem kleinen Home-Server und ist derzeit nicht für große Besucherzahlen ausgelegt.</li>
<li>Ich übernehme keine Gewähr für die Funktionalität und die Rechenergebnisse. Die KI, die das programmiert hat, übrigens auch nicht.</li>
<li>Falls du einen Fehler findest oder eine weitere Funktion wünschst, kannst du mir eine Mail schreiben (siehe Mailto Link in der Fußzeile)</li>
</ul>
<p><strong>Hab Spaß mit Elpatrons Datumsrechner! Dein M. Busche</strong></p>
</div>
<div id="help-content" class="sr-only">
Hilfe-Informationen für den Datumsrechner mit Erklärungen zu allen Funktionen
</div>
</div>
<footer style="text-align:center; margin-top:2em; color:#64748b; font-size:0.98em; padding-bottom:1.5em;">
Dies ist ein werbe- und trackingfreier <a href="https://codeberg.org/elpatron/datecalc/src/branch/main/README.md" target="_blank" style="color:#2563eb; text-decoration:underline;">Open Source Datumsrechner</a><br>
<a href="/api-docs" target="_blank" style="color:#2563eb; text-decoration:underline;">REST API Dokumentation (Swagger)</a><br>
© 2025 <a href="mailto:elpatron@mailbox.org?subject=Datumsrechner" style="color:#2563eb; text-decoration:underline;">M. Busche</a>
</footer>
</body>
</html>