Leads sind integriert
Dieser Commit ist enthalten in:
495
v2_adminpanel/leads/templates/leads/contact_detail.html
Normale Datei
495
v2_adminpanel/leads/templates/leads/contact_detail.html
Normale Datei
@@ -0,0 +1,495 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ contact.first_name }} {{ contact.last_name }} - Kontakt-Details{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<h1 class="h2 mb-0">
|
||||
<i class="bi bi-person"></i> {{ contact.first_name }} {{ contact.last_name }}
|
||||
</h1>
|
||||
<p class="mb-0">
|
||||
<span class="text-muted">{{ contact.position or 'Keine Position' }}</span>
|
||||
<span class="mx-2">•</span>
|
||||
<a href="{{ url_for('leads.institution_detail', institution_id=contact.institution_id) }}"
|
||||
class="text-decoration-none">
|
||||
{{ contact.institution_name }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-end">
|
||||
<button class="btn btn-outline-primary" onclick="editContact()">
|
||||
<i class="bi bi-pencil"></i> Bearbeiten
|
||||
</button>
|
||||
<a href="{{ url_for('leads.institution_detail', institution_id=contact.institution_id) }}"
|
||||
class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Zurück
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Contact Details -->
|
||||
<div class="col-md-6">
|
||||
<!-- Phone Numbers -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0"><i class="bi bi-telephone"></i> Telefonnummern</h5>
|
||||
<button class="btn btn-sm btn-primary" onclick="showAddPhoneModal()">
|
||||
<i class="bi bi-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if contact.phones %}
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for phone in contact.phones %}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<strong>{{ phone.detail_value }}</strong>
|
||||
{% if phone.detail_label %}
|
||||
<span class="badge bg-secondary">{{ phone.detail_label }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<button class="btn btn-sm btn-outline-danger"
|
||||
onclick="deleteDetail('{{ phone.id }}')">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-muted mb-0">Keine Telefonnummern hinterlegt.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Email Addresses -->
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0"><i class="bi bi-envelope"></i> E-Mail-Adressen</h5>
|
||||
<button class="btn btn-sm btn-primary" onclick="showAddEmailModal()">
|
||||
<i class="bi bi-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if contact.emails %}
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for email in contact.emails %}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<a href="mailto:{{ email.detail_value }}">{{ email.detail_value }}</a>
|
||||
{% if email.detail_label %}
|
||||
<span class="badge bg-secondary">{{ email.detail_label }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<button class="btn btn-sm btn-outline-danger"
|
||||
onclick="deleteDetail('{{ email.id }}')">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-muted mb-0">Keine E-Mail-Adressen hinterlegt.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notes -->
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="bi bi-journal-text"></i> Notizen</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- New Note Form -->
|
||||
<div class="mb-3">
|
||||
<textarea class="form-control" id="newNoteText" rows="3"
|
||||
placeholder="Neue Notiz hinzufügen..."></textarea>
|
||||
<button class="btn btn-primary btn-sm mt-2" onclick="addNote()">
|
||||
<i class="bi bi-plus"></i> Notiz speichern
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Notes List -->
|
||||
<div id="notesList">
|
||||
{% for note in contact.notes %}
|
||||
<div class="card mb-2" id="note-{{ note.id }}">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-clock"></i>
|
||||
{{ note.created_at.strftime('%d.%m.%Y %H:%M') }}
|
||||
{% if note.created_by %} • {{ note.created_by }}{% endif %}
|
||||
{% if note.version > 1 %}
|
||||
<span class="badge bg-info">v{{ note.version }}</span>
|
||||
{% endif %}
|
||||
</small>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-link p-0 mx-1"
|
||||
onclick="editNote('{{ note.id }}', {{ note.note_text|tojson }})">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-link text-danger p-0 mx-1"
|
||||
onclick="deleteNote('{{ note.id }}')">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note-content" id="note-content-{{ note.id }}">
|
||||
{{ note.note_text|nl2br|safe }}
|
||||
</div>
|
||||
<div class="note-edit d-none" id="note-edit-{{ note.id }}">
|
||||
<textarea class="form-control mb-2" id="note-edit-text-{{ note.id }}">{{ note.note_text }}</textarea>
|
||||
<button class="btn btn-sm btn-primary" onclick="saveNote('{{ note.id }}')">
|
||||
Speichern
|
||||
</button>
|
||||
<button class="btn btn-sm btn-secondary" onclick="cancelEdit('{{ note.id }}')">
|
||||
Abbrechen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if not contact.notes %}
|
||||
<p class="text-muted">Noch keine Notizen vorhanden.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modals -->
|
||||
<!-- Edit Contact Modal -->
|
||||
<div class="modal fade" id="editContactModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Kontakt bearbeiten</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="editContactForm">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="editFirstName" class="form-label">Vorname</label>
|
||||
<input type="text" class="form-control" id="editFirstName"
|
||||
value="{{ contact.first_name }}" required>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="editLastName" class="form-label">Nachname</label>
|
||||
<input type="text" class="form-control" id="editLastName"
|
||||
value="{{ contact.last_name }}" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editPosition" class="form-label">Position</label>
|
||||
<input type="text" class="form-control" id="editPosition"
|
||||
value="{{ contact.position or '' }}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
||||
<button type="button" class="btn btn-primary" onclick="updateContact()">Speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Phone Modal -->
|
||||
<div class="modal fade" id="addPhoneModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Telefonnummer hinzufügen</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="addPhoneForm">
|
||||
<div class="mb-3">
|
||||
<label for="phoneNumber" class="form-label">Telefonnummer</label>
|
||||
<input type="tel" class="form-control" id="phoneNumber" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="phoneType" class="form-label">Typ</label>
|
||||
<select class="form-select" id="phoneType">
|
||||
<option value="">Bitte wählen...</option>
|
||||
<option value="Mobil">Mobil</option>
|
||||
<option value="Geschäftlich">Geschäftlich</option>
|
||||
<option value="Privat">Privat</option>
|
||||
<option value="Fax">Fax</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
||||
<button type="button" class="btn btn-primary" onclick="savePhone()">Speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Email Modal -->
|
||||
<div class="modal fade" id="addEmailModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">E-Mail-Adresse hinzufügen</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="addEmailForm">
|
||||
<div class="mb-3">
|
||||
<label for="emailAddress" class="form-label">E-Mail-Adresse</label>
|
||||
<input type="email" class="form-control" id="emailAddress" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="emailType" class="form-label">Typ</label>
|
||||
<select class="form-select" id="emailType">
|
||||
<option value="">Bitte wählen...</option>
|
||||
<option value="Geschäftlich">Geschäftlich</option>
|
||||
<option value="Privat">Privat</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveEmail()">Speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const contactId = '{{ contact.id }}';
|
||||
|
||||
// Contact functions
|
||||
function editContact() {
|
||||
new bootstrap.Modal(document.getElementById('editContactModal')).show();
|
||||
}
|
||||
|
||||
async function updateContact() {
|
||||
const firstName = document.getElementById('editFirstName').value.trim();
|
||||
const lastName = document.getElementById('editLastName').value.trim();
|
||||
const position = document.getElementById('editPosition').value.trim();
|
||||
|
||||
if (!firstName || !lastName) {
|
||||
alert('Bitte geben Sie Vor- und Nachname ein.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/leads/api/contacts/${contactId}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
position: position
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Fehler: ' + (data.error || 'Unbekannter Fehler'));
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Fehler beim Speichern: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Phone functions
|
||||
function showAddPhoneModal() {
|
||||
document.getElementById('addPhoneForm').reset();
|
||||
new bootstrap.Modal(document.getElementById('addPhoneModal')).show();
|
||||
}
|
||||
|
||||
async function savePhone() {
|
||||
const phoneNumber = document.getElementById('phoneNumber').value.trim();
|
||||
const phoneType = document.getElementById('phoneType').value;
|
||||
|
||||
if (!phoneNumber) {
|
||||
alert('Bitte geben Sie eine Telefonnummer ein.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/leads/api/contacts/${contactId}/phones`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
phone_number: phoneNumber,
|
||||
phone_type: phoneType
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Fehler: ' + (data.error || 'Unbekannter Fehler'));
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Fehler beim Speichern: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Email functions
|
||||
function showAddEmailModal() {
|
||||
document.getElementById('addEmailForm').reset();
|
||||
new bootstrap.Modal(document.getElementById('addEmailModal')).show();
|
||||
}
|
||||
|
||||
async function saveEmail() {
|
||||
const email = document.getElementById('emailAddress').value.trim();
|
||||
const emailType = document.getElementById('emailType').value;
|
||||
|
||||
if (!email) {
|
||||
alert('Bitte geben Sie eine E-Mail-Adresse ein.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/leads/api/contacts/${contactId}/emails`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
email: email,
|
||||
email_type: emailType
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Fehler: ' + (data.error || 'Unbekannter Fehler'));
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Fehler beim Speichern: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete detail
|
||||
async function deleteDetail(detailId) {
|
||||
if (!confirm('Möchten Sie diesen Eintrag wirklich löschen?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/leads/api/details/${detailId}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Fehler beim Löschen');
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Fehler: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Note functions
|
||||
async function addNote() {
|
||||
const noteText = document.getElementById('newNoteText').value.trim();
|
||||
|
||||
if (!noteText) {
|
||||
alert('Bitte geben Sie eine Notiz ein.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/leads/api/contacts/${contactId}/notes`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ note_text: noteText })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Fehler: ' + (data.error || 'Unbekannter Fehler'));
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Fehler beim Speichern: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
function editNote(noteId, noteText) {
|
||||
document.getElementById(`note-content-${noteId}`).classList.add('d-none');
|
||||
document.getElementById(`note-edit-${noteId}`).classList.remove('d-none');
|
||||
document.getElementById(`note-edit-text-${noteId}`).value = noteText;
|
||||
}
|
||||
|
||||
function cancelEdit(noteId) {
|
||||
document.getElementById(`note-content-${noteId}`).classList.remove('d-none');
|
||||
document.getElementById(`note-edit-${noteId}`).classList.add('d-none');
|
||||
}
|
||||
|
||||
async function saveNote(noteId) {
|
||||
const noteText = document.getElementById(`note-edit-text-${noteId}`).value.trim();
|
||||
|
||||
if (!noteText) {
|
||||
alert('Notiz darf nicht leer sein.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/leads/api/notes/${noteId}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ note_text: noteText })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Fehler: ' + (data.error || 'Unbekannter Fehler'));
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Fehler beim Speichern: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteNote(noteId) {
|
||||
if (!confirm('Möchten Sie diese Notiz wirklich löschen?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/leads/api/notes/${noteId}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Fehler beim Löschen');
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Fehler: ' + error.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.note-content {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
159
v2_adminpanel/leads/templates/leads/institution_detail.html
Normale Datei
159
v2_adminpanel/leads/templates/leads/institution_detail.html
Normale Datei
@@ -0,0 +1,159 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ institution.name }} - Lead-Details{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<h1 class="h2 mb-0">
|
||||
<i class="bi bi-building"></i> {{ institution.name }}
|
||||
</h1>
|
||||
<small class="text-muted">
|
||||
Erstellt am {{ institution.created_at.strftime('%d.%m.%Y') }}
|
||||
{% if institution.created_by %}von {{ institution.created_by }}{% endif %}
|
||||
</small>
|
||||
</div>
|
||||
<div class="col-md-4 text-end">
|
||||
<button class="btn btn-primary" onclick="showCreateContactModal()">
|
||||
<i class="bi bi-person-plus"></i> Neuer Kontakt
|
||||
</button>
|
||||
<a href="{{ url_for('leads.institutions') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Zurück
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contacts Table -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-people"></i> Kontakte
|
||||
<span class="badge bg-secondary">{{ contacts|length }}</span>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Position</th>
|
||||
<th>Erstellt am</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for contact in contacts %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ url_for('leads.contact_detail', contact_id=contact.id) }}"
|
||||
class="text-decoration-none">
|
||||
<strong>{{ contact.first_name }} {{ contact.last_name }}</strong>
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ contact.position or '-' }}</td>
|
||||
<td>{{ contact.created_at.strftime('%d.%m.%Y') }}</td>
|
||||
<td>
|
||||
<a href="{{ url_for('leads.contact_detail', contact_id=contact.id) }}"
|
||||
class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-eye"></i> Details
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if not contacts %}
|
||||
<div class="text-center py-4">
|
||||
<p class="text-muted">Noch keine Kontakte für diese Institution.</p>
|
||||
<button class="btn btn-primary" onclick="showCreateContactModal()">
|
||||
<i class="bi bi-person-plus"></i> Ersten Kontakt anlegen
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Create Contact Modal -->
|
||||
<div class="modal fade" id="contactModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Neuer Kontakt</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="contactForm">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="firstName" class="form-label">Vorname</label>
|
||||
<input type="text" class="form-control" id="firstName" required>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="lastName" class="form-label">Nachname</label>
|
||||
<input type="text" class="form-control" id="lastName" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="position" class="form-label">Position</label>
|
||||
<input type="text" class="form-control" id="position"
|
||||
placeholder="z.B. Geschäftsführer, Vertriebsleiter">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveContact()">Speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Show create contact modal
|
||||
function showCreateContactModal() {
|
||||
document.getElementById('contactForm').reset();
|
||||
new bootstrap.Modal(document.getElementById('contactModal')).show();
|
||||
}
|
||||
|
||||
// Save contact
|
||||
async function saveContact() {
|
||||
const firstName = document.getElementById('firstName').value.trim();
|
||||
const lastName = document.getElementById('lastName').value.trim();
|
||||
const position = document.getElementById('position').value.trim();
|
||||
|
||||
if (!firstName || !lastName) {
|
||||
alert('Bitte geben Sie Vor- und Nachname ein.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/leads/api/contacts', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
institution_id: '{{ institution.id }}',
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
position: position
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Fehler: ' + (data.error || 'Unbekannter Fehler'));
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Fehler beim Speichern: ' + error.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
178
v2_adminpanel/leads/templates/leads/institutions.html
Normale Datei
178
v2_adminpanel/leads/templates/leads/institutions.html
Normale Datei
@@ -0,0 +1,178 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Lead-Verwaltung - Institutionen{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<h1 class="h2 mb-0">
|
||||
<i class="bi bi-building"></i> Lead-Institutionen
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-md-4 text-end">
|
||||
<button class="btn btn-primary" onclick="showCreateInstitutionModal()">
|
||||
<i class="bi bi-plus-circle"></i> Neue Institution
|
||||
</button>
|
||||
<a href="{{ url_for('customers.customers_licenses') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Zurück zu Kunden
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search Bar -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-search"></i></span>
|
||||
<input type="text" class="form-control" id="searchInput"
|
||||
placeholder="Institution suchen..." onkeyup="filterInstitutions()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Institutions Table -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover" id="institutionsTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Institution</th>
|
||||
<th>Anzahl Kontakte</th>
|
||||
<th>Erstellt am</th>
|
||||
<th>Erstellt von</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for institution in institutions %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ url_for('leads.institution_detail', institution_id=institution.id) }}"
|
||||
class="text-decoration-none">
|
||||
<strong>{{ institution.name }}</strong>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-secondary">{{ institution.contact_count }}</span>
|
||||
</td>
|
||||
<td>{{ institution.created_at.strftime('%d.%m.%Y') }}</td>
|
||||
<td>{{ institution.created_by or '-' }}</td>
|
||||
<td>
|
||||
<a href="{{ url_for('leads.institution_detail', institution_id=institution.id) }}"
|
||||
class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-eye"></i> Details
|
||||
</a>
|
||||
<button class="btn btn-sm btn-outline-secondary"
|
||||
onclick="editInstitution('{{ institution.id }}', '{{ institution.name }}')">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if not institutions %}
|
||||
<div class="text-center py-4">
|
||||
<p class="text-muted">Noch keine Institutionen vorhanden.</p>
|
||||
<button class="btn btn-primary" onclick="showCreateInstitutionModal()">
|
||||
<i class="bi bi-plus-circle"></i> Erste Institution anlegen
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Create/Edit Institution Modal -->
|
||||
<div class="modal fade" id="institutionModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="institutionModalTitle">Neue Institution</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="institutionForm">
|
||||
<input type="hidden" id="institutionId">
|
||||
<div class="mb-3">
|
||||
<label for="institutionName" class="form-label">Name der Institution</label>
|
||||
<input type="text" class="form-control" id="institutionName" required>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveInstitution()">Speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Filter institutions
|
||||
function filterInstitutions() {
|
||||
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
|
||||
const rows = document.querySelectorAll('#institutionsTable tbody tr');
|
||||
|
||||
rows.forEach(row => {
|
||||
const text = row.textContent.toLowerCase();
|
||||
row.style.display = text.includes(searchTerm) ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
// Show create institution modal
|
||||
function showCreateInstitutionModal() {
|
||||
document.getElementById('institutionModalTitle').textContent = 'Neue Institution';
|
||||
document.getElementById('institutionId').value = '';
|
||||
document.getElementById('institutionName').value = '';
|
||||
new bootstrap.Modal(document.getElementById('institutionModal')).show();
|
||||
}
|
||||
|
||||
// Edit institution
|
||||
function editInstitution(id, name) {
|
||||
document.getElementById('institutionModalTitle').textContent = 'Institution bearbeiten';
|
||||
document.getElementById('institutionId').value = id;
|
||||
document.getElementById('institutionName').value = name;
|
||||
new bootstrap.Modal(document.getElementById('institutionModal')).show();
|
||||
}
|
||||
|
||||
// Save institution
|
||||
async function saveInstitution() {
|
||||
const id = document.getElementById('institutionId').value;
|
||||
const name = document.getElementById('institutionName').value.trim();
|
||||
|
||||
if (!name) {
|
||||
alert('Bitte geben Sie einen Namen ein.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = id
|
||||
? `/leads/api/institutions/${id}`
|
||||
: '/leads/api/institutions';
|
||||
const method = id ? 'PUT' : 'POST';
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ name: name })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Fehler: ' + (data.error || 'Unbekannter Fehler'));
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Fehler beim Speichern: ' + error.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren