Add groups page. Update landing Add toastmagic

This commit is contained in:
joeplikestocode
2026-02-28 21:46:08 +01:00
parent 09b2b988f7
commit e405fec5c2
24 changed files with 2000 additions and 17 deletions

View File

@@ -47,13 +47,13 @@ REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null REDIS_PASSWORD=null
REDIS_PORT=6379 REDIS_PORT=6379
MAIL_MAILER=log MAIL_MAILER=smtp
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1 MAIL_HOST=127.0.0.1
MAIL_PORT=2525 MAIL_PORT=1025
MAIL_USERNAME=null MAIL_USERNAME=null
MAIL_PASSWORD=null MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.com" MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="dev@patchbook.test"
MAIL_FROM_NAME="${APP_NAME}" MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID= AWS_ACCESS_KEY_ID=

20
app/Models/Setting.php Normal file
View File

@@ -0,0 +1,20 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Setting extends Model
{
public $fillable = [
'user_id',
'key',
'value',
];
public function User(): belongsTo
{
return $this->belongsTo('App\Models\User');
}
}

View File

@@ -4,6 +4,8 @@ namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail; // use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
@@ -45,4 +47,18 @@ class User extends Authenticatable
'password' => 'hashed', 'password' => 'hashed',
]; ];
} }
public function crews(): BelongsToMany
{
return $this->belongsToMany(
Crew::class,
'crew_members',
'user_id',
'crew_id'
)->withTimestamps();
}
public function settings(): HasMany
{
return $this->hasMany(Setting::class);
}
} }

View File

@@ -12,8 +12,10 @@
"php": "^8.2", "php": "^8.2",
"blade-ui-kit/blade-icons": "^1.8", "blade-ui-kit/blade-icons": "^1.8",
"brunocfalcao/blade-feather-icons": "^6.0", "brunocfalcao/blade-feather-icons": "^6.0",
"codeat3/blade-phosphor-icons": "^2.3",
"codeat3/blade-solar-icons": "^1.3", "codeat3/blade-solar-icons": "^1.3",
"davidhsianturi/blade-bootstrap-icons": "^2.1", "davidhsianturi/blade-bootstrap-icons": "^2.1",
"devrabiul/laravel-toaster-magic": "^2.0",
"laravel/fortify": "^1.34", "laravel/fortify": "^1.34",
"laravel/framework": "^12.0", "laravel/framework": "^12.0",
"laravel/tinker": "^2.10.1", "laravel/tinker": "^2.10.1",

136
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "12ebee837f2c603afb84fe95d6258bf2", "content-hash": "e8ac2a3d2d0647f711ada077e6de914d",
"packages": [ "packages": [
{ {
"name": "bacon/bacon-qr-code", "name": "bacon/bacon-qr-code",
@@ -333,6 +333,77 @@
], ],
"time": "2024-02-09T16:56:22+00:00" "time": "2024-02-09T16:56:22+00:00"
}, },
{
"name": "codeat3/blade-phosphor-icons",
"version": "2.3.0",
"source": {
"type": "git",
"url": "https://github.com/codeat3/blade-phosphor-icons.git",
"reference": "2812a27ec642359344429d344c7f7bbdfad489ce"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/codeat3/blade-phosphor-icons/zipball/2812a27ec642359344429d344c7f7bbdfad489ce",
"reference": "2812a27ec642359344429d344c7f7bbdfad489ce",
"shasum": ""
},
"require": {
"blade-ui-kit/blade-icons": "^1.1",
"illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0",
"php": "^7.4|^8.0"
},
"require-dev": {
"codeat3/blade-icon-generation-helpers": "^0.10",
"codeat3/phpcs-styles": "^1.0",
"orchestra/testbench": "^6.0|^7.0|^8.0|^9.0|^10.0",
"phpunit/phpunit": "^9.0|^10.5|^11.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Codeat3\\BladePhosphorIcons\\BladePhosphorIconsServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Codeat3\\BladePhosphorIcons\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Swapnil Sarwe",
"homepage": "https://swapnilsarwe.com"
},
{
"name": "Dries Vints",
"homepage": "https://driesvints.com"
}
],
"description": "A package to easily make use of \"Phosphor Icons\" in your Laravel Blade views.",
"homepage": "https://github.com/codeat3/blade-phosphor-icons",
"keywords": [
"blade",
"laravel",
"phosphor-icons"
],
"support": {
"issues": "https://github.com/codeat3/blade-phosphor-icons/issues",
"source": "https://github.com/codeat3/blade-phosphor-icons/tree/2.3.0"
},
"funding": [
{
"url": "https://github.com/swapnilsarwe",
"type": "github"
}
],
"time": "2025-02-25T06:29:09+00:00"
},
{ {
"name": "codeat3/blade-solar-icons", "name": "codeat3/blade-solar-icons",
"version": "1.3.1", "version": "1.3.1",
@@ -517,6 +588,69 @@
}, },
"time": "2025-06-18T12:39:09+00:00" "time": "2025-06-18T12:39:09+00:00"
}, },
{
"name": "devrabiul/laravel-toaster-magic",
"version": "v2.0",
"source": {
"type": "git",
"url": "https://github.com/devrabiul/laravel-toaster-magic.git",
"reference": "6255bdbf6e8f9331e51e72af52c1a158dac86e71"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/devrabiul/laravel-toaster-magic/zipball/6255bdbf6e8f9331e51e72af52c1a158dac86e71",
"reference": "6255bdbf6e8f9331e51e72af52c1a158dac86e71",
"shasum": ""
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"ToastMagic": "Devrabiul\\ToastMagic\\Facades\\ToastMagic"
},
"providers": [
"Devrabiul\\ToastMagic\\ToastMagicServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Devrabiul\\ToastMagic\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Muhammad Rabiul",
"email": "devrabiul@gmail.com",
"homepage": "https://rixetbd.com",
"role": "Software Engineer"
}
],
"description": "Laravel Toaster Magic is a lightweight, flexible toast library for Laravel projects, with no jQuery, Bootstrap, or Tailwind dependency.",
"homepage": "https://github.com/devrabiul/laravel-toaster-magic",
"keywords": [
"dynamic-notifications",
"flash-notifications",
"laravel",
"laravel-toastr",
"livewire",
"livewire-toaster",
"magic",
"notifications",
"php",
"toaster",
"toastr"
],
"support": {
"issues": "https://github.com/devrabiul/laravel-toaster-magic/issues",
"source": "https://github.com/devrabiul/laravel-toaster-magic/tree/v2.0"
},
"time": "2026-01-19T07:31:44+00:00"
},
{ {
"name": "dflydev/dot-access-data", "name": "dflydev/dot-access-data",
"version": "v3.0.3", "version": "v3.0.3",

View File

@@ -0,0 +1,16 @@
<?php
return [
'options' => [
"closeButton" => true,
"positionClass" => "toast-top-end",
"preventDuplicates" => false,
"showDuration" => "300",
"timeOut" => "5000",
"theme" => "default", // Available themes: default, material, ios, glassmorphism, neon, minimal, neumorphism
"gradient_enable" => false, // Available for: default, material, ios, glassmorphism, neon themes
"color_mode" => false // Color mode (true or false)
],
'livewire_enabled' => true,
'livewire_version' => 'v4'
];

View File

@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('settings', function (Blueprint $table) {
$table->id();
$table->foreignid('user_id')->constrained()->cascadeOnDelete();
$table->string('key');
$table->string('value');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('settings');
}
};

View File

@@ -0,0 +1,981 @@
:root {
--toast-magic-success: #04bb7b;
--toast-magic-success-rgb: 4, 187, 123;
--toast-magic-danger: #dc3545;
--toast-magic-danger-rgb: 220, 53, 69;
--toast-magic-info: #0dcaf0;
--toast-magic-info-rgb: 13, 202, 240;
--toast-magic-warning: #ffc107;
--toast-magic-warning-rgb: 255, 193, 7;
--toast-item-bg: #fff;
--toast-item-color: #000;
--toast-magic-box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1);
--toast-close-btn-color: #000;
--toast-custom-btn-color: #000;
--toast-color-mode-progress-bg: 0, 0, 0;
}
body[theme="dark"] {
--toast-item-bg: #000;
--toast-item-color: #FFF;
--toast-magic-box-shadow: -5px 0px 30px 0px rgba(255, 255, 255, 0.1);
--toast-close-btn-color: #FFF;
--toast-custom-btn-color: rgba(255, 255, 255, .8);
--toast-color-mode-progress-bg: 255, 255, 255;
}
.toast-text-success {
color: var(--toast-magic-success);
}
.toast-text-danger {
color: var(--toast-magic-danger);
}
.toast-text-info {
color: var(--toast-magic-info);
}
.toast-text-warning {
color: var(--toast-magic-warning);
}
.position-relative {
position: relative;
}
.toast-container {
--tm-toast-z-index: 99990;
--toast-progress-start: 0%;
--toast-progress-end: 100%;
--toast-item-before-opacity: 0.4;
--toast-item-after-opacity: 1;
position: fixed;
inset-block-start: 30px;
inset-inline-end: 20px;
display: flex;
flex-direction: column;
gap: 0.5rem;
z-index: var(--tm-toast-z-index, 1050);
width: max-content;
max-width: 100%;
pointer-events: none;
}
.toast-container.toast-top-end {
inset-block-start: 30px;
inset-inline-end: 20px;
inset-block-end: auto;
inset-inline-start: auto;
}
.toast-container.toast-top-start {
inset-block-start: 30px;
inset-inline-start: 20px;
inset-block-end: auto;
inset-inline-end: auto;
--toast-progress-start: 100%;
--toast-progress-end: 0%;
--toast-item-before-opacity: 1;
--toast-item-after-opacity: 0.4;
}
.toast-container.toast-top-center {
inset-block-start: 30px;
inset-block-end: auto;
inset-inline-end: auto;
left: 50%;
transform: translateX(-50%);
align-items: center;
}
.toast-container.toast-bottom-end {
inset-block-end: 30px;
inset-inline-end: 20px;
inset-block-start: auto;
inset-inline-start: auto;
}
.toast-container.toast-bottom-start {
inset-block-end: 30px;
inset-inline-start: 20px;
inset-block-start: auto;
inset-inline-end: auto;
--toast-progress-start: 100%;
--toast-progress-end: 0%;
--toast-item-before-opacity: 1;
--toast-item-after-opacity: 0.4;
}
.toast-container.toast-bottom-center {
inset-block-end: 30px;
inset-block-start: auto;
inset-inline-end: auto;
left: 50%;
transform: translateX(-50%);
align-items: center;
}
.toast-container .toast-item {
--tm-toast-max-width: 370px;
position: relative;
overflow: hidden;
opacity: 0;
transform: translateX(110%);
transition: transform .5s ease-in-out, opacity 0.5s ease-in-out;
width: var(--tm-toast-max-width);
max-width: 100%;
font-size: 0.875rem;
color: var(--toast-item-color);
pointer-events: auto;
background-color: var(--toast-item-bg);
background-clip: padding-box;
border: 1px solid transparent;
box-shadow: var(--toast-magic-box-shadow);
border-radius: .5rem;
padding: 1.25rem !important;
align-items: center !important;
}
.toast-item.toast-success::before {
content: "";
position: absolute;
inset-block-start: 0;
inset-inline-start: 0;
width: 100%;
height: 4px;
background-color: rgba(var(--toast-magic-success-rgb), var(--toast-item-before-opacity, 0.4));
animation: toastProgressReverse 3s linear forwards;
}
.toast-item.toast-success::after {
content: "";
position: absolute;
inset-block-start: 0;
inset-inline-end: 0;
width: 0%;
height: 4px;
background-color: rgba(var(--toast-magic-success-rgb), var(--toast-item-after-opacity, 1));
transform-origin: right;
animation: toastProgress 3s linear forwards;
}
.toast-item.toast-danger::before {
content: "";
position: absolute;
inset-block-start: 0;
inset-inline-start: 0;
width: 100%;
height: 4px;
background-color: rgba(var(--toast-magic-danger-rgb), var(--toast-item-before-opacity, 0.4));
animation: toastProgressReverse 3s linear forwards;
}
.toast-item.toast-danger::after {
content: "";
position: absolute;
inset-block-start: 0;
inset-inline-end: 0;
width: 0%;
height: 4px;
background-color: rgba(var(--toast-magic-danger-rgb), var(--toast-item-after-opacity, 1));
transform-origin: right;
animation: toastProgress 3s linear forwards;
}
.toast-item.toast-warning::before {
content: "";
position: absolute;
inset-block-start: 0;
inset-inline-start: 0;
width: 100%;
height: 4px;
background-color: rgba(var(--toast-magic-warning-rgb), var(--toast-item-before-opacity, 0.4));
animation: toastProgressReverse 3s linear forwards;
}
.toast-item.toast-warning::after {
content: "";
position: absolute;
inset-block-start: 0;
inset-inline-end: 0;
width: 0%;
height: 4px;
background-color: rgba(var(--toast-magic-warning-rgb), var(--toast-item-after-opacity, 1));
transform-origin: right;
animation: toastProgress 3s linear forwards;
}
.toast-item.toast-info::before {
content: "";
position: absolute;
inset-block-start: 0;
inset-inline-start: 0;
width: 100%;
height: 4px;
background-color: rgba(var(--toast-magic-info-rgb), var(--toast-item-before-opacity, 0.4));
animation: toastProgressReverse 3s linear forwards;
}
.toast-item.toast-info::after {
content: "";
position: absolute;
inset-block-start: 0;
inset-inline-end: 0;
width: 0%;
height: 4px;
background-color: rgba(var(--toast-magic-info-rgb), var(--toast-item-after-opacity, 1));
transform-origin: right;
animation: toastProgress 3s linear forwards;
}
.toast-container.toast-top-start .toast-item,
.toast-container.toast-bottom-start .toast-item {
transform: translateX(-110%);
}
.toast-container.toast-top-center .toast-item {
transform: translateY(-110%);
}
.toast-container.toast-bottom-center .toast-item {
transform: translateY(110%);
}
.toast-container.theme-default.toast-gradient-enable .toast-item {
--toast-item-gradient-opacity: .1;
}
.toast-container.theme-default.toast-gradient-enable .toast-item.toast-success {
background-image: linear-gradient(to bottom, rgba(var(--toast-magic-success-rgb), var(--toast-item-gradient-opacity, .1)), #fff);
}
.toast-container.theme-default.toast-gradient-enable .toast-item.toast-info {
background-image: linear-gradient(to bottom, rgba(var(--toast-magic-info-rgb), var(--toast-item-gradient-opacity, .1)), #fff);
}
.toast-container.theme-default.toast-gradient-enable .toast-item.toast-warning {
background-image: linear-gradient(to bottom, rgba(var(--toast-magic-warning-rgb), var(--toast-item-gradient-opacity, .1)), #fff);
}
.toast-container.theme-default.toast-gradient-enable .toast-item.toast-danger {
background-image: linear-gradient(to bottom, rgba(var(--toast-magic-danger-rgb), var(--toast-item-gradient-opacity, .1)), #fff);
}
.toast-container.theme-material .toast-item {
border-radius: 0;
--toast-magic-box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1);
}
.toast-container.theme-material.toast-gradient-enable .toast-item {
--toast-item-gradient-opacity: .1;
}
.toast-container.theme-material.toast-gradient-enable .toast-item.toast-success {
background-image: linear-gradient(to bottom, rgba(var(--toast-magic-success-rgb), var(--toast-item-gradient-opacity, .1)), #fff);
}
.toast-container.theme-material.toast-gradient-enable .toast-item.toast-info {
background-image: linear-gradient(to bottom, rgba(var(--toast-magic-info-rgb), var(--toast-item-gradient-opacity, .1)), #fff);
}
.toast-container.theme-material.toast-gradient-enable .toast-item.toast-warning {
background-image: linear-gradient(to bottom, rgba(var(--toast-magic-warning-rgb), var(--toast-item-gradient-opacity, .1)), #fff);
}
.toast-container.theme-material.toast-gradient-enable .toast-item.toast-danger {
background-image: linear-gradient(to bottom, rgba(var(--toast-magic-danger-rgb), var(--toast-item-gradient-opacity, .1)), #fff);
}
.toast-container.theme-material .toast-custom-btn {
border-radius: 0;
}
.toast-container.theme-ios {
gap: 10px;
}
.toast-container.theme-ios .toast-item {
background: rgba(255, 255, 255, 0.75);
backdrop-filter: blur(50px) saturate(180%);
-webkit-backdrop-filter: blur(50px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.4);
box-shadow: 0 4px 24px -1px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.02);
border-radius: 22px;
padding: 14px 18px !important;
color: #1d1d1f;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
body[theme="dark"] .toast-container.theme-ios .toast-item {
background: rgba(28, 28, 30, 0.75);
border: 1px solid rgba(255, 255, 255, 0.1);
color: #f5f5f7;
box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.5);
}
.toast-container.theme-ios .toast-item.show {
transform: translateX(0) scale(1);
animation: iosBounce .4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.toast-container.theme-ios .toast-item:active {
transform: scale(0.98);
}
.toast-container.theme-ios .toast-item.toast-success,
.toast-container.theme-ios .toast-item.toast-danger,
.toast-container.theme-ios .toast-item.toast-warning,
.toast-container.theme-ios .toast-item.toast-info {
border-left: none;
}
.toast-container.theme-ios .toast-item::before,
.toast-container.theme-ios .toast-item::after {
display: none;
}
.toast-container.theme-ios .toast-item-content-center {
gap: 12px !important;
align-items: center;
}
.toast-container.theme-ios .toast-body-icon-container {
width: 38px;
height: 38px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
flex-shrink: 0;
}
.toast-container.theme-ios .toast-body-icon-container {
width: 38px;
height: 38px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
flex-shrink: 0;
}
body[theme="dark"] .toast-container.theme-ios .toast-body-icon-container {
background-color: rgba(235, 235, 245, 0.1);
}
.toast-container.theme-ios .toast-item.toast-success .toast-body-icon-container {
color: var(--toast-magic-success);
}
.toast-container.theme-ios .toast-item.toast-danger .toast-body-icon-container {
color: var(--toast-magic-danger);
}
.toast-container.theme-ios .toast-item.toast-warning .toast-body-icon-container {
color: var(--toast-magic-warning);
}
.toast-container.theme-ios .toast-item.toast-info .toast-body-icon-container {
color: var(--toast-magic-info);
}
.toast-container.theme-ios .toast-body h4 {
font-size: 15px;
font-weight: 600;
margin-bottom: 2px;
letter-spacing: -0.01em;
}
.toast-container.theme-ios .toast-body p {
font-size: 13px;
color: inherit;
opacity: 0.8;
line-height: 1.4;
}
.toast-container.theme-ios .toast-custom-btn {
border-radius: 16px;
font-weight: 600;
font-size: 13px;
padding: 6px 14px;
transition: all 0.2s ease;
background: rgba(120, 120, 128, 0.12);
color: inherit;
border: none;
}
.toast-container.theme-ios .toast-custom-btn:hover {
background: rgba(120, 120, 128, 0.2);
transform: none;
box-shadow: none;
}
.toast-container.theme-ios .toast-close-btn {
background: rgba(60, 60, 67, 0.1);
color: inherit;
width: 20px;
height: 20px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.6;
font-size: 12px;
}
body[theme="dark"] .toast-container.theme-ios .toast-close-btn {
background: rgba(235, 235, 245, 0.1);
}
.toast-container.theme-ios .toast-close-btn:hover {
opacity: 1;
background: rgba(60, 60, 67, 0.2);
}
@keyframes iosBounce {
0% {
transform: scale(0.9);
opacity: 0;
}
50% {
transform: scale(1.02);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 1;
}
}
.toast-container.theme-glassmorphism .toast-item {
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(50px) saturate(180%);
-webkit-backdrop-filter: blur(50px) saturate(180%);
border: 1.5px solid rgba(255, 255, 255, 0.18);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1), inset 0 1px 1px 0 rgba(255, 255, 255, 0.3);
border-radius: 1rem;
}
.toast-container.theme-glassmorphism .toast-item.toast-success {
background: linear-gradient(135deg, rgba(var(--toast-magic-success-rgb), 0.15), rgba(255, 255, 255, 0.05));
border: 1.5px solid rgba(var(--toast-magic-success-rgb), 0.5);
box-shadow: 0 8px 32px 0 rgba(var(--toast-magic-success-rgb), 0.25), inset 0 1px 1px 0 rgba(255, 255, 255, 0.4), inset 0 0 20px 0 rgba(var(--toast-magic-success-rgb), 0.1);
}
.toast-container.theme-glassmorphism .toast-item.toast-success::before {
background-color: rgba(var(--toast-magic-success-rgb), 0.4);
}
.toast-container.theme-glassmorphism .toast-item.toast-success::after {
background-color: rgba(var(--toast-magic-success-rgb), 0.9);
}
.toast-container.theme-glassmorphism .toast-item.toast-danger {
background: linear-gradient(135deg, rgba(var(--toast-magic-danger-rgb), 0.15), rgba(255, 255, 255, 0.05));
border: 1.5px solid rgba(var(--toast-magic-danger-rgb), 0.5);
box-shadow: 0 8px 32px 0 rgba(var(--toast-magic-danger-rgb), 0.25), inset 0 1px 1px 0 rgba(255, 255, 255, 0.4), inset 0 0 20px 0 rgba(var(--toast-magic-danger-rgb), 0.1);
}
.toast-container.theme-glassmorphism .toast-item.toast-danger::before {
background-color: rgba(var(--toast-magic-danger-rgb), 0.4);
}
.toast-container.theme-glassmorphism .toast-item.toast-danger::after {
background-color: rgba(var(--toast-magic-danger-rgb), 0.9);
}
.toast-container.theme-glassmorphism .toast-item.toast-warning {
background: linear-gradient(135deg, rgba(var(--toast-magic-warning-rgb), 0.15), rgba(255, 255, 255, 0.05));
border: 1.5px solid rgba(var(--toast-magic-warning-rgb), 0.5);
box-shadow: 0 8px 32px 0 rgba(var(--toast-magic-warning-rgb), 0.25), inset 0 1px 1px 0 rgba(255, 255, 255, 0.4), inset 0 0 20px 0 rgba(var(--toast-magic-warning-rgb), 0.1);
}
.toast-container.theme-glassmorphism .toast-item.toast-warning::before {
background-color: rgba(var(--toast-magic-warning-rgb), 0.4);
}
.toast-container.theme-glassmorphism .toast-item.toast-warning::after {
background-color: rgba(var(--toast-magic-warning-rgb), 0.9);
}
.toast-container.theme-glassmorphism .toast-item.toast-info {
background: linear-gradient(135deg, rgba(var(--toast-magic-info-rgb), 0.15), rgba(255, 255, 255, 0.05));
border: 1.5px solid rgba(var(--toast-magic-info-rgb), 0.5);
box-shadow: 0 2px 5px 0 rgba(var(--toast-magic-info-rgb), 0.25), inset 0 1px 1px 0 rgba(255, 255, 255, 0.4), inset 0 0 20px 0 rgba(var(--toast-magic-info-rgb), 0.1);
}
.toast-container.theme-glassmorphism .toast-item.toast-info::before {
background-color: rgba(var(--toast-magic-info-rgb), 0.4);
}
.toast-container.theme-glassmorphism .toast-item.toast-info::after {
background-color: rgba(var(--toast-magic-info-rgb), 0.9);
}
.toast-container.theme-glassmorphism.toast-gradient-enable .toast-item.toast-success {
background: linear-gradient(135deg, rgba(var(--toast-magic-success-rgb), 0.2), rgba(255, 255, 255, 0.1));
}
.toast-container.theme-glassmorphism.toast-gradient-enable .toast-item.toast-info {
background: linear-gradient(135deg, rgba(var(--toast-magic-info-rgb), 0.2), rgba(255, 255, 255, 0.1));
}
.toast-container.theme-glassmorphism.toast-gradient-enable .toast-item.toast-warning {
background: linear-gradient(135deg, rgba(var(--toast-magic-warning-rgb), 0.2), rgba(255, 255, 255, 0.1));
}
.toast-container.theme-glassmorphism.toast-gradient-enable .toast-item.toast-danger {
background: linear-gradient(135deg, rgba(var(--toast-magic-danger-rgb), 0.2), rgba(255, 255, 255, 0.1));
}
.toast-container.theme-glassmorphism .toast-custom-btn {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 0.625rem;
box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, 0.4);
}
.toast-container.theme-glassmorphism .toast-custom-btn:hover {
background: rgba(255, 255, 255, 0.3);
box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, 0.5), 0 4px 12px rgba(0, 0, 0, 0.1);
}
.toast-container.theme-neon {
gap: 12px;
}
.toast-container.theme-neon .toast-item {
background: linear-gradient(135deg, #09090b 0%, #16161a 100%);
border-radius: 4px;
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), 0 10px 30px -10px rgba(0, 0, 0, 0.5);
color: #e4e4e7;
position: relative;
overflow: visible;
}
.toast-container.theme-neon .toast-item.toast-success {
border-color: var(--toast-magic-success);
box-shadow: 0 0 10px rgba(var(--toast-magic-success-rgb), 0.2), inset 0 0 20px rgba(var(--toast-magic-success-rgb), 0.05);
}
.toast-container.theme-neon .toast-item.toast-danger {
border-color: var(--toast-magic-danger);
box-shadow: 0 0 10px rgba(var(--toast-magic-danger-rgb), 0.2), inset 0 0 20px rgba(var(--toast-magic-danger-rgb), 0.05);
}
.toast-container.theme-neon .toast-item.toast-warning {
border-color: var(--toast-magic-warning);
box-shadow: 0 0 10px rgba(var(--toast-magic-warning-rgb), 0.2), inset 0 0 20px rgba(var(--toast-magic-warning-rgb), 0.05);
}
.toast-container.theme-neon .toast-item.toast-info {
border-color: var(--toast-magic-info);
box-shadow: 0 0 10px rgba(var(--toast-magic-info-rgb), 0.2), inset 0 0 20px rgba(var(--toast-magic-info-rgb), 0.05);
}
.toast-container.theme-neon.toast-gradient-enable .toast-item.toast-success {
background: radial-gradient(circle at top left, rgba(var(--toast-magic-success-rgb), 0.15), transparent 60%), linear-gradient(135deg, #09090b 0%, #121214 100%);
}
.toast-container.theme-neon.toast-gradient-enable .toast-item.toast-info {
background: radial-gradient(circle at top left, rgba(var(--toast-magic-info-rgb), 0.15), transparent 60%), linear-gradient(135deg, #09090b 0%, #121214 100%);
}
.toast-container.theme-neon.toast-gradient-enable .toast-item.toast-warning {
background: radial-gradient(circle at top left, rgba(var(--toast-magic-warning-rgb), 0.15), transparent 60%), linear-gradient(135deg, #09090b 0%, #121214 100%);
}
.toast-container.theme-neon.toast-gradient-enable .toast-item.toast-danger {
background: radial-gradient(circle at top left, rgba(var(--toast-magic-danger-rgb), 0.15), transparent 60%), linear-gradient(135deg, #09090b 0%, #121214 100%);
}
.toast-container.theme-neon .toast-body h4 {
font-size: 14px;
margin-bottom: 4px;
}
.toast-container.theme-neon .toast-body p {
color: #a1a1aa;
font-size: 12px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
letter-spacing: 0.5px;
}
.toast-container.theme-neon .toast-body-icon-container {
background: transparent;
}
.toast-container.theme-neon .toast-item.toast-success .toast-body-icon-container {
color: var(--toast-magic-success);
}
.toast-container.theme-neon .toast-item.toast-danger .toast-body-icon-container {
color: var(--toast-magic-danger);
}
.toast-container.theme-neon .toast-item.toast-warning .toast-body-icon-container {
color: var(--toast-magic-warning);
}
.toast-container.theme-neon .toast-item.toast-info .toast-body-icon-container {
color: var(--toast-magic-info);
}
.toast-container.theme-neon .toast-custom-btn {
background: transparent;
border: 1px solid currentColor;
border-radius: 2px;
font-size: 11px;
padding: 6px 12px;
transition: all 0.2s ease;
box-shadow: 0 0 5px rgba(0, 0, 0, 0);
}
.toast-container.theme-neon .toast-item.toast-success .toast-custom-btn {
color: var(--toast-magic-success);
}
.toast-container.theme-neon .toast-item.toast-danger .toast-custom-btn {
color: var(--toast-magic-danger);
}
.toast-container.theme-neon .toast-item.toast-warning .toast-custom-btn {
color: var(--toast-magic-warning);
}
.toast-container.theme-neon .toast-item.toast-info .toast-custom-btn {
color: var(--toast-magic-info);
}
.toast-container.theme-neon .toast-custom-btn:hover {
box-shadow: 0 0 15px currentColor;
}
.toast-container.theme-neon .toast-close-btn {
color: #ffffff;
transition: color 0.2s;
}
.toast-container.theme-neon .toast-close-btn:hover {
color: #fff;
text-shadow: 0 0 8px #fff;
}
.toast-container.theme-neon .toast-item::before,
.toast-container.theme-neon .toast-item::after {
display: none;
}
.toast-container.theme-minimal .toast-item {
border-radius: 0.375rem;
border: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
padding: 1rem !important;
}
.toast-container.theme-minimal .toast-item.toast-success {
border-left: 4px solid var(--toast-magic-success);
}
.toast-container.theme-minimal .toast-item.toast-danger {
border-left: 4px solid var(--toast-magic-danger);
}
.toast-container.theme-minimal .toast-item.toast-warning {
border-left: 4px solid var(--toast-magic-warning);
}
.toast-container.theme-minimal .toast-item.toast-info {
border-left: 4px solid var(--toast-magic-info);
}
.toast-container.theme-minimal .toast-item::before,
.toast-container.theme-minimal .toast-item::after {
display: none;
}
.toast-container.theme-minimal .toast-custom-btn {
border-radius: 0.25rem;
font-weight: 500;
}
.toast-container.theme-neumorphism .toast-item {
background: #e0e5ec;
border-radius: 1rem;
border: none;
}
.toast-container.theme-neumorphism .toast-item.toast-success::after {
background-color: var(--toast-magic-success);
box-shadow: 0 1px 2px rgba(var(--toast-magic-success-rgb), 0.1);
}
.toast-container.theme-neumorphism .toast-item.toast-danger::after {
background-color: var(--toast-magic-danger);
box-shadow: 0 1px 2px rgba(var(--toast-magic-danger-rgb), 0.1);
}
.toast-container.theme-neumorphism .toast-item.toast-warning::after {
background-color: var(--toast-magic-warning);
box-shadow: 0 1px 2px rgba(var(--toast-magic-warning-rgb), 0.1);
}
.toast-container.theme-neumorphism .toast-item.toast-info::after {
background-color: var(--toast-magic-info);
box-shadow: 0 1px 2px rgba(var(--toast-magic-info-rgb), 0.1);
}
.toast-container.theme-neumorphism .toast-custom-btn {
background: #e0e5ec;
border: 1px solid transparent;
border-radius: 0.5rem;
box-shadow: 4px 4px 8px rgba(163, 177, 198, 0.6), -4px -4px 8px rgba(255, 255, 255, 0.5);
}
.toast-container.theme-neumorphism .toast-custom-btn:hover {
box-shadow: 2px 2px 4px rgba(163, 177, 198, 0.6), -2px -2px 4px rgba(255, 255, 255, 0.5);
}
.toast-container.toast-color-true .toast-item.toast-success {
background-color: rgba(var(--toast-magic-success-rgb), var(--toast-item-after-opacity, 1));
}
.toast-container.toast-color-true .toast-item.toast-info {
background-color: rgba(var(--toast-magic-info-rgb), var(--toast-item-after-opacity, 1));
}
.toast-container.toast-color-true .toast-item.toast-warning {
background-color: rgba(var(--toast-magic-warning-rgb), var(--toast-item-after-opacity, 1));
}
.toast-container.toast-color-true .toast-item.toast-danger {
background-color: rgba(var(--toast-magic-danger-rgb), var(--toast-item-after-opacity, 1));
}
.toast-container.toast-color-true .toast-item.toast-success::before,
.toast-container.toast-color-true .toast-item.toast-info::before,
.toast-container.toast-color-true .toast-item.toast-warning::before,
.toast-container.toast-color-true .toast-item.toast-danger::before {
background-color: rgba(var(--toast-color-mode-progress-bg), var(--toast-item-before-opacity, 0.4));
}
.toast-container.toast-color-true .toast-item.toast-success::after,
.toast-container.toast-color-true .toast-item.toast-info::after,
.toast-container.toast-color-true .toast-item.toast-warning::after,
.toast-container.toast-color-true .toast-item.toast-danger::after {
background-color: rgba(var(--toast-color-mode-progress-bg), var(--toast-item-after-opacity, 1));
}
.toast-container.toast-color-true .toast-body-icon-container,
.toast-container.toast-color-true .toast-close-btn,
.toast-container.toast-color-true .toast-item {
--toast-item-color: #FFFFFF;
--toast-close-btn-color: #FFFFFF;
color: var(--toast-item-color);
}
.toast-container.toast-color-true .toast-custom-btn {
background-color: #FFF;
color: #000;
}
.toast-container.toast-color-true .toast-custom-btn:hover {
border: 1px solid rgba(var(--toast-color-mode-progress-bg), var(--toast-item-before-opacity, 0.4));
}
.toast-container.toast-color-true .toast-item-content-center .toast-body h4,
.toast-container.toast-color-true .toast-item-content-center .toast-body p {
letter-spacing: 1px;
}
.toast-item-content-center {
display: flex;
align-items: start;
justify-content: space-between;
gap: .5rem !important;
}
.toast-item-content-center .toast-body {
padding: 0;
word-wrap: break-word;
display: flex;
gap: .5rem !important;
}
.toast-item-content-center .toast-body .toast-body-container {
display: flex;
flex-direction: column;
gap: .25rem;
}
.toast-item-content-center .toast-body-icon-container {
inline-size: 25px;
block-size: 25px;
padding: 2px;
font-size: 18px;
border-radius: 0.3125rem;
line-height: 1;
display: flex;
justify-content: center;
align-items: center;
}
.toast-item-content-center .toast-body-title {
display: flex;
justify-content: start;
align-items: center;
min-height: 22px;
line-height: 1;
}
.toast-item-content-center .toast-body h4 {
font-size: 0.875rem;
font-weight: 500;
margin: 0;
line-height: 1.15rem;
}
.toast-item-content-center .toast-body p {
font-size: 0.75rem;
font-weight: 400;
margin: 0;
padding: 0;
white-space: pre-line;
}
.toast-item-content-center .toast-body-end {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: flex-end;
gap: .65rem;
}
.toast-item-content-center .fi {
line-height: 1;
}
.toast-close-btn {
background: transparent;
border: 0;
font-size: 1rem;
color: var(--toast-close-btn-color);
width: 1.25rem;
cursor: pointer;
z-index: 99999;
display: flex;
align-items: start;
opacity: .65;
transition: opacity .25s ease-in-out;
padding: 0;
margin: 0;
}
.toast-close-btn:hover {
opacity: 1;
}
.toast-container .toast-custom-btn {
--toast-custom-btn-bg-rgb: var(--toast-magic-success-rgb);
font-size: 0.75rem;
white-space: nowrap;
background-color: rgba(var(--toast-custom-btn-bg-rgb), 0.2);
color: var(--toast-custom-btn-color);
transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out;
padding: 0.35rem 0.675rem;
max-width: 100px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
text-decoration: none;
border-radius: .275rem;
font-weight: 600;
border: 1px solid transparent;
}
.toast-container .toast-custom-btn:hover {
border: 1px solid rgba(var(--toast-custom-btn-bg-rgb), 1);
}
.toast-container .toast-custom-btn.toast-btn-bg-success {
--toast-custom-btn-bg-rgb: var(--toast-magic-success-rgb);
}
.toast-container .toast-custom-btn.toast-btn-bg-info {
--toast-custom-btn-bg-rgb: var(--toast-magic-info-rgb);
}
.toast-container .toast-custom-btn.toast-btn-bg-danger {
--toast-custom-btn-bg-rgb: var(--toast-magic-danger-rgb);
}
.toast-container .toast-custom-btn.toast-btn-bg-warning {
--toast-custom-btn-bg-rgb: var(--toast-magic-warning-rgb);
}
[dir=rtl] .toast-item.hide {
transform: translateX(-100%);
}
[dir=rtl] .toast-item .end-0 {
right: unset !important;
left: 0 !important;
}
[dir="rtl"] .toast-container.toast-top-start .toast-item,
[dir="rtl"] .toast-container.toast-bottom-start .toast-item {
transform: translateX(110%);
}
[dir="rtl"] .toast-container.toast-top-end .toast-item,
[dir="rtl"] .toast-container.toast-bottom-end .toast-item {
transform: translateX(-110%);
}
@media (max-width: 575px) {
.toast-container {
inset-inline-end: 12px;
}
}
@keyframes toastProgress {
from {
width: var(--toast-progress-start, 0%);
}
to {
width: var(--toast-progress-end, 100%);
}
}
.toast-container .toast-item.show,
[dir="rtl"] .toast-container .toast-item.show {
transform: translate(0, 0);
opacity: 1;
}
.toast-container .toast-item.hide,
[dir="rtl"] .toast-container .toast-item.hide {
transform: translateX(100%);
opacity: 0;
}
@keyframes toastProgressReverse {
from {
width: var(--toast-progress-end, 100%);
}
to {
width: var(--toast-progress-start, 0%);
}
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

View File

@@ -0,0 +1,199 @@
(function () {
// ===============================
// Utility: Close toast function
// ===============================
function closeToastMagicItem(toast) {
toast.classList.remove("show");
setTimeout(() => {
toast.remove();
}, 500);
}
// ===============================
// Utility: Icon generator
// ===============================
function getToasterIcon(key = null) {
if (key?.toString() === 'success') {
return `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve" width="250" height="25"><g><path fill="currentColor" d="M405.333,0H106.667C47.786,0.071,0.071,47.786,0,106.667v298.667C0.071,464.214,47.786,511.93,106.667,512h298.667 C464.214,511.93,511.93,464.214,512,405.333V106.667C511.93,47.786,464.214,0.071,405.333,0z M426.667,172.352L229.248,369.771 c-16.659,16.666-43.674,16.671-60.34,0.012c-0.004-0.004-0.008-0.008-0.012-0.012l-83.563-83.541 c-8.348-8.348-8.348-21.882,0-30.229s21.882-8.348,30.229,0l83.541,83.541l197.44-197.419c8.348-8.318,21.858-8.294,30.176,0.053 C435.038,150.524,435.014,164.034,426.667,172.352z"/></g></svg>
`;
} else if (key?.toString() === 'error') {
return `<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24" width="25" height="25"><path fill="currentColor" d="m19,0H5C2.243,0,0,2.243,0,5v14c0,2.757,2.243,5,5,5h14c2.757,0,5-2.243,5-5V5c0-2.757-2.243-5-5-5Zm-1.231,6.641l-4.466,5.359,4.466,5.359c.354.425.296,1.056-.128,1.409-.188.155-.414.231-.64.231-.287,0-.571-.122-.77-.359l-4.231-5.078-4.231,5.078c-.198.237-.482.359-.77.359-.226,0-.452-.076-.64-.231-.424-.354-.481-.984-.128-1.409l4.466-5.359-4.466-5.359c-.354-.425-.296-1.056.128-1.409.426-.353,1.056-.296,1.409.128l4.231,5.078,4.231-5.078c.354-.424.983-.48,1.409-.128.424.354.481.984.128,1.409Z"/></svg>`;
} else if (key?.toString() === 'info') {
return `<svg fill="currentColor" width="25px" height="25px" viewBox="0 0 1920 1920" xmlns="http://www.w3.org/2000/svg">
<path d="M960 0c530.193 0 960 429.807 960 960s-429.807 960-960 960S0 1490.193 0 960 429.807 0 960 0Zm223.797 707.147c-28.531-29.561-67.826-39.944-109.227-39.455-55.225.657-114.197 20.664-156.38 40.315-100.942 47.024-178.395 130.295-242.903 219.312-11.616 16.025-17.678 34.946 2.76 49.697 17.428 12.58 29.978 1.324 40.49-9.897l.69-.74c.801-.862 1.591-1.72 2.37-2.565 11.795-12.772 23.194-25.999 34.593-39.237l2.85-3.31 2.851-3.308c34.231-39.687 69.056-78.805 115.144-105.345 27.4-15.778 47.142 8.591 42.912 35.963-2.535 16.413-11.165 31.874-17.2 47.744-21.44 56.363-43.197 112.607-64.862 168.888-23.74 61.7-47.405 123.425-70.426 185.398l-2 5.38-1.998 5.375c-20.31 54.64-40.319 108.872-53.554 165.896-10.575 45.592-24.811 100.906-4.357 145.697 11.781 25.8 36.77 43.532 64.567 47.566 37.912 5.504 78.906 6.133 116.003-2.308 19.216-4.368 38.12-10.07 56.57-17.005 56.646-21.298 108.226-54.146 154.681-92.755 47.26-39.384 88.919-85.972 126.906-134.292 12.21-15.53 27.004-32.703 31.163-52.596 3.908-18.657-12.746-45.302-34.326-34.473-11.395 5.718-19.929 19.867-28.231 29.27-10.42 11.798-21.044 23.423-31.786 34.92-21.488 22.987-43.513 45.463-65.634 67.831-13.54 13.692-30.37 25.263-47.662 33.763-21.59 10.609-38.785-1.157-36.448-25.064 2.144-21.954 7.515-44.145 15.046-64.926 30.306-83.675 61.19-167.135 91.834-250.686 19.157-52.214 38.217-104.461 56.999-156.816 17.554-48.928 32.514-97.463 38.834-149.3 4.357-35.71-4.9-72.647-30.269-98.937Zm63.72-401.498c-91.342-35.538-200.232 25.112-218.574 121.757-13.25 69.784 13.336 131.23 67.998 157.155 105.765 50.16 232.284-29.954 232.29-147.084.005-64.997-28.612-111.165-81.715-131.828Z" fill-rule="evenodd"/>
</svg>`;
} else if (key?.toString() === 'warning') {
return `<svg fill="currentColor" width="25px" height="25px" viewBox="0 0 52 52" xmlns="http://www.w3.org/2000/svg"><path d="m51.4 42.5l-22.9-37c-1.2-2-3.8-2-5 0l-22.9 37c-1.4 2.3 0 5.5 2.5 5.5h45.8c2.5 0 4-3.2 2.5-5.5z m-25.4-2.5c-1.7 0-3-1.3-3-3s1.3-3 3-3 3 1.3 3 3-1.3 3-3 3z m3-9c0 0.6-0.4 1-1 1h-4c-0.6 0-1-0.4-1-1v-13c0-0.6 0.4-1 1-1h4c0.6 0 1 0.4 1 1v13z"></path></svg>`;
} else if (key?.toString() === 'close') {
return `<svg width="14px" height="14px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166 4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166 19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289 17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z" fill="currentColor"/></svg>`;
} else {
return `<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24" width="28" height="28"><path fill="currentColor" d="m19,0H5C2.243,0,0,2.243,0,5v14c0,2.757,2.243,5,5,5h14c2.757,0,5-2.243,5-5V5c0-2.757-2.243-5-5-5Zm-8,6c0-.553.447-1,1-1s1,.447,1,1v7.5c0,.553-.447,1-1,1s-1-.447-1-1v-7.5Zm1,13c-.828,0-1.5-.672-1.5-1.5s.672-1.5,1.5-1.5,1.5.672,1.5,1.5-.672,1.5-1.5,1.5Z"/></svg>`;
}
}
// ===============================
// ToastMagic Class Definition
// ===============================
if (typeof window.ToastMagic === "undefined") {
window.ToastMagic = class ToastMagic {
constructor() {
const config = window.toastMagicConfig || {};
this.toastMagicPosition = config.positionClass || "toast-top-end";
this.toastMagicCloseButton = config.closeButton || false;
this.toastMagicTheme = config.theme || 'default';
this.toastContainer = document.querySelector(".toast-container");
if (!this.toastContainer) {
this.toastContainer = document.createElement("div");
this.toastContainer.classList.add("toast-container");
document.body.appendChild(this.toastContainer);
}
this.toastContainer.className = "toast-container " + this.toastMagicPosition + " theme-" + this.toastMagicTheme;
}
show({
type,
heading,
description = "",
showCloseBtn = this.toastMagicCloseButton,
customBtnText = "",
customBtnLink = ""
}) {
let toastClass, toastClassBasic;
switch (type) {
case "success":
toastClass = "toast-success";
toastClassBasic = "success";
break;
case "error":
toastClass = "toast-danger";
toastClassBasic = "danger";
break;
case "warning":
toastClass = "toast-warning";
toastClassBasic = "warning";
break;
case "info":
default:
toastClass = "toast-info";
toastClassBasic = "info";
}
const toast = document.createElement("div");
toast.classList.add("toast-item", toastClass);
toast.setAttribute("role", "alert");
toast.setAttribute("aria-live", "assertive");
toast.setAttribute("aria-atomic", "true");
toast.innerHTML = `
<div class="theme-ios-toast-item-border"></div>
<div class="position-relative">
<div class="toast-item-content-center">
<div class="toast-body">
<span class="toast-body-icon-container toast-text-${toastClassBasic}">
${getToasterIcon(type)}
</span>
<div class="toast-body-container">
${heading ? `<div class="toast-body-title"><h4>${heading}</h4></div>` : ''}
${description ? `<p class="fs-12">${description}</p>` : ''}
</div>
</div>
<div class="toast-body-end">
${showCloseBtn ? `<button type="button" class="toast-close-btn">${getToasterIcon('close')}</button>` : ""}
${customBtnText && customBtnLink ? `<a href="${customBtnLink}" class="toast-custom-btn toast-btn-bg-${toastClassBasic}">${customBtnText}</a>` : ""}
</div>
</div>
</div>`;
const cfg = window.toastMagicConfig || {};
const toastMagicPosition = cfg.positionClass || "toast-top-end";
const toastMagicShowDuration = cfg?.showDuration || 100;
const toastMagicTimeOut = cfg?.timeOut || 5000;
if (
toastMagicPosition.includes('bottom')
) {
this.toastContainer.append(toast);
} else {
this.toastContainer.prepend(toast);
}
setTimeout(() => toast.classList.add("show"), toastMagicShowDuration);
setTimeout(() => closeToastMagicItem(toast), toastMagicTimeOut);
}
success(...args) {
this.show({ type: "success", ...this._parseArgs(args) });
}
error(...args) {
this.show({ type: "error", ...this._parseArgs(args) });
}
warning(...args) {
this.show({ type: "warning", ...this._parseArgs(args) });
}
info(...args) {
this.show({ type: "info", ...this._parseArgs(args) });
}
_parseArgs(args) {
const [heading = "", description = "", showCloseBtn = false, customBtnText = "", customBtnLink = ""] = args;
return { heading, description, showCloseBtn, customBtnText, customBtnLink };
}
};
}
// ===============================
// Initialize Instance Once
// ===============================
if (typeof window.toastMagic === "undefined") {
window.toastMagic = new window.ToastMagic();
}
// ===============================
// DOM Ready: Setup Container + Events
// ===============================
document.addEventListener("DOMContentLoaded", function () {
const config = window.toastMagicConfig || {};
const toastMagicPosition = config.positionClass || "toast-top-end";
if (!document.querySelector(".toast-container")) {
document.body.insertAdjacentHTML(
"afterbegin",
`<div><div class="toast-container ${toastMagicPosition}"></div></div>`
);
}
// Listen for toast trigger buttons
document.body.addEventListener("click", function (event) {
const btn = event.target.closest("[data-toast-type]");
if (!btn) return;
const type = btn.getAttribute("data-toast-type");
const heading = btn.getAttribute("data-toast-heading") || "Notification";
const description = btn.getAttribute("data-toast-description") || "";
const showCloseBtn = btn.hasAttribute("data-toast-close-btn");
const customBtnText = btn.getAttribute("data-toast-btn-text") || "";
const customBtnLink = btn.getAttribute("data-toast-btn-link") || "";
if (window.toastMagic[type]) {
window.toastMagic[type](heading, description, showCloseBtn, customBtnText, customBtnLink);
} else {
window.toastMagic.info(heading, description, showCloseBtn, customBtnText, customBtnLink);
}
});
// Listen for toast close buttons
document.body.addEventListener("click", function (event) {
const closeBtn = event.target.closest(".toast-close-btn");
if (closeBtn) {
const toast = closeBtn.closest(".toast-item");
if (toast) closeToastMagicItem(toast);
}
});
});
})();

View File

@@ -0,0 +1,204 @@
(function () {
// ===============================
// Utility: Close toast function
// ===============================
function closeToastMagicItem(toast) {
toast.classList.remove("show");
setTimeout(() => {
toast.remove();
}, 500);
}
// ===============================
// Utility: Icon generator
// ===============================
function getToasterIcon(key = null) {
if (key?.toString() === 'success') {
return `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve" width="28" height="28">
<g><path fill="currentColor" d="M405.333,0H106.667C47.786,0.071,0.071,47.786,0,106.667v298.667C0.071,464.214,47.786,511.93,106.667,512h298.667 C464.214,511.93,511.93,464.214,512,405.333V106.667C511.93,47.786,464.214,0.071,405.333,0z M426.667,172.352L229.248,369.771 c-16.659,16.666-43.674,16.671-60.34,0.012c-0.004-0.004-0.008-0.008-0.012-0.012l-83.563-83.541 c-8.348-8.348-8.348-21.882,0-30.229s21.882-8.348,30.229,0l83.541,83.541l197.44-197.419c8.348-8.318,21.858-8.294,30.176,0.053 C435.038,150.524,435.014,164.034,426.667,172.352z"/></g></svg>
`;
} else if (key?.toString() === 'error') {
return `<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24" width="28" height="28"><path fill="currentColor" d="m19,0H5C2.243,0,0,2.243,0,5v14c0,2.757,2.243,5,5,5h14c2.757,0,5-2.243,5-5V5c0-2.757-2.243-5-5-5Zm-1.231,6.641l-4.466,5.359,4.466,5.359c.354.425.296,1.056-.128,1.409-.188.155-.414.231-.64.231-.287,0-.571-.122-.77-.359l-4.231-5.078-4.231,5.078c-.198.237-.482.359-.77.359-.226,0-.452-.076-.64-.231-.424-.354-.481-.984-.128-1.409l4.466-5.359-4.466-5.359c-.354-.425-.296-1.056.128-1.409.426-.353,1.056-.296,1.409.128l4.231,5.078,4.231-5.078c.354-.424.983-.48,1.409-.128.424.354.481.984.128,1.409Z"/></svg>`;
} else if (key?.toString() === 'warning') {
return `<svg fill="currentColor" width="28px" height="28px" viewBox="0 0 52 52" xmlns="http://www.w3.org/2000/svg"><path d="m51.4 42.5l-22.9-37c-1.2-2-3.8-2-5 0l-22.9 37c-1.4 2.3 0 5.5 2.5 5.5h45.8c2.5 0 4-3.2 2.5-5.5z m-25.4-2.5c-1.7 0-3-1.3-3-3s1.3-3 3-3 3 1.3 3 3-1.3 3-3 3z m3-9c0 0.6-0.4 1-1 1h-4c-0.6 0-1-0.4-1-1v-13c0-0.6 0.4-1 1-1h4c0.6 0 1 0.4 1 1v13z"></path></svg>`;
} else if (key?.toString() === 'close') {
return `<svg width="14px" height="14px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166 4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166 19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289 17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z" fill="currentColor"/></svg>`;
} else {
return `<svg fill="currentColor" width="28px" height="28px" viewBox="0 0 1920 1920" xmlns="http://www.w3.org/2000/svg">
<path d="M960 0c530.193 0 960 429.807 960 960s-429.807 960-960 960S0 1490.193 0 960 429.807 0 960 0Zm223.797 707.147c-28.531-29.561-67.826-39.944-109.227-39.455-55.225.657-114.197 20.664-156.38 40.315-100.942 47.024-178.395 130.295-242.903 219.312-11.616 16.025-17.678 34.946 2.76 49.697 17.428 12.58 29.978 1.324 40.49-9.897l.69-.74c.801-.862 1.591-1.72 2.37-2.565 11.795-12.772 23.194-25.999 34.593-39.237l2.85-3.31 2.851-3.308c34.231-39.687 69.056-78.805 115.144-105.345 27.4-15.778 47.142 8.591 42.912 35.963-2.535 16.413-11.165 31.874-17.2 47.744-21.44 56.363-43.197 112.607-64.862 168.888-23.74 61.7-47.405 123.425-70.426 185.398l-2 5.38-1.998 5.375c-20.31 54.64-40.319 108.872-53.554 165.896-10.575 45.592-24.811 100.906-4.357 145.697 11.781 25.8 36.77 43.532 64.567 47.566 37.912 5.504 78.906 6.133 116.003-2.308 19.216-4.368 38.12-10.07 56.57-17.005 56.646-21.298 108.226-54.146 154.681-92.755 47.26-39.384 88.919-85.972 126.906-134.292 12.21-15.53 27.004-32.703 31.163-52.596 3.908-18.657-12.746-45.302-34.326-34.473-11.395 5.718-19.929 19.867-28.231 29.27-10.42 11.798-21.044 23.423-31.786 34.92-21.488 22.987-43.513 45.463-65.634 67.831-13.54 13.692-30.37 25.263-47.662 33.763-21.59 10.609-38.785-1.157-36.448-25.064 2.144-21.954 7.515-44.145 15.046-64.926 30.306-83.675 61.19-167.135 91.834-250.686 19.157-52.214 38.217-104.461 56.999-156.816 17.554-48.928 32.514-97.463 38.834-149.3 4.357-35.71-4.9-72.647-30.269-98.937Zm63.72-401.498c-91.342-35.538-200.232 25.112-218.574 121.757-13.25 69.784 13.336 131.23 67.998 157.155 105.765 50.16 232.284-29.954 232.29-147.084.005-64.997-28.612-111.165-81.715-131.828Z" fill-rule="evenodd"/>
</svg>`;
}
}
// ===============================
// ToastMagic Class Definition
// ===============================
if (typeof window.ToastMagic === "undefined") {
window.ToastMagic = class ToastMagic {
constructor() {
this.setupConfig();
this.initToastContainer();
}
setupConfig() {
const config = window.toastMagicConfig || {};
this.toastMagicPosition = config.positionClass || "toast-top-end";
this.toastMagicCloseButton = config.closeButton || false;
this.toastMagicTheme = config.theme || "default";
}
initToastContainer() {
this.toastContainer = document.querySelector(".toast-container");
if (!this.toastContainer) {
this.toastContainer = document.createElement("div");
this.toastContainer.classList.add("toast-container");
document.body.appendChild(this.toastContainer);
}
const classList = `toast-container ${this.toastMagicPosition} theme-${this.toastMagicTheme}`;
if (this.toastContainer.className !== classList) {
this.toastContainer.className = classList;
}
}
show({ type, heading, description = "", showCloseBtn = this.toastMagicCloseButton, customBtnText = "", customBtnLink = "" }) {
this.setupConfig();
this.initToastContainer();
let toastClass, toastClassBasic;
switch (type) {
case "success":
toastClass = "toast-success";
toastClassBasic = "success";
break;
case "error":
toastClass = "toast-danger";
toastClassBasic = "danger";
break;
case "warning":
toastClass = "toast-warning";
toastClassBasic = "warning";
break;
case "info":
default:
toastClass = "toast-info";
toastClassBasic = "info";
}
const toast = document.createElement("div");
toast.classList.add("toast-item", toastClass);
toast.setAttribute("role", "alert");
toast.setAttribute("aria-live", "assertive");
toast.setAttribute("aria-atomic", "true");
toast.innerHTML = `
<div class="theme-ios-toast-item-border"></div>
<div class="position-relative">
<div class="toast-item-content-center">
<div class="toast-body">
<span class="toast-body-icon-container toast-text-${toastClassBasic}">
${getToasterIcon(type)}
</span>
<div class="toast-body-container">
${heading ? `<div class="toast-body-title"><h4>${heading}</h4></div>` : ''}
${description ? `<p class="fs-12">${description}</p>` : ''}
</div>
</div>
<div class="toast-body-end">
${showCloseBtn ? `<button type="button" class="toast-close-btn">${getToasterIcon('close')}</button>` : ""}
${customBtnText && customBtnLink ? `<a href="${customBtnLink}" class="toast-custom-btn toast-btn-bg-${toastClassBasic}">${customBtnText}</a>` : ""}
</div>
</div>
</div>`;
const cfg = window.toastMagicConfig || {};
const toastMagicPosition = cfg.positionClass || "toast-top-end";
const toastMagicShowDuration = cfg?.showDuration || 100;
const toastMagicTimeOut = cfg?.timeOut || 5000;
if (
toastMagicPosition === 'toast-bottom-end' ||
toastMagicPosition === 'toast-bottom-start' ||
toastMagicPosition === 'toast-top-center'
) {
this.toastContainer.append(toast);
} else {
this.toastContainer.prepend(toast);
}
setTimeout(() => toast.classList.add("show"), toastMagicShowDuration);
setTimeout(() => closeToastMagicItem(toast), toastMagicTimeOut);
}
success(...args) {
this.show({ type: "success", ...this._parseArgs(args) });
}
error(...args) {
this.show({ type: "error", ...this._parseArgs(args) });
}
warning(...args) {
this.show({ type: "warning", ...this._parseArgs(args) });
}
info(...args) {
this.show({ type: "info", ...this._parseArgs(args) });
}
_parseArgs(args) {
const [heading = "", description = "", showCloseBtn = false, customBtnText = "", customBtnLink = ""] = args;
return { heading, description, showCloseBtn, customBtnText, customBtnLink };
}
};
}
// ===============================
// Initialize Instance Once
// ===============================
if (typeof window.toastMagic === "undefined") {
window.toastMagic = new window.ToastMagic();
}
// ===============================
// DOM Ready: Setup Container + Events
// ===============================
document.addEventListener("DOMContentLoaded", function () {
const config = window.toastMagicConfig || {};
const toastMagicPosition = config.positionClass || "toast-top-end";
if (!document.querySelector(".toast-container")) {
document.body.insertAdjacentHTML(
"afterbegin",
`<div><div class="toast-container ${toastMagicPosition}"></div></div>`
);
}
// Listen for toast trigger buttons
document.body.addEventListener("click", function (event) {
const btn = event.target.closest("[data-toast-type]");
if (!btn) return;
const type = btn.getAttribute("data-toast-type");
const heading = btn.getAttribute("data-toast-heading") || "Notification";
const description = btn.getAttribute("data-toast-description") || "";
const showCloseBtn = btn.hasAttribute("data-toast-close-btn");
const customBtnText = btn.getAttribute("data-toast-btn-text") || "";
const customBtnLink = btn.getAttribute("data-toast-btn-link") || "";
if (window.toastMagic[type]) {
window.toastMagic[type](heading, description, showCloseBtn, customBtnText, customBtnLink);
} else {
window.toastMagic.info(heading, description, showCloseBtn, customBtnText, customBtnLink);
}
});
// Listen for toast close buttons
document.body.addEventListener("click", function (event) {
const closeBtn = event.target.closest(".toast-close-btn");
if (closeBtn) {
const toast = closeBtn.closest(".toast-item");
if (toast) closeToastMagicItem(toast);
}
});
});
})();

View File

@@ -0,0 +1,43 @@
if (!window._toastMagicBound) {
window._toastQueue = [];
window._toastProcessing = false;
const processToastQueue = () => {
if (window._toastQueue.length === 0) {
window._toastProcessing = false;
return;
}
const toast = window._toastQueue.shift();
const { status, title, message, showCloseBtn, customBtnText, customBtnLink } = toast;
if (typeof toastMagic[status] === 'function') {
toastMagic[status](title, message, showCloseBtn, customBtnText, customBtnLink);
} else {
console.warn(`Unknown toast status: ${status}, defaulting to success.`);
toastMagic.success(title, message);
}
setTimeout(processToastQueue, 1000); // Wait 500ms before processing next
};
window.addEventListener('toastMagic', event => {
const detail = event.detail || {};
const status = detail.status ?? 'success';
const title = detail.title ?? 'Success!';
const message = detail.message ?? 'Your data has been saved!';
const showCloseBtn = detail?.options?.showCloseBtn ?? false;
const customBtnText = detail?.options?.customBtnText ?? '';
const customBtnLink = detail?.options?.customBtnLink ?? 'javascript:';
window._toastQueue.push({ status, title, message, showCloseBtn, customBtnText, customBtnLink });
if (!window._toastProcessing) {
window._toastProcessing = true;
processToastQueue();
}
});
window._toastMagicBound = true;
}

View File

@@ -0,0 +1,5 @@
<?php
return [
'version' => '2.0',
];

View File

@@ -0,0 +1,125 @@
<?php
use Livewire\Component;
use Illuminate\Support\Facades\Auth;
new class extends Component
{
public bool $isOpen = false;
public string $name = '';
public string $email = '';
public function openModal(): void
{
$user = Auth::user();
$this->name = (string) ($user?->name ?? '');
$this->email = (string) ($user?->email ?? '');
$this->isOpen = true;
}
public function closeModal(): void
{
$this->isOpen = false;
}
public function save(): void
{
$user = Auth::user();
$this->validate([
'name' => ['required', 'string', 'min:2', 'max:60'],
]);
$user->forceFill([
'name' => $this->name,
])->save();
$this->dispatch('toastMagic',
status: 'success',
title: 'Profile updated',
message: 'Your profile has been saved.'
);
$this->isOpen = false;
}
};
?>
<div class="relative">
<button type="button" wire:click="openModal" class="btn-primary w-full text-base">
Edit profile
</button>
@if($isOpen)
<div class="fixed inset-0 z-[9999]" role="dialog" aria-modal="true" wire:keydown.escape.window="closeModal">
<button
type="button"
class="absolute inset-0 bg-black/50"
wire:click="closeModal"
aria-label="Close modal"
></button>
<div class="absolute inset-0 bg-card">
<div class="mx-auto flex h-full w-full max-w-[420px] flex-col px-6 pt-6 pb-8">
<div class="flex items-center justify-between">
<div>
<h3 class="text-lg font-semibold">Edit profile</h3>
<p class="mt-1 text-xs text-muted-foreground">Update your display name.</p>
</div>
<button
type="button"
wire:click="closeModal"
class="grid h-10 w-10 place-items-center rounded-xl bg-background ring-1 ring-border"
aria-label="Close"
>
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</button>
</div>
<div class="mt-6 flex flex-col gap-3">
<label class="rounded-2xl bg-background p-4 ring-1 ring-border">
<p class="text-xs text-muted-foreground">Name</p>
<input
type="text"
wire:model.defer="name"
autocomplete="name"
class="mt-2 w-full rounded bg-transparent text-sm outline-none"
placeholder="Your name"
/>
@error('name')
<p class="mt-2 text-xs text-red-500">{{ $message }}</p>
@enderror
</label>
<label class="rounded-2xl bg-background p-4 ring-1 ring-border opacity-70">
<p class="text-xs text-muted-foreground">Email</p>
<input
type="email"
value="{{ auth()->user()->email }}"
disabled
class="mt-2 w-full rounded bg-transparent text-sm outline-none"
/>
</label>
</div>
<div class="mt-auto pt-6 flex flex-col gap-3">
<button type="button" wire:click="save" wire:loading.attr="disabled" class="btn-primary w-full text-base">
<span wire:loading.remove wire:target="save">Save</span>
<span wire:loading wire:target="save">Saving...</span>
</button>
<button type="button" wire:click="closeModal" class="btn-outline w-full text-base">
Cancel
</button>
</div>
</div>
</div>
</div>
@endif
</div>

View File

@@ -8,6 +8,7 @@
@vite(['resources/css/app.css', 'resources/js/app.js']) @vite(['resources/css/app.css', 'resources/js/app.js'])
@livewireStyles @livewireStyles
{!! ToastMagic::styles() !!}
</head> </head>
<body class="min-h-dvh bg-background text-foreground"> <body class="min-h-dvh bg-background text-foreground">
@@ -22,5 +23,6 @@
</div> </div>
@livewireScripts @livewireScripts
{!! ToastMagic::scripts() !!}
</body> </body>
</html> </html>

View File

@@ -0,0 +1,41 @@
<?php
use Livewire\Component;
new class extends Component
{
//
};
?>
<div class="flex flex-col gap-6 px-4 pt-6 pb-4">
<div>
<p class="text-sm text-muted-foreground">Welcome back,</p>
<h1 class="text-2xl font-bold text-foreground">Alex Rivera</h1>
</div>
<div class="grid grid-cols-3 gap-3">
<div class="flex flex-col items-center gap-1 rounded-xl border border-border bg-card p-3">
<div class="grid h-10 w-10 place-items-center rounded-xl bg-primary/15 text-primary-foreground mb-2">
<x-bi-people class="h-6 w-6 text-primary" />
</div>
<span class="font-bold text-xl">2</span>
<span class="text-muted-foreground text-sm">Groups</span>
</div>
<div class="flex flex-col items-center gap-1 rounded-xl border border-border bg-card p-3">
<div class="grid h-10 w-10 place-items-center rounded-xl bg-accent/15 text-primary-foreground mb-2">
<x-solar-medal-ribbon-linear class="h-6 w-6 text-accent" />
</div>
<span class="font-bold text-xl">2</span>
<span class="text-muted-foreground text-sm">Groups</span>
</div>
<div class="flex flex-col items-center gap-1 rounded-xl border border-border bg-card p-3">
<div class="grid h-10 w-10 place-items-center rounded-xl bg-card-foreground/15 text-primary-foreground mb-2">
<x-phosphor-trend-up class="h-6 w-6 text-card-foreground" />
</div>
<span class="font-bold text-xl">2</span>
<span class="text-muted-foreground text-sm">Groups</span>
</div>
</div>
</div>

View File

@@ -0,0 +1,13 @@
<?php
use Livewire\Component;
new class extends Component
{
//
};
?>
<div>
{{-- Order your soul. Reduce your wants. - Augustine --}}
</div>

View File

@@ -0,0 +1,13 @@
<?php
use Livewire\Component;
new class extends Component
{
//
};
?>
<div>
{{-- Knowing is not enough; we must apply. Being willing is not enough; we must do. - Leonardo da Vinci --}}
</div>

View File

@@ -31,13 +31,6 @@ new class extends Component
<a href="{{ route('register') }}" wire:navigate class="btn-primary w-full text-base"> <a href="{{ route('register') }}" wire:navigate class="btn-primary w-full text-base">
Get Started Get Started
</a> </a>
<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit" class="btn-primary w-full text-base">
Logout
</button>
</form>
<a href="{{ route('login') }}" wire:navigate class="btn-outline w-full text-base"> <a href="{{ route('login') }}" wire:navigate class="btn-outline w-full text-base">
I already have an account I already have an account
</a> </a>
@@ -109,24 +102,24 @@ new class extends Component
</div> </div>
<div class="aspect-square rounded-2xl bg-card ring-1 ring-border p-4 text-sm"> <div class="aspect-square rounded-2xl bg-card ring-1 ring-border p-4 text-sm">
<div class="grid h-10 w-10 place-items-center rounded-xl bg-primary/15 text-primary-foreground shadow-sm mb-2"> <div class="grid h-10 w-10 place-items-center rounded-xl bg-primary/15 text-primary-foreground shadow-sm mb-2">
<x-bi-people class="h-6 w-6 text-primary" /> <x-solar-medal-ribbon-linear class="h-6 w-6 text-primary" />
</div> </div>
<b>Patches</b> <b>Patches</b>
<p class="text-muted-foreground">Private crews with invite codes</p> <p class="text-muted-foreground">Custom badges with requirements</p>
</div> </div>
<div class="aspect-square rounded-2xl bg-card ring-1 ring-border p-4 text-sm"> <div class="aspect-square rounded-2xl bg-card ring-1 ring-border p-4 text-sm">
<div class="grid h-10 w-10 place-items-center rounded-xl bg-primary/15 text-primary-foreground shadow-sm mb-2"> <div class="grid h-10 w-10 place-items-center rounded-xl bg-primary/15 text-primary-foreground shadow-sm mb-2">
<x-bi-people class="h-6 w-6 text-primary" /> <x-bi-shield class="h-6 w-6 text-primary" />
</div> </div>
<b>Your sash</b> <b>Your sash</b>
<p class="text-muted-foreground">Private crews with invite codes</p> <p class="text-muted-foreground">Show off everything you earned</p>
</div> </div>
<div class="aspect-square rounded-2xl bg-card ring-1 ring-border p-4 text-sm"> <div class="aspect-square rounded-2xl bg-card ring-1 ring-border p-4 text-sm">
<div class="grid h-10 w-10 place-items-center rounded-xl bg-primary/15 text-primary-foreground shadow-sm mb-2"> <div class="grid h-10 w-10 place-items-center rounded-xl bg-primary/15 text-primary-foreground shadow-sm mb-2">
<x-feathericon-target class="h-6 w-6 text-primary" /> <x-feathericon-target class="h-6 w-6 text-primary" />
</div> </div>
<b>Progress</b> <b>Progress</b>
<p class="text-muted-foreground">Private crews with invite codes</p> <p class="text-muted-foreground">Track steps toward each patch</p>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,132 @@
<?php
use Illuminate\Support\Facades\Auth;
use Livewire\Component;
use App\Models\Setting;
new class extends Component
{
public bool $notifications = false;
public bool $public = false;
public function saveSettings()
{
Setting::updateOrCreate(['user_id' => Auth::id(), 'key' => 'notifications'], ['value' => $this->notifications]);
Setting::updateOrCreate(['user_id' => Auth::id(), 'key' => 'public'], ['value' => $this->public]);
$this->dispatch('toastMagic',
status: 'success',
title: 'Settings saved',
message: 'The settings have succesfully been saved.'
);
}
};
?>
<div class="px-6 pt-16 pb-10">
<div class="mx-auto max-w-[420px]">
<div class="text-center">
<div class="mx-auto flex items-center justify-center gap-2">
<div class="grid h-12 w-12 place-items-center rounded-xl bg-primary text-primary-foreground shadow-sm">
<svg class="h-7 w-7" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M12 2l7 4v6c0 5-3 9-7 10-4-1-7-5-7-10V6l7-4Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
</svg>
</div>
<span class="text-2xl font-bold tracking-tight">PatchBook</span>
</div>
<h1 class="mt-6 text-3xl font-bold leading-tight tracking-tight text-balance">
Account
</h1>
<p class="mx-auto mt-2 max-w-[320px] text-sm leading-relaxed text-muted-foreground">
Your profile and security settings.
</p>
</div>
<div class="mt-8 rounded-2xl bg-card p-5 ring-1 ring-border">
<div class="flex items-center gap-4">
<div class="grid h-14 w-14 place-items-center rounded-2xl bg-primary/15 ring-1 ring-border">
<span class="text-lg font-bold text-primary">
{{ strtoupper(substr(auth()->user()->name ?? 'U', 0, 1)) }}
</span>
</div>
<div class="min-w-0 flex-1">
<h2 class="truncate text-lg font-semibold leading-tight">
{{ auth()->user()->name }}
</h2>
<p class="truncate text-sm text-muted-foreground">
{{ auth()->user()->email }}
</p>
</div>
</div>
<div class="mt-5">
<div class="rounded-2xl bg-background p-4 ring-1 ring-border text-center">
<p class="text-xs text-muted-foreground">Member since</p>
<p class="mt-1 text-sm font-semibold">
{{ optional(auth()->user()->created_at)->format('d M Y') ?? 'Unknown' }}
</p>
</div>
</div>
<div class="mt-5 flex flex-col gap-3">
<livewire:profile.edit />
@if(Auth::user()->password)
<button type="button" class="btn-outline w-full text-base">
Change password
</button>
@endif
<form action="{{ route('logout') }}" method="post">
@csrf
<button type="submit" class="btn-outline w-full text-base">
Logout
</button>
</form>
</div>
</div>
<hr class="mt-6 mb-6 border-t border-muted-foreground opacity-20">
<div class="rounded-2xl bg-card p-5 ring-1 ring-border">
<h3 class="text-base font-semibold">Preferences</h3>
<div class="mt-4 flex flex-col gap-3">
<label class="flex items-center justify-between rounded-2xl bg-background px-4 py-3 ring-1 ring-border">
<div class="flex items-center gap-3">
<div class="grid h-10 w-10 place-items-center rounded-xl bg-primary/15">
<svg class="h-5 w-5 text-primary" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M12 22a2 2 0 0 0 2-2H10a2 2 0 0 0 2 2Z" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M18 16v-5a6 6 0 1 0-12 0v5l-2 2h16l-2-2Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
</svg>
</div>
<div>
<p class="text-sm font-semibold leading-tight">Notifications</p>
<p class="text-xs text-muted-foreground">Email updates and reminders</p>
</div>
</div>
<input wire:change.debounce="saveSettings()" wire:model="notifications" type="checkbox" class="h-5 w-5 rounded border-muted-foreground/30" />
</label>
<label class="flex items-center justify-between rounded-2xl bg-background px-4 py-3 ring-1 ring-border">
<div class="flex items-center gap-3">
<div class="grid h-10 w-10 place-items-center rounded-xl bg-primary/15">
<svg class="h-5 w-5 text-primary" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M12 3v18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M3 12h18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</div>
<div>
<p class="text-sm font-semibold leading-tight">Public profile</p>
<p class="text-xs text-muted-foreground">Let others see your name</p>
</div>
</div>
<input wire:change.debounce="saveSettings()" wire:model="public" type="checkbox" class="h-5 w-5 rounded border-muted-foreground/30" />
</label>
</div>
</div>
</div>
</div>

View File

@@ -5,6 +5,11 @@ use Illuminate\Support\Facades\Route;
Route::livewire('/', 'pages::landing'); Route::livewire('/', 'pages::landing');
Route::middleware(['auth'])->group(function () {
Route::livewire('/profile', 'pages::profile');
Route::livewire('/dashboard', 'pages::dashboard');
});
Route::get('/login', function () { Route::get('/login', function () {
return view('login'); return view('login');
})->name('login'); })->name('login');

View File

@@ -11,6 +11,14 @@ export default defineConfig({
tailwindcss(), tailwindcss(),
], ],
server: { server: {
host: '127.0.0.1',
port: 5173,
strictPort: true,
hmr: {
host: 'sendit.test',
protocol: 'ws',
port: 5173,
},
watch: { watch: {
ignored: ['**/storage/framework/views/**'], ignored: ['**/storage/framework/views/**'],
}, },