Filtrer le tableau de bord selon les permissions
Some checks are pending
CI - Tests et Syntax / lint-and-test (push) Waiting to run
Some checks are pending
CI - Tests et Syntax / lint-and-test (push) Waiting to run
This commit is contained in:
parent
699d094796
commit
ee8c0ad5a0
2 changed files with 128 additions and 90 deletions
|
|
@ -13,6 +13,7 @@ from ..models.college import Building, Room
|
|||
from ..models.company import Part
|
||||
from app_new.constants import INTERVENTION_STATUSES, EQUIPMENT_STATUSES
|
||||
from sqlalchemy import func
|
||||
from ..authorization import has_permission
|
||||
|
||||
dashboard_bp = Blueprint('dashboard', __name__)
|
||||
|
||||
|
|
@ -26,19 +27,32 @@ def index():
|
|||
from ..models.planning import ScheduledTask, AdminTask
|
||||
from ..models.maintenance import Lot
|
||||
|
||||
dashboard_permissions = {
|
||||
'intervention': has_permission('intervention.view', current_user),
|
||||
'intervention_manage': has_permission('intervention.manage', current_user),
|
||||
'patrimoine': has_permission('patrimoine.view', current_user),
|
||||
'planning': has_permission('planning.view', current_user),
|
||||
'planning_manage': has_permission('planning.manage', current_user),
|
||||
'stock': has_permission('stock.view', current_user),
|
||||
'prevention': has_permission('prevention.view', current_user),
|
||||
'ent': has_permission('integration.ent.view', current_user),
|
||||
'outlook': has_permission('integration.outlook.view', current_user),
|
||||
'ai': has_permission('system.configure', current_user),
|
||||
}
|
||||
|
||||
# Statistiques
|
||||
stats = {
|
||||
'interventions_total': Intervention.query.count(),
|
||||
'interventions_en_cours': Intervention.query.filter_by(status='en_cours').count(),
|
||||
'interventions_en_attente': Intervention.query.filter_by(status='en_attente').count(),
|
||||
'interventions_urgentes': Intervention.query.filter_by(priority='urgente', is_deleted=False).count(),
|
||||
'equipments_total': Equipment.query.count(),
|
||||
'equipments_actifs': Equipment.query.filter_by(status='en_service', is_deleted=False).count(),
|
||||
'equipments_panne': Equipment.query.filter(Equipment.status.in_(['en_panne', 'hors_service', 'hs']), Equipment.is_deleted.is_(False)).count(),
|
||||
'buildings_total': Building.query.count(),
|
||||
'rooms_total': Room.query.count(),
|
||||
'companies_total': Company.query.count(),
|
||||
'parts_low_stock': Part.query.filter(Part.quantity <= Part.min_quantity).count(),
|
||||
'interventions_total': Intervention.query.count() if dashboard_permissions['intervention'] else 0,
|
||||
'interventions_en_cours': Intervention.query.filter_by(status='en_cours').count() if dashboard_permissions['intervention'] else 0,
|
||||
'interventions_en_attente': Intervention.query.filter_by(status='en_attente').count() if dashboard_permissions['intervention'] else 0,
|
||||
'interventions_urgentes': Intervention.query.filter_by(priority='urgente', is_deleted=False).count() if dashboard_permissions['intervention'] else 0,
|
||||
'equipments_total': Equipment.query.count() if dashboard_permissions['patrimoine'] else 0,
|
||||
'equipments_actifs': Equipment.query.filter_by(status='en_service', is_deleted=False).count() if dashboard_permissions['patrimoine'] else 0,
|
||||
'equipments_panne': Equipment.query.filter(Equipment.status.in_(['en_panne', 'hors_service', 'hs']), Equipment.is_deleted.is_(False)).count() if dashboard_permissions['patrimoine'] else 0,
|
||||
'buildings_total': Building.query.count() if dashboard_permissions['patrimoine'] else 0,
|
||||
'rooms_total': Room.query.count() if dashboard_permissions['patrimoine'] else 0,
|
||||
'companies_total': Company.query.count() if dashboard_permissions['intervention'] else 0,
|
||||
'parts_low_stock': Part.query.filter(Part.quantity <= Part.min_quantity).count() if dashboard_permissions['stock'] else 0,
|
||||
}
|
||||
|
||||
# Contrôle des lots : un lot présent doit être rattaché à au moins un
|
||||
|
|
@ -52,8 +66,9 @@ def index():
|
|||
)
|
||||
.group_by(Equipment.lot_id)
|
||||
.all()
|
||||
if dashboard_permissions['patrimoine'] else {}
|
||||
)
|
||||
lots = Lot.query.order_by(Lot.name).all()
|
||||
lots = Lot.query.order_by(Lot.name).all() if dashboard_permissions['patrimoine'] else []
|
||||
lots_without_equipment = [
|
||||
lot for lot in lots
|
||||
if lot.is_present and equipment_counts.get(lot.id, 0) == 0
|
||||
|
|
@ -77,14 +92,14 @@ def index():
|
|||
stats['lots_missing_duration'] = len(lots_missing_duration)
|
||||
|
||||
# Dernières interventions
|
||||
recent_interventions = Intervention.query.order_by(Intervention.created_at.desc()).limit(10).all()
|
||||
recent_interventions = Intervention.query.order_by(Intervention.created_at.desc()).limit(10).all() if dashboard_permissions['intervention'] else []
|
||||
|
||||
# Interventions urgentes
|
||||
urgent_interventions = Intervention.query.filter_by(priority='urgente', is_deleted=False).limit(5).all()
|
||||
urgent_interventions = Intervention.query.filter_by(priority='urgente', is_deleted=False).limit(5).all() if dashboard_permissions['intervention'] else []
|
||||
|
||||
# Interventions du jour (planning)
|
||||
today = date.today()
|
||||
day_schedule = PlanningService.get_day_schedule(today)
|
||||
day_schedule = PlanningService.get_day_schedule(today) if dashboard_permissions['planning'] else None
|
||||
|
||||
# Interprétations en attente (non traitées)
|
||||
from ...outlook.models import OutlookMailInterpretation
|
||||
|
|
@ -94,12 +109,12 @@ def index():
|
|||
outlook_interpretations = OutlookMailInterpretation.query.filter_by(
|
||||
status='pending',
|
||||
user_id=current_user.id
|
||||
).order_by(OutlookMailInterpretation.created_at.desc()).limit(10).all()
|
||||
).order_by(OutlookMailInterpretation.created_at.desc()).limit(10).all() if dashboard_permissions['outlook'] else []
|
||||
|
||||
# Récupérer les interprétations ENT
|
||||
ent_interpretations = EntMessageInterpretation.query.filter_by(
|
||||
status='pending'
|
||||
).order_by(EntMessageInterpretation.created_at.desc()).limit(10).all()
|
||||
).order_by(EntMessageInterpretation.created_at.desc()).limit(10).all() if dashboard_permissions['ent'] else []
|
||||
|
||||
# Combiner les deux listes avec une source
|
||||
pending_interpretations = []
|
||||
|
|
@ -148,7 +163,8 @@ def index():
|
|||
lots_without_equipment=lots_without_equipment,
|
||||
lots_missing_duration=lots_missing_duration,
|
||||
today=today,
|
||||
statuses=INTERVENTION_STATUSES)
|
||||
statuses=INTERVENTION_STATUSES,
|
||||
dashboard_permissions=dashboard_permissions)
|
||||
|
||||
|
||||
@dashboard_bp.route('/api/stats')
|
||||
|
|
@ -158,17 +174,21 @@ def api_stats():
|
|||
from datetime import date, timedelta
|
||||
from sqlalchemy import func
|
||||
|
||||
can_intervention = has_permission('intervention.view', current_user)
|
||||
can_patrimoine = has_permission('patrimoine.view', current_user)
|
||||
|
||||
# Interventions par mois (6 derniers mois)
|
||||
six_months_ago = date.today() - timedelta(days=180)
|
||||
monthly = db.session.query(
|
||||
func.date_format(Intervention.created_at, '%Y-%m').label('month'),
|
||||
func.count().label('count')
|
||||
).filter(Intervention.created_at >= six_months_ago).group_by('month').order_by('month').all()
|
||||
).filter(Intervention.created_at >= six_months_ago).group_by('month').order_by('month').all() if can_intervention else []
|
||||
|
||||
monthly_data = [{'month': r.month, 'count': r.count} for r in monthly]
|
||||
|
||||
# Interventions par statut
|
||||
status_data = {}
|
||||
if can_intervention:
|
||||
for status_val, info in INTERVENTION_STATUSES.items():
|
||||
count = Intervention.query.filter_by(status=status_val).count()
|
||||
if count > 0:
|
||||
|
|
@ -176,6 +196,7 @@ def api_stats():
|
|||
|
||||
# Equipements par statut
|
||||
equip_status = {}
|
||||
if can_patrimoine:
|
||||
for s in EQUIPMENT_STATUSES:
|
||||
count = Equipment.query.filter_by(status=s).count()
|
||||
if count > 0:
|
||||
|
|
@ -188,7 +209,7 @@ def api_stats():
|
|||
).join(Intervention, Intervention.equipment_id == Equipment.id)\
|
||||
.group_by(Equipment.id)\
|
||||
.order_by(func.count(Intervention.id).desc())\
|
||||
.limit(5).all()
|
||||
.limit(5).all() if can_intervention and can_patrimoine else []
|
||||
|
||||
top_data = [{'name': r.name, 'count': r.count} for r in top_equip]
|
||||
|
||||
|
|
@ -213,7 +234,7 @@ def search():
|
|||
}
|
||||
|
||||
if query:
|
||||
# Recherche interventions
|
||||
if has_permission('intervention.view', current_user):
|
||||
results['interventions'] = Intervention.query.filter(
|
||||
db.or_(
|
||||
Intervention.title.ilike(f'%{query}%'),
|
||||
|
|
@ -221,7 +242,7 @@ def search():
|
|||
)
|
||||
).limit(10).all()
|
||||
|
||||
# Recherche équipements
|
||||
if has_permission('patrimoine.view', current_user):
|
||||
results['equipments'] = Equipment.query.filter(
|
||||
db.or_(
|
||||
Equipment.name.ilike(f'%{query}%'),
|
||||
|
|
@ -229,7 +250,7 @@ def search():
|
|||
)
|
||||
).limit(10).all()
|
||||
|
||||
# Recherche salles
|
||||
if has_permission('patrimoine.view', current_user):
|
||||
results['rooms'] = Room.query.filter(
|
||||
Room.name.ilike(f'%{query}%')
|
||||
).limit(10).all()
|
||||
|
|
@ -245,31 +266,36 @@ def alerts():
|
|||
from app_new.core.models.company import Alert
|
||||
alerts_list = Alert.query.filter_by(is_read=False).order_by(Alert.created_at.desc()).all()
|
||||
|
||||
can_intervention = has_permission('intervention.view', current_user)
|
||||
can_patrimoine = has_permission('patrimoine.view', current_user)
|
||||
can_outlook = has_permission('integration.outlook.view', current_user)
|
||||
can_ent = has_permission('integration.ent.view', current_user)
|
||||
|
||||
# Interventions urgentes (statut haute priorité)
|
||||
urgent_interventions = Intervention.query.filter(
|
||||
Intervention.priority == 'haute',
|
||||
Intervention.status.in_(['planifiee', 'en_cours', 'en_attente'])
|
||||
).order_by(Intervention.scheduled_date.desc()).all()
|
||||
).order_by(Intervention.scheduled_date.desc()).all() if can_intervention else []
|
||||
|
||||
# Équipements en panne
|
||||
from app_new.core.models.equipment import Equipment
|
||||
equipments_panne = Equipment.query.filter(
|
||||
Equipment.status.in_(['en_panne', 'hors_service', 'hs']),
|
||||
Equipment.is_deleted.is_(False),
|
||||
).all()
|
||||
).all() if can_patrimoine else []
|
||||
|
||||
# Équipements à jeter
|
||||
equipments_to_trash = Equipment.query.filter_by(status='a_jeter').all()
|
||||
equipments_to_trash = Equipment.query.filter_by(status='a_jeter').all() if can_patrimoine else []
|
||||
|
||||
# Interprétations en attente
|
||||
from ...outlook.models import OutlookMailInterpretation
|
||||
from ...ent.interpretation_models import EntMessageInterpretation
|
||||
pending_outlook = OutlookMailInterpretation.query.filter_by(
|
||||
status='pending', user_id=current_user.id
|
||||
).order_by(OutlookMailInterpretation.created_at.desc()).all()
|
||||
).order_by(OutlookMailInterpretation.created_at.desc()).all() if can_outlook else []
|
||||
pending_ent = EntMessageInterpretation.query.filter_by(
|
||||
status='pending'
|
||||
).order_by(EntMessageInterpretation.created_at.desc()).all()
|
||||
).order_by(EntMessageInterpretation.created_at.desc()).all() if can_ent else []
|
||||
|
||||
return render_template('dashboard/alerts.html',
|
||||
alerts=alerts_list,
|
||||
|
|
@ -302,6 +328,10 @@ def create_month_interventions():
|
|||
from ..models.company import Company
|
||||
from flask_login import current_user
|
||||
|
||||
if not (has_permission('planning.manage', current_user) or has_permission('intervention.manage', current_user)):
|
||||
from flask import abort
|
||||
abort(403)
|
||||
|
||||
today = date.today()
|
||||
one_month_later = today + timedelta(days=30)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,33 +11,35 @@
|
|||
{% block content %}
|
||||
<h1 class="mb-3 h3"><i class="bi bi-speedometer2"></i> Tableau de bord</h1>
|
||||
|
||||
<!-- Alerte modele IA -->
|
||||
{% if dashboard_permissions.ai %}<!-- Alerte modele IA -->
|
||||
<div id="ai-alert" style="display:none;" class="alert alert-danger mb-3">
|
||||
<i class="bi bi-exclamation-triangle"></i>
|
||||
<strong>Modele IA indisponible !</strong>
|
||||
<span id="ai-alert-msg"></span>
|
||||
<a href="{{ url_for('ai_config.index') }}" class="alert-link">Configurer le modele</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Alerte retards -->
|
||||
{% if dashboard_permissions.planning %}<!-- Alerte retards -->
|
||||
<div id="overdue-alert" style="display:none;" class="alert alert-warning mb-3">
|
||||
<i class="bi bi-clock-history"></i>
|
||||
<strong>Retards detectes !</strong>
|
||||
<span id="overdue-alert-msg"></span>
|
||||
<a href="{{ url_for('scheduler.index') }}" class="alert-link">Voir le planificateur</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Stats rapides -->
|
||||
<div class="row g-2 g-md-3 mb-3">
|
||||
<div class="col-12">
|
||||
{% if dashboard_permissions.intervention_manage or dashboard_permissions.planning_manage %}<div class="col-12">
|
||||
<form method="POST" action="{{ url_for('dashboard.create_month_interventions') }}" class="d-inline" onsubmit="return confirm('Créer les interventions pour le mois prochain?')">
|
||||
<button type="submit" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-calendar-plus me-1"></i>Créer interventions (1 mois)
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>{% endif %}
|
||||
|
||||
<div class="col-6 col-md-3">
|
||||
{% if dashboard_permissions.intervention %}<div class="col-6 col-md-3">
|
||||
<div class="card stat-card text-white bg-primary">
|
||||
<div class="card-body py-2 px-3">
|
||||
<small class="card-title">Interventions</small>
|
||||
|
|
@ -45,8 +47,8 @@
|
|||
<small>{{ stats.interventions_en_cours }} en cours</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
</div>{% endif %}
|
||||
{% if dashboard_permissions.intervention %}<div class="col-6 col-md-3">
|
||||
<div class="card stat-card text-white bg-success">
|
||||
<div class="card-body py-2 px-3">
|
||||
<small class="card-title">Curatif / Préventif</small>
|
||||
|
|
@ -54,8 +56,8 @@
|
|||
<small>{{ stats.interventions_recurrentes }} récurrentes</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
</div>{% endif %}
|
||||
{% if dashboard_permissions.patrimoine %}<div class="col-6 col-md-3">
|
||||
<div class="card stat-card text-white bg-warning">
|
||||
<div class="card-body py-2 px-3">
|
||||
<small class="card-title">Équipements</small>
|
||||
|
|
@ -63,8 +65,8 @@
|
|||
<small>{{ stats.equipments_hors_service }} hors service</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
</div>{% endif %}
|
||||
{% if dashboard_permissions.stock %}<div class="col-6 col-md-3">
|
||||
<div class="card stat-card {% if stats.parts_low_stock > 0 %}bg-danger{% else %}bg-info{% endif %} text-white">
|
||||
<div class="card-body py-2 px-3">
|
||||
<small class="card-title">Stock bas</small>
|
||||
|
|
@ -72,10 +74,10 @@
|
|||
<small>pièce(s)</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Échéances préventif -->
|
||||
{% if dashboard_permissions.planning %}<!-- Échéances préventif -->
|
||||
<div class="row g-2 g-md-3 mb-3">
|
||||
<div class="col-6 col-md-3">
|
||||
<a href="{{ url_for('interventions_planning.planning') }}" class="text-decoration-none">
|
||||
|
|
@ -117,9 +119,9 @@
|
|||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>{% endif %}
|
||||
|
||||
<!-- Contrôles des référentiels de maintenance -->
|
||||
{% if dashboard_permissions.patrimoine %}<!-- Contrôles des référentiels de maintenance -->
|
||||
<div class="row g-2 g-md-3 mb-3">
|
||||
<div class="col-lg-6">
|
||||
<div class="card h-100 {% if stats.lots_without_equipment %}border-warning{% else %}border-success{% endif %}">
|
||||
|
|
@ -177,9 +179,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>{% endif %}
|
||||
|
||||
<!-- Alertes -->
|
||||
{% if dashboard_permissions.intervention or dashboard_permissions.patrimoine or dashboard_permissions.ent or dashboard_permissions.outlook %}<!-- Alertes -->
|
||||
{% if alerts %}
|
||||
<div class="card border-warning mb-3">
|
||||
<div class="card-header bg-warning text-dark py-2">
|
||||
|
|
@ -196,9 +198,9 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}{% endif %}
|
||||
|
||||
<!-- Maintenances du jour -->
|
||||
{% if dashboard_permissions.planning %}<!-- Maintenances du jour -->
|
||||
{% if day_schedule and day_schedule.items %}
|
||||
<div class="card border-primary mb-3">
|
||||
<div class="card-header bg-primary text-white py-2">
|
||||
|
|
@ -299,9 +301,9 @@
|
|||
<p class="mb-0">{{ day_schedule.leave }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}{% endif %}
|
||||
|
||||
<!-- 2 colonnes : Maintenances + Fréquence -->
|
||||
{% if dashboard_permissions.planning %}<!-- 2 colonnes : Maintenances + Fréquence -->
|
||||
<div class="row g-2 g-md-3 mb-3">
|
||||
<!-- Maintenances à venir -->
|
||||
<div class="col-12 col-md-6">
|
||||
|
|
@ -376,9 +378,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>{% endif %}
|
||||
|
||||
<!-- Workflow + Activité récente -->
|
||||
{% if dashboard_permissions.intervention %}<!-- Workflow + Activité récente -->
|
||||
<div class="row g-2 g-md-3 mb-3">
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="card h-100 border-warning">
|
||||
|
|
@ -433,39 +435,40 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>{% endif %}
|
||||
|
||||
<!-- Graphiques -->
|
||||
{% if dashboard_permissions.intervention or dashboard_permissions.patrimoine %}<!-- Graphiques -->
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-6">
|
||||
{% if dashboard_permissions.intervention %}<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h6 class="mb-0"><i class="bi bi-bar-chart"></i> Interventions par mois</h6></div>
|
||||
<div class="card-body"><canvas id="chart-monthly" height="200"></canvas></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
</div>{% endif %}
|
||||
{% if dashboard_permissions.intervention %}<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h6 class="mb-0"><i class="bi bi-pie-chart"></i> Interventions par statut</h6></div>
|
||||
<div class="card-body"><canvas id="chart-status" height="200"></canvas></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-6">
|
||||
</div>{% endif %}
|
||||
</div>{% endif %}
|
||||
{% if dashboard_permissions.patrimoine or (dashboard_permissions.intervention and dashboard_permissions.patrimoine) %}<div class="row g-3 mb-3">
|
||||
{% if dashboard_permissions.patrimoine %}<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h6 class="mb-0"><i class="bi bi-pie-chart"></i> Equipements par statut</h6></div>
|
||||
<div class="card-body"><canvas id="chart-equip" height="200"></canvas></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
</div>{% endif %}
|
||||
{% if dashboard_permissions.intervention and dashboard_permissions.patrimoine %}<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h6 class="mb-0"><i class="bi bi-bar-chart"></i> Top 5 equipements</h6></div>
|
||||
<div class="card-body"><canvas id="chart-top" height="200"></canvas></div>
|
||||
</div>
|
||||
</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Dernières interventions -->
|
||||
{% if dashboard_permissions.intervention %}<!-- Dernières interventions -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header d-flex justify-content-between align-items-center py-2">
|
||||
<span><i class="bi bi-clock-history"></i> Dernières interventions</span>
|
||||
|
|
@ -516,10 +519,10 @@
|
|||
<div class="p-3 text-muted">Aucune intervention.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>{% endif %}
|
||||
|
||||
<!-- Interprétations en attente -->
|
||||
{% if pending_interpretations %}
|
||||
{% if pending_interpretations and (dashboard_permissions.ent or dashboard_permissions.outlook) %}
|
||||
<div class="card mb-3 border-warning">
|
||||
<div class="card-header bg-warning bg-opacity-10 d-flex justify-content-between align-items-center py-2">
|
||||
<span><i class="bi bi-robot"></i> Interprétations en attente <span class="badge bg-warning text-dark">{{ pending_interpretations|length }}</span></span>
|
||||
|
|
@ -684,7 +687,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
{% if dashboard_permissions.ai or dashboard_permissions.planning %}<script>
|
||||
{% if dashboard_permissions.ai %}
|
||||
// Verifier le statut du modele IA au chargement
|
||||
fetch('/ai-config/api/status').then(r => r.json()).then(d => {
|
||||
if (d.status === 'error' || d.status === 'no_model' || d.status === 'no_key') {
|
||||
|
|
@ -700,7 +704,9 @@ fetch('/ai-config/api/status').then(r => r.json()).then(d => {
|
|||
alert.style.display = 'block';
|
||||
}
|
||||
}).catch(() => {});
|
||||
{% endif %}
|
||||
|
||||
{% if dashboard_permissions.planning %}
|
||||
// Verifier les retards
|
||||
fetch('/scheduler/api/overdue-count').then(r => r.json()).then(d => {
|
||||
const total = (d.overdue_tasks || 0) + (d.overdue_interventions || 0);
|
||||
|
|
@ -711,5 +717,7 @@ fetch('/scheduler/api/overdue-count').then(r => r.json()).then(d => {
|
|||
alert.style.display = 'block';
|
||||
}
|
||||
}).catch(() => {});
|
||||
{% endif %}
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Reference in a new issue