06081159b6
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>
38 lines
2.3 KiB
PHP
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>
|