diff --git a/.github/workflows/tests-mysql.yml b/.github/workflows/tests-mysql.yml index 310414cda6..63fcb8e3e2 100644 --- a/.github/workflows/tests-mysql.yml +++ b/.github/workflows/tests-mysql.yml @@ -77,3 +77,7 @@ jobs: DB_PORT: ${{ job.services.mysql.ports[3306] }} DB_USERNAME: root run: php artisan test + + - name: Test failure + if: ${{ failure() }} + run: docker exec "$PROJECT_NAME-php-fpm" cat storage/logs/laravel.log diff --git a/.github/workflows/tests-postgres.yml b/.github/workflows/tests-postgres.yml index ae48277be3..3cc5244c5e 100644 --- a/.github/workflows/tests-postgres.yml +++ b/.github/workflows/tests-postgres.yml @@ -75,3 +75,7 @@ jobs: DB_USERNAME: snipeit DB_PASSWORD: password run: php artisan test + + - name: Test failure + if: ${{ failure() }} + run: docker exec "$PROJECT_NAME-php-fpm" cat storage/logs/laravel.log diff --git a/.github/workflows/tests-sqlite.yml b/.github/workflows/tests-sqlite.yml index 8bf0115169..011d19d783 100644 --- a/.github/workflows/tests-sqlite.yml +++ b/.github/workflows/tests-sqlite.yml @@ -59,3 +59,7 @@ jobs: env: DB_CONNECTION: sqlite_testing run: php artisan test + + - name: Test failure + if: ${{ failure() }} + run: docker exec "$PROJECT_NAME-php-fpm" cat storage/logs/laravel.log diff --git a/app/Http/Controllers/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php index e29fa7c63b..278d7e2081 100644 --- a/app/Http/Controllers/Account/AcceptanceController.php +++ b/app/Http/Controllers/Account/AcceptanceController.php @@ -338,4 +338,5 @@ class AcceptanceController extends Controller return redirect()->to('account/accept')->with('success', $return_msg); } + } diff --git a/app/Http/Controllers/Api/ReportsController.php b/app/Http/Controllers/Api/ReportsController.php index 566911dd27..63fca39d19 100644 --- a/app/Http/Controllers/Api/ReportsController.php +++ b/app/Http/Controllers/Api/ReportsController.php @@ -44,21 +44,6 @@ class ReportsController extends Controller }); } - if ($request->filled('action_type')) { - $actionlogs = $actionlogs->where('action_type', '=', $request->input('action_type'))->orderBy('created_at', 'desc'); - } - - if ($request->filled('created_by')) { - $actionlogs = $actionlogs->where('created_by', '=', $request->input('created_by')); - } - - if ($request->filled('action_source')) { - $actionlogs = $actionlogs->where('action_source', '=', $request->input('action_source'))->orderBy('created_at', 'desc'); - } - - if ($request->filled('remote_ip')) { - $actionlogs = $actionlogs->where('remote_ip', '=', $request->input('remote_ip'))->orderBy('created_at', 'desc'); - } if ($request->filled('uploads')) { $actionlogs = $actionlogs->whereNotNull('filename')->orderBy('created_at', 'desc'); @@ -74,6 +59,8 @@ class ReportsController extends Controller 'note', 'remote_ip', 'user_agent', + 'target_type', + 'item_type', 'action_source', 'action_date', ]; diff --git a/app/Http/Controllers/Assets/AssetsController.php b/app/Http/Controllers/Assets/AssetsController.php index 50a758ac37..52eb751a89 100755 --- a/app/Http/Controllers/Assets/AssetsController.php +++ b/app/Http/Controllers/Assets/AssetsController.php @@ -111,8 +111,10 @@ class AssetsController extends Controller $settings = Setting::getSettings(); - $success = false; + $successes = []; + $failures = []; $serials = $request->input('serials'); + $asset = null; for ($a = 1; $a <= count($asset_tags); $a++) { $asset = new Asset(); @@ -199,20 +201,35 @@ class AssetsController extends Controller $asset->checkOut($target, auth()->user(), date('Y-m-d H:i:s'), $request->input('expected_checkin', null), 'Checked out on asset creation', $request->get('name'), $location); } - $success = true; - + $successes[] = " $asset->id]) . "' style='color: white;'>" . e($asset->asset_tag) . ""; + + } else { + $failures[] = join(",", $asset->getErrors()->all()); } } session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => $request->get('checkout_to_type')]); - if ($success) { + if ($successes) { + if ($failures) { + //some succeeded, some failed + return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets')) //FIXME - not tested + ->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)])) + ->with('warning', trans_choice('admin/hardware/message.create.partial_failure', $failures, ['failures' => join("; ", $failures)])); + } else { + if (count($successes) == 1) { + //the most common case, keeping it so we don't have to make every use of that translation string be trans_choice'ed + //and re-translated + return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets')) + ->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', ['hardware' => $asset->id]), 'id', 'tag' => e($asset->asset_tag)])); + } else { + //multi-success + return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets')) + ->with('success-unescaped', trans_choice('admin/hardware/message.create.multi_success_linked', $successes, ['links' => join(", ", $successes)])); + } + } - return redirect()->to(Helper::getRedirectOption($request, $asset->id, 'Assets')) - ->with('success-unescaped', trans('admin/hardware/message.create.success_linked', ['link' => route('hardware.show', ['hardware' => $asset->id]), 'id', 'tag' => e($asset->asset_tag)])); - - } return redirect()->back()->withInput()->withErrors($asset->getErrors()); diff --git a/app/Listeners/CheckoutableListener.php b/app/Listeners/CheckoutableListener.php index ba19d68f20..7d8b5c1c9d 100644 --- a/app/Listeners/CheckoutableListener.php +++ b/app/Listeners/CheckoutableListener.php @@ -3,13 +3,19 @@ namespace App\Listeners; use App\Events\CheckoutableCheckedOut; +use App\Mail\CheckinAccessoryMail; +use App\Mail\CheckinLicenseMail; +use App\Mail\CheckoutAccessoryMail; +use App\Mail\CheckoutAssetMail; +use App\Mail\CheckinAssetMail; +use App\Mail\CheckoutConsumableMail; +use App\Mail\CheckoutLicenseMail; use App\Models\Accessory; use App\Models\Asset; use App\Models\CheckoutAcceptance; use App\Models\Component; use App\Models\Consumable; use App\Models\LicenseSeat; -use App\Models\Recipients\AdminRecipient; use App\Models\Setting; use App\Models\User; use App\Notifications\CheckinAccessoryNotification; @@ -20,10 +26,10 @@ use App\Notifications\CheckoutAssetNotification; use App\Notifications\CheckoutConsumableNotification; use App\Notifications\CheckoutLicenseSeatNotification; use GuzzleHttp\Exception\ClientException; +use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Notification; use Exception; use Illuminate\Support\Facades\Log; -use Osama\LaravelTeamsNotification\TeamsNotification; class CheckoutableListener { @@ -44,41 +50,42 @@ class CheckoutableListener /** * Make a checkout acceptance and attach it in the notification */ + $settings = Setting::getSettings(); $acceptance = $this->getCheckoutAcceptance($event); - $notifiables = $this->getNotifiables($event); + $adminCcEmailsArray = []; + if($settings->admin_cc_email !== '') { + $adminCcEmail = $settings->admin_cc_email; + $adminCcEmailsArray = array_map('trim', explode(',', $adminCcEmail)); + } + $ccEmails = array_filter($adminCcEmailsArray); + $notifiable = $event->checkedOutTo; + $mailable = $this->getCheckoutMailType($event, $acceptance); // Send email notifications try { -// foreach ($notifiables as $notifiable) { -// if ($notifiable instanceof User && $notifiable->email != '') { -// if (! $event->checkedOutTo->locale){ -// Notification::locale(Setting::getSettings()->locale)->send($notifiable, $this->getCheckoutNotification($event, $acceptance)); -// } -// else { -// Notification::send($notifiable, $this->getCheckoutNotification($event, $acceptance)); -// } -// } -// } - - // Send Webhook notification - if ($this->shouldSendWebhookNotification()) { - // Slack doesn't include the URL in its messaging format, so this is needed to hit the endpoint - if (Setting::getSettings()->webhook_selected === 'slack' || Setting::getSettings()->webhook_selected === 'general') { - Notification::route('slack', Setting::getSettings()->webhook_endpoint) - ->notify($this->getCheckoutNotification($event, $acceptance)); + if (!$event->checkedOutTo->locale){ + $mailable->locale($event->checkedOutTo->locale); } - // Handling Microsoft Teams notification - else if (Setting::getSettings()->webhook_selected === 'microsoft') { - $message = $this->getCheckoutNotification($event)->toMicrosoftTeams(); - $notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint); - $notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams - } - else { + /** + * Send an email if any of the following conditions are met: + * 1. The asset requires acceptance + * 2. The item has a EULA + * 3. The item should send an email at check-in/check-out + */ + if ($notifiable instanceof User && $notifiable->email != '') { + if ($event->checkoutable->requireAcceptance() || $event->checkoutable->getEula() || + (method_exists($event->checkoutable, 'checkin_email') && $event->checkoutable->checkin_email())) { + Mail::to($notifiable)->cc($ccEmails)->send($mailable); + Log::info('Sending email, Locale: ' . ($event->checkedOutTo->locale ?? 'default')); + } + } + +// Send Webhook notification + if ($this->shouldSendWebhookNotification()) { Notification::route(Setting::getSettings()->webhook_selected, Setting::getSettings()->webhook_endpoint) ->notify($this->getCheckoutNotification($event, $acceptance)); } - } } catch (ClientException $e) { Log::debug("Exception caught during checkout notification: " . $e->getMessage()); } catch (Exception $e) { @@ -112,39 +119,41 @@ class CheckoutableListener } } } + $settings = Setting::getSettings(); + $adminCcEmailsArray = []; + + if($settings->admin_cc_email !== '') { + $adminCcEmail = $settings->admin_cc_email; + $adminCcEmailsArray = array_map('trim', explode(',', $adminCcEmail)); + } + $ccEmails = array_filter($adminCcEmailsArray); + $notifiable = $event->checkedOutTo; + $mailable = $this->getCheckinMailType($event); - $notifiables = $this->getNotifiables($event); // Send email notifications try { -// foreach ($notifiables as $notifiable) { -// if ($notifiable instanceof User && $notifiable->email != '') { -// if (! $event->checkedOutTo->locale){ -// Notification::locale(Setting::getSettings()->locale)->send($notifiable, $this->getCheckoutNotification($event, $acceptance)); -// } -// else { -// Notification::send($notifiable, $this->getCheckinNotification($event)); -// } -// } -// } + if (!$event->checkedOutTo->locale){ + $mailable->locale($event->checkedOutTo->locale); + } + /** + * Send an email if any of the following conditions are met: + * 1. The asset requires acceptance + * 2. The item has a EULA + * 3. The item should send an email at check-in/check-out + */ + + if ($notifiable instanceof User && $notifiable->email != '') { + if ($event->checkoutable->requireAcceptance() || $event->checkoutable->getEula() || + (method_exists($event->checkoutable, 'checkin_email') && $event->checkoutable->checkin_email())) { + Mail::to($notifiable)->cc($ccEmails)->send($mailable); + Log::info('Sending email, Locale: ' . $event->checkedOutTo->locale); + } + } // Send Webhook notification if ($this->shouldSendWebhookNotification()) { - // Slack doesn't include the URL in its messaging format, so this is needed to hit the endpoint - if (Setting::getSettings()->webhook_selected === 'slack' || Setting::getSettings()->webhook_selected === 'general') { - Notification::route('slack', Setting::getSettings()->webhook_endpoint) - ->notify($this->getCheckinNotification($event)); - } // Handling Microsoft Teams notification - else if (Setting::getSettings()->webhook_selected === 'microsoft') { - - $message = $this->getCheckinNotification($event)->toMicrosoftTeams(); - $notification = new TeamsNotification(Setting::getSettings()->webhook_endpoint); - $notification->success()->sendMessage($message[0], $message[1]); // Send the message to Microsoft Teams - } - else { Notification::route(Setting::getSettings()->webhook_selected, Setting::getSettings()->webhook_endpoint) ->notify($this->getCheckinNotification($event)); } - } - } catch (ClientException $e) { Log::warning("Exception caught during checkout notification: " . $e->getMessage()); } catch (Exception $e) { @@ -175,33 +184,6 @@ class CheckoutableListener return $acceptance; } - /** - * Gets the entities to be notified of the passed event - * - * @param Event $event - * @return Collection - */ - private function getNotifiables($event) - { - $notifiables = collect(); - - /** - * Notify who checked out the item as long as the model can route notifications - */ - if (method_exists($event->checkedOutTo, 'routeNotificationFor')) { - $notifiables->push($event->checkedOutTo); - } - - /** - * Notify Admin users if the settings is activated - */ - if ((Setting::getSettings()) && (Setting::getSettings()->admin_cc_email != '')) { - $notifiables->push(new AdminRecipient()); - } - - return $notifiables; - } - /** * Get the appropriate notification for the event * @@ -250,7 +232,7 @@ class CheckoutableListener break; case Consumable::class: $notificationClass = CheckoutConsumableNotification::class; - break; + break; case LicenseSeat::class: $notificationClass = CheckoutLicenseSeatNotification::class; break; @@ -259,6 +241,30 @@ class CheckoutableListener return new $notificationClass($event->checkoutable, $event->checkedOutTo, $event->checkedOutBy, $acceptance, $event->note); } + private function getCheckoutMailType($event, $acceptance){ + $lookup = [ + Accessory::class => CheckoutAccessoryMail::class, + Asset::class => CheckoutAssetMail::class, + LicenseSeat::class => CheckoutLicenseMail::class, + Consumable::class => CheckoutConsumableMail::class, + ]; + $mailable= $lookup[get_class($event->checkoutable)]; + + return new $mailable($event->checkoutable, $event->checkedOutTo, $event->checkedOutBy, $event->note, $acceptance); + + } + private function getCheckinMailType($event){ + $lookup = [ + Accessory::class => CheckinAccessoryMail::class, + Asset::class => CheckinAssetMail::class, + LicenseSeat::class => CheckinLicenseMail::class, + ]; + + $mailable= $lookup[get_class($event->checkoutable)]; + + return new $mailable($event->checkoutable, $event->checkedOutTo, $event->checkedInBy, $event->note); + + } /** * Register the listeners for the subscriber. diff --git a/app/Mail/CheckinAccessoryMail.php b/app/Mail/CheckinAccessoryMail.php new file mode 100644 index 0000000000..fc8d1455f9 --- /dev/null +++ b/app/Mail/CheckinAccessoryMail.php @@ -0,0 +1,70 @@ +item = $accessory; + $this->target = $checkedOutTo; + $this->admin = $checkedInby; + $this->note = $note; + $this->settings = Setting::getSettings(); + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + $from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io')); + + return new Envelope( + from: $from, + subject: trans('mail.Accessory_Checkin_Notification'), + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + return new Content( + markdown: 'mail.markdown.checkin-accessory', + with: [ + 'item' => $this->item, + 'admin' => $this->admin, + 'note' => $this->note, + 'target' => $this->target, + ] + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/CheckinAssetMail.php b/app/Mail/CheckinAssetMail.php new file mode 100644 index 0000000000..5dd8394a5a --- /dev/null +++ b/app/Mail/CheckinAssetMail.php @@ -0,0 +1,93 @@ +target = $checkedOutTo; + $this->item = $asset; + $this->admin = $checkedInBy; + $this->note = $note; + + $this->settings = Setting::getSettings(); + $this->expected_checkin = ''; + + if ($this->item->expected_checkin) { + $this->expected_checkin = Helper::getFormattedDateObject($this->item->expected_checkin, 'date', + false); + } + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + $from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io')); + + return new Envelope( + from: $from, + subject: trans('mail.Asset_Checkin_Notification'), + ); + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return Content + */ + public function content(): Content + { + $this->item->load('assetstatus'); + $fields = []; + + // Check if the item has custom fields associated with it + if (($this->item->model) && ($this->item->model->fieldset)) { + $fields = $this->item->model->fieldset->fields; + } + + return new Content( + markdown: 'mail.markdown.checkin-asset', + with: [ + 'item' => $this->item, + 'status' => $this->item->assetstatus?->name, + 'admin' => $this->admin, + 'note' => $this->note, + 'target' => $this->target, + 'fields' => $fields, + 'expected_checkin' => $this->expected_checkin, + ], + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/CheckinLicenseMail.php b/app/Mail/CheckinLicenseMail.php new file mode 100644 index 0000000000..97ce4dd3c3 --- /dev/null +++ b/app/Mail/CheckinLicenseMail.php @@ -0,0 +1,70 @@ +target = $checkedOutTo; + $this->item = $licenseSeat->license; + $this->admin = $checkedInBy; + $this->note = $note; + $this->settings = Setting::getSettings(); + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + $from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io')); + + return new Envelope( + from: $from, + subject: trans('mail.License_Checkin_Notification'), + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + return new Content( + markdown: 'mail.markdown.checkin-license', + with: [ + 'item' => $this->item, + 'admin' => $this->admin, + 'note' => $this->note, + 'target' => $this->target, + ] + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/CheckoutAccessoryMail.php b/app/Mail/CheckoutAccessoryMail.php new file mode 100644 index 0000000000..f297c02754 --- /dev/null +++ b/app/Mail/CheckoutAccessoryMail.php @@ -0,0 +1,82 @@ +item = $accessory; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->checkout_qty = $accessory->checkout_qty; + $this->target = $checkedOutTo; + $this->acceptance = $acceptance; + $this->settings = Setting::getSettings(); + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + $from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io')); + + return new Envelope( + from: $from, + subject: (trans('mail.Accessory_Checkout_Notification')), + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + Log::debug($this->item->getImageUrl()); + $eula = $this->item->getEula(); + $req_accept = $this->item->requireAcceptance(); + $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); + + return new Content( + markdown: 'mail.markdown.checkout-accessory', + with: [ + 'item' => $this->item, + 'admin' => $this->admin, + 'note' => $this->note, + 'target' => $this->target, + 'eula' => $eula, + 'req_accept' => $req_accept, + 'accept_url' => $accept_url, + 'checkout_qty' => $this->checkout_qty, + ], + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/CheckoutAssetMail.php b/app/Mail/CheckoutAssetMail.php new file mode 100644 index 0000000000..fa1290e92d --- /dev/null +++ b/app/Mail/CheckoutAssetMail.php @@ -0,0 +1,110 @@ +item = $asset; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + $this->acceptance = $acceptance; + + $this->settings = Setting::getSettings(); + + $this->last_checkout = ''; + $this->expected_checkin = ''; + + if ($this->item->last_checkout) { + $this->last_checkout = Helper::getFormattedDateObject($this->item->last_checkout, 'date', + false); + } + + if ($this->item->expected_checkin) { + $this->expected_checkin = Helper::getFormattedDateObject($this->item->expected_checkin, 'date', + false); + } + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + $from = new Address(env('MAIL_FROM_ADDR', 'service@snipe-it.io')); + + return new Envelope( + from: $from, + subject: trans('mail.Asset_Checkout_Notification'), + ); + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return Content + */ + public function content(): Content + { + $this->item->load('assetstatus'); + $eula = method_exists($this->item, 'getEula') ? $this->item->getEula() : ''; + $req_accept = method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0; + $fields = []; + + // Check if the item has custom fields associated with it + if (($this->item->model) && ($this->item->model->fieldset)) { + $fields = $this->item->model->fieldset->fields; + } + + $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); + + return new Content( + markdown: 'mail.markdown.checkout-asset', + with: [ + 'item' => $this->item, + 'admin' => $this->admin, + 'status' => $this->item->assetstatus?->name, + 'note' => $this->note, + 'target' => $this->target, + 'fields' => $fields, + 'eula' => $eula, + 'req_accept' => $req_accept, + 'accept_url' => $accept_url, + 'last_checkout' => $this->last_checkout, + 'expected_checkin' => $this->expected_checkin, + ], + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/CheckoutConsumableMail.php b/app/Mail/CheckoutConsumableMail.php new file mode 100644 index 0000000000..18fe228258 --- /dev/null +++ b/app/Mail/CheckoutConsumableMail.php @@ -0,0 +1,84 @@ +item = $consumable; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + $this->acceptance = $acceptance; + $this->qty = $consumable->checkout_qty; + + $this->settings = Setting::getSettings(); + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + $from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io')); + + return new Envelope( + from: $from, + subject: trans('mail.Confirm_consumable_delivery'), + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + + $eula = $this->item->getEula(); + $req_accept = $this->item->requireAcceptance(); + + $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); + + return new Content( + markdown: 'mail.markdown.checkout-consumable', + with: [ + 'item' => $this->item, + 'admin' => $this->admin, + 'note' => $this->note, + 'target' => $this->target, + 'eula' => $eula, + 'req_accept' => $req_accept, + 'accept_url' => $accept_url, + 'qty' => $this->qty, + ] + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/CheckoutLicenseMail.php b/app/Mail/CheckoutLicenseMail.php new file mode 100644 index 0000000000..ecd0d7390d --- /dev/null +++ b/app/Mail/CheckoutLicenseMail.php @@ -0,0 +1,80 @@ +item = $licenseSeat->license; + $this->admin = $checkedOutBy; + $this->note = $note; + $this->target = $checkedOutTo; + $this->acceptance = $acceptance; + + $this->settings = Setting::getSettings(); + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + $from = new Address(env('MAIL_FROM_ADDR','service@snipe-it.io')); + + return new Envelope( + from: $from, + subject: trans('mail.Confirm_license_delivery'), + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + $eula = method_exists($this->item, 'getEula') ? $this->item->getEula() : ''; + $req_accept = method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0; + + $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); + + return new Content( + markdown: 'mail.markdown.checkout-license', + with: [ + 'item' => $this->item, + 'admin' => $this->admin, + 'note' => $this->note, + 'target' => $this->target, + 'eula' => $eula, + 'req_accept' => $req_accept, + 'accept_url' => $accept_url, + ] + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Models/Actionlog.php b/app/Models/Actionlog.php index 0831352b87..008c5b1146 100755 --- a/app/Models/Actionlog.php +++ b/app/Models/Actionlog.php @@ -55,6 +55,8 @@ class Actionlog extends SnipeModel 'created_by', 'remote_ip', 'user_agent', + 'item_type', + 'target_type', 'action_source' ]; @@ -64,10 +66,10 @@ class Actionlog extends SnipeModel * @var array */ protected $searchableRelations = [ - 'company' => ['name'], - 'adminuser' => ['first_name','last_name','username', 'email'], - 'user' => ['first_name','last_name','username', 'email'], - 'assets' => ['asset_tag','name'], + 'company' => ['name'], + 'adminuser' => ['first_name','last_name','username', 'email'], + 'user' => ['first_name','last_name','username', 'email'], + 'assets' => ['asset_tag','name'], ]; /** diff --git a/app/Models/Recipients/AdminRecipient.php b/app/Models/Recipients/AdminRecipient.php index 433bd00209..90e39d4ee5 100644 --- a/app/Models/Recipients/AdminRecipient.php +++ b/app/Models/Recipients/AdminRecipient.php @@ -6,9 +6,15 @@ use App\Models\Setting; class AdminRecipient extends Recipient { + + protected $email; public function __construct() { $settings = Setting::getSettings(); $this->email = trim($settings->admin_cc_email); } + + public function getEmail(){ + return $this->email; + } } diff --git a/app/Notifications/CheckinAccessoryNotification.php b/app/Notifications/CheckinAccessoryNotification.php index 92c3574eb7..28e6c054f7 100644 --- a/app/Notifications/CheckinAccessoryNotification.php +++ b/app/Notifications/CheckinAccessoryNotification.php @@ -6,6 +6,7 @@ use App\Models\Accessory; use App\Models\Setting; use App\Models\User; use Illuminate\Bus\Queueable; +use Illuminate\Notifications\Channels\SlackWebhookChannel; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; @@ -56,22 +57,9 @@ class CheckinAccessoryNotification extends Notification } if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) { - $notifyBy[] = 'slack'; + $notifyBy[] = SlackWebhookChannel::class; } - /** - * Only send notifications to users that have email addresses - */ - if ($this->target instanceof User && $this->target->email != '') { - Log::debug('The target is a user'); - - if ($this->item->checkin_email()) { - $notifyBy[] = 'mail'; - } - } - - Log::debug('checkin_email on this category is '.$this->item->checkin_email()); - return $notifyBy; } @@ -154,24 +142,4 @@ class CheckinAccessoryNotification extends Notification ); } - - /** - * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage - */ - public function toMail() - { - Log::debug('to email called'); - - return (new MailMessage)->markdown('notifications.markdown.checkin-accessory', - [ - 'item' => $this->item, - 'admin' => $this->admin, - 'note' => $this->note, - 'target' => $this->target, - ]) - ->subject(trans('mail.Accessory_Checkin_Notification')); - } } diff --git a/app/Notifications/CheckinAssetNotification.php b/app/Notifications/CheckinAssetNotification.php index 1cde77c7e3..75077aeb46 100644 --- a/app/Notifications/CheckinAssetNotification.php +++ b/app/Notifications/CheckinAssetNotification.php @@ -51,7 +51,6 @@ class CheckinAssetNotification extends Notification */ public function via() { - $notifyBy = []; if (Setting::getSettings()->webhook_selected == 'google' && Setting::getSettings()->webhook_endpoint) { $notifyBy[] = GoogleChatChannel::class; @@ -66,14 +65,6 @@ class CheckinAssetNotification extends Notification $notifyBy[] = 'slack'; } - /** - * Only send checkin notifications to users if the category - * has the corresponding checkbox checked. - */ - if ($this->item->checkin_email() && $this->target instanceof User && $this->target->email != '') { - $notifyBy[] = 'mail'; - } - return $notifyBy; } @@ -157,35 +148,5 @@ class CheckinAssetNotification extends Notification ) ) ); - - } - - /** - * Get the mail representation of the notification. - * - * @return \Illuminate\Notifications\Messages\MailMessage - */ - public function toMail() - { - $fields = []; - - // Check if the item has custom fields associated with it - if (($this->item->model) && ($this->item->model->fieldset)) { - $fields = $this->item->model->fieldset->fields; - } - - $message = (new MailMessage)->markdown('notifications.markdown.checkin-asset', - [ - 'item' => $this->item, - 'status' => $this->item->assetstatus?->name, - 'admin' => $this->admin, - 'note' => $this->note, - 'target' => $this->target, - 'fields' => $fields, - 'expected_checkin' => $this->expected_checkin, - ]) - ->subject(trans('mail.Asset_Checkin_Notification')); - - return $message; } } diff --git a/app/Notifications/CheckinLicenseSeatNotification.php b/app/Notifications/CheckinLicenseSeatNotification.php index 825777fb3d..1cb8706e67 100644 --- a/app/Notifications/CheckinLicenseSeatNotification.php +++ b/app/Notifications/CheckinLicenseSeatNotification.php @@ -6,6 +6,7 @@ use App\Models\LicenseSeat; use App\Models\Setting; use App\Models\User; use Illuminate\Bus\Queueable; +use Illuminate\Notifications\Channels\SlackWebhookChannel; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; @@ -59,15 +60,7 @@ class CheckinLicenseSeatNotification extends Notification } if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) { - $notifyBy[] = 'slack'; - } - - /** - * Only send checkin notifications to users if the category - * has the corresponding checkbox checked. - */ - if ($this->item->checkin_email() && $this->target instanceof User && $this->target->email != '') { - $notifyBy[] = 'mail'; + $notifyBy[] = SlackWebhookChannel::class; } return $notifyBy; @@ -162,23 +155,4 @@ class CheckinLicenseSeatNotification extends Notification ); } - - - /** - * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage - */ - public function toMail() - { - return (new MailMessage)->markdown('notifications.markdown.checkin-license', - [ - 'item' => $this->item, - 'admin' => $this->admin, - 'note' => $this->note, - 'target' => $this->target, - ]) - ->subject(trans('mail.License_Checkin_Notification')); - } } diff --git a/app/Notifications/CheckoutAssetNotification.php b/app/Notifications/CheckoutAssetNotification.php index 771251ab45..83011e5c8e 100644 --- a/app/Notifications/CheckoutAssetNotification.php +++ b/app/Notifications/CheckoutAssetNotification.php @@ -8,7 +8,7 @@ use App\Models\Setting; use App\Models\User; use Exception; use Illuminate\Bus\Queueable; -use Illuminate\Notifications\Messages\MailMessage; +use Illuminate\Notifications\Channels\SlackWebhookChannel; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Str; @@ -36,14 +36,11 @@ class CheckoutAssetNotification extends Notification */ public function __construct(Asset $asset, $checkedOutTo, User $checkedOutBy, $acceptance, $note) { + $this->settings = Setting::getSettings(); $this->item = $asset; $this->admin = $checkedOutBy; $this->note = $note; $this->target = $checkedOutTo; - $this->acceptance = $acceptance; - - $this->settings = Setting::getSettings(); - $this->last_checkout = ''; $this->expected_checkin = ''; @@ -57,7 +54,6 @@ class CheckoutAssetNotification extends Notification false); } } - /** * Get the notification's delivery channels. * @@ -66,61 +62,34 @@ class CheckoutAssetNotification extends Notification public function via() { $notifyBy = []; - if (Setting::getSettings()->webhook_selected == 'google' && Setting::getSettings()->webhook_endpoint) { + + if (Setting::getSettings()->webhook_selected === 'google' && Setting::getSettings()->webhook_endpoint) { $notifyBy[] = GoogleChatChannel::class; } - if (Setting::getSettings()->webhook_selected == 'microsoft' && Setting::getSettings()->webhook_endpoint) { + if (Setting::getSettings()->webhook_selected === 'microsoft' && Setting::getSettings()->webhook_endpoint) { $notifyBy[] = TeamsNotification::class; } - if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) { + if (Setting::getSettings()->webhook_selected === 'slack' || Setting::getSettings()->webhook_selected === 'general' ) { Log::debug('use webhook'); - $notifyBy[] = 'slack'; - } - - /** - * Only send notifications to users that have email addresses - */ - if ($this->target instanceof User && $this->target->email != '') { - - /** - * Send an email if the asset requires acceptance, - * so the user can accept or decline the asset - */ - if ($this->item->requireAcceptance()) { - $notifyBy[1] = 'mail'; - } - - /** - * Send an email if the item has a EULA, since the user should always receive it - */ - if ($this->item->getEula()) { - $notifyBy[1] = 'mail'; - } - - /** - * Send an email if an email should be sent at checkin/checkout - */ - if ($this->item->checkin_email()) { - $notifyBy[1] = 'mail'; - } + $notifyBy[] = SlackWebhookChannel::class; } return $notifyBy; } - public function toSlack() + public function toSlack() :SlackMessage { $target = $this->target; $admin = $this->admin; $item = $this->item; $note = $this->note; - $botname = ($this->settings->webhook_botname) ? $this->settings->webhook_botname : 'Snipe-Bot'; + $botname = ($this->settings->webhook_botname) ?: 'Snipe-Bot'; $channel = ($this->settings->webhook_channel) ? $this->settings->webhook_channel : ''; $fields = [ @@ -128,7 +97,7 @@ class CheckoutAssetNotification extends Notification 'By' => '<'.$admin->present()->viewUrl().'|'.$admin->present()->fullName().'>', ]; - if (($this->expected_checkin) && ($this->expected_checkin != '')) { + if (($this->expected_checkin) && ($this->expected_checkin !== '')) { $fields['Expected Checkin'] = $this->expected_checkin; } @@ -197,42 +166,4 @@ public function toGoogleChat() ); } - - /** - * Get the mail representation of the notification. - * - * @param mixed $notifiable - * @return \Illuminate\Notifications\Messages\MailMessage - */ - public function toMail() - { $this->item->load('assetstatus'); - $eula = method_exists($this->item, 'getEula') ? $this->item->getEula() : ''; - $req_accept = method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0; - $fields = []; - - // Check if the item has custom fields associated with it - if (($this->item->model) && ($this->item->model->fieldset)) { - $fields = $this->item->model->fieldset->fields; - } - - $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); - - $message = (new MailMessage)->markdown('notifications.markdown.checkout-asset', - [ - 'item' => $this->item, - 'admin' => $this->admin, - 'status' => $this->item->assetstatus?->name, - 'note' => $this->note, - 'target' => $this->target, - 'fields' => $fields, - 'eula' => $eula, - 'req_accept' => $req_accept, - 'accept_url' => $accept_url, - 'last_checkout' => $this->last_checkout, - 'expected_checkin' => $this->expected_checkin, - ]) - ->subject(trans('mail.Confirm_asset_delivery')); - - return $message; - } } diff --git a/app/Notifications/CheckoutConsumableNotification.php b/app/Notifications/CheckoutConsumableNotification.php index 7113ba5131..ba7c5646ab 100644 --- a/app/Notifications/CheckoutConsumableNotification.php +++ b/app/Notifications/CheckoutConsumableNotification.php @@ -6,6 +6,7 @@ use App\Models\Consumable; use App\Models\Setting; use App\Models\User; use Illuminate\Bus\Queueable; +use Illuminate\Notifications\Channels\SlackWebhookChannel; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; @@ -63,35 +64,7 @@ class CheckoutConsumableNotification extends Notification } if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) { - $notifyBy[] = 'slack'; - } - - /** - * Only send notifications to users that have email addresses - */ - if ($this->target instanceof User && $this->target->email != '') { - - /** - * Send an email if the asset requires acceptance, - * so the user can accept or decline the asset - */ - if ($this->item->requireAcceptance()) { - $notifyBy[1] = 'mail'; - } - - /** - * Send an email if the item has a EULA, since the user should always receive it - */ - if ($this->item->getEula()) { - $notifyBy[1] = 'mail'; - } - - /** - * Send an email if an email should be sent at checkin/checkout - */ - if ((method_exists($this->item, 'checkin_email')) && ($this->item->checkin_email())) { - $notifyBy[1] = 'mail'; - } + $notifyBy[] = SlackWebhookChannel::class; } return $notifyBy; @@ -180,30 +153,4 @@ class CheckoutConsumableNotification extends Notification ); } - - /** - * Get the mail representation of the notification. - * - * @return \Illuminate\Notifications\Messages\MailMessage - */ - public function toMail() - { - $eula = $this->item->getEula(); - $req_accept = $this->item->requireAcceptance(); - - $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); - - return (new MailMessage)->markdown('notifications.markdown.checkout-consumable', - [ - 'item' => $this->item, - 'admin' => $this->admin, - 'note' => $this->note, - 'target' => $this->target, - 'eula' => $eula, - 'req_accept' => $req_accept, - 'accept_url' => $accept_url, - 'qty' => $this->qty, - ]) - ->subject(trans('mail.Confirm_consumable_delivery')); - } } diff --git a/app/Notifications/CheckoutLicenseSeatNotification.php b/app/Notifications/CheckoutLicenseSeatNotification.php index f59c4416e9..1aed0d2004 100644 --- a/app/Notifications/CheckoutLicenseSeatNotification.php +++ b/app/Notifications/CheckoutLicenseSeatNotification.php @@ -6,6 +6,7 @@ use App\Models\LicenseSeat; use App\Models\Setting; use App\Models\User; use Illuminate\Bus\Queueable; +use Illuminate\Notifications\Channels\SlackWebhookChannel; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; @@ -61,35 +62,7 @@ class CheckoutLicenseSeatNotification extends Notification } if (Setting::getSettings()->webhook_selected == 'slack' || Setting::getSettings()->webhook_selected == 'general' ) { - $notifyBy[] = 'slack'; - } - - /** - * Only send notifications to users that have email addresses - */ - if ($this->target instanceof User && $this->target->email != '') { - - /** - * Send an email if the asset requires acceptance, - * so the user can accept or decline the asset - */ - if ($this->item->requireAcceptance()) { - $notifyBy[1] = 'mail'; - } - - /** - * Send an email if the item has a EULA, since the user should always receive it - */ - if ($this->item->getEula()) { - $notifyBy[1] = 'mail'; - } - - /** - * Send an email if an email should be sent at checkin/checkout - */ - if ($this->item->checkin_email()) { - $notifyBy[1] = 'mail'; - } + $notifyBy[] = SlackWebhookChannel::class; } return $notifyBy; @@ -177,29 +150,4 @@ class CheckoutLicenseSeatNotification extends Notification ); } - - /** - * Get the mail representation of the notification. - * - * @return \Illuminate\Notifications\Messages\MailMessage - */ - public function toMail() - { - $eula = method_exists($this->item, 'getEula') ? $this->item->getEula() : ''; - $req_accept = method_exists($this->item, 'requireAcceptance') ? $this->item->requireAcceptance() : 0; - - $accept_url = is_null($this->acceptance) ? null : route('account.accept.item', $this->acceptance); - - return (new MailMessage)->markdown('notifications.markdown.checkout-license', - [ - 'item' => $this->item, - 'admin' => $this->admin, - 'note' => $this->note, - 'target' => $this->target, - 'eula' => $eula, - 'req_accept' => $req_accept, - 'accept_url' => $accept_url, - ]) - ->subject(trans('mail.Confirm_license_delivery')); - } } diff --git a/app/Presenters/ActionlogPresenter.php b/app/Presenters/ActionlogPresenter.php index 37a1adbc28..9251ce6347 100644 --- a/app/Presenters/ActionlogPresenter.php +++ b/app/Presenters/ActionlogPresenter.php @@ -42,27 +42,27 @@ class ActionlogPresenter extends Presenter // User related icons if ($this->itemType() == 'user') { - if ($this->actionType()=='2fa reset') { + if ($this->action_type == '2fa reset') { return 'fa-solid fa-mobile-screen'; } - if ($this->actionType()=='create new') { + if ($this->action_type == 'create new') { return 'fa-solid fa-user-plus'; } - if ($this->actionType()=='merged') { + if ($this->action_type == 'merged') { return 'fa-solid fa-people-arrows'; } - if ($this->actionType()=='delete') { + if ($this->action_type == 'delete') { return 'fa-solid fa-user-minus'; } - if ($this->actionType()=='delete') { + if ($this->action_type == 'delete') { return 'fa-solid fa-user-minus'; } - if ($this->actionType()=='update') { + if ($this->action_type == 'update') { return 'fa-solid fa-user-pen'; } @@ -70,31 +70,31 @@ class ActionlogPresenter extends Presenter } // Everything else - if ($this->actionType()=='create new') { + if ($this->action_type == 'create new') { return 'fa-solid fa-plus'; } - if ($this->actionType()=='delete') { + if ($this->action_type == 'delete') { return 'fa-solid fa-trash'; } - if ($this->actionType()=='update') { + if ($this->action_type == 'update') { return 'fa-solid fa-pen'; } - if ($this->actionType()=='restore') { + if ($this->action_type == 'restore') { return 'fa-solid fa-trash-arrow-up'; } - if ($this->actionType()=='upload') { + if ($this->action_type == 'upload') { return 'fas fa-paperclip'; } - if ($this->actionType()=='checkout') { + if ($this->action_type == 'checkout') { return 'fa-solid fa-rotate-left'; } - if ($this->actionType()=='checkin from') { + if ($this->action_type == 'checkin from') { return 'fa-solid fa-rotate-right'; } diff --git a/composer.lock b/composer.lock index b04d7a9569..cdc86c22b4 100644 --- a/composer.lock +++ b/composer.lock @@ -4461,16 +4461,16 @@ }, { "name": "livewire/livewire", - "version": "v3.5.9", + "version": "v3.5.12", "source": { "type": "git", "url": "https://github.com/livewire/livewire.git", - "reference": "d04a229058afa76116d0e39209943a8ea3a7f888" + "reference": "3c8d1f9d7d9098aaea663093ae168f2d5d2ae73d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/livewire/zipball/d04a229058afa76116d0e39209943a8ea3a7f888", - "reference": "d04a229058afa76116d0e39209943a8ea3a7f888", + "url": "https://api.github.com/repos/livewire/livewire/zipball/3c8d1f9d7d9098aaea663093ae168f2d5d2ae73d", + "reference": "3c8d1f9d7d9098aaea663093ae168f2d5d2ae73d", "shasum": "" }, "require": { @@ -4525,7 +4525,7 @@ "description": "A front-end framework for Laravel.", "support": { "issues": "https://github.com/livewire/livewire/issues", - "source": "https://github.com/livewire/livewire/tree/v3.5.9" + "source": "https://github.com/livewire/livewire/tree/v3.5.12" }, "funding": [ { @@ -4533,7 +4533,7 @@ "type": "github" } ], - "time": "2024-10-01T12:40:06+00:00" + "time": "2024-10-15T19:35:06+00:00" }, { "name": "masterminds/html5", diff --git a/database/factories/ComponentFactory.php b/database/factories/ComponentFactory.php index fcec2dac87..786e780895 100644 --- a/database/factories/ComponentFactory.php +++ b/database/factories/ComponentFactory.php @@ -31,6 +31,7 @@ class ComponentFactory extends Factory */ public function definition() { + return [ 'name' => $this->faker->text(20), 'category_id' => Category::factory(), @@ -42,7 +43,6 @@ class ComponentFactory extends Factory 'purchase_cost' => $this->faker->randomFloat(2), 'min_amt' => $this->faker->numberBetween($min = 1, $max = 2), 'company_id' => Company::factory(), - 'manufacturer_id' => Manufacturer::factory(), 'supplier_id' => Supplier::factory(), 'model_number' => $this->faker->numberBetween(1000000, 50000000), ]; @@ -50,7 +50,8 @@ class ComponentFactory extends Factory public function ramCrucial4() { - return $this->state(function () { + $manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']); + return $this->state(function () use ($manufacturer) { return [ 'name' => 'Crucial 4GB DDR3L-1600 SODIMM', 'category_id' => function () { @@ -58,6 +59,7 @@ class ComponentFactory extends Factory }, 'qty' => 10, 'min_amt' => 2, + 'manufacturer_id' => $manufacturer->id, 'location_id' => Location::factory(), ]; }); @@ -65,7 +67,8 @@ class ComponentFactory extends Factory public function ramCrucial8() { - return $this->state(function () { + $manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']); + return $this->state(function () use ($manufacturer) { return [ 'name' => 'Crucial 8GB DDR3L-1600 SODIMM Memory for Mac', 'category_id' => function () { @@ -73,13 +76,15 @@ class ComponentFactory extends Factory }, 'qty' => 10, 'min_amt' => 2, + 'manufacturer_id' => $manufacturer->id, ]; }); } public function ssdCrucial120() { - return $this->state(function () { + $manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']); + return $this->state(function () use ($manufacturer) { return [ 'name' => 'Crucial BX300 120GB SATA Internal SSD', 'category_id' => function () { @@ -87,13 +92,15 @@ class ComponentFactory extends Factory }, 'qty' => 10, 'min_amt' => 2, + 'manufacturer_id' => $manufacturer->id, ]; }); } public function ssdCrucial240() { - return $this->state(function () { + $manufacturer = Manufacturer::where('name', 'Crucial')->first() ?? Manufacturer::factory()->create(['name' => 'Crucial']); + return $this->state(function () use ($manufacturer) { return [ 'name' => 'Crucial BX300 240GB SATA Internal SSD', 'category_id' => function () { @@ -101,6 +108,7 @@ class ComponentFactory extends Factory }, 'qty' => 10, 'min_amt' => 2, + 'manufacturer_id' => $manufacturer->id, ]; }); } diff --git a/public/vendor/livewire/livewire.esm.js b/public/vendor/livewire/livewire.esm.js index ae4bb2cb78..2d7ef4791c 100644 --- a/public/vendor/livewire/livewire.esm.js +++ b/public/vendor/livewire/livewire.esm.js @@ -2557,7 +2557,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); } } function bindInputValue(el, value) { - if (el.type === "radio") { + if (isRadio(el)) { if (el.attributes.value === void 0) { el.value = value; } @@ -2568,7 +2568,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); el.checked = checkedAttrLooseCompare(el.value, value); } } - } else if (el.type === "checkbox") { + } else if (isCheckbox(el)) { if (Number.isInteger(value)) { el.value = value; } else if (!Array.isArray(value) && typeof value !== "boolean" && ![null, void 0].includes(value)) { @@ -2707,6 +2707,12 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); } return attr; } + function isCheckbox(el) { + return el.type === "checkbox" || el.localName === "ui-checkbox" || el.localName === "ui-switch"; + } + function isRadio(el) { + return el.type === "radio" || el.localName === "ui-radio"; + } function debounce2(func, wait) { var timeout; return function() { @@ -2860,7 +2866,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); get raw() { return raw; }, - version: "3.14.1", + version: "3.14.3", flushAndStopDeferringMutations, dontAutoEvaluateFunctions, disableEffectScheduling, @@ -3296,7 +3302,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); setValue(getInputValue(el, modifiers, e, getValue())); }); if (modifiers.includes("fill")) { - if ([void 0, null, ""].includes(getValue()) || el.type === "checkbox" && Array.isArray(getValue()) || el.tagName.toLowerCase() === "select" && el.multiple) { + if ([void 0, null, ""].includes(getValue()) || isCheckbox(el) && Array.isArray(getValue()) || el.tagName.toLowerCase() === "select" && el.multiple) { setValue(getInputValue(el, modifiers, { target: el }, getValue())); } } @@ -3336,7 +3342,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); return mutateDom(() => { if (event instanceof CustomEvent && event.detail !== void 0) return event.detail !== null && event.detail !== void 0 ? event.detail : event.target.value; - else if (el.type === "checkbox") { + else if (isCheckbox(el)) { if (Array.isArray(currentValue)) { let newValue = null; if (modifiers.includes("number")) { @@ -3367,7 +3373,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); }); } else { let newValue; - if (el.type === "radio") { + if (isRadio(el)) { if (event.target.checked) { newValue = event.target.value; } else { @@ -6820,8 +6826,6 @@ var require_module_cjs8 = __commonJS({ let toAttributes = Array.from(to.attributes); for (let i = domAttributes.length - 1; i >= 0; i--) { let name = domAttributes[i].name; - if (name === "style") - continue; if (!to.hasAttribute(name)) { from2.removeAttribute(name); } @@ -6829,8 +6833,6 @@ var require_module_cjs8 = __commonJS({ for (let i = toAttributes.length - 1; i >= 0; i--) { let name = toAttributes[i].name; let value = toAttributes[i].value; - if (name === "style") - continue; if (from2.getAttribute(name) !== value) { from2.setAttribute(name, value); } @@ -9015,6 +9017,44 @@ function injectStyles() { document.head.appendChild(style); } +// js/plugins/navigate/popover.js +function packUpPersistedPopovers(persistedEl) { + persistedEl.querySelectorAll(":popover-open").forEach((el) => { + el.setAttribute("data-navigate-popover-open", ""); + let animations = el.getAnimations(); + el._pausedAnimations = animations.map((animation) => ({ + keyframes: animation.effect.getKeyframes(), + options: { + duration: animation.effect.getTiming().duration, + easing: animation.effect.getTiming().easing, + fill: animation.effect.getTiming().fill, + iterations: animation.effect.getTiming().iterations + }, + currentTime: animation.currentTime, + playState: animation.playState + })); + animations.forEach((i) => i.pause()); + }); +} +function unPackPersistedPopovers(persistedEl) { + persistedEl.querySelectorAll("[data-navigate-popover-open]").forEach((el) => { + el.removeAttribute("data-navigate-popover-open"); + queueMicrotask(() => { + if (!el.isConnected) + return; + el.showPopover(); + el.getAnimations().forEach((i) => i.finish()); + if (el._pausedAnimations) { + el._pausedAnimations.forEach(({ keyframes, options, currentTime, now, playState }) => { + let animation = el.animate(keyframes, options); + animation.currentTime = currentTime; + }); + delete el._pausedAnimations; + } + }); + }); +} + // js/plugins/navigate/page.js var oldBodyScriptTagHashes = []; var attributesExemptFromScriptTagHashing = [ @@ -9153,7 +9193,7 @@ var autofocus = false; function navigate_default(Alpine19) { Alpine19.navigate = (url) => { let destination = createUrlObjectFromString(url); - let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", { + let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", { url: destination, history: false, cached: false @@ -9184,7 +9224,7 @@ function navigate_default(Alpine19) { storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination); }); whenItIsReleased(() => { - let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", { + let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", { url: destination, history: false, cached: false @@ -9198,7 +9238,7 @@ function navigate_default(Alpine19) { function navigateTo(destination, shouldPushToHistoryState = true) { showProgressBar && showAndStartProgressBar(); fetchHtmlOrUsePrefetchedHtml(destination, (html, finalDestination) => { - fireEventForOtherLibariesToHookInto("alpine:navigating"); + fireEventForOtherLibrariesToHookInto("alpine:navigating"); restoreScroll && storeScrollInformationInHtmlBeforeNavigatingAway(); showProgressBar && finishAndHideProgressBar(); cleanupAlpineElementsOnThePageThatArentInsideAPersistedElement(); @@ -9206,6 +9246,7 @@ function navigate_default(Alpine19) { preventAlpineFromPickingUpDomChanges(Alpine19, (andAfterAllThis) => { enablePersist && storePersistantElementsForLater((persistedEl) => { packUpPersistedTeleports(persistedEl); + packUpPersistedPopovers(persistedEl); }); if (shouldPushToHistoryState) { updateUrlAndStoreLatestHtmlForFutureBackButtons(html, finalDestination); @@ -9216,6 +9257,7 @@ function navigate_default(Alpine19) { removeAnyLeftOverStaleTeleportTargets(document.body); enablePersist && putPersistantElementsBack((persistedEl, newStub) => { unPackPersistedTeleports(persistedEl); + unPackPersistedPopovers(persistedEl); }); restoreScrollPositionOrScrollToTop(); afterNewScriptsAreDoneLoading(() => { @@ -9224,7 +9266,7 @@ function navigate_default(Alpine19) { autofocus && autofocusElementsWithTheAutofocusAttribute(); }); nowInitializeAlpineOnTheNewPage(Alpine19); - fireEventForOtherLibariesToHookInto("alpine:navigated"); + fireEventForOtherLibrariesToHookInto("alpine:navigated"); }); }); }); @@ -9234,7 +9276,7 @@ function navigate_default(Alpine19) { whenTheBackOrForwardButtonIsClicked((ifThePageBeingVisitedHasntBeenCached) => { ifThePageBeingVisitedHasntBeenCached((url) => { let destination = createUrlObjectFromString(url); - let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", { + let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", { url: destination, history: true, cached: false @@ -9246,7 +9288,7 @@ function navigate_default(Alpine19) { }); }, (html, url, currentPageUrl, currentPageKey) => { let destination = createUrlObjectFromString(url); - let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", { + let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", { url: destination, history: true, cached: true @@ -9254,29 +9296,31 @@ function navigate_default(Alpine19) { if (prevented) return; storeScrollInformationInHtmlBeforeNavigatingAway(); - fireEventForOtherLibariesToHookInto("alpine:navigating"); + fireEventForOtherLibrariesToHookInto("alpine:navigating"); updateCurrentPageHtmlInSnapshotCacheForLaterBackButtonClicks(currentPageUrl, currentPageKey); preventAlpineFromPickingUpDomChanges(Alpine19, (andAfterAllThis) => { enablePersist && storePersistantElementsForLater((persistedEl) => { packUpPersistedTeleports(persistedEl); + packUpPersistedPopovers(persistedEl); }); swapCurrentPageWithNewHtml(html, () => { removeAnyLeftOverStaleProgressBars(); removeAnyLeftOverStaleTeleportTargets(document.body); enablePersist && putPersistantElementsBack((persistedEl, newStub) => { unPackPersistedTeleports(persistedEl); + unPackPersistedPopovers(persistedEl); }); restoreScrollPositionOrScrollToTop(); andAfterAllThis(() => { autofocus && autofocusElementsWithTheAutofocusAttribute(); nowInitializeAlpineOnTheNewPage(Alpine19); - fireEventForOtherLibariesToHookInto("alpine:navigated"); + fireEventForOtherLibrariesToHookInto("alpine:navigated"); }); }); }); }); setTimeout(() => { - fireEventForOtherLibariesToHookInto("alpine:navigated"); + fireEventForOtherLibrariesToHookInto("alpine:navigated"); }); } function fetchHtmlOrUsePrefetchedHtml(fromDestination, callback) { @@ -9293,7 +9337,7 @@ function preventAlpineFromPickingUpDomChanges(Alpine19, callback) { }); }); } -function fireEventForOtherLibariesToHookInto(name, detail) { +function fireEventForOtherLibrariesToHookInto(name, detail) { let event = new CustomEvent(name, { cancelable: true, bubbles: true, @@ -9809,6 +9853,7 @@ function morph2(component, el, html) { }, lookahead: false }); + trigger("morphed", { el, component }); } function isntElement(el) { return typeof el.hasAttribute !== "function"; @@ -10878,3 +10923,4 @@ focus-trap/dist/focus-trap.js: * @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE *) */ +//# sourceMappingURL=livewire.esm.js.map diff --git a/public/vendor/livewire/livewire.js b/public/vendor/livewire/livewire.js index 687180f6b1..670591055f 100644 --- a/public/vendor/livewire/livewire.js +++ b/public/vendor/livewire/livewire.js @@ -1975,7 +1975,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); } } function bindInputValue(el, value) { - if (el.type === "radio") { + if (isRadio(el)) { if (el.attributes.value === void 0) { el.value = value; } @@ -1986,7 +1986,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); el.checked = checkedAttrLooseCompare(el.value, value); } } - } else if (el.type === "checkbox") { + } else if (isCheckbox(el)) { if (Number.isInteger(value)) { el.value = value; } else if (!Array.isArray(value) && typeof value !== "boolean" && ![null, void 0].includes(value)) { @@ -2125,6 +2125,12 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); } return attr; } + function isCheckbox(el) { + return el.type === "checkbox" || el.localName === "ui-checkbox" || el.localName === "ui-switch"; + } + function isRadio(el) { + return el.type === "radio" || el.localName === "ui-radio"; + } function debounce(func, wait) { var timeout; return function() { @@ -2278,7 +2284,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); get raw() { return raw; }, - version: "3.14.1", + version: "3.14.3", flushAndStopDeferringMutations, dontAutoEvaluateFunctions, disableEffectScheduling, @@ -3361,7 +3367,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); setValue(getInputValue(el, modifiers, e, getValue())); }); if (modifiers.includes("fill")) { - if ([void 0, null, ""].includes(getValue()) || el.type === "checkbox" && Array.isArray(getValue()) || el.tagName.toLowerCase() === "select" && el.multiple) { + if ([void 0, null, ""].includes(getValue()) || isCheckbox(el) && Array.isArray(getValue()) || el.tagName.toLowerCase() === "select" && el.multiple) { setValue(getInputValue(el, modifiers, { target: el }, getValue())); } } @@ -3401,7 +3407,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); return mutateDom(() => { if (event instanceof CustomEvent && event.detail !== void 0) return event.detail !== null && event.detail !== void 0 ? event.detail : event.target.value; - else if (el.type === "checkbox") { + else if (isCheckbox(el)) { if (Array.isArray(currentValue)) { let newValue = null; if (modifiers.includes("number")) { @@ -3432,7 +3438,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); }); } else { let newValue; - if (el.type === "radio") { + if (isRadio(el)) { if (event.target.checked) { newValue = event.target.value; } else { @@ -4971,11 +4977,11 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); var checked = getCheckedRadio(radioSet, node.form); return !checked || checked === node; }; - var isRadio = function isRadio2(node) { + var isRadio2 = function isRadio22(node) { return isInput(node) && node.type === "radio"; }; var isNonTabbableRadio = function isNonTabbableRadio2(node) { - return isRadio(node) && !isTabbableRadio(node); + return isRadio2(node) && !isTabbableRadio(node); }; var isZeroArea = function isZeroArea2(node) { var _node$getBoundingClie = node.getBoundingClientRect(), width = _node$getBoundingClie.width, height = _node$getBoundingClie.height; @@ -7625,6 +7631,44 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); document.head.appendChild(style); } + // js/plugins/navigate/popover.js + function packUpPersistedPopovers(persistedEl) { + persistedEl.querySelectorAll(":popover-open").forEach((el) => { + el.setAttribute("data-navigate-popover-open", ""); + let animations = el.getAnimations(); + el._pausedAnimations = animations.map((animation) => ({ + keyframes: animation.effect.getKeyframes(), + options: { + duration: animation.effect.getTiming().duration, + easing: animation.effect.getTiming().easing, + fill: animation.effect.getTiming().fill, + iterations: animation.effect.getTiming().iterations + }, + currentTime: animation.currentTime, + playState: animation.playState + })); + animations.forEach((i) => i.pause()); + }); + } + function unPackPersistedPopovers(persistedEl) { + persistedEl.querySelectorAll("[data-navigate-popover-open]").forEach((el) => { + el.removeAttribute("data-navigate-popover-open"); + queueMicrotask(() => { + if (!el.isConnected) + return; + el.showPopover(); + el.getAnimations().forEach((i) => i.finish()); + if (el._pausedAnimations) { + el._pausedAnimations.forEach(({ keyframes, options, currentTime, now, playState }) => { + let animation = el.animate(keyframes, options); + animation.currentTime = currentTime; + }); + delete el._pausedAnimations; + } + }); + }); + } + // js/plugins/navigate/page.js var oldBodyScriptTagHashes = []; var attributesExemptFromScriptTagHashing = [ @@ -7763,7 +7807,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); function navigate_default(Alpine3) { Alpine3.navigate = (url) => { let destination = createUrlObjectFromString(url); - let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", { + let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", { url: destination, history: false, cached: false @@ -7794,7 +7838,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination); }); whenItIsReleased(() => { - let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", { + let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", { url: destination, history: false, cached: false @@ -7808,7 +7852,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); function navigateTo(destination, shouldPushToHistoryState = true) { showProgressBar && showAndStartProgressBar(); fetchHtmlOrUsePrefetchedHtml(destination, (html, finalDestination) => { - fireEventForOtherLibariesToHookInto("alpine:navigating"); + fireEventForOtherLibrariesToHookInto("alpine:navigating"); restoreScroll && storeScrollInformationInHtmlBeforeNavigatingAway(); showProgressBar && finishAndHideProgressBar(); cleanupAlpineElementsOnThePageThatArentInsideAPersistedElement(); @@ -7816,6 +7860,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); preventAlpineFromPickingUpDomChanges(Alpine3, (andAfterAllThis) => { enablePersist && storePersistantElementsForLater((persistedEl) => { packUpPersistedTeleports(persistedEl); + packUpPersistedPopovers(persistedEl); }); if (shouldPushToHistoryState) { updateUrlAndStoreLatestHtmlForFutureBackButtons(html, finalDestination); @@ -7826,6 +7871,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); removeAnyLeftOverStaleTeleportTargets(document.body); enablePersist && putPersistantElementsBack((persistedEl, newStub) => { unPackPersistedTeleports(persistedEl); + unPackPersistedPopovers(persistedEl); }); restoreScrollPositionOrScrollToTop(); afterNewScriptsAreDoneLoading(() => { @@ -7834,7 +7880,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); autofocus && autofocusElementsWithTheAutofocusAttribute(); }); nowInitializeAlpineOnTheNewPage(Alpine3); - fireEventForOtherLibariesToHookInto("alpine:navigated"); + fireEventForOtherLibrariesToHookInto("alpine:navigated"); }); }); }); @@ -7844,7 +7890,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); whenTheBackOrForwardButtonIsClicked((ifThePageBeingVisitedHasntBeenCached) => { ifThePageBeingVisitedHasntBeenCached((url) => { let destination = createUrlObjectFromString(url); - let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", { + let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", { url: destination, history: true, cached: false @@ -7856,7 +7902,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); }); }, (html, url, currentPageUrl, currentPageKey) => { let destination = createUrlObjectFromString(url); - let prevented = fireEventForOtherLibariesToHookInto("alpine:navigate", { + let prevented = fireEventForOtherLibrariesToHookInto("alpine:navigate", { url: destination, history: true, cached: true @@ -7864,29 +7910,31 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); if (prevented) return; storeScrollInformationInHtmlBeforeNavigatingAway(); - fireEventForOtherLibariesToHookInto("alpine:navigating"); + fireEventForOtherLibrariesToHookInto("alpine:navigating"); updateCurrentPageHtmlInSnapshotCacheForLaterBackButtonClicks(currentPageUrl, currentPageKey); preventAlpineFromPickingUpDomChanges(Alpine3, (andAfterAllThis) => { enablePersist && storePersistantElementsForLater((persistedEl) => { packUpPersistedTeleports(persistedEl); + packUpPersistedPopovers(persistedEl); }); swapCurrentPageWithNewHtml(html, () => { removeAnyLeftOverStaleProgressBars(); removeAnyLeftOverStaleTeleportTargets(document.body); enablePersist && putPersistantElementsBack((persistedEl, newStub) => { unPackPersistedTeleports(persistedEl); + unPackPersistedPopovers(persistedEl); }); restoreScrollPositionOrScrollToTop(); andAfterAllThis(() => { autofocus && autofocusElementsWithTheAutofocusAttribute(); nowInitializeAlpineOnTheNewPage(Alpine3); - fireEventForOtherLibariesToHookInto("alpine:navigated"); + fireEventForOtherLibrariesToHookInto("alpine:navigated"); }); }); }); }); setTimeout(() => { - fireEventForOtherLibariesToHookInto("alpine:navigated"); + fireEventForOtherLibrariesToHookInto("alpine:navigated"); }); } function fetchHtmlOrUsePrefetchedHtml(fromDestination, callback) { @@ -7903,7 +7951,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); }); }); } - function fireEventForOtherLibariesToHookInto(name, detail) { + function fireEventForOtherLibrariesToHookInto(name, detail) { let event = new CustomEvent(name, { cancelable: true, bubbles: true, @@ -8212,8 +8260,6 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); let toAttributes = Array.from(to.attributes); for (let i = domAttributes.length - 1; i >= 0; i--) { let name = domAttributes[i].name; - if (name === "style") - continue; if (!to.hasAttribute(name)) { from2.removeAttribute(name); } @@ -8221,8 +8267,6 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); for (let i = toAttributes.length - 1; i >= 0; i--) { let name = toAttributes[i].name; let value = toAttributes[i].value; - if (name === "style") - continue; if (from2.getAttribute(name) !== value) { from2.setAttribute(name, value); } @@ -8914,6 +8958,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el); }, lookahead: false }); + trigger2("morphed", { el, component }); } function isntElement(el) { return typeof el.hasAttribute !== "function"; diff --git a/public/vendor/livewire/livewire.min.js b/public/vendor/livewire/livewire.min.js index 90a91971d8..32197b3ebf 100644 --- a/public/vendor/livewire/livewire.min.js +++ b/public/vendor/livewire/livewire.min.js @@ -1,14 +1,14 @@ -(()=>{var nl=Object.create;var ii=Object.defineProperty;var il=Object.getOwnPropertyDescriptor;var ol=Object.getOwnPropertyNames;var sl=Object.getPrototypeOf,al=Object.prototype.hasOwnProperty;var ll=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var ul=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of ol(t))!al.call(e,i)&&i!==r&&ii(e,i,{get:()=>t[i],enumerable:!(n=il(t,i))||n.enumerable});return e};var cl=(e,t,r)=>(r=e!=null?nl(sl(e)):{},ul(t||!e||!e.__esModule?ii(r,"default",{value:e,enumerable:!0}):r,e));var da=ll((Hn,fa)=>{(function(e,t){typeof define=="function"&&define.amd?define(t):typeof Hn=="object"?fa.exports=t():e.NProgress=t()})(Hn,function(){var e={};e.version="0.2.0";var t=e.settings={minimum:.08,easing:"ease",positionUsing:"",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'
'};e.configure=function(c){var d,m;for(d in c)m=c[d],m!==void 0&&c.hasOwnProperty(d)&&(t[d]=m);return this},e.status=null,e.set=function(c){var d=e.isStarted();c=r(c,t.minimum,1),e.status=c===1?null:c;var m=e.render(!d),b=m.querySelector(t.barSelector),g=t.speed,y=t.easing;return m.offsetWidth,o(function(v){t.positionUsing===""&&(t.positionUsing=e.getPositioningCSS()),s(b,i(c,g,y)),c===1?(s(m,{transition:"none",opacity:1}),m.offsetWidth,setTimeout(function(){s(m,{transition:"all "+g+"ms linear",opacity:0}),setTimeout(function(){e.remove(),v()},g)},g)):setTimeout(v,g)}),this},e.isStarted=function(){return typeof e.status=="number"},e.start=function(){e.status||e.set(0);var c=function(){setTimeout(function(){!e.status||(e.trickle(),c())},t.trickleSpeed)};return t.trickle&&c(),this},e.done=function(c){return!c&&!e.status?this:e.inc(.3+.5*Math.random()).set(1)},e.inc=function(c){var d=e.status;return d?(typeof c!="number"&&(c=(1-d)*r(Math.random()*d,.1,.95)),d=r(d+c,0,.994),e.set(d)):e.start()},e.trickle=function(){return e.inc(Math.random()*t.trickleRate)},function(){var c=0,d=0;e.promise=function(m){return!m||m.state()==="resolved"?this:(d===0&&e.start(),c++,d++,m.always(function(){d--,d===0?(c=0,e.done()):e.set((c-d)/c)}),this)}}(),e.render=function(c){if(e.isRendered())return document.getElementById("nprogress");l(document.documentElement,"nprogress-busy");var d=document.createElement("div");d.id="nprogress",d.innerHTML=t.template;var m=d.querySelector(t.barSelector),b=c?"-100":n(e.status||0),g=document.querySelector(t.parent),y;return s(m,{transition:"all 0 linear",transform:"translate3d("+b+"%,0,0)"}),t.showSpinner||(y=d.querySelector(t.spinnerSelector),y&&p(y)),g!=document.body&&l(g,"nprogress-custom-parent"),g.appendChild(d),d},e.remove=function(){u(document.documentElement,"nprogress-busy"),u(document.querySelector(t.parent),"nprogress-custom-parent");var c=document.getElementById("nprogress");c&&p(c)},e.isRendered=function(){return!!document.getElementById("nprogress")},e.getPositioningCSS=function(){var c=document.body.style,d="WebkitTransform"in c?"Webkit":"MozTransform"in c?"Moz":"msTransform"in c?"ms":"OTransform"in c?"O":"";return d+"Perspective"in c?"translate3d":d+"Transform"in c?"translate":"margin"};function r(c,d,m){return cm?m:c}function n(c){return(-1+c)*100}function i(c,d,m){var b;return t.positionUsing==="translate3d"?b={transform:"translate3d("+n(c)+"%,0,0)"}:t.positionUsing==="translate"?b={transform:"translate("+n(c)+"%,0)"}:b={"margin-left":n(c)+"%"},b.transition="all "+d+"ms "+m,b}var o=function(){var c=[];function d(){var m=c.shift();m&&m(d)}return function(m){c.push(m),c.length==1&&d()}}(),s=function(){var c=["Webkit","O","Moz","ms"],d={};function m(v){return v.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(_,T){return T.toUpperCase()})}function b(v){var _=document.body.style;if(v in _)return v;for(var T=c.length,A=v.charAt(0).toUpperCase()+v.slice(1),w;T--;)if(w=c[T]+A,w in _)return w;return v}function g(v){return v=m(v),d[v]||(d[v]=b(v))}function y(v,_,T){_=g(_),v.style[_]=T}return function(v,_){var T=arguments,A,w;if(T.length==2)for(A in _)w=_[A],w!==void 0&&_.hasOwnProperty(A)&&y(v,A,w);else y(v,T[1],T[2])}}();function a(c,d){var m=typeof c=="string"?c:f(c);return m.indexOf(" "+d+" ")>=0}function l(c,d){var m=f(c),b=m+d;a(m,d)||(c.className=b.substring(1))}function u(c,d){var m=f(c),b;!a(c,d)||(b=m.replace(" "+d+" "," "),c.className=b.substring(1,b.length-1))}function f(c){return(" "+(c.className||"")+" ").replace(/\s+/gi," ")}function p(c){c&&c.parentNode&&c.parentNode.removeChild(c)}return e})});var yt=class{constructor(){this.arrays={}}add(t,r){this.arrays[t]||(this.arrays[t]=[]),this.arrays[t].push(r)}remove(t){this.arrays[t]&&delete this.arrays[t]}get(t){return this.arrays[t]||[]}each(t,r){return this.get(t).forEach(r)}},Fe=class{constructor(){this.arrays=new WeakMap}add(t,r){this.arrays.has(t)||this.arrays.set(t,[]),this.arrays.get(t).push(r)}remove(t){this.arrays.has(t)&&this.arrays.delete(t,[])}get(t){return this.arrays.has(t)?this.arrays.get(t):[]}each(t,r){return this.get(t).forEach(r)}};function xt(e,t,r={},n=!0){e.dispatchEvent(new CustomEvent(t,{detail:r,bubbles:n,composed:!0,cancelable:!0}))}function _t(e,t,r){return e.addEventListener(t,r),()=>e.removeEventListener(t,r)}function St(e){return typeof e=="object"&&e!==null}function oi(e){return St(e)&&!br(e)}function br(e){return Array.isArray(e)}function wr(e){return typeof e=="function"}function si(e){return typeof e!="object"||e===null}function ce(e){return JSON.parse(JSON.stringify(e))}function W(e,t){return t===""?e:t.split(".").reduce((r,n)=>{if(r!==void 0)return r[n]},e)}function we(e,t,r){let n=t.split(".");if(n.length===1)return e[t]=r;let i=n.shift(),o=n.join(".");e[i]===void 0&&(e[i]={}),we(e[i],o,r)}function Ze(e,t,r={},n=""){if(e===t)return r;if(typeof e!=typeof t||oi(e)&&br(t)||br(e)&&oi(t)||si(e)||si(t))return r[n]=t,r;let i=Object.keys(e);return Object.entries(t).forEach(([o,s])=>{r={...r,...Ze(e[o],t[o],r,n===""?o:`${n}.${o}`)},i=i.filter(a=>a!==o)}),i.forEach(o=>{r[`${n}.${o}`]="__rm__"}),r}function ye(e){let t=ai(e)?e[0]:e,r=ai(e)?e[1]:void 0;return St(t)&&Object.entries(t).forEach(([n,i])=>{t[n]=ye(i)}),t}function ai(e){return Array.isArray(e)&&e.length===2&&typeof e[1]=="object"&&Object.keys(e[1]).includes("s")}function Et(){if(document.querySelector('meta[name="csrf-token"]'))return document.querySelector('meta[name="csrf-token"]').getAttribute("content");if(document.querySelector("[data-csrf]"))return document.querySelector("[data-csrf]").getAttribute("data-csrf");if(window.livewireScriptConfig.csrf??!1)return window.livewireScriptConfig.csrf;throw"Livewire: No CSRF token detected"}var Ie;function li(){if(Ie)return Ie;if(window.livewireScriptConfig&&(window.livewireScriptConfig.nonce??!1))return Ie=window.livewireScriptConfig.nonce,Ie;let e=document.querySelector("style[data-livewire-style][nonce]");return e?(Ie=e.nonce,Ie):null}function ui(){return document.querySelector("[data-update-uri]")?.getAttribute("data-update-uri")??window.livewireScriptConfig.uri??null}function At(e){return!!e.match(/