Kontakt-Modul
Dieser Commit ist enthalten in:
committet von
Server Deploy
Ursprung
623bbdf5dd
Commit
7d67557be4
@ -27,6 +27,8 @@
|
||||
<link rel="stylesheet" href="css/gitea.css">
|
||||
<link rel="stylesheet" href="css/coding.css">
|
||||
<link rel="stylesheet" href="css/knowledge.css">
|
||||
<link rel="stylesheet" href="css/reminders.css">
|
||||
<link rel="stylesheet" href="css/contacts.css">
|
||||
<link rel="stylesheet" href="css/responsive.css">
|
||||
<link rel="stylesheet" href="css/mobile.css">
|
||||
|
||||
@ -43,7 +45,7 @@
|
||||
<p>Melden Sie sich an, um fortzufahren</p>
|
||||
</div>
|
||||
|
||||
<form id="login-form" class="login-form">
|
||||
<form id="login-form" class="login-form" method="POST" action="/api/auth/login" onsubmit="return false;">
|
||||
<div class="form-group">
|
||||
<label for="login-username">E-Mail</label>
|
||||
<input type="text" id="login-username" name="username" required autocomplete="email" autofocus placeholder="benutzer@beispiel.de">
|
||||
@ -177,6 +179,7 @@
|
||||
<button class="view-tab" data-view="proposals">Genehmigung</button>
|
||||
<button class="view-tab" data-view="coding">Coding</button>
|
||||
<button class="view-tab" data-view="knowledge">Wissen</button>
|
||||
<button class="view-tab" data-view="contacts">Kontakte</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@ -402,6 +405,10 @@
|
||||
<!-- Dynamisch gefüllt -->
|
||||
</div>
|
||||
</div>
|
||||
<button id="btn-new-reminder" class="btn btn-secondary btn-reminder" title="Erinnerung hinzufügen">
|
||||
<svg class="icon" viewBox="0 0 24 24"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9M13.73 21a2 2 0 0 1-3.46 0" stroke="currentColor" stroke-width="2" fill="none"/></svg>
|
||||
Erinnerung
|
||||
</button>
|
||||
<button id="btn-today" class="btn btn-secondary">Heute</button>
|
||||
<div class="calendar-view-toggle">
|
||||
<button class="btn btn-toggle active" data-calendar-view="month">Monat</button>
|
||||
@ -504,6 +511,8 @@
|
||||
</svg>
|
||||
<p>Keine Kategorien</p>
|
||||
</div>
|
||||
<!-- Resize Handle -->
|
||||
<div id="knowledge-resize-handle" class="knowledge-resize-handle"></div>
|
||||
</aside>
|
||||
|
||||
<!-- Hauptbereich (rechts) - Einträge -->
|
||||
@ -568,6 +577,47 @@
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Contacts View -->
|
||||
<div id="view-contacts" class="view view-contacts hidden">
|
||||
<div class="contacts-header">
|
||||
<h2>Kontakte</h2>
|
||||
<button id="btn-new-contact" class="btn btn-primary">
|
||||
<i class="fas fa-plus"></i>
|
||||
Neuer Kontakt
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="contacts-controls">
|
||||
<div class="contacts-filters">
|
||||
<select id="contacts-tag-filter">
|
||||
<option value="">Alle Tags</option>
|
||||
</select>
|
||||
<select id="contacts-sort">
|
||||
<option value="created_at-desc">Neueste zuerst</option>
|
||||
<option value="created_at-asc">Älteste zuerst</option>
|
||||
<option value="name-asc">Name (A-Z)</option>
|
||||
<option value="name-desc">Name (Z-A)</option>
|
||||
<option value="company-asc">Firma (A-Z)</option>
|
||||
<option value="company-desc">Firma (Z-A)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="contacts-grid" class="contacts-grid">
|
||||
<!-- Contact cards will be rendered here -->
|
||||
</div>
|
||||
|
||||
<div id="contacts-empty" class="contacts-empty hidden">
|
||||
<i class="fas fa-address-book"></i>
|
||||
<h3>Keine Kontakte vorhanden</h3>
|
||||
<p>Erstellen Sie Ihren ersten Kontakt.</p>
|
||||
<button class="btn btn-primary" onclick="document.getElementById('btn-new-contact').click()">
|
||||
<i class="fas fa-plus"></i>
|
||||
Ersten Kontakt erstellen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@ -1579,6 +1629,103 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contact Modal -->
|
||||
<div id="contact-modal" class="modal modal-medium hidden">
|
||||
<div class="modal-header">
|
||||
<h2 id="contact-modal-title">Neuer Kontakt</h2>
|
||||
<button class="modal-close" data-close-modal>×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="contact-form">
|
||||
<input type="hidden" id="contact-id" />
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="contact-first-name">Vorname</label>
|
||||
<input type="text" id="contact-first-name" placeholder="Max" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="contact-last-name">Nachname</label>
|
||||
<input type="text" id="contact-last-name" placeholder="Mustermann" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="contact-company">Firma</label>
|
||||
<input type="text" id="contact-company" placeholder="Musterfirma GmbH" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="contact-position">Position</label>
|
||||
<input type="text" id="contact-position" placeholder="Geschäftsführer" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="contact-email">E-Mail</label>
|
||||
<input type="email" id="contact-email" placeholder="max@musterfirma.de" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="contact-website">Website</label>
|
||||
<input type="url" id="contact-website" placeholder="https://www.musterfirma.de" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="contact-phone">Telefon</label>
|
||||
<input type="tel" id="contact-phone" placeholder="+49 30 12345678" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="contact-mobile">Mobil</label>
|
||||
<input type="tel" id="contact-mobile" placeholder="+49 170 1234567" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group full-width">
|
||||
<label for="contact-address">Adresse</label>
|
||||
<input type="text" id="contact-address" placeholder="Musterstraße 123" />
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="contact-postal-code">PLZ</label>
|
||||
<input type="text" id="contact-postal-code" placeholder="12345" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="contact-city">Stadt</label>
|
||||
<input type="text" id="contact-city" placeholder="Berlin" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="contact-country">Land</label>
|
||||
<input type="text" id="contact-country" placeholder="Deutschland" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group full-width">
|
||||
<label for="contact-notes">Notizen</label>
|
||||
<textarea id="contact-notes" rows="3" placeholder="Zusätzliche Informationen..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group full-width">
|
||||
<label for="contact-tags">Tags</label>
|
||||
<input type="text" id="contact-tags" placeholder="Kunde, Partner, Lieferant" />
|
||||
<div class="tags-help">Mehrere Tags mit Komma trennen</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" id="btn-delete-contact" class="btn btn-danger hidden">
|
||||
<i class="fas fa-trash"></i>
|
||||
Löschen
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary modal-cancel">Abbrechen</button>
|
||||
<button type="submit" form="contact-form" class="btn btn-primary">Speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toast Container -->
|
||||
<div id="toast-container" class="toast-container"></div>
|
||||
|
||||
@ -1627,6 +1774,10 @@
|
||||
<svg viewBox="0 0 24 24" width="20" height="20"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20" stroke="currentColor" stroke-width="2" fill="none"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" stroke="currentColor" stroke-width="2" fill="none"/></svg>
|
||||
<span>Wissen</span>
|
||||
</button>
|
||||
<button class="mobile-nav-item" data-view="contacts">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" stroke="currentColor" stroke-width="2" fill="none"/><circle cx="8.5" cy="7" r="4" stroke="currentColor" stroke-width="2" fill="none"/><line x1="20" y1="8" x2="20" y2="14" stroke="currentColor" stroke-width="2"/><line x1="23" y1="11" x2="17" y2="11" stroke="currentColor" stroke-width="2"/></svg>
|
||||
<span>Kontakte</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1692,11 +1843,231 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Reminder Modal -->
|
||||
<div id="reminder-modal" class="modal hidden">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 id="reminder-modal-title">Neue Erinnerung</h3>
|
||||
<button class="modal-close" aria-label="Schließen">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="reminder-form">
|
||||
<div class="form-group">
|
||||
<label for="reminder-title">Titel *</label>
|
||||
<input type="text" id="reminder-title" class="form-control" required maxlength="255" placeholder="z.B. Meeting mit Kunde">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="reminder-description">Beschreibung</label>
|
||||
<textarea id="reminder-description" class="form-control" rows="3" placeholder="Optionale Beschreibung..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="reminder-assignee">Zugewiesen an</label>
|
||||
<div class="custom-select" id="reminder-assignee-wrapper">
|
||||
<div class="custom-select-trigger" id="reminder-assignee-trigger">
|
||||
<span class="custom-select-value">Alle Benutzer</span>
|
||||
<svg class="custom-select-arrow" viewBox="0 0 24 24" width="16" height="16">
|
||||
<path d="M6 9l6 6 6-6" stroke="currentColor" stroke-width="2" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="custom-select-options" id="reminder-assignee-options">
|
||||
<div class="custom-select-option" data-value="">
|
||||
<span class="option-text">Alle Benutzer</span>
|
||||
</div>
|
||||
<!-- Wird dynamisch gefüllt -->
|
||||
</div>
|
||||
<input type="hidden" id="reminder-assignee" name="reminder-assignee" value="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="reminder-date">Datum *</label>
|
||||
<input type="date" id="reminder-date" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reminder-time">Uhrzeit</label>
|
||||
<input type="time" id="reminder-time" class="form-control" value="09:00">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="reminder-color">Farbe</label>
|
||||
<div class="color-picker-wrapper">
|
||||
<div class="color-picker-trigger" id="color-picker-trigger" style="background-color: #F59E0B">
|
||||
<svg class="color-picker-icon" viewBox="0 0 24 24" width="16" height="16">
|
||||
<path d="M6 9l6 6 6-6" stroke="currentColor" stroke-width="2" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="color-picker-dropdown hidden" id="color-picker-dropdown">
|
||||
<div class="color-option" data-color="#F59E0B" style="background: #F59E0B" title="Amber"></div>
|
||||
<div class="color-option" data-color="#EF4444" style="background: #EF4444" title="Rot"></div>
|
||||
<div class="color-option" data-color="#10B981" style="background: #10B981" title="Grün"></div>
|
||||
<div class="color-option" data-color="#3B82F6" style="background: #3B82F6" title="Blau"></div>
|
||||
<div class="color-option" data-color="#8B5CF6" style="background: #8B5CF6" title="Lila"></div>
|
||||
<div class="color-option" data-color="#F97316" style="background: #F97316" title="Orange"></div>
|
||||
<div class="color-option" data-color="#06B6D4" style="background: #06B6D4" title="Cyan"></div>
|
||||
<div class="color-option" data-color="#84CC16" style="background: #84CC16" title="Lime"></div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" id="reminder-color" value="#F59E0B">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reminder-advance">Erinnerung</label>
|
||||
<div class="advance-options">
|
||||
<label class="checklist-item">
|
||||
<input type="checkbox" name="advance-days" value="1" checked>
|
||||
<span class="checklist-checkbox"></span>
|
||||
<span class="checklist-text">1 Tag vorher</span>
|
||||
</label>
|
||||
<label class="checklist-item">
|
||||
<input type="checkbox" name="advance-days" value="2">
|
||||
<span class="checklist-checkbox"></span>
|
||||
<span class="checklist-text">2 Tage vorher</span>
|
||||
</label>
|
||||
<label class="checklist-item">
|
||||
<input type="checkbox" name="advance-days" value="3">
|
||||
<span class="checklist-checkbox"></span>
|
||||
<span class="checklist-text">3 Tage vorher</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<div class="form-actions-left">
|
||||
<button type="button" class="btn btn-secondary" id="btn-cancel-reminder">Abbrechen</button>
|
||||
<button type="submit" class="btn btn-primary" id="btn-save-reminder">
|
||||
<span class="btn-text">Speichern</span>
|
||||
<span class="btn-loading hidden">Speichere...</span>
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" class="btn btn-danger btn-delete-reminder hidden" id="btn-delete-reminder">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16">
|
||||
<path d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2m3 0v12a2 2 0 01-2 2H7a2 2 0 01-2-2V6h14M10 11v6M14 11v6" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
Löschen
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Socket.io Client -->
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
|
||||
<!-- Main App (ES Module) -->
|
||||
<script type="module" src="js/app.js"></script>
|
||||
<script type="module" src="js/reminders.js"></script>
|
||||
<script type="module" src="js/contacts.js"></script>
|
||||
|
||||
<!-- Emergency Login Handler -->
|
||||
<script>
|
||||
console.log('[Login] Fallback handler wird geladen...');
|
||||
|
||||
function setupLoginHandler() {
|
||||
const loginForm = document.getElementById('login-form');
|
||||
const submitButton = loginForm?.querySelector('button[type="submit"]');
|
||||
|
||||
if (!loginForm) {
|
||||
console.error('[Login] Login-Formular nicht gefunden!');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[Login] Login-Formular gefunden, Handler wird eingerichtet...');
|
||||
|
||||
// Force override - entferne alle existierenden Handler
|
||||
const newForm = loginForm.cloneNode(true);
|
||||
loginForm.parentNode.replaceChild(newForm, loginForm);
|
||||
|
||||
newForm.addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
console.log('[Login] Submit Event ausgelöst');
|
||||
|
||||
const username = document.getElementById('login-username').value.trim();
|
||||
const password = document.getElementById('login-password').value;
|
||||
const errorEl = document.getElementById('login-error');
|
||||
const newSubmitButton = newForm.querySelector('button[type="submit"]');
|
||||
|
||||
console.log('[Login] Username:', username);
|
||||
|
||||
if (!username || !password) {
|
||||
errorEl.textContent = 'Bitte Benutzername und Passwort eingeben';
|
||||
errorEl.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
// Loading state
|
||||
if (newSubmitButton) {
|
||||
newSubmitButton.disabled = true;
|
||||
newSubmitButton.textContent = 'Anmelden...';
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('[Login] Sende Login-Request...');
|
||||
|
||||
const response = await fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
console.log('[Login] Response Status:', response.status);
|
||||
console.log('[Login] Response Data:', data);
|
||||
|
||||
if (response.ok && data.token) {
|
||||
console.log('[Login] Login erfolgreich!');
|
||||
localStorage.setItem('auth_token', data.token);
|
||||
localStorage.setItem('current_user', JSON.stringify(data.user));
|
||||
window.location.reload();
|
||||
} else {
|
||||
console.log('[Login] Login fehlgeschlagen:', data.error);
|
||||
errorEl.textContent = data.error || 'Anmeldung fehlgeschlagen';
|
||||
errorEl.classList.remove('hidden');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Login] Netzwerkfehler:', err);
|
||||
errorEl.textContent = 'Netzwerkfehler - bitte versuchen Sie es erneut';
|
||||
errorEl.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// Reset button
|
||||
if (newSubmitButton) {
|
||||
newSubmitButton.disabled = false;
|
||||
newSubmitButton.textContent = 'Anmelden';
|
||||
}
|
||||
});
|
||||
|
||||
// Additional click handler for button
|
||||
const newSubmitButton = newForm.querySelector('button[type="submit"]');
|
||||
if (newSubmitButton) {
|
||||
newSubmitButton.addEventListener('click', function(e) {
|
||||
console.log('[Login] Button clicked');
|
||||
// Let form submit handle it
|
||||
});
|
||||
}
|
||||
|
||||
console.log('[Login] Handler eingerichtet');
|
||||
}
|
||||
|
||||
// Try multiple times to ensure DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', setupLoginHandler);
|
||||
} else {
|
||||
setupLoginHandler();
|
||||
}
|
||||
|
||||
// Backup after 1 second
|
||||
setTimeout(setupLoginHandler, 1000);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren