Files
autocrm/resources/views/components/hint.blade.php
T
Vasyka 06081159b6 feat(hints): contextual help system + FAQ page (MVP)
Infrastructure for a portal-wide in-app help system:

* Users have hints_enabled (default true) and dismissed_hints (JSON
  array) columns. User::shouldSeeHint(key) checks both.
* app/Support/Hints.php — single-source-of-truth registry with 14
  pilot hints across Service (5), CRM (3), Depozit (3), Finanțe (3).
  Each entry has RO/RU/EN title + body + optional next-step + links.
* <x-hint key="wo.dashboard.overview" /> Blade component renders a
  small "?" icon with Alpine.js popover; the popover shows title,
  body, next-step, related links and an "X" button that POSTs to
  /app/hints/{key}/dismiss.
* HintController handles dismiss (per key), toggle (global on/off)
  and reset (clear dismissed + re-enable). Routes are auth:web.
* /app/faq page (Filament Page under Admin group) renders the whole
  registry grouped by area with a live search box and buttons to
  toggle global hints or reset dismissed ones.
* Wired 3 pilot hints into the WO dashboard: title (overview), Docs
  tab PDF preview, and the Chat client card.

Follow-ups: extend registry to cover more pages and add <x-hint>
tags where useful. Filament resource fields can also reuse the same
copy via ->hint()/->helperText().

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

38 lines
2.3 KiB
PHP

@props(['key'])
@php
$key = $key ?? null;
$hint = $key ? \App\Support\Hints::get($key) : null;
$user = auth()->user();
$show = $hint && $user && $user->shouldSeeHint($key);
if (! $show) { return; }
$lc = \App\Support\Hints::locale();
$title = $hint['title'][$lc] ?? $hint['title']['ro'];
$body = $hint['body'][$lc] ?? $hint['body']['ro'];
$links = $hint['links'] ?? [];
$next = $hint['next'][$lc] ?? null;
@endphp
<span x-data="{ open: false }" style="display:inline-block;position:relative;">
<button type="button" @click.prevent="open = !open" @click.outside="open = false"
title="{{ $title }}"
style="display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;background:#3b82f6;color:#fff;font-size:11px;font-weight:700;border:none;cursor:pointer;line-height:1;vertical-align:middle;margin-left:4px;flex-shrink:0;">?</button>
<div x-show="open" x-cloak
@keydown.escape.window="open = false"
style="position:absolute;top:24px;left:0;z-index:1000;width:320px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;box-shadow:0 10px 25px rgba(0,0,0,0.12);padding:12px 14px;text-align:left;color:#111;font-size:13px;line-height:1.4;">
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:6px;">
<b style="font-size:13px;">{{ $title }}</b>
<button type="button"
onclick="fetch('{{ route('hints.dismiss', ['key' => $key]) }}', {method:'POST', headers:{'X-CSRF-TOKEN':'{{ csrf_token() }}','Accept':'application/json'}}).then(()=>this.closest('span').remove());"
style="background:none;border:none;color:#9ca3af;cursor:pointer;font-size:16px;padding:0;line-height:1;" title="{{ __('Nu mai arăta') }}"></button>
</div>
<div style="color:#374151;margin-bottom:8px;">{{ $body }}</div>
@if ($next)
<div style="color:#059669;font-size:12px;margin-bottom:8px;"> {{ $next }}</div>
@endif
@foreach ($links as $ln)
<a href="{{ url($ln['url']) }}" style="display:inline-block;font-size:12px;color:#2563eb;text-decoration:none;margin-right:8px;">
{{ $ln['label_' . $lc] ?? $ln['label_ro'] }}
</a>
@endforeach
</div>
</span>