├── database ├── .gitignore ├── seeders │ └── DatabaseSeeder.php ├── migrations │ ├── 0001_01_01_000001_create_cache_table.php │ ├── 0001_01_01_000000_create_users_table.php │ └── 0001_01_01_000002_create_jobs_table.php └── factories │ └── UserFactory.php ├── bootstrap ├── cache │ └── .gitignore ├── providers.php └── app.php ├── storage ├── logs │ └── .gitignore ├── app │ ├── private │ │ └── .gitignore │ ├── public │ │ └── .gitignore │ └── .gitignore └── framework │ ├── testing │ └── .gitignore │ ├── views │ └── .gitignore │ ├── cache │ ├── data │ │ └── .gitignore │ └── .gitignore │ ├── sessions │ └── .gitignore │ └── .gitignore ├── public ├── robots.txt ├── favicon.ico ├── images │ ├── user │ │ └── owner.jpg │ ├── error │ │ ├── 404.svg │ │ └── 404-dark.svg │ ├── logo │ │ ├── logo-icon.svg │ │ ├── logo-dark.svg │ │ ├── logo.svg │ │ └── auth-logo.svg │ └── shape │ │ └── grid-01.svg ├── index.php └── .htaccess ├── tailadmin-laravel.png ├── tests ├── Unit │ └── ExampleTest.php ├── Feature │ ├── ExampleTest.php │ ├── DashboardTest.php │ ├── Auth │ │ ├── RegistrationTest.php │ │ ├── PasswordConfirmationTest.php │ │ ├── AuthenticationTest.php │ │ ├── EmailVerificationTest.php │ │ └── PasswordResetTest.php │ └── Settings │ │ ├── PasswordUpdateTest.php │ │ └── ProfileUpdateTest.php ├── TestCase.php └── Pest.php ├── resources ├── js │ ├── app.js │ └── bootstrap.js └── views │ ├── layouts │ ├── backdrop.blade.php │ ├── sidebar-widget.blade.php │ ├── fullscreen-layout.blade.php │ └── app.blade.php │ ├── dashboard.blade.php │ ├── components │ ├── common │ │ ├── common-grid-shape.blade.php │ │ ├── preloader.blade.php │ │ ├── component-card.blade.php │ │ ├── table-dropdown.blade.php │ │ ├── page-breadcrumb.blade.php │ │ ├── theme-toggle.blade.php │ │ └── dropdown-menu.blade.php │ ├── forms │ │ └── input.blade.php │ ├── layouts │ │ └── settings.blade.php │ ├── ui │ │ ├── button.blade.php │ │ └── alert.blade.php │ ├── header │ │ └── user-dropdown.blade.php │ └── ecommerce │ │ └── ecommerce-metrics.blade.php │ ├── pages │ ├── profile.blade.php │ └── auth │ │ ├── settings │ │ └── profile.blade.php │ │ ├── verify-email.blade.php │ │ └── forgot-password.blade.php │ ├── errors │ └── 404.blade.php │ └── users │ ├── edit.blade.php │ └── index.blade.php ├── app ├── Http │ ├── Controllers │ │ ├── Controller.php │ │ ├── DashboardController.php │ │ ├── Auth │ │ │ ├── PasswordResetLinkController.php │ │ │ ├── ConfirmationController.php │ │ │ ├── RegistrationController.php │ │ │ ├── VerificationController.php │ │ │ ├── NewPasswordController.php │ │ │ └── LoginController.php │ │ ├── UserController.php │ │ └── Settings │ │ │ ├── PasswordController.php │ │ │ └── ProfileController.php │ └── Requests │ │ └── UpdateUserRequest.php ├── Providers │ └── AppServiceProvider.php └── Models │ └── User.php ├── .gitattributes ├── routes ├── console.php ├── web.php └── auth.php ├── .editorconfig ├── vite.config.js ├── .gitignore ├── artisan ├── package.json ├── LICENSE ├── .github └── workflows │ ├── lint.yml │ └── tests.yml ├── config ├── services.php ├── filesystems.php ├── cache.php ├── mail.php ├── queue.php ├── auth.php ├── app.php ├── logging.php ├── database.php └── session.php ├── phpunit.xml ├── .env.example ├── README.md └── composer.json /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /storage/app/private/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !private/ 3 | !public/ 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /bootstrap/providers.php: -------------------------------------------------------------------------------- 1 | toBeTrue(); 5 | }); 6 | -------------------------------------------------------------------------------- /public/images/user/owner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaravelDaily/TailAdmin-Laravel-Starter-Kit/HEAD/public/images/user/owner.jpg -------------------------------------------------------------------------------- /resources/js/app.js: -------------------------------------------------------------------------------- 1 | import './bootstrap'; 2 | import Alpine from 'alpinejs'; 3 | 4 | window.Alpine = Alpine; 5 | 6 | Alpine.start(); 7 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | compiled.php 2 | config.php 3 | down 4 | events.scanned.php 5 | maintenance.php 6 | routes.php 7 | routes.scanned.php 8 | schedule-* 9 | services.json 10 | -------------------------------------------------------------------------------- /tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/'); 5 | 6 | $response->assertStatus(200); 7 | }); 8 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 8 | })->purpose('Display an inspiring quote'); 9 | -------------------------------------------------------------------------------- /resources/views/dashboard.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 |
5 |
6 | 7 |
8 |
9 | @endsection 10 | -------------------------------------------------------------------------------- /app/Http/Controllers/DashboardController.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | grid 4 |
5 |
6 | grid 7 |
8 | 9 | -------------------------------------------------------------------------------- /resources/views/components/common/preloader.blade.php: -------------------------------------------------------------------------------- 1 |
6 |
9 |
10 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | handleCommand(new ArgvInput); 17 | 18 | exit($status); 19 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | name('home'); 9 | 10 | Route::middleware(['auth'])->group(function () { 11 | Route::get('/dashboard', function () { 12 | return view('dashboard', ['title' => 'Dashboard']); 13 | })->name('dashboard'); 14 | 15 | Route::resource('users', UserController::class); 16 | }); 17 | 18 | require __DIR__.'/auth.php'; 19 | -------------------------------------------------------------------------------- /resources/views/pages/profile.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 | 5 |
6 |

Profile

7 | 8 | 9 | 10 |
11 | @endsection 12 | -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 17 | 18 | User::factory()->create([ 19 | 'name' => 'Test User', 20 | 'email' => 'test@example.com', 21 | ]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | withRouting( 9 | web: __DIR__.'/../routes/web.php', 10 | commands: __DIR__.'/../routes/console.php', 11 | health: '/up', 12 | ) 13 | ->withMiddleware(function (Middleware $middleware): void { 14 | // 15 | }) 16 | ->withExceptions(function (Exceptions $exceptions): void { 17 | // 18 | })->create(); 19 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | handleRequest(Request::capture()); 21 | -------------------------------------------------------------------------------- /tests/Feature/DashboardTest.php: -------------------------------------------------------------------------------- 1 | get('/dashboard')->assertRedirect('/login'); 16 | } 17 | 18 | public function test_authenticated_users_can_visit_the_dashboard(): void 19 | { 20 | $this->actingAs($user = User::factory()->create()); 21 | 22 | $this->get('/dashboard')->assertStatus(200); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /resources/views/layouts/sidebar-widget.blade.php: -------------------------------------------------------------------------------- 1 |
2 |

3 | #1 Tailwind CSS Dashboard 4 |

5 |

6 | Leading Tailwind CSS Admin Template with 500+ UI Component and Pages. 7 |

8 | 10 | Purchase Plan 11 | 12 |
13 | -------------------------------------------------------------------------------- /resources/views/components/common/component-card.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'title', 3 | 'desc' => '', 4 | ]) 5 | 6 |
merge(['class' => 'rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]']) }}> 7 | 8 |
9 |

10 | {{ $title }} 11 |

12 | @if($desc) 13 |

14 | {{ $desc }} 15 |

16 | @endif 17 |
18 | 19 | 20 |
21 |
22 | {{ $slot }} 23 |
24 |
25 |
-------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Handle X-XSRF-Token Header 13 | RewriteCond %{HTTP:x-xsrf-token} . 14 | RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}] 15 | 16 | # Redirect Trailing Slashes If Not A Folder... 17 | RewriteCond %{REQUEST_FILENAME} !-d 18 | RewriteCond %{REQUEST_URI} (.+)/$ 19 | RewriteRule ^ %1 [L,R=301] 20 | 21 | # Send Requests To Front Controller... 22 | RewriteCond %{REQUEST_FILENAME} !-d 23 | RewriteCond %{REQUEST_FILENAME} !-f 24 | RewriteRule ^ index.php [L] 25 | 26 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/PasswordResetLinkController.php: -------------------------------------------------------------------------------- 1 | validate([ 21 | 'email' => ['required', 'email'], 22 | ]); 23 | 24 | Password::sendResetLink($request->only('email')); 25 | 26 | return back()->with('status', __('A reset link will be sent if the account exists.')); 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /tests/Feature/Auth/RegistrationTest.php: -------------------------------------------------------------------------------- 1 | get('/register'); 15 | 16 | $response->assertStatus(200); 17 | } 18 | 19 | public function test_new_users_can_register(): void 20 | { 21 | $response = $this->post('/register', [ 22 | 'name' => 'Test User', 23 | 'email' => 'test@example.com', 24 | 'password' => 'password', 25 | 'password_confirmation' => 'password', 26 | ]); 27 | 28 | $this->assertAuthenticated(); 29 | $response->assertRedirect(route('dashboard', absolute: false)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Http/Requests/UpdateUserRequest.php: -------------------------------------------------------------------------------- 1 | |string> 23 | */ 24 | public function rules(): array 25 | { 26 | return [ 27 | 'name' => ['required', 'string', 'max:255'], 28 | 'email' => ['required', 'email', 'max:255', Rule::unique(User::class)->ignore($this->user->id)], 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Http/Controllers/UserController.php: -------------------------------------------------------------------------------- 1 | update($request->validated()); 34 | 35 | return redirect()->route('users.index')->with('success', 'User updated successfully'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /database/migrations/0001_01_01_000001_create_cache_table.php: -------------------------------------------------------------------------------- 1 | string('key')->primary(); 16 | $table->mediumText('value'); 17 | $table->integer('expiration'); 18 | }); 19 | 20 | Schema::create('cache_locks', function (Blueprint $table) { 21 | $table->string('key')->primary(); 22 | $table->string('owner'); 23 | $table->integer('expiration'); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | */ 30 | public function down(): void 31 | { 32 | Schema::dropIfExists('cache'); 33 | Schema::dropIfExists('cache_locks'); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/package.json", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "build": "vite build", 7 | "dev": "vite" 8 | }, 9 | "devDependencies": { 10 | "@tailwindcss/vite": "^4.1.12", 11 | "axios": "^1.11.0", 12 | "concurrently": "^9.0.1", 13 | "laravel-vite-plugin": "^2.0.0", 14 | "tailwindcss": "^4.1.12", 15 | "vite": "^7.0.4" 16 | }, 17 | "dependencies": { 18 | "@floating-ui/dom": "^1.7.4", 19 | "@fullcalendar/core": "^6.1.19", 20 | "@fullcalendar/daygrid": "^6.1.19", 21 | "@fullcalendar/interaction": "^6.1.19", 22 | "@fullcalendar/list": "^6.1.19", 23 | "@fullcalendar/timegrid": "^6.1.19", 24 | "@popperjs/core": "^2.11.8", 25 | "alpinejs": "^3.14.9", 26 | "apexcharts": "^5.3.5", 27 | "flatpickr": "^4.6.13", 28 | "jsvectormap": "^1.7.0", 29 | "prismjs": "^1.30.0", 30 | "swiper": "^12.0.3" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Http/Controllers/Settings/PasswordController.php: -------------------------------------------------------------------------------- 1 | $request->user(), 18 | ]); 19 | } 20 | 21 | public function update(Request $request): RedirectResponse 22 | { 23 | $validated = $request->validate([ 24 | 'current_password' => ['required', 'current_password'], 25 | 'password' => ['required', Rules\Password::defaults(), 'confirmed'], 26 | ]); 27 | 28 | $request->user()->update([ 29 | 'password' => Hash::make($validated['password']), 30 | ]); 31 | 32 | return back()->with('status', 'password-updated'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ConfirmationController.php: -------------------------------------------------------------------------------- 1 | validate([ 22 | 'email' => $request->user()->email, 23 | 'password' => $request->password, 24 | ])) { 25 | throw ValidationException::withMessages([ 26 | 'password' => __('auth.password'), 27 | ]); 28 | } 29 | 30 | $request->session()->put('auth.password_confirmed_at', time()); 31 | 32 | return redirect()->intended(route('dashboard', absolute: false)); 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 TailAdmin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: linter 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | - main 8 | pull_request: 9 | branches: 10 | - develop 11 | - main 12 | 13 | permissions: 14 | contents: write 15 | 16 | jobs: 17 | quality: 18 | runs-on: ubuntu-latest 19 | environment: Testing 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - name: Setup PHP 24 | uses: shivammathur/setup-php@v2 25 | with: 26 | php-version: "8.4" 27 | 28 | - name: Install Dependencies 29 | run: | 30 | composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist 31 | npm install 32 | 33 | - name: Run Pint 34 | run: vendor/bin/pint 35 | 36 | - name: Run Prettier 37 | run: npx prettier --write . 38 | 39 | # - name: Commit Changes 40 | # uses: stefanzweifel/git-auto-commit-action@v5 41 | # with: 42 | # commit_message: fix code style 43 | # commit_options: '--no-verify' 44 | # file_pattern: | 45 | # **/* 46 | # !.github/workflows/* 47 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'token' => env('POSTMARK_TOKEN'), 19 | ], 20 | 21 | 'resend' => [ 22 | 'key' => env('RESEND_KEY'), 23 | ], 24 | 25 | 'ses' => [ 26 | 'key' => env('AWS_ACCESS_KEY_ID'), 27 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 28 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 29 | ], 30 | 31 | 'slack' => [ 32 | 'notifications' => [ 33 | 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), 34 | 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), 35 | ], 36 | ], 37 | 38 | ]; 39 | -------------------------------------------------------------------------------- /resources/views/components/forms/input.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'label' => null, 3 | 'name', 4 | 'type' => 'text', 5 | 'placeholder' => '', 6 | 'value' => '', 7 | 'required' => false, 8 | 'autofocus' => false, 9 | ]) 10 | 11 | @if ($label) 12 | 15 | @endif 16 | 17 | merge(['class' => 'dark:bg-dark-900 shadow-theme-xs focus:border-brand-300 focus:ring-brand-500/10 dark:focus:border-brand-800 h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 placeholder:text-gray-400 focus:ring-3 focus:outline-hidden dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30']) }} 26 | /> 27 | 28 | @error($name) 29 |

{{ $message }}

30 | @enderror 31 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | - main 8 | pull_request: 9 | branches: 10 | - develop 11 | - main 12 | 13 | jobs: 14 | ci: 15 | runs-on: ubuntu-latest 16 | environment: Testing 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | 22 | - name: Setup PHP 23 | uses: shivammathur/setup-php@v2 24 | with: 25 | php-version: 8.4 26 | tools: composer:v2 27 | coverage: xdebug 28 | 29 | - name: Setup Node 30 | uses: actions/setup-node@v4 31 | with: 32 | node-version: "22" 33 | cache: "npm" 34 | 35 | - name: Install Node Dependencies 36 | run: npm i 37 | 38 | - name: Install Dependencies 39 | run: composer install --no-interaction --prefer-dist --optimize-autoloader 40 | 41 | - name: Copy Environment File 42 | run: cp .env.example .env 43 | 44 | - name: Generate Application Key 45 | run: php artisan key:generate 46 | 47 | - name: Build Assets 48 | run: npm run build 49 | 50 | - name: Run Tests 51 | run: php artisan test 52 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/RegistrationController.php: -------------------------------------------------------------------------------- 1 | validate([ 25 | 'name' => ['required', 'string', 'max:255'], 26 | 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], 27 | 'password' => ['required', 'confirmed', Rules\Password::defaults()], 28 | ]); 29 | 30 | $validated['password'] = Hash::make($validated['password']); 31 | 32 | event(new Registered(($user = User::create($validated)))); 33 | 34 | Auth::login($user); 35 | 36 | return redirect(route('dashboard', absolute: false)); 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class UserFactory extends Factory 13 | { 14 | /** 15 | * The current password being used by the factory. 16 | */ 17 | protected static ?string $password; 18 | 19 | /** 20 | * Define the model's default state. 21 | * 22 | * @return array 23 | */ 24 | public function definition(): array 25 | { 26 | return [ 27 | 'name' => fake()->name(), 28 | 'email' => fake()->unique()->safeEmail(), 29 | 'email_verified_at' => now(), 30 | 'password' => static::$password ??= Hash::make('password'), 31 | 'remember_token' => Str::random(10), 32 | ]; 33 | } 34 | 35 | /** 36 | * Indicate that the model's email address should be unverified. 37 | */ 38 | public function unverified(): static 39 | { 40 | return $this->state(fn (array $attributes) => [ 41 | 'email_verified_at' => null, 42 | ]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Feature/Auth/PasswordConfirmationTest.php: -------------------------------------------------------------------------------- 1 | create(); 16 | 17 | $response = $this->actingAs($user)->get('/confirm-password'); 18 | 19 | $response->assertStatus(200); 20 | } 21 | 22 | public function test_password_can_be_confirmed(): void 23 | { 24 | $user = User::factory()->create(); 25 | 26 | $response = $this->actingAs($user)->post('/confirm-password', [ 27 | 'password' => 'password', 28 | ]); 29 | 30 | $response->assertRedirect(); 31 | $response->assertSessionHasNoErrors(); 32 | } 33 | 34 | public function test_password_is_not_confirmed_with_invalid_password(): void 35 | { 36 | $user = User::factory()->create(); 37 | 38 | $response = $this->actingAs($user)->post('/confirm-password', [ 39 | 'password' => 'wrong-password', 40 | ]); 41 | 42 | $response->assertSessionHasErrors(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | tests/Unit 10 | 11 | 12 | tests/Feature 13 | 14 | 15 | 16 | 17 | app 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=Laravel 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_URL=http://localhost 6 | 7 | APP_LOCALE=en 8 | APP_FALLBACK_LOCALE=en 9 | APP_FAKER_LOCALE=en_US 10 | 11 | APP_MAINTENANCE_DRIVER=file 12 | # APP_MAINTENANCE_STORE=database 13 | 14 | PHP_CLI_SERVER_WORKERS=4 15 | 16 | BCRYPT_ROUNDS=12 17 | 18 | LOG_CHANNEL=stack 19 | LOG_STACK=single 20 | LOG_DEPRECATIONS_CHANNEL=null 21 | LOG_LEVEL=debug 22 | 23 | DB_CONNECTION=sqlite 24 | #DB_HOST=127.0.0.1 25 | #DB_PORT=3306 26 | #DB_DATABASE=tailadmin_laravel 27 | #DB_USERNAME=root 28 | #DB_PASSWORD= 29 | 30 | SESSION_DRIVER=database 31 | SESSION_LIFETIME=120 32 | SESSION_ENCRYPT=false 33 | SESSION_PATH=/ 34 | SESSION_DOMAIN=null 35 | 36 | BROADCAST_CONNECTION=log 37 | FILESYSTEM_DISK=local 38 | QUEUE_CONNECTION=database 39 | 40 | CACHE_STORE=database 41 | # CACHE_PREFIX= 42 | 43 | MEMCACHED_HOST=127.0.0.1 44 | 45 | REDIS_CLIENT=phpredis 46 | REDIS_HOST=127.0.0.1 47 | REDIS_PASSWORD=null 48 | REDIS_PORT=6379 49 | 50 | MAIL_MAILER=log 51 | MAIL_SCHEME=null 52 | MAIL_HOST=127.0.0.1 53 | MAIL_PORT=2525 54 | MAIL_USERNAME=null 55 | MAIL_PASSWORD=null 56 | MAIL_FROM_ADDRESS="hello@example.com" 57 | MAIL_FROM_NAME="${APP_NAME}" 58 | 59 | AWS_ACCESS_KEY_ID= 60 | AWS_SECRET_ACCESS_KEY= 61 | AWS_DEFAULT_REGION=us-east-1 62 | AWS_BUCKET= 63 | AWS_USE_PATH_STYLE_ENDPOINT=false 64 | 65 | VITE_APP_NAME="${APP_NAME}" 66 | -------------------------------------------------------------------------------- /resources/views/components/common/table-dropdown.blade.php: -------------------------------------------------------------------------------- 1 |
28 |
29 | {{ $button }} 30 |
31 | 32 |
33 |
34 | 37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TailAdmin Laravel Starter Kit 2 | 3 | This starter kit is based on [TailAdmin Laravel - Free Laravel Dashboard](https://github.com/TailAdmin/tailadmin-laravel). 4 | 5 | We decided to merge our [LaravelDaily/starter-kit](https://github.com/LaravelDaily/starter-kit) with TailAdmin components. 6 | 7 | As a result, you get full **simple** Laravel Auth (*login, register, forget password, profile*), styled as TailAdmin. 8 | 9 | ![](https://laraveldaily.com/uploads/2025/11/tailadmin-starter-kit-profile.png) 10 | 11 | ![](https://laraveldaily.com/uploads/2025/11/tailadmin-starter-kit-login.png) 12 | 13 | The main point is no React/Vue/Livewire required. Only Blade and Tailwind. 14 | 15 | Also, you're getting an example table/form with two-level menu on the sidebar to manage Users. 16 | 17 | ![](https://laraveldaily.com/uploads/2025/12/tailadmin-starter-kit-users-list.png) 18 | 19 | ![](https://laraveldaily.com/uploads/2025/12/tailadmin-starter-kit-user-edit.png) 20 | 21 | --- 22 | 23 | ## How to use 24 | 25 | To use this kit, you can install it using: 26 | 27 | ``` 28 | laravel new --using=laraveldaily/tailadmin-starter-kit 29 | ``` 30 | 31 | From there, you can modify the kit to your needs and add more pages. 32 | 33 | For more components, TailAdmin theme also has a [Pro version](https://checkout.tailadmin.com/buy/ed68b4bb-f0c6-4d20-a241-d3a5a81b0f25?aff=EEK4LN) (*affiliate link to support my work*) with 500+ components and dashboard variants. 34 | -------------------------------------------------------------------------------- /resources/views/components/common/page-breadcrumb.blade.php: -------------------------------------------------------------------------------- 1 | @props(['pageTitle' => 'Page']) 2 | 3 |
4 |

5 | {{ $pageTitle }} 6 |

7 | 38 |
39 | -------------------------------------------------------------------------------- /tests/Feature/Auth/AuthenticationTest.php: -------------------------------------------------------------------------------- 1 | get('/login'); 16 | 17 | $response->assertStatus(200); 18 | } 19 | 20 | public function test_users_can_authenticate_using_the_login_screen(): void 21 | { 22 | $user = User::factory()->create(); 23 | 24 | $response = $this->post('/login', [ 25 | 'email' => $user->email, 26 | 'password' => 'password', 27 | ]); 28 | 29 | $this->assertAuthenticated(); 30 | $response->assertRedirect(route('dashboard', absolute: false)); 31 | } 32 | 33 | public function test_users_can_not_authenticate_with_invalid_password(): void 34 | { 35 | $user = User::factory()->create(); 36 | 37 | $this->post('/login', [ 38 | 'email' => $user->email, 39 | 'password' => 'wrong-password', 40 | ]); 41 | 42 | $this->assertGuest(); 43 | } 44 | 45 | public function test_users_can_logout(): void 46 | { 47 | $user = User::factory()->create(); 48 | 49 | $response = $this->actingAs($user)->post('/logout'); 50 | 51 | $this->assertGuest(); 52 | $response->assertRedirect('/'); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/Models/User.php: -------------------------------------------------------------------------------- 1 | */ 14 | use HasFactory, Notifiable; 15 | 16 | /** 17 | * The attributes that are mass assignable. 18 | * 19 | * @var list 20 | */ 21 | protected $fillable = [ 22 | 'name', 23 | 'email', 24 | 'password', 25 | ]; 26 | 27 | /** 28 | * The attributes that should be hidden for serialization. 29 | * 30 | * @var list 31 | */ 32 | protected $hidden = [ 33 | 'password', 34 | 'remember_token', 35 | ]; 36 | 37 | /** 38 | * Get the attributes that should be cast. 39 | * 40 | * @return array 41 | */ 42 | protected function casts(): array 43 | { 44 | return [ 45 | 'email_verified_at' => 'datetime', 46 | 'password' => 'hashed', 47 | ]; 48 | } 49 | 50 | /** 51 | * Get the user's initials 52 | */ 53 | public function initials(): string 54 | { 55 | return Str::of($this->name) 56 | ->explode(' ') 57 | ->map(fn (string $name) => Str::of($name)->substr(0, 1)) 58 | ->implode(''); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /resources/views/errors/404.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.fullscreen-layout') 2 | 3 | @section('content') 4 | @php 5 | $currentYear = date('Y'); 6 | @endphp 7 |
8 | {{-- common grid shape --}} 9 | 10 | 11 |
12 |

13 | ERROR 14 |

15 | 16 | 404 17 | 18 | 19 |

20 | We can't seem to find the page you are looking for! 21 |

22 | 23 | 25 | Back to Home Page 26 | 27 |
28 | 29 |

30 | © {{ $currentYear }} - TailAdmin 31 |

32 |
33 | @endsection 34 | -------------------------------------------------------------------------------- /tests/Feature/Settings/PasswordUpdateTest.php: -------------------------------------------------------------------------------- 1 | create(); 17 | 18 | $response = $this 19 | ->actingAs($user) 20 | ->from('/settings/profile') 21 | ->put('/settings/password', [ 22 | 'current_password' => 'password', 23 | 'password' => 'new-password', 24 | 'password_confirmation' => 'new-password', 25 | ]); 26 | 27 | $response 28 | ->assertSessionHasNoErrors() 29 | ->assertRedirect('/settings/profile'); 30 | 31 | $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); 32 | } 33 | 34 | public function test_correct_password_must_be_provided_to_update_password(): void 35 | { 36 | $user = User::factory()->create(); 37 | 38 | $response = $this 39 | ->actingAs($user) 40 | ->from('/settings/profile') 41 | ->put('/settings/password', [ 42 | 'current_password' => 'wrong-password', 43 | 'password' => 'new-password', 44 | 'password_confirmation' => 'new-password', 45 | ]); 46 | 47 | $response 48 | ->assertSessionHasErrors('current_password') 49 | ->assertRedirect('/settings/profile'); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/VerificationController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail() 17 | ? redirect()->intended(route('dashboard', absolute: false)) 18 | : view('pages.auth.verify-email'); 19 | } 20 | 21 | public function store(Request $request): RedirectResponse 22 | { 23 | if ($request->user()->hasVerifiedEmail()) { 24 | return redirect()->intended(route('dashboard', absolute: false)); 25 | } 26 | 27 | $request->user()->sendEmailVerificationNotification(); 28 | 29 | return back()->with('status', 'verification-link-sent'); 30 | } 31 | 32 | public function verify(EmailVerificationRequest $request): RedirectResponse 33 | { 34 | if ($request->user()->hasVerifiedEmail()) { 35 | return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); 36 | } 37 | 38 | if ($request->user()->markEmailAsVerified()) { 39 | /** @var \Illuminate\Contracts\Auth\MustVerifyEmail $user */ 40 | $user = $request->user(); 41 | 42 | event(new Verified($user)); 43 | } 44 | 45 | return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /database/migrations/0001_01_01_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('name'); 17 | $table->string('email')->unique(); 18 | $table->timestamp('email_verified_at')->nullable(); 19 | $table->string('password'); 20 | $table->rememberToken(); 21 | $table->timestamps(); 22 | }); 23 | 24 | Schema::create('password_reset_tokens', function (Blueprint $table) { 25 | $table->string('email')->primary(); 26 | $table->string('token'); 27 | $table->timestamp('created_at')->nullable(); 28 | }); 29 | 30 | Schema::create('sessions', function (Blueprint $table) { 31 | $table->string('id')->primary(); 32 | $table->foreignId('user_id')->nullable()->index(); 33 | $table->string('ip_address', 45)->nullable(); 34 | $table->text('user_agent')->nullable(); 35 | $table->longText('payload'); 36 | $table->integer('last_activity')->index(); 37 | }); 38 | } 39 | 40 | /** 41 | * Reverse the migrations. 42 | */ 43 | public function down(): void 44 | { 45 | Schema::dropIfExists('users'); 46 | Schema::dropIfExists('password_reset_tokens'); 47 | Schema::dropIfExists('sessions'); 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/NewPasswordController.php: -------------------------------------------------------------------------------- 1 | $request]); 21 | } 22 | 23 | public function store(Request $request): RedirectResponse 24 | { 25 | $request->validate([ 26 | 'token' => ['required'], 27 | 'email' => ['required', 'email'], 28 | 'password' => ['required', 'confirmed', Rules\Password::defaults()], 29 | ]); 30 | 31 | $status = Password::reset( 32 | $request->only('email', 'password', 'password_confirmation', 'token'), 33 | function (User $user) use ($request) { 34 | $user->forceFill([ 35 | 'password' => Hash::make($request->password), 36 | 'remember_token' => Str::random(60), 37 | ])->save(); 38 | 39 | event(new PasswordReset($user)); 40 | } 41 | ); 42 | 43 | return $status == Password::PASSWORD_RESET 44 | ? to_route('login')->with('status', __($status)) 45 | : back()->withInput($request->only('email')) 46 | ->withErrors(['email' => __($status)]); 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /tests/Pest.php: -------------------------------------------------------------------------------- 1 | extend(Tests\TestCase::class) 15 | ->use(Illuminate\Foundation\Testing\RefreshDatabase::class) 16 | ->in('Feature'); 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Expectations 21 | |-------------------------------------------------------------------------- 22 | | 23 | | When you're writing tests, you often need to check that values meet certain conditions. The 24 | | "expect()" function gives you access to a set of "expectations" methods that you can use 25 | | to assert different things. Of course, you may extend the Expectation API at any time. 26 | | 27 | */ 28 | 29 | expect()->extend('toBeOne', function () { 30 | return $this->toBe(1); 31 | }); 32 | 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | Functions 36 | |-------------------------------------------------------------------------- 37 | | 38 | | While Pest is very powerful out-of-the-box, you may have some testing code specific to your 39 | | project that you don't want to repeat in every file. Here you can also expose helpers as 40 | | global functions to help you to reduce the number of lines of code in your test files. 41 | | 42 | */ 43 | 44 | function something() 45 | { 46 | // .. 47 | } 48 | -------------------------------------------------------------------------------- /app/Http/Controllers/Settings/ProfileController.php: -------------------------------------------------------------------------------- 1 | $request->user(), 19 | ]); 20 | } 21 | 22 | public function update(Request $request): RedirectResponse 23 | { 24 | $user = $request->user(); 25 | 26 | $validated = $request->validate([ 27 | 'name' => ['required', 'string', 'max:255'], 28 | 'email' => [ 29 | 'required', 30 | 'string', 31 | 'lowercase', 32 | 'email', 33 | 'max:255', 34 | Rule::unique(User::class)->ignore($user->id), 35 | ], 36 | ]); 37 | 38 | $user->fill($validated); 39 | 40 | if ($user->isDirty('email')) { 41 | $user->email_verified_at = null; 42 | } 43 | 44 | $user->save(); 45 | 46 | return to_route('settings.profile.edit')->with('status', __('Profile updated successfully')); 47 | } 48 | 49 | public function destroy(Request $request): RedirectResponse 50 | { 51 | $user = $request->user(); 52 | 53 | Auth::logout(); 54 | 55 | $user->delete(); 56 | 57 | $request->session()->invalidate(); 58 | $request->session()->regenerateToken(); 59 | 60 | return to_route('home'); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /resources/views/components/common/theme-toggle.blade.php: -------------------------------------------------------------------------------- 1 | 31 | -------------------------------------------------------------------------------- /resources/views/components/common/dropdown-menu.blade.php: -------------------------------------------------------------------------------- 1 | @props(['items' => ['View More','Delete']]) 2 |
3 | 11 | 12 |
14 | @forelse($items as $item) 15 | 18 | @empty 19 | {{ $slot }} 20 | @endforelse 21 |
22 |
-------------------------------------------------------------------------------- /resources/views/components/layouts/settings.blade.php: -------------------------------------------------------------------------------- 1 | @props(['title', 'description']) 2 | 3 |
4 |
5 | 6 | 20 | 21 | 22 |
23 |
24 |

{{ $title }}

25 |

{{ $description }}

26 |
27 | 28 | {{ $slot }} 29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /tests/Feature/Auth/EmailVerificationTest.php: -------------------------------------------------------------------------------- 1 | unverified()->create(); 19 | 20 | $response = $this->actingAs($user)->get('/verify-email'); 21 | 22 | $response->assertStatus(200); 23 | } 24 | 25 | public function test_email_can_be_verified(): void 26 | { 27 | $user = User::factory()->unverified()->create(); 28 | 29 | Event::fake(); 30 | 31 | $verificationUrl = URL::temporarySignedRoute( 32 | 'verification.verify', 33 | now()->addMinutes(60), 34 | ['id' => $user->id, 'hash' => sha1($user->email)] 35 | ); 36 | 37 | $response = $this->actingAs($user)->get($verificationUrl); 38 | 39 | Event::assertDispatched(Verified::class); 40 | 41 | $this->assertTrue($user->fresh()->hasVerifiedEmail()); 42 | $response->assertRedirect(route('dashboard', absolute: false).'?verified=1'); 43 | } 44 | 45 | public function test_email_is_not_verified_with_invalid_hash(): void 46 | { 47 | $user = User::factory()->unverified()->create(); 48 | 49 | $verificationUrl = URL::temporarySignedRoute( 50 | 'verification.verify', 51 | now()->addMinutes(60), 52 | ['id' => $user->id, 'hash' => sha1('wrong-email')] 53 | ); 54 | 55 | $this->actingAs($user)->get($verificationUrl); 56 | 57 | $this->assertFalse($user->fresh()->hasVerifiedEmail()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /resources/views/users/edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 | 5 | 6 |
7 |
8 |
9 |

10 | Edit: {{ $user->name }} 11 |

12 |
13 | 14 |
15 | @csrf 16 | @method('PUT') 17 | 18 |
19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 |
27 |
28 | 31 | 32 | 34 | Cancel 35 | 36 |
37 |
38 |
39 |
40 |
41 | @endsection 42 | 43 | -------------------------------------------------------------------------------- /database/migrations/0001_01_01_000002_create_jobs_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('queue')->index(); 17 | $table->longText('payload'); 18 | $table->unsignedTinyInteger('attempts'); 19 | $table->unsignedInteger('reserved_at')->nullable(); 20 | $table->unsignedInteger('available_at'); 21 | $table->unsignedInteger('created_at'); 22 | }); 23 | 24 | Schema::create('job_batches', function (Blueprint $table) { 25 | $table->string('id')->primary(); 26 | $table->string('name'); 27 | $table->integer('total_jobs'); 28 | $table->integer('pending_jobs'); 29 | $table->integer('failed_jobs'); 30 | $table->longText('failed_job_ids'); 31 | $table->mediumText('options')->nullable(); 32 | $table->integer('cancelled_at')->nullable(); 33 | $table->integer('created_at'); 34 | $table->integer('finished_at')->nullable(); 35 | }); 36 | 37 | Schema::create('failed_jobs', function (Blueprint $table) { 38 | $table->id(); 39 | $table->string('uuid')->unique(); 40 | $table->text('connection'); 41 | $table->text('queue'); 42 | $table->longText('payload'); 43 | $table->longText('exception'); 44 | $table->timestamp('failed_at')->useCurrent(); 45 | }); 46 | } 47 | 48 | /** 49 | * Reverse the migrations. 50 | */ 51 | public function down(): void 52 | { 53 | Schema::dropIfExists('jobs'); 54 | Schema::dropIfExists('job_batches'); 55 | Schema::dropIfExists('failed_jobs'); 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /public/images/error/404.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /public/images/error/404-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /resources/views/components/ui/button.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'size' => 'md', 3 | 'variant' => 'primary', 4 | 'startIcon' => null, 5 | 'endIcon' => null, 6 | 'className' => '', 7 | 'disabled' => false, 8 | ]) 9 | 10 | @php 11 | // Base classes 12 | $base = 'inline-flex items-center justify-center font-medium gap-2 rounded-lg transition'; 13 | 14 | // Size map 15 | $sizeMap = [ 16 | 'sm' => 'px-4 py-3 text-sm', 17 | 'md' => 'px-5 py-3.5 text-sm', 18 | ]; 19 | $sizeClass = $sizeMap[$size] ?? $sizeMap['md']; 20 | 21 | // Variant map 22 | $variantMap = [ 23 | 'primary' => 'bg-brand-500 text-white shadow-theme-xs hover:bg-brand-600 disabled:bg-brand-300', 24 | 'outline' => 'bg-white text-gray-700 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-400 dark:ring-gray-700 dark:hover:bg-white/[0.03] dark:hover:text-gray-300', 25 | ]; 26 | $variantClass = $variantMap[$variant] ?? $variantMap['primary']; 27 | 28 | // disabled classes 29 | $disabledClass = $disabled ? 'cursor-not-allowed opacity-50' : ''; 30 | 31 | // final classes (merge user className too) 32 | $classes = trim("{$base} {$sizeClass} {$variantClass} {$className} {$disabledClass}"); 33 | @endphp 34 | 35 | 62 | -------------------------------------------------------------------------------- /tests/Feature/Settings/ProfileUpdateTest.php: -------------------------------------------------------------------------------- 1 | actingAs(User::factory()->create()); 16 | 17 | $this->get('/settings/profile')->assertOk(); 18 | } 19 | 20 | public function test_profile_information_can_be_updated(): void 21 | { 22 | $user = User::factory()->create(); 23 | 24 | $response = $this 25 | ->actingAs($user) 26 | ->put('/settings/profile', [ 27 | 'name' => 'Test User', 28 | 'email' => 'test@example.com', 29 | ]); 30 | 31 | $response 32 | ->assertSessionHasNoErrors() 33 | ->assertRedirect('/settings/profile'); 34 | 35 | $user->refresh(); 36 | 37 | $this->assertSame('Test User', $user->name); 38 | $this->assertSame('test@example.com', $user->email); 39 | $this->assertNull($user->email_verified_at); 40 | } 41 | 42 | public function test_email_verification_status_is_unchanged_when_email_address_is_unchanged(): void 43 | { 44 | $user = User::factory()->create(); 45 | 46 | $response = $this 47 | ->actingAs($user) 48 | ->put('/settings/profile', [ 49 | 'name' => 'Test User', 50 | 'email' => $user->email, 51 | ]); 52 | 53 | $response 54 | ->assertSessionHasNoErrors() 55 | ->assertRedirect('/settings/profile'); 56 | 57 | $this->assertNotNull($user->refresh()->email_verified_at); 58 | } 59 | 60 | public function test_user_can_delete_their_account(): void 61 | { 62 | $user = User::factory()->create(); 63 | 64 | $response = $this 65 | ->actingAs($user) 66 | ->delete('/settings/profile'); 67 | 68 | $response 69 | ->assertSessionHasNoErrors() 70 | ->assertRedirect('/'); 71 | 72 | $this->assertGuest(); 73 | $this->assertNull($user->fresh()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests/Feature/Auth/PasswordResetTest.php: -------------------------------------------------------------------------------- 1 | get('/forgot-password'); 18 | 19 | $response->assertStatus(200); 20 | } 21 | 22 | public function test_reset_password_link_can_be_requested(): void 23 | { 24 | Notification::fake(); 25 | 26 | $user = User::factory()->create(); 27 | 28 | $this->post('/forgot-password', ['email' => $user->email]); 29 | 30 | Notification::assertSentTo($user, ResetPassword::class); 31 | } 32 | 33 | public function test_reset_password_screen_can_be_rendered(): void 34 | { 35 | Notification::fake(); 36 | 37 | $user = User::factory()->create(); 38 | 39 | $this->post('/forgot-password', ['email' => $user->email]); 40 | 41 | Notification::assertSentTo($user, ResetPassword::class, function ($notification) { 42 | $response = $this->get('/reset-password/'.$notification->token); 43 | 44 | $response->assertStatus(200); 45 | 46 | return true; 47 | }); 48 | } 49 | 50 | public function test_password_can_be_reset_with_valid_token(): void 51 | { 52 | Notification::fake(); 53 | 54 | $user = User::factory()->create(); 55 | 56 | $this->post('/forgot-password', ['email' => $user->email]); 57 | 58 | Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) { 59 | $response = $this->post('/reset-password', [ 60 | 'token' => $notification->token, 61 | 'email' => $user->email, 62 | 'password' => 'password', 63 | 'password_confirmation' => 'password', 64 | ]); 65 | 66 | $response 67 | ->assertSessionHasNoErrors() 68 | ->assertRedirect(route('login')); 69 | 70 | return true; 71 | }); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/LoginController.php: -------------------------------------------------------------------------------- 1 | validate([ 25 | 'email' => ['required', 'string', 'email'], 26 | 'password' => ['required', 'string'], 27 | ]); 28 | 29 | $this->ensureIsNotRateLimited($request); 30 | 31 | if (! Auth::attempt($request->only('email', 'password'), $request->boolean('remember'))) { 32 | RateLimiter::hit($this->throttleKey($request)); 33 | 34 | throw ValidationException::withMessages([ 35 | 'email' => trans('auth.failed'), 36 | ]); 37 | } 38 | 39 | RateLimiter::clear($this->throttleKey($request)); 40 | 41 | $request->session()->regenerate(); 42 | 43 | return redirect()->intended(route('dashboard', absolute: false)); 44 | } 45 | 46 | public function destroy(Request $request): RedirectResponse 47 | { 48 | Auth::guard('web')->logout(); 49 | 50 | $request->session()->invalidate(); 51 | 52 | $request->session()->regenerateToken(); 53 | 54 | return redirect('/'); 55 | } 56 | 57 | protected function ensureIsNotRateLimited(Request $request): void 58 | { 59 | if (! RateLimiter::tooManyAttempts($this->throttleKey($request), 5)) { 60 | return; 61 | } 62 | 63 | event(new Lockout($request)); 64 | 65 | $seconds = RateLimiter::availableIn($this->throttleKey($request)); 66 | 67 | throw ValidationException::withMessages([ 68 | 'email' => trans('auth.throttle', [ 69 | 'seconds' => $seconds, 70 | 'minutes' => ceil($seconds / 60), 71 | ]), 72 | ]); 73 | } 74 | 75 | public function throttleKey(Request $request): string 76 | { 77 | return Str::transliterate(Str::lower($request->string('email')).'|'.$request->ip()); 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /routes/auth.php: -------------------------------------------------------------------------------- 1 | group(function () { 13 | Route::get('register', [RegistrationController::class, 'create'])->name('register'); 14 | Route::post('register', [RegistrationController::class, 'store']); 15 | 16 | Route::get('login', [LoginController::class, 'create'])->name('login'); 17 | Route::post('login', [LoginController::class, 'store']); 18 | 19 | Route::get('forgot-password', [PasswordResetLinkController::class, 'create'])->name('password.request'); 20 | Route::post('forgot-password', [PasswordResetLinkController::class, 'store'])->name('password.email'); 21 | 22 | Route::get('reset-password/{token}', [NewPasswordController::class, 'create'])->name('password.reset'); 23 | Route::post('reset-password', [NewPasswordController::class, 'store'])->name('password.store'); 24 | }); 25 | 26 | Route::middleware('auth')->group(function () { 27 | Route::get('verify-email', [VerificationController::class, 'notice'])->name('verification.notice'); 28 | Route::post('verify-email', [VerificationController::class, 'store'])->middleware('throttle:6,1')->name('verification.store'); 29 | Route::get('verify-email/{id}/{hash}', [VerificationController::class, 'verify'])->middleware(['signed', 'throttle:6,1'])->name('verification.verify'); 30 | 31 | Route::get('confirm-password', [ConfirmationController::class, 'create'])->name('password.confirm'); 32 | Route::post('confirm-password', [ConfirmationController::class, 'store'])->name('confirmation.store'); 33 | }); 34 | 35 | Route::middleware(['auth'])->group(function () { 36 | Route::get('settings/profile', [Settings\ProfileController::class, 'edit'])->name('settings.profile.edit'); 37 | Route::put('settings/profile', [Settings\ProfileController::class, 'update'])->name('settings.profile.update'); 38 | Route::delete('settings/profile', [Settings\ProfileController::class, 'destroy'])->name('settings.profile.destroy'); 39 | Route::get('settings/password', [Settings\PasswordController::class, 'edit'])->name('settings.password.edit'); 40 | Route::put('settings/password', [Settings\PasswordController::class, 'update'])->name('settings.password.update'); 41 | }); 42 | 43 | Route::post('logout', [LoginController::class, 'destroy'])->name('logout'); 44 | 45 | -------------------------------------------------------------------------------- /config/filesystems.php: -------------------------------------------------------------------------------- 1 | env('FILESYSTEM_DISK', 'local'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Filesystem Disks 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Below you may configure as many filesystem disks as necessary, and you 24 | | may even configure multiple disks for the same driver. Examples for 25 | | most supported storage drivers are configured here for reference. 26 | | 27 | | Supported drivers: "local", "ftp", "sftp", "s3" 28 | | 29 | */ 30 | 31 | 'disks' => [ 32 | 33 | 'local' => [ 34 | 'driver' => 'local', 35 | 'root' => storage_path('app/private'), 36 | 'serve' => true, 37 | 'throw' => false, 38 | 'report' => false, 39 | ], 40 | 41 | 'public' => [ 42 | 'driver' => 'local', 43 | 'root' => storage_path('app/public'), 44 | 'url' => env('APP_URL').'/storage', 45 | 'visibility' => 'public', 46 | 'throw' => false, 47 | 'report' => false, 48 | ], 49 | 50 | 's3' => [ 51 | 'driver' => 's3', 52 | 'key' => env('AWS_ACCESS_KEY_ID'), 53 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 54 | 'region' => env('AWS_DEFAULT_REGION'), 55 | 'bucket' => env('AWS_BUCKET'), 56 | 'url' => env('AWS_URL'), 57 | 'endpoint' => env('AWS_ENDPOINT'), 58 | 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), 59 | 'throw' => false, 60 | 'report' => false, 61 | ], 62 | 63 | ], 64 | 65 | /* 66 | |-------------------------------------------------------------------------- 67 | | Symbolic Links 68 | |-------------------------------------------------------------------------- 69 | | 70 | | Here you may configure the symbolic links that will be created when the 71 | | `storage:link` Artisan command is executed. The array keys should be 72 | | the locations of the links and the values should be their targets. 73 | | 74 | */ 75 | 76 | 'links' => [ 77 | public_path('storage') => storage_path('app/public'), 78 | ], 79 | 80 | ]; 81 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://getcomposer.org/schema.json", 3 | "name": "laraveldaily/tailadmin-starter-kit", 4 | "type": "project", 5 | "description": "LaravelDaily Starter Kit for the Laravel framework, with TailAdmin components.", 6 | "keywords": [ 7 | "laravel", 8 | "framework" 9 | ], 10 | "license": "MIT", 11 | "require": { 12 | "php": "^8.2", 13 | "laravel/framework": "^12.0", 14 | "laravel/tinker": "^2.10.1" 15 | }, 16 | "require-dev": { 17 | "fakerphp/faker": "^1.23", 18 | "laravel/pail": "^1.2.2", 19 | "laravel/pint": "^1.24", 20 | "laravel/sail": "^1.41", 21 | "mockery/mockery": "^1.6", 22 | "nunomaduro/collision": "^8.6", 23 | "pestphp/pest": "^4.0", 24 | "pestphp/pest-plugin-laravel": "^4.0" 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "App\\": "app/", 29 | "Database\\Factories\\": "database/factories/", 30 | "Database\\Seeders\\": "database/seeders/" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "Tests\\": "tests/" 36 | } 37 | }, 38 | "scripts": { 39 | "post-autoload-dump": [ 40 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", 41 | "@php artisan package:discover --ansi" 42 | ], 43 | "post-update-cmd": [ 44 | "@php artisan vendor:publish --tag=laravel-assets --ansi --force" 45 | ], 46 | "post-root-package-install": [ 47 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" 48 | ], 49 | "post-create-project-cmd": [ 50 | "@php artisan key:generate --ansi", 51 | "@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"", 52 | "@php artisan migrate --graceful --ansi" 53 | ], 54 | "dev": [ 55 | "Composer\\Config::disableProcessTimeout", 56 | "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite --kill-others" 57 | ], 58 | "test": [ 59 | "@php artisan config:clear --ansi", 60 | "@php artisan test" 61 | ] 62 | }, 63 | "extra": { 64 | "laravel": { 65 | "dont-discover": [] 66 | } 67 | }, 68 | "config": { 69 | "optimize-autoloader": true, 70 | "preferred-install": "dist", 71 | "sort-packages": true, 72 | "allow-plugins": { 73 | "pestphp/pest-plugin": true, 74 | "php-http/discovery": true 75 | } 76 | }, 77 | "minimum-stability": "stable", 78 | "prefer-stable": true 79 | } -------------------------------------------------------------------------------- /resources/views/pages/auth/settings/profile.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 | 5 | 6 |
  • 7 | Dashboard 8 |
  • 9 |
  • 10 | Profile 11 |
  • 12 |
    13 |
    14 | 15 | 16 | @if (session('status')) 17 |
    18 | 19 |
    20 | @endif 21 | 22 |
    23 | @csrf 24 | @method('PUT') 25 | 26 | 27 |
    28 | 36 |
    37 | 38 | 39 |
    40 | 47 |
    48 | 49 | 50 |
    51 | 52 | Save 53 | 54 |
    55 |
    56 | 57 | 58 |
    59 |

    Delete account

    60 |

    Delete your account and all of its resources

    61 | 62 |
    64 | @csrf 65 | @method('DELETE') 66 | 71 | Delete account 72 | 73 |
    74 |
    75 |
    76 | @endsection 77 | -------------------------------------------------------------------------------- /public/images/logo/logo-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /config/cache.php: -------------------------------------------------------------------------------- 1 | env('CACHE_STORE', 'database'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Cache Stores 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the cache "stores" for your application as 26 | | well as their drivers. You may even define multiple stores for the 27 | | same cache driver to group types of items stored in your caches. 28 | | 29 | | Supported drivers: "array", "database", "file", "memcached", 30 | | "redis", "dynamodb", "octane", "null" 31 | | 32 | */ 33 | 34 | 'stores' => [ 35 | 36 | 'array' => [ 37 | 'driver' => 'array', 38 | 'serialize' => false, 39 | ], 40 | 41 | 'database' => [ 42 | 'driver' => 'database', 43 | 'connection' => env('DB_CACHE_CONNECTION'), 44 | 'table' => env('DB_CACHE_TABLE', 'cache'), 45 | 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), 46 | 'lock_table' => env('DB_CACHE_LOCK_TABLE'), 47 | ], 48 | 49 | 'file' => [ 50 | 'driver' => 'file', 51 | 'path' => storage_path('framework/cache/data'), 52 | 'lock_path' => storage_path('framework/cache/data'), 53 | ], 54 | 55 | 'memcached' => [ 56 | 'driver' => 'memcached', 57 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), 58 | 'sasl' => [ 59 | env('MEMCACHED_USERNAME'), 60 | env('MEMCACHED_PASSWORD'), 61 | ], 62 | 'options' => [ 63 | // Memcached::OPT_CONNECT_TIMEOUT => 2000, 64 | ], 65 | 'servers' => [ 66 | [ 67 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 68 | 'port' => env('MEMCACHED_PORT', 11211), 69 | 'weight' => 100, 70 | ], 71 | ], 72 | ], 73 | 74 | 'redis' => [ 75 | 'driver' => 'redis', 76 | 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), 77 | 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), 78 | ], 79 | 80 | 'dynamodb' => [ 81 | 'driver' => 'dynamodb', 82 | 'key' => env('AWS_ACCESS_KEY_ID'), 83 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 84 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 85 | 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), 86 | 'endpoint' => env('DYNAMODB_ENDPOINT'), 87 | ], 88 | 89 | 'octane' => [ 90 | 'driver' => 'octane', 91 | ], 92 | 93 | ], 94 | 95 | /* 96 | |-------------------------------------------------------------------------- 97 | | Cache Key Prefix 98 | |-------------------------------------------------------------------------- 99 | | 100 | | When utilizing the APC, database, memcached, Redis, and DynamoDB cache 101 | | stores, there might be other applications using the same cache. For 102 | | that reason, you may prefix every cache key to avoid collisions. 103 | | 104 | */ 105 | 106 | 'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'), 107 | 108 | ]; 109 | -------------------------------------------------------------------------------- /config/mail.php: -------------------------------------------------------------------------------- 1 | env('MAIL_MAILER', 'log'), 18 | 19 | /* 20 | |-------------------------------------------------------------------------- 21 | | Mailer Configurations 22 | |-------------------------------------------------------------------------- 23 | | 24 | | Here you may configure all of the mailers used by your application plus 25 | | their respective settings. Several examples have been configured for 26 | | you and you are free to add your own as your application requires. 27 | | 28 | | Laravel supports a variety of mail "transport" drivers that can be used 29 | | when delivering an email. You may specify which one you're using for 30 | | your mailers below. You may also add additional mailers if needed. 31 | | 32 | | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", 33 | | "postmark", "resend", "log", "array", 34 | | "failover", "roundrobin" 35 | | 36 | */ 37 | 38 | 'mailers' => [ 39 | 40 | 'smtp' => [ 41 | 'transport' => 'smtp', 42 | 'scheme' => env('MAIL_SCHEME'), 43 | 'url' => env('MAIL_URL'), 44 | 'host' => env('MAIL_HOST', '127.0.0.1'), 45 | 'port' => env('MAIL_PORT', 2525), 46 | 'username' => env('MAIL_USERNAME'), 47 | 'password' => env('MAIL_PASSWORD'), 48 | 'timeout' => null, 49 | 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)), 50 | ], 51 | 52 | 'ses' => [ 53 | 'transport' => 'ses', 54 | ], 55 | 56 | 'postmark' => [ 57 | 'transport' => 'postmark', 58 | // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), 59 | // 'client' => [ 60 | // 'timeout' => 5, 61 | // ], 62 | ], 63 | 64 | 'resend' => [ 65 | 'transport' => 'resend', 66 | ], 67 | 68 | 'sendmail' => [ 69 | 'transport' => 'sendmail', 70 | 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), 71 | ], 72 | 73 | 'log' => [ 74 | 'transport' => 'log', 75 | 'channel' => env('MAIL_LOG_CHANNEL'), 76 | ], 77 | 78 | 'array' => [ 79 | 'transport' => 'array', 80 | ], 81 | 82 | 'failover' => [ 83 | 'transport' => 'failover', 84 | 'mailers' => [ 85 | 'smtp', 86 | 'log', 87 | ], 88 | 'retry_after' => 60, 89 | ], 90 | 91 | 'roundrobin' => [ 92 | 'transport' => 'roundrobin', 93 | 'mailers' => [ 94 | 'ses', 95 | 'postmark', 96 | ], 97 | 'retry_after' => 60, 98 | ], 99 | 100 | ], 101 | 102 | /* 103 | |-------------------------------------------------------------------------- 104 | | Global "From" Address 105 | |-------------------------------------------------------------------------- 106 | | 107 | | You may wish for all emails sent by your application to be sent from 108 | | the same address. Here you may specify a name and address that is 109 | | used globally for all emails that are sent by your application. 110 | | 111 | */ 112 | 113 | 'from' => [ 114 | 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), 115 | 'name' => env('MAIL_FROM_NAME', 'Example'), 116 | ], 117 | 118 | ]; 119 | -------------------------------------------------------------------------------- /config/queue.php: -------------------------------------------------------------------------------- 1 | env('QUEUE_CONNECTION', 'database'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Queue Connections 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may configure the connection options for every queue backend 24 | | used by your application. An example configuration is provided for 25 | | each backend supported by Laravel. You're also free to add more. 26 | | 27 | | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'sync' => [ 34 | 'driver' => 'sync', 35 | ], 36 | 37 | 'database' => [ 38 | 'driver' => 'database', 39 | 'connection' => env('DB_QUEUE_CONNECTION'), 40 | 'table' => env('DB_QUEUE_TABLE', 'jobs'), 41 | 'queue' => env('DB_QUEUE', 'default'), 42 | 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90), 43 | 'after_commit' => false, 44 | ], 45 | 46 | 'beanstalkd' => [ 47 | 'driver' => 'beanstalkd', 48 | 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), 49 | 'queue' => env('BEANSTALKD_QUEUE', 'default'), 50 | 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), 51 | 'block_for' => 0, 52 | 'after_commit' => false, 53 | ], 54 | 55 | 'sqs' => [ 56 | 'driver' => 'sqs', 57 | 'key' => env('AWS_ACCESS_KEY_ID'), 58 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 59 | 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), 60 | 'queue' => env('SQS_QUEUE', 'default'), 61 | 'suffix' => env('SQS_SUFFIX'), 62 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 63 | 'after_commit' => false, 64 | ], 65 | 66 | 'redis' => [ 67 | 'driver' => 'redis', 68 | 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), 69 | 'queue' => env('REDIS_QUEUE', 'default'), 70 | 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), 71 | 'block_for' => null, 72 | 'after_commit' => false, 73 | ], 74 | 75 | ], 76 | 77 | /* 78 | |-------------------------------------------------------------------------- 79 | | Job Batching 80 | |-------------------------------------------------------------------------- 81 | | 82 | | The following options configure the database and table that store job 83 | | batching information. These options can be updated to any database 84 | | connection and table which has been defined by your application. 85 | | 86 | */ 87 | 88 | 'batching' => [ 89 | 'database' => env('DB_CONNECTION', 'sqlite'), 90 | 'table' => 'job_batches', 91 | ], 92 | 93 | /* 94 | |-------------------------------------------------------------------------- 95 | | Failed Queue Jobs 96 | |-------------------------------------------------------------------------- 97 | | 98 | | These options configure the behavior of failed queue job logging so you 99 | | can control how and where failed jobs are stored. Laravel ships with 100 | | support for storing failed jobs in a simple file or in a database. 101 | | 102 | | Supported drivers: "database-uuids", "dynamodb", "file", "null" 103 | | 104 | */ 105 | 106 | 'failed' => [ 107 | 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), 108 | 'database' => env('DB_CONNECTION', 'sqlite'), 109 | 'table' => 'failed_jobs', 110 | ], 111 | 112 | ]; 113 | -------------------------------------------------------------------------------- /config/auth.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'guard' => env('AUTH_GUARD', 'web'), 18 | 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), 19 | ], 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Authentication Guards 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Next, you may define every authentication guard for your application. 27 | | Of course, a great default configuration has been defined for you 28 | | which utilizes session storage plus the Eloquent user provider. 29 | | 30 | | All authentication guards have a user provider, which defines how the 31 | | users are actually retrieved out of your database or other storage 32 | | system used by the application. Typically, Eloquent is utilized. 33 | | 34 | | Supported: "session" 35 | | 36 | */ 37 | 38 | 'guards' => [ 39 | 'web' => [ 40 | 'driver' => 'session', 41 | 'provider' => 'users', 42 | ], 43 | ], 44 | 45 | /* 46 | |-------------------------------------------------------------------------- 47 | | User Providers 48 | |-------------------------------------------------------------------------- 49 | | 50 | | All authentication guards have a user provider, which defines how the 51 | | users are actually retrieved out of your database or other storage 52 | | system used by the application. Typically, Eloquent is utilized. 53 | | 54 | | If you have multiple user tables or models you may configure multiple 55 | | providers to represent the model / table. These providers may then 56 | | be assigned to any extra authentication guards you have defined. 57 | | 58 | | Supported: "database", "eloquent" 59 | | 60 | */ 61 | 62 | 'providers' => [ 63 | 'users' => [ 64 | 'driver' => 'eloquent', 65 | 'model' => env('AUTH_MODEL', App\Models\User::class), 66 | ], 67 | 68 | // 'users' => [ 69 | // 'driver' => 'database', 70 | // 'table' => 'users', 71 | // ], 72 | ], 73 | 74 | /* 75 | |-------------------------------------------------------------------------- 76 | | Resetting Passwords 77 | |-------------------------------------------------------------------------- 78 | | 79 | | These configuration options specify the behavior of Laravel's password 80 | | reset functionality, including the table utilized for token storage 81 | | and the user provider that is invoked to actually retrieve users. 82 | | 83 | | The expiry time is the number of minutes that each reset token will be 84 | | considered valid. This security feature keeps tokens short-lived so 85 | | they have less time to be guessed. You may change this as needed. 86 | | 87 | | The throttle setting is the number of seconds a user must wait before 88 | | generating more password reset tokens. This prevents the user from 89 | | quickly generating a very large amount of password reset tokens. 90 | | 91 | */ 92 | 93 | 'passwords' => [ 94 | 'users' => [ 95 | 'provider' => 'users', 96 | 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), 97 | 'expire' => 60, 98 | 'throttle' => 60, 99 | ], 100 | ], 101 | 102 | /* 103 | |-------------------------------------------------------------------------- 104 | | Password Confirmation Timeout 105 | |-------------------------------------------------------------------------- 106 | | 107 | | Here you may define the number of seconds before a password confirmation 108 | | window expires and users are asked to re-enter their password via the 109 | | confirmation screen. By default, the timeout lasts for three hours. 110 | | 111 | */ 112 | 113 | 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), 114 | 115 | ]; 116 | -------------------------------------------------------------------------------- /resources/views/layouts/fullscreen-layout.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {{ $title ?? 'Dashboard' }} | TailAdmin - Laravel Tailwind CSS Admin Dashboard Template 10 | 11 | 12 | @vite(['resources/css/app.css', 'resources/js/app.js']) 13 | 14 | 15 | {{-- --}} 16 | 17 | 18 | 77 | 78 | 79 | 93 | 94 | 95 | 106 | 107 | {{-- preloader --}} 108 | 109 | {{-- preloader end --}} 110 | 111 | @yield('content') 112 | 113 | 114 | 115 | @stack('scripts') 116 | 117 | 118 | -------------------------------------------------------------------------------- /resources/views/users/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 | 5 | 6 |
    7 | @session('success') 8 | 9 | {{ $value }} 10 | 11 | @endsession 12 | 13 |
    14 |
    15 | 16 | 17 | 18 | 21 | 24 | 27 | 30 | 31 | 32 | 33 | @forelse ($users as $user) 34 | 35 | 38 | 41 | 44 | 47 | 60 | 61 | @empty 62 | 63 | 66 | 67 | @endforelse 68 | 69 |
    19 |

    Name

    20 |
    22 |

    Email

    23 |
    25 |

    Created At

    26 |
    28 |

    Updated At

    29 |
    36 |

    {{ $user->name }}

    37 |
    39 |

    {{ $user->email }}

    40 |
    42 |

    {{ $user->created_at }}

    43 |
    45 |

    {{ $user->updated_at }}

    46 |
    48 | 59 |
    64 |

    No users found.

    65 |
    70 |
    71 |
    72 | 73 | @if ($users->hasPages()) 74 |
    75 | {{ $users->links() }} 76 |
    77 | @endif 78 |
    79 | @endsection 80 | 81 | -------------------------------------------------------------------------------- /config/app.php: -------------------------------------------------------------------------------- 1 | env('APP_NAME', 'Laravel'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Application Environment 21 | |-------------------------------------------------------------------------- 22 | | 23 | | This value determines the "environment" your application is currently 24 | | running in. This may determine how you prefer to configure various 25 | | services the application utilizes. Set this in your ".env" file. 26 | | 27 | */ 28 | 29 | 'env' => env('APP_ENV', 'production'), 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Application Debug Mode 34 | |-------------------------------------------------------------------------- 35 | | 36 | | When your application is in debug mode, detailed error messages with 37 | | stack traces will be shown on every error that occurs within your 38 | | application. If disabled, a simple generic error page is shown. 39 | | 40 | */ 41 | 42 | 'debug' => (bool) env('APP_DEBUG', false), 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Application URL 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This URL is used by the console to properly generate URLs when using 50 | | the Artisan command line tool. You should set this to the root of 51 | | the application so that it's available within Artisan commands. 52 | | 53 | */ 54 | 55 | 'url' => env('APP_URL', 'http://localhost'), 56 | 57 | /* 58 | |-------------------------------------------------------------------------- 59 | | Application Timezone 60 | |-------------------------------------------------------------------------- 61 | | 62 | | Here you may specify the default timezone for your application, which 63 | | will be used by the PHP date and date-time functions. The timezone 64 | | is set to "UTC" by default as it is suitable for most use cases. 65 | | 66 | */ 67 | 68 | 'timezone' => 'UTC', 69 | 70 | /* 71 | |-------------------------------------------------------------------------- 72 | | Application Locale Configuration 73 | |-------------------------------------------------------------------------- 74 | | 75 | | The application locale determines the default locale that will be used 76 | | by Laravel's translation / localization methods. This option can be 77 | | set to any locale for which you plan to have translation strings. 78 | | 79 | */ 80 | 81 | 'locale' => env('APP_LOCALE', 'en'), 82 | 83 | 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), 84 | 85 | 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), 86 | 87 | /* 88 | |-------------------------------------------------------------------------- 89 | | Encryption Key 90 | |-------------------------------------------------------------------------- 91 | | 92 | | This key is utilized by Laravel's encryption services and should be set 93 | | to a random, 32 character string to ensure that all encrypted values 94 | | are secure. You should do this prior to deploying the application. 95 | | 96 | */ 97 | 98 | 'cipher' => 'AES-256-CBC', 99 | 100 | 'key' => env('APP_KEY'), 101 | 102 | 'previous_keys' => [ 103 | ...array_filter( 104 | explode(',', (string) env('APP_PREVIOUS_KEYS', '')) 105 | ), 106 | ], 107 | 108 | /* 109 | |-------------------------------------------------------------------------- 110 | | Maintenance Mode Driver 111 | |-------------------------------------------------------------------------- 112 | | 113 | | These configuration options determine the driver used to determine and 114 | | manage Laravel's "maintenance mode" status. The "cache" driver will 115 | | allow maintenance mode to be controlled across multiple machines. 116 | | 117 | | Supported drivers: "file", "cache" 118 | | 119 | */ 120 | 121 | 'maintenance' => [ 122 | 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), 123 | 'store' => env('APP_MAINTENANCE_STORE', 'database'), 124 | ], 125 | 126 | ]; 127 | -------------------------------------------------------------------------------- /config/logging.php: -------------------------------------------------------------------------------- 1 | env('LOG_CHANNEL', 'stack'), 22 | 23 | /* 24 | |-------------------------------------------------------------------------- 25 | | Deprecations Log Channel 26 | |-------------------------------------------------------------------------- 27 | | 28 | | This option controls the log channel that should be used to log warnings 29 | | regarding deprecated PHP and library features. This allows you to get 30 | | your application ready for upcoming major versions of dependencies. 31 | | 32 | */ 33 | 34 | 'deprecations' => [ 35 | 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), 36 | 'trace' => env('LOG_DEPRECATIONS_TRACE', false), 37 | ], 38 | 39 | /* 40 | |-------------------------------------------------------------------------- 41 | | Log Channels 42 | |-------------------------------------------------------------------------- 43 | | 44 | | Here you may configure the log channels for your application. Laravel 45 | | utilizes the Monolog PHP logging library, which includes a variety 46 | | of powerful log handlers and formatters that you're free to use. 47 | | 48 | | Available drivers: "single", "daily", "slack", "syslog", 49 | | "errorlog", "monolog", "custom", "stack" 50 | | 51 | */ 52 | 53 | 'channels' => [ 54 | 55 | 'stack' => [ 56 | 'driver' => 'stack', 57 | 'channels' => explode(',', (string) env('LOG_STACK', 'single')), 58 | 'ignore_exceptions' => false, 59 | ], 60 | 61 | 'single' => [ 62 | 'driver' => 'single', 63 | 'path' => storage_path('logs/laravel.log'), 64 | 'level' => env('LOG_LEVEL', 'debug'), 65 | 'replace_placeholders' => true, 66 | ], 67 | 68 | 'daily' => [ 69 | 'driver' => 'daily', 70 | 'path' => storage_path('logs/laravel.log'), 71 | 'level' => env('LOG_LEVEL', 'debug'), 72 | 'days' => env('LOG_DAILY_DAYS', 14), 73 | 'replace_placeholders' => true, 74 | ], 75 | 76 | 'slack' => [ 77 | 'driver' => 'slack', 78 | 'url' => env('LOG_SLACK_WEBHOOK_URL'), 79 | 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), 80 | 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), 81 | 'level' => env('LOG_LEVEL', 'critical'), 82 | 'replace_placeholders' => true, 83 | ], 84 | 85 | 'papertrail' => [ 86 | 'driver' => 'monolog', 87 | 'level' => env('LOG_LEVEL', 'debug'), 88 | 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), 89 | 'handler_with' => [ 90 | 'host' => env('PAPERTRAIL_URL'), 91 | 'port' => env('PAPERTRAIL_PORT'), 92 | 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), 93 | ], 94 | 'processors' => [PsrLogMessageProcessor::class], 95 | ], 96 | 97 | 'stderr' => [ 98 | 'driver' => 'monolog', 99 | 'level' => env('LOG_LEVEL', 'debug'), 100 | 'handler' => StreamHandler::class, 101 | 'handler_with' => [ 102 | 'stream' => 'php://stderr', 103 | ], 104 | 'formatter' => env('LOG_STDERR_FORMATTER'), 105 | 'processors' => [PsrLogMessageProcessor::class], 106 | ], 107 | 108 | 'syslog' => [ 109 | 'driver' => 'syslog', 110 | 'level' => env('LOG_LEVEL', 'debug'), 111 | 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), 112 | 'replace_placeholders' => true, 113 | ], 114 | 115 | 'errorlog' => [ 116 | 'driver' => 'errorlog', 117 | 'level' => env('LOG_LEVEL', 'debug'), 118 | 'replace_placeholders' => true, 119 | ], 120 | 121 | 'null' => [ 122 | 'driver' => 'monolog', 123 | 'handler' => NullHandler::class, 124 | ], 125 | 126 | 'emergency' => [ 127 | 'path' => storage_path('logs/laravel.log'), 128 | ], 129 | 130 | ], 131 | 132 | ]; 133 | -------------------------------------------------------------------------------- /resources/views/layouts/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {{ $title ?? 'Dashboard' }} | TailAdmin - Laravel Tailwind CSS Admin Dashboard Template 10 | 11 | 12 | @vite(['resources/css/app.css', 'resources/js/app.js']) 13 | 14 | 15 | {{-- --}} 16 | 17 | 18 | 77 | 78 | 79 | 91 | 92 | 93 | 94 | 107 | 108 | {{-- preloader --}} 109 | 110 | {{-- preloader end --}} 111 | 112 |
    113 | @include('layouts.backdrop') 114 | @include('layouts.sidebar') 115 | 116 |
    122 | 123 | @include('layouts.app-header') 124 | 125 |
    126 | @yield('content') 127 |
    128 |
    129 | 130 |
    131 | 132 | 133 | 134 | @stack('scripts') 135 | 136 | 137 | -------------------------------------------------------------------------------- /resources/views/components/header/user-dropdown.blade.php: -------------------------------------------------------------------------------- 1 |
    10 | 11 | 34 | 35 | 36 | 104 | 105 |
    106 | -------------------------------------------------------------------------------- /public/images/shape/grid-01.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /public/images/logo/logo-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /public/images/logo/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /public/images/logo/auth-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /resources/views/components/ui/alert.blade.php: -------------------------------------------------------------------------------- 1 | {{-- resources/views/components/alert.blade.php --}} 2 | 3 | @props([ 4 | 'variant' => 'info', 5 | 'title' => '', 6 | 'message' => '', 7 | 'showLink' => false, 8 | 'linkHref' => '#', 9 | 'linkText' => 'Learn more' 10 | ]) 11 | 12 | @php 13 | $variantClasses = [ 14 | 'success' => [ 15 | 'container' => 'border-green-500 bg-green-50 dark:border-green-500/30 dark:bg-green-500/15', 16 | 'icon' => 'text-green-500', 17 | ], 18 | 'error' => [ 19 | 'container' => 'border-red-500 bg-red-50 dark:border-red-500/30 dark:bg-red-500/15', 20 | 'icon' => 'text-red-500', 21 | ], 22 | 'warning' => [ 23 | 'container' => 'border-yellow-500 bg-yellow-50 dark:border-yellow-500/30 dark:bg-yellow-500/15', 24 | 'icon' => 'text-yellow-500', 25 | ], 26 | 'info' => [ 27 | 'container' => 'border-blue-500 bg-blue-50 dark:border-blue-500/30 dark:bg-blue-500/15', 28 | 'icon' => 'text-blue-500', 29 | ], 30 | ]; 31 | 32 | $icons = [ 33 | 'success' => ' 34 | 35 | ', 36 | 'error' => ' 37 | 38 | ', 39 | 'warning' => ' 40 | 41 | ', 42 | 'info' => ' 43 | 44 | ', 45 | ]; 46 | 47 | $containerClass = $variantClasses[$variant]['container'] ?? $variantClasses['info']['container']; 48 | $iconClass = $variantClasses[$variant]['icon'] ?? $variantClasses['info']['icon']; 49 | $icon = $icons[$variant] ?? $icons['info']; 50 | @endphp 51 | 52 |
    53 |
    54 |
    55 | {!! $icon !!} 56 |
    57 | 58 |
    59 | @if($title) 60 |

    61 | {{ $title }} 62 |

    63 | @endif 64 | 65 | @if($message) 66 |

    {{ $message }}

    67 | @endif 68 | 69 | @if($showLink) 70 | 74 | {{ $linkText }} 75 | 76 | @endif 77 | 78 | {{-- Slot for custom content --}} 79 | {{ $slot }} 80 |
    81 |
    82 |
    -------------------------------------------------------------------------------- /config/database.php: -------------------------------------------------------------------------------- 1 | env('DB_CONNECTION', 'sqlite'), 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Database Connections 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Below are all of the database connections defined for your application. 27 | | An example configuration is provided for each database system which 28 | | is supported by Laravel. You're free to add / remove connections. 29 | | 30 | */ 31 | 32 | 'connections' => [ 33 | 34 | 'sqlite' => [ 35 | 'driver' => 'sqlite', 36 | 'url' => env('DB_URL'), 37 | 'database' => env('DB_DATABASE', database_path('database.sqlite')), 38 | 'prefix' => '', 39 | 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), 40 | 'busy_timeout' => null, 41 | 'journal_mode' => null, 42 | 'synchronous' => null, 43 | ], 44 | 45 | 'mysql' => [ 46 | 'driver' => 'mysql', 47 | 'url' => env('DB_URL'), 48 | 'host' => env('DB_HOST', '127.0.0.1'), 49 | 'port' => env('DB_PORT', '3306'), 50 | 'database' => env('DB_DATABASE', 'laravel'), 51 | 'username' => env('DB_USERNAME', 'root'), 52 | 'password' => env('DB_PASSWORD', ''), 53 | 'unix_socket' => env('DB_SOCKET', ''), 54 | 'charset' => env('DB_CHARSET', 'utf8mb4'), 55 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 56 | 'prefix' => '', 57 | 'prefix_indexes' => true, 58 | 'strict' => true, 59 | 'engine' => null, 60 | 'options' => extension_loaded('pdo_mysql') ? array_filter([ 61 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), 62 | ]) : [], 63 | ], 64 | 65 | 'mariadb' => [ 66 | 'driver' => 'mariadb', 67 | 'url' => env('DB_URL'), 68 | 'host' => env('DB_HOST', '127.0.0.1'), 69 | 'port' => env('DB_PORT', '3306'), 70 | 'database' => env('DB_DATABASE', 'laravel'), 71 | 'username' => env('DB_USERNAME', 'root'), 72 | 'password' => env('DB_PASSWORD', ''), 73 | 'unix_socket' => env('DB_SOCKET', ''), 74 | 'charset' => env('DB_CHARSET', 'utf8mb4'), 75 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 76 | 'prefix' => '', 77 | 'prefix_indexes' => true, 78 | 'strict' => true, 79 | 'engine' => null, 80 | 'options' => extension_loaded('pdo_mysql') ? array_filter([ 81 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), 82 | ]) : [], 83 | ], 84 | 85 | 'pgsql' => [ 86 | 'driver' => 'pgsql', 87 | 'url' => env('DB_URL'), 88 | 'host' => env('DB_HOST', '127.0.0.1'), 89 | 'port' => env('DB_PORT', '5432'), 90 | 'database' => env('DB_DATABASE', 'laravel'), 91 | 'username' => env('DB_USERNAME', 'root'), 92 | 'password' => env('DB_PASSWORD', ''), 93 | 'charset' => env('DB_CHARSET', 'utf8'), 94 | 'prefix' => '', 95 | 'prefix_indexes' => true, 96 | 'search_path' => 'public', 97 | 'sslmode' => 'prefer', 98 | ], 99 | 100 | 'sqlsrv' => [ 101 | 'driver' => 'sqlsrv', 102 | 'url' => env('DB_URL'), 103 | 'host' => env('DB_HOST', 'localhost'), 104 | 'port' => env('DB_PORT', '1433'), 105 | 'database' => env('DB_DATABASE', 'laravel'), 106 | 'username' => env('DB_USERNAME', 'root'), 107 | 'password' => env('DB_PASSWORD', ''), 108 | 'charset' => env('DB_CHARSET', 'utf8'), 109 | 'prefix' => '', 110 | 'prefix_indexes' => true, 111 | // 'encrypt' => env('DB_ENCRYPT', 'yes'), 112 | // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'), 113 | ], 114 | 115 | ], 116 | 117 | /* 118 | |-------------------------------------------------------------------------- 119 | | Migration Repository Table 120 | |-------------------------------------------------------------------------- 121 | | 122 | | This table keeps track of all the migrations that have already run for 123 | | your application. Using this information, we can determine which of 124 | | the migrations on disk haven't actually been run on the database. 125 | | 126 | */ 127 | 128 | 'migrations' => [ 129 | 'table' => 'migrations', 130 | 'update_date_on_publish' => true, 131 | ], 132 | 133 | /* 134 | |-------------------------------------------------------------------------- 135 | | Redis Databases 136 | |-------------------------------------------------------------------------- 137 | | 138 | | Redis is an open source, fast, and advanced key-value store that also 139 | | provides a richer body of commands than a typical key-value system 140 | | such as Memcached. You may define your connection settings here. 141 | | 142 | */ 143 | 144 | 'redis' => [ 145 | 146 | 'client' => env('REDIS_CLIENT', 'phpredis'), 147 | 148 | 'options' => [ 149 | 'cluster' => env('REDIS_CLUSTER', 'redis'), 150 | 'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'), 151 | 'persistent' => env('REDIS_PERSISTENT', false), 152 | ], 153 | 154 | 'default' => [ 155 | 'url' => env('REDIS_URL'), 156 | 'host' => env('REDIS_HOST', '127.0.0.1'), 157 | 'username' => env('REDIS_USERNAME'), 158 | 'password' => env('REDIS_PASSWORD'), 159 | 'port' => env('REDIS_PORT', '6379'), 160 | 'database' => env('REDIS_DB', '0'), 161 | ], 162 | 163 | 'cache' => [ 164 | 'url' => env('REDIS_URL'), 165 | 'host' => env('REDIS_HOST', '127.0.0.1'), 166 | 'username' => env('REDIS_USERNAME'), 167 | 'password' => env('REDIS_PASSWORD'), 168 | 'port' => env('REDIS_PORT', '6379'), 169 | 'database' => env('REDIS_CACHE_DB', '1'), 170 | ], 171 | 172 | ], 173 | 174 | ]; 175 | -------------------------------------------------------------------------------- /config/session.php: -------------------------------------------------------------------------------- 1 | env('SESSION_DRIVER', 'database'), 22 | 23 | /* 24 | |-------------------------------------------------------------------------- 25 | | Session Lifetime 26 | |-------------------------------------------------------------------------- 27 | | 28 | | Here you may specify the number of minutes that you wish the session 29 | | to be allowed to remain idle before it expires. If you want them 30 | | to expire immediately when the browser is closed then you may 31 | | indicate that via the expire_on_close configuration option. 32 | | 33 | */ 34 | 35 | 'lifetime' => (int) env('SESSION_LIFETIME', 120), 36 | 37 | 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false), 38 | 39 | /* 40 | |-------------------------------------------------------------------------- 41 | | Session Encryption 42 | |-------------------------------------------------------------------------- 43 | | 44 | | This option allows you to easily specify that all of your session data 45 | | should be encrypted before it's stored. All encryption is performed 46 | | automatically by Laravel and you may use the session like normal. 47 | | 48 | */ 49 | 50 | 'encrypt' => env('SESSION_ENCRYPT', false), 51 | 52 | /* 53 | |-------------------------------------------------------------------------- 54 | | Session File Location 55 | |-------------------------------------------------------------------------- 56 | | 57 | | When utilizing the "file" session driver, the session files are placed 58 | | on disk. The default storage location is defined here; however, you 59 | | are free to provide another location where they should be stored. 60 | | 61 | */ 62 | 63 | 'files' => storage_path('framework/sessions'), 64 | 65 | /* 66 | |-------------------------------------------------------------------------- 67 | | Session Database Connection 68 | |-------------------------------------------------------------------------- 69 | | 70 | | When using the "database" or "redis" session drivers, you may specify a 71 | | connection that should be used to manage these sessions. This should 72 | | correspond to a connection in your database configuration options. 73 | | 74 | */ 75 | 76 | 'connection' => env('SESSION_CONNECTION'), 77 | 78 | /* 79 | |-------------------------------------------------------------------------- 80 | | Session Database Table 81 | |-------------------------------------------------------------------------- 82 | | 83 | | When using the "database" session driver, you may specify the table to 84 | | be used to store sessions. Of course, a sensible default is defined 85 | | for you; however, you're welcome to change this to another table. 86 | | 87 | */ 88 | 89 | 'table' => env('SESSION_TABLE', 'sessions'), 90 | 91 | /* 92 | |-------------------------------------------------------------------------- 93 | | Session Cache Store 94 | |-------------------------------------------------------------------------- 95 | | 96 | | When using one of the framework's cache driven session backends, you may 97 | | define the cache store which should be used to store the session data 98 | | between requests. This must match one of your defined cache stores. 99 | | 100 | | Affects: "dynamodb", "memcached", "redis" 101 | | 102 | */ 103 | 104 | 'store' => env('SESSION_STORE'), 105 | 106 | /* 107 | |-------------------------------------------------------------------------- 108 | | Session Sweeping Lottery 109 | |-------------------------------------------------------------------------- 110 | | 111 | | Some session drivers must manually sweep their storage location to get 112 | | rid of old sessions from storage. Here are the chances that it will 113 | | happen on a given request. By default, the odds are 2 out of 100. 114 | | 115 | */ 116 | 117 | 'lottery' => [2, 100], 118 | 119 | /* 120 | |-------------------------------------------------------------------------- 121 | | Session Cookie Name 122 | |-------------------------------------------------------------------------- 123 | | 124 | | Here you may change the name of the session cookie that is created by 125 | | the framework. Typically, you should not need to change this value 126 | | since doing so does not grant a meaningful security improvement. 127 | | 128 | */ 129 | 130 | 'cookie' => env( 131 | 'SESSION_COOKIE', 132 | Str::slug(env('APP_NAME', 'laravel')).'-session' 133 | ), 134 | 135 | /* 136 | |-------------------------------------------------------------------------- 137 | | Session Cookie Path 138 | |-------------------------------------------------------------------------- 139 | | 140 | | The session cookie path determines the path for which the cookie will 141 | | be regarded as available. Typically, this will be the root path of 142 | | your application, but you're free to change this when necessary. 143 | | 144 | */ 145 | 146 | 'path' => env('SESSION_PATH', '/'), 147 | 148 | /* 149 | |-------------------------------------------------------------------------- 150 | | Session Cookie Domain 151 | |-------------------------------------------------------------------------- 152 | | 153 | | This value determines the domain and subdomains the session cookie is 154 | | available to. By default, the cookie will be available to the root 155 | | domain and all subdomains. Typically, this shouldn't be changed. 156 | | 157 | */ 158 | 159 | 'domain' => env('SESSION_DOMAIN'), 160 | 161 | /* 162 | |-------------------------------------------------------------------------- 163 | | HTTPS Only Cookies 164 | |-------------------------------------------------------------------------- 165 | | 166 | | By setting this option to true, session cookies will only be sent back 167 | | to the server if the browser has a HTTPS connection. This will keep 168 | | the cookie from being sent to you when it can't be done securely. 169 | | 170 | */ 171 | 172 | 'secure' => env('SESSION_SECURE_COOKIE'), 173 | 174 | /* 175 | |-------------------------------------------------------------------------- 176 | | HTTP Access Only 177 | |-------------------------------------------------------------------------- 178 | | 179 | | Setting this value to true will prevent JavaScript from accessing the 180 | | value of the cookie and the cookie will only be accessible through 181 | | the HTTP protocol. It's unlikely you should disable this option. 182 | | 183 | */ 184 | 185 | 'http_only' => env('SESSION_HTTP_ONLY', true), 186 | 187 | /* 188 | |-------------------------------------------------------------------------- 189 | | Same-Site Cookies 190 | |-------------------------------------------------------------------------- 191 | | 192 | | This option determines how your cookies behave when cross-site requests 193 | | take place, and can be used to mitigate CSRF attacks. By default, we 194 | | will set this value to "lax" to permit secure cross-site requests. 195 | | 196 | | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value 197 | | 198 | | Supported: "lax", "strict", "none", null 199 | | 200 | */ 201 | 202 | 'same_site' => env('SESSION_SAME_SITE', 'lax'), 203 | 204 | /* 205 | |-------------------------------------------------------------------------- 206 | | Partitioned Cookies 207 | |-------------------------------------------------------------------------- 208 | | 209 | | Setting this value to true will tie the cookie to the top-level site for 210 | | a cross-site context. Partitioned cookies are accepted by the browser 211 | | when flagged "secure" and the Same-Site attribute is set to "none". 212 | | 213 | */ 214 | 215 | 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), 216 | 217 | ]; 218 | -------------------------------------------------------------------------------- /resources/views/pages/auth/verify-email.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.fullscreen-layout') 2 | 3 | @section('content') 4 |
    5 |
    6 | 7 |
    8 | 17 |
    18 |
    19 |
    20 |
    21 | 22 | 23 | 24 |
    25 |

    26 | Verify Your Email 27 |

    28 |

    29 | Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn't receive the email, we will gladly send you another. 30 |

    31 |
    32 |
    33 | @if (session('status') == 'verification-link-sent') 34 |
    35 | A new verification link has been sent to the email address you provided during registration. 36 |
    37 | @endif 38 |
    39 |
    40 | @csrf 41 | 45 |
    46 |
    47 | @csrf 48 | 52 |
    53 |
    54 |
    55 |
    56 |
    57 |
    58 | 59 | 73 | 74 |
    75 | 84 |
    85 |
    86 |
    87 | @endsection 88 | 89 | -------------------------------------------------------------------------------- /resources/views/pages/auth/forgot-password.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.fullscreen-layout') 2 | 3 | @section('content') 4 |
    5 |
    6 | 7 |
    8 | 17 |
    18 |
    19 |
    20 |

    21 | Forgot Password 22 |

    23 |

    24 | Enter your email and we'll send you a link to reset your password. 25 |

    26 |
    27 |
    28 | @if (session('status')) 29 |
    30 | {{ session('status') }} 31 |
    32 | @endif 33 |
    34 | @csrf 35 |
    36 | 37 |
    38 | 41 | 43 | @error('email') 44 |

    {{ $message }}

    45 | @enderror 46 |
    47 | 48 |
    49 | 53 |
    54 |
    55 |
    56 |
    57 |

    58 | Remember your password? 59 | Sign In 60 |

    61 |
    62 |
    63 |
    64 |
    65 |
    66 | 67 | 81 | 82 |
    83 | 92 |
    93 |
    94 |
    95 | @endsection 96 | 97 | -------------------------------------------------------------------------------- /resources/views/components/ecommerce/ecommerce-metrics.blade.php: -------------------------------------------------------------------------------- 1 |
    2 |
    5 |
    8 | 16 | 22 | 23 |
    24 | 25 |
    26 |
    27 | Customers 28 |

    3,782

    29 |
    30 | 31 | 34 | 42 | 48 | 49 | 50 | 11.01% 51 | 52 |
    53 |
    54 | 55 |
    58 |
    61 | 69 | 75 | 76 |
    77 | 78 |
    79 |
    80 | Orders 81 |

    5,359

    82 |
    83 | 84 | 87 | 95 | 101 | 102 | 103 | 9.05% 104 | 105 |
    106 |
    107 | 108 |
    111 |
    114 | 121 | 127 | 128 |
    129 | 130 |
    131 |
    132 | Total Revenue 133 |

    5,359

    134 |
    135 | 136 | 139 | 147 | 153 | 154 | 155 | 9.05% 156 | 157 |
    158 |
    159 |
    160 | --------------------------------------------------------------------------------