Batch 2: Workload heatmap + Site PSauto + VIN search

═══ Workload heatmap (Încărcare STO) ═══
- /app/workload custom Page (group Analiză)
- Săptămână (Lu-Du) × posturi → matrice ore programate
- Heatmap colorat: verde→galben→roșu pe ratio capacity (10h/zi)
- Navigare săpt anterior/curent/următor
- Programări fără pod → row '— fără pod —' separat

═══ Site PSauto (landing public) ═══
- / pe tenant subdomain → resources/views/site/landing.blade.php
- Hero cu logo + nume + slogan; gradient theme color
- Servicii (din settings.services) — grid card-uri
- Locație/contact + program lucru standardizat
- Mărci suportate (din settings.cars)
- CTA: phone + email
- Footer cu tenant name + powered by AutoCRM

═══ VIN search ═══
- VinDecoder service: WMI hardcoded (24 producători EU/Asia/USA)
  + year codes (2001-2026) — pure offline, fără API extern
- /app/vin-search Page (group Depozit) cu:
  • Input VIN cu uppercase + monospace
  • Decode → producător/țară/an/serial
  • Match VIN-uri din baza Vehicles
  • Search piese din catalog (live debounce 300ms)
- Rezultatele linkează la editor Vehicle/Part

Total tenant routes: 102.
This commit is contained in:
2026-05-07 17:16:09 +00:00
parent 67da97178d
commit 4b3201ca1c
7 changed files with 534 additions and 3 deletions
+115
View File
@@ -0,0 +1,115 @@
<!DOCTYPE html>
<html lang="ro">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>{{ $name }} autoservice {{ $city ?? '' }}</title>
@if ($faviconUrl)
<link rel="icon" href="{{ $faviconUrl }}">
@endif
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; color: #1f2937; line-height: 1.5; }
.hero {
background: linear-gradient(135deg, {{ $themeColor }}, {{ $themeColor }}cc);
color: #fff; padding: 80px 20px; text-align: center;
}
.hero img { max-height: 80px; margin-bottom: 16px; }
.hero h1 { font-size: 38px; font-weight: 700; margin-bottom: 12px; }
.hero p { font-size: 18px; opacity: .92; max-width: 600px; margin: 0 auto; }
.section { padding: 60px 20px; max-width: 1100px; margin: 0 auto; }
.section h2 { font-size: 28px; font-weight: 700; margin-bottom: 24px; text-align: center; }
.services-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; }
.service-card {
background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 12px;
padding: 24px; text-align: center; transition: transform .15s;
}
.service-card:hover { transform: translateY(-2px); border-color: {{ $themeColor }}; }
.service-card .icon { font-size: 32px; margin-bottom: 12px; }
.service-card h3 { font-size: 16px; font-weight: 600; }
.info { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; }
.info-block { padding: 24px; background: #f9fafb; border-radius: 12px; }
.info-block h3 { font-size: 16px; font-weight: 600; margin-bottom: 12px; color: {{ $themeColor }}; }
.info-block p { font-size: 14px; color: #4b5563; margin-bottom: 6px; }
.cta { background: {{ $themeColor }}; color: #fff; text-align: center; padding: 60px 20px; }
.cta h2 { font-size: 26px; margin-bottom: 16px; }
.cta a {
display: inline-block; padding: 14px 32px;
background: #fff; color: {{ $themeColor }};
border-radius: 8px; text-decoration: none; font-weight: 600;
margin: 8px; transition: opacity .15s;
}
.cta a:hover { opacity: .9; }
footer { background: #1f2937; color: #9ca3af; padding: 24px; text-align: center; font-size: 13px; }
footer a { color: #d1d5db; text-decoration: none; }
</style>
</head>
<body>
<header class="hero">
@if ($logoUrl)
<img src="{{ $logoUrl }}" alt="logo">
@endif
<h1>{{ $name }}</h1>
<p>Autoservice profesional{{ $city ? ' — ' . $city : '' }}. Diagnostic, reparații, piese, ITP.</p>
</header>
@if (! empty($services))
<section class="section">
<h2>Servicii oferite</h2>
<div class="services-grid">
@foreach ($services as $s)
<div class="service-card">
<div class="icon">🔧</div>
<h3>{{ $s }}</h3>
</div>
@endforeach
</div>
</section>
@endif
<section class="section">
<div class="info">
<div class="info-block">
<h3>📍 Locație & contact</h3>
@if ($city) <p><b>Oraș:</b> {{ $city }}</p> @endif
@if ($phone) <p><b>Telefon:</b> <a href="tel:{{ $phone }}" style="color:inherit;">{{ $phone }}</a></p> @endif
@if ($email) <p><b>Email:</b> <a href="mailto:{{ $email }}" style="color:inherit;">{{ $email }}</a></p> @endif
</div>
<div class="info-block">
<h3>🕒 Program de lucru</h3>
<p>Luni Vineri: 08:00 18:00</p>
<p>Sâmbătă: 09:00 14:00</p>
<p>Duminică: închis</p>
</div>
@if (! empty($cars))
<div class="info-block">
<h3>🚗 Mărci suportate</h3>
<p>{{ implode(', ', array_slice($cars, 0, 12)) }}@if (count($cars) > 12), @endif</p>
</div>
@endif
</div>
</section>
<section class="cta">
<h2>Rezervă o programare</h2>
@if ($phone)
<a href="tel:{{ $phone }}">📞 {{ $phone }}</a>
@endif
@if ($email)
<a href="mailto:{{ $email }}"> {{ $email }}</a>
@endif
</section>
<footer>
© {{ date('Y') }} {{ $name }} · powered by AutoCRM
</footer>
</body>
</html>