Files
aufmass-web/_aufmass_web/app/templates/aufmass/list.html
T

509 lines
28 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.
{% extends "base.html" %}
{% block content %}
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
:root {
--lp-primary: #2F5496;
--lp-primary-light: #4a7bc4;
--lp-primary-dark: #1a3055;
--lp-primary-glow: rgba(47,84,150,.25);
--lp-success: #27ae60;
--lp-bg: #f0f2f8;
--lp-card-bg: rgba(255,255,255,.85);
--lp-border: rgba(0,0,0,.06);
--lp-text: #1a1a2e;
--lp-text-light: #6b7280;
--lp-radius: 14px;
--lp-shadow: 0 1px 3px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.04);
--lp-shadow-hover: 0 4px 12px rgba(47,84,150,.12), 0 8px 32px rgba(0,0,0,.06);
--lp-transition: all .35s cubic-bezier(.25,.46,.45,.94);
}
body { background: var(--lp-bg); font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; }
.lp-page { max-width: 1000px; margin: 0 auto; padding: 0 16px; }
/* Hero */
.lp-hero {
display:flex;align-items:center;justify-content:space-between;
padding:32px 0 24px;flex-wrap:wrap;gap:16px;
}
.lp-hero-left{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.lp-hero-left .proj-icon {
width:44px;height:44px;border-radius:12px;
display:flex;align-items:center;justify-content:center;
font-size:1.3rem;flex-shrink:0;
background:linear-gradient(135deg,#eef2fa,#e0e7f5);
}
.lp-hero-left h1 {
font-size:1.45rem;font-weight:700;letter-spacing:-.02em;margin:0;
background:linear-gradient(135deg,var(--lp-primary-dark),var(--lp-primary-light));
-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;
display:flex;align-items:center;gap:8px;
}
.lp-hero-left .hero-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.lp-hero-left .status-pill{
padding:3px 14px;border-radius:20px;font-size:.72rem;font-weight:600;
}
.lp-hero-left .status-pill.aktiv{
background:linear-gradient(135deg,#e8f8f0,#d0f0e0);color:#1a8a4a;
box-shadow:0 0 0 1px rgba(39,174,96,.15);
}
.lp-hero-left .status-pill.archiv{background:#f0f2f8;color:#999}
.lp-hero-left .status-pill.storniert{background:#fde8e8;color:#c0392b}
.lp-hero-right{display:flex;gap:6px;flex-wrap:wrap}
.lp-btn{
padding:8px 16px;border-radius:10px;border:1.5px solid var(--lp-border);
background:var(--lp-card-bg);backdrop-filter:blur(8px);
font-size:.78rem;font-weight:500;cursor:pointer;text-decoration:none;
transition:var(--lp-transition);color:var(--lp-text);
display:inline-flex;align-items:center;gap:5px;font-family:inherit;
box-shadow:var(--lp-shadow);white-space:nowrap;
}
.lp-btn:hover{transform:translateY(-2px);box-shadow:var(--lp-shadow-hover);border-color:rgba(47,84,150,.2)}
.lp-btn:active{transform:scale(.97)}
.lp-btn.primary{
background:linear-gradient(135deg,var(--lp-primary),var(--lp-primary-light));
color:#fff;border-color:transparent;box-shadow:0 4px 14px var(--lp-primary-glow);
}
.lp-btn.primary:hover{box-shadow:0 8px 24px var(--lp-primary-glow);}
.lp-btn.danger:hover{border-color:#e74c3c;color:#e74c3c;background:#fef2f2}
/* Main Card */
.lp-main-card{
background:var(--lp-card-bg);backdrop-filter:blur(12px);
border:1px solid var(--lp-border);border-radius:var(--lp-radius);
box-shadow:var(--lp-shadow);overflow:hidden;
animation:cardIn .45s cubic-bezier(.25,.46,.45,.94) both;
}
@keyframes cardIn{from{opacity:0;transform:translateY(20px) scale(.97)}to{opacity:1;transform:translateY(0) scale(1)}}
.lp-card-header{
display:flex;align-items:center;justify-content:space-between;
padding:16px 20px;border-bottom:1px solid var(--lp-border);flex-wrap:wrap;gap:8px;
}
.lp-card-header h2{font-size:1rem;font-weight:600;margin:0;color:var(--lp-text);display:flex;align-items:center;gap:6px}
.lp-card-body{padding:14px 20px 20px}
/* Aufmass Cards */
.lp-aufmass-grid{display:flex;flex-direction:column;gap:6px}
.lp-aufmass-card{
display:flex;align-items:center;gap:10px;
padding:10px 14px;border-radius:10px;text-decoration:none;color:var(--lp-text);
transition:var(--lp-transition);cursor:pointer;position:relative;
background:rgba(255,255,255,.5);border:1px solid var(--lp-border);
}
.lp-aufmass-card:hover{
background:#fff;border-color:rgba(47,84,150,.12);
box-shadow:0 2px 8px rgba(0,0,0,.04);transform:translateX(4px);
}
.lp-aufmass-card:active{transform:translateX(2px) scale(.99)}
.lp-aufmass-card .row-dot{
width:8px;height:8px;border-radius:50%;flex-shrink:0;
background:var(--lp-primary);opacity:.25;transition:var(--lp-transition);
}
.lp-aufmass-card:hover .row-dot{opacity:.6;transform:scale(1.3)}
.lp-aufmass-card .a-name{font-weight:500;font-size:.85rem;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lp-aufmass-card .a-meta{font-size:.72rem;color:var(--lp-text-light);display:flex;align-items:center;gap:6px;white-space:nowrap;flex-shrink:0}
.lp-aufmass-card .a-meta .tag{
font-size:.65rem;padding:2px 10px;border-radius:10px;font-weight:500;
}
.lp-aufmass-card .a-meta .tag-aktiv{background:rgba(39,174,96,.1);color:#1a8a4a}
.lp-aufmass-card .a-meta .tag-abgeschlossen{background:rgba(47,84,150,.08);color:var(--lp-primary)}
.lp-aufmass-card .a-meta .tag-storniert{background:#fde8e8;color:#c0392b}
.lp-aufmass-card .a-actions{
display:flex;gap:2px;opacity:0;transition:opacity .25s,transform .25s;transform:translateX(-6px);
}
.lp-aufmass-card:hover .a-actions{opacity:1;transform:translateX(0)}
.lp-aufmass-card .a-actions .act-btn{
width:28px;height:28px;border:none;background:transparent;
border-radius:6px;cursor:pointer;font-size:.78rem;
transition:var(--lp-transition);display:flex;align-items:center;justify-content:center;color:#bbb;
}
.lp-aufmass-card .a-actions .act-btn:hover{background:rgba(47,84,150,.08);color:var(--lp-primary);transform:scale(1.15)}
.lp-aufmass-card .a-actions .act-btn.danger:hover{background:#fde8e8;color:#e74c3c}
@media(max-width:768px){
.lp-aufmass-card .a-actions{opacity:1}
.lp-aufmass-card{flex-wrap:wrap}
}
/* Empty */
.lp-empty{
text-align:center;padding:50px 20px;
}
.lp-empty .empty-icon{font-size:3rem;margin-bottom:10px;display:block;animation:float 3s ease-in-out infinite}
@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
.lp-empty p{color:var(--lp-text-light);font-size:.9rem;margin-bottom:16px}
/* Settings Section */
.lp-settings{
margin-top:12px;background:var(--lp-card-bg);backdrop-filter:blur(12px);
border:1px solid var(--lp-border);border-radius:var(--lp-radius);
box-shadow:var(--lp-shadow);overflow:hidden;animation:cardIn .45s both;animation-delay:.1s;
}
.lp-settings .lp-card-body{padding:20px}
.lp-settings-grid{display:flex;flex-direction:column;gap:14px}
.lp-setting-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.lp-setting-row label{font-size:.8rem;font-weight:500;color:var(--lp-text-light);min-width:100px}
.lp-setting-row select{
padding:6px 10px;border-radius:8px;border:1.5px solid var(--lp-border);
font-size:.78rem;background:#fff;font-family:inherit;min-width:180px;
transition:var(--lp-transition);
}
.lp-setting-row select:focus{outline:none;border-color:var(--lp-primary);box-shadow:0 0 0 3px var(--lp-primary-glow)}
.lp-setting-row .lp-btn{padding:6px 14px;font-size:.75rem}
/* Form Styles */
.lp-neu-form{display:none;margin-top:12px;animation:formSlide .35s cubic-bezier(.25,.46,.45,.94)}
@keyframes formSlide{from{opacity:0;transform:translateY(-12px) scale(.97)}to{opacity:1;transform:translateY(0) scale(1)}}
.lp-form-wrap{
background:linear-gradient(135deg,#f8f9fd,#f0f2f8);
border-radius:12px;padding:20px;border:1px solid rgba(47,84,150,.08);
}
.lp-form-wrap .form-card{
background:#fff;border-radius:10px;padding:16px;margin-bottom:12px;
box-shadow:0 1px 4px rgba(0,0,0,.04);border:1px solid var(--lp-border);
}
.lp-form-wrap .form-card h3{font-size:.82rem;font-weight:600;margin-bottom:10px;color:var(--lp-primary-dark);display:flex;align-items:center;gap:6px}
.lp-form-wrap .form-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:10px}
.lp-form-wrap .form-field label{display:block;font-size:.7rem;font-weight:500;color:var(--lp-text-light);margin-bottom:3px}
.lp-form-wrap .form-field .input,.lp-form-wrap .form-field select{
width:100%;border-radius:8px;border:1.5px solid var(--lp-border);
padding:6px 10px;font-size:.8rem;font-family:inherit;
transition:var(--lp-transition);background:#fff;
}
.lp-form-wrap .form-field .input:focus,.lp-form-wrap .form-field select:focus{
outline:none;border-color:var(--lp-primary);box-shadow:0 0 0 3px var(--lp-primary-glow);
}
.lp-form-wrap .form-field-full{grid-column:1/-1}
.lp-form-wrap .form-footer{display:flex;justify-content:space-between;align-items:center;margin-top:12px}
/* Toast reuse */
.toast-container{position:fixed;bottom:30px;right:30px;z-index:9999;display:flex;flex-direction:column;gap:8px}
.toast{padding:14px 22px;border-radius:12px;color:#fff;font-size:.85rem;font-weight:500;box-shadow:0 8px 32px rgba(0,0,0,.15);animation:toastIn .4s cubic-bezier(.34,1.56,.64,1);display:flex;align-items:center;gap:8px;backdrop-filter:blur(12px);cursor:pointer;transition:opacity .3s,transform .3s;font-family:'Inter',sans-serif}
.toast:hover{transform:scale(1.03)}
.toast.toast-success{background:linear-gradient(135deg,#27ae60,#2ecc71)}
.toast.toast-error{background:linear-gradient(135deg,#e74c3c,#f06050)}
.toast.toast-info{background:linear-gradient(135deg,var(--lp-primary),var(--lp-primary-light))}
@keyframes toastIn{from{opacity:0;transform:translateX(40px) scale(.9)}to{opacity:1;transform:translateX(0) scale(1)}}
/* Inline edit */
.inline-edit-input{
border:2px solid var(--lp-primary);border-radius:8px;padding:4px 10px;
font-size:.82rem;font-family:inherit;background:#fff;
box-shadow:0 0 0 3px var(--lp-primary-glow);width:100%;
}
.inline-edit-input:focus{outline:none}
</style>
<div class="lp-page">
<!-- Hero -->
<div class="lp-hero">
<div class="lp-hero-left">
<span class="proj-icon">📁</span>
<div>
<h1 class="js-name-ctnr">
<span class="js-projekt-name">{{ project.bezeichnung or project.sm_nr }}</span>
<span class="js-name-edit-trigger" style="font-size:.65rem;cursor:pointer;opacity:0;transition:opacity .2s;padding:2px 6px;border-radius:4px;" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='0'" onclick="event.stopPropagation();projectNameEdit(this)"></span>
<span class="js-name-edit-form" style="display:none;font-size:.85rem">
<input type="text" value="{{ project.bezeichnung or project.sm_nr or '' }}" style="border:2px solid var(--lp-primary);border-radius:8px;padding:4px 10px;font-size:.82rem;font-family:inherit;width:220px;box-shadow:0 0 0 3px var(--lp-primary-glow)">
<button class="mini-btn" style="width:26px;height:26px;border-radius:6px;border:none;background:#27ae60;color:#fff;cursor:pointer;font-size:.65rem;transition:all .2s" onclick="projectNameSave(this)"></button>
<button class="mini-btn" style="width:26px;height:26px;border-radius:6px;border:none;background:#f0f2f8;color:#888;cursor:pointer;font-size:.65rem;transition:all .2s" onclick="projectNameCancel(this)"></button>
</span>
</h1>
<div class="hero-meta">
<span class="status-pill {{ project.status }}">{{ project.status }}</span>
<span style="font-size:.75rem;color:var(--lp-text-light)">{{ project.sm_nr or '' }}</span>
</div>
</div>
</div>
<div class="lp-hero-right">
<a class="lp-btn" href="{{ url_for('export.excel', project_id=project.id) }}">📊 Excel</a>
<a class="lp-btn" href="{{ url_for('export.pdf', project_id=project.id) }}">📄 PDF</a>
{% if current_user.is_firmadmin() or current_user.is_superadmin() %}
<a class="lp-btn" href="{{ url_for('aufmass.typen_liste') }}">🏷 Typen</a>
{% endif %}
<a class="lp-btn" href="{{ url_for('aufmass.index') }}">← Übersicht</a>
</div>
</div>
<!-- Main Card -->
<div class="lp-main-card">
<div class="lp-card-header">
<h2>📋 Aufmaße</h2>
<div style="display:flex;gap:6px;flex-wrap:wrap">
<button class="lp-btn primary js-aufmass-neu-btn">+ Neues Aufmaß</button>
<button class="lp-btn" onclick="document.getElementById('import-file').click()">📥 Import</button>
<form method="POST" action="{{ url_for('aufmass.aufmass_import', project_id=project.id) }}" enctype="multipart/form-data" style="display:none">
<input type="file" name="file" accept=".txt" id="import-file" onchange="this.form.submit()">
</form>
</div>
</div>
<div class="lp-card-body">
<!-- Neu Form -->
<div class="lp-neu-form js-aufmass-neu-form">
<div class="lp-form-wrap">
<form method="POST" action="{{ url_for('aufmass.aufmass_neu_voll', project_id=project.id) }}">
<input type="hidden" name="ev_details_id" value="{{ project.ev_details_id or '' }}">
<input type="hidden" name="name" id="aufmass-name-{{ project.id }}">
<div class="form-card"><h3>Basisdaten</h3>
<div class="form-grid">
<div class="form-field"><label>Vertrag</label><select name="contract_id"><option value=""> Kein Vertrag </option>{% for c in contracts %}<option value="{{ c.id }}" {{ 'selected' if project.contract_id == c.id }}>{{ c.name }}</option>{% endfor %}</select></div>
<div class="form-field"><label>LV-Name</label><input class="input" name="lv_name" value="{{ project.lv_name or '' }}"></div>
<div class="form-field"><label>Typ</label><select name="typ"><option value=""> Typ wählen </option>{% for t in typen %}<option value="{{ t.name }}">{{ t.name }}</option>{% endfor %}</select></div>
<div class="form-field"><label>Aufmaß-Datum</label><input class="input" name="datum" type="date" value="{{ project.datum or '' }}"></div>
</div>
<div class="form-field form-field-full" style="margin-top:8px"><label>Projekt</label><input class="input js-aufmass-auto-name js-validate-name" name="bezeichnung" value=""><span class="js-name-warn" style="display:none;font-size:.7rem;color:#e74c3c">Ungültige Zeichen</span></div>
<div class="form-field form-field-full"><label>Baustelle</label><input class="input" name="baustelle" value="{{ project.baustelle or '' }}"></div>
<div class="form-field form-field-full"><label>Bauabschnitt</label><input class="input js-aufmass-auto-name js-validate-name" name="bauabschnitt" value="{{ project.bauabschnitt or '' }}"></div>
</div>
<div class="form-card"><h3>🕐 Zeitraum & Referenz</h3>
<div class="form-grid">
<div class="form-field"><label>SM-Nr.</label><input class="input js-aufmass-auto-name" name="sm_nr" value="{{ project.sm_nr or '' }}"></div>
<div class="form-field"><label>Abruf-Nr.</label><input class="input js-aufmass-auto-name" name="abruf_nr" value="{{ project.abruf_nr or '' }}"></div>
<div class="form-field"><label>Startdatum</label><input class="input" name="datum_start" type="date" value="{{ project.datum_start or '' }}"></div>
<div class="form-field"><label>Enddatum</label><input class="input" name="datum_ende" type="date" value="{{ project.datum_ende or '' }}"></div>
</div>
{% if current_user.darf_evergabe_nutzen and current_user.darf_kopfdaten_holen and company.evergabe_aktiviert and company.evergabe_benutzer and company.evergabe_passwort %}
<button class="lp-btn" type="button" style="margin-top:8px" onclick="kopfdatenHolen({{ project.id }})">⬇️ Kopfdaten EV holen</button>
{% endif %}
</div>
<div class="form-card"><h3>👤 Ansprechpartner</h3>
<div class="form-grid">
<div class="form-field"><label>Vorname</label><input class="input" name="ansprechpartner_vorname" value="{{ project.ansprechpartner_vorname or '' }}"></div>
<div class="form-field"><label>Nachname</label><input class="input" name="ansprechpartner_nachname" value="{{ project.ansprechpartner_nachname or '' }}"></div>
<div class="form-field"><label>Telefon</label><input class="input" name="ansprechpartner_tel" value="{{ project.ansprechpartner_tel or '' }}"></div>
<div class="form-field"><label>Email</label><input class="input" name="ansprechpartner_email" value="{{ project.ansprechpartner_email or '' }}"></div>
</div>
</div>
<div class="form-footer">
<button class="lp-btn" type="button" onclick="this.closest('.lp-neu-form').style.display='none'">Abbrechen</button>
<div style="display:flex;gap:8px">
<button class="lp-btn js-aufmass-neu-reset" type="button">🗑️ Zurücksetzen</button>
<button class="lp-btn primary" type="submit">Aufmaß anlegen</button>
</div>
</div>
</form>
</div>
</div>
{% if aufmass_liste %}
<div class="lp-aufmass-grid">
{% for a in aufmass_liste %}
<div class="lp-aufmass-card" data-id="{{ a.id }}" data-name="{{ a.name }}" data-typ="{{ a.typ }}" data-status="{{ a.status }}">
<span class="row-dot"></span>
<span class="a-name" data-field="name">{{ a.name }}</span>
<span class="a-meta">
{% if a.typ %}<span class="tag" style="background:rgba(240,192,64,.12);color:#b8941a">{{ a.typ }}</span>{% endif %}
<span class="tag tag-{{ a.status }}">{{ a.status }}</span>
<span>{{ a.positionen.count() }} Pos.</span>
{% if preise_sichtbar %}<span>{{ aufmass_preise.get(a.id, 0)|german_number }} €</span>{% endif %}
</span>
<span class="a-actions">
<button class="act-btn js-edit-btn" title="Bearbeiten"></button>
<a class="act-btn" href="{{ url_for('aufmass.bearbeiten', project_id=project.id, aufmass_id=a.id) }}" title="Öffnen"></a>
<form method="POST" action="{{ url_for('aufmass.aufmass_duplizieren', project_id=project.id, aufmass_id=a.id) }}" style="display:inline" onclick="event.stopPropagation()">
<button class="act-btn" title="Duplizieren">📋</button>
</form>
<form method="POST" action="{{ url_for('aufmass.aufmass_loeschen', project_id=project.id, aufmass_id=a.id) }}" style="display:inline" onsubmit="return confirm('Aufmaß wirklich löschen?')" onclick="event.stopPropagation()">
<button class="act-btn danger" title="Löschen"></button>
</form>
</span>
</div>
{% endfor %}
</div>
{% else %}
<div class="lp-empty">
<span class="empty-icon">📋</span>
<p>Noch keine Aufmaße vorhanden.</p>
<button class="lp-btn primary js-aufmass-neu-btn">+ Erstes Aufmaß anlegen</button>
</div>
{% endif %}
</div>
</div>
<!-- Settings -->
<div class="lp-settings">
<div class="lp-card-header"><h2>⚙ Projekt-Einstellungen</h2></div>
<div class="lp-card-body">
<div class="lp-settings-grid">
<div class="lp-setting-row">
<label>Vertrag & LV</label>
<form method="POST" action="{{ url_for('aufmass.project_lv_set', project_id=project.id) }}" style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
<select name="contract_id" id="settings-contract-select" onchange="loadSettingsLV(this.value)">
<option value=""> Kein Vertrag </option>
{% for c in contracts %}
<option value="{{ c.id }}" {{ 'selected' if project.contract_id == c.id }}>{{ c.name }}</option>
{% endfor %}
</select>
<select name="lv_name">
<option value=""> LV wählen </option>
{% for n in lv_names %}
<option value="{{ n }}" {{ 'selected' if project.lv_name == n }}>{{ n }}</option>
{% endfor %}
</select>
<button class="lp-btn primary" style="padding:6px 14px;font-size:.75rem">Speichern</button>
</form>
</div>
<div class="lp-setting-row">
<label>Status</label>
<form method="POST" action="{{ url_for('aufmass.status_aendern', project_id=project.id) }}" style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
<select name="status">
<option value="aktiv" {{ 'selected' if project.status == 'aktiv' }}>Aktiv</option>
<option value="abgeschlossen" {{ 'selected' if project.status == 'abgeschlossen' }}>Abgeschlossen</option>
<option value="storniert" {{ 'selected' if project.status == 'storniert' }}>Storniert</option>
</select>
<button class="lp-btn" style="padding:6px 14px;font-size:.75rem">Status ändern</button>
</form>
</div>
<div class="lp-setting-row" style="justify-content:flex-end">
<form method="POST" action="{{ url_for('aufmass.project_loeschen', project_id=project.id) }}" onsubmit="return confirm('WIRKLICH das ganze Projekt löschen?')">
<button class="lp-btn danger" style="padding:6px 14px;font-size:.75rem">Projekt löschen</button>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="toast-container" id="toast-container"></div>
<script>
/* Toast */
function showToast(msg,type){
type=type||'success';
var c=document.getElementById('toast-container');
var t=document.createElement('div');t.className='toast toast-'+type;
var icons={success:'✓',error:'✕',info:''};
t.innerHTML=(icons[type]||'')+' '+msg;
c.appendChild(t);
setTimeout(function(){t.style.opacity='0';t.style.transform='translateX(40px) scale(.9)';setTimeout(function(){if(t.parentNode)t.remove()},400)},3000);
t.addEventListener('click',function(){t.style.opacity='0';t.style.transform='translateX(40px) scale(.9)';setTimeout(function(){if(t.parentNode)t.remove()},400)});
}
/* Flash toasts */
(function(){
document.querySelectorAll('.notification').forEach(function(n){
var msg=n.textContent.trim();var cat='info';
if(n.classList.contains('is-success'))cat='success';
else if(n.classList.contains('is-danger'))cat='error';
showToast(msg,cat);n.style.display='none';
});
})();
/* Toggle neu form */
document.querySelectorAll('.js-aufmass-neu-btn').forEach(function(btn){
btn.addEventListener('click',function(e){
e.preventDefault();
var f=document.querySelector('.js-aufmass-neu-form');
if(f)f.style.display=f.style.display==='none'?'block':'none';
});
});
/* Auto name */
function updateAufmassName(pid){
var nameInput=document.getElementById('aufmass-name-'+pid);
if(!nameInput)return;
var f=nameInput.closest('form');
var parts=[f.querySelector('[name="bezeichnung"]').value.trim(),f.querySelector('[name="bauabschnitt"]').value.trim(),f.querySelector('[name="sm_nr"]').value.trim(),f.querySelector('[name="abruf_nr"]').value.trim()].filter(Boolean);
nameInput.value=parts.join(' - ').replace(/[<>:"\/\\|?*&#%{}~\[\]]/g,'').replace(/\s+/g,' ').trim();
}
document.querySelectorAll('.js-aufmass-auto-name').forEach(function(inp){
inp.addEventListener('input',function(){var f=inp.closest('form');var ni=f.querySelector('[name="name"]');if(ni)updateAufmassName(ni.id.replace('aufmass-name-',''));});
});
document.querySelectorAll('.js-validate-name').forEach(function(inp){
inp.addEventListener('input',function(){
var warn=inp.closest('.lp-form-wrap').querySelector('.js-name-warn');
if(warn)warn.style.display=/[<>:"\/\\|?*&#%{}~\[\]]/.test(inp.value)?'block':'none';
});
});
/* Reset */
document.querySelector('.js-aufmass-neu-reset')?.addEventListener('click',function(e){
e.preventDefault();
var rf=this.closest('form');
if(rf){rf.querySelectorAll('input[name]:not([type=hidden])').forEach(function(i){i.value=''});updateAufmassName({{ project.id }});}
});
/* Inline edit aufmass */
document.querySelector('.lp-aufmass-grid')?.addEventListener('click',function(e){
var btn=e.target.closest('.js-edit-btn');
if(!btn)return;
e.stopPropagation();
var card=btn.closest('.lp-aufmass-card');
var nameEl=card.querySelector('.a-name');
var old=nameEl.textContent.trim();
var inp=document.createElement('input');
inp.className='inline-edit-input';inp.value=old;
nameEl.style.display='none';
nameEl.parentNode.insertBefore(inp,nameEl);
inp.focus();
inp.addEventListener('blur',save);
inp.addEventListener('keydown',function(ev){
if(ev.key==='Enter'){ev.preventDefault();save()}
if(ev.key==='Escape'){ev.preventDefault();inp.remove();nameEl.style.display=''}
});
function save(){
var v=inp.value.trim();if(!v){inp.remove();nameEl.style.display='';return}
nameEl.textContent=v;inp.remove();nameEl.style.display='';
fetch('/projekt/'+card.closest('[data-project-id]')?.dataset.projectId||{{ project.id }}+'/aufmass/'+card.dataset.id+'/umbenennen',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:v})})
.then(function(r){if(!r.ok)throw new Error('Fehler');showToast('Aufmaß umbenannt','success')})
.catch(function(){nameEl.textContent=old;showToast('Fehler beim Umbenennen','error')});
}
});
/* Projekt name inline edit */
function projectNameEdit(btn){
var c=btn.closest('.js-name-ctnr');
c.querySelector('.js-projekt-name').style.display='none';
btn.style.display='none';
c.querySelector('.js-name-edit-form').style.display='inline';
c.querySelector('.js-name-edit-form input').focus();
}
function projectNameSave(btn){
var c=btn.closest('.js-name-ctnr'),i=c.querySelector('input'),n=i.value;
fetch('/projekt/{{ project.id }}/update-name',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'name='+encodeURIComponent(n)})
.then(function(r){if(!r.ok)return r.json().then(function(e){throw new Error(e.error)});return r.json()})
.then(function(dt){c.querySelector('.js-projekt-name').textContent=dt.name;c.querySelector('.js-projekt-name').style.display='';btn.style.display='';c.querySelector('.js-name-edit-form').style.display='none';showToast('Projekt umbenannt','success')})
.catch(function(e){alert('Fehler: '+e.message)});
}
function projectNameCancel(btn){
var c=btn.closest('.js-name-ctnr');
c.querySelector('.js-projekt-name').style.display='';
btn.style.display='';
c.querySelector('.js-name-edit-form').style.display='none';
}
/* Load LV names */
function loadSettingsLV(contractId){
var url=contractId?'/contracts/api/lv-names?contract_id='+contractId:'/contracts/api/lv-names';
fetch(url).then(function(r){return r.json()}).then(function(names){
var sel=document.querySelector('[name="lv_name"]');
var current=sel.value;
sel.innerHTML='<option value=""> LV wählen </option>';
names.forEach(function(n){sel.innerHTML+='<option value="'+n.replace(/"/g,'&quot;')+'">'+n+'</option>'});
sel.value=current;
});
}
/* Kopfdaten holen */
function kopfdatenHolen(pid){
var form=document.querySelector('.lp-neu-form form');
if(!form)return;
var smNr=form.querySelector('[name="sm_nr"]').value.trim();
if(!smNr){alert('Bitte SM-Nr eingeben.');return}
var btn=form.querySelector('[onclick*="kopfdatenHolen"]');if(btn){btn.disabled=true;btn.textContent='Laden...'}
fetch('/projekt/'+pid+'/kopfdaten-ev-holen',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sm_nr:smNr})})
.then(function(r){return r.json()})
.then(function(data){
if(btn){btn.disabled=false;btn.textContent='⬇️ Kopfdaten EV holen'}
if(data.error){alert(data.error);return}
['bauabschnitt','sm_nr','abruf_nr','datum_start','datum_ende','datum','ev_details_id','ansprechpartner_vorname','ansprechpartner_nachname','ansprechpartner_tel','ansprechpartner_email'].forEach(function(f){
var inp=form.querySelector('[name="'+f+'"]');
if(inp&&data[f])inp.value=data[f];
});
updateAufmassName(pid);
}).catch(function(err){if(btn){btn.disabled=false;btn.textContent='⬇️ Kopfdaten EV holen'}alert('Fehler: '+err)});
}
</script>
{% endblock %}