Files
Vasyka 2ca6ab58a1 feat(nav): FAQ shows all 8 sections + drag-drop reorder in Settings
FAQ sections were only 4 (service/crm/depozit/finante) even though
the sidebar has 8. Added the missing 4 to Hints::AREAS with proper
icons + accent colors:
  📊 Analiză (pink)   📣 Marketing (orange)
  🛒 Magazin (cyan)   ⚙️ Admin (slate)

Retagged 15 hints that were shoved into wrong areas as a workaround
(admin.*/marketing.*/shop.*/analytics.* now use the matching bucket).
FAQ blade reads accent color from the AREAS registry instead of a
hardcoded local map.

Also added per-tenant navigation group ordering:
- Settings → new "Ordine meniu lateral" section with a Filament
  Repeater that has reorderable buttons (drag/arrow buttons) but no
  add/delete (fixed list of 8 groups)
- Persisted to settings.nav_group_order as an array of keys
- New ConfigureNavGroups middleware (runs after ResolveTenant on
  the web stack) reads the current tenant's order and calls
  $panel->navigationGroups([...]) so the sidebar renders in that
  order per-request

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-09-01 11:09:24 +00:00

378 lines
14 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<x-filament-panels::page>
@php
$lc = \App\Support\Hints::locale();
$areas = $this->getAreas();
$grouped = $this->getGrouped();
$user = auth()->user();
$totalHints = collect($grouped)->sum(fn ($g) => count($g));
$videoEmbed = $this->getVideoEmbedUrl();
$videoRaw = $this->getVideoRawUrl();
// Read accent color from AREAS registry (single source of truth)
@endphp
<style>
.faq-shell { width: 100%; }
/* ── VIDEO ── */
.faq-video-card {
background: #000;
border-radius: 12px;
overflow: hidden;
margin-bottom: 20px;
position: relative;
aspect-ratio: 16 / 9;
max-height: 480px;
}
.faq-video-card iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
border: 0;
}
.faq-video-fallback {
background: linear-gradient(135deg, #374151 0%, #111827 100%);
border-radius: 12px;
padding: 40px 28px;
color: #fff;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 20px;
flex-wrap: wrap;
}
.faq-video-fallback-icon { font-size: 40px; }
.faq-video-fallback-txt { flex: 1; min-width: 200px; }
.faq-video-fallback-title { font-size: 16px; font-weight: 600; margin: 0 0 4px; }
.faq-video-fallback-sub { font-size: 13px; opacity: .8; margin: 0; }
.faq-video-fallback a { color: #60a5fa; text-decoration: none; font-weight: 500; }
.faq-video-fallback a:hover { text-decoration: underline; }
/* ── HERO STRIP ── */
.faq-hero {
background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%);
border-radius: 12px;
padding: 24px 28px;
color: #fff;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 24px;
flex-wrap: wrap;
}
.dark .faq-hero { background: linear-gradient(135deg, #1e3a8a 0%, #0f172a 100%); }
.faq-hero-icon { font-size: 48px; line-height: 1; }
.faq-hero-txt { flex: 1; min-width: 200px; }
.faq-hero-title { font-size: 22px; font-weight: 700; margin: 0 0 4px; letter-spacing: -.3px; }
.faq-hero-sub { font-size: 13px; opacity: .85; margin: 0; }
.faq-hero-stats { display: flex; gap: 16px; }
.faq-hero-stat { text-align: center; }
.faq-hero-stat-n { font-size: 24px; font-weight: 700; line-height: 1; }
.faq-hero-stat-l { font-size: 11px; opacity: .8; text-transform: uppercase; letter-spacing: .5px; }
/* ── CONTROLS BAR ── */
.faq-controls {
display: grid;
grid-template-columns: 1fr auto;
gap: 12px;
align-items: center;
margin-bottom: 24px;
padding: 12px 16px;
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 10px;
}
.dark .faq-controls { background: #1f2937; border-color: #374151; }
.faq-search {
width: 100%;
padding: 10px 14px 10px 40px;
border: 1px solid #d1d5db;
border-radius: 8px;
font-size: 14px;
background: #fff url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2'><circle cx='11' cy='11' r='8'/><path d='m21 21-4.35-4.35'/></svg>") no-repeat 12px center;
}
.dark .faq-search { background-color: #111827; border-color: #4b5563; color: #f3f4f6; }
.faq-search:focus { outline: 2px solid #3b82f6; outline-offset: -1px; border-color: #3b82f6; }
.faq-toggle { display: flex; gap: 8px; align-items: center; font-size: 13px; }
.faq-toggle-lbl { color: #6b7280; font-size: 12px; }
/* iOS-style switch — makes clear both ON and OFF states are clickable */
.faq-switch {
position: relative;
width: 46px; height: 24px;
background: #d1d5db;
border-radius: 12px;
border: 0; padding: 0;
cursor: pointer;
transition: background .2s;
display: inline-block;
}
.faq-switch.on { background: #10b981; }
.faq-switch::after {
content: '';
position: absolute;
top: 2px; left: 2px;
width: 20px; height: 20px;
background: #fff;
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0,0,0,.3);
transition: left .2s;
}
.faq-switch.on::after { left: 24px; }
.faq-switch-lbl {
font-size: 12px; font-weight: 600;
margin-left: 8px;
min-width: 62px; display: inline-block;
}
.faq-switch-lbl.on { color: #059669; }
.faq-switch-lbl.off { color: #6b7280; }
.faq-btn-reset {
padding: 7px 14px; border-radius: 20px;
border: 1px solid #d1d5db; background: #fff; color: #6b7280;
font-size: 12px; cursor: pointer;
}
.dark .faq-btn-reset { background: #374151; border-color: #4b5563; color: #d1d5db; }
.faq-btn-reset:hover { background: #f9fafb; color: #111; }
.dark .faq-btn-reset:hover { background: #4b5563; color: #f3f4f6; }
/* ── GRID OF AREA CARDS ── */
.faq-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(420px, 1fr)); gap: 20px; }
@media (max-width: 900px) { .faq-grid { grid-template-columns: 1fr; } }
.faq-card {
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0,0,0,.05);
}
.dark .faq-card { background: #1f2937; border-color: #374151; }
.faq-card-hd {
padding: 16px 18px;
border-bottom: 1px solid #e5e7eb;
display: flex; align-items: center; gap: 12px;
color: #fff;
}
.dark .faq-card-hd { border-bottom-color: #374151; }
.faq-card-icon { font-size: 26px; line-height: 1; }
.faq-card-title { font-size: 15px; font-weight: 700; letter-spacing: -.2px; flex: 1; }
.faq-card-badge {
background: rgba(255,255,255,.25);
padding: 2px 10px; border-radius: 10px;
font-size: 11px; font-weight: 700;
}
.faq-card-body { padding: 8px; }
.faq-item {
border-radius: 8px;
margin-bottom: 4px;
overflow: hidden;
transition: background .15s;
}
.faq-item summary {
padding: 10px 14px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
color: #111;
list-style: none;
display: flex;
justify-content: space-between;
align-items: center;
user-select: none;
border-radius: 8px;
}
.dark .faq-item summary { color: #f3f4f6; }
.faq-item summary:hover { background: #f9fafb; }
.dark .faq-item summary:hover { background: #374151; }
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: '+'; color: #9ca3af; font-size: 18px; font-weight: 400; line-height: 1; }
.faq-item[open] { background: #f0f9ff; }
.dark .faq-item[open] { background: #1e3a5f; }
.faq-item[open] summary::after { content: ''; }
.faq-body-txt {
padding: 0 14px 14px;
font-size: 12.5px;
line-height: 1.55;
color: #374151;
}
.dark .faq-body-txt { color: #d1d5db; }
.faq-next {
margin-top: 10px;
padding: 8px 12px;
background: #d1fae5;
color: #065f46;
border-radius: 6px;
font-size: 12px;
font-weight: 500;
}
.dark .faq-next { background: #064e3b; color: #a7f3d0; }
.faq-next::before { content: '→ '; font-weight: 700; }
.faq-links {
margin-top: 10px;
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.faq-link {
padding: 4px 10px;
background: #eff6ff;
color: #1d4ed8;
border-radius: 6px;
font-size: 11.5px;
text-decoration: none;
font-weight: 500;
}
.dark .faq-link { background: #1e3a8a; color: #93c5fd; }
.faq-link:hover { background: #dbeafe; }
.dark .faq-link:hover { background: #1e40af; }
/* ── EMPTY STATE ── */
.faq-empty {
grid-column: 1 / -1;
text-align: center;
padding: 60px 20px;
color: #9ca3af;
font-size: 14px;
background: #fff;
border: 1px dashed #e5e7eb;
border-radius: 12px;
}
.dark .faq-empty { background: #1f2937; border-color: #374151; }
.faq-empty-icon { font-size: 40px; margin-bottom: 12px; }
</style>
<div class="faq-shell">
{{-- HERO --}}
<div class="faq-hero">
<div class="faq-hero-icon">📖</div>
<div class="faq-hero-txt">
<h1 class="faq-hero-title">{{ __('Ghid rapid AutoCRM') }}</h1>
<p class="faq-hero-sub">{{ __('Toate răspunsurile la un click distanță. Fiecare pagină are propriile hint-uri „?" — sau caută mai jos.') }}</p>
</div>
<div class="faq-hero-stats">
<div class="faq-hero-stat">
<div class="faq-hero-stat-n">{{ $totalHints }}</div>
<div class="faq-hero-stat-l">{{ __('subiecte') }}</div>
</div>
<div class="faq-hero-stat">
<div class="faq-hero-stat-n">{{ count($areas) }}</div>
<div class="faq-hero-stat-l">{{ __('secțiuni') }}</div>
</div>
</div>
</div>
{{-- VIDEO TUTORIAL (dacă e configurat) --}}
@if ($videoEmbed)
<div class="faq-video-card">
<iframe src="{{ $videoEmbed }}"
title="{{ __('Video tutorial') }}"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
</div>
@elseif ($videoRaw)
<div class="faq-video-fallback">
<div class="faq-video-fallback-icon">🎥</div>
<div class="faq-video-fallback-txt">
<h3 class="faq-video-fallback-title">{{ __('Video tutorial') }}</h3>
<p class="faq-video-fallback-sub">
{{ __('URL nerecunoscut — deschide-l direct:') }}
<a href="{{ $videoRaw }}" target="_blank">{{ $videoRaw }}</a>
</p>
</div>
</div>
@elseif ($user?->isAdmin())
<div class="faq-video-fallback">
<div class="faq-video-fallback-icon">🎥</div>
<div class="faq-video-fallback-txt">
<h3 class="faq-video-fallback-title">{{ __('Video tutorial') }}</h3>
<p class="faq-video-fallback-sub">
{{ __('Adaugă un link YouTube/Vimeo în') }}
<a href="{{ url('/app/settings') }}">{{ __('Setări → Liste configurabile') }} </a>
</p>
</div>
</div>
@endif
{{-- CONTROLS --}}
<div class="faq-controls">
<input type="text" class="faq-search" placeholder="{{ __('Caută în ghid...') }}" wire:model.live.debounce.300ms="search">
<div class="faq-toggle">
<span class="faq-toggle-lbl">{{ __('Sugestii „?" pe pagini:') }}</span>
<button type="button"
class="faq-switch {{ $user?->hints_enabled ? 'on' : 'off' }}"
title="{{ $user?->hints_enabled ? __('Click pentru a DEZACTIVA') : __('Click pentru a ACTIVA') }}"
aria-label="{{ __('Toggle hints') }}"
onclick="faqPostAction('{{ route('hints.toggle') }}')"></button>
<span class="faq-switch-lbl {{ $user?->hints_enabled ? 'on' : 'off' }}">
{{ $user?->hints_enabled ? __('ACTIVE') : __('DEZACTIV') }}
</span>
<button type="button"
class="faq-btn-reset"
title="{{ __('Aduce înapoi hint-urile ascunse') }}"
onclick="faqPostAction('{{ route('hints.reset') }}')">
{{ __('Resetează') }}
</button>
</div>
<script>
function faqPostAction(url) {
fetch(url, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
},
credentials: 'same-origin',
}).then(() => window.location.reload()).catch(() => window.location.reload());
}
</script>
</div>
{{-- GRID --}}
<div class="faq-grid">
@if (empty($grouped))
<div class="faq-empty">
<div class="faq-empty-icon">🔍</div>
{{ __('Nicio potrivire pentru „:q".', ['q' => $search]) }}
</div>
@endif
@foreach ($grouped as $areaKey => $hints)
@php
$area = $areas[$areaKey] ?? null;
$accent = $area['color'] ?? '#6b7280';
@endphp
<div class="faq-card">
<div class="faq-card-hd" style="background:{{ $accent }};">
<span class="faq-card-icon">{{ $area['icon'] ?? '📖' }}</span>
<span class="faq-card-title">{{ $area['label'][$lc] ?? $areaKey }}</span>
<span class="faq-card-badge">{{ count($hints) }}</span>
</div>
<div class="faq-card-body">
@foreach ($hints as $key => $h)
<details class="faq-item">
<summary>{{ $h['title'][$lc] ?? $h['title']['ro'] }}</summary>
<div class="faq-body-txt">
{{ $h['body'][$lc] ?? $h['body']['ro'] }}
@if (! empty($h['next'][$lc]))
<div class="faq-next">{{ $h['next'][$lc] }}</div>
@endif
@if (! empty($h['links']))
<div class="faq-links">
@foreach ($h['links'] as $ln)
<a class="faq-link" href="{{ url($ln['url']) }}">{{ $ln['label_' . $lc] ?? $ln['label_ro'] }}</a>
@endforeach
</div>
@endif
</div>
</details>
@endforeach
</div>
</div>
@endforeach
</div>
</div>
</x-filament-panels::page>