- Triggers (Admin > Wyzwalacze): event-driven rules that fire immediately on a ticket lifecycle event (created/updated/status/priority/assignee/team/ category changed, new reply), with AND-conditions and ordered actions (set status/priority/team/assignee, send e-mail). Ships its own dedicated, freely add/edit/delete-able e-mail templates, kept separate from the fixed system templates. - Ticket watching: operators can star/"Obserwuj" any ticket to follow it regardless of assignment/team. - Real-time notification bell (private per-user broadcast channel, 30s fallback poll) with an opt-in in-tab browser push notification. - Per-user notification preferences (/settings/notifications): scope (mine/unassigned/watched/all) and e-mail toggle per event category. - Admin > Integracje: new tab for LDAP/AD + BookStack config, split out of Konfiguracja. - Operator queue: Podkategoria/Zespół/Utworzono columns (off by default). - Obserwuj button moved next to the auto-refresh countdown; trigger condition builder shows subcategory/zgłaszający as name dropdowns instead of raw IDs; /settings/notifications got a back link, full-width push card, and a bordered table container; admin panel tab and operator queue view now persist across a plain page refresh. - Docs: README/ARCHITECTURE/wiki updated for all of the above. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
81 lines
3.6 KiB
PHP
81 lines
3.6 KiB
PHP
<?php
|
|
|
|
use App\Livewire\Admin\Panel as AdminPanel;
|
|
use App\Livewire\Auth\Login;
|
|
use App\Livewire\Client\Dashboard as ClientDashboard;
|
|
use App\Livewire\Client\NewTicket as ClientNewTicket;
|
|
use App\Livewire\Client\TicketShow as ClientTicketShow;
|
|
use App\Livewire\Landing;
|
|
use App\Livewire\Operator\NewTicket as OperatorNewTicket;
|
|
use App\Livewire\Operator\Queue as OperatorQueue;
|
|
use App\Livewire\Operator\Stats as OperatorStats;
|
|
use App\Livewire\Operator\TicketShow as OperatorTicketShow;
|
|
use App\Livewire\Settings\NotificationPreferences;
|
|
use App\Models\Ticket;
|
|
use App\Support\Settings;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Route;
|
|
|
|
Route::get('/', Landing::class)->name('landing');
|
|
|
|
Route::get('/manifest.webmanifest', function () {
|
|
return response()->json([
|
|
'name' => Settings::get('company_name'),
|
|
'short_name' => Settings::get('company_name'),
|
|
'start_url' => '/',
|
|
'scope' => '/',
|
|
'display' => 'standalone',
|
|
'background_color' => '#f3f5fe',
|
|
'theme_color' => Settings::accentColor(),
|
|
'lang' => 'pl',
|
|
'icons' => [
|
|
['src' => asset('pwa-icons/icon-192.png'), 'sizes' => '192x192', 'type' => 'image/png', 'purpose' => 'any'],
|
|
['src' => asset('pwa-icons/icon-512.png'), 'sizes' => '512x512', 'type' => 'image/png', 'purpose' => 'any'],
|
|
['src' => asset('pwa-icons/icon-192-maskable.png'), 'sizes' => '192x192', 'type' => 'image/png', 'purpose' => 'maskable'],
|
|
['src' => asset('pwa-icons/icon-512-maskable.png'), 'sizes' => '512x512', 'type' => 'image/png', 'purpose' => 'maskable'],
|
|
],
|
|
])->header('Content-Type', 'application/manifest+json');
|
|
})->name('manifest');
|
|
|
|
Route::get('/login', Login::class)->middleware('guest')->name('login');
|
|
Route::post('/logout', function () {
|
|
Auth::logout();
|
|
request()->session()->invalidate();
|
|
request()->session()->regenerateToken();
|
|
|
|
return redirect('/');
|
|
})->middleware('auth')->name('logout');
|
|
|
|
Route::middleware(['auth', 'role:client'])->prefix('client')->name('client.')->group(function () {
|
|
Route::get('/', ClientDashboard::class)->name('dashboard');
|
|
Route::get('/new', ClientNewTicket::class)->name('new');
|
|
Route::get('/tickets/{ticket}', ClientTicketShow::class)->name('ticket');
|
|
});
|
|
|
|
Route::middleware(['auth', 'role:operator'])->prefix('operator')->name('operator.')->group(function () {
|
|
Route::get('/', OperatorQueue::class)->name('queue');
|
|
Route::get('/new', OperatorNewTicket::class)->name('new');
|
|
Route::get('/stats', OperatorStats::class)->name('stats');
|
|
Route::get('/tickets/{ticket}', OperatorTicketShow::class)->name('ticket');
|
|
|
|
// Fired via navigator.sendBeacon on tab/browser close (see the
|
|
// MONITOR CZASU PRACY widget in ticket-show.blade.php) — a plain
|
|
// Livewire call can't be relied on to complete once the page is
|
|
// actually unloading, and sendBeacon needs a normal endpoint to hit.
|
|
// Excluded from CSRF verification in bootstrap/app.php since a beacon
|
|
// request can't carry the token header.
|
|
Route::post('/tickets/{ticket}/stop-timer', function (Ticket $ticket) {
|
|
$ticket->stopTimer();
|
|
|
|
return response()->noContent();
|
|
})->name('ticket.stop-timer');
|
|
});
|
|
|
|
Route::middleware(['auth', 'role:admin'])->prefix('admin')->name('admin.')->group(function () {
|
|
Route::get('/', AdminPanel::class)->name('panel');
|
|
});
|
|
|
|
Route::middleware(['auth', 'role:operator,admin'])->prefix('settings')->name('settings.')->group(function () {
|
|
Route::get('/notifications', NotificationPreferences::class)->name('notifications');
|
|
});
|