Qualys: - Recherche API temps réel + cache 24h base locale - Tags: liste DYN/STAT, mapping V3 (DOM-*, TYP-*, APP-*), nb assets cliquable - CRUD tags: créer STAT, supprimer, resync API - Détail asset: infos + décodage nomenclature V3 + tags assignés - Ajout/retrait tag unitaire avec autocomplete filtrable - Bulk add/remove tag en masse avec dropdown filtrable - Tags retirer: charge dynamiquement les STAT assignés aux assets sélectionnés - Resync assets sélectionnés + retour même recherche Contacts: - 50 contacts importés avec 93 scopes (domaine/app/serveur/zone par env) - 13 rôles (responsable_domaine, ra_prod, ra_recette, referent_technique...) - Recherche par nom/email/serveur (affiche contacts liés) - CRUD complet: éditer, scopes, activer/désactiver, supprimer - Serveurs liés calculés dynamiquement depuis les scopes Audit: - Restructuré: Audit général + sous-menu Spécifique - Dernier audit global affiché avec date - Lancer audit général avec exclusions (domaines/zones) et parallélisme - KPIs Qualys KO et S1 KO cliquables - Export CSV Serveurs: - Actions groupées bulk (domaine, env, tier, état, owner, licence) - Dashboard: KPI EOL ajouté - Filtre état: EOL + en décommissionnement ajoutés - 138 serveurs EOL importés depuis Qualys (owner=na, hors périmètre) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
86 lines
4.5 KiB
HTML
86 lines
4.5 KiB
HTML
<div>
|
||
<div class="flex justify-between items-center mb-4">
|
||
<h3 class="text-lg font-bold text-cyber-accent">{{ c.name }}</h3>
|
||
<button onclick="document.getElementById('contact-detail').style.display='none'" class="text-gray-500 hover:text-white text-xl">×</button>
|
||
</div>
|
||
|
||
<!-- Édition -->
|
||
<form method="POST" action="/contacts/{{ c.id }}/edit" class="flex gap-3 items-end mb-4">
|
||
<div>
|
||
<label class="text-xs text-gray-500">Nom</label>
|
||
<input type="text" name="name" value="{{ c.name }}" class="text-xs py-1 px-2 w-44">
|
||
</div>
|
||
<div>
|
||
<label class="text-xs text-gray-500">Email</label>
|
||
<input type="email" name="email" value="{{ c.email }}" class="text-xs py-1 px-2 w-52">
|
||
</div>
|
||
<div>
|
||
<label class="text-xs text-gray-500">Rôle</label>
|
||
<select name="contact_role" class="text-xs py-1 px-2">
|
||
{% for code, label in roles %}<option value="{{ code }}" {% if c.role == code %}selected{% endif %}>{{ label }}</option>{% endfor %}
|
||
</select>
|
||
</div>
|
||
<button type="submit" class="btn-sm bg-cyber-accent text-black">Modifier</button>
|
||
<form method="POST" action="/contacts/{{ c.id }}/toggle" style="display:inline">
|
||
<button type="submit" class="btn-sm {% if c.is_active %}bg-red-900/30 text-cyber-red{% else %}bg-green-900/30 text-cyber-green{% endif %}">
|
||
{{ 'Désactiver' if c.is_active else 'Activer' }}
|
||
</button>
|
||
</form>
|
||
</form>
|
||
|
||
<!-- Scopes existants -->
|
||
<h4 class="text-xs text-cyber-accent font-bold uppercase mb-2 border-b border-cyber-border pb-1">Périmètre de responsabilité</h4>
|
||
<div class="space-y-1 mb-3">
|
||
{% for s in scopes %}
|
||
<div class="flex items-center gap-2 text-xs">
|
||
<span class="badge {% if s.scope_type == 'domain' %}badge-blue{% elif s.scope_type == 'application' %}badge-yellow{% elif s.scope_type == 'server' %}badge-red{% else %}badge-gray{% endif %}">{{ s.scope_type }}</span>
|
||
<span class="font-mono text-cyber-accent">{{ s.scope_value }}</span>
|
||
{% if s.env_scope != 'all' %}<span class="badge badge-green">{{ s.env_scope }}</span>{% endif %}
|
||
<form method="POST" action="/contacts/scope/{{ s.id }}/delete" style="display:inline">
|
||
<button class="text-gray-600 hover:text-cyber-red text-xs" onclick="return confirm('Supprimer ?')">×</button>
|
||
</form>
|
||
</div>
|
||
{% endfor %}
|
||
{% if not scopes %}<p class="text-xs text-gray-500">Aucun scope défini</p>{% endif %}
|
||
</div>
|
||
|
||
<!-- Ajouter scope -->
|
||
<form method="POST" action="/contacts/{{ c.id }}/scope/add" class="flex gap-2 items-end mb-4">
|
||
<div>
|
||
<label class="text-xs text-gray-500">Type</label>
|
||
<select name="scope_type" class="text-xs py-1 px-2">
|
||
{% for code, label in scope_types %}<option value="{{ code }}">{{ label }}</option>{% endfor %}
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label class="text-xs text-gray-500">Valeur</label>
|
||
<input type="text" name="scope_value" class="text-xs py-1 px-2 w-36" required list="scope-values" placeholder="EMV, COMMVAULT...">
|
||
<datalist id="scope-values">
|
||
{% for d in domains %}<option value="{{ d.code }}">{{ d.name }}</option>{% endfor %}
|
||
{% for a in app_types %}<option value="{{ a }}">{{ a }}</option>{% endfor %}
|
||
</datalist>
|
||
</div>
|
||
<div>
|
||
<label class="text-xs text-gray-500">Env</label>
|
||
<select name="env_scope" class="text-xs py-1 px-2">
|
||
{% for es in env_scopes %}<option value="{{ es }}">{{ es }}</option>{% endfor %}
|
||
</select>
|
||
</div>
|
||
<button type="submit" class="btn-sm bg-cyber-accent text-black">Ajouter</button>
|
||
</form>
|
||
|
||
<!-- Serveurs liés -->
|
||
{% if servers %}
|
||
<h4 class="text-xs text-cyber-accent font-bold uppercase mb-2 border-b border-cyber-border pb-1">Serveurs concernés ({{ servers|length }})</h4>
|
||
<div class="grid grid-cols-3 gap-1 text-xs">
|
||
{% for s in servers %}
|
||
<div class="flex items-center gap-1">
|
||
<span class="font-mono text-cyber-accent">{{ s.hostname }}</span>
|
||
<span class="text-gray-500">{{ s.domaine or '' }}</span>
|
||
<span class="badge {% if s.environnement == 'Production' %}badge-green{% else %}badge-yellow{% endif %} text-[9px]">{{ (s.environnement or '')[:4] }}</span>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|