Fix popup toast messages (Remove Flasher) (#16090)

* Remove flasher
Just use a bit of custom code to interface with toastr js
This is able to retain our custom theme and work properly

* Fix style issues

* Missed reference rename

* Remove test code :)

* Fix a missed rename

* Fix one more missed reference

* Fix typo
This commit is contained in:
Tony Murray 2024-06-05 08:07:42 -05:00 committed by GitHub
parent 7879b450ff
commit fa16c025ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 222 additions and 338 deletions

View File

@ -54,32 +54,26 @@ class Checks
if ($user->isAdmin()) {
$notifications = Notification::isUnread($user)->where('severity', '>', Severity::Ok->value)->get();
foreach ($notifications as $notification) {
flash()
->using('template.librenms')
->title($notification->title)
->addWarning("<a href='notifications/'>$notification->body</a>");
toast()->warning($notification->title, "<a href='notifications/'>$notification->body</a>");
}
$warn_sec = Config::get('rrd.step', 300) * 3;
if (Device::isUp()->where('last_polled', '<=', Carbon::now()->subSeconds($warn_sec))->exists()) {
$warn_min = $warn_sec / 60;
flash()
->using('template.librenms')
->title('Devices unpolled')
->addWarning('<a href="poller/log?filter=unpolled/">It appears as though you have some devices that haven\'t completed polling within the last ' . $warn_min . ' minutes, you may want to check that out :)</a>');
toast()->warning('Devices unpolled', '<a href="poller/log?filter=unpolled/">It appears as though you have some devices that haven\'t completed polling within the last ' . $warn_min . ' minutes, you may want to check that out :)</a>');
}
// Directory access checks
$rrd_dir = Config::get('rrd_dir');
if (! is_dir($rrd_dir)) {
flash()->addError("RRD Directory is missing ($rrd_dir). Graphing may fail. <a href=" . url('validate') . '>Validate your install</a>');
toast()->error("RRD Directory is missing ($rrd_dir). Graphing may fail. <a href=" . url('validate') . '>Validate your install</a>');
}
$temp_dir = Config::get('temp_dir');
if (! is_dir($temp_dir)) {
flash()->addError("Temp Directory is missing ($temp_dir). Graphing may fail. <a href=" . url('validate') . '>Validate your install</a>');
toast()->error("Temp Directory is missing ($temp_dir). Graphing may fail. <a href=" . url('validate') . '>Validate your install</a>');
} elseif (! is_writable($temp_dir)) {
flash()->addError("Temp Directory is not writable ($temp_dir). Graphing may fail. <a href='" . url('validate') . "'>Validate your install</a>");
toast()->error("Temp Directory is not writable ($temp_dir). Graphing may fail. <a href='" . url('validate') . "'>Validate your install</a>");
}
}
}

View File

@ -74,7 +74,7 @@ class SocialiteController extends Controller
if (array_key_exists('error', $request->query())) {
$error = $request->query('error');
$error_description = $request->query('error_description');
flash()->addError($error . ': ' . $error_description);
toast()->error($error . ': ' . $error_description);
return redirect()->route('login');
}
@ -121,7 +121,7 @@ class SocialiteController extends Controller
return redirect()->intended();
} catch (AuthenticationException $e) {
flash()->addError($e->getMessage());
toast()->error($e->getMessage());
}
return redirect()->route('login');

View File

@ -26,6 +26,7 @@
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Http\Interfaces\ToastInterface;
use App\Models\User;
use App\Models\UserPref;
use Illuminate\Http\Request;
@ -37,14 +38,14 @@ use Session;
class TwoFactorController extends Controller
{
public function verifyTwoFactor(Request $request)
public function verifyTwoFactor(Request $request, ToastInterface $toast)
{
$this->validate($request, [
'twofactor' => 'required|numeric',
]);
try {
$this->checkToken($request->user(), $request->input('twofactor'));
$this->checkToken($request->user(), $request->input('twofactor'), $toast);
} catch (AuthenticationException $e) {
return redirect()->route('2fa.form')->withErrors($e->getMessage());
}
@ -54,7 +55,7 @@ class TwoFactorController extends Controller
UserPref::forgetPref(auth()->user(), 'twofactor');
$request->session()->forget(['twofactor', 'twofactorremove']);
flash()->addInfo(__('TwoFactor auth removed.'));
$toast->info(__('TwoFactor auth removed.'));
return redirect('preferences');
}
@ -153,7 +154,7 @@ class TwoFactorController extends Controller
*
* @throws AuthenticationException
*/
private function checkToken($user, $token)
private function checkToken($user, $token, ToastInterface $toast)
{
if (! $token) {
throw new AuthenticationException(__('No Two-Factor Token entered.'));
@ -192,7 +193,7 @@ class TwoFactorController extends Controller
// notify if added
if (Session::has('twofactoradd')) {
flash()->addSuccess(__('TwoFactor auth added.'));
$toast->success(__('TwoFactor auth added.'));
Session::forget('twofactoradd');
}

View File

@ -2,8 +2,8 @@
namespace App\Http\Controllers;
use App\Http\Interfaces\ToastInterface;
use App\Models\DeviceGroup;
use Flasher\Prime\FlasherInterface;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use LibreNMS\Alerting\QueryBuilderFilter;
@ -49,7 +49,7 @@ class DeviceGroupController extends Controller
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request, FlasherInterface $flasher)
public function store(Request $request, ToastInterface $toast)
{
$this->validate($request, [
'name' => 'required|string|unique:device_groups',
@ -67,7 +67,7 @@ class DeviceGroupController extends Controller
$deviceGroup->devices()->sync($request->devices);
}
$flasher->addSuccess(__('Device Group :name created', ['name' => htmlentities($deviceGroup->name)]));
$toast->success(__('Device Group :name created', ['name' => htmlentities($deviceGroup->name)]));
return redirect()->route('device-groups.index');
}
@ -110,7 +110,7 @@ class DeviceGroupController extends Controller
* @param \App\Models\DeviceGroup $deviceGroup
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Request $request, DeviceGroup $deviceGroup, FlasherInterface $flasher)
public function update(Request $request, DeviceGroup $deviceGroup, ToastInterface $toast)
{
$this->validate($request, [
'name' => [
@ -143,9 +143,9 @@ class DeviceGroupController extends Controller
if ($deviceGroup->isDirty() || $devices_updated) {
try {
if ($deviceGroup->save() || $devices_updated) {
$flasher->addSuccess(__('Device Group :name updated', ['name' => htmlentities($deviceGroup->name)]));
$toast->success(__('Device Group :name updated', ['name' => htmlentities($deviceGroup->name)]));
} else {
$flasher->addError(__('Failed to save'));
$toast->error(__('Failed to save'));
return redirect()->back()->withInput();
}
@ -155,7 +155,7 @@ class DeviceGroupController extends Controller
]);
}
} else {
$flasher->addInfo(__('No changes made'));
$toast->info(__('No changes made'));
}
return redirect()->route('device-groups.index');

View File

@ -2,8 +2,8 @@
namespace App\Http\Controllers;
use App\Http\Interfaces\ToastInterface;
use App\Models\PortGroup;
use Flasher\Prime\FlasherInterface;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
@ -39,7 +39,7 @@ class PortGroupController extends Controller
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request, FlasherInterface $flasher)
public function store(Request $request, ToastInterface $toast)
{
$this->validate($request, [
'name' => 'required|string|unique:port_groups',
@ -48,7 +48,7 @@ class PortGroupController extends Controller
$portGroup = PortGroup::make($request->only(['name', 'desc']));
$portGroup->save();
$flasher->addSuccess(__('Port Group :name created', ['name' => $portGroup->name]));
$toast->success(__('Port Group :name created', ['name' => $portGroup->name]));
return redirect()->route('port-groups.index');
}
@ -73,7 +73,7 @@ class PortGroupController extends Controller
* @param \App\Models\PortGroup $portGroup
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Request $request, PortGroup $portGroup, FlasherInterface $flasher)
public function update(Request $request, PortGroup $portGroup, ToastInterface $toast)
{
$this->validate($request, [
'name' => [
@ -90,9 +90,9 @@ class PortGroupController extends Controller
$portGroup->fill($request->only(['name', 'desc']));
if ($portGroup->save()) {
$flasher->addSuccess(__('Port Group :name updated', ['name' => $portGroup->name]));
$toast->success(__('Port Group :name updated', ['name' => $portGroup->name]));
} else {
$flasher->addError(__('Failed to save'));
$toast->error(__('Failed to save'));
return redirect()->back()->withInput();
}

View File

@ -2,6 +2,7 @@
namespace App\Http\Controllers;
use App\Http\Interfaces\ToastInterface;
use App\Models\Service;
use Illuminate\Http\Request;
@ -13,7 +14,7 @@ class ServiceController extends Controller
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\View\View
*/
public function store(Request $request)
public function store(Request $request, ToastInterface $toast)
{
$request = [
'service_name' => 'required|string|unique:service',
@ -44,7 +45,7 @@ class ServiceController extends Controller
);
$service->save();
flash()->addSuccess(__('Service :name created', ['name' => $service->service_name]));
$toast->success(__('Service :name created', ['name' => $service->service_name]));
return redirect()->route('services.templates.index');
}

View File

@ -2,11 +2,11 @@
namespace App\Http\Controllers;
use App\Http\Interfaces\ToastInterface;
use App\Models\Device;
use App\Models\DeviceGroup;
use App\Models\Service;
use App\Models\ServiceTemplate;
use Flasher\Prime\FlasherInterface;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use LibreNMS\Alerting\QueryBuilderFilter;
@ -60,7 +60,7 @@ class ServiceTemplateController extends Controller
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\View\View
*/
public function store(Request $request, FlasherInterface $flasher)
public function store(Request $request, ToastInterface $toast)
{
$this->validate(
$request, [
@ -105,7 +105,7 @@ class ServiceTemplateController extends Controller
}
$template->groups()->sync($request->groups);
$flasher->addSuccess(__('Service Template :name created', ['name' => $template->name]));
$toast->success(__('Service Template :name created', ['name' => $template->name]));
return redirect()->route('services.templates.index');
}
@ -146,7 +146,7 @@ class ServiceTemplateController extends Controller
* @param \App\Models\ServiceTemplate $template
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\View\View
*/
public function update(Request $request, ServiceTemplate $template, FlasherInterface $flasher)
public function update(Request $request, ServiceTemplate $template, ToastInterface $toast)
{
$this->validate(
$request, [
@ -214,9 +214,9 @@ class ServiceTemplateController extends Controller
if ($template->isDirty() || $devices_updated || isset($device_groups_updated)) {
try {
if ($template->save() || $devices_updated || isset($device_groups_updated)) {
$flasher->addSuccess(__('Service Template :name updated', ['name' => $template->name]));
$toast->success(__('Service Template :name updated', ['name' => $template->name]));
} else {
$flasher->addError(__('Failed to save'));
$toast->error(__('Failed to save'));
return redirect()->back()->withInput();
}
@ -226,7 +226,7 @@ class ServiceTemplateController extends Controller
]);
}
} else {
$flasher->addInfo(__('No changes made'));
$toast->info(__('No changes made'));
}
return redirect()->route('services.templates.index');

View File

@ -25,6 +25,7 @@
namespace App\Http\Controllers;
use App\Http\Interfaces\ToastInterface;
use App\Http\Requests\StoreUserRequest;
use App\Http\Requests\UpdateUserRequest;
use App\Models\AuthLog;
@ -32,7 +33,6 @@ use App\Models\Dashboard;
use App\Models\User;
use App\Models\UserPref;
use Auth;
use Flasher\Prime\FlasherInterface;
use Illuminate\Support\Str;
use LibreNMS\Authentication\LegacyAuth;
use LibreNMS\Config;
@ -90,7 +90,7 @@ class UserController extends Controller
* @param StoreUserRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreUserRequest $request, FlasherInterface $flasher)
public function store(StoreUserRequest $request, ToastInterface $toast)
{
$user = $request->only(['username', 'realname', 'email', 'descr', 'can_modify_passwd']);
$user['auth_type'] = LegacyAuth::getType();
@ -105,12 +105,12 @@ class UserController extends Controller
$this->updateTimezone($user, $request->get('timezone'));
if ($user->save()) {
$flasher->addSuccess(__('User :username created', ['username' => $user->username]));
$toast->success(__('User :username created', ['username' => $user->username]));
return redirect(route('users.index'));
}
$flasher->addError(__('Failed to create user'));
$toast->error(__('Failed to create user'));
return redirect()->back();
}
@ -169,7 +169,7 @@ class UserController extends Controller
* @param User $user
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateUserRequest $request, User $user, FlasherInterface $flasher)
public function update(UpdateUserRequest $request, User $user, ToastInterface $toast)
{
if ($request->get('new_password') && $user->canSetPassword($request->user())) {
$user->setPassword($request->new_password);
@ -191,24 +191,24 @@ class UserController extends Controller
}
if ($request->has('dashboard') && $this->updateDashboard($user, $request->get('dashboard'))) {
$flasher->addSuccess(__('Updated dashboard for :username', ['username' => $user->username]));
$toast->success(__('Updated dashboard for :username', ['username' => $user->username]));
}
if ($request->has('timezone') && $this->updateTimezone($user, $request->get('timezone'))) {
if ($request->get('timezone') != 'default') {
$flasher->addSuccess(__('Updated timezone for :username', ['username' => $user->username]));
$toast->success(__('Updated timezone for :username', ['username' => $user->username]));
} else {
$flasher->addSuccess(__('Cleared timezone for :username', ['username' => $user->username]));
$toast->success(__('Cleared timezone for :username', ['username' => $user->username]));
}
}
if ($user->save()) {
$flasher->addSuccess(__('User :username updated', ['username' => $user->username]));
$toast->success(__('User :username updated', ['username' => $user->username]));
return redirect(route(Str::contains(URL::previous(), 'preferences') ? 'preferences.index' : 'users.index'));
}
$flasher->addError(__('Failed to update user :username', ['username' => $user->username]));
$toast->error(__('Failed to update user :username', ['username' => $user->username]));
return redirect()->back();
}

View File

@ -0,0 +1,70 @@
<?php
/**
* ToastInterface.php
*
* -Description-
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* @link https://www.librenms.org
*
* @copyright 2024 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
namespace App\Http\Interfaces;
use Illuminate\Session\SessionManager;
class ToastInterface
{
public function __construct(
private SessionManager $session
) {
}
public function info(string $title, ?string $message = null, ?array $options = null): static
{
return $this->message('info', $title, $message, $options);
}
public function success(string $title, ?string $message = null, ?array $options = null): static
{
return $this->message('success', $title, $message, $options);
}
public function error(string $title, ?string $message = null, ?array $options = null): static
{
return $this->message('error', $title, $message, $options);
}
public function warning(string $title, ?string $message = null, ?array $options = null): static
{
return $this->message('warning', $title, $message, $options);
}
public function message(string $level, string $title, ?string $message = null, ?array $options = null): static
{
$notifications = $this->session->get('toasts', []);
array_push($notifications, [
'level' => $level,
'title' => $message === null ? '' : $title,
'message' => $message ?? $title,
'options' => $options ?? [],
]);
$this->session->flash('toasts', $notifications);
return $this;
}
}

View File

@ -33,7 +33,7 @@ class AuthEventListener
DB::table('authlog')->insert(['user' => $user->username ?: '', 'address' => Request::ip(), 'result' => 'Logged In']);
flash()->addInfo('Welcome ' . ($user->realname ?: $user->username));
toast()->info('Welcome ' . ($user->realname ?: $user->username));
}
/**

View File

@ -136,7 +136,7 @@ class LegacyUserProvider implements UserProvider
if (Debug::isEnabled()) {
$auth_message .= '<br /> ' . $ae->getFile() . ': ' . $ae->getLine();
}
flash()->addError($auth_message);
toast()->error($auth_message);
$username = $username ?? Session::get('username', $credentials['username']);
@ -183,7 +183,7 @@ class LegacyUserProvider implements UserProvider
error_reporting(-1);
} catch (AuthenticationException $ae) {
flash()->addError($ae->getMessage());
toast()->error($ae->getMessage());
}
if (empty($new_user)) {

View File

@ -0,0 +1,36 @@
<?php
namespace App\View\Components;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
use Illuminate\Session\SessionManager;
use Illuminate\View\Component;
class Toast extends Component
{
public array $purifier_config = [
'HTML.Allowed' => 'a[href],b,i,ul,ol,li,h1,h2,h3,h4,br,p,pre',
'URI.DisableExternal' => true,
];
public ?array $toasts;
/**
* Create a new component instance.
*/
public function __construct(Request $request, SessionManager $session)
{
$this->purifier_config['URI.Host'] = $request->getHttpHost();
$this->toasts = $session->get('toasts');
$session->forget('toasts'); // to ward againsts double toasts
}
/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return view('components.toast');
}
}

View File

@ -48,7 +48,6 @@
"pear/console_table": "^1.3",
"pear/net_dns2": "^1.5",
"php-amqplib/php-amqplib": "^3.1",
"php-flasher/flasher-laravel": "^1.12",
"phpmailer/phpmailer": "~6.0",
"predis/predis": "^2.0",
"silber/bouncer": "^1.0",

191
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "dd407332d453e562b204848f9044161b",
"content-hash": "f4f2fd9863881ce9c624955837bcb192",
"packages": [
{
"name": "amenadiel/jpgraph",
@ -4283,195 +4283,6 @@
},
"time": "2024-02-07T17:21:26+00:00"
},
{
"name": "php-flasher/flasher",
"version": "v1.15.10",
"source": {
"type": "git",
"url": "https://github.com/php-flasher/flasher.git",
"reference": "33ae74e73f62814fff4e78e78f912d9b6ddf82d0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-flasher/flasher/zipball/33ae74e73f62814fff4e78e78f912d9b6ddf82d0",
"reference": "33ae74e73f62814fff4e78e78f912d9b6ddf82d0",
"shasum": ""
},
"require": {
"php": ">=5.3"
},
"type": "library",
"autoload": {
"files": [
"helpers.php"
],
"psr-4": {
"Flasher\\Prime\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Younes KHOUBZA",
"email": "younes.khoubza@gmail.com",
"homepage": "https://www.linkedin.com/in/younes-khoubza",
"role": "Developer"
}
],
"description": "PHPFlasher - A powerful & easy-to-use package for adding flash messages to Laravel or Symfony projects. Provides feedback to users, improves engagement & enhances user experience. Intuitive design for beginners & experienced developers. A reliable, flexible solution.",
"homepage": "https://php-flasher.io",
"keywords": [
"custom-adapter",
"dark-mode",
"desktop-notifications",
"flash-messages",
"framework-agnostic",
"javascript",
"laravel",
"notification-system",
"noty",
"notyf",
"php",
"php-flasher",
"phpstorm-auto-complete",
"pnotify",
"rtl",
"sweetalert",
"symfony",
"toastr",
"user-experience",
"user-feedback",
"yoeunes"
],
"support": {
"source": "https://github.com/php-flasher/flasher/tree/v1.15.10"
},
"funding": [
{
"url": "https://www.paypal.com/paypalme/yoeunes",
"type": "custom"
},
{
"url": "https://github.com/yoeunes",
"type": "github"
},
{
"url": "https://ko-fi.com/yoeunes",
"type": "ko_fi"
},
{
"url": "https://opencollective.com/php-flasher",
"type": "open_collective"
},
{
"url": "https://www.patreon.com/yoeunes",
"type": "patreon"
}
],
"time": "2023-12-16T17:11:36+00:00"
},
{
"name": "php-flasher/flasher-laravel",
"version": "v1.15.10",
"source": {
"type": "git",
"url": "https://github.com/php-flasher/flasher-laravel.git",
"reference": "18b6ed815a791d483dd90a4f4561abb1b4cdb099"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-flasher/flasher-laravel/zipball/18b6ed815a791d483dd90a4f4561abb1b4cdb099",
"reference": "18b6ed815a791d483dd90a4f4561abb1b4cdb099",
"shasum": ""
},
"require": {
"illuminate/support": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0",
"php": ">=5.3",
"php-flasher/flasher": "^1.15.9"
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"Flasher": "Flasher\\Laravel\\Facade\\Flasher"
},
"providers": [
"Flasher\\Laravel\\FlasherServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Flasher\\Laravel\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Younes KHOUBZA",
"email": "younes.khoubza@gmail.com",
"homepage": "https://www.linkedin.com/in/younes-khoubza",
"role": "Developer"
}
],
"description": "PHPFlasher - A powerful & easy-to-use package for adding flash messages to Laravel or Symfony projects. Provides feedback to users, improves engagement & enhances user experience. Intuitive design for beginners & experienced developers. A reliable, flexible solution.",
"homepage": "https://php-flasher.io",
"keywords": [
"custom-adapter",
"dark-mode",
"desktop-notifications",
"flash-messages",
"framework-agnostic",
"javascript",
"laravel",
"notification-system",
"noty",
"notyf",
"php",
"php-flasher",
"phpstorm-auto-complete",
"pnotify",
"rtl",
"sweetalert",
"symfony",
"toastr",
"user-experience",
"user-feedback",
"yoeunes"
],
"support": {
"source": "https://github.com/php-flasher/flasher-laravel/tree/v1.15.10"
},
"funding": [
{
"url": "https://www.paypal.com/paypalme/yoeunes",
"type": "custom"
},
{
"url": "https://github.com/yoeunes",
"type": "github"
},
{
"url": "https://ko-fi.com/yoeunes",
"type": "ko_fi"
},
{
"url": "https://opencollective.com/php-flasher",
"type": "open_collective"
},
{
"url": "https://www.patreon.com/yoeunes",
"type": "patreon"
}
],
"time": "2024-01-21T17:30:21+00:00"
},
{
"name": "php-http/client-common",
"version": "2.7.1",

View File

@ -1,21 +0,0 @@
<?php
return [
'default' => 'template.librenms',
'root_script' => null,
'template_factory' => [
'templates' => [
'librenms' => [
'view' => 'layouts.flasher-notification',
'options' => [
'timeout' => 12000,
'style' => [
'top' => '55px',
],
],
],
],
],
];

File diff suppressed because one or more lines are too long

View File

@ -34,13 +34,13 @@ toastr.options = {
titleClass: 'tw-text-xl tw-leading-7 tw-font-semibold tw-capitalize',
messageClass: 'tw-mt-1 tw-text-base tw-leading-5 tw-text-gray-500 dark:tw-text-white',
iconClasses: {
error: 'flasher-error tw-text-red-600 tw-border-red-600',
info: 'flasher-info tw-text-blue-600 tw-border-blue-600',
success: 'flasher-success tw-text-green-600 tw-border-green-600',
warning: 'flasher-warning tw-text-yellow-600 tw-border-yellow-600'
error: 'toast-error tw-text-red-600 tw-border-red-600',
info: 'toast-info tw-text-blue-600 tw-border-blue-600',
success: 'toast-success tw-text-green-600 tw-border-green-600',
warning: 'toast-warning tw-text-yellow-600 tw-border-yellow-600'
},
timeOut: 12000,
progressBar: true,
progressClass: 'toast-progress tw-h-1 tw-bg-current tw-absolute tw-bottom-0 tw-left-0 tw-mr-0.5',
containerId: 'flasher-container-top-right'
containerId: 'toast-container-top-right'
};

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
!function(a){a(["jquery"],function(a){return function(){function b(a,b,c){return o({type:u.error,iconClass:p().iconClasses.error,message:a,optionsOverride:c,title:b})}function c(b,c){return b||(b=p()),r=a("#"+b.containerId),r.length?r:(c&&(r=l(b)),r)}function d(a,b,c){return o({type:u.info,iconClass:p().iconClasses.info,message:a,optionsOverride:c,title:b})}function e(a){s=a}function f(a,b,c){return o({type:u.success,iconClass:p().iconClasses.success,message:a,optionsOverride:c,title:b})}function g(a,b,c){return o({type:u.warning,iconClass:p().iconClasses.warning,message:a,optionsOverride:c,title:b})}function h(a){var b=p();r||c(b),k(a,b)||j(b)}function i(b){var d=p();return r||c(d),b&&0===a(":focus",b).length?void q(b):void(r.children().length&&r.remove())}function j(b){for(var c=r.children(),d=c.length-1;d>=0;d--)k(a(c[d]),b)}function k(b,c){return b&&0===a(":focus",b).length?(b[c.hideMethod]({duration:c.hideDuration,easing:c.hideEasing,complete:function(){q(b)}}),!0):!1}function l(b){return r=a("<div/>").attr("id",b.containerId).addClass(b.positionClass).attr("aria-live","polite").attr("role","alert"),r.appendTo(a(b.target)),r}function m(){return{tapToDismiss:!0,toastClass:"toast",containerId:"toast-container",debug:!1,showMethod:"fadeIn",showDuration:300,showEasing:"swing",onShown:void 0,hideMethod:"fadeOut",hideDuration:1e3,hideEasing:"swing",onHidden:void 0,extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},iconClass:"toast-info",positionClass:"toast-top-right",timeOut:5e3,titleClass:"toast-title",messageClass:"toast-message",target:"body",closeHtml:"<button>&times;</button>",newestOnTop:!0}}function n(a){s&&s(a)}function o(b){function d(b){return!a(":focus",j).length||b?j[g.hideMethod]({duration:g.hideDuration,easing:g.hideEasing,complete:function(){q(j),g.onHidden&&"hidden"!==o.state&&g.onHidden(),o.state="hidden",o.endTime=new Date,n(o)}}):void 0}function e(){(g.timeOut>0||g.extendedTimeOut>0)&&(i=setTimeout(d,g.extendedTimeOut))}function f(){clearTimeout(i),j.stop(!0,!0)[g.showMethod]({duration:g.showDuration,easing:g.showEasing})}var g=p(),h=b.iconClass||g.iconClass;"undefined"!=typeof b.optionsOverride&&(g=a.extend(g,b.optionsOverride),h=b.optionsOverride.iconClass||h),t++,r=c(g,!0);var i=null,j=a("<div/>"),k=a("<div/>"),l=a("<div/>"),m=a(g.closeHtml),o={toastId:t,state:"visible",startTime:new Date,options:g,map:b};return b.iconClass&&j.addClass(g.toastClass).addClass(h),b.title&&(k.append(b.title).addClass(g.titleClass),j.append(k)),b.message&&(l.append(b.message).addClass(g.messageClass),j.append(l)),g.closeButton&&(m.addClass("toast-close-button").attr("role","button"),j.prepend(m)),j.hide(),g.newestOnTop?r.prepend(j):r.append(j),j[g.showMethod]({duration:g.showDuration,easing:g.showEasing,complete:g.onShown}),g.timeOut>0&&(i=setTimeout(d,g.timeOut)),j.on("mouseenter",f).on("mouseleave",e),!g.onclick&&g.tapToDismiss&&j.on("click",d),g.closeButton&&m&&m.on("click",function(a){a.stopPropagation?a.stopPropagation():void 0!==a.cancelBubble&&a.cancelBubble!==!0&&(a.cancelBubble=!0),d(!0)}),g.onclick&&j.on("click", function(){g.onclick(),d()}),n(o),g.debug&&console&&console.log(o),j}function p(){return a.extend({},m(),v.options)}function q(a){r||(r=c()),a.is(":visible")||(a.remove(),a=null,0===r.children().length&&r.remove())}var r,s,t=0,u={error:"error",info:"info",success:"success",warning:"warning"},v={clear:h,remove:i,error:b,getContainer:c,info:d,options:{},subscribe:e,success:f,version:"2.0.3",warning:g};return v}()})}("function"==typeof define&&define.amd?define:function(a,b){"undefined"!=typeof module&&module.exports?module.exports=b(require("jquery")):window.toastr=b(window.jQuery)});
!function(e){e(["jquery"],function(e){return function(){function t(e,t,n){return f({type:O.error,iconClass:g().iconClasses.error,message:e,optionsOverride:n,title:t})}function n(t,n){return t||(t=g()),v=e("#"+t.containerId),v.length?v:(n&&(v=c(t)),v)}function i(e,t,n){return f({type:O.info,iconClass:g().iconClasses.info,message:e,optionsOverride:n,title:t})}function o(e){w=e}function s(e,t,n){return f({type:O.success,iconClass:g().iconClasses.success,message:e,optionsOverride:n,title:t})}function a(e,t,n){return f({type:O.warning,iconClass:g().iconClasses.warning,message:e,optionsOverride:n,title:t})}function r(e){var t=g();v||n(t),l(e,t)||u(t)}function d(t){var i=g();return v||n(i),t&&0===e(":focus",t).length?void h(t):void(v.children().length&&v.remove())}function u(t){for(var n=v.children(),i=n.length-1;i>=0;i--)l(e(n[i]),t)}function l(t,n){return t&&0===e(":focus",t).length?(t[n.hideMethod]({duration:n.hideDuration,easing:n.hideEasing,complete:function(){h(t)}}),!0):!1}function c(t){return v=e("<div/>").attr("id",t.containerId).addClass(t.positionClass).attr("aria-live","polite").attr("role","alert"),v.appendTo(e(t.target)),v}function p(){return{tapToDismiss:!0,toastClass:"toast",containerId:"toast-container",debug:!1,showMethod:"fadeIn",showDuration:300,showEasing:"swing",onShown:void 0,hideMethod:"fadeOut",hideDuration:1e3,hideEasing:"swing",onHidden:void 0,extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},iconClass:"toast-info",positionClass:"toast-top-right",timeOut:5e3,titleClass:"toast-title",messageClass:"toast-message",target:"body",closeHtml:'<button type="button">&times;</button>',newestOnTop:!0,preventDuplicates:!1,progressBar:!1}}function m(e){w&&w(e)}function f(t){function i(t){return!e(":focus",l).length||t?(clearTimeout(O.intervalId),l[r.hideMethod]({duration:r.hideDuration,easing:r.hideEasing,complete:function(){h(l),r.onHidden&&"hidden"!==b.state&&r.onHidden(),b.state="hidden",b.endTime=new Date,m(b)}})):void 0}function o(){(r.timeOut>0||r.extendedTimeOut>0)&&(u=setTimeout(i,r.extendedTimeOut),O.maxHideTime=parseFloat(r.extendedTimeOut),O.hideEta=(new Date).getTime()+O.maxHideTime)}function s(){clearTimeout(u),O.hideEta=0,l.stop(!0,!0)[r.showMethod]({duration:r.showDuration,easing:r.showEasing})}function a(){var e=(O.hideEta-(new Date).getTime())/O.maxHideTime*100;f.width(e+"%")}var r=g(),d=t.iconClass||r.iconClass;if("undefined"!=typeof t.optionsOverride&&(r=e.extend(r,t.optionsOverride),d=t.optionsOverride.iconClass||d),r.preventDuplicates){if(t.message===C)return;C=t.message}T++,v=n(r,!0);var u=null,l=e("<div/>"),c=e("<div/>"),p=e("<div/>"),f=e("<div/>"),w=e(r.closeHtml),O={intervalId:null,hideEta:null,maxHideTime:null},b={toastId:T,state:"visible",startTime:new Date,options:r,map:t};return t.iconClass&&l.addClass(r.toastClass).addClass(d),t.title&&(c.append(t.title).addClass(r.titleClass),l.append(c)),t.message&&(p.append(t.message).addClass(r.messageClass),l.append(p)),r.closeButton&&(w.addClass("toast-close-button").attr("role","button"),l.prepend(w)),r.progressBar&&(f.addClass("toast-progress"),l.prepend(f)),l.hide(),r.newestOnTop?v.prepend(l):v.append(l),l[r.showMethod]({duration:r.showDuration,easing:r.showEasing,complete:r.onShown}),r.timeOut>0&&(u=setTimeout(i,r.timeOut),O.maxHideTime=parseFloat(r.timeOut),O.hideEta=(new Date).getTime()+O.maxHideTime,r.progressBar&&(O.intervalId=setInterval(a,10))),l.hover(s,o),!r.onclick&&r.tapToDismiss&&l.click(i),r.closeButton&&w&&w.click(function(e){e.stopPropagation?e.stopPropagation():void 0!==e.cancelBubble&&e.cancelBubble!==!0&&(e.cancelBubble=!0),i(!0)}),r.onclick&&l.click(function(){r.onclick(),i()}),m(b),r.debug&&console&&console.log(b),l}function g(){return e.extend({},p(),b.options)}function h(e){v||(v=n()),e.is(":visible")||(e.remove(),e=null,0===v.children().length&&(v.remove(),C=void 0))}var v,w,C,T=0,O={error:"error",info:"info",success:"success",warning:"warning"},b={clear:r,remove:d,error:t,getContainer:n,info:i,options:{},subscribe:o,success:s,version:"2.1.0",warning:a};return b}()})}("function"==typeof define&&define.amd?define:function(e,t){"undefined"!=typeof module&&module.exports?module.exports=t(require("jquery")):window.toastr=t(window.jQuery)});

View File

@ -2,7 +2,7 @@
"/js/app.js": "/js/app.js?id=1ecd9b13d60fe23a9729684f4d9dc663",
"/js/manifest.js": "/js/manifest.js?id=2eb19d92c19953027907b72ff5963ebb",
"/css/vendor.css": "/css/vendor.css?id=d520734ded0ec75b0a572aa8db1c2161",
"/css/app.css": "/css/app.css?id=0d16038c1a9e73edd6ffaa33b3c30683",
"/css/app.css": "/css/app.css?id=38985fa8ef06f355f2934f256b639666",
"/js/vendor.js": "/js/vendor.js?id=3b22b85b4e5a64e37dd954c0b147b3f3",
"/js/lang/de.js": "/js/lang/de.js?id=9a6f9c23a4b209504cce12ce85315a3c",
"/js/lang/en.js": "/js/lang/en.js?id=43cfd926c2a415bdbb2e59676ab29875",

View File

@ -115,3 +115,19 @@ if (! function_exists('preg_match_any')) {
return false;
}
}
if (! function_exists('toast')) {
/**
* send a toastr popup or return ToastInterface
*/
function toast(?string $title = null, ?string $message = null, string $level = 'info', ?array $options = null)
{
$toast = app(\App\Http\Interfaces\ToastInterface::class);
if (! is_null($message)) {
return $toast->message($title, $message, $level, $options);
}
return $toast;
}
}

View File

@ -14,9 +14,9 @@ if (Auth::user()->hasGlobalAdmin()) {
if (class_exists($class)) {
$transport = app($class);
if ($transport->handleOauth($request)) {
flash()->addSuccess("$transport_name added successfully.");
toast()->sucess("$transport_name added successfully.");
} else {
flash()->addError("$transport_name was not added. Check the log for details.");
toast()->error("$transport_name was not added. Check the log for details.");
}
}
}

View File

@ -48,9 +48,9 @@ if (! empty($_POST['editing'])) {
if ($device_model->isDirty()) {
if ($device_model->save()) {
flash()->addSuccess(__('Device record updated'));
toast()->sucess(__('Device record updated'));
} else {
flash()->addError(__('Device record update error'));
toast()->error(__('Device record update error'));
}
}
@ -58,13 +58,13 @@ if (! empty($_POST['editing'])) {
if (Auth::user()->hasGlobalAdmin()) {
$result = renamehost($device['device_id'], trim($_POST['hostname']), 'webui');
if ($result == '') {
flash()->addSuccess("Hostname updated from {$device['hostname']} to {$_POST['hostname']}");
toast()->success("Hostname updated from {$device['hostname']} to {$_POST['hostname']}");
$reload = true;
} else {
flash()->addError($result . '. Does your web server have permission to modify the rrd files?');
toast()->error($result . '. Does your web server have permission to modify the rrd files?');
}
} else {
flash()->addError('Only administrative users may update the device hostname');
toast()->error('Only administrative users may update the device hostname');
}
}

View File

@ -159,31 +159,27 @@ $max_repeaters = $device->getAttrib('snmp_max_repeaters');
if (isset($update_message)) {
if (is_array($update_message)) {
foreach ($update_message as $message) {
flash()->addSuccess($message);
toast()->success($message);
}
}
if (is_string($update_message)) {
flash()->addSuccess($update_message);
toast()->success($update_message);
}
unset($message, $update_message);
}
// use flash()->addError to call attention to the problem; don't let it time out
// use flash()->error to call attention to the problem; don't let it time out
if (isset($update_failed_message)) {
if (is_array($update_failed_message)) {
foreach ($update_failed_message as $error) {
flash()
->option('timeout', 30000)
->addError($error);
toast()->error($error, options: ['timeOut' => 30000]);
}
}
if (is_string($update_failed_message)) {
flash()
->option('timeout', 30000)
->addError($update_failed_message);
toast()->error($update_failed_message, options: ['timeOut' => 30000]);
}
unset($error, $update_failed_message);

View File

@ -2,4 +2,4 @@
@tailwind components;
@tailwind utilities;
@import 'flasher.css';
@import 'toast.css';

View File

@ -22,37 +22,47 @@
* @author Tony Murray <murraytony@gmail.com>
*/
#flasher-container-top-right {
#toast-container-top-right {
position: fixed;
z-index: 999999;
top: 55px;
right: 12px;
}
#flasher-container-top-right a {
#toast-container-top-right a {
font-weight: bold;
}
#flasher-container-top-right > div {
#toast-container-top-right > div {
width: 304px;
min-height: 50px;
background-position: 10px center;
background-repeat: no-repeat;
}
.flasher-error {
.toast-error {
background-image: url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMzIgMzIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNiIgcj0iMTUiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNMTYgMEExNiAxNiAwIDAgMCAwIDE2YTE2IDE2IDAgMCAwIDE2IDE2IDE2IDE2IDAgMCAwIDE2LTE2QTE2IDE2IDAgMCAwIDE2IDB6bS02IDlhMSAxIDAgMCAxIC43MDcuMjkzTDE2IDE0LjU4Nmw1LjI5My01LjI5M2ExIDEgMCAwIDEgMS40MTQgMCAxIDEgMCAwIDEgMCAxLjQxNEwxNy40MTQgMTZsNS4yOTMgNS4yOTNhMSAxIDAgMCAxIDAgMS40MTQgMSAxIDAgMCAxLTEuNDE0IDBMMTYgMTcuNDE0bC01LjI5MyA1LjI5M2ExIDEgMCAwIDEtMS40MTQgMCAxIDEgMCAwIDEgMC0xLjQxNEwxNC41ODYgMTZsLTUuMjkzLTUuMjkzYTEgMSAwIDAgMSAwLTEuNDE0QTEgMSAwIDAgMSAxMCA5eiIgZmlsbD0iI2RjMjYyNiIvPjwvc3ZnPg==");
background-size: 32px;
}
.flasher-info {
.toast-info {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNiIgcj0iMTUiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNMTYgMEExNiAxNiAwIDAgMCAwIDE2YTE2IDE2IDAgMCAwIDE2IDE2IDE2IDE2IDAgMCAwIDE2LTE2QTE2IDE2IDAgMCAwIDE2IDB6bTAgNmM1LjUxMSAwIDEwIDQuNDg5IDEwIDEwcy00LjQ4OSAxMC0xMCAxMFM2IDIxLjUxMSA2IDE2IDEwLjQ4OSA2IDE2IDZ6bTAgMmMtNC40MyAwLTggMy41Ny04IDhzMy41NyA4IDggOCA4LTMuNTcgOC04LTMuNTctOC04LTh6bTAgM2ExIDEgMCAwIDEgMSAxdjRhMSAxIDAgMCAxLTEgMSAxIDEgMCAwIDEtMS0xdi00YTEgMSAwIDAgMSAxLTF6bTAgOGguMDFhMSAxIDAgMCAxIDEgMSAxIDEgMCAwIDEtMSAxSDE2YTEgMSAwIDAgMS0xLTEgMSAxIDAgMCAxIDEtMXoiIGZpbGw9IiMyNTYzZWIiIC8+PC9zdmc+");
background-size: 32px;
}
.flasher-success {
.toast-success {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNiIgcj0iMTUiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNMTYgMEExNiAxNiAwIDAgMCAwIDE2YTE2IDE2IDAgMCAwIDE2IDE2IDE2IDE2IDAgMCAwIDE2LTE2QTE2IDE2IDAgMCAwIDE2IDB6bTcgMTBhMSAxIDAgMCAxIC43MDcuMjkzIDEgMSAwIDAgMSAwIDEuNDE0bC0xMCAxMGExIDEgMCAwIDEtMS40MTQgMGwtNC00YTEgMSAwIDAgMSAwLTEuNDE0IDEgMSAwIDAgMSAxLjQxNCAwTDEzIDE5LjU4Nmw5LjI5My05LjI5M0ExIDEgMCAwIDEgMjMgMTB6IiBmaWxsPSIjMDU5NjY5IiAvPjwvc3ZnPg==");
background-size: 32px;
}
.flasher-warning {
.toast-warning {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNiIgcj0iMTUiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNMTYgMEExNiAxNiAwIDAgMCAwIDE2YTE2IDE2IDAgMCAwIDE2IDE2IDE2IDE2IDAgMCAwIDE2LTE2QTE2IDE2IDAgMCAwIDE2IDB6bTAgNi4xNTZjMS4wMTYgMCAyLjAzMi40OSAyLjU5OCAxLjQ2OWw2LjkyNyAxMmMxLjEzMSAxLjk1OC0uMzM2IDQuNS0yLjU5NyA0LjVIOS4wNzJjLTIuMjYxIDAtMy43MjgtMi41NDItMi41OTctNC41bDYuOTI3LTEyYy41NjYtLjk3OSAxLjU4Mi0xLjQ2OSAyLjU5OC0xLjQ2OXptMCAxLjkzOGMtLjMzIDAtLjY2LjE3Ny0uODY1LjUzMWwtNi45MyAxMmMtLjQwOS43MDguMDQ5IDEuNS44NjcgMS41aDEzLjg1NmMuODE4IDAgMS4yNzYtLjc5Mi44NjctMS41bC02LjkzLTEyYy0uMjA0LS4zNTQtLjUzNC0uNTMxLS44NjUtLjUzMXptMCA0LjAzMWExIDEgMCAwIDEgMSAxdjJhMSAxIDAgMCAxLTEgMSAxIDEgMCAwIDEtMS0xdi0yYTEgMSAwIDAgMSAxLTF6bTAgNmguMDFhMSAxIDAgMCAxIDEgMSAxIDEgMCAwIDEtMSAxSDE2YTEgMSAwIDAgMS0xLTEgMSAxIDAgMCAxIDEtMXoiIGZpbGw9IiNkOTc3MDYiLz48L3N2Zz4=");
background-size: 32px;
}
.toast-progress {
position: absolute;
left: 0;
bottom: 0;
height: 4px;
background-color: #000000;
opacity: 0.4;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40);
filter: alpha(opacity=40);
}

View File

@ -0,0 +1,9 @@
@if($toasts)
<script>
document.addEventListener("DOMContentLoaded", function() {
@foreach ($toasts as $toast)
toastr["{{ $toast['level'] }}"]({{ Js::from(\LibreNMS\Util\Clean::html($toast['message'], $purifier_config)) }}, "{{ $toast['title'] }}", {{ JS::from($toast['options']) }});
@endforeach
});
</script>
@endif

View File

@ -1,37 +0,0 @@
<?php
$title = $envelope->getTitle();
switch ($envelope->getType()) {
case 'success':
$color = 'tw-text-emerald-600';
$class = 'flasher-success';
break;
case 'error':
$color = 'tw-text-red-600';
$class = 'flasher-error';
break;
case 'warning':
$color = 'tw-text-amber-600';
$class = 'flasher-warning';
break;
case 'info':
default:
$color = 'tw-text-blue-600';
$class = 'flasher-info';
break;
}
?>
<div class="{{ $class }} {{ $color }} tw-border-current tw-flex tw-flex-col tw-justify-between tw-bg-white dark:tw-bg-dark-gray-300 tw-opacity-80 hover:tw-opacity-100 tw-rounded-md tw-shadow-lg hover:tw-shadow-xl tw-border-l-8 tw-border-t-0.5 tw-border-r-0.5 tw-border-b-0.5 tw-mt-2 tw-cursor-pointer">
<div class="tw-pl-20 tw-py-4 tw-pr-2 tw-overflow-hidden">
@if($title)
<div class="tw-text-xl tw-leading-7 tw-font-semibold tw-capitalize">
{{ $title }}
</div>
@endif
<div class="tw-mt-1 tw-text-base tw-leading-5 tw-text-gray-500 dark:tw-text-white">
{!! clean(stripslashes($envelope->getMessage()), 'notifications') !!}
</div>
</div>
<div class="tw-h-1 tw-flex tw-mr-0.5">
<span class="flasher-progress tw-bg-current"></span>
</div>
</div>

View File

@ -78,7 +78,6 @@
</script>
<script src="{{ asset('js/librenms.js?ver=22052024') }}"></script>
<script type="text/javascript" src="{{ asset('js/overlib_mini.js') }}"></script>
<script type="text/javascript" src="{{ asset('js/flasher.min.js?ver=0.6.1') }}"></script>
<script type="text/javascript" src="{{ asset('js/toastr.min.js?ver=05072021') }}"></script>
<script type="text/javascript" src="{{ asset('js/boot.js?ver=10272021') }}"></script>
<script>
@ -119,7 +118,7 @@
@yield('scripts')
@flasher_render
<x-toast />
@stack('scripts')
</body>

View File

@ -5,6 +5,7 @@ module.exports = {
'./resources/**/*.blade.php',
'./resources/**/*.js',
'./resources/**/*.vue',
'./html/js/boot.js',
],
darkMode: 'class',
theme: {