Lead Management - Zwischenstand
Dieser Commit ist enthalten in:
11
CLAUDE.md
11
CLAUDE.md
@@ -140,4 +140,15 @@ Public access: Port 80 via Nginx
|
|||||||
- Don't abstract code that's only used once
|
- Don't abstract code that's only used once
|
||||||
- Implement exactly what's requested, nothing more
|
- Implement exactly what's requested, nothing more
|
||||||
|
|
||||||
|
## Recent Updates
|
||||||
|
|
||||||
|
### June 22, 2025 - 20:26
|
||||||
|
- Added Lead Management to main navigation (above Ressourcen Pool)
|
||||||
|
- Created Lead Management dashboard with:
|
||||||
|
- Overview statistics (institutions, contacts, user attribution)
|
||||||
|
- Recent activity feed showing who added/edited what
|
||||||
|
- Quick actions (add institution, view all, export)
|
||||||
|
- Shared information view between users rac00n and w@rh@mm3r
|
||||||
|
- Route: `/leads/management` accessible via navbar "Lead Management"
|
||||||
|
|
||||||
## Last Updated: June 22, 2025
|
## Last Updated: June 22, 2025
|
||||||
@@ -133,13 +133,16 @@ Das Admin Panel bietet alle benötigten Überwachungsfunktionen:
|
|||||||
## Features Overview
|
## Features Overview
|
||||||
|
|
||||||
### Lead Management System
|
### Lead Management System
|
||||||
- Accessible via "Leads" button on Customers & Licenses page
|
- **UPDATE 22.06.2025**: Jetzt direkt über Navbar "Lead Management" erreichbar
|
||||||
- Manage potential customers and contacts
|
- Lead Management Dashboard unter `/leads/management`
|
||||||
|
- Gemeinsame Kontaktdatenbank zwischen rac00n und w@rh@mm3r
|
||||||
- Features:
|
- Features:
|
||||||
|
- Dashboard mit Statistiken und Aktivitätsfeed
|
||||||
- Institution management
|
- Institution management
|
||||||
- Contact persons with multiple phones/emails
|
- Contact persons with multiple phones/emails
|
||||||
- Versioned notes system
|
- Versioned notes system
|
||||||
- Full audit trail
|
- Full audit trail
|
||||||
|
- Benutzer-Attribution (wer hat was hinzugefügt)
|
||||||
|
|
||||||
### Resource Pool Management
|
### Resource Pool Management
|
||||||
- Domain allocation system
|
- Domain allocation system
|
||||||
|
|||||||
@@ -137,6 +137,14 @@ Key feature: Monthly partitioned `license_heartbeats` table.
|
|||||||
### Status
|
### Status
|
||||||
**Vollständig implementiert** als Teil des Admin Panels unter `/leads/`
|
**Vollständig implementiert** als Teil des Admin Panels unter `/leads/`
|
||||||
|
|
||||||
|
### Update June 22, 2025 - 20:26
|
||||||
|
- **Neuer Navbar-Eintrag**: "Lead Management" über "Ressourcen Pool"
|
||||||
|
- **Lead Management Dashboard** unter `/leads/management` mit:
|
||||||
|
- Übersicht Statistiken (Institutionen, Kontakte, Benutzer-Attribution)
|
||||||
|
- Aktivitätsfeed zeigt wer was hinzugefügt/bearbeitet hat
|
||||||
|
- Schnellaktionen (Institution hinzufügen, alle anzeigen, exportieren)
|
||||||
|
- Geteilte Informationsansicht zwischen rac00n und w@rh@mm3r
|
||||||
|
|
||||||
### Architecture
|
### Architecture
|
||||||
- **Modular Architecture**: Clean separation of concerns
|
- **Modular Architecture**: Clean separation of concerns
|
||||||
- **Service Layer Pattern**: Business logic in `leads/services.py`
|
- **Service Layer Pattern**: Business logic in `leads/services.py`
|
||||||
|
|||||||
@@ -9,22 +9,144 @@ from db import get_db_connection
|
|||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
# Initialize service
|
# Service will be initialized per request
|
||||||
lead_repository = LeadRepository(get_db_connection)
|
lead_repository = None
|
||||||
lead_service = LeadService(lead_repository)
|
lead_service = None
|
||||||
|
|
||||||
|
def get_lead_service():
|
||||||
|
"""Get or create lead service instance"""
|
||||||
|
global lead_repository, lead_service
|
||||||
|
if lead_service is None:
|
||||||
|
lead_repository = LeadRepository(get_db_connection) # Pass the function, not call it
|
||||||
|
lead_service = LeadService(lead_repository)
|
||||||
|
return lead_service
|
||||||
|
|
||||||
# HTML Routes
|
# HTML Routes
|
||||||
|
@leads_bp.route('/management')
|
||||||
|
@login_required
|
||||||
|
def lead_management():
|
||||||
|
"""Lead Management Dashboard"""
|
||||||
|
try:
|
||||||
|
# Get current user
|
||||||
|
current_user = flask_session.get('username', 'System')
|
||||||
|
other_user = 'w@rh@mm3r' if current_user == 'rac00n' else 'rac00n'
|
||||||
|
|
||||||
|
# Initialize defaults
|
||||||
|
total_institutions = 0
|
||||||
|
total_contacts = 0
|
||||||
|
my_entries = 0
|
||||||
|
other_entries = 0
|
||||||
|
activities = []
|
||||||
|
|
||||||
|
with get_db_connection() as conn:
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# Get statistics
|
||||||
|
cur.execute("SELECT COUNT(*) FROM lead_institutions")
|
||||||
|
result = cur.fetchone()
|
||||||
|
if result:
|
||||||
|
total_institutions = result[0]
|
||||||
|
|
||||||
|
cur.execute("SELECT COUNT(*) FROM lead_contacts")
|
||||||
|
result = cur.fetchone()
|
||||||
|
if result:
|
||||||
|
total_contacts = result[0]
|
||||||
|
|
||||||
|
# Count entries by current user
|
||||||
|
cur.execute("""
|
||||||
|
SELECT COUNT(*) FROM audit_log
|
||||||
|
WHERE username = %s
|
||||||
|
AND entity_type IN ('lead_institution', 'lead_contact')
|
||||||
|
AND action = 'CREATE'
|
||||||
|
""", (current_user,))
|
||||||
|
result = cur.fetchone()
|
||||||
|
if result:
|
||||||
|
my_entries = result[0]
|
||||||
|
|
||||||
|
# Count entries by other user
|
||||||
|
cur.execute("""
|
||||||
|
SELECT COUNT(*) FROM audit_log
|
||||||
|
WHERE username = %s
|
||||||
|
AND entity_type IN ('lead_institution', 'lead_contact')
|
||||||
|
AND action = 'CREATE'
|
||||||
|
""", (other_user,))
|
||||||
|
result = cur.fetchone()
|
||||||
|
if result:
|
||||||
|
other_entries = result[0]
|
||||||
|
|
||||||
|
# Get recent activities
|
||||||
|
cur.execute("""
|
||||||
|
SELECT timestamp, username, action, entity_type, additional_info
|
||||||
|
FROM audit_log
|
||||||
|
WHERE entity_type IN ('lead_institution', 'lead_contact')
|
||||||
|
ORDER BY timestamp DESC
|
||||||
|
LIMIT 10
|
||||||
|
""")
|
||||||
|
rows = cur.fetchall()
|
||||||
|
if rows:
|
||||||
|
for row in rows:
|
||||||
|
activities.append({
|
||||||
|
'timestamp': row[0],
|
||||||
|
'username': row[1],
|
||||||
|
'action': row[2],
|
||||||
|
'entity_type': row[3].replace('lead_', ''),
|
||||||
|
'additional_info': row[4]
|
||||||
|
})
|
||||||
|
|
||||||
|
cur.close()
|
||||||
|
|
||||||
|
return render_template('leads/lead_management.html',
|
||||||
|
total_institutions=total_institutions,
|
||||||
|
total_contacts=total_contacts,
|
||||||
|
my_entries=my_entries,
|
||||||
|
other_entries=other_entries,
|
||||||
|
other_user=other_user,
|
||||||
|
current_user=current_user,
|
||||||
|
recent_activities=activities)
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
print(f"Error in lead_management: {str(e)}")
|
||||||
|
print(traceback.format_exc())
|
||||||
|
flash(f'Fehler beim Laden des Dashboards: {str(e)}', 'error')
|
||||||
|
current_user = flask_session.get('username', 'System')
|
||||||
|
return render_template('leads/lead_management.html',
|
||||||
|
total_institutions=0,
|
||||||
|
total_contacts=0,
|
||||||
|
my_entries=0,
|
||||||
|
other_entries=0,
|
||||||
|
other_user='',
|
||||||
|
current_user=current_user,
|
||||||
|
recent_activities=[])
|
||||||
|
|
||||||
@leads_bp.route('/')
|
@leads_bp.route('/')
|
||||||
@login_required
|
@login_required
|
||||||
def institutions():
|
def institutions():
|
||||||
"""List all institutions"""
|
"""List all institutions"""
|
||||||
try:
|
try:
|
||||||
institutions = lead_service.list_institutions()
|
institutions = get_lead_service().list_institutions()
|
||||||
return render_template('leads/institutions.html', institutions=institutions)
|
return render_template('leads/institutions.html', institutions=institutions)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
flash(f'Fehler beim Laden der Institutionen: {str(e)}', 'error')
|
flash(f'Fehler beim Laden der Institutionen: {str(e)}', 'error')
|
||||||
return render_template('leads/institutions.html', institutions=[])
|
return render_template('leads/institutions.html', institutions=[])
|
||||||
|
|
||||||
|
@leads_bp.route('/institution/add', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def add_institution():
|
||||||
|
"""Add new institution from form"""
|
||||||
|
try:
|
||||||
|
name = request.form.get('name')
|
||||||
|
if not name:
|
||||||
|
flash('Name ist erforderlich', 'error')
|
||||||
|
return redirect(url_for('leads.lead_management'))
|
||||||
|
|
||||||
|
# Add institution
|
||||||
|
get_lead_service().create_institution(name, flask_session.get('username', 'System'))
|
||||||
|
flash(f'Institution "{name}" wurde erfolgreich hinzugefügt', 'success')
|
||||||
|
except Exception as e:
|
||||||
|
flash(f'Fehler beim Hinzufügen der Institution: {str(e)}', 'error')
|
||||||
|
|
||||||
|
return redirect(url_for('leads.lead_management'))
|
||||||
|
|
||||||
@leads_bp.route('/institution/<uuid:institution_id>')
|
@leads_bp.route('/institution/<uuid:institution_id>')
|
||||||
@login_required
|
@login_required
|
||||||
def institution_detail(institution_id):
|
def institution_detail(institution_id):
|
||||||
@@ -35,7 +157,7 @@ def institution_detail(institution_id):
|
|||||||
flash('Institution nicht gefunden', 'error')
|
flash('Institution nicht gefunden', 'error')
|
||||||
return redirect(url_for('leads.institutions'))
|
return redirect(url_for('leads.institutions'))
|
||||||
|
|
||||||
contacts = lead_service.list_contacts_by_institution(institution_id)
|
contacts = get_lead_service().list_contacts_by_institution(institution_id)
|
||||||
return render_template('leads/institution_detail.html',
|
return render_template('leads/institution_detail.html',
|
||||||
institution=institution,
|
institution=institution,
|
||||||
contacts=contacts)
|
contacts=contacts)
|
||||||
@@ -48,7 +170,7 @@ def institution_detail(institution_id):
|
|||||||
def contact_detail(contact_id):
|
def contact_detail(contact_id):
|
||||||
"""Show contact details with notes"""
|
"""Show contact details with notes"""
|
||||||
try:
|
try:
|
||||||
contact = lead_service.get_contact_details(contact_id)
|
contact = get_lead_service().get_contact_details(contact_id)
|
||||||
return render_template('leads/contact_detail.html', contact=contact)
|
return render_template('leads/contact_detail.html', contact=contact)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
flash(f'Fehler beim Laden des Kontakts: {str(e)}', 'error')
|
flash(f'Fehler beim Laden des Kontakts: {str(e)}', 'error')
|
||||||
@@ -59,7 +181,7 @@ def contact_detail(contact_id):
|
|||||||
def all_contacts():
|
def all_contacts():
|
||||||
"""Show all contacts across all institutions"""
|
"""Show all contacts across all institutions"""
|
||||||
try:
|
try:
|
||||||
contacts = lead_service.list_all_contacts()
|
contacts = get_lead_service().list_all_contacts()
|
||||||
return render_template('leads/all_contacts.html', contacts=contacts)
|
return render_template('leads/all_contacts.html', contacts=contacts)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
flash(f'Fehler beim Laden der Kontakte: {str(e)}', 'error')
|
flash(f'Fehler beim Laden der Kontakte: {str(e)}', 'error')
|
||||||
@@ -72,7 +194,7 @@ def create_institution():
|
|||||||
"""Create new institution"""
|
"""Create new institution"""
|
||||||
try:
|
try:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
institution = lead_service.create_institution(
|
institution = get_lead_service().create_institution(
|
||||||
data['name'],
|
data['name'],
|
||||||
flask_session.get('username')
|
flask_session.get('username')
|
||||||
)
|
)
|
||||||
@@ -88,7 +210,7 @@ def update_institution(institution_id):
|
|||||||
"""Update institution"""
|
"""Update institution"""
|
||||||
try:
|
try:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
institution = lead_service.update_institution(
|
institution = get_lead_service().update_institution(
|
||||||
institution_id,
|
institution_id,
|
||||||
data['name'],
|
data['name'],
|
||||||
flask_session.get('username')
|
flask_session.get('username')
|
||||||
@@ -105,7 +227,7 @@ def create_contact():
|
|||||||
"""Create new contact"""
|
"""Create new contact"""
|
||||||
try:
|
try:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
contact = lead_service.create_contact(data, flask_session.get('username'))
|
contact = get_lead_service().create_contact(data, flask_session.get('username'))
|
||||||
return jsonify({'success': True, 'contact': contact})
|
return jsonify({'success': True, 'contact': contact})
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
return jsonify({'success': False, 'error': str(e)}), 400
|
return jsonify({'success': False, 'error': str(e)}), 400
|
||||||
@@ -118,7 +240,7 @@ def update_contact(contact_id):
|
|||||||
"""Update contact"""
|
"""Update contact"""
|
||||||
try:
|
try:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
contact = lead_service.update_contact(
|
contact = get_lead_service().update_contact(
|
||||||
contact_id,
|
contact_id,
|
||||||
data,
|
data,
|
||||||
flask_session.get('username')
|
flask_session.get('username')
|
||||||
@@ -135,7 +257,7 @@ def add_phone(contact_id):
|
|||||||
"""Add phone to contact"""
|
"""Add phone to contact"""
|
||||||
try:
|
try:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
detail = lead_service.add_phone(
|
detail = get_lead_service().add_phone(
|
||||||
contact_id,
|
contact_id,
|
||||||
data['phone_number'],
|
data['phone_number'],
|
||||||
data.get('phone_type'),
|
data.get('phone_type'),
|
||||||
@@ -153,7 +275,7 @@ def add_email(contact_id):
|
|||||||
"""Add email to contact"""
|
"""Add email to contact"""
|
||||||
try:
|
try:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
detail = lead_service.add_email(
|
detail = get_lead_service().add_email(
|
||||||
contact_id,
|
contact_id,
|
||||||
data['email'],
|
data['email'],
|
||||||
data.get('email_type'),
|
data.get('email_type'),
|
||||||
@@ -171,7 +293,7 @@ def update_detail(detail_id):
|
|||||||
"""Update contact detail (phone/email)"""
|
"""Update contact detail (phone/email)"""
|
||||||
try:
|
try:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
detail = lead_service.update_contact_detail(
|
detail = get_lead_service().update_contact_detail(
|
||||||
detail_id,
|
detail_id,
|
||||||
data['detail_value'],
|
data['detail_value'],
|
||||||
data.get('detail_label'),
|
data.get('detail_label'),
|
||||||
@@ -188,7 +310,7 @@ def update_detail(detail_id):
|
|||||||
def delete_detail(detail_id):
|
def delete_detail(detail_id):
|
||||||
"""Delete contact detail"""
|
"""Delete contact detail"""
|
||||||
try:
|
try:
|
||||||
success = lead_service.delete_contact_detail(
|
success = get_lead_service().delete_contact_detail(
|
||||||
detail_id,
|
detail_id,
|
||||||
flask_session.get('username')
|
flask_session.get('username')
|
||||||
)
|
)
|
||||||
@@ -202,7 +324,7 @@ def add_note(contact_id):
|
|||||||
"""Add note to contact"""
|
"""Add note to contact"""
|
||||||
try:
|
try:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
note = lead_service.add_note(
|
note = get_lead_service().add_note(
|
||||||
contact_id,
|
contact_id,
|
||||||
data['note_text'],
|
data['note_text'],
|
||||||
flask_session.get('username')
|
flask_session.get('username')
|
||||||
@@ -219,7 +341,7 @@ def update_note(note_id):
|
|||||||
"""Update note"""
|
"""Update note"""
|
||||||
try:
|
try:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
note = lead_service.update_note(
|
note = get_lead_service().update_note(
|
||||||
note_id,
|
note_id,
|
||||||
data['note_text'],
|
data['note_text'],
|
||||||
flask_session.get('username')
|
flask_session.get('username')
|
||||||
@@ -235,7 +357,7 @@ def update_note(note_id):
|
|||||||
def delete_note(note_id):
|
def delete_note(note_id):
|
||||||
"""Delete note"""
|
"""Delete note"""
|
||||||
try:
|
try:
|
||||||
success = lead_service.delete_note(
|
success = get_lead_service().delete_note(
|
||||||
note_id,
|
note_id,
|
||||||
flask_session.get('username')
|
flask_session.get('username')
|
||||||
)
|
)
|
||||||
|
|||||||
141
v2_adminpanel/leads/templates/leads/lead_management.html
Normale Datei
141
v2_adminpanel/leads/templates/leads/lead_management.html
Normale Datei
@@ -0,0 +1,141 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Lead Management{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container py-5">
|
||||||
|
<div class="mb-4">
|
||||||
|
<h2>📊 Lead Management</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Overview Stats -->
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<h3 class="mb-0">{{ total_institutions }}</h3>
|
||||||
|
<small class="text-muted">Institutionen</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<h3 class="mb-0">{{ total_contacts }}</h3>
|
||||||
|
<small class="text-muted">Kontakte</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Quick Actions -->
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Schnellaktionen</h5>
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<a href="{{ url_for('leads.institutions') }}" class="btn btn-primary">
|
||||||
|
<i class="bi bi-building"></i> Institutionen anzeigen
|
||||||
|
</a>
|
||||||
|
<button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#addInstitutionModal">
|
||||||
|
<i class="bi bi-plus"></i> Institution hinzufügen
|
||||||
|
</button>
|
||||||
|
<a href="{{ url_for('leads.export_leads') }}" class="btn btn-outline-secondary">
|
||||||
|
<i class="bi bi-download"></i> Exportieren
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Recent Activity -->
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="mb-0">Letzte Aktivitäten</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Zeitpunkt</th>
|
||||||
|
<th>Benutzer</th>
|
||||||
|
<th>Aktion</th>
|
||||||
|
<th>Details</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for activity in recent_activities %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ activity.timestamp.strftime('%d.%m.%Y %H:%M') }}</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge bg-{{ 'primary' if activity.username == session.username else 'secondary' }}">
|
||||||
|
{{ activity.username }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if activity.action == 'CREATE' %}
|
||||||
|
<span class="text-success">➕ Erstellt</span>
|
||||||
|
{% elif activity.action == 'UPDATE' %}
|
||||||
|
<span class="text-info">✏️ Bearbeitet</span>
|
||||||
|
{% elif activity.action == 'DELETE' %}
|
||||||
|
<span class="text-danger">🗑️ Gelöscht</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ activity.entity_type|title }}
|
||||||
|
{% if activity.additional_info %}
|
||||||
|
- {{ activity.additional_info }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% if not recent_activities %}
|
||||||
|
<p class="text-muted text-center py-3">Noch keine Aktivitäten vorhanden.</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Search -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="mb-0">Suche</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="get" action="{{ url_for('leads.institutions') }}">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control" name="search" placeholder="Nach Institution oder Kontakt suchen...">
|
||||||
|
<button class="btn btn-primary" type="submit">
|
||||||
|
<i class="bi bi-search"></i> Suchen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Add Institution Modal -->
|
||||||
|
<div class="modal fade" id="addInstitutionModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<form method="POST" action="{{ url_for('leads.add_institution') }}">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Neue Institution hinzufügen</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="name" class="form-label">Name der Institution</label>
|
||||||
|
<input type="text" class="form-control" id="name" name="name" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
||||||
|
<button type="submit" class="btn btn-primary">Hinzufügen</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -401,6 +401,12 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link {% if request.endpoint and request.endpoint.startswith('leads.') %}active{% endif %}" href="{{ url_for('leads.lead_management') }}">
|
||||||
|
<i class="bi bi-people"></i>
|
||||||
|
<span>Lead Management</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item {% if request.endpoint in ['resources.resources', 'resources.add_resources'] %}has-active-child{% endif %}">
|
<li class="nav-item {% if request.endpoint in ['resources.resources', 'resources.add_resources'] %}has-active-child{% endif %}">
|
||||||
<a class="nav-link has-submenu {% if request.endpoint == 'resources.resources' %}active{% endif %}" href="{{ url_for('resources.resources') }}">
|
<a class="nav-link has-submenu {% if request.endpoint == 'resources.resources' %}active{% endif %}" href="{{ url_for('resources.resources') }}">
|
||||||
<i class="bi bi-box-seam"></i>
|
<i class="bi bi-box-seam"></i>
|
||||||
|
|||||||
@@ -9,9 +9,6 @@
|
|||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h2 class="mb-0">Kunden & Lizenzen</h2>
|
<h2 class="mb-0">Kunden & Lizenzen</h2>
|
||||||
<div>
|
<div>
|
||||||
<a href="{{ url_for('leads.institutions') }}" class="btn btn-primary">
|
|
||||||
<i class="bi bi-people"></i> Leads
|
|
||||||
</a>
|
|
||||||
<!-- Export Buttons ohne Dropdown -->
|
<!-- Export Buttons ohne Dropdown -->
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<a href="{{ url_for('export.export_customers', format='excel') }}" class="btn btn-success btn-sm">
|
<a href="{{ url_for('export.export_customers', format='excel') }}" class="btn btn-success btn-sm">
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren