feat: redesign dark futuriste (Luca) — thème cyan/volt, sidebar dark, JetBrains Mono + README

This commit is contained in:
Luca
2026-03-15 21:24:59 +01:00
parent 97ae64caf0
commit c8b704216d
12 changed files with 1552 additions and 691 deletions
+610 -18
View File
@@ -4,31 +4,623 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}GarageManager{% endblock %}</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg-primary: #0a0f1a;
--bg-secondary: #111827;
--bg-card: #1a2234;
--bg-input: #0d1321;
--accent-electric: #00d4ff;
--accent-volt: #facc15;
--accent-green: #22c55e;
--accent-red: #ef4444;
--text-primary: #f1f5f9;
--text-secondary: #94a3b8;
--text-muted: #64748b;
--border: #2d3a52;
--shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
--glow-electric: 0 0 20px rgba(0, 212, 255, 0.3);
}
body {
font-family: "Outfit", sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
min-height: 100vh;
display: flex;
}
body::before {
content: "";
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background:
radial-gradient(circle at 20% 20%, rgba(0,212,255,0.08) 0%, transparent 50%),
radial-gradient(circle at 80% 80%, rgba(250,204,21,0.06) 0%, transparent 50%),
linear-gradient(180deg, var(--bg-primary) 0%, var(--bg-secondary) 100%);
pointer-events: none;
z-index: -1;
}
/* ── Sidebar ── */
.app-sidebar {
width: 220px;
background: var(--bg-card);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
position: fixed;
top: 0; left: 0; bottom: 0;
z-index: 100;
}
.sidebar-brand {
padding: 1.25rem 1.25rem 1rem;
font-size: 1.15rem;
font-weight: 700;
background: linear-gradient(135deg, var(--accent-electric), var(--accent-volt));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
gap: 0.5rem;
}
.sidebar-brand i {
background: linear-gradient(135deg, var(--accent-electric), var(--accent-volt));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.sidebar-nav {
flex: 1;
padding: 0.75rem 0.5rem;
}
.sidebar-section {
font-size: 0.65rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--text-muted);
padding: 0.75rem 0.75rem 0.25rem;
font-weight: 600;
}
.sidebar-link {
display: flex;
align-items: center;
gap: 0.6rem;
padding: 0.6rem 0.75rem;
color: var(--text-secondary);
text-decoration: none;
border-radius: 8px;
margin: 0.1rem 0;
transition: all 0.2s;
font-size: 0.9rem;
font-weight: 500;
}
.sidebar-link i {
width: 16px;
text-align: center;
font-size: 0.85rem;
}
.sidebar-link:hover {
background: rgba(0, 212, 255, 0.08);
color: var(--accent-electric);
}
.sidebar-link.active {
background: rgba(0, 212, 255, 0.12);
color: var(--accent-electric);
border: 1px solid rgba(0, 212, 255, 0.2);
}
.sidebar-footer {
padding: 0.75rem 1rem;
border-top: 1px solid var(--border);
font-size: 0.7rem;
color: var(--text-muted);
text-align: center;
}
/* ── Main content ── */
.app-main {
margin-left: 220px;
flex: 1;
padding: 2rem;
min-height: 100vh;
}
/* ── Cards ── */
.card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 1.5rem;
transition: border-color 0.2s, box-shadow 0.2s;
}
.card:hover {
border-color: rgba(0, 212, 255, 0.3);
box-shadow: var(--glow-electric);
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 1.5rem;
border-bottom: 1px solid var(--border);
}
.card-title {
font-size: 0.95rem;
font-weight: 600;
color: var(--text-primary);
}
/* ── Tables ── */
table {
width: 100%;
border-collapse: collapse;
}
th {
background: var(--bg-secondary);
color: var(--text-muted);
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 0.75rem 1rem;
text-align: left;
font-weight: 600;
}
td {
padding: 0.875rem 1rem;
border-bottom: 1px solid var(--border);
color: var(--text-primary);
font-size: 0.88rem;
}
tr:last-child td {
border-bottom: none;
}
tr:hover td {
background: rgba(0, 212, 255, 0.03);
}
tbody tr {
transition: background 0.15s;
}
/* ── Buttons ── */
.btn-primary {
background: linear-gradient(135deg, var(--accent-electric), #0099cc);
color: #0a0f1a;
font-weight: 600;
padding: 0.5rem 1.25rem;
border-radius: 8px;
border: none;
cursor: pointer;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.4rem;
font-size: 0.875rem;
font-family: "Outfit", sans-serif;
transition: box-shadow 0.2s, opacity 0.2s;
}
.btn-primary:hover {
box-shadow: var(--glow-electric);
opacity: 0.95;
}
.btn-secondary {
background: transparent;
color: var(--text-secondary);
border: 1px solid var(--border);
padding: 0.5rem 1.25rem;
border-radius: 8px;
cursor: pointer;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.4rem;
font-size: 0.875rem;
font-family: "Outfit", sans-serif;
transition: border-color 0.2s, color 0.2s;
}
.btn-secondary:hover {
border-color: var(--accent-electric);
color: var(--accent-electric);
}
.btn-danger {
background: rgba(239, 68, 68, 0.1);
color: var(--accent-red);
border: 1px solid rgba(239, 68, 68, 0.3);
padding: 0.5rem 1.25rem;
border-radius: 8px;
cursor: pointer;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.4rem;
font-size: 0.875rem;
font-family: "Outfit", sans-serif;
transition: background 0.2s;
}
.btn-danger:hover {
background: rgba(239, 68, 68, 0.2);
}
.btn-sm {
padding: 0.35rem 0.875rem;
font-size: 0.8rem;
}
.btn-icon {
width: 32px;
height: 32px;
padding: 0;
justify-content: center;
border-radius: 6px;
}
/* ── Inputs ── */
input, select, textarea {
background: var(--bg-input);
border: 1px solid var(--border);
color: var(--text-primary);
border-radius: 8px;
padding: 0.5rem 0.75rem;
font-family: "Outfit", sans-serif;
font-size: 0.875rem;
width: 100%;
transition: border-color 0.2s, box-shadow 0.2s;
}
input:focus, select:focus, textarea:focus {
border-color: var(--accent-electric);
outline: none;
box-shadow: 0 0 0 3px rgba(0, 212, 255, 0.1);
}
input::placeholder, textarea::placeholder {
color: var(--text-muted);
}
select option {
background: var(--bg-secondary);
}
label {
display: block;
font-size: 0.8rem;
font-weight: 600;
color: var(--text-secondary);
margin-bottom: 0.4rem;
text-transform: uppercase;
letter-spacing: 0.04em;
}
/* ── Badges ── */
.badge {
padding: 0.2rem 0.65rem;
border-radius: 20px;
font-size: 0.72rem;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 0.3rem;
}
.badge-success {
background: rgba(34, 197, 94, 0.12);
color: var(--accent-green);
border: 1px solid rgba(34, 197, 94, 0.2);
}
.badge-warning {
background: rgba(250, 204, 21, 0.12);
color: var(--accent-volt);
border: 1px solid rgba(250, 204, 21, 0.2);
}
.badge-danger {
background: rgba(239, 68, 68, 0.12);
color: var(--accent-red);
border: 1px solid rgba(239, 68, 68, 0.2);
}
.badge-info {
background: rgba(0, 212, 255, 0.1);
color: var(--accent-electric);
border: 1px solid rgba(0, 212, 255, 0.2);
}
.badge-neutral {
background: rgba(100, 116, 139, 0.12);
color: var(--text-secondary);
border: 1px solid var(--border);
}
/* ── Stats cards ── */
.stat-value {
font-size: 2.25rem;
font-weight: 700;
color: var(--accent-electric);
line-height: 1;
}
.stat-label {
font-size: 0.8rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.04em;
}
/* ── Mono ── */
.mono {
font-family: "JetBrains Mono", monospace;
}
/* ── Page header ── */
.page-header {
margin-bottom: 2rem;
}
.page-title {
font-size: 1.5rem;
font-weight: 700;
color: var(--text-primary);
}
.page-subtitle {
font-size: 0.875rem;
color: var(--text-muted);
margin-top: 0.25rem;
}
/* ── Back link ── */
.back-link {
display: inline-flex;
align-items: center;
gap: 0.4rem;
color: var(--text-muted);
text-decoration: none;
font-size: 0.85rem;
margin-bottom: 1rem;
transition: color 0.2s;
}
.back-link:hover {
color: var(--accent-electric);
}
/* ── Grid ── */
.grid { display: grid; }
.grid-2 { grid-template-columns: repeat(2, 1fr); gap: 1rem; }
.grid-3 { grid-template-columns: repeat(3, 1fr); gap: 1rem; }
.grid-4 { grid-template-columns: repeat(4, 1fr); gap: 1rem; }
/* ── Flex utils ── */
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.items-start { align-items: flex-start; }
.justify-between { justify-content: space-between; }
.gap-1 { gap: 0.25rem; }
.gap-2 { gap: 0.5rem; }
.gap-3 { gap: 0.75rem; }
.gap-4 { gap: 1rem; }
.flex-1 { flex: 1; }
.flex-wrap { flex-wrap: wrap; }
/* ── Spacing ── */
.mb-1 { margin-bottom: 0.25rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 0.75rem; }
.mb-4 { margin-bottom: 1rem; }
.mb-6 { margin-bottom: 1.5rem; }
.mb-8 { margin-bottom: 2rem; }
.mt-1 { margin-top: 0.25rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-4 { margin-top: 1rem; }
.ml-2 { margin-left: 0.5rem; }
/* ── Text utils ── */
.text-electric { color: var(--accent-electric); }
.text-volt { color: var(--accent-volt); }
.text-green { color: var(--accent-green); }
.text-red { color: var(--accent-red); }
.text-muted { color: var(--text-muted); }
.text-secondary { color: var(--text-secondary); }
.text-xs { font-size: 0.75rem; }
.text-sm { font-size: 0.875rem; }
.text-right { text-align: right; }
.text-center { text-align: center; }
.font-bold { font-weight: 700; }
.font-semibold { font-weight: 600; }
.font-medium { font-weight: 500; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.uppercase { text-transform: uppercase; }
/* ── Empty state ── */
.empty-state {
padding: 3rem;
text-align: center;
color: var(--text-muted);
}
.empty-state i {
font-size: 2.5rem;
margin-bottom: 0.75rem;
display: block;
color: var(--border);
}
.empty-state p {
margin-bottom: 0.5rem;
}
/* ── Overflow ── */
.overflow-x-auto { overflow-x: auto; }
/* ── Link actions ── */
.link-action {
color: var(--accent-electric);
text-decoration: none;
font-size: 0.8rem;
font-weight: 500;
transition: opacity 0.2s;
}
.link-action:hover { opacity: 0.7; }
.link-danger {
color: var(--accent-red);
text-decoration: none;
font-size: 0.8rem;
font-weight: 500;
background: none;
border: none;
cursor: pointer;
font-family: "Outfit", sans-serif;
padding: 0;
transition: opacity 0.2s;
}
.link-danger:hover { opacity: 0.7; }
.link-edit {
color: var(--accent-volt);
text-decoration: none;
font-size: 0.8rem;
font-weight: 500;
transition: opacity 0.2s;
}
.link-edit:hover { opacity: 0.7; }
/* ── Section dividers ── */
.section-divider {
border: none;
border-top: 1px solid var(--border);
margin: 1.5rem 0;
}
/* ── Scrollable list ── */
.scroll-list {
max-height: 200px;
overflow-y: auto;
border: 1px solid var(--border);
border-radius: 8px;
padding: 0.5rem;
background: var(--bg-input);
}
.scroll-list-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.5rem 0.75rem;
border-radius: 6px;
cursor: pointer;
transition: background 0.15s;
}
.scroll-list-item:hover {
background: rgba(0, 212, 255, 0.06);
}
/* ── Toggle section ── */
.toggle-section {
display: none;
background: var(--bg-secondary);
border-top: 1px solid var(--border);
padding: 1.25rem 1.5rem;
}
.toggle-section.open {
display: block;
}
/* ── Collapsible ── */
details summary {
list-style: none;
cursor: pointer;
}
details summary::-webkit-details-marker { display: none; }
/* ── Photo thumb ── */
.photo-thumb {
width: 40px;
height: 40px;
object-fit: cover;
border-radius: 6px;
border: 1px solid var(--border);
cursor: pointer;
}
.photo-placeholder {
width: 40px;
height: 40px;
background: var(--bg-secondary);
border-radius: 6px;
border: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: center;
color: var(--border);
}
</style>
{% block extra_head %}{% endblock %}
</head>
<body class="bg-gray-100 min-h-screen flex">
<div class="w-64 bg-slate-800 min-h-screen flex flex-col fixed left-0 top-0">
<div class="p-6 border-b border-slate-700">
<h1 class="text-white text-xl font-bold flex items-center gap-2">
<i class="fas fa-wrench text-orange-400"></i> GarageManager
</h1>
<body>
<!-- Sidebar -->
<aside class="app-sidebar">
<div class="sidebar-brand">
<i class="fas fa-wrench"></i> GarageManager
</div>
<nav class="flex-1 p-4 space-y-1">
<a href="/" class="flex items-center gap-3 px-4 py-3 rounded-lg text-sm font-medium transition {% if page == 'dashboard' %}bg-orange-500 text-white{% else %}text-slate-300 hover:bg-slate-700 hover:text-white{% endif %}">
<i class="fas fa-chart-line w-5"></i> Dashboard
<nav class="sidebar-nav">
<div class="sidebar-section">Navigation</div>
<a href="/" class="sidebar-link {% if page == 'dashboard' %}active{% endif %}">
<i class="fas fa-chart-line"></i> Dashboard
</a>
<a href="/vehicules" class="flex items-center gap-3 px-4 py-3 rounded-lg text-sm font-medium transition {% if page == 'vehicules' %}bg-orange-500 text-white{% else %}text-slate-300 hover:bg-slate-700 hover:text-white{% endif %}">
<i class="fas fa-car w-5"></i> Véhicules
<a href="/vehicules" class="sidebar-link {% if page == 'vehicules' %}active{% endif %}">
<i class="fas fa-car"></i> Véhicules
</a>
<a href="/interventions" class="flex items-center gap-3 px-4 py-3 rounded-lg text-sm font-medium transition {% if page == 'interventions' %}bg-orange-500 text-white{% else %}text-slate-300 hover:bg-slate-700 hover:text-white{% endif %}">
<i class="fas fa-tools w-5"></i> Interventions
<a href="/interventions" class="sidebar-link {% if page == 'interventions' %}active{% endif %}">
<i class="fas fa-tools"></i> Interventions
</a>
<a href="/clients" class="sidebar-link {% if page == 'clients' %}active{% endif %}">
<i class="fas fa-users"></i> Clients
</a>
</nav>
<div class="p-4 border-t border-slate-700 text-xs text-slate-500 text-center">GarageManager v2.1</div>
</div>
<div class="ml-64 flex-1 p-8">
<div class="sidebar-footer">GarageManager v2.1</div>
</aside>
<!-- Main -->
<main class="app-main">
{% block content %}{% endblock %}
</div>
</main>
</body>
</html>
+55 -30
View File
@@ -1,49 +1,74 @@
{% extends "base.html" %}
{% block title %}{{ client.nom }} {{ client.prenom }} - GarageManager{% endblock %}
{% block title %}{{ client.nom }} {{ client.prenom }} GarageManager{% endblock %}
{% block content %}
<div class="mb-6">
<a href="/clients" class="text-slate-500 hover:text-slate-700 text-sm flex items-center gap-1 mb-4"><i class="fas fa-arrow-left"></i> Retour aux clients</a>
<div class="flex justify-between items-start">
<div>
<h2 class="text-2xl font-bold text-slate-800">{{ client.nom }} {{ client.prenom }}</h2>
<div class="flex flex-wrap gap-4 mt-2 text-sm text-slate-500">
{% if client.telephone %}<span><i class="fas fa-phone mr-1"></i>{{ client.telephone }}</span>{% endif %}
{% if client.email %}<span><i class="fas fa-envelope mr-1"></i>{{ client.email }}</span>{% endif %}
{% if client.adresse %}<span><i class="fas fa-map-marker-alt mr-1"></i>{{ client.adresse }}</span>{% endif %}
</div>
</div>
<div class="flex gap-2">
<a href="/clients/{{ client.id }}/edit" class="border border-orange-300 text-orange-500 px-4 py-2 rounded-lg text-sm hover:bg-orange-50">Modifier</a>
<form method="post" action="/clients/{{ client.id }}/delete" onsubmit="return confirm('Supprimer ce client et tous ses véhicules ?')">
<button class="border border-red-300 text-red-500 px-4 py-2 rounded-lg text-sm hover:bg-red-50">Supprimer</button>
</form>
<a href="/clients" class="back-link"><i class="fas fa-arrow-left"></i> Retour aux clients</a>
<!-- Header -->
<div class="flex justify-between items-start mb-8">
<div>
<h1 class="page-title">{{ client.nom }} {{ client.prenom }}</h1>
<div class="flex gap-4 mt-2" style="flex-wrap:wrap">
{% if client.telephone %}
<span class="text-sm text-secondary"><i class="fas fa-phone mr-1 text-electric"></i>{{ client.telephone }}</span>
{% endif %}
{% if client.email %}
<span class="text-sm text-secondary"><i class="fas fa-envelope mr-1 text-electric"></i>{{ client.email }}</span>
{% endif %}
{% if client.adresse %}
<span class="text-sm text-secondary"><i class="fas fa-map-marker-alt mr-1 text-electric"></i>{{ client.adresse }}</span>
{% endif %}
</div>
</div>
<div class="flex gap-2">
<a href="/clients/{{ client.id }}/edit" class="btn-secondary btn-sm">
<i class="fas fa-edit"></i> Modifier
</a>
<form method="post" action="/clients/{{ client.id }}/delete" onsubmit="return confirm('Supprimer ce client et tous ses véhicules ?')">
<button class="btn-danger btn-sm">
<i class="fas fa-trash"></i> Supprimer
</button>
</form>
</div>
</div>
<div class="bg-white rounded-xl shadow">
<div class="p-6 border-b border-gray-100 flex justify-between items-center">
<h3 class="font-semibold text-slate-800">Véhicules ({{ vehicules|length }})</h3>
<a href="/vehicules/new" class="bg-orange-500 text-white px-4 py-2 rounded-lg text-sm hover:bg-orange-600">+ Ajouter un véhicule</a>
<!-- Véhicules du client -->
<div class="card" style="padding:0">
<div class="card-header">
<span class="card-title">
<i class="fas fa-car text-electric" style="margin-right:0.4rem"></i>
Véhicules
<span class="badge badge-info ml-2">{{ vehicules|length }}</span>
</span>
<a href="/vehicules/new" class="btn-primary btn-sm">
<i class="fas fa-plus"></i> Ajouter
</a>
</div>
{% if vehicules %}
<div class="divide-y divide-gray-100">
<div>
{% for v in vehicules %}
<div class="px-6 py-4 flex justify-between items-center hover:bg-gray-50">
<div style="padding:1rem 1.5rem;border-bottom:1px solid var(--border);display:flex;justify-content:space-between;align-items:center;transition:background 0.15s" onmouseover="this.style.background='rgba(0,212,255,0.03)'" onmouseout="this.style.background=''">
<div>
<span class="font-medium text-slate-800">{{ v.immatriculation }}</span>
<span class="text-slate-500 ml-2">{{ v.marque }} {{ v.modele }}</span>
{% if v.annee %}<span class="text-slate-400 text-sm ml-2">({{ v.annee }})</span>{% endif %}
{% if v.kilometrage %}<span class="text-slate-400 text-sm ml-2">— {{ v.kilometrage }} km</span>{% endif %}
<span class="font-semibold mono" style="color:var(--accent-electric)">{{ v.immatriculation }}</span>
<span class="text-secondary ml-2">{{ v.marque }} {{ v.modele }}</span>
{% if v.annee %}<span class="text-muted text-xs ml-2">({{ v.annee }})</span>{% endif %}
{% if v.kilometrage %}<span class="text-muted text-xs ml-2 mono">— {{ v.kilometrage }} km</span>{% endif %}
</div>
<div class="flex items-center gap-3">
<span class="bg-gray-100 text-gray-600 px-2 py-1 rounded-full text-xs">{{ v.nb_interventions }} intervention(s)</span>
<a href="/vehicules/{{ v.id }}" class="text-blue-500 hover:underline text-sm">Voir →</a>
<span class="badge badge-info">{{ v.nb_interventions }} intervention(s)</span>
<a href="/vehicules/{{ v.id }}" class="link-action text-sm">Voir →</a>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="p-8 text-center text-slate-400 text-sm">Aucun véhicule enregistré pour ce client.</div>
<div class="empty-state">
<i class="fas fa-car"></i>
<p>Aucun véhicule enregistré pour ce client</p>
<a href="/vehicules/new" class="link-action">Ajouter un véhicule →</a>
</div>
{% endif %}
</div>
{% endblock %}
+34 -20
View File
@@ -1,38 +1,52 @@
{% extends "base.html" %}
{% block title %}{% if client %}Modifier{% else %}Nouveau{% endif %} client - GarageManager{% endblock %}
{% block title %}{% if client %}Modifier{% else %}Nouveau{% endif %} client GarageManager{% endblock %}
{% block content %}
<div class="mb-8">
<a href="/clients" class="text-slate-500 hover:text-slate-700 text-sm flex items-center gap-1 mb-4"><i class="fas fa-arrow-left"></i> Retour</a>
<h2 class="text-2xl font-bold text-slate-800">{% if client %}Modifier le client{% else %}Nouveau client{% endif %}</h2>
<a href="/clients" class="back-link"><i class="fas fa-arrow-left"></i> Retour aux clients</a>
<div class="page-header">
<h1 class="page-title">
<i class="fas fa-{% if client %}edit{% else %}user-plus{% endif %} text-electric" style="margin-right:0.5rem"></i>
{% if client %}Modifier le client{% else %}Nouveau client{% endif %}
</h1>
</div>
<div class="bg-white rounded-xl shadow p-8 max-w-2xl">
<form method="post" class="space-y-4">
<div class="grid grid-cols-2 gap-4">
<div class="card" style="max-width:580px">
<form method="post" style="display:flex;flex-direction:column;gap:1.25rem">
<div class="grid grid-2">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Nom *</label>
<input name="nom" value="{{ client.nom if client else '' }}" required class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<label>Nom *</label>
<input name="nom" value="{{ client.nom if client else '' }}" required>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Prénom *</label>
<input name="prenom" value="{{ client.prenom if client else '' }}" required class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<label>Prénom *</label>
<input name="prenom" value="{{ client.prenom if client else '' }}" required>
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Téléphone</label>
<input name="telephone" value="{{ client.telephone if client else '' }}" class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<label>Téléphone</label>
<input name="telephone" value="{{ client.telephone if client else '' }}" placeholder="06 xx xx xx xx">
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Email</label>
<input name="email" type="email" value="{{ client.email if client else '' }}" class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<label>Email</label>
<input name="email" type="email" value="{{ client.email if client else '' }}" placeholder="contact@exemple.fr">
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Adresse</label>
<textarea name="adresse" rows="2" class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">{{ client.adresse if client else '' }}</textarea>
<label>Adresse</label>
<textarea name="adresse" rows="2" placeholder="Adresse complète…">{{ client.adresse if client else '' }}</textarea>
</div>
<div class="flex gap-3 pt-4">
<button type="submit" class="bg-orange-500 text-white px-6 py-2 rounded-lg hover:bg-orange-600 transition text-sm font-medium">Enregistrer</button>
<a href="/clients" class="text-slate-500 px-6 py-2 rounded-lg border border-gray-200 hover:bg-gray-50 text-sm">Annuler</a>
<div style="display:flex;gap:0.75rem;padding-top:0.5rem;border-top:1px solid var(--border)">
<button type="submit" class="btn-primary">
<i class="fas fa-save"></i> Enregistrer
</button>
<a href="/clients" class="btn-secondary">Annuler</a>
</div>
</form>
</div>
{% endblock %}
+55 -34
View File
@@ -1,54 +1,75 @@
{% extends "base.html" %}
{% block title %}Clients - GarageManager{% endblock %}
{% block title %}Clients GarageManager{% endblock %}
{% block content %}
<div class="flex justify-between items-center mb-8">
<div><h2 class="text-2xl font-bold text-slate-800">Clients</h2><p class="text-slate-500 mt-1">{{ clients|length }} client(s)</p></div>
<a href="/clients/new" class="bg-orange-500 text-white px-4 py-2 rounded-lg text-sm hover:bg-orange-600 transition flex items-center gap-2"><i class="fas fa-plus"></i> Nouveau client</a>
</div>
<div class="bg-white rounded-xl shadow">
<div class="p-4 border-b border-gray-100">
<form method="get" class="flex gap-2">
<input name="q" value="{{ q }}" placeholder="Rechercher par nom, téléphone..." class="flex-1 border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<button type="submit" class="bg-slate-700 text-white px-4 py-2 rounded-lg text-sm hover:bg-slate-800">Rechercher</button>
{% if q %}<a href="/clients" class="text-slate-500 px-3 py-2 text-sm hover:text-slate-700 flex items-center"></a>{% endif %}
</form>
<div class="page-header flex justify-between items-center">
<div>
<h1 class="page-title"><i class="fas fa-users text-electric" style="margin-right:0.5rem"></i>Clients</h1>
<p class="page-subtitle">{{ clients|length }} client(s) enregistré(s)</p>
</div>
<a href="/clients/new" class="btn-primary">
<i class="fas fa-plus"></i> Nouveau client
</a>
</div>
<!-- Search -->
<div class="card mb-4" style="padding:1rem 1.25rem">
<form method="get" class="flex gap-2">
<input name="q" value="{{ q }}" placeholder="Rechercher par nom, téléphone…" style="flex:1">
<button type="submit" class="btn-secondary" style="width:auto;white-space:nowrap">
<i class="fas fa-search"></i> Rechercher
</button>
{% if q %}
<a href="/clients" class="btn-secondary" style="width:auto"></a>
{% endif %}
</form>
</div>
<div class="card" style="padding:0">
<div class="overflow-x-auto">
{% if clients %}
<table class="w-full text-sm">
<thead class="bg-gray-50 text-slate-600 text-xs uppercase">
<table>
<thead>
<tr>
<th class="px-6 py-3 text-left">Nom</th>
<th class="px-6 py-3 text-left">Téléphone</th>
<th class="px-6 py-3 text-left">Email</th>
<th class="px-6 py-3 text-left">Véhicules</th>
<th class="px-6 py-3 text-left">Actions</th>
<th>Nom</th>
<th>Téléphone</th>
<th>Email</th>
<th>Véhicules</th>
<th>Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
<tbody>
{% for c in clients %}
<tr class="hover:bg-gray-50">
<td class="px-6 py-4"><a href="/clients/{{ c.id }}" class="font-medium text-slate-800 hover:text-orange-500">{{ c.nom }} {{ c.prenom }}</a></td>
<td class="px-6 py-4 text-slate-600">{{ c.telephone or '-' }}</td>
<td class="px-6 py-4 text-slate-600">{{ c.email or '-' }}</td>
<td class="px-6 py-4"><span class="bg-purple-100 text-purple-700 px-2 py-1 rounded-full text-xs">{{ c.nb_vehicules }}</span></td>
<td class="px-6 py-4 flex gap-3">
<a href="/clients/{{ c.id }}" class="text-blue-500 hover:underline">Voir</a>
<a href="/clients/{{ c.id }}/edit" class="text-orange-500 hover:underline">Modifier</a>
<form method="post" action="/clients/{{ c.id }}/delete" class="inline" onsubmit="return confirm('Supprimer ce client ?')">
<button type="submit" class="text-red-500 hover:underline">Supprimer</button>
</form>
<tr>
<td>
<a href="/clients/{{ c.id }}" class="font-semibold" style="text-decoration:none;color:var(--text-primary)">{{ c.nom }} {{ c.prenom }}</a>
</td>
<td class="text-secondary text-sm">{{ c.telephone or '—' }}</td>
<td class="text-secondary text-sm">{{ c.email or '—' }}</td>
<td>
<span class="badge badge-info">{{ c.nb_vehicules }}</span>
</td>
<td>
<div class="flex gap-3">
<a href="/clients/{{ c.id }}" class="link-action">Voir</a>
<a href="/clients/{{ c.id }}/edit" class="link-edit">Modifier</a>
<form method="post" action="/clients/{{ c.id }}/delete" style="display:inline" onsubmit="return confirm('Supprimer ce client ?')">
<button class="link-danger">Supprimer</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="p-8 text-center text-slate-400">
<i class="fas fa-users text-3xl mb-3"></i><p>Aucun client trouvé</p>
<a href="/clients/new" class="text-orange-500 hover:underline text-sm mt-2 inline-block">Ajouter le premier →</a>
<div class="empty-state">
<i class="fas fa-users"></i>
<p>Aucun client trouvé</p>
<a href="/clients/new" class="link-action">Ajouter le premier →</a>
</div>
{% endif %}
</div>
</div>
{% endblock %}
+89 -59
View File
@@ -1,89 +1,119 @@
{% extends "base.html" %}
{% block title %}Dashboard - GarageManager{% endblock %}
{% block title %}Dashboard GarageManager{% endblock %}
{% block content %}
<div class="mb-8">
<h2 class="text-2xl font-bold text-slate-800">Dashboard</h2>
<p class="text-slate-500 mt-1">Vue d'ensemble de votre garage</p>
<div class="page-header flex justify-between items-center">
<div>
<h1 class="page-title"><i class="fas fa-chart-line text-electric" style="margin-right:0.5rem"></i>Dashboard</h1>
<p class="page-subtitle">Vue d'ensemble de votre garage</p>
</div>
<a href="/interventions/new" class="btn-primary">
<i class="fas fa-plus"></i> Nouvelle intervention
</a>
</div>
<div class="grid grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
<div class="bg-white rounded-xl shadow p-6">
<div class="flex items-center justify-between">
<div><p class="text-sm text-slate-500">Véhicules</p><p class="text-3xl font-bold text-slate-800 mt-1">{{ stats.nb_vehicules }}</p></div>
<div class="bg-purple-100 p-3 rounded-full"><i class="fas fa-car text-purple-500 text-xl"></i></div>
<!-- Stats -->
<div class="grid grid-4 mb-8">
<div class="card" style="cursor:default">
<div class="flex items-center justify-between mb-3">
<div class="stat-label">Véhicules</div>
<div style="width:38px;height:38px;background:rgba(0,212,255,0.1);border-radius:8px;display:flex;align-items:center;justify-content:center">
<i class="fas fa-car text-electric"></i>
</div>
</div>
<a href="/vehicules" class="text-xs text-purple-500 hover:underline mt-3 inline-block">Voir tous →</a>
<div class="stat-value">{{ stats.nb_vehicules }}</div>
<a href="/vehicules" class="link-action text-xs mt-2" style="display:inline-block">Voir tous →</a>
</div>
<div class="bg-white rounded-xl shadow p-6">
<div class="flex items-center justify-between">
<div><p class="text-sm text-slate-500">En attente</p><p class="text-3xl font-bold text-yellow-500 mt-1">{{ stats.nb_en_attente }}</p></div>
<div class="bg-yellow-100 p-3 rounded-full"><i class="fas fa-clock text-yellow-500 text-xl"></i></div>
<div class="card" style="cursor:default">
<div class="flex items-center justify-between mb-3">
<div class="stat-label">Interventions</div>
<div style="width:38px;height:38px;background:rgba(250,204,21,0.1);border-radius:8px;display:flex;align-items:center;justify-content:center">
<i class="fas fa-tools text-volt"></i>
</div>
</div>
<a href="/interventions?statut=en_attente" class="text-xs text-yellow-500 hover:underline mt-3 inline-block">Voir →</a>
<div class="stat-value" style="color:var(--accent-volt)">{{ stats.nb_interventions }}</div>
<a href="/interventions" class="link-action text-xs mt-2" style="display:inline-block;color:var(--accent-volt)">Voir toutes →</a>
</div>
<div class="bg-white rounded-xl shadow p-6">
<div class="flex items-center justify-between">
<div><p class="text-sm text-slate-500">En cours</p><p class="text-3xl font-bold text-blue-500 mt-1">{{ stats.nb_en_cours }}</p></div>
<div class="bg-blue-100 p-3 rounded-full"><i class="fas fa-spinner text-blue-500 text-xl"></i></div>
<div class="card" style="cursor:default">
<div class="flex items-center justify-between mb-3">
<div class="stat-label">Pièces en stock</div>
<div style="width:38px;height:38px;background:rgba(34,197,94,0.1);border-radius:8px;display:flex;align-items:center;justify-content:center">
<i class="fas fa-cog text-green"></i>
</div>
</div>
<a href="/interventions?statut=en_cours" class="text-xs text-blue-500 hover:underline mt-3 inline-block">Voir →</a>
<div class="stat-value" style="color:var(--accent-green)">{{ stats.nb_pieces_stock }}</div>
<span class="text-xs text-muted mt-2" style="display:inline-block">Pièces référencées</span>
</div>
<div class="bg-white rounded-xl shadow p-6">
<div class="flex items-center justify-between">
<div><p class="text-sm text-slate-500">Terminées</p><p class="text-3xl font-bold text-green-500 mt-1">{{ stats.nb_termine }}</p></div>
<div class="bg-green-100 p-3 rounded-full"><i class="fas fa-check-circle text-green-500 text-xl"></i></div>
</div>
<a href="/interventions?statut=termine" class="text-xs text-green-500 hover:underline mt-3 inline-block">Voir →</a>
</div>
<div class="bg-white rounded-xl shadow p-6">
<div class="flex items-center justify-between">
<div><p class="text-sm text-slate-500">Coût pièces</p><p class="text-3xl font-bold text-slate-800 mt-1">{{ "%.0f"|format(stats.cout_pieces) }} €</p></div>
<div class="bg-red-100 p-3 rounded-full"><i class="fas fa-cog text-red-400 text-xl"></i></div>
</div>
</div>
<div class="bg-white rounded-xl shadow p-6">
<div class="flex items-center justify-between">
<div><p class="text-sm text-slate-500">Total dépensé</p><p class="text-3xl font-bold text-orange-500 mt-1">{{ "%.0f"|format(stats.cout_total) }} €</p></div>
<div class="bg-orange-100 p-3 rounded-full"><i class="fas fa-euro-sign text-orange-500 text-xl"></i></div>
<div class="card" style="cursor:default;border-color:rgba(250,204,21,0.2)">
<div class="flex items-center justify-between mb-3">
<div class="stat-label" style="color:var(--accent-volt)">Total dépensé</div>
<div style="width:38px;height:38px;background:rgba(250,204,21,0.1);border-radius:8px;display:flex;align-items:center;justify-content:center">
<i class="fas fa-euro-sign text-volt"></i>
</div>
</div>
<div class="stat-value" style="color:var(--accent-volt)">{{ "%.0f"|format(stats.cout_total) }} €</div>
<span class="text-xs text-muted mt-2" style="display:inline-block">Toutes interventions</span>
</div>
</div>
<div class="bg-white rounded-xl shadow">
<div class="p-6 border-b border-gray-100 flex justify-between items-center">
<h3 class="font-semibold text-slate-800">Interventions récentes</h3>
<a href="/interventions/new" class="bg-orange-500 text-white px-4 py-2 rounded-lg text-sm hover:bg-orange-600 transition">+ Nouvelle</a>
<!-- Interventions récentes -->
<div class="card" style="padding:0">
<div class="card-header">
<span class="card-title"><i class="fas fa-clock-rotate-left text-electric" style="margin-right:0.4rem"></i>Interventions récentes</span>
<a href="/interventions/new" class="btn-primary btn-sm">
<i class="fas fa-plus"></i> Nouvelle
</a>
</div>
{% if recent %}
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead class="bg-gray-50 text-slate-600 text-xs uppercase">
<table>
<thead>
<tr>
<th class="px-6 py-3 text-left">Véhicule</th>
<th class="px-6 py-3 text-left">Description</th>
<th class="px-6 py-3 text-left">Statut</th>
<th class="px-6 py-3 text-left">Coût total</th>
<th>Véhicule</th>
<th>Description</th>
<th>Date</th>
<th>Kilométrage</th>
<th>Pièces</th>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
<tbody>
{% for i in recent %}
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 font-medium text-slate-800"><a href="/vehicules/{{ i.vehicule_id }}" class="hover:text-orange-500">{{ i.immatriculation }}<br><span class="text-xs font-normal text-slate-400">{{ i.marque }} {{ i.modele }}</span></a></td>
<td class="px-6 py-4 text-slate-600"><a href="/interventions/{{ i.id }}" class="hover:text-orange-500">{{ i.description[:60] }}{% if i.description|length > 60 %}...{% endif %}</a></td>
<td class="px-6 py-4">
{% if i.statut == 'en_attente' %}<span class="bg-yellow-100 text-yellow-700 px-2 py-1 rounded-full text-xs">En attente</span>
{% elif i.statut == 'en_cours' %}<span class="bg-blue-100 text-blue-700 px-2 py-1 rounded-full text-xs">En cours</span>
{% else %}<span class="bg-green-100 text-green-700 px-2 py-1 rounded-full text-xs">Terminé</span>{% endif %}
<tr style="cursor:pointer" onclick="window.location='/interventions/{{ i.id }}'">
<td>
<span class="font-semibold mono" style="color:var(--accent-electric);font-size:0.85rem">{{ i.immatriculation }}</span>
<br>
<span class="text-xs text-muted">{{ i.marque }} {{ i.modele }}</span>
</td>
<td style="max-width:280px">
<span class="truncate" style="display:block;max-width:280px">{{ i.description[:60] }}{% if i.description|length > 60 %}…{% endif %}</span>
</td>
<td class="text-xs text-secondary">{{ i.date_intervention or '—' }}</td>
<td class="text-xs text-secondary mono">{{ (i.kilometrage_intervention|string + ' km') if i.kilometrage_intervention else '—' }}</td>
<td>
<span class="text-xs text-secondary">{{ i.nb_pieces }} pièce(s)</span>
<br>
<span class="text-xs text-muted">{{ "%.0f"|format(i.cout_pieces) }} €</span>
</td>
<td class="text-right">
<span class="font-semibold" style="color:var(--accent-volt)">{{ "%.0f"|format(i.cout_main_oeuvre + i.cout_pieces) }} €</span>
</td>
<td class="px-6 py-4 font-semibold text-slate-700">{{ "%.0f"|format(i.cout_main_oeuvre + i.cout_pieces) }} €</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="p-8 text-center text-slate-400">
<i class="fas fa-tools text-3xl mb-3"></i><p>Aucune intervention pour l'instant</p>
<a href="/interventions/new" class="text-orange-500 hover:underline text-sm mt-2 inline-block">Créer la première →</a>
<div class="empty-state">
<i class="fas fa-tools"></i>
<p>Aucune intervention pour l'instant</p>
<a href="/interventions/new" class="link-action">Créer la première →</a>
</div>
{% endif %}
</div>
{% endblock %}
+145 -84
View File
@@ -1,142 +1,203 @@
{% extends "base.html" %}
{% block title %}Intervention #{{ intervention.id }} - GarageManager{% endblock %}
{% block title %}Intervention GarageManager{% endblock %}
{% block content %}
<div class="mb-6">
<a href="/vehicules/{{ intervention.vid }}" class="text-slate-500 hover:text-slate-700 text-sm flex items-center gap-1 mb-4"><i class="fas fa-arrow-left"></i> Retour au véhicule</a>
<div class="flex justify-between items-start">
<div>
<h2 class="text-2xl font-bold text-slate-800">{{ intervention.description }}</h2>
<p class="text-slate-500 mt-1">{{ intervention.immatriculation }} — {{ intervention.marque }} {{ intervention.modele }}</p>
<p class="text-xs text-slate-400 mt-1">{{ intervention.created_at[:10] }}{% if intervention.technicien %} • <i class="fas fa-user-cog mr-1"></i>{{ intervention.technicien }}{% endif %}</p>
</div>
<div class="flex gap-2 items-center">
{% if intervention.statut == 'en_attente' %}<span class="bg-yellow-100 text-yellow-700 px-3 py-1 rounded-full text-sm">⏳ En attente</span>
{% elif intervention.statut == 'en_cours' %}<span class="bg-blue-100 text-blue-700 px-3 py-1 rounded-full text-sm">🔧 En cours</span>
{% else %}<span class="bg-green-100 text-green-700 px-3 py-1 rounded-full text-sm">✅ Terminé</span>{% endif %}
<a href="/interventions/{{ intervention.id }}/edit" class="border border-orange-300 text-orange-500 px-4 py-2 rounded-lg text-sm hover:bg-orange-50">Modifier</a>
<form method="post" action="/interventions/{{ intervention.id }}/delete" onsubmit="return confirm('Supprimer cette intervention ?')">
<button class="border border-red-300 text-red-500 px-4 py-2 rounded-lg text-sm hover:bg-red-50">Supprimer</button>
</form>
<a href="/vehicules/{{ intervention.vid }}" class="back-link"><i class="fas fa-arrow-left"></i> Retour au véhicule</a>
<!-- Header -->
<div class="flex justify-between items-start mb-8">
<div>
<h1 class="page-title" style="max-width:600px">{{ intervention.description }}</h1>
<p class="page-subtitle" style="margin-top:0.4rem">
<a href="/vehicules/{{ intervention.vid }}" class="mono" style="color:var(--accent-electric);text-decoration:none;font-size:0.95rem">{{ intervention.immatriculation }}</a>
<span class="text-muted"> — {{ intervention.marque }} {{ intervention.modele }}</span>
</p>
<div class="flex gap-4 mt-2" style="flex-wrap:wrap">
{% if intervention.date_intervention %}
<span class="text-xs text-secondary"><i class="fas fa-calendar mr-1"></i>{{ intervention.date_intervention }}</span>
{% endif %}
{% if intervention.kilometrage_intervention %}
<span class="text-xs text-secondary mono"><i class="fas fa-tachometer-alt mr-1"></i>{{ intervention.kilometrage_intervention }} km</span>
{% endif %}
{% if intervention.technicien %}
<span class="text-xs text-secondary"><i class="fas fa-user-cog mr-1"></i>{{ intervention.technicien }}</span>
{% endif %}
</div>
</div>
<div class="flex gap-2">
<a href="/interventions/{{ intervention.id }}/edit" class="btn-secondary btn-sm">
<i class="fas fa-edit"></i> Modifier
</a>
<form method="post" action="/interventions/{{ intervention.id }}/delete" onsubmit="return confirm('Supprimer cette intervention ?')">
<button class="btn-danger btn-sm">
<i class="fas fa-trash"></i> Supprimer
</button>
</form>
</div>
</div>
<!-- Résumé coûts -->
<div class="grid grid-cols-3 gap-4 mb-6">
<div class="bg-white rounded-xl shadow p-5">
<p class="text-xs text-slate-500">Main d'œuvre</p>
<p class="text-2xl font-bold text-slate-800 mt-1">{{ "%.2f"|format(intervention.cout_main_oeuvre) }} €</p>
<div class="grid grid-3 mb-6">
<div class="card" style="cursor:default">
<div class="stat-label mb-2">Main d'œuvre</div>
<div class="stat-value" style="color:var(--accent-electric)">{{ "%.2f"|format(intervention.cout_main_oeuvre) }} €</div>
</div>
<div class="bg-white rounded-xl shadow p-5">
<p class="text-xs text-slate-500">Pièces</p>
<p class="text-2xl font-bold text-slate-800 mt-1">{{ "%.2f"|format(intervention.cout_pieces) }} €</p>
<div class="card" style="cursor:default">
<div class="stat-label mb-2">Pièces</div>
<div class="stat-value" style="color:var(--text-secondary)">{{ "%.2f"|format(intervention.cout_pieces) }} €</div>
</div>
<div class="bg-orange-50 rounded-xl shadow p-5 border border-orange-200">
<p class="text-xs text-orange-600">Total</p>
<p class="text-2xl font-bold text-orange-600 mt-1">{{ "%.2f"|format(intervention.cout_main_oeuvre + intervention.cout_pieces) }} €</p>
<div class="card" style="cursor:default;border-color:rgba(250,204,21,0.25)">
<div class="stat-label mb-2" style="color:var(--accent-volt)">Total</div>
<div class="stat-value" style="color:var(--accent-volt)">{{ "%.2f"|format(intervention.cout_main_oeuvre + intervention.cout_pieces) }} €</div>
</div>
</div>
{% if intervention.notes %}
<div class="bg-white rounded-xl shadow p-5 mb-6">
<h3 class="font-semibold text-slate-800 mb-2">📋 Notes</h3>
<p class="text-sm text-slate-600 whitespace-pre-wrap">{{ intervention.notes }}</p>
<div class="card mb-6">
<h3 class="card-title mb-3"><i class="fas fa-clipboard-list text-secondary" style="margin-right:0.4rem"></i>Notes</h3>
<p class="text-sm" style="white-space:pre-wrap;color:var(--text-secondary)">{{ intervention.notes }}</p>
</div>
{% endif %}
<!-- Pièces -->
<div class="bg-white rounded-xl shadow">
<div class="p-5 border-b border-gray-100 flex justify-between items-center">
<h3 class="font-semibold text-slate-800">🔩 Pièces utilisées ({{ pieces|length }})</h3>
<button onclick="document.getElementById('add-piece-form').classList.toggle('hidden')" class="bg-orange-500 text-white px-4 py-2 rounded-lg text-sm hover:bg-orange-600">+ Ajouter une pièce</button>
<!-- ── Pièces utilisées ── -->
<div class="card" style="padding:0">
<div class="card-header">
<span class="card-title">
<i class="fas fa-cog text-volt" style="margin-right:0.4rem"></i>
Pièces utilisées
<span class="badge badge-warning ml-2">{{ pieces_utilisees|length }}</span>
</span>
<button onclick="toggleAddPiece()" class="btn-primary btn-sm">
<i class="fas fa-plus"></i> Ajouter une pièce
</button>
</div>
<!-- Formulaire ajout pièce -->
<div id="add-piece-form" class="hidden p-5 border-b border-gray-100 bg-gray-50">
<!-- Form ajout pièce -->
<div id="add-piece-form" style="display:none;background:var(--bg-secondary);border-bottom:1px solid var(--border);padding:1.25rem 1.5rem">
<form method="post" action="/interventions/{{ intervention.id }}/pieces/add" enctype="multipart/form-data">
<div class="grid grid-cols-2 gap-3 mb-3">
{% if pieces_connues %}
<div class="mb-4">
<p class="text-xs font-semibold uppercase" style="color:var(--text-muted);letter-spacing:0.06em;margin-bottom:0.5rem">
🔄 Réutiliser une pièce connue sur ce véhicule
</p>
<div class="scroll-list">
{% for p in pieces_connues %}
<label class="scroll-list-item">
<input type="radio" name="_prefill" value="{{ loop.index0 }}"
data-nom="{{ p.nom }}" data-ref="{{ p.reference or '' }}" data-marque="{{ p.marque or '' }}" data-prix="{{ p.prix }}"
onchange="prefillPiece(this)"
style="width:auto;background:none;border:none;padding:0">
<span class="font-medium text-sm" style="flex:1">{{ p.nom }}</span>
{% if p.reference %}<span class="mono text-xs text-muted">{{ p.reference }}</span>{% endif %}
{% if p.marque %}<span class="text-xs text-secondary">{{ p.marque }}</span>{% endif %}
<span class="font-semibold text-sm text-electric mono">{{ "%.2f"|format(p.prix) }} €</span>
</label>
{% endfor %}
</div>
<p class="text-xs text-muted mt-1">Les infos se pré-remplissent automatiquement, tu peux les modifier.</p>
</div>
{% endif %}
<div class="grid grid-2 mb-3">
<div>
<label class="block text-xs font-medium text-slate-600 mb-1">Nom de la pièce *</label>
<input name="nom" required placeholder="Ex: Filtre à huile, Courroie..." class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<label>Nom *</label>
<input id="p-nom" name="nom" required placeholder="Nom de la pièce">
</div>
<div>
<label class="block text-xs font-medium text-slate-600 mb-1">Référence</label>
<input name="reference" placeholder="Ex: OC90, 06A115561B..." class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-orange-300">
<label>Référence</label>
<input id="p-ref" name="reference" placeholder="Ex: 06A115561B" style="font-family:'JetBrains Mono',monospace">
</div>
</div>
<div class="grid grid-cols-3 gap-3 mb-3">
<div class="grid grid-3 mb-3">
<div>
<label class="block text-xs font-medium text-slate-600 mb-1">Marque</label>
<input name="marque" placeholder="Ex: Bosch, NGK, Valeo..." class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<label>Marque</label>
<input id="p-marque" name="marque" placeholder="Bosch, NGK…">
</div>
<div>
<label class="block text-xs font-medium text-slate-600 mb-1">Prix unitaire (€)</label>
<input name="prix" type="number" step="0.01" min="0" value="0" class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<label>Prix unitaire (€)</label>
<input id="p-prix" name="prix" type="number" step="0.01" min="0" value="0" class="mono">
</div>
<div>
<label class="block text-xs font-medium text-slate-600 mb-1">Quantité</label>
<input name="quantite" type="number" min="1" value="1" class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<label>Quantité</label>
<input name="quantite" type="number" min="1" value="1" class="mono">
</div>
</div>
<div class="mb-3">
<label class="block text-xs font-medium text-slate-600 mb-1">Photo (optionnel)</label>
<input type="file" name="photo" accept="image/*" class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm">
<label>Photo (optionnel)</label>
<input type="file" name="photo" accept="image/*" style="background:none;border:none;padding:0.25rem 0;color:var(--text-secondary)">
</div>
<button type="submit" class="bg-orange-500 text-white px-5 py-2 rounded-lg text-sm hover:bg-orange-600">Ajouter la pièce</button>
<button type="submit" class="btn-primary btn-sm">
<i class="fas fa-plus"></i> Ajouter la pièce
</button>
</form>
</div>
<!-- Liste des pièces -->
{% if pieces %}
{% if pieces_utilisees %}
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead class="bg-gray-50 text-slate-600 text-xs uppercase">
<table>
<thead>
<tr>
<th class="px-5 py-3 text-left">Photo</th>
<th class="px-5 py-3 text-left">Pièce</th>
<th class="px-5 py-3 text-left">Référence</th>
<th class="px-5 py-3 text-left">Marque</th>
<th class="px-5 py-3 text-left">Qté</th>
<th class="px-5 py-3 text-left">Prix unit.</th>
<th class="px-5 py-3 text-left">Total</th>
<th class="px-5 py-3 text-left"></th>
<th>Photo</th>
<th>Pièce</th>
<th>Référence</th>
<th>Marque</th>
<th class="text-center">Qté</th>
<th class="text-right">Prix unit.</th>
<th class="text-right">Total</th>
<th></th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
{% for p in pieces %}
<tr class="hover:bg-gray-50">
<td class="px-5 py-3">
<tbody>
{% for p in pieces_utilisees %}
<tr>
<td>
{% if p.photo %}
<img src="{{ p.photo }}" alt="{{ p.nom }}" class="w-12 h-12 object-cover rounded-lg cursor-pointer border" onclick="window.open('{{ p.photo }}','_blank')">
<img src="{{ p.photo }}" class="photo-thumb" style="width:48px;height:48px" onclick="window.open('{{ p.photo }}','_blank')">
{% else %}
<div class="w-12 h-12 bg-gray-100 rounded-lg flex items-center justify-center text-slate-300"><i class="fas fa-cog"></i></div>
<div class="photo-placeholder" style="width:48px;height:48px"><i class="fas fa-cog"></i></div>
{% endif %}
</td>
<td class="px-5 py-3 font-medium text-slate-800">{{ p.nom }}</td>
<td class="px-5 py-3 text-slate-500 font-mono text-xs">{{ p.reference or '-' }}</td>
<td class="px-5 py-3 text-slate-600">{{ p.marque or '-' }}</td>
<td class="px-5 py-3 text-slate-600">{{ p.quantite }}</td>
<td class="px-5 py-3 text-slate-600">{{ "%.2f"|format(p.prix) }} €</td>
<td class="px-5 py-3 font-semibold text-slate-800">{{ "%.2f"|format(p.prix * p.quantite) }} €</td>
<td class="px-5 py-3">
<td class="font-medium">{{ p.nom }}</td>
<td class="mono text-xs text-muted">{{ p.reference or '' }}</td>
<td class="text-secondary text-sm">{{ p.marque or '' }}</td>
<td class="text-center text-secondary mono">{{ p.quantite }}</td>
<td class="text-right mono text-sm">{{ "%.2f"|format(p.prix) }} €</td>
<td class="text-right font-semibold mono" style="color:var(--accent-volt)">{{ "%.2f"|format(p.prix * p.quantite) }} €</td>
<td>
<form method="post" action="/interventions/{{ intervention.id }}/pieces/{{ p.id }}/delete" onsubmit="return confirm('Supprimer cette pièce ?')">
<button class="text-red-400 hover:text-red-600 text-xs hover:underline">Supprimer</button>
<button class="link-danger"></button>
</form>
</td>
</tr>
{% endfor %}
<tr class="bg-orange-50">
<td colspan="6" class="px-5 py-3 text-right font-semibold text-slate-700">Total pièces</td>
<td colspan="2" class="px-5 py-3 font-bold text-orange-600">{{ "%.2f"|format(intervention.cout_pieces) }} €</td>
<tr style="background:var(--bg-secondary)">
<td colspan="6" class="text-right font-semibold text-secondary text-sm">Total pièces</td>
<td class="text-right font-bold mono" style="color:var(--accent-volt)">{{ "%.2f"|format(intervention.cout_pieces) }} €</td>
<td></td>
</tr>
</tbody>
</table>
</div>
{% else %}
<div class="p-8 text-center text-slate-400 text-sm">
Aucune pièce enregistrée.<br>
<button onclick="document.getElementById('add-piece-form').classList.remove('hidden')" class="text-orange-500 hover:underline mt-1 inline-block">Ajouter la première →</button>
<div class="empty-state">
<i class="fas fa-cog"></i>
<p>Aucune pièce pour cette intervention</p>
<button onclick="toggleAddPiece()" class="link-action">Ajouter →</button>
</div>
{% endif %}
</div>
<script>
function toggleAddPiece() {
const f = document.getElementById('add-piece-form');
f.style.display = f.style.display === 'none' ? 'block' : 'none';
}
function prefillPiece(radio) {
document.getElementById('p-nom').value = radio.dataset.nom;
document.getElementById('p-ref').value = radio.dataset.ref;
document.getElementById('p-marque').value = radio.dataset.marque;
document.getElementById('p-prix').value = radio.dataset.prix;
}
</script>
{% endblock %}
+45 -28
View File
@@ -1,51 +1,68 @@
{% extends "base.html" %}
{% block title %}{% if intervention %}Modifier{% else %}Nouvelle{% endif %} intervention - GarageManager{% endblock %}
{% block title %}{% if intervention %}Modifier{% else %}Nouvelle{% endif %} intervention GarageManager{% endblock %}
{% block content %}
<div class="mb-8">
<a href="/interventions" class="text-slate-500 hover:text-slate-700 text-sm flex items-center gap-1 mb-4"><i class="fas fa-arrow-left"></i> Retour</a>
<h2 class="text-2xl font-bold text-slate-800">{% if intervention %}Modifier l'intervention{% else %}Nouvelle intervention{% endif %}</h2>
<a href="/interventions" class="back-link"><i class="fas fa-arrow-left"></i> Retour aux interventions</a>
<div class="page-header">
<h1 class="page-title">
<i class="fas fa-{% if intervention %}edit{% else %}plus-circle{% endif %} text-electric" style="margin-right:0.5rem"></i>
{% if intervention %}Modifier l'intervention{% else %}Nouvelle intervention{% endif %}
</h1>
</div>
<div class="bg-white rounded-xl shadow p-8 max-w-2xl">
<form method="post" class="space-y-4">
<div class="card" style="max-width:680px">
<form method="post" style="display:flex;flex-direction:column;gap:1.25rem">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Véhicule *</label>
<select name="vehicule_id" required class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<option value="">-- Sélectionner un véhicule --</option>
<label>Véhicule *</label>
<select name="vehicule_id" required>
<option value=""> Sélectionner un véhicule </option>
{% for v in vehicules %}
<option value="{{ v.id }}" {% if (intervention and intervention.vehicule_id == v.id) or preselect_vehicule == v.id %}selected{% endif %}>{{ v.label }}</option>
{% endfor %}
</select>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Description *</label>
<textarea name="description" rows="2" required placeholder="Ex: Vidange + filtre à huile, Changement courroie de distribution..." class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">{{ intervention.description if intervention else '' }}</textarea>
<label>Description *</label>
<textarea name="description" rows="2" required placeholder="Ex: Vidange + filtre à huile, Changement courroie de distribution">{{ intervention.description if intervention else '' }}</textarea>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="grid grid-2">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Statut</label>
<select name="statut" class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<option value="en_attente" {% if not intervention or intervention.statut == 'en_attente' %}selected{% endif %}>⏳ En attente</option>
<option value="en_cours" {% if intervention and intervention.statut == 'en_cours' %}selected{% endif %}>🔧 En cours</option>
<option value="termine" {% if intervention and intervention.statut == 'termine' %}selected{% endif %}>✅ Terminé</option>
</select>
<label>Date de l'intervention</label>
<input name="date_intervention" type="date" value="{{ intervention.date_intervention if intervention and intervention.date_intervention else '' }}">
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Main d'œuvre ()</label>
<input name="cout_main_oeuvre" type="number" step="0.01" min="0" value="{{ intervention.cout_main_oeuvre if intervention else '0' }}" class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<label>Kilométrage (km)</label>
<input name="kilometrage_intervention" type="number" value="{{ intervention.kilometrage_intervention if intervention and intervention.kilometrage_intervention else '' }}" placeholder="85000" class="mono">
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Technicien</label>
<input name="technicien" value="{{ intervention.technicien if intervention else '' }}" placeholder="Nom du technicien / garage" class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<div class="grid grid-2">
<div>
<label>Main d'œuvre (€)</label>
<input name="cout_main_oeuvre" type="number" step="0.01" min="0" value="{{ intervention.cout_main_oeuvre if intervention else '0' }}" class="mono">
</div>
<div>
<label>Technicien / Garage</label>
<input name="technicien" value="{{ intervention.technicien if intervention else '' }}" placeholder="Nom du garage, soi-même…">
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Notes</label>
<textarea name="notes" rows="3" placeholder="Observations, prochaine révision prévue, problèmes restants..." class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">{{ intervention.notes if intervention else '' }}</textarea>
<label>Notes</label>
<textarea name="notes" rows="3" placeholder="Observations, prochaine révision, problèmes restants">{{ intervention.notes if intervention else '' }}</textarea>
</div>
<div class="flex gap-3 pt-4">
<button type="submit" class="bg-orange-500 text-white px-6 py-2 rounded-lg hover:bg-orange-600 transition text-sm font-medium">Enregistrer</button>
<a href="/interventions" class="text-slate-500 px-6 py-2 rounded-lg border border-gray-200 hover:bg-gray-50 text-sm">Annuler</a>
<div style="display:flex;gap:0.75rem;padding-top:0.5rem;border-top:1px solid var(--border)">
<button type="submit" class="btn-primary">
<i class="fas fa-save"></i> Enregistrer
</button>
<a href="/interventions" class="btn-secondary">Annuler</a>
</div>
</form>
</div>
{% endblock %}
+72 -58
View File
@@ -1,63 +1,77 @@
{% extends "base.html" %}
{% block title %}Interventions - GarageManager{% endblock %}
{% block title %}Interventions GarageManager{% endblock %}
{% block content %}
<div class="flex justify-between items-center mb-8">
<div><h2 class="text-2xl font-bold text-slate-800">Interventions</h2><p class="text-slate-500 mt-1">{{ interventions|length }} intervention(s)</p></div>
<a href="/interventions/new" class="bg-orange-500 text-white px-4 py-2 rounded-lg text-sm hover:bg-orange-600 flex items-center gap-2"><i class="fas fa-plus"></i> Nouvelle intervention</a>
</div>
<div class="flex gap-2 mb-6 flex-wrap">
<a href="/interventions" class="px-4 py-2 rounded-full text-sm {% if not statut %}bg-slate-800 text-white{% else %}bg-white text-slate-600 border hover:bg-gray-50{% endif %}">Toutes</a>
<a href="/interventions?statut=en_attente" class="px-4 py-2 rounded-full text-sm {% if statut == 'en_attente' %}bg-yellow-500 text-white{% else %}bg-white text-slate-600 border hover:bg-gray-50{% endif %}">⏳ En attente</a>
<a href="/interventions?statut=en_cours" class="px-4 py-2 rounded-full text-sm {% if statut == 'en_cours' %}bg-blue-500 text-white{% else %}bg-white text-slate-600 border hover:bg-gray-50{% endif %}">🔧 En cours</a>
<a href="/interventions?statut=termine" class="px-4 py-2 rounded-full text-sm {% if statut == 'termine' %}bg-green-500 text-white{% else %}bg-white text-slate-600 border hover:bg-gray-50{% endif %}">✅ Terminées</a>
</div>
<div class="bg-white rounded-xl shadow overflow-x-auto">
{% if interventions %}
<table class="w-full text-sm">
<thead class="bg-gray-50 text-slate-600 text-xs uppercase">
<tr>
<th class="px-6 py-3 text-left">Véhicule</th>
<th class="px-6 py-3 text-left">Description</th>
<th class="px-6 py-3 text-left">Statut</th>
<th class="px-6 py-3 text-left">Pièces</th>
<th class="px-6 py-3 text-left">Total</th>
<th class="px-6 py-3 text-left">Date</th>
<th class="px-6 py-3 text-left">Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
{% for i in interventions %}
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 font-medium text-slate-800">
<a href="/vehicules/{{ i.vehicule_id }}" class="hover:text-orange-500">{{ i.immatriculation }}<br><span class="text-xs font-normal text-slate-400">{{ i.marque }} {{ i.modele }}</span></a>
</td>
<td class="px-6 py-4 text-slate-600 max-w-xs">
<a href="/interventions/{{ i.id }}" class="hover:text-orange-500">{{ i.description[:60] }}{% if i.description|length > 60 %}...{% endif %}</a>
</td>
<td class="px-6 py-4">
{% if i.statut == 'en_attente' %}<span class="bg-yellow-100 text-yellow-700 px-2 py-1 rounded-full text-xs">En attente</span>
{% elif i.statut == 'en_cours' %}<span class="bg-blue-100 text-blue-700 px-2 py-1 rounded-full text-xs">En cours</span>
{% else %}<span class="bg-green-100 text-green-700 px-2 py-1 rounded-full text-xs">Terminé</span>{% endif %}
</td>
<td class="px-6 py-4 text-slate-500 text-xs">{{ "%.2f"|format(i.cout_pieces) }} €</td>
<td class="px-6 py-4 font-semibold text-slate-700">{{ "%.0f"|format(i.cout_main_oeuvre + i.cout_pieces) }} €</td>
<td class="px-6 py-4 text-slate-400 text-xs">{{ i.created_at[:10] }}</td>
<td class="px-6 py-4 flex gap-3">
<a href="/interventions/{{ i.id }}" class="text-blue-500 hover:underline">Voir</a>
<a href="/interventions/{{ i.id }}/edit" class="text-orange-500 hover:underline">Modifier</a>
<form method="post" action="/interventions/{{ i.id }}/delete" class="inline" onsubmit="return confirm('Supprimer ?')">
<button class="text-red-500 hover:underline">Supprimer</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="p-8 text-center text-slate-400">
<i class="fas fa-tools text-3xl mb-3"></i><p>Aucune intervention trouvée</p>
<a href="/interventions/new" class="text-orange-500 hover:underline text-sm mt-2 inline-block">Créer la première →</a>
<div class="page-header flex justify-between items-center">
<div>
<h1 class="page-title"><i class="fas fa-tools text-electric" style="margin-right:0.5rem"></i>Interventions</h1>
<p class="page-subtitle">{{ interventions|length }} intervention(s)</p>
</div>
{% endif %}
<a href="/interventions/new" class="btn-primary">
<i class="fas fa-plus"></i> Nouvelle intervention
</a>
</div>
<div class="card" style="padding:0">
<div class="overflow-x-auto">
{% if interventions %}
<table>
<thead>
<tr>
<th>Véhicule</th>
<th>Description</th>
<th>Date</th>
<th>Kilométrage</th>
<th>Pièces</th>
<th class="text-right">Total</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for i in interventions %}
<tr>
<td>
<a href="/vehicules/{{ i.vehicule_id }}" style="text-decoration:none">
<span class="font-semibold mono" style="color:var(--accent-electric);font-size:0.85rem">{{ i.immatriculation }}</span>
<br>
<span class="text-xs text-muted">{{ i.marque }} {{ i.modele }}</span>
</a>
</td>
<td style="max-width:260px">
<a href="/interventions/{{ i.id }}" class="font-medium" style="text-decoration:none;color:var(--text-primary)">
{{ i.description[:60] }}{% if i.description|length > 60 %}…{% endif %}
</a>
</td>
<td class="text-secondary text-xs">{{ i.date_intervention or '—' }}</td>
<td class="mono text-xs text-secondary">{{ (i.kilometrage_intervention|string + ' km') if i.kilometrage_intervention else '—' }}</td>
<td>
<span class="text-xs text-secondary">{{ i.nb_pieces }} pièce(s)</span><br>
<span class="text-xs text-muted">{{ "%.2f"|format(i.cout_pieces) }} €</span>
</td>
<td class="text-right font-semibold" style="color:var(--accent-volt)">
{{ "%.0f"|format(i.cout_main_oeuvre + i.cout_pieces) }} €
</td>
<td>
<div class="flex gap-3">
<a href="/interventions/{{ i.id }}" class="link-action">Voir</a>
<a href="/interventions/{{ i.id }}/edit" class="link-edit">Modifier</a>
<form method="post" action="/interventions/{{ i.id }}/delete" style="display:inline" onsubmit="return confirm('Supprimer cette intervention ?')">
<button class="link-danger">Supprimer</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="empty-state">
<i class="fas fa-tools"></i>
<p>Aucune intervention pour l'instant</p>
<a href="/interventions/new" class="link-action">Créer la première →</a>
</div>
{% endif %}
</div>
</div>
{% endblock %}
+219 -99
View File
@@ -1,128 +1,248 @@
{% extends "base.html" %}
{% block title %}{{ vehicule.immatriculation }} - GarageManager{% endblock %}
{% block title %}{{ vehicule.immatriculation }} GarageManager{% endblock %}
{% block content %}
<div class="mb-6">
<a href="/vehicules" class="text-slate-500 hover:text-slate-700 text-sm flex items-center gap-1 mb-4"><i class="fas fa-arrow-left"></i> Retour aux véhicules</a>
<div class="flex justify-between items-start">
<div>
<h2 class="text-2xl font-bold text-slate-800">{{ vehicule.immatriculation }}</h2>
<p class="text-slate-500 mt-1">{{ vehicule.marque }} {{ vehicule.modele }}{% if vehicule.annee %} ({{ vehicule.annee }}){% endif %}{% if vehicule.kilometrage %} • {{ vehicule.kilometrage }} km{% endif %}</p>
{% if vehicule.vin %}<p class="text-xs font-mono text-slate-400 mt-1">VIN : {{ vehicule.vin }}</p>{% endif %}
</div>
<div class="flex gap-2">
<a href="/interventions/new?vehicule_id={{ vehicule.id }}" class="bg-orange-500 text-white px-4 py-2 rounded-lg text-sm hover:bg-orange-600">+ Intervention</a>
<a href="/vehicules/{{ vehicule.id }}/edit" class="border border-orange-300 text-orange-500 px-4 py-2 rounded-lg text-sm hover:bg-orange-50">Modifier</a>
<form method="post" action="/vehicules/{{ vehicule.id }}/delete" onsubmit="return confirm('Supprimer ce véhicule ?')">
<button class="border border-red-300 text-red-500 px-4 py-2 rounded-lg text-sm hover:bg-red-50">Supprimer</button>
</form>
</div>
<a href="/vehicules" class="back-link"><i class="fas fa-arrow-left"></i> Retour aux véhicules</a>
<!-- Header véhicule -->
<div class="flex justify-between items-start mb-8">
<div>
<h1 class="page-title mono" style="color:var(--accent-electric);font-size:2rem;letter-spacing:0.06em">{{ vehicule.immatriculation }}</h1>
<p class="page-subtitle" style="margin-top:0.35rem">
{{ vehicule.marque }} {{ vehicule.modele }}
{% if vehicule.annee %} — {{ vehicule.annee }}{% endif %}
{% if vehicule.kilometrage %} — <span class="mono">{{ vehicule.kilometrage }} km</span>{% endif %}
</p>
{% if vehicule.vin %}
<p class="text-xs text-muted mono" style="margin-top:0.35rem">VIN : {{ vehicule.vin }}</p>
{% endif %}
</div>
<div class="flex gap-2">
<a href="/interventions/new?vehicule_id={{ vehicule.id }}" class="btn-primary btn-sm">
<i class="fas fa-plus"></i> Intervention
</a>
<a href="/vehicules/{{ vehicule.id }}/edit" class="btn-secondary btn-sm">
<i class="fas fa-edit"></i> Modifier
</a>
<form method="post" action="/vehicules/{{ vehicule.id }}/delete" onsubmit="return confirm('Supprimer ce véhicule et toutes ses données ?')">
<button class="btn-danger btn-sm">
<i class="fas fa-trash"></i> Supprimer
</button>
</form>
</div>
</div>
<!-- Infos / Notes -->
<div class="bg-white rounded-xl shadow mb-6">
<div class="p-5 border-b border-gray-100 flex justify-between items-center">
<h3 class="font-semibold text-slate-800">📎 Infos & Notes</h3>
<button onclick="document.getElementById('add-info-form').classList.toggle('hidden')" class="text-orange-500 text-sm hover:underline">+ Ajouter</button>
<!-- ── Historique interventions ── -->
<div class="card mb-6" style="padding:0" id="interventions">
<div class="card-header">
<span class="card-title">
<i class="fas fa-tools text-electric" style="margin-right:0.4rem"></i>
Historique interventions
<span class="badge badge-info ml-2">{{ interventions|length }}</span>
</span>
<a href="/interventions/new?vehicule_id={{ vehicule.id }}" class="link-action text-xs">+ Nouvelle</a>
</div>
<div id="add-info-form" class="hidden p-5 border-b border-gray-100 bg-gray-50">
<form method="post" action="/vehicules/{{ vehicule.id }}/infos/add" enctype="multipart/form-data" class="space-y-3">
<div class="grid grid-cols-3 gap-3">
<div>
<label class="block text-xs font-medium text-slate-600 mb-1">Type</label>
<select name="type" id="info-type" onchange="toggleInfoType()" class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm">
<option value="texte">📝 Texte</option>
<option value="url">🔗 URL / Lien</option>
<option value="image">🖼️ Image</option>
</select>
</div>
<div class="col-span-2">
<label class="block text-xs font-medium text-slate-600 mb-1">Titre (optionnel)</label>
<input name="titre" placeholder="Ex: Manuel d'entretien, Lien forum..." class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm">
</div>
</div>
<div id="input-texte">
<label class="block text-xs font-medium text-slate-600 mb-1">Contenu *</label>
<textarea name="contenu" rows="3" placeholder="Notes libres, infos techniques..." class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm"></textarea>
</div>
<div id="input-url" class="hidden">
<label class="block text-xs font-medium text-slate-600 mb-1">URL *</label>
<input name="contenu" placeholder="https://..." class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm">
</div>
<div id="input-image" class="hidden">
<label class="block text-xs font-medium text-slate-600 mb-1">Photo *</label>
<input type="file" name="photo" accept="image/*" class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm">
<input type="hidden" name="contenu" value="photo">
</div>
<button type="submit" class="bg-orange-500 text-white px-4 py-2 rounded-lg text-sm hover:bg-orange-600">Enregistrer</button>
</form>
</div>
{% if infos %}
<div class="divide-y divide-gray-100">
{% for info in infos %}
<div class="px-5 py-4 flex justify-between items-start hover:bg-gray-50">
<div class="flex-1">
{% if info.titre %}<p class="text-xs font-semibold text-slate-500 uppercase mb-1">{{ info.titre }}</p>{% endif %}
{% if info.type == 'texte' %}
<p class="text-sm text-slate-700 whitespace-pre-wrap">{{ info.contenu }}</p>
{% elif info.type == 'url' %}
<a href="{{ info.contenu }}" target="_blank" class="text-sm text-blue-500 hover:underline break-all">🔗 {{ info.contenu }}</a>
{% elif info.type == 'image' %}
<img src="{{ info.contenu }}" alt="{{ info.titre or 'image' }}" class="max-h-48 rounded-lg mt-1 cursor-pointer" onclick="window.open('{{ info.contenu }}','_blank')">
{% endif %}
<p class="text-xs text-slate-400 mt-1">{{ info.created_at[:10] }}</p>
</div>
<form method="post" action="/vehicules/{{ vehicule.id }}/infos/{{ info.id }}/delete" class="ml-4" onsubmit="return confirm('Supprimer ?')">
<button class="text-red-400 hover:text-red-600 text-xs"></button>
</form>
</div>
{% endfor %}
{% if interventions %}
<div class="overflow-x-auto">
<table>
<thead>
<tr>
<th>Description</th>
<th>Date</th>
<th>Kilométrage</th>
<th class="text-right">Pièces</th>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody>
{% for i in interventions %}
<tr style="cursor:pointer" onclick="window.location='/interventions/{{ i.id }}'">
<td>
<p class="font-medium">{{ i.description }}</p>
{% if i.technicien %}<p class="text-xs text-muted mt-1">{{ i.technicien }}</p>{% endif %}
</td>
<td class="text-secondary text-sm">{{ i.date_intervention or '—' }}</td>
<td class="mono text-sm text-secondary">{{ (i.kilometrage_intervention|string + ' km') if i.kilometrage_intervention else '—' }}</td>
<td class="text-right">
<span class="text-xs text-secondary">{{ i.nb_pieces }} pce(s)</span><br>
<span class="text-xs text-muted">{{ "%.2f"|format(i.cout_pieces) }} €</span>
</td>
<td class="text-right font-semibold" style="color:var(--accent-volt)">
{{ "%.0f"|format(i.cout_main_oeuvre + i.cout_pieces) }} €
</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr style="background:var(--bg-secondary)">
<td colspan="4" class="text-right text-secondary font-semibold text-sm" style="padding:0.75rem 1rem">Total véhicule</td>
<td class="text-right font-bold" style="color:var(--accent-volt);padding:0.75rem 1rem">
{{ "%.0f"|format(interventions|sum(attribute='cout_main_oeuvre') + interventions|sum(attribute='cout_pieces')) }} €
</td>
</tr>
</tfoot>
</table>
</div>
{% else %}
<div class="p-6 text-center text-slate-400 text-sm">Aucune info pour l'instant.</div>
<div class="empty-state">
<i class="fas fa-tools"></i>
<p>Aucune intervention</p>
<a href="/interventions/new?vehicule_id={{ vehicule.id }}" class="link-action">Créer →</a>
</div>
{% endif %}
</div>
<!-- Interventions -->
<div class="bg-white rounded-xl shadow">
<div class="p-5 border-b border-gray-100 flex justify-between items-center">
<h3 class="font-semibold text-slate-800">🔧 Interventions ({{ interventions|length }})</h3>
<a href="/interventions/new?vehicule_id={{ vehicule.id }}" class="text-orange-500 text-sm hover:underline">+ Nouvelle intervention</a>
<!-- ── Toutes les pièces ── -->
<div class="card mb-6" style="padding:0" id="pieces">
<div class="card-header">
<span class="card-title">
<i class="fas fa-cog text-volt" style="margin-right:0.4rem"></i>
Toutes les pièces utilisées
<span class="badge badge-warning ml-2">{{ toutes_pieces|length }}</span>
</span>
</div>
{% if interventions %}
<div class="divide-y divide-gray-100">
{% for i in interventions %}
<div class="px-5 py-4 hover:bg-gray-50">
<div class="flex justify-between items-start">
<div class="flex-1">
<a href="/interventions/{{ i.id }}" class="font-medium text-slate-800 hover:text-orange-500">{{ i.description }}</a>
{% if i.technicien %}<p class="text-xs text-slate-400 mt-1"><i class="fas fa-user-cog mr-1"></i>{{ i.technicien }}</p>{% endif %}
<p class="text-xs text-slate-400 mt-1">{{ i.created_at[:10] }} • {{ i.nb_pieces }} pièce(s)</p>
<p class="text-xs text-muted" style="padding:0.6rem 1.5rem;border-bottom:1px solid var(--border)">Récapitulatif de toutes les pièces de toutes les interventions — pour les retrouver et racheter facilement.</p>
{% if toutes_pieces %}
<div class="overflow-x-auto">
<table>
<thead>
<tr>
<th>Photo</th>
<th>Pièce</th>
<th>Référence</th>
<th>Marque</th>
<th>Prix unit.</th>
<th class="text-center">Qté</th>
<th>Intervention</th>
<th>Date</th>
</tr>
</thead>
<tbody>
{% for p in toutes_pieces %}
<tr>
<td>
{% if p.photo %}
<img src="{{ p.photo }}" class="photo-thumb" onclick="window.open('{{ p.photo }}','_blank')">
{% else %}
<div class="photo-placeholder"><i class="fas fa-cog text-xs"></i></div>
{% endif %}
</td>
<td class="font-medium">{{ p.nom }}</td>
<td class="mono text-xs text-muted">{{ p.reference or '—' }}</td>
<td class="text-secondary text-sm">{{ p.marque or '—' }}</td>
<td class="mono text-sm">{{ "%.2f"|format(p.prix) }} €</td>
<td class="text-center text-secondary">{{ p.quantite }}</td>
<td class="text-xs text-secondary" style="max-width:200px">
<a href="/interventions/{{ p.intervention_id }}" class="link-action truncate" style="display:block;max-width:200px">
{{ p.intervention_desc[:40] }}{% if p.intervention_desc|length > 40 %}…{% endif %}
</a>
</td>
<td class="text-xs text-muted">{{ p.date_intervention or '—' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="empty-state">
<i class="fas fa-cog"></i>
<p>Aucune pièce enregistrée</p>
<span class="text-xs text-muted">Les pièces s'affichent ici dès qu'elles sont ajoutées dans une intervention.</span>
</div>
{% endif %}
</div>
<!-- ── Infos & Notes ── -->
<div class="card" style="padding:0" id="infos">
<div class="card-header">
<span class="card-title">
<i class="fas fa-paperclip text-secondary" style="margin-right:0.4rem"></i>
Infos &amp; Notes
</span>
<button onclick="toggleAddInfo()" class="link-action text-xs">+ Ajouter</button>
</div>
<!-- Form ajout info -->
<div id="add-info-form" style="display:none;background:var(--bg-secondary);border-bottom:1px solid var(--border);padding:1.25rem 1.5rem">
<form method="post" action="/vehicules/{{ vehicule.id }}/infos/add" enctype="multipart/form-data">
<div class="grid grid-3 mb-3">
<div>
<label>Type</label>
<select name="type" id="info-type" onchange="toggleInfoType()">
<option value="texte">📝 Texte libre</option>
<option value="url">🔗 Lien / URL</option>
<option value="image">🖼️ Image / Photo</option>
</select>
</div>
<div class="flex items-center gap-3 ml-4 flex-shrink-0">
<span class="text-sm font-semibold text-slate-700">{{ "%.0f"|format(i.cout_main_oeuvre + i.cout_pieces) }} €</span>
{% if i.statut == 'en_attente' %}<span class="bg-yellow-100 text-yellow-700 px-2 py-1 rounded-full text-xs">En attente</span>
{% elif i.statut == 'en_cours' %}<span class="bg-blue-100 text-blue-700 px-2 py-1 rounded-full text-xs">En cours</span>
{% else %}<span class="bg-green-100 text-green-700 px-2 py-1 rounded-full text-xs">Terminé</span>{% endif %}
<div style="grid-column:span 2">
<label>Titre (optionnel)</label>
<input name="titre" placeholder="Ex: Forum, Fiche technique, Photo moteur…">
</div>
</div>
<div class="mb-3">
<div id="input-texte">
<label>Contenu</label>
<textarea name="contenu" rows="2" placeholder="Notes, infos techniques…"></textarea>
</div>
<div id="input-url" style="display:none">
<label>URL</label>
<input name="contenu" placeholder="https://…">
</div>
<div id="input-image" style="display:none">
<label>Photo</label>
<input type="file" name="photo" accept="image/*" style="background:none;border:none;padding:0.25rem 0;color:var(--text-secondary)">
<input type="hidden" name="contenu" value="photo">
</div>
</div>
<button type="submit" class="btn-primary btn-sm">
<i class="fas fa-save"></i> Enregistrer
</button>
</form>
</div>
{% if infos %}
<div>
{% for info in infos %}
<div style="padding:1rem 1.5rem;border-bottom:1px solid var(--border);display:flex;justify-content:space-between;align-items:flex-start;transition:background 0.15s" onmouseover="this.style.background='rgba(0,212,255,0.03)'" onmouseout="this.style.background=''">
<div style="flex:1">
{% if info.titre %}
<p class="text-xs font-semibold uppercase" style="letter-spacing:0.06em;color:var(--text-muted);margin-bottom:0.3rem">{{ info.titre }}</p>
{% endif %}
{% if info.type == 'texte' %}
<p class="text-sm" style="white-space:pre-wrap;color:var(--text-primary)">{{ info.contenu }}</p>
{% elif info.type == 'url' %}
<a href="{{ info.contenu }}" target="_blank" class="link-action text-sm" style="word-break:break-all">🔗 {{ info.contenu }}</a>
{% elif info.type == 'image' %}
<img src="{{ info.contenu }}" style="max-height:160px;border-radius:8px;border:1px solid var(--border);margin-top:0.25rem;cursor:pointer" onclick="window.open('{{ info.contenu }}','_blank')">
{% endif %}
<p class="text-xs text-muted" style="margin-top:0.35rem">{{ info.created_at[:10] }}</p>
</div>
<form method="post" action="/vehicules/{{ vehicule.id }}/infos/{{ info.id }}/delete" style="margin-left:1rem" onsubmit="return confirm('Supprimer ?')">
<button class="link-danger"></button>
</form>
</div>
{% endfor %}
</div>
{% else %}
<div class="p-6 text-center text-slate-400 text-sm">
Aucune intervention.<br>
<a href="/interventions/new?vehicule_id={{ vehicule.id }}" class="text-orange-500 hover:underline mt-1 inline-block">Créer la première →</a>
<div class="empty-state">
<i class="fas fa-paperclip"></i>
<p>Aucune info pour l'instant</p>
</div>
{% endif %}
</div>
<script>
function toggleAddInfo() {
const f = document.getElementById('add-info-form');
f.style.display = f.style.display === 'none' ? 'block' : 'none';
}
function toggleInfoType() {
const type = document.getElementById('info-type').value;
document.getElementById('input-texte').classList.toggle('hidden', type !== 'texte');
document.getElementById('input-url').classList.toggle('hidden', type !== 'url');
document.getElementById('input-image').classList.toggle('hidden', type !== 'image');
document.getElementById('input-texte').style.display = type === 'texte' ? 'block' : 'none';
document.getElementById('input-url').style.display = type === 'url' ? 'block' : 'none';
document.getElementById('input-image').style.display = type === 'image' ? 'block' : 'none';
}
</script>
{% endblock %}
+38 -23
View File
@@ -1,44 +1,59 @@
{% extends "base.html" %}
{% block title %}{% if vehicule %}Modifier{% else %}Nouveau{% endif %} véhicule - GarageManager{% endblock %}
{% block title %}{% if vehicule %}Modifier{% else %}Nouveau{% endif %} véhicule GarageManager{% endblock %}
{% block content %}
<div class="mb-8">
<a href="/vehicules" class="text-slate-500 hover:text-slate-700 text-sm flex items-center gap-1 mb-4"><i class="fas fa-arrow-left"></i> Retour</a>
<h2 class="text-2xl font-bold text-slate-800">{% if vehicule %}Modifier le véhicule{% else %}Nouveau véhicule{% endif %}</h2>
<a href="/vehicules" class="back-link"><i class="fas fa-arrow-left"></i> Retour aux véhicules</a>
<div class="page-header">
<h1 class="page-title">
<i class="fas fa-{% if vehicule %}edit{% else %}plus-circle{% endif %} text-electric" style="margin-right:0.5rem"></i>
{% if vehicule %}Modifier le véhicule{% else %}Nouveau véhicule{% endif %}
</h1>
</div>
<div class="bg-white rounded-xl shadow p-8 max-w-2xl">
<form method="post" class="space-y-4">
<div class="card" style="max-width:640px">
<form method="post" class="space" style="display:flex;flex-direction:column;gap:1.25rem">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Immatriculation *</label>
<input name="immatriculation" value="{{ vehicule.immatriculation if vehicule else '' }}" required placeholder="AB-123-CD" class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300 uppercase">
<label>Immatriculation *</label>
<input name="immatriculation" value="{{ vehicule.immatriculation if vehicule else '' }}" required placeholder="AB-123-CD" style="text-transform:uppercase;font-family:'JetBrains Mono',monospace;font-size:1rem;letter-spacing:0.05em">
</div>
<div class="grid grid-cols-2 gap-4">
<div class="grid grid-2">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Marque *</label>
<input name="marque" value="{{ vehicule.marque if vehicule else '' }}" required class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<label>Marque *</label>
<input name="marque" value="{{ vehicule.marque if vehicule else '' }}" required placeholder="Renault, Peugeot…">
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Modèle *</label>
<input name="modele" value="{{ vehicule.modele if vehicule else '' }}" required class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<label>Modèle *</label>
<input name="modele" value="{{ vehicule.modele if vehicule else '' }}" required placeholder="Clio, 308…">
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="grid grid-2">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Année</label>
<input name="annee" type="number" value="{{ vehicule.annee if vehicule else '' }}" min="1900" max="2030" class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<label>Année</label>
<input name="annee" type="number" value="{{ vehicule.annee if vehicule else '' }}" min="1900" max="2030" placeholder="2019">
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Kilométrage</label>
<input name="kilometrage" type="number" value="{{ vehicule.kilometrage if vehicule else '' }}" class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<label>Kilométrage (km)</label>
<input name="kilometrage" type="number" value="{{ vehicule.kilometrage if vehicule else '' }}" placeholder="85000">
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">VIN (numéro de châssis)</label>
<input name="vin" value="{{ vehicule.vin if vehicule else '' }}" placeholder="17 caractères" maxlength="17" class="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-orange-300 uppercase">
<label>VIN (numéro de châssis)</label>
<input name="vin" value="{{ vehicule.vin if vehicule else '' }}" placeholder="17 caractères" maxlength="17" style="text-transform:uppercase;font-family:'JetBrains Mono',monospace;letter-spacing:0.08em">
<span class="text-xs text-muted" style="margin-top:0.3rem;display:block">Format : 17 caractères alphanumériques</span>
</div>
<div class="flex gap-3 pt-4">
<button type="submit" class="bg-orange-500 text-white px-6 py-2 rounded-lg hover:bg-orange-600 transition text-sm font-medium">Enregistrer</button>
<a href="/vehicules" class="text-slate-500 px-6 py-2 rounded-lg border border-gray-200 hover:bg-gray-50 text-sm">Annuler</a>
<div style="display:flex;gap:0.75rem;padding-top:0.5rem;border-top:1px solid var(--border)">
<button type="submit" class="btn-primary">
<i class="fas fa-save"></i> Enregistrer
</button>
<a href="/vehicules" class="btn-secondary">Annuler</a>
</div>
</form>
</div>
{% endblock %}
+60 -38
View File
@@ -1,58 +1,80 @@
{% extends "base.html" %}
{% block title %}Véhicules - GarageManager{% endblock %}
{% block title %}Véhicules GarageManager{% endblock %}
{% block content %}
<div class="flex justify-between items-center mb-8">
<div><h2 class="text-2xl font-bold text-slate-800">Véhicules</h2><p class="text-slate-500 mt-1">{{ vehicules|length }} véhicule(s)</p></div>
<a href="/vehicules/new" class="bg-orange-500 text-white px-4 py-2 rounded-lg text-sm hover:bg-orange-600 transition flex items-center gap-2"><i class="fas fa-plus"></i> Nouveau véhicule</a>
</div>
<div class="bg-white rounded-xl shadow">
<div class="p-4 border-b border-gray-100">
<form method="get" class="flex gap-2">
<input name="q" value="{{ q }}" placeholder="Immatriculation, marque, modèle, VIN..." class="flex-1 border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-300">
<button type="submit" class="bg-slate-700 text-white px-4 py-2 rounded-lg text-sm">Rechercher</button>
{% if q %}<a href="/vehicules" class="text-slate-500 px-3 py-2 text-sm flex items-center"></a>{% endif %}
</form>
<div class="page-header flex justify-between items-center">
<div>
<h1 class="page-title"><i class="fas fa-car text-electric" style="margin-right:0.5rem"></i>Véhicules</h1>
<p class="page-subtitle">{{ vehicules|length }} véhicule(s) enregistré(s)</p>
</div>
<a href="/vehicules/new" class="btn-primary">
<i class="fas fa-plus"></i> Nouveau véhicule
</a>
</div>
<!-- Search -->
<div class="card mb-4" style="padding:1rem 1.25rem">
<form method="get" class="flex gap-2">
<input name="q" value="{{ q }}" placeholder="Immatriculation, marque, modèle, VIN…" style="flex:1">
<button type="submit" class="btn-secondary" style="width:auto;white-space:nowrap">
<i class="fas fa-search"></i> Rechercher
</button>
{% if q %}
<a href="/vehicules" class="btn-secondary" style="width:auto"></a>
{% endif %}
</form>
</div>
<!-- Table -->
<div class="card" style="padding:0">
<div class="overflow-x-auto">
{% if vehicules %}
<table class="w-full text-sm">
<thead class="bg-gray-50 text-slate-600 text-xs uppercase">
<table>
<thead>
<tr>
<th class="px-6 py-3 text-left">Immatriculation</th>
<th class="px-6 py-3 text-left">Marque / Modèle</th>
<th class="px-6 py-3 text-left">Année</th>
<th class="px-6 py-3 text-left">VIN</th>
<th class="px-6 py-3 text-left">Kilométrage</th>
<th class="px-6 py-3 text-left">Interventions</th>
<th class="px-6 py-3 text-left">Actions</th>
<th>Immatriculation</th>
<th>Marque / Modèle</th>
<th>Année</th>
<th>VIN</th>
<th>Kilométrage</th>
<th>Interventions</th>
<th>Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
<tbody>
{% for v in vehicules %}
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 font-bold text-slate-800">{{ v.immatriculation }}</td>
<td class="px-6 py-4 font-medium text-slate-700">{{ v.marque }} {{ v.modele }}</td>
<td class="px-6 py-4 text-slate-500">{{ v.annee or '-' }}</td>
<td class="px-6 py-4 text-slate-400 text-xs font-mono">{{ v.vin or '-' }}</td>
<td class="px-6 py-4 text-slate-600">{{ (v.kilometrage|string + ' km') if v.kilometrage else '-' }}</td>
<td class="px-6 py-4"><span class="bg-gray-100 text-gray-600 px-2 py-1 rounded-full text-xs">{{ v.nb_interventions }}</span></td>
<td class="px-6 py-4 flex gap-3">
<a href="/vehicules/{{ v.id }}" class="text-blue-500 hover:underline">Voir</a>
<a href="/vehicules/{{ v.id }}/edit" class="text-orange-500 hover:underline">Modifier</a>
<form method="post" action="/vehicules/{{ v.id }}/delete" class="inline" onsubmit="return confirm('Supprimer ce véhicule ?')">
<button class="text-red-500 hover:underline">Supprimer</button>
</form>
<tr>
<td>
<a href="/vehicules/{{ v.id }}" class="font-bold mono" style="color:var(--accent-electric);font-size:0.95rem;text-decoration:none">{{ v.immatriculation }}</a>
</td>
<td class="font-medium">{{ v.marque }} {{ v.modele }}</td>
<td class="text-secondary">{{ v.annee or '' }}</td>
<td class="mono text-xs text-muted">{{ v.vin or '—' }}</td>
<td class="mono text-sm">{{ (v.kilometrage|string + ' km') if v.kilometrage else '—' }}</td>
<td>
<span class="badge badge-info">{{ v.nb_interventions }}</span>
</td>
<td>
<div class="flex gap-3">
<a href="/vehicules/{{ v.id }}" class="link-action">Voir</a>
<a href="/vehicules/{{ v.id }}/edit" class="link-edit">Modifier</a>
<form method="post" action="/vehicules/{{ v.id }}/delete" style="display:inline" onsubmit="return confirm('Supprimer ce véhicule et toutes ses données ?')">
<button class="link-danger">Supprimer</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="p-8 text-center text-slate-400">
<i class="fas fa-car text-3xl mb-3"></i><p>Aucun véhicule trouvé</p>
<a href="/vehicules/new" class="text-orange-500 hover:underline text-sm mt-2 inline-block">Ajouter le premier →</a>
<div class="empty-state">
<i class="fas fa-car"></i>
<p>Aucun véhicule trouvé</p>
<a href="/vehicules/new" class="link-action">Ajouter le premier →</a>
</div>
{% endif %}
</div>
</div>
{% endblock %}