├── .editorconfig ├── .env.example ├── .env.testing ├── .gitattributes ├── .gitignore ├── .prettierignore ├── .prettierrc ├── README.md ├── app ├── Actions │ └── Fortify │ │ ├── CreateNewUser.php │ │ ├── PasswordValidationRules.php │ │ ├── ResetUserPassword.php │ │ ├── UpdateUserPassword.php │ │ └── UpdateUserProfileInformation.php ├── Http │ ├── Controllers │ │ ├── Account │ │ │ ├── ProfileController.php │ │ │ ├── SecurityController.php │ │ │ └── SessionController.php │ │ ├── Auth │ │ │ └── ConfirmablePasswordController.php │ │ └── Controller.php │ ├── Middleware │ │ └── HandleInertiaRequests.php │ └── Requests │ │ ├── Auth │ │ └── LoginRequest.php │ │ └── ProfileUpdateRequest.php ├── Models │ ├── Session.php │ └── User.php └── Providers │ ├── AppServiceProvider.php │ └── FortifyServiceProvider.php ├── artisan ├── bootstrap ├── app.php ├── cache │ ├── packages.php │ └── services.php └── providers.php ├── components.json ├── composer.json ├── composer.lock ├── config ├── app.php ├── auth.php ├── cache.php ├── database.php ├── filesystems.php ├── fortify.php ├── logging.php ├── mail.php ├── queue.php ├── services.php └── session.php ├── database ├── .gitignore ├── factories │ └── UserFactory.php ├── migrations │ ├── 0001_01_01_000000_create_users_table.php │ ├── 0001_01_01_000001_create_cache_table.php │ ├── 0001_01_01_000002_create_jobs_table.php │ ├── 2025_01_25_140923_create_personal_access_tokens_table.php │ └── 2025_01_29_181350_add_two_factor_columns_to_users_table.php └── seeders │ └── DatabaseSeeder.php ├── docs ├── backend.md ├── frontend.md └── getting-started.md ├── eslint.config.js ├── package.json ├── phpunit.xml ├── pnpm-lock.yaml ├── postcss.config.js ├── public ├── .htaccess ├── favicon.png ├── index.php └── robots.txt ├── resources ├── css │ └── app.css ├── js │ ├── Pages │ │ ├── Auth │ │ │ ├── ForgotPassword.svelte │ │ │ ├── ForgotPasswordSent.svelte │ │ │ ├── Login.svelte │ │ │ ├── PasswordReset.svelte │ │ │ ├── Register.svelte │ │ │ ├── TwoFactorChallenge.svelte │ │ │ └── VerifyEmail.svelte │ │ ├── Dashboard.svelte │ │ ├── Profile │ │ │ ├── Partials │ │ │ │ ├── DeleteUserForm.svelte │ │ │ │ ├── UpdatePasswordForm.svelte │ │ │ │ └── UpdateProfileInformationForm.svelte │ │ │ └── Show.svelte │ │ ├── Security │ │ │ ├── Partials │ │ │ │ └── TwoFactorAuthenticationForm.svelte │ │ │ └── Show.svelte │ │ └── Welcome.svelte │ ├── app.ts │ ├── bootstrap.ts │ ├── lib │ │ ├── components │ │ │ └── ui │ │ │ │ ├── alert-dialog │ │ │ │ ├── alert-dialog-action.svelte │ │ │ │ ├── alert-dialog-cancel.svelte │ │ │ │ ├── alert-dialog-content.svelte │ │ │ │ ├── alert-dialog-description.svelte │ │ │ │ ├── alert-dialog-footer.svelte │ │ │ │ ├── alert-dialog-header.svelte │ │ │ │ ├── alert-dialog-overlay.svelte │ │ │ │ ├── alert-dialog-title.svelte │ │ │ │ └── index.ts │ │ │ │ ├── avatar │ │ │ │ ├── avatar-fallback.svelte │ │ │ │ ├── avatar-image.svelte │ │ │ │ ├── avatar.svelte │ │ │ │ └── index.ts │ │ │ │ ├── badge │ │ │ │ ├── badge.svelte │ │ │ │ └── index.ts │ │ │ │ ├── breadcrumb │ │ │ │ ├── breadcrumb-ellipsis.svelte │ │ │ │ ├── breadcrumb-item.svelte │ │ │ │ ├── breadcrumb-link.svelte │ │ │ │ ├── breadcrumb-list.svelte │ │ │ │ ├── breadcrumb-page.svelte │ │ │ │ ├── breadcrumb-separator.svelte │ │ │ │ ├── breadcrumb.svelte │ │ │ │ └── index.ts │ │ │ │ ├── button │ │ │ │ ├── button.svelte │ │ │ │ └── index.ts │ │ │ │ ├── collapsible │ │ │ │ └── index.ts │ │ │ │ ├── command │ │ │ │ ├── command-dialog.svelte │ │ │ │ ├── command-empty.svelte │ │ │ │ ├── command-group.svelte │ │ │ │ ├── command-input.svelte │ │ │ │ ├── command-item.svelte │ │ │ │ ├── command-link-item.svelte │ │ │ │ ├── command-list.svelte │ │ │ │ ├── command-separator.svelte │ │ │ │ ├── command-shortcut.svelte │ │ │ │ ├── command.svelte │ │ │ │ └── index.ts │ │ │ │ ├── custom │ │ │ │ ├── app-breadcrumb.svelte │ │ │ │ ├── app-command.svelte │ │ │ │ ├── app-sidebar.svelte │ │ │ │ ├── confirm-with-password.svelte │ │ │ │ ├── error-feedback.svelte │ │ │ │ ├── nav-main.svelte │ │ │ │ ├── nav-project-members.svelte │ │ │ │ ├── nav-secondary.svelte │ │ │ │ ├── nav-user.svelte │ │ │ │ ├── project-switcher.svelte │ │ │ │ └── ripple.svelte │ │ │ │ ├── dialog │ │ │ │ ├── dialog-content.svelte │ │ │ │ ├── dialog-description.svelte │ │ │ │ ├── dialog-footer.svelte │ │ │ │ ├── dialog-header.svelte │ │ │ │ ├── dialog-overlay.svelte │ │ │ │ ├── dialog-title.svelte │ │ │ │ └── index.ts │ │ │ │ ├── dropdown-menu │ │ │ │ ├── dropdown-menu-checkbox-item.svelte │ │ │ │ ├── dropdown-menu-content.svelte │ │ │ │ ├── dropdown-menu-group-heading.svelte │ │ │ │ ├── dropdown-menu-item.svelte │ │ │ │ ├── dropdown-menu-label.svelte │ │ │ │ ├── dropdown-menu-radio-item.svelte │ │ │ │ ├── dropdown-menu-separator.svelte │ │ │ │ ├── dropdown-menu-shortcut.svelte │ │ │ │ ├── dropdown-menu-sub-content.svelte │ │ │ │ ├── dropdown-menu-sub-trigger.svelte │ │ │ │ └── index.ts │ │ │ │ ├── input-otp │ │ │ │ ├── index.ts │ │ │ │ ├── input-otp-group.svelte │ │ │ │ ├── input-otp-separator.svelte │ │ │ │ ├── input-otp-slot.svelte │ │ │ │ └── input-otp.svelte │ │ │ │ ├── input │ │ │ │ ├── index.ts │ │ │ │ └── input.svelte │ │ │ │ ├── label │ │ │ │ ├── index.ts │ │ │ │ └── label.svelte │ │ │ │ ├── separator │ │ │ │ ├── index.ts │ │ │ │ └── separator.svelte │ │ │ │ ├── sheet │ │ │ │ ├── index.ts │ │ │ │ ├── sheet-content.svelte │ │ │ │ ├── sheet-description.svelte │ │ │ │ ├── sheet-footer.svelte │ │ │ │ ├── sheet-header.svelte │ │ │ │ ├── sheet-overlay.svelte │ │ │ │ └── sheet-title.svelte │ │ │ │ ├── sidebar │ │ │ │ ├── constants.ts │ │ │ │ ├── context.svelte.ts │ │ │ │ ├── index.ts │ │ │ │ ├── sidebar-content.svelte │ │ │ │ ├── sidebar-footer.svelte │ │ │ │ ├── sidebar-group-action.svelte │ │ │ │ ├── sidebar-group-content.svelte │ │ │ │ ├── sidebar-group-label.svelte │ │ │ │ ├── sidebar-group.svelte │ │ │ │ ├── sidebar-header.svelte │ │ │ │ ├── sidebar-input.svelte │ │ │ │ ├── sidebar-inset.svelte │ │ │ │ ├── sidebar-menu-action.svelte │ │ │ │ ├── sidebar-menu-badge.svelte │ │ │ │ ├── sidebar-menu-button.svelte │ │ │ │ ├── sidebar-menu-item.svelte │ │ │ │ ├── sidebar-menu-skeleton.svelte │ │ │ │ ├── sidebar-menu-sub-button.svelte │ │ │ │ ├── sidebar-menu-sub-item.svelte │ │ │ │ ├── sidebar-menu-sub.svelte │ │ │ │ ├── sidebar-menu.svelte │ │ │ │ ├── sidebar-provider.svelte │ │ │ │ ├── sidebar-rail.svelte │ │ │ │ ├── sidebar-separator.svelte │ │ │ │ ├── sidebar-trigger.svelte │ │ │ │ └── sidebar.svelte │ │ │ │ ├── skeleton │ │ │ │ ├── index.ts │ │ │ │ └── skeleton.svelte │ │ │ │ ├── sonner │ │ │ │ ├── index.ts │ │ │ │ └── sonner.svelte │ │ │ │ └── tooltip │ │ │ │ ├── index.ts │ │ │ │ └── tooltip-content.svelte │ │ ├── hooks │ │ │ └── is-mobile.svelte.ts │ │ ├── layouts │ │ │ ├── AuthenticatedLayout.svelte │ │ │ └── AuthenticationLayout.svelte │ │ ├── types │ │ │ ├── global.d.ts │ │ │ ├── index.d.ts │ │ │ └── vite-env.d.ts │ │ └── utils.ts │ ├── pages │ │ ├── Auth │ │ │ ├── ForgotPassword.svelte │ │ │ ├── ForgotPasswordSent.svelte │ │ │ ├── Login.svelte │ │ │ ├── PasswordReset.svelte │ │ │ ├── Register.svelte │ │ │ ├── TwoFactorChallenge.svelte │ │ │ └── VerifyEmail.svelte │ │ ├── Dashboard.svelte │ │ ├── Profile │ │ │ ├── Partials │ │ │ │ ├── DeleteUserForm.svelte │ │ │ │ ├── UpdatePasswordForm.svelte │ │ │ │ └── UpdateProfileInformationForm.svelte │ │ │ └── Show.svelte │ │ ├── Security │ │ │ ├── Partials │ │ │ │ └── TwoFactorAuthenticationForm.svelte │ │ │ └── Show.svelte │ │ └── Welcome.svelte │ └── ssr.ts └── views │ └── app.blade.php ├── routes ├── auth.php └── web.php ├── storage ├── app │ ├── .gitignore │ ├── private │ │ └── .gitignore │ └── public │ │ └── .gitignore ├── framework │ ├── .gitignore │ ├── cache │ │ ├── .gitignore │ │ └── data │ │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ ├── testing │ │ └── .gitignore │ └── views │ │ └── .gitignore └── logs │ └── .gitignore ├── svelte.config.js ├── tailwind.config.js ├── tests ├── Feature │ ├── Auth │ │ ├── AuthenticationTest.php │ │ ├── EmailVerificationTest.php │ │ ├── PasswordConfirmationTest.php │ │ ├── PasswordResetTest.php │ │ ├── PasswordUpdateTest.php │ │ └── RegistrationTest.php │ ├── ExampleTest.php │ └── ProfileTest.php ├── Pest.php ├── TestCase.php └── Unit │ └── ExampleTest.php ├── tsconfig.eslint.json ├── tsconfig.json └── vite.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME="Svelte Inertia Laravel" 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_TIMEZONE=UTC 6 | APP_URL=http://svelte-inertia-laravel.test 7 | 8 | APP_LOCALE=en 9 | APP_FALLBACK_LOCALE=en 10 | APP_FAKER_LOCALE=en_US 11 | 12 | APP_MAINTENANCE_DRIVER=file 13 | # APP_MAINTENANCE_STORE=database 14 | 15 | PHP_CLI_SERVER_WORKERS=4 16 | 17 | BCRYPT_ROUNDS=12 18 | 19 | LOG_CHANNEL=stack 20 | LOG_STACK=single 21 | LOG_DEPRECATIONS_CHANNEL=null 22 | LOG_LEVEL=debug 23 | 24 | DB_CONNECTION=sqlite 25 | # DB_HOST=127.0.0.1 26 | # DB_PORT=3306 27 | # DB_DATABASE=laravel 28 | # DB_USERNAME=root 29 | # DB_PASSWORD= 30 | 31 | SESSION_DRIVER=database 32 | SESSION_LIFETIME=120 33 | SESSION_ENCRYPT=false 34 | SESSION_PATH=/ 35 | SESSION_DOMAIN=null 36 | 37 | BROADCAST_CONNECTION=log 38 | FILESYSTEM_DISK=local 39 | QUEUE_CONNECTION=database 40 | 41 | CACHE_STORE=database 42 | CACHE_PREFIX= 43 | 44 | MEMCACHED_HOST=127.0.0.1 45 | 46 | REDIS_CLIENT=phpredis 47 | REDIS_HOST=127.0.0.1 48 | REDIS_PASSWORD=null 49 | REDIS_PORT=6379 50 | 51 | MAIL_MAILER=log 52 | MAIL_SCHEME=null 53 | MAIL_HOST=127.0.0.1 54 | MAIL_PORT=2525 55 | MAIL_USERNAME=null 56 | MAIL_PASSWORD=null 57 | MAIL_FROM_ADDRESS="hello@example.com" 58 | MAIL_FROM_NAME="${APP_NAME}" 59 | 60 | AWS_ACCESS_KEY_ID= 61 | AWS_SECRET_ACCESS_KEY= 62 | AWS_DEFAULT_REGION=us-east-1 63 | AWS_BUCKET= 64 | AWS_USE_PATH_STYLE_ENDPOINT=false 65 | 66 | VITE_APP_NAME="${APP_NAME}" 67 | -------------------------------------------------------------------------------- /.env.testing: -------------------------------------------------------------------------------- 1 | APP_NAME="Laravel" 2 | APP_ENV=testing 3 | APP_KEY=base64:vdJO+cS13hn5KPCE0H2pTRdbsV1h5eAUlogToz390G4= 4 | APP_DEBUG=true 5 | APP_URL=http://localhost 6 | 7 | DB_CONNECTION=sqlite 8 | DB_DATABASE=:memory: 9 | 10 | BROADCAST_DRIVER=log 11 | CACHE_DRIVER=file 12 | FILESYSTEM_DISK=local 13 | QUEUE_CONNECTION=sync 14 | SESSION_DRIVER=database 15 | SESSION_LIFETIME=120 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.blade.php diff=html 4 | *.css diff=css 5 | *.html diff=html 6 | *.md diff=markdown 7 | *.php diff=php 8 | 9 | /.github export-ignore 10 | CHANGELOG.md export-ignore 11 | .styleci.yml export-ignore 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.phpunit.cache 2 | /bootstrap/ssr 3 | /node_modules 4 | /public/build 5 | /public/hot 6 | /public/storage 7 | /storage/*.key 8 | /storage/pail 9 | /vendor 10 | .env 11 | .env.backup 12 | .env.production 13 | .phpactor.json 14 | .phpunit.result.cache 15 | Homestead.json 16 | Homestead.yaml 17 | npm-debug.log 18 | yarn-error.log 19 | /auth.json 20 | /.fleet 21 | /.idea 22 | /.nova 23 | /.vscode 24 | /.zed 25 | .windsurfrules -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | /vendor 4 | 5 | # Build outputs 6 | /public/build 7 | /public/hot 8 | /public/storage 9 | /storage/*.key 10 | /bootstrap/ssr 11 | /bootstrap/cache 12 | 13 | # Environment and config files 14 | .env 15 | .env.* 16 | *.config.js 17 | *.config.ts 18 | 19 | # IDE and editor files 20 | /.idea 21 | /.vscode 22 | /.fleet 23 | /.nova 24 | /.zed 25 | 26 | # Cache and logs 27 | .phpunit.result.cache 28 | .phpunit.cache 29 | .phpactor.json 30 | npm-debug.log 31 | yarn-error.log 32 | *.log 33 | 34 | # Generated files 35 | /_ide_helper.php 36 | /.phpstorm.meta.php 37 | /composer.lock 38 | /package-lock.json 39 | /yarn.lock 40 | /pnpm-lock.yaml 41 | 42 | # Laravel specific 43 | /storage/framework/cache/* 44 | /storage/framework/sessions/* 45 | /storage/framework/views/* 46 | /storage/logs/* 47 | /storage/app/* 48 | 49 | # Compiled assets 50 | *.css.map 51 | *.js.map 52 | 53 | # Test coverage 54 | /coverage 55 | /tests/coverage 56 | 57 | # Misc 58 | .DS_Store 59 | Thumbs.db -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "plugins": [ 4 | "prettier-plugin-organize-imports", 5 | "prettier-plugin-tailwindcss", 6 | "prettier-plugin-svelte" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /app/Actions/Fortify/CreateNewUser.php: -------------------------------------------------------------------------------- 1 | $input 19 | */ 20 | public function create(array $input): User 21 | { 22 | Validator::make($input, [ 23 | 'name' => ['required', 'string', 'max:255'], 24 | 'email' => [ 25 | 'required', 26 | 'string', 27 | 'email', 28 | 'max:255', 29 | Rule::unique(User::class), 30 | ], 31 | 'password' => $this->passwordRules(), 32 | ])->validate(); 33 | 34 | return User::create([ 35 | 'name' => $input['name'], 36 | 'email' => $input['email'], 37 | 'password' => Hash::make($input['password']), 38 | ]); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Actions/Fortify/PasswordValidationRules.php: -------------------------------------------------------------------------------- 1 | |string> 13 | */ 14 | protected function passwordRules(): array 15 | { 16 | return ['required', 'string', Password::default(), 'confirmed']; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Actions/Fortify/ResetUserPassword.php: -------------------------------------------------------------------------------- 1 | $input 18 | */ 19 | public function reset(User $user, array $input): void 20 | { 21 | Validator::make($input, [ 22 | 'password' => $this->passwordRules(), 23 | ])->validate(); 24 | 25 | $user->forceFill([ 26 | 'password' => Hash::make($input['password']), 27 | ])->save(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Actions/Fortify/UpdateUserPassword.php: -------------------------------------------------------------------------------- 1 | $input 18 | */ 19 | public function update(User $user, array $input): void 20 | { 21 | Validator::make($input, [ 22 | 'current_password' => ['required', 'string', 'current_password:web'], 23 | 'password' => $this->passwordRules(), 24 | ], [ 25 | 'current_password.current_password' => __('The provided password does not match your current password.'), 26 | ])->validateWithBag('updatePassword'); 27 | 28 | $user->forceFill([ 29 | 'password' => Hash::make($input['password']), 30 | ])->save(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Actions/Fortify/UpdateUserProfileInformation.php: -------------------------------------------------------------------------------- 1 | $input 17 | */ 18 | public function update(User $user, array $input): void 19 | { 20 | Validator::make($input, [ 21 | 'name' => ['required', 'string', 'max:255'], 22 | 23 | 'email' => [ 24 | 'required', 25 | 'string', 26 | 'email', 27 | 'max:255', 28 | Rule::unique('users')->ignore($user->id), 29 | ], 30 | ])->validateWithBag('updateProfileInformation'); 31 | 32 | if ($input['email'] !== $user->email && 33 | $user instanceof MustVerifyEmail) { 34 | $this->updateVerifiedUser($user, $input); 35 | } else { 36 | $user->forceFill([ 37 | 'name' => $input['name'], 38 | 'email' => $input['email'], 39 | ])->save(); 40 | } 41 | } 42 | 43 | /** 44 | * Update the given verified user's profile information. 45 | * 46 | * @param array $input 47 | */ 48 | protected function updateVerifiedUser(User $user, array $input): void 49 | { 50 | $user->forceFill([ 51 | 'name' => $input['name'], 52 | 'email' => $input['email'], 53 | 'email_verified_at' => null, 54 | ])->save(); 55 | 56 | $user->sendEmailVerificationNotification(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/Http/Controllers/Account/SecurityController.php: -------------------------------------------------------------------------------- 1 | Session::where('user_id', $request->user()->id)->get(), 25 | 'isTwoFactorAuthenticationFeatureEnabled' => Features::enabled(Features::twoFactorAuthentication()), 26 | ]); 27 | } 28 | 29 | /** 30 | * Update the user's profile information. 31 | */ 32 | public function update(ProfileUpdateRequest $request): RedirectResponse 33 | { 34 | $request->user()->fill($request->validated()); 35 | 36 | if ($request->user()->isDirty('email')) { 37 | $request->user()->email_verified_at = null; 38 | } 39 | 40 | $request->user()->save(); 41 | 42 | return Redirect::route('profile.show'); 43 | } 44 | 45 | /** 46 | * Delete the user's account. 47 | */ 48 | public function destroy(Request $request): RedirectResponse 49 | { 50 | $request->validate([ 51 | 'password' => ['required', 'current_password'], 52 | ]); 53 | 54 | $user = $request->user(); 55 | 56 | Auth::logout(); 57 | 58 | $user->delete(); 59 | 60 | $request->session()->invalidate(); 61 | $request->session()->regenerateToken(); 62 | 63 | return Redirect::to('/'); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/Http/Controllers/Account/SessionController.php: -------------------------------------------------------------------------------- 1 | validate([ 24 | 'password' => ['required', 'string', 'current_password'], 25 | ]); 26 | 27 | DB::table('sessions') 28 | ->where('user_id', Auth::id()) 29 | ->where('id', '!=', request()->session()->getId()) 30 | ->delete(); 31 | 32 | return back(303)->with('status', 'other-browser-sessions-terminated'); 33 | } 34 | 35 | /** 36 | * Destroy a specific session. 37 | */ 38 | public function destroySession(Request $request, string $id): RedirectResponse 39 | { 40 | if (config('session.driver') !== 'database') { 41 | return back(409); 42 | } 43 | 44 | $request->validate([ 45 | 'password' => ['required', 'string', 'current_password'], 46 | ]); 47 | 48 | // Don't allow destroying the current session 49 | if ($id === $request->session()->getId()) { 50 | throw ValidationException::withMessages([ 51 | 'session' => ['Cannot terminate current session'], 52 | ]); 53 | } 54 | 55 | // Verify the session belongs to the current user 56 | $session = DB::table('sessions') 57 | ->where('user_id', Auth::id()) 58 | ->where('id', $id) 59 | ->first(); 60 | 61 | if (! $session) { 62 | throw ValidationException::withMessages([ 63 | 'session' => ['Session not found or does not belong to you'], 64 | ]); 65 | } 66 | 67 | DB::table('sessions') 68 | ->where('user_id', Auth::id()) 69 | ->where('id', $id) 70 | ->delete(); 71 | 72 | return back(303)->with('status', 'browser-session-terminated'); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ConfirmablePasswordController.php: -------------------------------------------------------------------------------- 1 | validate([ 19 | 'email' => $request->user()->email, 20 | 'password' => $request->password, 21 | ])) { 22 | throw ValidationException::withMessages([ 23 | 'password' => __('auth.password'), 24 | ]); 25 | } 26 | 27 | $request->session()->put('auth.password_confirmed_at', time()); 28 | 29 | return redirect()->intended(route('dashboard', absolute: false)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | 30 | */ 31 | public function share(Request $request): array 32 | { 33 | return [ 34 | ...parent::share($request), 35 | 'auth' => [ 36 | 'user' => $request->user(), 37 | ], 38 | 'ziggy' => fn () => [ 39 | ...(new Ziggy)->toArray(), 40 | 'location' => $request->url(), 41 | ], 42 | ]; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/Http/Requests/Auth/LoginRequest.php: -------------------------------------------------------------------------------- 1 | |string> 26 | */ 27 | public function rules(): array 28 | { 29 | return [ 30 | 'email' => ['required', 'string', 'email'], 31 | 'password' => ['required', 'string'], 32 | ]; 33 | } 34 | 35 | /** 36 | * Attempt to authenticate the request's credentials. 37 | * 38 | * @throws \Illuminate\Validation\ValidationException 39 | */ 40 | public function authenticate(): void 41 | { 42 | $this->ensureIsNotRateLimited(); 43 | 44 | if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) { 45 | RateLimiter::hit($this->throttleKey()); 46 | 47 | throw ValidationException::withMessages([ 48 | 'email' => trans('auth.failed'), 49 | ]); 50 | } 51 | 52 | RateLimiter::clear($this->throttleKey()); 53 | } 54 | 55 | /** 56 | * Ensure the login request is not rate limited. 57 | * 58 | * @throws \Illuminate\Validation\ValidationException 59 | */ 60 | public function ensureIsNotRateLimited(): void 61 | { 62 | if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { 63 | return; 64 | } 65 | 66 | event(new Lockout($this)); 67 | 68 | $seconds = RateLimiter::availableIn($this->throttleKey()); 69 | 70 | throw ValidationException::withMessages([ 71 | 'email' => trans('auth.throttle', [ 72 | 'seconds' => $seconds, 73 | 'minutes' => ceil($seconds / 60), 74 | ]), 75 | ]); 76 | } 77 | 78 | /** 79 | * Get the rate limiting throttle key for the request. 80 | */ 81 | public function throttleKey(): string 82 | { 83 | return Str::transliterate(Str::lower($this->string('email')).'|'.$this->ip()); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/Http/Requests/ProfileUpdateRequest.php: -------------------------------------------------------------------------------- 1 | |string> 15 | */ 16 | public function rules(): array 17 | { 18 | return [ 19 | 'name' => ['required', 'string', 'max:255'], 20 | 'email' => [ 21 | 'required', 22 | 'string', 23 | 'lowercase', 24 | 'email', 25 | 'max:255', 26 | Rule::unique(User::class)->ignore($this->user()->id), 27 | ], 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Models/Session.php: -------------------------------------------------------------------------------- 1 | 'datetime', 19 | ]; 20 | 21 | protected $appends = [ 22 | 'last_active_ago', 23 | ]; 24 | 25 | public function user(): BelongsTo 26 | { 27 | return $this->belongsTo(User::class); 28 | } 29 | 30 | public function getLastActiveAgoAttribute() 31 | { 32 | return $this->last_activity->diffForHumans(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/Models/User.php: -------------------------------------------------------------------------------- 1 | */ 16 | use HasFactory, Notifiable, TwoFactorAuthenticatable; 17 | 18 | /** 19 | * The attributes that are mass assignable. 20 | * 21 | * @var list 22 | */ 23 | protected $fillable = [ 24 | 'name', 25 | 'email', 26 | 'password', 27 | ]; 28 | 29 | /** 30 | * The attributes that should be hidden for serialization. 31 | * 32 | * @var list 33 | */ 34 | protected $hidden = [ 35 | 'password', 36 | 'remember_token', 37 | 'two_factor_recovery_codes', 38 | 'two_factor_secret', 39 | ]; 40 | 41 | /** 42 | * The accessors to append to the model's array form. 43 | * 44 | * @var array 45 | */ 46 | protected $appends = [ 47 | 'profile_photo_url', 48 | ]; 49 | 50 | /** 51 | * Get the attributes that should be cast. 52 | * 53 | * @return array 54 | */ 55 | protected function casts(): array 56 | { 57 | return [ 58 | 'email_verified_at' => 'datetime', 59 | 'password' => 'hashed', 60 | ]; 61 | } 62 | 63 | /** 64 | * Update the user's profile photo. 65 | */ 66 | public function updateProfilePhoto(UploadedFile $photo): void 67 | { 68 | tap($this->profile_photo_path, function ($previous) use ($photo) { 69 | $this->forceFill([ 70 | 'profile_photo_path' => $photo->storePublicly( 71 | 'profile-photos', 72 | ['disk' => 'public'] 73 | ), 74 | ])->save(); 75 | 76 | if ($previous) { 77 | Storage::disk('public')->delete($previous); 78 | } 79 | }); 80 | } 81 | 82 | /** 83 | * Get the URL to the user's profile photo. 84 | */ 85 | public function getProfilePhotoUrlAttribute(): string 86 | { 87 | return $this->profile_photo_path 88 | ? asset('storage/'.$this->profile_photo_path) 89 | : ''; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | input(Fortify::username())).'|'.$request->ip()); 38 | 39 | return Limit::perMinute(5)->by($throttleKey); 40 | }); 41 | 42 | RateLimiter::for('two-factor', function (Request $request) { 43 | return Limit::perMinute(5)->by($request->session()->get('login.id')); 44 | }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | handleCommand(new ArgvInput); 14 | 15 | exit($status); 16 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | withRouting( 9 | web: __DIR__.'/../routes/web.php', 10 | health: '/up', 11 | ) 12 | ->withMiddleware(function (Middleware $middleware) { 13 | $middleware->web(append: [ 14 | \App\Http\Middleware\HandleInertiaRequests::class, 15 | \Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets::class, 16 | ]); 17 | 18 | // 19 | }) 20 | ->withExceptions(function (Exceptions $exceptions) { 21 | // 22 | })->create(); 23 | -------------------------------------------------------------------------------- /bootstrap/cache/packages.php: -------------------------------------------------------------------------------- 1 | 3 | array ( 4 | 'providers' => 5 | array ( 6 | 0 => 'Inertia\\ServiceProvider', 7 | ), 8 | ), 9 | 'jenssegers/agent' => 10 | array ( 11 | 'aliases' => 12 | array ( 13 | 'Agent' => 'Jenssegers\\Agent\\Facades\\Agent', 14 | ), 15 | 'providers' => 16 | array ( 17 | 0 => 'Jenssegers\\Agent\\AgentServiceProvider', 18 | ), 19 | ), 20 | 'laravel/breeze' => 21 | array ( 22 | 'providers' => 23 | array ( 24 | 0 => 'Laravel\\Breeze\\BreezeServiceProvider', 25 | ), 26 | ), 27 | 'laravel/fortify' => 28 | array ( 29 | 'providers' => 30 | array ( 31 | 0 => 'Laravel\\Fortify\\FortifyServiceProvider', 32 | ), 33 | ), 34 | 'laravel/pail' => 35 | array ( 36 | 'providers' => 37 | array ( 38 | 0 => 'Laravel\\Pail\\PailServiceProvider', 39 | ), 40 | ), 41 | 'laravel/sail' => 42 | array ( 43 | 'providers' => 44 | array ( 45 | 0 => 'Laravel\\Sail\\SailServiceProvider', 46 | ), 47 | ), 48 | 'laravel/sanctum' => 49 | array ( 50 | 'providers' => 51 | array ( 52 | 0 => 'Laravel\\Sanctum\\SanctumServiceProvider', 53 | ), 54 | ), 55 | 'laravel/tinker' => 56 | array ( 57 | 'providers' => 58 | array ( 59 | 0 => 'Laravel\\Tinker\\TinkerServiceProvider', 60 | ), 61 | ), 62 | 'nesbot/carbon' => 63 | array ( 64 | 'providers' => 65 | array ( 66 | 0 => 'Carbon\\Laravel\\ServiceProvider', 67 | ), 68 | ), 69 | 'nunomaduro/collision' => 70 | array ( 71 | 'providers' => 72 | array ( 73 | 0 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider', 74 | ), 75 | ), 76 | 'nunomaduro/termwind' => 77 | array ( 78 | 'providers' => 79 | array ( 80 | 0 => 'Termwind\\Laravel\\TermwindServiceProvider', 81 | ), 82 | ), 83 | 'pestphp/pest-plugin-laravel' => 84 | array ( 85 | 'providers' => 86 | array ( 87 | 0 => 'Pest\\Laravel\\PestServiceProvider', 88 | ), 89 | ), 90 | 'railsware/mailtrap-php' => 91 | array ( 92 | 'providers' => 93 | array ( 94 | 0 => 'Mailtrap\\Bridge\\Laravel\\MailtrapApiProvider', 95 | ), 96 | ), 97 | 'tightenco/ziggy' => 98 | array ( 99 | 'providers' => 100 | array ( 101 | 0 => 'Tighten\\Ziggy\\ZiggyServiceProvider', 102 | ), 103 | ), 104 | ); -------------------------------------------------------------------------------- /bootstrap/providers.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 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'token' => env('POSTMARK_TOKEN'), 19 | ], 20 | 21 | 'ses' => [ 22 | 'key' => env('AWS_ACCESS_KEY_ID'), 23 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 24 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 25 | ], 26 | 27 | 'resend' => [ 28 | 'key' => env('RESEND_KEY'), 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 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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->string('profile_photo_path', 2048)->nullable(); 22 | $table->timestamps(); 23 | }); 24 | 25 | Schema::create('password_reset_tokens', function (Blueprint $table) { 26 | $table->string('email')->primary(); 27 | $table->string('token'); 28 | $table->timestamp('created_at')->nullable(); 29 | }); 30 | 31 | Schema::create('sessions', function (Blueprint $table) { 32 | $table->string('id')->primary(); 33 | $table->foreignId('user_id')->nullable()->index(); 34 | $table->string('ip_address', 45)->nullable(); 35 | $table->text('user_agent')->nullable(); 36 | $table->longText('payload'); 37 | $table->integer('last_activity')->index(); 38 | }); 39 | } 40 | 41 | /** 42 | * Reverse the migrations. 43 | */ 44 | public function down(): void 45 | { 46 | Schema::dropIfExists('users'); 47 | Schema::dropIfExists('password_reset_tokens'); 48 | Schema::dropIfExists('sessions'); 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /database/migrations/2025_01_25_140923_create_personal_access_tokens_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->morphs('tokenable'); 17 | $table->string('name'); 18 | $table->string('token', 64)->unique(); 19 | $table->text('abilities')->nullable(); 20 | $table->timestamp('last_used_at')->nullable(); 21 | $table->timestamp('expires_at')->nullable(); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | */ 29 | public function down(): void 30 | { 31 | Schema::dropIfExists('personal_access_tokens'); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /database/migrations/2025_01_29_181350_add_two_factor_columns_to_users_table.php: -------------------------------------------------------------------------------- 1 | text('two_factor_secret') 16 | ->after('password') 17 | ->nullable(); 18 | 19 | $table->text('two_factor_recovery_codes') 20 | ->after('two_factor_secret') 21 | ->nullable(); 22 | 23 | $table->timestamp('two_factor_confirmed_at') 24 | ->after('two_factor_recovery_codes') 25 | ->nullable(); 26 | }); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | */ 32 | public function down(): void 33 | { 34 | Schema::table('users', function (Blueprint $table) { 35 | $table->dropColumn([ 36 | 'two_factor_secret', 37 | 'two_factor_recovery_codes', 38 | 'two_factor_confirmed_at', 39 | ]); 40 | }); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | create([ 17 | 'name' => 'User', 18 | 'email' => 'user@example.com', 19 | 'email_verified_at' => now(), 20 | 'password' => bcrypt('password'), // password 21 | 'remember_token' => Str::random(10), 22 | ]); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import eslint from '@eslint/js'; 2 | import tseslint from '@typescript-eslint/eslint-plugin'; 3 | import tsParser from '@typescript-eslint/parser'; 4 | import eslintPluginPrettier from 'eslint-plugin-prettier'; 5 | import eslintPluginSvelte from 'eslint-plugin-svelte'; 6 | import svelteParser from 'svelte-eslint-parser'; 7 | 8 | export default [ 9 | { ignores: ['dist', 'node_modules', 'vendor', 'public', 'bootstrap'] }, 10 | { 11 | files: ['**/*.{js,ts}'], 12 | ...eslint.configs.recommended, 13 | languageOptions: { 14 | ecmaVersion: 'latest', 15 | sourceType: 'module', 16 | parser: tsParser, 17 | parserOptions: { 18 | project: './tsconfig.eslint.json', 19 | }, 20 | globals: { 21 | document: 'readonly', 22 | window: 'readonly', 23 | console: 'readonly', 24 | }, 25 | }, 26 | plugins: { 27 | '@typescript-eslint': tseslint, 28 | prettier: eslintPluginPrettier, 29 | }, 30 | rules: { 31 | ...tseslint.configs.recommended.rules, 32 | 'prettier/prettier': 'error', 33 | }, 34 | }, 35 | { 36 | files: ['**/*.svelte'], 37 | plugins: { 38 | svelte: eslintPluginSvelte, 39 | '@typescript-eslint': tseslint, 40 | prettier: eslintPluginPrettier, 41 | }, 42 | languageOptions: { 43 | ecmaVersion: 'latest', 44 | sourceType: 'module', 45 | parser: svelteParser, 46 | parserOptions: { 47 | parser: { 48 | ts: tsParser, 49 | js: tsParser, 50 | }, 51 | extraFileExtensions: ['.svelte'], 52 | }, 53 | globals: { 54 | document: 'readonly', 55 | window: 'readonly', 56 | console: 'readonly', 57 | }, 58 | }, 59 | rules: { 60 | ...tseslint.configs.recommended.rules, 61 | 'prettier/prettier': 'error', 62 | 'svelte/valid-compile': 'error', 63 | '@typescript-eslint/no-explicit-any': 'warn', 64 | '@typescript-eslint/no-unsafe-assignment': 'off', 65 | '@typescript-eslint/no-unsafe-member-access': 'off', 66 | '@typescript-eslint/no-unsafe-call': 'off', 67 | }, 68 | }, 69 | ]; 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-inertia-laravel-starter", 3 | "version": "0.1.0", 4 | "description": "The Svelte Inertia Laravel Starter", 5 | "keywords": [ 6 | "laravel", 7 | "inertia", 8 | "svelte", 9 | "shadcn-ui" 10 | ], 11 | "license": "MIT", 12 | "private": true, 13 | "type": "module", 14 | "scripts": { 15 | "build": "vite build && vite build --ssr", 16 | "dev": "vite", 17 | "lint": "eslint .", 18 | "lint:fix": "eslint . --fix", 19 | "format": "prettier --write .", 20 | "format:check": "prettier --check ." 21 | }, 22 | "dependencies": { 23 | "@inertiajs/svelte": "^2.0.11", 24 | "axios": "^1.9.0" 25 | }, 26 | "devDependencies": { 27 | "@sveltejs/vite-plugin-svelte": "^5.0.3", 28 | "@tailwindcss/forms": "^0.5.10", 29 | "@tsconfig/svelte": "^5.0.4", 30 | "@types/node": "^22.15.19", 31 | "@typescript-eslint/eslint-plugin": "^8.32.1", 32 | "@typescript-eslint/parser": "^8.32.1", 33 | "autoprefixer": "^10.4.21", 34 | "bits-ui": "^1.5.2", 35 | "clsx": "^2.1.1", 36 | "concurrently": "^9.1.2", 37 | "eslint": "^9.27.0", 38 | "eslint-config-prettier": "^9.1.0", 39 | "eslint-plugin-prettier": "^5.4.0", 40 | "eslint-plugin-svelte": "^2.46.1", 41 | "laravel-vite-plugin": "^1.2.0", 42 | "lucide-svelte": "^0.474.0", 43 | "mode-watcher": "^0.5.1", 44 | "postcss": "^8.5.3", 45 | "prettier": "^3.5.3", 46 | "prettier-plugin-organize-imports": "^4.1.0", 47 | "prettier-plugin-svelte": "^3.4.0", 48 | "prettier-plugin-tailwindcss": "^0.6.11", 49 | "svelte": "^5.31.0", 50 | "svelte-check": "^4.2.1", 51 | "svelte-eslint-parser": "^0.43.0", 52 | "svelte-sonner": "^0.3.28", 53 | "tailwind-merge": "^3.3.0", 54 | "tailwind-variants": "^0.3.1", 55 | "tailwindcss": "^3.4.17", 56 | "tailwindcss-animate": "^1.0.7", 57 | "typescript": "^5.8.3", 58 | "vite": "^6.3.5" 59 | }, 60 | "pnpm": { 61 | "onlyBuiltDependencies": [ 62 | "esbuild" 63 | ] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ferjal0/svelte-inertia-laravel/6bab12253a01201f92c27daff66f090a596b093d/public/favicon.png -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | handleRequest(Request::capture()); 18 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /resources/css/app.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --accent-foreground: 0 0% 9%; 8 | --accent: 0 0% 96.1%; 9 | --background: 0 0% 100%; 10 | --border: 0 0% 89.8%; 11 | --card-foreground: 0 0% 3.9%; 12 | --card: 0 0% 100%; 13 | --chart-1: 12 76% 61%; 14 | --chart-2: 173 58% 39%; 15 | --chart-3: 197 37% 24%; 16 | --chart-4: 43 74% 66%; 17 | --chart-5: 27 87% 67%; 18 | --destructive-foreground: 0 0% 98%; 19 | --destructive: 0 84.2% 60.2%; 20 | --foreground: 0 0% 3.9%; 21 | --input: 0 0% 89.8%; 22 | --muted-foreground: 0 0% 45.1%; 23 | --muted: 0 0% 96.1%; 24 | --popover-foreground: 0 0% 3.9%; 25 | --popover: 0 0% 100%; 26 | --primary-foreground: 0 0% 98%; 27 | --primary: 0 0% 9%; 28 | --radius: 0.5rem; 29 | --ring: 0 0% 3.9%; 30 | --secondary-foreground: 0 0% 9%; 31 | --secondary: 0 0% 96.1%; 32 | --sidebar-accent-foreground: 240 5.9% 10%; 33 | --sidebar-accent: 240 4.8% 95.9%; 34 | --sidebar-background: 0 0% 98%; 35 | --sidebar-border: 220 13% 91%; 36 | --sidebar-foreground: 240 5.3% 26.1%; 37 | --sidebar-primary-foreground: 0 0% 98%; 38 | --sidebar-primary: 240 5.9% 10%; 39 | --sidebar-ring: 217.2 91.2% 59.8%; 40 | } 41 | 42 | .dark { 43 | --accent-foreground: 0 0% 98%; 44 | --accent: 0 0% 14.9%; 45 | --background: 0 0% 3.9%; 46 | --border: 0 0% 14.9%; 47 | --card-foreground: 0 0% 98%; 48 | --card: 0 0% 3.9%; 49 | --chart-1: 220 70% 50%; 50 | --chart-2: 160 60% 45%; 51 | --chart-3: 30 80% 55%; 52 | --chart-4: 280 65% 60%; 53 | --chart-5: 340 75% 55%; 54 | --destructive-foreground: 0 0% 98%; 55 | --destructive: 0 62.8% 30.6%; 56 | --foreground: 0 0% 98%; 57 | --input: 0 0% 14.9%; 58 | --muted-foreground: 0 0% 63.9%; 59 | --muted: 0 0% 14.9%; 60 | --popover-foreground: 0 0% 98%; 61 | --popover: 0 0% 3.9%; 62 | --primary-foreground: 0 0% 9%; 63 | --primary: 0 0% 98%; 64 | --ring: 0 0% 83.1%; 65 | --secondary-foreground: 0 0% 98%; 66 | --secondary: 0 0% 14.9%; 67 | --sidebar-accent-foreground: 240 4.8% 95.9%; 68 | --sidebar-accent: 240 3.7% 15.9%; 69 | --sidebar-background: 240 5.9% 10%; 70 | --sidebar-border: 240 3.7% 15.9%; 71 | --sidebar-foreground: 240 4.8% 95.9%; 72 | --sidebar-primary-foreground: 0 0% 100%; 73 | --sidebar-primary: 224.3 76.3% 48%; 74 | --sidebar-ring: 217.2 91.2% 59.8%; 75 | } 76 | } 77 | 78 | @layer base { 79 | * { 80 | @apply border-border; 81 | } 82 | body { 83 | @apply bg-background text-foreground; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /resources/js/Pages/Auth/ForgotPassword.svelte: -------------------------------------------------------------------------------- 1 | 26 | 27 | 28 | Forgot Password 29 | 30 | 31 | 32 |
33 |
34 |

Forgot your password?

35 |

36 | No problem. Let us know your email address and we will email you 37 | a password reset link. 38 |

39 |
40 | 41 | {#if status} 42 |
45 | {status} 46 |
47 | {/if} 48 | 49 | 50 | 51 |
52 |
53 | 54 | 65 |
66 | 67 | 70 | 71 |
72 | Did you remember?{' '} 73 | 77 | Log in 78 | 79 |
80 |
81 | 82 |
83 | -------------------------------------------------------------------------------- /resources/js/Pages/Auth/ForgotPasswordSent.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | Password Reset Link Sent 10 | 11 | 12 | 13 |
14 |
15 |
16 | 17 |
18 |

Check your email

19 |

20 | We have sent a password reset link to your email address. Please 21 | check your inbox and follow the instructions to reset your 22 | password. 23 |

24 |
25 | 26 |
27 | 30 | 31 |
32 | Didn't receive the email?{' '} 33 | 37 | Try again 38 | 39 |
40 |
41 |
42 |
43 | -------------------------------------------------------------------------------- /resources/js/Pages/Auth/TwoFactorChallenge.svelte: -------------------------------------------------------------------------------- 1 | 30 | 31 | 32 | Login 33 | 34 | 35 | 36 |
37 |
38 | 39 |

Two-Factor Authentication

40 |
41 | 42 | 48 | {#snippet children({ cells })} 49 | 50 | {#each cells as cell} 51 | 52 | {/each} 53 | 54 | {/snippet} 55 | 56 | 57 |

58 | Please enter the one-time password from your authenticator app. 59 |

60 | 61 | 62 |
63 |
64 | -------------------------------------------------------------------------------- /resources/js/Pages/Auth/VerifyEmail.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | Verify Email 22 | 23 | 24 | 25 |
26 |
27 |

Verify your email

28 |

29 | Please verify your email address by clicking the link in the 30 | email we sent you. If you haven't received it, we can send a new 31 | verification link. 32 |

33 |
34 | 35 | {#if status === 'verification-link-sent'} 36 |
39 | A new verification link has been sent to your email address. 40 |
41 | {/if} 42 | 43 |
44 | 47 | 48 |
49 | 55 | Log out 56 | 57 |
58 |
59 |
60 |
61 | -------------------------------------------------------------------------------- /resources/js/Pages/Dashboard.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Dashboard 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 | -------------------------------------------------------------------------------- /resources/js/Pages/Profile/Show.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | Profile 17 | 18 | 19 | 20 |
21 | {#if isUpdateProfileEnabled} 22 |
23 | 24 |
25 | {/if} 26 | 27 | {#if isUpdatePasswordEnabled} 28 |
29 | 30 |
31 | {/if} 32 | 33 |
34 | 35 |
36 |
37 |
38 | -------------------------------------------------------------------------------- /resources/js/Pages/Security/Show.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | Security 14 | 15 | 16 | 17 |
18 | {#if isTwoFactorAuthenticationFeatureEnabled} 19 |
20 | 21 |
22 | {/if} 23 |
24 |
25 | -------------------------------------------------------------------------------- /resources/js/Pages/Welcome.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | Welcome 12 | 13 | 14 |
15 |
16 |
19 | 20 |
23 | 24 |
25 | Svelte Inertia Laravel 26 | 27 | 28 | 51 |
52 |
53 | 54 |
55 |
56 |

57 | Your landing goes here 58 |

59 | 60 |
61 |
62 |
63 | -------------------------------------------------------------------------------- /resources/js/app.ts: -------------------------------------------------------------------------------- 1 | import '../css/app.css'; 2 | import './bootstrap'; 3 | 4 | import { createInertiaApp, type ResolvedComponent } from '@inertiajs/svelte'; 5 | import { mount } from 'svelte'; 6 | 7 | createInertiaApp({ 8 | resolve: (name) => { 9 | const pages = import.meta.glob( 10 | './Pages/**/*.svelte', 11 | { eager: true }, 12 | ); 13 | return pages[`./Pages/${name}.svelte`]; 14 | }, 15 | setup({ el, App, props }) { 16 | mount(App, { target: el!, props }); 17 | }, 18 | progress: { 19 | color: '#ffffff', 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /resources/js/bootstrap.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | window.axios = axios; 3 | 4 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 5 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/alert-dialog/alert-dialog-action.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 22 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/alert-dialog/alert-dialog-content.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 29 | 30 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/alert-dialog/alert-dialog-description.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/alert-dialog/alert-dialog-footer.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
22 | {@render children?.()} 23 |
24 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/alert-dialog/alert-dialog-header.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
19 | {@render children?.()} 20 |
21 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/alert-dialog/alert-dialog-title.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/alert-dialog/index.ts: -------------------------------------------------------------------------------- 1 | import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'; 2 | import Action from './alert-dialog-action.svelte'; 3 | import Cancel from './alert-dialog-cancel.svelte'; 4 | import Content from './alert-dialog-content.svelte'; 5 | import Description from './alert-dialog-description.svelte'; 6 | import Footer from './alert-dialog-footer.svelte'; 7 | import Header from './alert-dialog-header.svelte'; 8 | import Overlay from './alert-dialog-overlay.svelte'; 9 | import Title from './alert-dialog-title.svelte'; 10 | 11 | const Root = AlertDialogPrimitive.Root; 12 | const Trigger = AlertDialogPrimitive.Trigger; 13 | const Portal = AlertDialogPrimitive.Portal; 14 | 15 | export { 16 | Action, 17 | // 18 | Root as AlertDialog, 19 | Action as AlertDialogAction, 20 | Cancel as AlertDialogCancel, 21 | Content as AlertDialogContent, 22 | Description as AlertDialogDescription, 23 | Footer as AlertDialogFooter, 24 | Header as AlertDialogHeader, 25 | Overlay as AlertDialogOverlay, 26 | Portal as AlertDialogPortal, 27 | Title as AlertDialogTitle, 28 | Trigger as AlertDialogTrigger, 29 | Cancel, 30 | Content, 31 | Description, 32 | Footer, 33 | Header, 34 | Overlay, 35 | Portal, 36 | Root, 37 | Title, 38 | Trigger, 39 | }; 40 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/avatar/avatar-fallback.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/avatar/avatar-image.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/avatar/avatar.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/avatar/index.ts: -------------------------------------------------------------------------------- 1 | import Fallback from './avatar-fallback.svelte'; 2 | import Image from './avatar-image.svelte'; 3 | import Root from './avatar.svelte'; 4 | 5 | export { 6 | // 7 | Root as Avatar, 8 | Fallback as AvatarFallback, 9 | Image as AvatarImage, 10 | Fallback, 11 | Image, 12 | Root, 13 | }; 14 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/badge/badge.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 | 41 | 42 | 49 | {@render children?.()} 50 | 51 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/badge/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | default as Badge, 3 | badgeVariants, 4 | type BadgeVariant, 5 | } from './badge.svelte'; 6 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 26 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/breadcrumb/breadcrumb-item.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
  • 19 | {@render children?.()} 20 |
  • 21 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/breadcrumb/breadcrumb-link.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 | {#if child} 26 | {@render child({ props: attrs })} 27 | {:else} 28 | 29 | {@render children?.()} 30 | 31 | {/if} 32 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/breadcrumb/breadcrumb-list.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
      22 | {@render children?.()} 23 |
    24 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/breadcrumb/breadcrumb-page.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 22 | {@render children?.()} 23 | 24 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/breadcrumb/breadcrumb-separator.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 28 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/breadcrumb/breadcrumb.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/breadcrumb/index.ts: -------------------------------------------------------------------------------- 1 | import Ellipsis from './breadcrumb-ellipsis.svelte'; 2 | import Item from './breadcrumb-item.svelte'; 3 | import Link from './breadcrumb-link.svelte'; 4 | import List from './breadcrumb-list.svelte'; 5 | import Page from './breadcrumb-page.svelte'; 6 | import Separator from './breadcrumb-separator.svelte'; 7 | import Root from './breadcrumb.svelte'; 8 | 9 | export { 10 | // 11 | Root as Breadcrumb, 12 | Ellipsis as BreadcrumbEllipsis, 13 | Item as BreadcrumbItem, 14 | Link as BreadcrumbLink, 15 | List as BreadcrumbList, 16 | Page as BreadcrumbPage, 17 | Separator as BreadcrumbSeparator, 18 | Ellipsis, 19 | Item, 20 | Link, 21 | List, 22 | Page, 23 | Root, 24 | Separator, 25 | }; 26 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/button/button.svelte: -------------------------------------------------------------------------------- 1 | 46 | 47 | 61 | 62 | {#if href} 63 | 69 | {@render children?.()} 70 | 71 | {:else} 72 | 80 | {/if} 81 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/button/index.ts: -------------------------------------------------------------------------------- 1 | import Root, { 2 | type ButtonProps, 3 | type ButtonSize, 4 | type ButtonVariant, 5 | buttonVariants, 6 | } from './button.svelte'; 7 | 8 | export { 9 | // 10 | Root as Button, 11 | buttonVariants, 12 | Root, 13 | type ButtonProps, 14 | type ButtonSize, 15 | type ButtonVariant, 16 | type ButtonProps as Props, 17 | }; 18 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/collapsible/index.ts: -------------------------------------------------------------------------------- 1 | import { Collapsible as CollapsiblePrimitive } from 'bits-ui'; 2 | 3 | const Root = CollapsiblePrimitive.Root; 4 | const Trigger = CollapsiblePrimitive.Trigger; 5 | const Content = CollapsiblePrimitive.Content; 6 | 7 | export { 8 | // 9 | Root as Collapsible, 10 | Content as CollapsibleContent, 11 | Trigger as CollapsibleTrigger, 12 | Content, 13 | Root, 14 | Trigger, 15 | }; 16 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/command/command-dialog.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 27 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/command/command-empty.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 16 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/command/command-group.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 21 | {#if heading} 22 | 25 | {heading} 26 | 27 | {/if} 28 | 29 | 30 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/command/command-input.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
    15 | 16 | 25 |
    26 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/command/command-item.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/command/command-link-item.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/command/command-list.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/command/command-separator.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/command/command-shortcut.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 22 | {@render children?.()} 23 | 24 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/command/command.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 22 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/command/index.ts: -------------------------------------------------------------------------------- 1 | import { Command as CommandPrimitive } from 'bits-ui'; 2 | 3 | import Dialog from './command-dialog.svelte'; 4 | import Empty from './command-empty.svelte'; 5 | import Group from './command-group.svelte'; 6 | import Input from './command-input.svelte'; 7 | import Item from './command-item.svelte'; 8 | import LinkItem from './command-link-item.svelte'; 9 | import List from './command-list.svelte'; 10 | import Separator from './command-separator.svelte'; 11 | import Shortcut from './command-shortcut.svelte'; 12 | import Root from './command.svelte'; 13 | 14 | const Loading = CommandPrimitive.Loading; 15 | 16 | export { 17 | // 18 | Root as Command, 19 | Dialog as CommandDialog, 20 | Empty as CommandEmpty, 21 | Group as CommandGroup, 22 | Input as CommandInput, 23 | Item as CommandItem, 24 | LinkItem as CommandLinkItem, 25 | List as CommandList, 26 | Loading as CommandLoading, 27 | Separator as CommandSeparator, 28 | Shortcut as CommandShortcut, 29 | Dialog, 30 | Empty, 31 | Group, 32 | Input, 33 | Item, 34 | LinkItem, 35 | List, 36 | Loading, 37 | Root, 38 | Separator, 39 | Shortcut, 40 | }; 41 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/custom/app-breadcrumb.svelte: -------------------------------------------------------------------------------- 1 | 29 | 30 | 31 | 32 | {#each breadcrumbSegments as segment, index} 33 | {#if index < breadcrumbSegments.length - 1} 34 | 37 | 45 | 46 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/custom/app-sidebar.svelte: -------------------------------------------------------------------------------- 1 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/custom/error-feedback.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | {#if message} 11 |

    14 | {message} 15 |

    16 | {/if} 17 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/custom/nav-secondary.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | {#each items as item (item.title)} 22 | 23 | 24 | {#snippet child({ props })} 25 | 26 | {#if item.icon} 27 | 28 | {/if} 29 | {item.title} 30 | 31 | {/snippet} 32 | 33 | 34 | {/each} 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/custom/ripple.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
    9 | {#each { length: 8 } as iteration, i} 10 |
    22 | {/each} 23 |
    24 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dialog/dialog-content.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | 25 | 33 | {@render children?.()} 34 | 37 | 38 | Close 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dialog/dialog-description.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dialog/dialog-footer.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
    22 | {@render children?.()} 23 |
    24 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dialog/dialog-header.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
    19 | {@render children?.()} 20 |
    21 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dialog/dialog-overlay.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dialog/dialog-title.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dialog/index.ts: -------------------------------------------------------------------------------- 1 | import { Dialog as DialogPrimitive } from 'bits-ui'; 2 | 3 | import Content from './dialog-content.svelte'; 4 | import Description from './dialog-description.svelte'; 5 | import Footer from './dialog-footer.svelte'; 6 | import Header from './dialog-header.svelte'; 7 | import Overlay from './dialog-overlay.svelte'; 8 | import Title from './dialog-title.svelte'; 9 | 10 | const Root = DialogPrimitive.Root; 11 | const Trigger = DialogPrimitive.Trigger; 12 | const Close = DialogPrimitive.Close; 13 | const Portal = DialogPrimitive.Portal; 14 | 15 | export { 16 | Close, 17 | Content, 18 | Description, 19 | // 20 | Root as Dialog, 21 | Close as DialogClose, 22 | Content as DialogContent, 23 | Description as DialogDescription, 24 | Footer as DialogFooter, 25 | Header as DialogHeader, 26 | Overlay as DialogOverlay, 27 | Portal as DialogPortal, 28 | Title as DialogTitle, 29 | Trigger as DialogTrigger, 30 | Footer, 31 | Header, 32 | Overlay, 33 | Portal, 34 | Root, 35 | Title, 36 | Trigger, 37 | }; 38 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 | 33 | {#snippet children({ checked, indeterminate })} 34 | 35 | {#if indeterminate} 36 | 37 | {:else} 38 | 39 | {/if} 40 | 41 | {@render childrenProp?.()} 42 | {/snippet} 43 | 44 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 26 | 27 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 20 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 24 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 |
    22 | {@render children?.()} 23 |
    24 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 25 | {#snippet children({ checked })} 26 | 27 | {#if checked} 28 | 29 | {/if} 30 | 31 | {@render childrenProp?.({ checked })} 32 | {/snippet} 33 | 34 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 19 | {@render children?.()} 20 | 21 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 26 | {@render children?.()} 27 | 28 | 29 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/dropdown-menu/index.ts: -------------------------------------------------------------------------------- 1 | import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; 2 | import CheckboxItem from './dropdown-menu-checkbox-item.svelte'; 3 | import Content from './dropdown-menu-content.svelte'; 4 | import GroupHeading from './dropdown-menu-group-heading.svelte'; 5 | import Item from './dropdown-menu-item.svelte'; 6 | import Label from './dropdown-menu-label.svelte'; 7 | import RadioItem from './dropdown-menu-radio-item.svelte'; 8 | import Separator from './dropdown-menu-separator.svelte'; 9 | import Shortcut from './dropdown-menu-shortcut.svelte'; 10 | import SubContent from './dropdown-menu-sub-content.svelte'; 11 | import SubTrigger from './dropdown-menu-sub-trigger.svelte'; 12 | 13 | const Sub = DropdownMenuPrimitive.Sub; 14 | const Root = DropdownMenuPrimitive.Root; 15 | const Trigger = DropdownMenuPrimitive.Trigger; 16 | const Group = DropdownMenuPrimitive.Group; 17 | const RadioGroup = DropdownMenuPrimitive.RadioGroup; 18 | 19 | export { 20 | CheckboxItem, 21 | Content, 22 | Root as DropdownMenu, 23 | CheckboxItem as DropdownMenuCheckboxItem, 24 | Content as DropdownMenuContent, 25 | Group as DropdownMenuGroup, 26 | GroupHeading as DropdownMenuGroupHeading, 27 | Item as DropdownMenuItem, 28 | Label as DropdownMenuLabel, 29 | RadioGroup as DropdownMenuRadioGroup, 30 | RadioItem as DropdownMenuRadioItem, 31 | Separator as DropdownMenuSeparator, 32 | Shortcut as DropdownMenuShortcut, 33 | Sub as DropdownMenuSub, 34 | SubContent as DropdownMenuSubContent, 35 | SubTrigger as DropdownMenuSubTrigger, 36 | Trigger as DropdownMenuTrigger, 37 | Group, 38 | GroupHeading, 39 | Item, 40 | Label, 41 | RadioGroup, 42 | RadioItem, 43 | Root, 44 | Separator, 45 | Shortcut, 46 | Sub, 47 | SubContent, 48 | SubTrigger, 49 | Trigger, 50 | }; 51 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/input-otp/index.ts: -------------------------------------------------------------------------------- 1 | import Group from './input-otp-group.svelte'; 2 | import Separator from './input-otp-separator.svelte'; 3 | import Slot from './input-otp-slot.svelte'; 4 | import Root from './input-otp.svelte'; 5 | 6 | export { 7 | Group, 8 | Root as InputOTP, 9 | Group as InputOTPGroup, 10 | Separator as InputOTPSeparator, 11 | Slot as InputOTPSlot, 12 | Root, 13 | Separator, 14 | Slot, 15 | }; 16 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/input-otp/input-otp-group.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
    15 | {@render children?.()} 16 |
    17 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/input-otp/input-otp-separator.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 |
    14 | {#if children} 15 | {@render children?.()} 16 | {:else} 17 | 18 | {/if} 19 |
    20 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/input-otp/input-otp-slot.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 24 | {cell.char} 25 | {#if cell.hasFakeCaret} 26 |
    29 | 32 |
    33 | {/if} 34 |
    35 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/input-otp/input-otp.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 23 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/input/index.ts: -------------------------------------------------------------------------------- 1 | import Root from './input.svelte'; 2 | 3 | export { 4 | // 5 | Root as Input, 6 | Root, 7 | }; 8 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/input/input.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 23 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/label/index.ts: -------------------------------------------------------------------------------- 1 | import Root from './label.svelte'; 2 | 3 | export { 4 | // 5 | Root as Label, 6 | Root, 7 | }; 8 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/label/label.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/separator/index.ts: -------------------------------------------------------------------------------- 1 | import Root from './separator.svelte'; 2 | 3 | export { 4 | Root, 5 | // 6 | Root as Separator, 7 | }; 8 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/separator/separator.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 23 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sheet/index.ts: -------------------------------------------------------------------------------- 1 | import { Dialog as SheetPrimitive } from 'bits-ui'; 2 | import Content from './sheet-content.svelte'; 3 | import Description from './sheet-description.svelte'; 4 | import Footer from './sheet-footer.svelte'; 5 | import Header from './sheet-header.svelte'; 6 | import Overlay from './sheet-overlay.svelte'; 7 | import Title from './sheet-title.svelte'; 8 | 9 | const Root = SheetPrimitive.Root; 10 | const Close = SheetPrimitive.Close; 11 | const Trigger = SheetPrimitive.Trigger; 12 | const Portal = SheetPrimitive.Portal; 13 | 14 | export { 15 | Close, 16 | Content, 17 | Description, 18 | Footer, 19 | Header, 20 | Overlay, 21 | Portal, 22 | Root, 23 | // 24 | Root as Sheet, 25 | Close as SheetClose, 26 | Content as SheetContent, 27 | Description as SheetDescription, 28 | Footer as SheetFooter, 29 | Header as SheetHeader, 30 | Overlay as SheetOverlay, 31 | Portal as SheetPortal, 32 | Title as SheetTitle, 33 | Trigger as SheetTrigger, 34 | Title, 35 | Trigger, 36 | }; 37 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sheet/sheet-content.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 | 44 | 45 | 46 | 47 | 52 | {@render children?.()} 53 | 56 | 57 | Close 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sheet/sheet-description.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sheet/sheet-footer.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
    22 | {@render children?.()} 23 |
    24 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sheet/sheet-header.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
    19 | {@render children?.()} 20 |
    21 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sheet/sheet-overlay.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 22 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sheet/sheet-title.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/constants.ts: -------------------------------------------------------------------------------- 1 | export const SIDEBAR_COOKIE_NAME = 'sidebar:state'; 2 | export const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; 3 | export const SIDEBAR_WIDTH = '16rem'; 4 | export const SIDEBAR_WIDTH_MOBILE = '18rem'; 5 | export const SIDEBAR_WIDTH_ICON = '3rem'; 6 | export const SIDEBAR_KEYBOARD_SHORTCUT = 'b'; 7 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/index.ts: -------------------------------------------------------------------------------- 1 | import { useSidebar } from './context.svelte.js'; 2 | import Content from './sidebar-content.svelte'; 3 | import Footer from './sidebar-footer.svelte'; 4 | import GroupAction from './sidebar-group-action.svelte'; 5 | import GroupContent from './sidebar-group-content.svelte'; 6 | import GroupLabel from './sidebar-group-label.svelte'; 7 | import Group from './sidebar-group.svelte'; 8 | import Header from './sidebar-header.svelte'; 9 | import Input from './sidebar-input.svelte'; 10 | import Inset from './sidebar-inset.svelte'; 11 | import MenuAction from './sidebar-menu-action.svelte'; 12 | import MenuBadge from './sidebar-menu-badge.svelte'; 13 | import MenuButton from './sidebar-menu-button.svelte'; 14 | import MenuItem from './sidebar-menu-item.svelte'; 15 | import MenuSkeleton from './sidebar-menu-skeleton.svelte'; 16 | import MenuSubButton from './sidebar-menu-sub-button.svelte'; 17 | import MenuSubItem from './sidebar-menu-sub-item.svelte'; 18 | import MenuSub from './sidebar-menu-sub.svelte'; 19 | import Menu from './sidebar-menu.svelte'; 20 | import Provider from './sidebar-provider.svelte'; 21 | import Rail from './sidebar-rail.svelte'; 22 | import Separator from './sidebar-separator.svelte'; 23 | import Trigger from './sidebar-trigger.svelte'; 24 | import Root from './sidebar.svelte'; 25 | 26 | export { 27 | Content, 28 | Footer, 29 | Group, 30 | GroupAction, 31 | GroupContent, 32 | GroupLabel, 33 | Header, 34 | Input, 35 | Inset, 36 | Menu, 37 | MenuAction, 38 | MenuBadge, 39 | MenuButton, 40 | MenuItem, 41 | MenuSkeleton, 42 | MenuSub, 43 | MenuSubButton, 44 | MenuSubItem, 45 | Provider, 46 | Rail, 47 | Root, 48 | Separator, 49 | // 50 | Root as Sidebar, 51 | Content as SidebarContent, 52 | Footer as SidebarFooter, 53 | Group as SidebarGroup, 54 | GroupAction as SidebarGroupAction, 55 | GroupContent as SidebarGroupContent, 56 | GroupLabel as SidebarGroupLabel, 57 | Header as SidebarHeader, 58 | Input as SidebarInput, 59 | Inset as SidebarInset, 60 | Menu as SidebarMenu, 61 | MenuAction as SidebarMenuAction, 62 | MenuBadge as SidebarMenuBadge, 63 | MenuButton as SidebarMenuButton, 64 | MenuItem as SidebarMenuItem, 65 | MenuSkeleton as SidebarMenuSkeleton, 66 | MenuSub as SidebarMenuSub, 67 | MenuSubButton as SidebarMenuSubButton, 68 | MenuSubItem as SidebarMenuSubItem, 69 | Provider as SidebarProvider, 70 | Rail as SidebarRail, 71 | Separator as SidebarSeparator, 72 | Trigger as SidebarTrigger, 73 | Trigger, 74 | useSidebar, 75 | }; 76 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-content.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
    23 | {@render children?.()} 24 |
    25 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-footer.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
    20 | {@render children?.()} 21 |
    22 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-group-action.svelte: -------------------------------------------------------------------------------- 1 | 29 | 30 | {#if child} 31 | {@render child({ props: propObj })} 32 | {:else} 33 | 36 | {/if} 37 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-group-content.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
    20 | {@render children?.()} 21 |
    22 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-group-label.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 | {#if child} 29 | {@render child({ props: mergedProps })} 30 | {:else} 31 |
    32 | {@render children?.()} 33 |
    34 | {/if} 35 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-group.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
    20 | {@render children?.()} 21 |
    22 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-header.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
    20 | {@render children?.()} 21 |
    22 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-input.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 24 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-inset.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
    23 | {@render children?.()} 24 |
    25 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-menu-action.svelte: -------------------------------------------------------------------------------- 1 | 36 | 37 | {#if child} 38 | {@render child({ props: mergedProps })} 39 | {:else} 40 | 43 | {/if} 44 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-menu-badge.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
    28 | {@render children?.()} 29 |
    30 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-menu-item.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
  • 20 | {@render children?.()} 21 |
  • 22 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 |
    27 | {#if showIcon} 28 | 29 | {/if} 30 | 35 | {@render children?.()} 36 |
    37 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte: -------------------------------------------------------------------------------- 1 | 36 | 37 | {#if child} 38 | {@render child({ props: mergedProps })} 39 | {:else} 40 | 41 | {@render children?.()} 42 | 43 | {/if} 44 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 |
  • 13 | {@render children?.()} 14 |
  • 15 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-menu-sub.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
      24 | {@render children?.()} 25 |
    26 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-menu.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 |
      23 | {@render children?.()} 24 |
    25 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-provider.svelte: -------------------------------------------------------------------------------- 1 | 38 | 39 | 40 | 41 | 42 |
    51 | {@render children?.()} 52 |
    53 |
    54 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-rail.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | 40 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-separator.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sidebar/sidebar-trigger.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | 35 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/skeleton/index.ts: -------------------------------------------------------------------------------- 1 | import Root from './skeleton.svelte'; 2 | 3 | export { 4 | Root, 5 | // 6 | Root as Skeleton, 7 | }; 8 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/skeleton/skeleton.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 |
    20 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sonner/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Toaster } from './sonner.svelte'; 2 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/sonner/sonner.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 26 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/tooltip/index.ts: -------------------------------------------------------------------------------- 1 | import { Tooltip as TooltipPrimitive } from 'bits-ui'; 2 | import Content from './tooltip-content.svelte'; 3 | 4 | const Root = TooltipPrimitive.Root; 5 | const Trigger = TooltipPrimitive.Trigger; 6 | const Provider = TooltipPrimitive.Provider; 7 | 8 | export { 9 | Content, 10 | Provider, 11 | Root, 12 | // 13 | Root as Tooltip, 14 | Content as TooltipContent, 15 | Provider as TooltipProvider, 16 | Trigger as TooltipTrigger, 17 | Trigger, 18 | }; 19 | -------------------------------------------------------------------------------- /resources/js/lib/components/ui/tooltip/tooltip-content.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 22 | -------------------------------------------------------------------------------- /resources/js/lib/hooks/is-mobile.svelte.ts: -------------------------------------------------------------------------------- 1 | import { MediaQuery } from 'svelte/reactivity'; 2 | 3 | const MOBILE_BREAKPOINT = 768; 4 | 5 | export class IsMobile extends MediaQuery { 6 | constructor() { 7 | super(`max-width: ${MOBILE_BREAKPOINT - 1}px`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /resources/js/lib/layouts/AuthenticatedLayout.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | 19 |
    22 |
    23 | 24 | 25 | 26 |
    27 |
    28 |
    29 | 30 |
    31 |
    32 | 33 | 34 |
    35 | -------------------------------------------------------------------------------- /resources/js/lib/layouts/AuthenticationLayout.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 |
    14 |
    15 |
    16 | 17 |
    20 | 21 |
    22 | Svelte Inertia Laravel 23 | 24 |
    25 |
    26 |
    27 | {@render children?.()} 28 |
    29 |
    30 |
    31 |
    32 | -------------------------------------------------------------------------------- /resources/js/lib/types/global.d.ts: -------------------------------------------------------------------------------- 1 | import { PageProps as InertiaPageProps } from '@inertiajs/core'; 2 | import { AxiosInstance } from 'axios'; 3 | import { route as ziggyRoute } from 'ziggy-js'; 4 | import { PageProps as AppPageProps } from '../../types'; 5 | 6 | declare global { 7 | interface Window { 8 | axios: AxiosInstance; 9 | } 10 | 11 | var route: typeof ziggyRoute; 12 | } 13 | 14 | declare module '@inertiajs/core' { 15 | interface PageProps extends InertiaPageProps, AppPageProps {} 16 | } 17 | -------------------------------------------------------------------------------- /resources/js/lib/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import { Config } from 'ziggy-js'; 2 | 3 | export interface User { 4 | id: number; 5 | name: string; 6 | email: string; 7 | email_verified_at?: string; 8 | profile_photo_url: string | undefined; 9 | two_factor_confirmed_at: string | null; 10 | } 11 | 12 | export type PageProps< 13 | T extends Record = Record, 14 | > = T & { 15 | auth: { 16 | user: User; 17 | }; 18 | ziggy: Config & { location: string }; 19 | }; 20 | -------------------------------------------------------------------------------- /resources/js/lib/types/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /resources/js/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from 'clsx'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /resources/js/pages/Auth/ForgotPassword.svelte: -------------------------------------------------------------------------------- 1 | 26 | 27 | 28 | Forgot Password 29 | 30 | 31 | 32 |
    33 |
    34 |

    Forgot your password?

    35 |

    36 | No problem. Let us know your email address and we will email you 37 | a password reset link. 38 |

    39 |
    40 | 41 | {#if status} 42 |
    45 | {status} 46 |
    47 | {/if} 48 | 49 | 50 | 51 |
    52 |
    53 | 54 | 65 |
    66 | 67 | 70 | 71 |
    72 | Did you remember?{' '} 73 | 77 | Log in 78 | 79 |
    80 |
    81 | 82 |
    83 | -------------------------------------------------------------------------------- /resources/js/pages/Auth/ForgotPasswordSent.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | Password Reset Link Sent 10 | 11 | 12 | 13 |
    14 |
    15 |
    16 | 17 |
    18 |

    Check your email

    19 |

    20 | We have sent a password reset link to your email address. Please 21 | check your inbox and follow the instructions to reset your 22 | password. 23 |

    24 |
    25 | 26 |
    27 | 30 | 31 |
    32 | Didn't receive the email?{' '} 33 | 37 | Try again 38 | 39 |
    40 |
    41 |
    42 |
    43 | -------------------------------------------------------------------------------- /resources/js/pages/Auth/TwoFactorChallenge.svelte: -------------------------------------------------------------------------------- 1 | 30 | 31 | 32 | Login 33 | 34 | 35 | 36 |
    37 |
    38 | 39 |

    Two-Factor Authentication

    40 |
    41 | 42 | 48 | {#snippet children({ cells })} 49 | 50 | {#each cells as cell} 51 | 52 | {/each} 53 | 54 | {/snippet} 55 | 56 | 57 |

    58 | Please enter the one-time password from your authenticator app. 59 |

    60 | 61 | 62 |
    63 |
    64 | -------------------------------------------------------------------------------- /resources/js/pages/Auth/VerifyEmail.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | Verify Email 22 | 23 | 24 | 25 |
    26 |
    27 |

    Verify your email

    28 |

    29 | Please verify your email address by clicking the link in the 30 | email we sent you. If you haven't received it, we can send a new 31 | verification link. 32 |

    33 |
    34 | 35 | {#if status === 'verification-link-sent'} 36 |
    39 | A new verification link has been sent to your email address. 40 |
    41 | {/if} 42 | 43 |
    44 | 47 | 48 |
    49 | 55 | Log out 56 | 57 |
    58 |
    59 |
    60 |
    61 | -------------------------------------------------------------------------------- /resources/js/pages/Dashboard.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Dashboard 7 | 8 | 9 | 10 |
    11 |
    12 |
    13 |
    14 |
    15 |
    16 |
    19 |
    20 |
    21 | -------------------------------------------------------------------------------- /resources/js/pages/Profile/Show.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | Profile 17 | 18 | 19 | 20 |
    21 | {#if isUpdateProfileEnabled} 22 |
    23 | 24 |
    25 | {/if} 26 | 27 | {#if isUpdatePasswordEnabled} 28 |
    29 | 30 |
    31 | {/if} 32 | 33 |
    34 | 35 |
    36 |
    37 |
    38 | -------------------------------------------------------------------------------- /resources/js/pages/Security/Show.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | Security 14 | 15 | 16 | 17 |
    18 | {#if isTwoFactorAuthenticationFeatureEnabled} 19 |
    20 | 21 |
    22 | {/if} 23 |
    24 |
    25 | -------------------------------------------------------------------------------- /resources/js/pages/Welcome.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | Welcome 12 | 13 | 14 |
    15 |
    16 |
    19 | 20 |
    23 | 24 |
    25 | Svelte Inertia Laravel 26 | 27 | 28 | 51 |
    52 |
    53 | 54 |
    55 |
    56 |

    57 | Your landing goes here 58 |

    59 | 60 |
    61 |
    62 |
    63 | -------------------------------------------------------------------------------- /resources/js/ssr.ts: -------------------------------------------------------------------------------- 1 | import { createInertiaApp, type ResolvedComponent } from '@inertiajs/svelte'; 2 | import createServer from '@inertiajs/svelte/server'; 3 | import type { RouteName } from 'ziggy-js'; 4 | import { route } from '../../vendor/tightenco/ziggy'; 5 | 6 | createServer((page) => 7 | createInertiaApp({ 8 | page, 9 | resolve: (name) => { 10 | const pages = import.meta.glob( 11 | './Pages/**/*.svelte', 12 | { eager: true }, 13 | ); 14 | return pages[`./Pages/${name}.svelte`]; 15 | }, 16 | setup: ({ el, App, props }) => { 17 | // @ts-expect-error - Ziggy types 18 | global.route = (name: RouteName, params?, absolute?) => 19 | route(name, params, absolute, { 20 | // @ts-expect-error - Ziggy types 21 | ...page.props.ziggy, 22 | // @ts-expect-error - Ziggy types 23 | location: new URL(page.props.ziggy.location), 24 | }); 25 | 26 | return new App({ target: el!, props }); 27 | }, 28 | }), 29 | ); 30 | -------------------------------------------------------------------------------- /resources/views/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | @routes 17 | @vite(['resources/js/app.ts', "resources/js/Pages/{$page['component']}.svelte"]) 18 | @inertiaHead 19 | 20 | 21 | 22 | @inertia 23 | 24 | 25 | -------------------------------------------------------------------------------- /routes/auth.php: -------------------------------------------------------------------------------- 1 | group(function () { 9 | Route::get('/register', function () { 10 | return Inertia::render('Auth/Register', [ 11 | 'isRegisterEnabled' => Features::enabled(Features::registration()), 12 | ]); 13 | })->name('register'); 14 | 15 | Route::get('/login', function () { 16 | return Inertia::render('Auth/Login', [ 17 | 'isRegisterEnabled' => Features::enabled(Features::registration()), 18 | ]); 19 | })->name('login'); 20 | 21 | Route::get('/login/challenge', function () { 22 | return Inertia::render('Auth/TwoFactorChallenge'); 23 | })->name('two-factor.login'); 24 | 25 | Route::get('/forgot-password', function () { 26 | return Inertia::render('Auth/ForgotPassword'); 27 | })->name('auth.forgot-password'); 28 | 29 | Route::get('/reset-password/{token}', function (string $token) { 30 | return Inertia::render('Auth/PasswordReset', [ 31 | 'token' => $token, 32 | 'email' => request('email'), 33 | ]); 34 | })->name('password.reset'); 35 | 36 | Route::get('/forgot-password/sent', function () { 37 | return Inertia::render('Auth/ForgotPasswordSent'); 38 | })->name('forgot-password.sent'); 39 | }); 40 | 41 | Route::middleware('auth')->group(function () { 42 | Route::post('/confirm-password', [ConfirmablePasswordController::class, 'store'])->name('password.confirm'); 43 | 44 | Route::get('/verify-email', function () { 45 | return Inertia::render('Auth/VerifyEmail'); 46 | })->name('verification.notice'); 47 | }); 48 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | Route::has('login'), 12 | 'canRegister' => Route::has('register'), 13 | 'laravelVersion' => Application::VERSION, 14 | 'phpVersion' => PHP_VERSION, 15 | ]); 16 | }); 17 | 18 | Route::middleware('auth', 'verified')->group(function () { 19 | // Dashboard 20 | Route::get('/dashboard', function () { 21 | return Inertia::render('Dashboard'); 22 | })->name('dashboard'); 23 | 24 | // Profile 25 | Route::get('/account/profile', [ProfileController::class, 'show'])->name('profile.show'); 26 | Route::patch('/account/profile', [ProfileController::class, 'update'])->name('profile.update'); 27 | Route::delete('/account/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); 28 | 29 | // Security 30 | Route::get('/account/security', [SecurityController::class, 'show'])->name('security.show'); 31 | }); 32 | 33 | require __DIR__.'/auth.php'; 34 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !private/ 3 | !public/ 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /storage/app/private/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 2 | 3 | export default { 4 | // Consult https://svelte.dev/docs#compile-time-svelte-preprocess 5 | // for more information about preprocessors 6 | preprocess: vitePreprocess(), 7 | }; 8 | -------------------------------------------------------------------------------- /tests/Feature/Auth/AuthenticationTest.php: -------------------------------------------------------------------------------- 1 | get('/login'); 7 | 8 | $response->assertStatus(200); 9 | }); 10 | 11 | test('users can authenticate using the login screen', function () { 12 | $user = User::factory()->create(); 13 | 14 | $response = $this->post('/login', [ 15 | 'email' => $user->email, 16 | 'password' => 'password', 17 | ]); 18 | 19 | $this->assertAuthenticated(); 20 | $response->assertRedirect(route('dashboard', absolute: false)); 21 | }); 22 | 23 | test('users can not authenticate with invalid password', function () { 24 | $user = User::factory()->create(); 25 | 26 | $this->post('/login', [ 27 | 'email' => $user->email, 28 | 'password' => 'wrong-password', 29 | ]); 30 | 31 | $this->assertGuest(); 32 | }); 33 | 34 | test('users can logout', function () { 35 | $user = User::factory()->create(); 36 | 37 | $response = $this->actingAs($user)->post('/logout'); 38 | 39 | $this->assertGuest(); 40 | $response->assertRedirect('/'); 41 | }); 42 | -------------------------------------------------------------------------------- /tests/Feature/Auth/EmailVerificationTest.php: -------------------------------------------------------------------------------- 1 | unverified()->create(); 10 | 11 | $response = $this->actingAs($user)->get('/verify-email'); 12 | 13 | $response->assertStatus(200); 14 | }); 15 | 16 | test('email can be verified', function () { 17 | $user = User::factory()->unverified()->create(); 18 | 19 | Event::fake(); 20 | 21 | $verificationUrl = URL::temporarySignedRoute( 22 | 'verification.verify', 23 | now()->addMinutes(60), 24 | ['id' => $user->id, 'hash' => sha1($user->email)] 25 | ); 26 | 27 | $response = $this->actingAs($user)->get($verificationUrl); 28 | 29 | Event::assertDispatched(Verified::class); 30 | expect($user->fresh()->hasVerifiedEmail())->toBeTrue(); 31 | $response->assertRedirect(route('dashboard', absolute: false).'?verified=1'); 32 | }); 33 | 34 | test('email is not verified with invalid hash', function () { 35 | $user = User::factory()->unverified()->create(); 36 | 37 | $verificationUrl = URL::temporarySignedRoute( 38 | 'verification.verify', 39 | now()->addMinutes(60), 40 | ['id' => $user->id, 'hash' => sha1('wrong-email')] 41 | ); 42 | 43 | $this->actingAs($user)->get($verificationUrl); 44 | 45 | expect($user->fresh()->hasVerifiedEmail())->toBeFalse(); 46 | }); 47 | -------------------------------------------------------------------------------- /tests/Feature/Auth/PasswordConfirmationTest.php: -------------------------------------------------------------------------------- 1 | create(); 7 | 8 | $response = $this->actingAs($user)->get('/confirm-password'); 9 | 10 | $response->assertStatus(200); 11 | }); 12 | 13 | test('password can be confirmed', function () { 14 | $user = User::factory()->create(); 15 | 16 | $response = $this->actingAs($user)->post('/confirm-password', [ 17 | 'password' => 'password', 18 | ]); 19 | 20 | $response->assertRedirect(); 21 | $response->assertSessionHasNoErrors(); 22 | }); 23 | 24 | test('password is not confirmed with invalid password', function () { 25 | $user = User::factory()->create(); 26 | 27 | $response = $this->actingAs($user)->post('/confirm-password', [ 28 | 'password' => 'wrong-password', 29 | ]); 30 | 31 | $response->assertSessionHasErrors(); 32 | }); 33 | -------------------------------------------------------------------------------- /tests/Feature/Auth/PasswordResetTest.php: -------------------------------------------------------------------------------- 1 | get('/forgot-password'); 9 | 10 | $response->assertStatus(200); 11 | }); 12 | 13 | test('reset password link can be requested', function () { 14 | Notification::fake(); 15 | 16 | $user = User::factory()->create(); 17 | 18 | $this->post('/forgot-password', ['email' => $user->email]); 19 | 20 | Notification::assertSentTo($user, ResetPassword::class); 21 | }); 22 | 23 | test('reset password screen can be rendered', function () { 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, function ($notification) { 31 | $response = $this->get('/reset-password/'.$notification->token); 32 | 33 | $response->assertStatus(200); 34 | 35 | return true; 36 | }); 37 | }); 38 | 39 | test('password can be reset with valid token', function () { 40 | Notification::fake(); 41 | 42 | $user = User::factory()->create(); 43 | 44 | $this->post('/forgot-password', ['email' => $user->email]); 45 | 46 | Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) { 47 | $response = $this->post('/reset-password', [ 48 | 'token' => $notification->token, 49 | 'email' => $user->email, 50 | 'password' => 'password', 51 | 'password_confirmation' => 'password', 52 | ]); 53 | 54 | $response 55 | ->assertSessionHasNoErrors() 56 | ->assertRedirect(route('dashboard')); 57 | 58 | return true; 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /tests/Feature/Auth/PasswordUpdateTest.php: -------------------------------------------------------------------------------- 1 | create(); 8 | 9 | $response = $this 10 | ->actingAs($user) 11 | ->from('/profile') 12 | ->put('/password', [ 13 | 'current_password' => 'password', 14 | 'password' => 'new-password', 15 | 'password_confirmation' => 'new-password', 16 | ]); 17 | 18 | $response 19 | ->assertSessionHasNoErrors() 20 | ->assertRedirect('/profile'); 21 | 22 | $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); 23 | }); 24 | 25 | test('user cannot update password with incorrect current password', function () { 26 | $user = User::factory()->create(); 27 | 28 | $response = $this 29 | ->actingAs($user) 30 | ->from('/profile') 31 | ->put('/password', [ 32 | 'current_password' => 'wrong-password', 33 | 'password' => 'new-password', 34 | 'password_confirmation' => 'new-password', 35 | ]); 36 | 37 | $response 38 | ->assertSessionHasErrors('current_password') 39 | ->assertRedirect('/profile'); 40 | }); 41 | 42 | test('new password must be different from current password', function () { 43 | $user = User::factory()->create(); 44 | 45 | $response = $this 46 | ->actingAs($user) 47 | ->from('/profile') 48 | ->put('/password', [ 49 | 'current_password' => 'password', 50 | 'password' => 'password', 51 | 'password_confirmation' => 'password', 52 | ]); 53 | 54 | $response 55 | ->assertSessionHasErrors('password') 56 | ->assertRedirect('/profile'); 57 | }); 58 | 59 | test('new password must be confirmed', function () { 60 | $user = User::factory()->create(); 61 | 62 | $response = $this 63 | ->actingAs($user) 64 | ->from('/profile') 65 | ->put('/password', [ 66 | 'current_password' => 'password', 67 | 'password' => 'new-password', 68 | 'password_confirmation' => 'different-password', 69 | ]); 70 | 71 | $response 72 | ->assertSessionHasErrors('password') 73 | ->assertRedirect('/profile'); 74 | }); 75 | 76 | test('new password must be at least 8 characters', function () { 77 | $user = User::factory()->create(); 78 | 79 | $response = $this 80 | ->actingAs($user) 81 | ->from('/profile') 82 | ->put('/password', [ 83 | 'current_password' => 'password', 84 | 'password' => 'short', 85 | 'password_confirmation' => 'short', 86 | ]); 87 | 88 | $response 89 | ->assertSessionHasErrors('password') 90 | ->assertRedirect('/profile'); 91 | }); 92 | -------------------------------------------------------------------------------- /tests/Feature/Auth/RegistrationTest.php: -------------------------------------------------------------------------------- 1 | get('/register'); 5 | 6 | $response->assertStatus(200); 7 | }); 8 | 9 | test('new users can register', function () { 10 | $response = $this->post('/register', [ 11 | 'name' => 'Test User', 12 | 'email' => 'test@example.com', 13 | 'password' => 'password', 14 | 'password_confirmation' => 'password', 15 | ]); 16 | 17 | $this->assertAuthenticated(); 18 | $response->assertRedirect(route('dashboard', absolute: false)); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/'); 5 | 6 | $response->assertStatus(200); 7 | }); 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | toBeTrue(); 5 | }); 6 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "resources/js/**/*.ts", 5 | "resources/js/**/*.svelte", 6 | "resources/js/**/*.d.ts", 7 | "eslint.config.js", 8 | "*.js", 9 | "*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/svelte/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "strict": true, 8 | "isolatedModules": true, 9 | "target": "ESNext", 10 | "esModuleInterop": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "skipLibCheck": true, 13 | "noEmit": true, 14 | "baseUrl": ".", 15 | "paths": { 16 | "$lib": ["./resources/js/lib"], 17 | "$lib/*": ["./resources/js/lib/*"], 18 | "@/*": ["./resources/js/*"], 19 | "ziggy-js": ["./vendor/tightenco/ziggy"] 20 | } 21 | }, 22 | "include": [ 23 | "resources/js/**/*.ts", 24 | "resources/js/**/*.svelte", 25 | "resources/js/**/*.d.ts" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { svelte } from '@sveltejs/vite-plugin-svelte'; 2 | import laravel from 'laravel-vite-plugin'; 3 | import { defineConfig } from 'vite'; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | svelte(), 8 | laravel({ 9 | input: 'resources/js/app.ts', 10 | ssr: 'resources/js/ssr.ts', 11 | refresh: true, 12 | }), 13 | ], 14 | resolve: { 15 | alias: { 16 | $lib: '/resources/js/lib', 17 | }, 18 | }, 19 | }); 20 | --------------------------------------------------------------------------------