├── .DS_Store ├── .editorconfig ├── .env.example ├── .gitattributes ├── .gitignore ├── .nvmrc ├── .prettierrc ├── README.md ├── app ├── .DS_Store ├── Casts │ └── HumanReadableTime.php ├── Concerns │ └── BackupDatabase.php ├── Events │ └── BackupCompleted.php ├── Http │ ├── Controllers │ │ ├── Auth │ │ │ ├── AuthenticatedSessionController.php │ │ │ ├── ConfirmablePasswordController.php │ │ │ ├── EmailVerificationNotificationController.php │ │ │ ├── EmailVerificationPromptController.php │ │ │ ├── NewPasswordController.php │ │ │ ├── PasswordController.php │ │ │ ├── PasswordResetLinkController.php │ │ │ ├── RegisteredUserController.php │ │ │ └── VerifyEmailController.php │ │ ├── BackupController.php │ │ ├── Controller.php │ │ └── ProfileController.php │ ├── Middleware │ │ └── HandleInertiaRequests.php │ └── Requests │ │ ├── Auth │ │ └── LoginRequest.php │ │ └── ProfileUpdateRequest.php ├── Jobs │ └── ProcessDbBackup.php ├── Models │ ├── DatabaseBackup.php │ └── User.php └── Providers │ └── AppServiceProvider.php ├── artisan ├── bootstrap ├── app.php ├── cache │ └── .gitignore └── providers.php ├── components.json ├── composer.json ├── composer.lock ├── config ├── app.php ├── auth.php ├── broadcasting.php ├── cache.php ├── database.php ├── devdojo │ └── auth │ │ ├── appearance.php │ │ ├── descriptions.php │ │ ├── language.php │ │ ├── providers.php │ │ └── settings.php ├── filesystems.php ├── logging.php ├── mail.php ├── queue.php ├── reverb.php ├── services.php └── session.php ├── database ├── .DS_Store ├── .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 │ ├── 2024_04_24_000001_add_user_social_provider_table.php │ ├── 2024_04_24_000002_update_passwords_field_to_be_nullable.php │ ├── 2024_05_07_000003_add_two_factor_auth_columns.php │ └── 2024_08_18_080518_create_database_backups_table.php └── seeders │ ├── DatabaseSeeder.php │ └── UserSeeder.php ├── package-lock.json ├── package.json ├── phpunit.xml ├── postcss.config.js ├── public ├── .DS_Store ├── .htaccess ├── auth │ ├── app.css │ ├── build │ │ ├── assets │ │ │ ├── scripts.js │ │ │ └── styles.css │ │ └── manifest.json │ └── img │ │ ├── favicon-dark.png │ │ ├── favicon.ico │ │ └── favicon.png ├── favicon.ico ├── images │ ├── avatar.jpg │ ├── jsonfakery-avatar.svg │ └── sample-logo.svg ├── index.php └── robots.txt ├── resources ├── .DS_Store ├── css │ ├── app.css │ └── custom-font.css ├── fonts │ ├── geist_mono │ │ ├── GeistMonoVF.woff │ │ └── GeistMonoVF.woff2 │ ├── geist_sans │ │ ├── GeistVF.woff │ │ └── GeistVF.woff2 │ └── jetbrains_mono │ │ ├── .DS_Store │ │ ├── generator_config.txt │ │ ├── jetbrainsmono-regular-demo.html │ │ ├── jetbrainsmono-regular-webfont.woff │ │ ├── jetbrainsmono-regular-webfont.woff2 │ │ ├── specimen_files │ │ ├── grid_12-825-55-15.css │ │ └── specimen_stylesheet.css │ │ └── stylesheet.css ├── js │ ├── Components │ │ ├── AppHeader.tsx │ │ ├── ApplicationLogo.tsx │ │ ├── Checkbox.tsx │ │ ├── CustomToaster.tsx │ │ ├── CustomTooltip.tsx │ │ ├── DangerButton.tsx │ │ ├── Dropdown.tsx │ │ ├── Footer.tsx │ │ ├── InputError.tsx │ │ ├── InputLabel.tsx │ │ ├── Modal.tsx │ │ ├── NavLink.tsx │ │ ├── Navigation.tsx │ │ ├── PrimaryButton.tsx │ │ ├── ResponsiveNavLink.tsx │ │ ├── SearchButton.tsx │ │ ├── SearchMenu.tsx │ │ ├── SecondaryButton.tsx │ │ ├── TaskEmails.tsx │ │ ├── TextInput.tsx │ │ ├── UserDropdownMenu.tsx │ │ ├── statistics │ │ │ ├── Applicants.tsx │ │ │ ├── MonthlyRevenue.tsx │ │ │ ├── SalesChart.tsx │ │ │ ├── Subscriptions.tsx │ │ │ └── UserEngagement.tsx │ │ ├── ui │ │ │ ├── avatar.tsx │ │ │ ├── badge.tsx │ │ │ ├── button.tsx │ │ │ ├── card.tsx │ │ │ ├── chart.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── table.tsx │ │ │ ├── toast.tsx │ │ │ ├── toaster.tsx │ │ │ ├── tooltip.tsx │ │ │ └── use-toast.ts │ │ └── v1.upload-progress.tsx │ ├── Layouts │ │ ├── AuthenticatedLayout.tsx │ │ └── GuestLayout.tsx │ ├── Pages │ │ ├── Auth │ │ │ ├── ConfirmPassword.tsx │ │ │ ├── ForgotPassword.tsx │ │ │ ├── Login.tsx │ │ │ ├── Register.tsx │ │ │ ├── ResetPassword.tsx │ │ │ └── VerifyEmail.tsx │ │ ├── BackupDatabase.tsx │ │ ├── Dashboard.tsx │ │ ├── Profile │ │ │ ├── Edit.tsx │ │ │ └── Partials │ │ │ │ ├── DeleteUserForm.tsx │ │ │ │ ├── UpdatePasswordForm.tsx │ │ │ │ └── UpdateProfileInformationForm.tsx │ │ └── Welcome.tsx │ ├── app.tsx │ ├── bootstrap.ts │ ├── echo.js │ ├── lib │ │ └── utils.ts │ ├── menus.tsx │ └── types │ │ ├── global.d.ts │ │ ├── index.d.ts │ │ └── vite-env.d.ts └── views │ └── app.blade.php ├── routes ├── auth.php ├── channels.php ├── console.php └── web.php ├── storage ├── .DS_Store ├── app │ ├── .gitignore │ └── public │ │ └── .gitignore ├── framework │ ├── .DS_Store │ ├── .gitignore │ ├── cache │ │ ├── .gitignore │ │ └── data │ │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ ├── testing │ │ └── .gitignore │ └── views │ │ └── .gitignore └── logs │ └── .gitignore ├── tailwind.config.js ├── tests ├── .DS_Store ├── 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.json └── vite.config.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/.DS_Store -------------------------------------------------------------------------------- /.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="Untitled Admin v1.0" 2 | APP_ENV=local 3 | APP_KEY=base64:4nIfwmQw/KAYK9eKOYodFrkXZwJW+eMlSRRhouaSXn4= 4 | APP_DEBUG=true 5 | APP_TIMEZONE=UTC 6 | APP_URL=http://localhost 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 | BCRYPT_ROUNDS=12 16 | 17 | LOG_CHANNEL=stack 18 | LOG_STACK=single 19 | LOG_DEPRECATIONS_CHANNEL=null 20 | LOG_LEVEL=debug 21 | 22 | DB_CONNECTION=mysql 23 | DB_HOST=127.0.0.1 24 | DB_PORT=3306 25 | DB_DATABASE=resend 26 | DB_USERNAME=root 27 | DB_PASSWORD= 28 | 29 | SESSION_DRIVER=database 30 | SESSION_LIFETIME=120 31 | SESSION_ENCRYPT=false 32 | SESSION_PATH=/ 33 | SESSION_DOMAIN=null 34 | 35 | BROADCAST_CONNECTION=log 36 | FILESYSTEM_DISK=local 37 | QUEUE_CONNECTION=database 38 | 39 | CACHE_STORE=database 40 | CACHE_PREFIX= 41 | 42 | MEMCACHED_HOST=127.0.0.1 43 | 44 | REDIS_CLIENT=phpredis 45 | REDIS_HOST=127.0.0.1 46 | REDIS_PASSWORD=null 47 | REDIS_PORT=6379 48 | 49 | RESEND_API_KEY= 50 | RESEND_WEBHOOK_SECRET= 51 | 52 | MAIL_MAILER=log 53 | MAIL_HOST=127.0.0.1 54 | MAIL_PORT=2525 55 | MAIL_USERNAME=null 56 | MAIL_PASSWORD=null 57 | MAIL_ENCRYPTION=null 58 | MAIL_FROM_ADDRESS="hello@example.com" 59 | MAIL_FROM_NAME="${APP_NAME}" 60 | 61 | AWS_ACCESS_KEY_ID= 62 | AWS_SECRET_ACCESS_KEY= 63 | AWS_DEFAULT_REGION=us-east-1 64 | AWS_BUCKET= 65 | AWS_USE_PATH_STYLE_ENDPOINT=false 66 | 67 | VITE_APP_NAME="${APP_NAME}" 68 | 69 | REVERB_APP_ID= 70 | REVERB_APP_KEY= 71 | REVERB_APP_SECRET= 72 | REVERB_HOST= 73 | REVERB_PORT= 74 | REVERB_SCHEME= 75 | 76 | VITE_REVERB_APP_KEY="${REVERB_APP_KEY}" 77 | VITE_REVERB_HOST="${REVERB_HOST}" 78 | VITE_REVERB_PORT="${REVERB_PORT}" 79 | VITE_REVERB_SCHEME="${REVERB_SCHEME}" 80 | -------------------------------------------------------------------------------- /.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 | /node_modules 3 | /public/build 4 | /public/hot 5 | /public/storage 6 | /storage/*.key 7 | /vendor 8 | .env 9 | .env.backup 10 | .env.production 11 | .phpactor.json 12 | .phpunit.result.cache 13 | Homestead.json 14 | Homestead.yaml 15 | auth.json 16 | npm-debug.log 17 | yarn-error.log 18 | /.fleet 19 | /.idea 20 | /.vscode 21 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "singleQuote": true, 4 | "semi": false, 5 | "arrowParens": "avoid", 6 | "trailingComma": "none", 7 | "plugins": ["prettier-plugin-tailwindcss"] 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Laravel Logo

2 | 3 |

4 | Build Status 5 | Total Downloads 6 | Latest Stable Version 7 | License 8 |

9 | 10 | ## About Laravel 11 | 12 | Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: 13 | 14 | - [Simple, fast routing engine](https://laravel.com/docs/routing). 15 | - [Powerful dependency injection container](https://laravel.com/docs/container). 16 | - Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage. 17 | - Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent). 18 | - Database agnostic [schema migrations](https://laravel.com/docs/migrations). 19 | - [Robust background job processing](https://laravel.com/docs/queues). 20 | - [Real-time event broadcasting](https://laravel.com/docs/broadcasting). 21 | 22 | Laravel is accessible, powerful, and provides tools required for large, robust applications. 23 | 24 | ## Learning Laravel 25 | 26 | Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. 27 | 28 | You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch. 29 | 30 | If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. 31 | 32 | ## Laravel Sponsors 33 | 34 | We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com). 35 | 36 | ### Premium Partners 37 | 38 | - **[Vehikl](https://vehikl.com/)** 39 | - **[Tighten Co.](https://tighten.co)** 40 | - **[WebReinvent](https://webreinvent.com/)** 41 | - **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)** 42 | - **[64 Robots](https://64robots.com)** 43 | - **[Curotec](https://www.curotec.com/services/technologies/laravel/)** 44 | - **[Cyber-Duck](https://cyber-duck.co.uk)** 45 | - **[DevSquad](https://devsquad.com/hire-laravel-developers)** 46 | - **[Jump24](https://jump24.co.uk)** 47 | - **[Redberry](https://redberry.international/laravel/)** 48 | - **[Active Logic](https://activelogic.com)** 49 | - **[byte5](https://byte5.de)** 50 | - **[OP.GG](https://op.gg)** 51 | 52 | ## Contributing 53 | 54 | Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). 55 | 56 | ## Code of Conduct 57 | 58 | In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). 59 | 60 | ## Security Vulnerabilities 61 | 62 | If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. 63 | 64 | ## License 65 | 66 | The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). 67 | -------------------------------------------------------------------------------- /app/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/app/.DS_Store -------------------------------------------------------------------------------- /app/Casts/HumanReadableTime.php: -------------------------------------------------------------------------------- 1 | $attributes 15 | */ 16 | public function get(Model $model, string $key, mixed $value, array $attributes): mixed 17 | { 18 | return Carbon::parse($value)->diffForHumans(); 19 | } 20 | 21 | /** 22 | * Prepare the given value for storage. 23 | * 24 | * @param array $attributes 25 | */ 26 | public function set(Model $model, string $key, mixed $value, array $attributes): mixed 27 | { 28 | return $value; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Concerns/BackupDatabase.php: -------------------------------------------------------------------------------- 1 | $gzip_file_name, 'user_id' => $user_id]); 30 | 31 | // MySQL dump command, streamed directly into gzip 32 | $backup_cmd = "/opt/homebrew/bin/mysqldump --user=$username --password=$password --host=$host --port=$port --single-transaction --quick --lock-tables=false $database | gzip > $gzip_file_path"; 33 | 34 | // Execute the command 35 | exec($backup_cmd, $output, $result); 36 | 37 | if ($result) { 38 | Log::error("Backup failed for user $user_id: " . implode("\n", $output)); 39 | throw new \Exception('Database backup failed.'); 40 | } 41 | 42 | return $gzip_file_name; 43 | 44 | } catch (\Exception $e) { 45 | // Handle or rethrow the exception 46 | Log::error("Backup process failed for user $user_id: " . $e->getMessage()); 47 | throw $e; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/Events/BackupCompleted.php: -------------------------------------------------------------------------------- 1 | 29 | */ 30 | public function broadcastOn(): array 31 | { 32 | return [ 33 | new PrivateChannel("backup.$this->user_id"), 34 | ]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/AuthenticatedSessionController.php: -------------------------------------------------------------------------------- 1 | Route::has('password.request'), 23 | 'status' => session('status'), 24 | ]); 25 | } 26 | 27 | /** 28 | * Handle an incoming authentication request. 29 | */ 30 | public function store(LoginRequest $request): RedirectResponse 31 | { 32 | $request->authenticate(); 33 | 34 | $request->session()->regenerate(); 35 | 36 | return redirect()->intended(route('dashboard', absolute: false)); 37 | } 38 | 39 | /** 40 | * Destroy an authenticated session. 41 | */ 42 | public function destroy(Request $request): RedirectResponse 43 | { 44 | Auth::guard('web')->logout(); 45 | 46 | $request->session()->invalidate(); 47 | 48 | $request->session()->regenerateToken(); 49 | 50 | return redirect('/'); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ConfirmablePasswordController.php: -------------------------------------------------------------------------------- 1 | validate([ 29 | 'email' => $request->user()->email, 30 | 'password' => $request->password, 31 | ])) { 32 | throw ValidationException::withMessages([ 33 | 'password' => __('auth.password'), 34 | ]); 35 | } 36 | 37 | $request->session()->put('auth.password_confirmed_at', time()); 38 | 39 | return redirect()->intended(route('dashboard', absolute: false)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/EmailVerificationNotificationController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail()) { 17 | return redirect()->intended(route('dashboard', absolute: false)); 18 | } 19 | 20 | $request->user()->sendEmailVerificationNotification(); 21 | 22 | return back()->with('status', 'verification-link-sent'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/EmailVerificationPromptController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail() 19 | ? redirect()->intended(route('dashboard', absolute: false)) 20 | : Inertia::render('Auth/VerifyEmail', ['status' => session('status')]); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/NewPasswordController.php: -------------------------------------------------------------------------------- 1 | $request->email, 26 | 'token' => $request->route('token'), 27 | ]); 28 | } 29 | 30 | /** 31 | * Handle an incoming new password request. 32 | * 33 | * @throws \Illuminate\Validation\ValidationException 34 | */ 35 | public function store(Request $request): RedirectResponse 36 | { 37 | $request->validate([ 38 | 'token' => 'required', 39 | 'email' => 'required|email', 40 | 'password' => ['required', 'confirmed', Rules\Password::defaults()], 41 | ]); 42 | 43 | // Here we will attempt to reset the user's password. If it is successful we 44 | // will update the password on an actual user model and persist it to the 45 | // database. Otherwise we will parse the error and return the response. 46 | $status = Password::reset( 47 | $request->only('email', 'password', 'password_confirmation', 'token'), 48 | function ($user) use ($request) { 49 | $user->forceFill([ 50 | 'password' => Hash::make($request->password), 51 | 'remember_token' => Str::random(60), 52 | ])->save(); 53 | 54 | event(new PasswordReset($user)); 55 | } 56 | ); 57 | 58 | // If the password was successfully reset, we will redirect the user back to 59 | // the application's home authenticated view. If there is an error we can 60 | // redirect them back to where they came from with their error message. 61 | if ($status == Password::PASSWORD_RESET) { 62 | return redirect()->route('login')->with('status', __($status)); 63 | } 64 | 65 | throw ValidationException::withMessages([ 66 | 'email' => [trans($status)], 67 | ]); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/PasswordController.php: -------------------------------------------------------------------------------- 1 | validate([ 19 | 'current_password' => ['required', 'current_password'], 20 | 'password' => ['required', Password::defaults(), 'confirmed'], 21 | ]); 22 | 23 | $request->user()->update([ 24 | 'password' => Hash::make($validated['password']), 25 | ]); 26 | 27 | return back(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/PasswordResetLinkController.php: -------------------------------------------------------------------------------- 1 | session('status'), 22 | ]); 23 | } 24 | 25 | /** 26 | * Handle an incoming password reset link request. 27 | * 28 | * @throws \Illuminate\Validation\ValidationException 29 | */ 30 | public function store(Request $request): RedirectResponse 31 | { 32 | $request->validate([ 33 | 'email' => 'required|email', 34 | ]); 35 | 36 | // We will send the password reset link to this user. Once we have attempted 37 | // to send the link, we will examine the response then see the message we 38 | // need to show to the user. Finally, we'll send out a proper response. 39 | $status = Password::sendResetLink( 40 | $request->only('email') 41 | ); 42 | 43 | if ($status == Password::RESET_LINK_SENT) { 44 | return back()->with('status', __($status)); 45 | } 46 | 47 | throw ValidationException::withMessages([ 48 | 'email' => [trans($status)], 49 | ]); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/RegisteredUserController.php: -------------------------------------------------------------------------------- 1 | validate([ 34 | 'name' => 'required|string|max:255', 35 | 'email' => 'required|string|lowercase|email|max:255|unique:'.User::class, 36 | 'password' => ['required', 'confirmed', Rules\Password::defaults()], 37 | ]); 38 | 39 | $user = User::create([ 40 | 'name' => $request->name, 41 | 'email' => $request->email, 42 | 'password' => Hash::make($request->password), 43 | ]); 44 | 45 | event(new Registered($user)); 46 | 47 | Auth::login($user); 48 | 49 | return redirect(route('dashboard', absolute: false)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/VerifyEmailController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail()) { 18 | return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); 19 | } 20 | 21 | if ($request->user()->markEmailAsVerified()) { 22 | event(new Verified($request->user())); 23 | } 24 | 25 | return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Http/Controllers/BackupController.php: -------------------------------------------------------------------------------- 1 | id); 25 | unlink(storage_path('app/'. $request->backup_file)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | $request->user() instanceof MustVerifyEmail, 23 | 'status' => session('status'), 24 | ]); 25 | } 26 | 27 | /** 28 | * Update the user's profile information. 29 | */ 30 | public function update(ProfileUpdateRequest $request): RedirectResponse 31 | { 32 | $request->user()->fill($request->validated()); 33 | 34 | if ($request->user()->isDirty('email')) { 35 | $request->user()->email_verified_at = null; 36 | } 37 | 38 | $request->user()->save(); 39 | 40 | return Redirect::route('profile.edit'); 41 | } 42 | 43 | /** 44 | * Delete the user's account. 45 | */ 46 | public function destroy(Request $request): RedirectResponse 47 | { 48 | $request->validate([ 49 | 'password' => ['required', 'current_password'], 50 | ]); 51 | 52 | $user = $request->user(); 53 | 54 | Auth::logout(); 55 | 56 | $user->delete(); 57 | 58 | $request->session()->invalidate(); 59 | $request->session()->regenerateToken(); 60 | 61 | return Redirect::to('/'); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/Http/Middleware/HandleInertiaRequests.php: -------------------------------------------------------------------------------- 1 | 29 | */ 30 | public function share(Request $request): array 31 | { 32 | return [ 33 | ...parent::share($request), 34 | 'auth' => [ 35 | 'user' => $request->user(), 36 | ], 37 | 'app' => config('app.name'), 38 | ]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Http/Requests/Auth/LoginRequest.php: -------------------------------------------------------------------------------- 1 | 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 | 15 | */ 16 | public function rules(): array 17 | { 18 | return [ 19 | 'name' => ['required', 'string', 'max:255'], 20 | 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', Rule::unique(User::class)->ignore($this->user()->id)], 21 | ]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/Jobs/ProcessDbBackup.php: -------------------------------------------------------------------------------- 1 | performBackup($this->user_id); 34 | 35 | broadcast(new BackupCompleted($this->user_id, 'The database has been successfully backed up.', $backup_file)); 36 | } catch (\Exception $e) { 37 | broadcast(new BackupCompleted($this->user_id, 'An error has occurred: ' . $e->getMessage(), $backup_file)); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Models/DatabaseBackup.php: -------------------------------------------------------------------------------- 1 | HumanReadableTime::class, 21 | 'updated_at' => HumanReadableTime::class, 22 | ]; 23 | 24 | public function user(): BelongsTo 25 | { 26 | return $this->belongsTo(User::class); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Models/User.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | protected $fillable = [ 23 | 'name', 24 | 'email', 25 | 'password', 26 | ]; 27 | 28 | /** 29 | * The attributes that should be hidden for serialization. 30 | * 31 | * @var array 32 | */ 33 | protected $hidden = [ 34 | 'password', 35 | 'remember_token', 36 | ]; 37 | 38 | /** 39 | * Get the attributes that should be cast. 40 | * 41 | * @return array 42 | */ 43 | protected function casts(): array 44 | { 45 | return [ 46 | 'email_verified_at' => 'datetime', 47 | 'password' => 'hashed', 48 | ]; 49 | } 50 | 51 | public function databaseBackups(): HasMany 52 | { 53 | return $this->hasMany(DatabaseBackup::class); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | handleCommand(new ArgvInput); 14 | 15 | exit($status); 16 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | withRouting( 9 | web: __DIR__.'/../routes/web.php', 10 | commands: __DIR__.'/../routes/console.php', 11 | channels: __DIR__.'/../routes/channels.php', 12 | health: '/up', 13 | ) 14 | ->withMiddleware(function (Middleware $middleware) { 15 | $middleware->web(append: [ 16 | \App\Http\Middleware\HandleInertiaRequests::class, 17 | \Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets::class, 18 | ]); 19 | 20 | // 21 | }) 22 | ->withExceptions(function (Exceptions $exceptions) { 23 | // 24 | })->create(); 25 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /bootstrap/providers.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'guard' => env('AUTH_GUARD', 'web'), 18 | 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), 19 | ], 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Authentication Guards 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Next, you may define every authentication guard for your application. 27 | | Of course, a great default configuration has been defined for you 28 | | which utilizes session storage plus the Eloquent user provider. 29 | | 30 | | All authentication guards have a user provider, which defines how the 31 | | users are actually retrieved out of your database or other storage 32 | | system used by the application. Typically, Eloquent is utilized. 33 | | 34 | | Supported: "session" 35 | | 36 | */ 37 | 38 | 'guards' => [ 39 | 'web' => [ 40 | 'driver' => 'session', 41 | 'provider' => 'users', 42 | ], 43 | ], 44 | 45 | /* 46 | |-------------------------------------------------------------------------- 47 | | User Providers 48 | |-------------------------------------------------------------------------- 49 | | 50 | | All authentication guards have a user provider, which defines how the 51 | | users are actually retrieved out of your database or other storage 52 | | system used by the application. Typically, Eloquent is utilized. 53 | | 54 | | If you have multiple user tables or models you may configure multiple 55 | | providers to represent the model / table. These providers may then 56 | | be assigned to any extra authentication guards you have defined. 57 | | 58 | | Supported: "database", "eloquent" 59 | | 60 | */ 61 | 62 | 'providers' => [ 63 | 'users' => [ 64 | 'driver' => 'eloquent', 65 | 'model' => env('AUTH_MODEL', App\Models\User::class), 66 | ], 67 | 68 | // 'users' => [ 69 | // 'driver' => 'database', 70 | // 'table' => 'users', 71 | // ], 72 | ], 73 | 74 | /* 75 | |-------------------------------------------------------------------------- 76 | | Resetting Passwords 77 | |-------------------------------------------------------------------------- 78 | | 79 | | These configuration options specify the behavior of Laravel's password 80 | | reset functionality, including the table utilized for token storage 81 | | and the user provider that is invoked to actually retrieve users. 82 | | 83 | | The expiry time is the number of minutes that each reset token will be 84 | | considered valid. This security feature keeps tokens short-lived so 85 | | they have less time to be guessed. You may change this as needed. 86 | | 87 | | The throttle setting is the number of seconds a user must wait before 88 | | generating more password reset tokens. This prevents the user from 89 | | quickly generating a very large amount of password reset tokens. 90 | | 91 | */ 92 | 93 | 'passwords' => [ 94 | 'users' => [ 95 | 'provider' => 'users', 96 | 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), 97 | 'expire' => 60, 98 | 'throttle' => 60, 99 | ], 100 | ], 101 | 102 | /* 103 | |-------------------------------------------------------------------------- 104 | | Password Confirmation Timeout 105 | |-------------------------------------------------------------------------- 106 | | 107 | | Here you may define the amount of seconds before a password confirmation 108 | | window expires and users are asked to re-enter their password via the 109 | | confirmation screen. By default, the timeout lasts for three hours. 110 | | 111 | */ 112 | 113 | 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), 114 | 115 | ]; 116 | -------------------------------------------------------------------------------- /config/broadcasting.php: -------------------------------------------------------------------------------- 1 | env('BROADCAST_CONNECTION', 'null'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Broadcast Connections 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the broadcast connections that will be used 26 | | to broadcast events to other systems or over WebSockets. Samples of 27 | | each available type of connection are provided inside this array. 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'reverb' => [ 34 | 'driver' => 'reverb', 35 | 'key' => env('REVERB_APP_KEY'), 36 | 'secret' => env('REVERB_APP_SECRET'), 37 | 'app_id' => env('REVERB_APP_ID'), 38 | 'options' => [ 39 | 'host' => env('REVERB_HOST'), 40 | 'port' => env('REVERB_PORT', 443), 41 | 'scheme' => env('REVERB_SCHEME', 'https'), 42 | 'useTLS' => env('REVERB_SCHEME', 'https') === 'https', 43 | ], 44 | 'client_options' => [ 45 | // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html 46 | ], 47 | ], 48 | 49 | 'pusher' => [ 50 | 'driver' => 'pusher', 51 | 'key' => env('PUSHER_APP_KEY'), 52 | 'secret' => env('PUSHER_APP_SECRET'), 53 | 'app_id' => env('PUSHER_APP_ID'), 54 | 'options' => [ 55 | 'cluster' => env('PUSHER_APP_CLUSTER'), 56 | 'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com', 57 | 'port' => env('PUSHER_PORT', 443), 58 | 'scheme' => env('PUSHER_SCHEME', 'https'), 59 | 'encrypted' => true, 60 | 'useTLS' => env('PUSHER_SCHEME', 'https') === 'https', 61 | ], 62 | 'client_options' => [ 63 | // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html 64 | ], 65 | ], 66 | 67 | 'ably' => [ 68 | 'driver' => 'ably', 69 | 'key' => env('ABLY_KEY'), 70 | ], 71 | 72 | 'log' => [ 73 | 'driver' => 'log', 74 | ], 75 | 76 | 'null' => [ 77 | 'driver' => 'null', 78 | ], 79 | 80 | ], 81 | 82 | ]; 83 | -------------------------------------------------------------------------------- /config/cache.php: -------------------------------------------------------------------------------- 1 | env('CACHE_STORE', 'database'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Cache Stores 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the cache "stores" for your application as 26 | | well as their drivers. You may even define multiple stores for the 27 | | same cache driver to group types of items stored in your caches. 28 | | 29 | | Supported drivers: "array", "database", "file", "memcached", 30 | | "redis", "dynamodb", "octane", "null" 31 | | 32 | */ 33 | 34 | 'stores' => [ 35 | 36 | 'array' => [ 37 | 'driver' => 'array', 38 | 'serialize' => false, 39 | ], 40 | 41 | 'database' => [ 42 | 'driver' => 'database', 43 | 'table' => env('DB_CACHE_TABLE', 'cache'), 44 | 'connection' => env('DB_CACHE_CONNECTION'), 45 | 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), 46 | ], 47 | 48 | 'file' => [ 49 | 'driver' => 'file', 50 | 'path' => storage_path('framework/cache/data'), 51 | 'lock_path' => storage_path('framework/cache/data'), 52 | ], 53 | 54 | 'memcached' => [ 55 | 'driver' => 'memcached', 56 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), 57 | 'sasl' => [ 58 | env('MEMCACHED_USERNAME'), 59 | env('MEMCACHED_PASSWORD'), 60 | ], 61 | 'options' => [ 62 | // Memcached::OPT_CONNECT_TIMEOUT => 2000, 63 | ], 64 | 'servers' => [ 65 | [ 66 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 67 | 'port' => env('MEMCACHED_PORT', 11211), 68 | 'weight' => 100, 69 | ], 70 | ], 71 | ], 72 | 73 | 'redis' => [ 74 | 'driver' => 'redis', 75 | 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), 76 | 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), 77 | ], 78 | 79 | 'dynamodb' => [ 80 | 'driver' => 'dynamodb', 81 | 'key' => env('AWS_ACCESS_KEY_ID'), 82 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 83 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 84 | 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), 85 | 'endpoint' => env('DYNAMODB_ENDPOINT'), 86 | ], 87 | 88 | 'octane' => [ 89 | 'driver' => 'octane', 90 | ], 91 | 92 | ], 93 | 94 | /* 95 | |-------------------------------------------------------------------------- 96 | | Cache Key Prefix 97 | |-------------------------------------------------------------------------- 98 | | 99 | | When utilizing the APC, database, memcached, Redis, and DynamoDB cache 100 | | stores, there might be other applications using the same cache. For 101 | | that reason, you may prefix every cache key to avoid collisions. 102 | | 103 | */ 104 | 105 | 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'), 106 | 107 | ]; 108 | -------------------------------------------------------------------------------- /config/devdojo/auth/descriptions.php: -------------------------------------------------------------------------------- 1 | [ 8 | 'redirect_after_auth' => 'Where should the user be redirected to after they are authenticated?', 9 | 'registration_show_password_same_screen' => 'During registrations, show the password on the same screen or show it on an individual screen.', 10 | 'registration_include_name_field' => 'During registration, include the Name field.', 11 | 'registration_include_password_confirmation_field' => 'During registration, include the Password Confirmation field.', 12 | 'registration_require_email_verification' => 'During registration, require users to verify their email.', 13 | 'enable_branding' => 'This will toggle on/off the Auth branding at the bottom of each auth screen. Consider leaving on to support and help grow this project.', 14 | 'dev_mode' => 'This is for development mode, when set in Dev Mode Assets will be loaded from Vite', 15 | 'enable_2fa' => 'Enable the ability for users to turn on Two Factor Authentication', 16 | 'login_show_social_providers' => 'Show the social providers login buttons on the login form', 17 | 'center_align_social_provider_button_content' => 'Center align the content in the social provider button?', 18 | 'social_providers_location' => 'The location of the social provider buttons (top or bottom)', 19 | ], 20 | ]; 21 | -------------------------------------------------------------------------------- /config/devdojo/auth/language.php: -------------------------------------------------------------------------------- 1 | [ 8 | 'page_title' => 'Sign in', 9 | 'headline' => 'Sign in', 10 | 'subheadline' => 'Login to your account below', 11 | 'show_subheadline' => false, 12 | 'email_address' => 'Email Address', 13 | 'password' => 'Password', 14 | 'edit' => 'Edit', 15 | 'button' => 'Continue', 16 | 'forget_password' => 'Forget your password?', 17 | 'dont_have_an_account' => "Don't have an account?", 18 | 'sign_up' => 'Sign up', 19 | 'social_auth_authenticated_message' => 'You have been authenticated via __social_providers_list__. Please login to that network below.', 20 | 'change_email' => 'Change Email', 21 | ], 22 | 'register' => [ 23 | 'page_title' => 'Sign up', 24 | 'headline' => 'Sign up', 25 | 'subheadline' => 'Register for your free account below.', 26 | 'show_subheadline' => false, 27 | 'name' => 'Name', 28 | 'email_address' => 'Email Address', 29 | 'password' => 'Password', 30 | 'password_confirmation' => 'Confirm Password', 31 | 'already_have_an_account' => 'Already have an account?', 32 | 'sign_in' => 'Sign in', 33 | 'button' => 'Continue', 34 | ], 35 | 'verify' => [ 36 | 'page_title' => 'Verify Your Account', 37 | 'headline' => 'Verify your email address', 38 | 'subheadline' => 'Before you can proceed you must verify your email.', 39 | 'show_subheadline' => false, 40 | 'description' => 'Before proceeding, please check your email for a verification link. If you did not receive the email,', 41 | 'new_request_link' => 'click here to request another', 42 | 'new_link_sent' => 'A new link has been sent to your email address.', 43 | 'or' => 'Or', 44 | 'logout' => 'click here to logout', 45 | ], 46 | 'passwordConfirm' => [ 47 | 'page_title' => 'Confirm Your Password', 48 | 'headline' => 'Confirm Password', 49 | 'subheadline' => 'Be sure to confirm your password below', 50 | 'show_subheadline' => false, 51 | 'password' => 'Password', 52 | 'button' => 'Confirm password', 53 | ], 54 | 'passwordResetRequest' => [ 55 | 'page_title' => 'Request a Password Reset', 56 | 'headline' => 'Reset password', 57 | 'subheadline' => 'Enter your email below to reset your password', 58 | 'show_subheadline' => false, 59 | 'email' => 'Email Address', 60 | 'button' => 'Send password reset link', 61 | 'or' => 'or', 62 | 'return_to_login' => 'return to login', 63 | ], 64 | 'passwordReset' => [ 65 | 'page_title' => 'Reset Your Password', 66 | 'headline' => 'Reset Password', 67 | 'subheadline' => 'Reset your password below', 68 | 'show_subheadline' => false, 69 | 'email' => 'Email Address', 70 | 'password' => 'Password', 71 | 'password_confirm' => 'Confirm Password', 72 | 'button' => 'Reset Password', 73 | ], 74 | 'twoFactorChallenge' => [ 75 | 'page_title' => 'Two Factor Challenge', 76 | 'headline_auth' => 'Authentication Code', 77 | 'subheadline_auth' => 'Enter the authentication code provided by your authenticator application.', 78 | 'show_subheadline_auth' => false, 79 | 'headline_recovery' => 'Recovery Code', 80 | 'subheadline_recovery' => 'Please confirm access to your account by entering one of your emergency recovery codes.', 81 | 'show_subheadline_recovery' => false, 82 | ], 83 | 84 | ]; 85 | -------------------------------------------------------------------------------- /config/devdojo/auth/settings.php: -------------------------------------------------------------------------------- 1 | '/dashboard', 8 | 'registration_show_password_same_screen' => true, 9 | 'registration_include_name_field' => true, 10 | 'registration_include_password_confirmation_field' => true, 11 | 'registration_require_email_verification' => false, 12 | 'enable_branding' => true, 13 | 'dev_mode' => false, 14 | 'enable_2fa' => false, // Enable or disable 2FA functionality globally 15 | 'login_show_social_providers' => true, 16 | 'center_align_social_provider_button_content' => false, 17 | 'social_providers_location' => 'bottom', 18 | ]; 19 | -------------------------------------------------------------------------------- /config/filesystems.php: -------------------------------------------------------------------------------- 1 | env('FILESYSTEM_DISK', 'local'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Filesystem Disks 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Below you may configure as many filesystem disks as necessary, and you 24 | | may even configure multiple disks for the same driver. Examples for 25 | | most supported storage drivers are configured here for reference. 26 | | 27 | | Supported drivers: "local", "ftp", "sftp", "s3" 28 | | 29 | */ 30 | 31 | 'disks' => [ 32 | 33 | 'local' => [ 34 | 'driver' => 'local', 35 | 'root' => storage_path('app'), 36 | 'throw' => false, 37 | ], 38 | 39 | 'public' => [ 40 | 'driver' => 'local', 41 | 'root' => storage_path('app/public'), 42 | 'url' => env('APP_URL').'/storage', 43 | 'visibility' => 'public', 44 | 'throw' => false, 45 | ], 46 | 47 | 's3' => [ 48 | 'driver' => 's3', 49 | 'key' => env('AWS_ACCESS_KEY_ID'), 50 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 51 | 'region' => env('AWS_DEFAULT_REGION'), 52 | 'bucket' => env('AWS_BUCKET'), 53 | 'url' => env('AWS_URL'), 54 | 'endpoint' => env('AWS_ENDPOINT'), 55 | 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), 56 | 'throw' => false, 57 | ], 58 | 59 | ], 60 | 61 | /* 62 | |-------------------------------------------------------------------------- 63 | | Symbolic Links 64 | |-------------------------------------------------------------------------- 65 | | 66 | | Here you may configure the symbolic links that will be created when the 67 | | `storage:link` Artisan command is executed. The array keys should be 68 | | the locations of the links and the values should be their targets. 69 | | 70 | */ 71 | 72 | 'links' => [ 73 | public_path('storage') => storage_path('app/public'), 74 | ], 75 | 76 | ]; 77 | -------------------------------------------------------------------------------- /config/mail.php: -------------------------------------------------------------------------------- 1 | env('MAIL_MAILER', 'log'), 18 | 19 | /* 20 | |-------------------------------------------------------------------------- 21 | | Mailer Configurations 22 | |-------------------------------------------------------------------------- 23 | | 24 | | Here you may configure all of the mailers used by your application plus 25 | | their respective settings. Several examples have been configured for 26 | | you and you are free to add your own as your application requires. 27 | | 28 | | Laravel supports a variety of mail "transport" drivers that can be used 29 | | when delivering an email. You may specify which one you're using for 30 | | your mailers below. You may also add additional mailers if needed. 31 | | 32 | | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", 33 | | "postmark", "resend", "log", "array", 34 | | "failover", "roundrobin" 35 | | 36 | */ 37 | 38 | 'mailers' => [ 39 | 40 | 'smtp' => [ 41 | 'transport' => 'smtp', 42 | 'url' => env('MAIL_URL'), 43 | 'host' => env('MAIL_HOST', '127.0.0.1'), 44 | 'port' => env('MAIL_PORT', 2525), 45 | 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 46 | 'username' => env('MAIL_USERNAME'), 47 | 'password' => env('MAIL_PASSWORD'), 48 | 'timeout' => null, 49 | 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url(env('APP_URL', 'http://localhost'), PHP_URL_HOST)), 50 | ], 51 | 52 | 'ses' => [ 53 | 'transport' => 'ses', 54 | ], 55 | 56 | 'postmark' => [ 57 | 'transport' => 'postmark', 58 | // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), 59 | // 'client' => [ 60 | // 'timeout' => 5, 61 | // ], 62 | ], 63 | 64 | 'resend' => [ 65 | 'transport' => 'resend', 66 | ], 67 | 68 | 'sendmail' => [ 69 | 'transport' => 'sendmail', 70 | 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), 71 | ], 72 | 73 | 'log' => [ 74 | 'transport' => 'log', 75 | 'channel' => env('MAIL_LOG_CHANNEL'), 76 | ], 77 | 78 | 'array' => [ 79 | 'transport' => 'array', 80 | ], 81 | 82 | 'failover' => [ 83 | 'transport' => 'failover', 84 | 'mailers' => [ 85 | 'smtp', 86 | 'log', 87 | ], 88 | ], 89 | 90 | 'roundrobin' => [ 91 | 'transport' => 'roundrobin', 92 | 'mailers' => [ 93 | 'ses', 94 | 'postmark', 95 | ], 96 | ], 97 | 98 | ], 99 | 100 | /* 101 | |-------------------------------------------------------------------------- 102 | | Global "From" Address 103 | |-------------------------------------------------------------------------- 104 | | 105 | | You may wish for all emails sent by your application to be sent from 106 | | the same address. Here you may specify a name and address that is 107 | | used globally for all emails that are sent by your application. 108 | | 109 | */ 110 | 111 | 'from' => [ 112 | 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), 113 | 'name' => env('MAIL_FROM_NAME', 'Example'), 114 | ], 115 | 116 | ]; 117 | -------------------------------------------------------------------------------- /config/queue.php: -------------------------------------------------------------------------------- 1 | env('QUEUE_CONNECTION', 'database'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Queue Connections 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may configure the connection options for every queue backend 24 | | used by your application. An example configuration is provided for 25 | | each backend supported by Laravel. You're also free to add more. 26 | | 27 | | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'sync' => [ 34 | 'driver' => 'sync', 35 | ], 36 | 37 | 'database' => [ 38 | 'driver' => 'database', 39 | 'connection' => env('DB_QUEUE_CONNECTION'), 40 | 'table' => env('DB_QUEUE_TABLE', 'jobs'), 41 | 'queue' => env('DB_QUEUE', 'default'), 42 | 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90), 43 | 'after_commit' => false, 44 | ], 45 | 46 | 'beanstalkd' => [ 47 | 'driver' => 'beanstalkd', 48 | 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), 49 | 'queue' => env('BEANSTALKD_QUEUE', 'default'), 50 | 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), 51 | 'block_for' => 0, 52 | 'after_commit' => false, 53 | ], 54 | 55 | 'sqs' => [ 56 | 'driver' => 'sqs', 57 | 'key' => env('AWS_ACCESS_KEY_ID'), 58 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 59 | 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), 60 | 'queue' => env('SQS_QUEUE', 'default'), 61 | 'suffix' => env('SQS_SUFFIX'), 62 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 63 | 'after_commit' => false, 64 | ], 65 | 66 | 'redis' => [ 67 | 'driver' => 'redis', 68 | 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), 69 | 'queue' => env('REDIS_QUEUE', 'default'), 70 | 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), 71 | 'block_for' => null, 72 | 'after_commit' => false, 73 | ], 74 | 75 | ], 76 | 77 | /* 78 | |-------------------------------------------------------------------------- 79 | | Job Batching 80 | |-------------------------------------------------------------------------- 81 | | 82 | | The following options configure the database and table that store job 83 | | batching information. These options can be updated to any database 84 | | connection and table which has been defined by your application. 85 | | 86 | */ 87 | 88 | 'batching' => [ 89 | 'database' => env('DB_CONNECTION', 'sqlite'), 90 | 'table' => 'job_batches', 91 | ], 92 | 93 | /* 94 | |-------------------------------------------------------------------------- 95 | | Failed Queue Jobs 96 | |-------------------------------------------------------------------------- 97 | | 98 | | These options configure the behavior of failed queue job logging so you 99 | | can control how and where failed jobs are stored. Laravel ships with 100 | | support for storing failed jobs in a simple file or in a database. 101 | | 102 | | Supported drivers: "database-uuids", "dynamodb", "file", "null" 103 | | 104 | */ 105 | 106 | 'failed' => [ 107 | 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), 108 | 'database' => env('DB_CONNECTION', 'sqlite'), 109 | 'table' => 'failed_jobs', 110 | ], 111 | 112 | ]; 113 | -------------------------------------------------------------------------------- /config/reverb.php: -------------------------------------------------------------------------------- 1 | env('REVERB_SERVER', 'reverb'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Reverb Servers 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may define details for each of the supported Reverb servers. 24 | | Each server has its own configuration options that are defined in 25 | | the array below. You should ensure all the options are present. 26 | | 27 | */ 28 | 29 | 'servers' => [ 30 | 31 | 'reverb' => [ 32 | 'host' => env('REVERB_SERVER_HOST', '0.0.0.0'), 33 | 'port' => env('REVERB_SERVER_PORT', 8080), 34 | 'hostname' => env('REVERB_HOST'), 35 | 'options' => [ 36 | 'tls' => [], 37 | ], 38 | 'max_request_size' => env('REVERB_MAX_REQUEST_SIZE', 10_000), 39 | 'scaling' => [ 40 | 'enabled' => env('REVERB_SCALING_ENABLED', false), 41 | 'channel' => env('REVERB_SCALING_CHANNEL', 'reverb'), 42 | 'server' => [ 43 | 'url' => env('REDIS_URL'), 44 | 'host' => env('REDIS_HOST', '127.0.0.1'), 45 | 'port' => env('REDIS_PORT', '6379'), 46 | 'username' => env('REDIS_USERNAME'), 47 | 'password' => env('REDIS_PASSWORD'), 48 | 'database' => env('REDIS_DB', '0'), 49 | ], 50 | ], 51 | 'pulse_ingest_interval' => env('REVERB_PULSE_INGEST_INTERVAL', 15), 52 | 'telescope_ingest_interval' => env('REVERB_TELESCOPE_INGEST_INTERVAL', 15), 53 | ], 54 | 55 | ], 56 | 57 | /* 58 | |-------------------------------------------------------------------------- 59 | | Reverb Applications 60 | |-------------------------------------------------------------------------- 61 | | 62 | | Here you may define how Reverb applications are managed. If you choose 63 | | to use the "config" provider, you may define an array of apps which 64 | | your server will support, including their connection credentials. 65 | | 66 | */ 67 | 68 | 'apps' => [ 69 | 70 | 'provider' => 'config', 71 | 72 | 'apps' => [ 73 | [ 74 | 'key' => env('REVERB_APP_KEY'), 75 | 'secret' => env('REVERB_APP_SECRET'), 76 | 'app_id' => env('REVERB_APP_ID'), 77 | 'options' => [ 78 | 'host' => env('REVERB_HOST'), 79 | 'port' => env('REVERB_PORT', 443), 80 | 'scheme' => env('REVERB_SCHEME', 'https'), 81 | 'useTLS' => env('REVERB_SCHEME', 'https') === 'https', 82 | ], 83 | 'allowed_origins' => ['*'], 84 | 'ping_interval' => env('REVERB_APP_PING_INTERVAL', 60), 85 | 'max_message_size' => env('REVERB_APP_MAX_MESSAGE_SIZE', 10_000), 86 | ], 87 | ], 88 | 89 | ], 90 | 91 | ]; 92 | -------------------------------------------------------------------------------- /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/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/database/.DS_Store -------------------------------------------------------------------------------- /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('role'); 17 | $table->string('name'); 18 | $table->string('email')->unique(); 19 | $table->timestamp('task_checked_at')->nullable(); 20 | $table->timestamp('email_verified_at')->nullable(); 21 | $table->string('password'); 22 | $table->rememberToken(); 23 | $table->timestamps(); 24 | }); 25 | 26 | Schema::create('password_reset_tokens', function (Blueprint $table) { 27 | $table->string('email')->primary(); 28 | $table->string('token'); 29 | $table->timestamp('created_at')->nullable(); 30 | }); 31 | 32 | Schema::create('sessions', function (Blueprint $table) { 33 | $table->string('id')->primary(); 34 | $table->foreignId('user_id')->nullable()->index(); 35 | $table->string('ip_address', 45)->nullable(); 36 | $table->text('user_agent')->nullable(); 37 | $table->longText('payload'); 38 | $table->integer('last_activity')->index(); 39 | }); 40 | } 41 | 42 | /** 43 | * Reverse the migrations. 44 | */ 45 | public function down(): void 46 | { 47 | Schema::dropIfExists('users'); 48 | Schema::dropIfExists('password_reset_tokens'); 49 | Schema::dropIfExists('sessions'); 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /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/2024_04_24_000001_add_user_social_provider_table.php: -------------------------------------------------------------------------------- 1 | foreignId('user_id')->constrained()->onDelete('cascade'); 16 | $table->string('provider_slug'); // maps to providers slug in the devdojo.auth.providers 17 | 18 | $table->string('provider_user_id'); 19 | $table->string('nickname')->nullable(); 20 | $table->string('name')->nullable(); 21 | $table->string('email')->nullable(); 22 | $table->string('avatar')->nullable(); 23 | $table->text('provider_data')->nullable(); // JSON data containing additional provider data we want to include 24 | 25 | $table->string('token'); 26 | $table->string('refresh_token')->nullable(); 27 | $table->timestamp('token_expires_at')->nullable(); 28 | $table->timestamps(); 29 | 30 | $table->primary(['user_id', 'provider_slug']); 31 | }); 32 | } 33 | 34 | /** 35 | * Reverse the migrations. 36 | */ 37 | public function down(): void 38 | { 39 | Schema::dropIfExists('social_provider_user'); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /database/migrations/2024_04_24_000002_update_passwords_field_to_be_nullable.php: -------------------------------------------------------------------------------- 1 | string('password')->nullable()->change(); 17 | $table->string('name')->nullable()->change(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down() 25 | { 26 | // Update records with NULL values to avoid constraint violations 27 | DB::table('users')->whereNull('name')->update(['name' => '']); 28 | DB::table('users')->whereNull('password')->update(['password' => '']); 29 | 30 | // Change the table structure 31 | Schema::table('users', function (Blueprint $table) { 32 | $table->string('name')->nullable(false)->change(); 33 | $table->string('password')->nullable(false)->change(); 34 | }); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /database/migrations/2024_05_07_000003_add_two_factor_auth_columns.php: -------------------------------------------------------------------------------- 1 | text('two_factor_secret') 18 | ->after('password') 19 | ->nullable(); 20 | } 21 | 22 | if (! Schema::hasColumn('users', 'two_factor_recovery_codes')) { 23 | $table->text('two_factor_recovery_codes') 24 | ->after('two_factor_secret') 25 | ->nullable(); 26 | } 27 | 28 | if (! Schema::hasColumn('users', 'two_factor_confirmed_at')) { 29 | $table->timestamp('two_factor_confirmed_at') 30 | ->after('two_factor_recovery_codes') 31 | ->nullable(); 32 | } 33 | 34 | }); 35 | } 36 | 37 | /** 38 | * Reverse the migrations. 39 | */ 40 | public function down(): void 41 | { 42 | Schema::table('users', function (Blueprint $table) { 43 | $table->dropColumn([ 44 | 'two_factor_secret', 45 | 'two_factor_recovery_codes', 46 | 'two_factor_confirmed_at', 47 | ]); 48 | }); 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /database/migrations/2024_08_18_080518_create_database_backups_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->integer('user_id'); 17 | $table->string('path'); 18 | $table->timestamps(); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | */ 25 | public function down(): void 26 | { 27 | Schema::dropIfExists('database_backups'); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 17 | 18 | // User::factory()->create([ 19 | // 'name' => 'Glenn Raya', 20 | // 'email' => 'glenn@example.com', 21 | // ]); 22 | $this->call([ 23 | UserSeeder::class, 24 | ]); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /database/seeders/UserSeeder.php: -------------------------------------------------------------------------------- 1 | $role) { 25 | User::create([ 26 | 'name' => fake()->name(), 27 | 'role' => $role, 28 | 'email' => fake()->unique()->safeEmail(), 29 | 'email_verified_at' => now(), 30 | 'password' => Hash::make('password'), 31 | 'remember_token' => Str::random(10), 32 | ]); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "tsc && vite build" 7 | }, 8 | "devDependencies": { 9 | "@headlessui/react": "^2.0.0", 10 | "@inertiajs/react": "^1.0.0", 11 | "@tailwindcss/forms": "^0.5.3", 12 | "@types/d3-scale": "^4.0.8", 13 | "@types/d3-shape": "^3.1.6", 14 | "@types/lodash": "^4.17.6", 15 | "@types/node": "^18.13.0", 16 | "@types/react": "^18.0.28", 17 | "@types/react-dom": "^18.0.10", 18 | "@vitejs/plugin-react": "^4.2.0", 19 | "autoprefixer": "^10.4.12", 20 | "axios": "^1.6.4", 21 | "laravel-echo": "^1.16.1", 22 | "laravel-vite-plugin": "^1.0", 23 | "postcss": "^8.4.31", 24 | "prettier": "^3.3.2", 25 | "prettier-plugin-tailwindcss": "^0.6.5", 26 | "pusher-js": "^8.4.0-rc2", 27 | "react": "^18.2.0", 28 | "react-dom": "^18.2.0", 29 | "tailwindcss": "^3.2.1", 30 | "typescript": "^5.0.2", 31 | "vite": "^5.0" 32 | }, 33 | "dependencies": { 34 | "@emotion/react": "^11.11.4", 35 | "@emotion/styled": "^11.11.5", 36 | "@radix-ui/react-avatar": "^1.1.0", 37 | "@radix-ui/react-dropdown-menu": "^2.1.1", 38 | "@radix-ui/react-icons": "^1.3.0", 39 | "@radix-ui/react-label": "^2.1.0", 40 | "@radix-ui/react-slot": "^1.1.0", 41 | "@radix-ui/react-toast": "^1.2.1", 42 | "@radix-ui/react-tooltip": "^1.1.2", 43 | "class-variance-authority": "^0.7.0", 44 | "clsx": "^2.1.1", 45 | "lucide-react": "^0.400.0", 46 | "react-icons": "^5.2.1", 47 | "recharts": "^2.12.7", 48 | "tailwind-merge": "^2.3.0", 49 | "tailwindcss-animate": "^1.0.7" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /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/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/public/.DS_Store -------------------------------------------------------------------------------- /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 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Send Requests To Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | 22 | -------------------------------------------------------------------------------- /public/auth/app.css: -------------------------------------------------------------------------------- 1 | #auth-body{ 2 | opacity:80; 3 | } 4 | 5 | #auth-container{ 6 | border:0px !important; 7 | } 8 | 9 | #auth-heading-container{ 10 | padding-left:5px; 11 | } -------------------------------------------------------------------------------- /public/auth/build/assets/scripts.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/auth/build/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "resources/css/auth.css": { 3 | "file": "assets/styles.css", 4 | "src": "resources/css/auth.css", 5 | "isEntry": true 6 | }, 7 | "resources/js/auth.js": { 8 | "file": "assets/scripts.js", 9 | "name": "scripts", 10 | "src": "resources/js/auth.js", 11 | "isEntry": true 12 | } 13 | } -------------------------------------------------------------------------------- /public/auth/img/favicon-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/public/auth/img/favicon-dark.png -------------------------------------------------------------------------------- /public/auth/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/public/auth/img/favicon.ico -------------------------------------------------------------------------------- /public/auth/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/public/auth/img/favicon.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/public/favicon.ico -------------------------------------------------------------------------------- /public/images/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/public/images/avatar.jpg -------------------------------------------------------------------------------- /public/images/jsonfakery-avatar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/images/sample-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 14 | 15 | 16 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | handleRequest(Request::capture()); 18 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /resources/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/resources/.DS_Store -------------------------------------------------------------------------------- /resources/css/app.css: -------------------------------------------------------------------------------- 1 | @import 'custom-font.css'; 2 | 3 | @tailwind base; 4 | @tailwind components; 5 | @tailwind utilities; 6 | 7 | /* ShadCN Chart styles */ 8 | @layer base { 9 | :root { 10 | --chart-1: 173 58% 39%; 11 | --chart-2: 12 76% 61%; 12 | } 13 | 14 | .dark { 15 | --chart-1: 220 70% 50%; 16 | --chart-5: 160 60% 45%; 17 | } 18 | } 19 | 20 | @layer base { 21 | :root { 22 | --background: 0 0% 100%; 23 | --foreground: 222.2 84% 4.9%; 24 | 25 | --card: 0 0% 100%; 26 | --card-foreground: 222.2 84% 4.9%; 27 | 28 | --popover: 0 0% 100%; 29 | --popover-foreground: 222.2 84% 4.9%; 30 | 31 | --primary: 222.2 47.4% 11.2%; 32 | --primary-foreground: 210 40% 98%; 33 | 34 | --secondary: 210 40% 96.1%; 35 | --secondary-foreground: 222.2 47.4% 11.2%; 36 | 37 | --muted: 210 40% 96.1%; 38 | --muted-foreground: 215.4 16.3% 46.9%; 39 | 40 | --accent: 210 40% 96.1%; 41 | --accent-foreground: 222.2 47.4% 11.2%; 42 | 43 | --destructive: 0 84.2% 60.2%; 44 | --destructive-foreground: 210 40% 98%; 45 | 46 | --border: 214.3 31.8% 91.4%; 47 | --input: 214.3 31.8% 91.4%; 48 | --ring: 222.2 84% 4.9%; 49 | 50 | --radius: 0.5rem; 51 | } 52 | 53 | .dark { 54 | --background: 222.2 84% 4.9%; 55 | --foreground: 210 40% 98%; 56 | 57 | --card: 222.2 84% 4.9%; 58 | --card-foreground: 210 40% 98%; 59 | 60 | --popover: 222.2 84% 4.9%; 61 | --popover-foreground: 210 40% 98%; 62 | 63 | --primary: 210 40% 98%; 64 | --primary-foreground: 222.2 47.4% 11.2%; 65 | 66 | --secondary: 217.2 32.6% 17.5%; 67 | --secondary-foreground: 210 40% 98%; 68 | 69 | --muted: 217.2 32.6% 17.5%; 70 | --muted-foreground: 215 20.2% 65.1%; 71 | 72 | --accent: 217.2 32.6% 17.5%; 73 | --accent-foreground: 210 40% 98%; 74 | 75 | --destructive: 0 62.8% 30.6%; 76 | --destructive-foreground: 210 40% 98%; 77 | 78 | --border: 217.2 32.6% 17.5%; 79 | --input: 217.2 32.6% 17.5%; 80 | --ring: 212.7 26.8% 83.9%; 81 | } 82 | } 83 | 84 | @layer base { 85 | * { 86 | @apply border-border; 87 | } 88 | 89 | body { 90 | @apply bg-background text-foreground; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /resources/css/custom-font.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'JetBrainsMono'; 3 | src: 4 | url('../fonts/jetbrains_mono/jetbrainsmono-regular-webfont.woff2') 5 | format('woff2'), 6 | url('../fonts/jetbrains_mono/jetbrainsmono-regular-webfont.woff') 7 | format('woff'); 8 | } 9 | 10 | @font-face { 11 | font-family: 'GeistVF'; 12 | src: 13 | url('../fonts/geist_sans/GeistVF.woff2') format('woff2'), 14 | url('../fonts/geist_sans/GeistVF.woff') format('woff'); 15 | /* font-weight: normal; 16 | font-style: normal; */ 17 | } 18 | 19 | @font-face { 20 | font-family: 'GeistMonoVF'; 21 | src: 22 | url('../fonts/geist_mono/GeistMonoVF.woff2') format('woff2'), 23 | url('../fonts/geist_mono/GeistMonoVF.woff') format('woff'); 24 | } 25 | -------------------------------------------------------------------------------- /resources/fonts/geist_mono/GeistMonoVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/resources/fonts/geist_mono/GeistMonoVF.woff -------------------------------------------------------------------------------- /resources/fonts/geist_mono/GeistMonoVF.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/resources/fonts/geist_mono/GeistMonoVF.woff2 -------------------------------------------------------------------------------- /resources/fonts/geist_sans/GeistVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/resources/fonts/geist_sans/GeistVF.woff -------------------------------------------------------------------------------- /resources/fonts/geist_sans/GeistVF.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/resources/fonts/geist_sans/GeistVF.woff2 -------------------------------------------------------------------------------- /resources/fonts/jetbrains_mono/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/resources/fonts/jetbrains_mono/.DS_Store -------------------------------------------------------------------------------- /resources/fonts/jetbrains_mono/generator_config.txt: -------------------------------------------------------------------------------- 1 | # Font Squirrel Font-face Generator Configuration File 2 | # Upload this file to the generator to recreate the settings 3 | # you used to create these fonts. 4 | 5 | {"mode":"optimal","formats":["woff","woff2"],"tt_instructor":"default","fix_gasp":"xy","fix_vertical_metrics":"Y","metrics_ascent":"","metrics_descent":"","metrics_linegap":"","add_spaces":"Y","add_hyphens":"Y","fallback":"none","fallback_custom":"100","options_subset":"basic","subset_custom":"","subset_custom_range":"","subset_ot_features_list":"","css_stylesheet":"stylesheet.css","filename_suffix":"-webfont","emsquare":"2048","spacing_adjustment":"0"} -------------------------------------------------------------------------------- /resources/fonts/jetbrains_mono/jetbrainsmono-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/resources/fonts/jetbrains_mono/jetbrainsmono-regular-webfont.woff -------------------------------------------------------------------------------- /resources/fonts/jetbrains_mono/jetbrainsmono-regular-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/resources/fonts/jetbrains_mono/jetbrainsmono-regular-webfont.woff2 -------------------------------------------------------------------------------- /resources/fonts/jetbrains_mono/stylesheet.css: -------------------------------------------------------------------------------- 1 | /*! Generated by Font Squirrel (https://www.fontsquirrel.com) on June 25, 2024 */ 2 | 3 | 4 | 5 | @font-face { 6 | font-family: 'jetbrains_monoregular'; 7 | src: url('jetbrainsmono-regular-webfont.woff2') format('woff2'), 8 | url('jetbrainsmono-regular-webfont.woff') format('woff'); 9 | font-weight: normal; 10 | font-style: normal; 11 | 12 | } -------------------------------------------------------------------------------- /resources/js/Components/AppHeader.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '@/Components/ui/button' 2 | import { PiBellFill, PiChatCenteredDotsFill } from 'react-icons/pi' 3 | import { ReactNode } from 'react' 4 | import { User } from '@/types' 5 | import SearchButton from './SearchButton' 6 | import UserDropdownMenu from './UserDropdownMenu' 7 | 8 | const AppHeader = ({ user, header }: { user: User; header?: ReactNode }) => { 9 | return ( 10 |
11 | {header} 12 |
13 | 14 |
15 |
16 |
17 | 27 | 37 |
{' '} 38 | 39 |
40 |
41 | ) 42 | } 43 | 44 | export default AppHeader 45 | -------------------------------------------------------------------------------- /resources/js/Components/ApplicationLogo.tsx: -------------------------------------------------------------------------------- 1 | import { SVGAttributes } from 'react'; 2 | 3 | export default function ApplicationLogo(props: SVGAttributes) { 4 | return ( 5 | 6 | 7 | 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /resources/js/Components/Checkbox.tsx: -------------------------------------------------------------------------------- 1 | import { InputHTMLAttributes } from 'react'; 2 | 3 | export default function Checkbox({ className = '', ...props }: InputHTMLAttributes) { 4 | return ( 5 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /resources/js/Components/CustomToaster.tsx: -------------------------------------------------------------------------------- 1 | import { PiInfoDuotone, PiX } from 'react-icons/pi' 2 | 3 | const CustomToaster = ({ 4 | message, 5 | closeToaster 6 | }: { 7 | message: string 8 | closeToaster: () => void 9 | }) => { 10 | return ( 11 |
12 | 16 |

17 | 18 | Database Backup 19 |

20 |

{message}

21 |
22 | ) 23 | } 24 | 25 | export default CustomToaster 26 | -------------------------------------------------------------------------------- /resources/js/Components/CustomTooltip.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Tooltip, 3 | TooltipContent, 4 | TooltipProvider, 5 | TooltipTrigger 6 | } from '@/Components/ui/tooltip' 7 | 8 | const CustomTooltip = ({ label, children }: { label: string }) => { 9 | return ( 10 | 11 | 12 | {children} 13 | 14 |

{label}

15 |
16 |
17 |
18 | ) 19 | } 20 | 21 | export default CustomTooltip 22 | -------------------------------------------------------------------------------- /resources/js/Components/DangerButton.tsx: -------------------------------------------------------------------------------- 1 | import { ButtonHTMLAttributes } from 'react'; 2 | 3 | export default function DangerButton({ className = '', disabled, children, ...props }: ButtonHTMLAttributes) { 4 | return ( 5 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /resources/js/Components/Dropdown.tsx: -------------------------------------------------------------------------------- 1 | import { useState, createContext, useContext, PropsWithChildren, Dispatch, SetStateAction } from 'react'; 2 | import { Link, InertiaLinkProps } from '@inertiajs/react'; 3 | import { Transition } from '@headlessui/react'; 4 | 5 | const DropDownContext = createContext<{ 6 | open: boolean; 7 | setOpen: Dispatch>; 8 | toggleOpen: () => void; 9 | }>({ 10 | open: false, 11 | setOpen: () => {}, 12 | toggleOpen: () => {}, 13 | }); 14 | 15 | const Dropdown = ({ children }: PropsWithChildren) => { 16 | const [open, setOpen] = useState(false); 17 | 18 | const toggleOpen = () => { 19 | setOpen((previousState) => !previousState); 20 | }; 21 | 22 | return ( 23 | 24 |
{children}
25 |
26 | ); 27 | }; 28 | 29 | const Trigger = ({ children }: PropsWithChildren) => { 30 | const { open, setOpen, toggleOpen } = useContext(DropDownContext); 31 | 32 | return ( 33 | <> 34 |
{children}
35 | 36 | {open &&
setOpen(false)}>
} 37 | 38 | ); 39 | }; 40 | 41 | const Content = ({ align = 'right', width = '48', contentClasses = 'py-1 bg-white', children }: PropsWithChildren<{ align?: 'left'|'right', width?: '48', contentClasses?: string }>) => { 42 | const { open, setOpen } = useContext(DropDownContext); 43 | 44 | let alignmentClasses = 'origin-top'; 45 | 46 | if (align === 'left') { 47 | alignmentClasses = 'ltr:origin-top-left rtl:origin-top-right start-0'; 48 | } else if (align === 'right') { 49 | alignmentClasses = 'ltr:origin-top-right rtl:origin-top-left end-0'; 50 | } 51 | 52 | let widthClasses = ''; 53 | 54 | if (width === '48') { 55 | widthClasses = 'w-48'; 56 | } 57 | 58 | return ( 59 | <> 60 | 69 |
setOpen(false)} 72 | > 73 |
{children}
74 |
75 |
76 | 77 | ); 78 | }; 79 | 80 | const DropdownLink = ({ className = '', children, ...props }: InertiaLinkProps) => { 81 | return ( 82 | 89 | {children} 90 | 91 | ); 92 | }; 93 | 94 | Dropdown.Trigger = Trigger; 95 | Dropdown.Content = Content; 96 | Dropdown.Link = DropdownLink; 97 | 98 | export default Dropdown; 99 | -------------------------------------------------------------------------------- /resources/js/Components/Footer.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | PiCurrencyCircleDollarDuotone, 3 | PiLightningDuotone, 4 | PiUsersFourDuotone 5 | } from 'react-icons/pi' 6 | import { Badge } from '@/Components/ui/badge' 7 | 8 | const Footer = () => { 9 | return ( 10 |
11 |
12 | 13 |
14 | 15 | 16 | Users Online: 1,455 17 | 18 |
19 | 20 |
21 | 22 | 23 | Today's Sale: $3,298.75 24 | 25 |
26 | 27 |
28 | 29 | 30 | System Status:{' '} 31 | 32 | Operational 33 | 34 | 35 |
36 |
37 | ) 38 | } 39 | 40 | export default Footer 41 | -------------------------------------------------------------------------------- /resources/js/Components/InputError.tsx: -------------------------------------------------------------------------------- 1 | import { HTMLAttributes } from 'react'; 2 | 3 | export default function InputError({ message, className = '', ...props }: HTMLAttributes & { message?: string }) { 4 | return message ? ( 5 |

6 | {message} 7 |

8 | ) : null; 9 | } 10 | -------------------------------------------------------------------------------- /resources/js/Components/InputLabel.tsx: -------------------------------------------------------------------------------- 1 | import { LabelHTMLAttributes } from 'react'; 2 | 3 | export default function InputLabel({ value, className = '', children, ...props }: LabelHTMLAttributes & { value?: string }) { 4 | return ( 5 | 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /resources/js/Components/Modal.tsx: -------------------------------------------------------------------------------- 1 | import { PropsWithChildren } from 'react'; 2 | import { Dialog, DialogPanel, Transition, TransitionChild } from '@headlessui/react'; 3 | 4 | export default function Modal({ 5 | children, 6 | show = false, 7 | maxWidth = '2xl', 8 | closeable = true, 9 | onClose = () => {}, 10 | }: PropsWithChildren<{ 11 | show: boolean; 12 | maxWidth?: 'sm' | 'md' | 'lg' | 'xl' | '2xl'; 13 | closeable?: boolean; 14 | onClose: CallableFunction; 15 | }>) { 16 | const close = () => { 17 | if (closeable) { 18 | onClose(); 19 | } 20 | }; 21 | 22 | const maxWidthClass = { 23 | sm: 'sm:max-w-sm', 24 | md: 'sm:max-w-md', 25 | lg: 'sm:max-w-lg', 26 | xl: 'sm:max-w-xl', 27 | '2xl': 'sm:max-w-2xl', 28 | }[maxWidth]; 29 | 30 | return ( 31 | 32 | 38 | 46 |
47 | 48 | 49 | 57 | 60 | {children} 61 | 62 | 63 |
64 |
65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /resources/js/Components/NavLink.tsx: -------------------------------------------------------------------------------- 1 | import { Link, InertiaLinkProps } from '@inertiajs/react'; 2 | 3 | export default function NavLink({ active = false, className = '', children, ...props }: InertiaLinkProps & { active: boolean }) { 4 | return ( 5 | 15 | {children} 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /resources/js/Components/Navigation.tsx: -------------------------------------------------------------------------------- 1 | import menuItems from '@/menus' 2 | import SearchMenu from '@/Components/SearchMenu' 3 | import { Link, usePage } from '@inertiajs/react' 4 | 5 | const Navigation = () => { 6 | const { url } = usePage() 7 | return ( 8 | 30 | ) 31 | } 32 | 33 | export default Navigation 34 | -------------------------------------------------------------------------------- /resources/js/Components/PrimaryButton.tsx: -------------------------------------------------------------------------------- 1 | import { ButtonHTMLAttributes } from 'react'; 2 | 3 | export default function PrimaryButton({ className = '', disabled, children, ...props }: ButtonHTMLAttributes) { 4 | return ( 5 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /resources/js/Components/ResponsiveNavLink.tsx: -------------------------------------------------------------------------------- 1 | import { Link, InertiaLinkProps } from '@inertiajs/react'; 2 | 3 | export default function ResponsiveNavLink({ active = false, className = '', children, ...props }: InertiaLinkProps & { active?: boolean }) { 4 | return ( 5 | 13 | {children} 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /resources/js/Components/SearchButton.tsx: -------------------------------------------------------------------------------- 1 | const SearchButton = ({ className = '' }: { className?: string }) => { 2 | return ( 3 |
4 | 33 |
34 | ) 35 | } 36 | 37 | export default SearchButton 38 | -------------------------------------------------------------------------------- /resources/js/Components/SearchMenu.tsx: -------------------------------------------------------------------------------- 1 | const SearchMenu = ({ className = '' }: { className?: string }) => { 2 | return ( 3 |
4 | 27 |
28 | ) 29 | } 30 | 31 | export default SearchMenu 32 | -------------------------------------------------------------------------------- /resources/js/Components/SecondaryButton.tsx: -------------------------------------------------------------------------------- 1 | import { ButtonHTMLAttributes } from 'react'; 2 | 3 | export default function SecondaryButton({ type = 'button', className = '', disabled, children, ...props }: ButtonHTMLAttributes) { 4 | return ( 5 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /resources/js/Components/TextInput.tsx: -------------------------------------------------------------------------------- 1 | import { forwardRef, useEffect, useImperativeHandle, useRef, InputHTMLAttributes } from 'react'; 2 | 3 | export default forwardRef(function TextInput( 4 | { type = 'text', className = '', isFocused = false, ...props }: InputHTMLAttributes & { isFocused?: boolean }, 5 | ref 6 | ) { 7 | const localRef = useRef(null); 8 | 9 | useImperativeHandle(ref, () => ({ 10 | focus: () => localRef.current?.focus(), 11 | })); 12 | 13 | useEffect(() => { 14 | if (isFocused) { 15 | localRef.current?.focus(); 16 | } 17 | }, []); 18 | 19 | return ( 20 | 29 | ); 30 | }); 31 | -------------------------------------------------------------------------------- /resources/js/Components/UserDropdownMenu.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | DropdownMenu, 3 | DropdownMenuContent, 4 | DropdownMenuGroup, 5 | DropdownMenuItem, 6 | DropdownMenuPortal, 7 | DropdownMenuSeparator, 8 | DropdownMenuSub, 9 | DropdownMenuSubContent, 10 | DropdownMenuSubTrigger, 11 | DropdownMenuTrigger 12 | } from '@/Components/ui/dropdown-menu' 13 | import { Avatar, AvatarFallback, AvatarImage } from '@/Components/ui/avatar' 14 | import { router } from '@inertiajs/react' 15 | import { User } from '@/types' 16 | import { PiArrowDownBold } from 'react-icons/pi' 17 | import { useState } from 'react' 18 | 19 | const UserDropdownMenu = ({ user }: { user: User }) => { 20 | const [dropDownOpen, setDropDownOpen] = useState(false) 21 | const toggleDropdown = () => { 22 | setDropDownOpen(dropDownOpen => !dropDownOpen) 23 | } 24 | 25 | return ( 26 | toggleDropdown()}> 27 | 28 |
29 | 30 | 34 | GR 35 | 36 |
37 | 38 | {user.name} 39 | 40 | {user.email} 41 |
42 | 45 |
46 |
47 | 48 | 49 | Profile 50 | Billing 51 | Settings 52 | Keyboard shortcuts 53 | 54 | 55 | 56 | Team 57 | 58 | 59 | Invite users 60 | 61 | 62 | 63 | Email 64 | Message 65 | 66 | More... 67 | 68 | 69 | 70 | New Team 71 | 72 | 73 | GitHub 74 | Support 75 | API 76 | 77 | { 79 | router.post('/logout') 80 | }} 81 | > 82 | Log out 83 | 84 | 85 |
86 | ) 87 | } 88 | 89 | export default UserDropdownMenu 90 | -------------------------------------------------------------------------------- /resources/js/Components/statistics/Applicants.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Card, 3 | CardContent, 4 | CardDescription, 5 | CardHeader, 6 | CardTitle 7 | } from '@/Components/ui/card' 8 | import { PiSuitcaseSimpleDuotone } from 'react-icons/pi' 9 | 10 | const Applicants = () => { 11 | return ( 12 | 13 | 14 | 15 | 387 16 | 17 | 18 | 19 | 20 | Pending Job Applications 21 | 22 | 23 |

24 | +45 hired last month 25 |

26 |
27 |
28 | ) 29 | } 30 | 31 | export default Applicants 32 | -------------------------------------------------------------------------------- /resources/js/Components/statistics/MonthlyRevenue.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Card, 3 | CardContent, 4 | CardDescription, 5 | CardHeader, 6 | CardTitle 7 | } from '@/Components/ui/card' 8 | import { PiCurrencyCircleDollarDuotone } from 'react-icons/pi' 9 | 10 | const MonthlyRevenue = () => { 11 | return ( 12 | 13 | 14 | 15 | $154,561.64 16 | 17 | 18 | 19 | 20 | Total Revenue (September) 21 | 22 | 23 |

24 | +15% from last month 25 |

26 |
27 |
28 | ) 29 | } 30 | 31 | export default MonthlyRevenue 32 | -------------------------------------------------------------------------------- /resources/js/Components/statistics/Subscriptions.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Card, 3 | CardContent, 4 | CardDescription, 5 | CardHeader, 6 | CardTitle 7 | } from '@/Components/ui/card' 8 | import { PiUsersThreeDuotone } from 'react-icons/pi' 9 | 10 | const Subscriptions = () => { 11 | return ( 12 | 13 | 14 | 15 | 109 16 | 17 | 18 | 19 | 20 | Subscriptions this month 21 | 22 | 23 |

24 | +561 last month 25 |

26 |
27 |
28 | ) 29 | } 30 | 31 | export default Subscriptions 32 | -------------------------------------------------------------------------------- /resources/js/Components/statistics/UserEngagement.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Card, 3 | CardContent, 4 | CardDescription, 5 | CardHeader, 6 | CardTitle 7 | } from '@/Components/ui/card' 8 | import { PiUserSoundDuotone } from 'react-icons/pi' 9 | 10 | const UserEngagement = () => { 11 | return ( 12 | 13 | 14 | 15 | 1,329 16 | 17 | 18 | 19 | 20 | Average Daily Active Users 21 | 22 | 23 |

24 | +8% from yesterday 25 |

26 |
27 |
28 | ) 29 | } 30 | 31 | export default UserEngagement 32 | -------------------------------------------------------------------------------- /resources/js/Components/ui/avatar.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as AvatarPrimitive from "@radix-ui/react-avatar" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const Avatar = React.forwardRef< 7 | React.ElementRef, 8 | React.ComponentPropsWithoutRef 9 | >(({ className, ...props }, ref) => ( 10 | 18 | )) 19 | Avatar.displayName = AvatarPrimitive.Root.displayName 20 | 21 | const AvatarImage = React.forwardRef< 22 | React.ElementRef, 23 | React.ComponentPropsWithoutRef 24 | >(({ className, ...props }, ref) => ( 25 | 30 | )) 31 | AvatarImage.displayName = AvatarPrimitive.Image.displayName 32 | 33 | const AvatarFallback = React.forwardRef< 34 | React.ElementRef, 35 | React.ComponentPropsWithoutRef 36 | >(({ className, ...props }, ref) => ( 37 | 45 | )) 46 | AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName 47 | 48 | export { Avatar, AvatarImage, AvatarFallback } 49 | -------------------------------------------------------------------------------- /resources/js/Components/ui/badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { cva, type VariantProps } from "class-variance-authority" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const badgeVariants = cva( 7 | "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", 8 | { 9 | variants: { 10 | variant: { 11 | default: 12 | "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", 13 | secondary: 14 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", 15 | destructive: 16 | "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", 17 | outline: "text-foreground", 18 | }, 19 | }, 20 | defaultVariants: { 21 | variant: "default", 22 | }, 23 | } 24 | ) 25 | 26 | export interface BadgeProps 27 | extends React.HTMLAttributes, 28 | VariantProps {} 29 | 30 | function Badge({ className, variant, ...props }: BadgeProps) { 31 | return ( 32 |
33 | ) 34 | } 35 | 36 | export { Badge, badgeVariants } 37 | -------------------------------------------------------------------------------- /resources/js/Components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Slot } from "@radix-ui/react-slot" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", 9 | { 10 | variants: { 11 | variant: { 12 | default: 13 | "bg-primary text-primary-foreground shadow hover:bg-primary/90", 14 | destructive: 15 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", 16 | outline: 17 | "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", 18 | secondary: 19 | "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", 20 | ghost: "hover:bg-accent hover:text-accent-foreground", 21 | link: "text-primary underline-offset-4 hover:underline", 22 | }, 23 | size: { 24 | default: "h-9 px-4 py-2", 25 | sm: "h-8 rounded-md px-3 text-xs", 26 | lg: "h-10 rounded-md px-8", 27 | icon: "h-9 w-9", 28 | }, 29 | }, 30 | defaultVariants: { 31 | variant: "default", 32 | size: "default", 33 | }, 34 | } 35 | ) 36 | 37 | export interface ButtonProps 38 | extends React.ButtonHTMLAttributes, 39 | VariantProps { 40 | asChild?: boolean 41 | } 42 | 43 | const Button = React.forwardRef( 44 | ({ className, variant, size, asChild = false, ...props }, ref) => { 45 | const Comp = asChild ? Slot : "button" 46 | return ( 47 | 52 | ) 53 | } 54 | ) 55 | Button.displayName = "Button" 56 | 57 | export { Button, buttonVariants } 58 | -------------------------------------------------------------------------------- /resources/js/Components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Card = React.forwardRef< 6 | HTMLDivElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
17 | )) 18 | Card.displayName = "Card" 19 | 20 | const CardHeader = React.forwardRef< 21 | HTMLDivElement, 22 | React.HTMLAttributes 23 | >(({ className, ...props }, ref) => ( 24 |
29 | )) 30 | CardHeader.displayName = "CardHeader" 31 | 32 | const CardTitle = React.forwardRef< 33 | HTMLParagraphElement, 34 | React.HTMLAttributes 35 | >(({ className, ...props }, ref) => ( 36 |

41 | )) 42 | CardTitle.displayName = "CardTitle" 43 | 44 | const CardDescription = React.forwardRef< 45 | HTMLParagraphElement, 46 | React.HTMLAttributes 47 | >(({ className, ...props }, ref) => ( 48 |

53 | )) 54 | CardDescription.displayName = "CardDescription" 55 | 56 | const CardContent = React.forwardRef< 57 | HTMLDivElement, 58 | React.HTMLAttributes 59 | >(({ className, ...props }, ref) => ( 60 |

61 | )) 62 | CardContent.displayName = "CardContent" 63 | 64 | const CardFooter = React.forwardRef< 65 | HTMLDivElement, 66 | React.HTMLAttributes 67 | >(({ className, ...props }, ref) => ( 68 |
73 | )) 74 | CardFooter.displayName = "CardFooter" 75 | 76 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } 77 | -------------------------------------------------------------------------------- /resources/js/Components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ) 21 | } 22 | ) 23 | Input.displayName = "Input" 24 | 25 | export { Input } 26 | -------------------------------------------------------------------------------- /resources/js/Components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as LabelPrimitive from "@radix-ui/react-label" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const labelVariants = cva( 8 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 9 | ) 10 | 11 | const Label = React.forwardRef< 12 | React.ElementRef, 13 | React.ComponentPropsWithoutRef & 14 | VariantProps 15 | >(({ className, ...props }, ref) => ( 16 | 21 | )) 22 | Label.displayName = LabelPrimitive.Root.displayName 23 | 24 | export { Label } 25 | -------------------------------------------------------------------------------- /resources/js/Components/ui/table.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Table = React.forwardRef< 6 | HTMLTableElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
10 | 15 | 16 | )) 17 | Table.displayName = "Table" 18 | 19 | const TableHeader = React.forwardRef< 20 | HTMLTableSectionElement, 21 | React.HTMLAttributes 22 | >(({ className, ...props }, ref) => ( 23 | 24 | )) 25 | TableHeader.displayName = "TableHeader" 26 | 27 | const TableBody = React.forwardRef< 28 | HTMLTableSectionElement, 29 | React.HTMLAttributes 30 | >(({ className, ...props }, ref) => ( 31 | 36 | )) 37 | TableBody.displayName = "TableBody" 38 | 39 | const TableFooter = React.forwardRef< 40 | HTMLTableSectionElement, 41 | React.HTMLAttributes 42 | >(({ className, ...props }, ref) => ( 43 | tr]:last:border-b-0", 47 | className 48 | )} 49 | {...props} 50 | /> 51 | )) 52 | TableFooter.displayName = "TableFooter" 53 | 54 | const TableRow = React.forwardRef< 55 | HTMLTableRowElement, 56 | React.HTMLAttributes 57 | >(({ className, ...props }, ref) => ( 58 | 66 | )) 67 | TableRow.displayName = "TableRow" 68 | 69 | const TableHead = React.forwardRef< 70 | HTMLTableCellElement, 71 | React.ThHTMLAttributes 72 | >(({ className, ...props }, ref) => ( 73 |
[role=checkbox]]:translate-y-[2px]", 77 | className 78 | )} 79 | {...props} 80 | /> 81 | )) 82 | TableHead.displayName = "TableHead" 83 | 84 | const TableCell = React.forwardRef< 85 | HTMLTableCellElement, 86 | React.TdHTMLAttributes 87 | >(({ className, ...props }, ref) => ( 88 | [role=checkbox]]:translate-y-[2px]", 92 | className 93 | )} 94 | {...props} 95 | /> 96 | )) 97 | TableCell.displayName = "TableCell" 98 | 99 | const TableCaption = React.forwardRef< 100 | HTMLTableCaptionElement, 101 | React.HTMLAttributes 102 | >(({ className, ...props }, ref) => ( 103 |
108 | )) 109 | TableCaption.displayName = "TableCaption" 110 | 111 | export { 112 | Table, 113 | TableHeader, 114 | TableBody, 115 | TableFooter, 116 | TableHead, 117 | TableRow, 118 | TableCell, 119 | TableCaption, 120 | } 121 | -------------------------------------------------------------------------------- /resources/js/Components/ui/toaster.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Toast, 3 | ToastClose, 4 | ToastDescription, 5 | ToastProvider, 6 | ToastTitle, 7 | ToastViewport, 8 | } from "@/Components/ui/toast" 9 | import { useToast } from "@/Components/ui/use-toast" 10 | 11 | export function Toaster() { 12 | const { toasts } = useToast() 13 | 14 | return ( 15 | 16 | {toasts.map(function ({ id, title, description, action, ...props }) { 17 | return ( 18 | 19 |
20 | {title && {title}} 21 | {description && ( 22 | {description} 23 | )} 24 |
25 | {action} 26 | 27 |
28 | ) 29 | })} 30 | 31 |
32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /resources/js/Components/ui/tooltip.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as TooltipPrimitive from "@radix-ui/react-tooltip" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const TooltipProvider = TooltipPrimitive.Provider 7 | 8 | const Tooltip = TooltipPrimitive.Root 9 | 10 | const TooltipTrigger = TooltipPrimitive.Trigger 11 | 12 | const TooltipContent = React.forwardRef< 13 | React.ElementRef, 14 | React.ComponentPropsWithoutRef 15 | >(({ className, sideOffset = 4, ...props }, ref) => ( 16 | 25 | )) 26 | TooltipContent.displayName = TooltipPrimitive.Content.displayName 27 | 28 | export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } 29 | -------------------------------------------------------------------------------- /resources/js/Components/v1.upload-progress.tsx: -------------------------------------------------------------------------------- 1 | ; 2 | 3 | Upload a file 4 | 5 | 6 | {fileName === '' && ( 7 | <> 8 | 16 | 17 | )} 18 |
fileInputRef.current && fileInputRef.current.click()} 21 | > 22 | {fileName !== '' ? `${fileName}` : 'Browse file'} 23 |
24 | {!isUploading && file !== null && ( 25 | 28 | )} 29 |
30 | {isUploading && ( 31 | 32 | {uploadPercentage}% 33 | 34 | )} 35 | {isUploading && ( 36 |
37 |
43 |
44 | )} 45 |
46 |
47 |
48 | -------------------------------------------------------------------------------- /resources/js/Layouts/AuthenticatedLayout.tsx: -------------------------------------------------------------------------------- 1 | import { PropsWithChildren, ReactNode, useEffect, useState } from 'react' 2 | import { User } from '@/types' 3 | import Navigation from '@/Components/Navigation' 4 | import Footer from '@/Components/Footer' 5 | import AppHeader from '@/Components/AppHeader' 6 | import CustomToaster from '@/Components/CustomToaster' 7 | 8 | interface EventResponse { 9 | user_id: number 10 | message: string 11 | backup_file: string 12 | } 13 | 14 | export default function Authenticated({ 15 | user, 16 | header, 17 | children 18 | }: PropsWithChildren<{ user: User; header?: ReactNode }>) { 19 | const [showToaster, setShowToaster] = useState(false) 20 | const [message, setMessage] = useState('') 21 | 22 | useEffect(() => { 23 | const echoInstance = (window as any).Echo 24 | echoInstance 25 | .private(`backup.${user.id}`) 26 | .listen('BackupCompleted', (event: EventResponse) => { 27 | setShowToaster(true) 28 | setMessage(event.message) 29 | }) 30 | }, []) 31 | 32 | const handleCloseToaster = () => { 33 | setShowToaster(false) 34 | } 35 | return ( 36 |
37 | {showToaster && ( 38 | 42 | )} 43 |
44 |
45 | {/* Sidebar */} 46 |
47 |
48 |
49 | Logo 54 | 55 |

56 | Untitled Admin v1.0 57 |

58 |
59 | 60 | {/* Main navigation */} 61 | 62 |
63 |
64 | Untitled Admin v1.0 65 |
66 |
67 | {/* End of sidebar */} 68 | 69 | {/* Main contents */} 70 |
71 | {/* Main header */} 72 | 73 | {/* End of main header */} 74 | 75 |
76 |
77 | {children} 78 |
79 | 80 | {/* Footer */} 81 |
82 | {/* End of footer */} 83 |
84 |
85 |
86 |
87 | ) 88 | } 89 | -------------------------------------------------------------------------------- /resources/js/Layouts/GuestLayout.tsx: -------------------------------------------------------------------------------- 1 | import ApplicationLogo from '@/Components/ApplicationLogo'; 2 | import { Link } from '@inertiajs/react'; 3 | import { PropsWithChildren } from 'react'; 4 | 5 | export default function Guest({ children }: PropsWithChildren) { 6 | return ( 7 |
8 |
9 | 10 | 11 | 12 |
13 | 14 |
15 | {children} 16 |
17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /resources/js/Pages/Auth/ConfirmPassword.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, FormEventHandler } from 'react'; 2 | import GuestLayout from '@/Layouts/GuestLayout'; 3 | import InputError from '@/Components/InputError'; 4 | import InputLabel from '@/Components/InputLabel'; 5 | import PrimaryButton from '@/Components/PrimaryButton'; 6 | import TextInput from '@/Components/TextInput'; 7 | import { Head, useForm } from '@inertiajs/react'; 8 | 9 | export default function ConfirmPassword() { 10 | const { data, setData, post, processing, errors, reset } = useForm({ 11 | password: '', 12 | }); 13 | 14 | useEffect(() => { 15 | return () => { 16 | reset('password'); 17 | }; 18 | }, []); 19 | 20 | const submit: FormEventHandler = (e) => { 21 | e.preventDefault(); 22 | 23 | post(route('password.confirm')); 24 | }; 25 | 26 | return ( 27 | 28 | 29 | 30 |
31 | This is a secure area of the application. Please confirm your password before continuing. 32 |
33 | 34 |
35 |
36 | 37 | 38 | setData('password', e.target.value)} 46 | /> 47 | 48 | 49 |
50 | 51 |
52 | 53 | Confirm 54 | 55 |
56 |
57 |
58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /resources/js/Pages/Auth/ForgotPassword.tsx: -------------------------------------------------------------------------------- 1 | import GuestLayout from '@/Layouts/GuestLayout'; 2 | import InputError from '@/Components/InputError'; 3 | import PrimaryButton from '@/Components/PrimaryButton'; 4 | import TextInput from '@/Components/TextInput'; 5 | import { Head, useForm } from '@inertiajs/react'; 6 | import { FormEventHandler } from 'react'; 7 | 8 | export default function ForgotPassword({ status }: { status?: string }) { 9 | const { data, setData, post, processing, errors } = useForm({ 10 | email: '', 11 | }); 12 | 13 | const submit: FormEventHandler = (e) => { 14 | e.preventDefault(); 15 | 16 | post(route('password.email')); 17 | }; 18 | 19 | return ( 20 | 21 | 22 | 23 |
24 | Forgot your password? No problem. Just let us know your email address and we will email you a password 25 | reset link that will allow you to choose a new one. 26 |
27 | 28 | {status &&
{status}
} 29 | 30 |
31 | setData('email', e.target.value)} 39 | /> 40 | 41 | 42 | 43 |
44 | 45 | Email Password Reset Link 46 | 47 |
48 | 49 |
50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /resources/js/Pages/Auth/Login.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, FormEventHandler } from 'react'; 2 | import Checkbox from '@/Components/Checkbox'; 3 | import GuestLayout from '@/Layouts/GuestLayout'; 4 | import InputError from '@/Components/InputError'; 5 | import InputLabel from '@/Components/InputLabel'; 6 | import PrimaryButton from '@/Components/PrimaryButton'; 7 | import TextInput from '@/Components/TextInput'; 8 | import { Head, Link, useForm } from '@inertiajs/react'; 9 | 10 | export default function Login({ status, canResetPassword }: { status?: string, canResetPassword: boolean }) { 11 | const { data, setData, post, processing, errors, reset } = useForm({ 12 | email: '', 13 | password: '', 14 | remember: false, 15 | }); 16 | 17 | useEffect(() => { 18 | return () => { 19 | reset('password'); 20 | }; 21 | }, []); 22 | 23 | const submit: FormEventHandler = (e) => { 24 | e.preventDefault(); 25 | 26 | post(route('login')); 27 | }; 28 | 29 | return ( 30 | 31 | 32 | 33 | {status &&
{status}
} 34 | 35 |
36 |
37 | 38 | 39 | setData('email', e.target.value)} 48 | /> 49 | 50 | 51 |
52 | 53 |
54 | 55 | 56 | setData('password', e.target.value)} 64 | /> 65 | 66 | 67 |
68 | 69 |
70 | 78 |
79 | 80 |
81 | {canResetPassword && ( 82 | 86 | Forgot your password? 87 | 88 | )} 89 | 90 | 91 | Log in 92 | 93 |
94 |
95 |
96 | ); 97 | } 98 | -------------------------------------------------------------------------------- /resources/js/Pages/Auth/ResetPassword.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, FormEventHandler } from 'react'; 2 | import GuestLayout from '@/Layouts/GuestLayout'; 3 | import InputError from '@/Components/InputError'; 4 | import InputLabel from '@/Components/InputLabel'; 5 | import PrimaryButton from '@/Components/PrimaryButton'; 6 | import TextInput from '@/Components/TextInput'; 7 | import { Head, useForm } from '@inertiajs/react'; 8 | 9 | export default function ResetPassword({ token, email }: { token: string, email: string }) { 10 | const { data, setData, post, processing, errors, reset } = useForm({ 11 | token: token, 12 | email: email, 13 | password: '', 14 | password_confirmation: '', 15 | }); 16 | 17 | useEffect(() => { 18 | return () => { 19 | reset('password', 'password_confirmation'); 20 | }; 21 | }, []); 22 | 23 | const submit: FormEventHandler = (e) => { 24 | e.preventDefault(); 25 | 26 | post(route('password.store')); 27 | }; 28 | 29 | return ( 30 | 31 | 32 | 33 |
34 |
35 | 36 | 37 | setData('email', e.target.value)} 45 | /> 46 | 47 | 48 |
49 | 50 |
51 | 52 | 53 | setData('password', e.target.value)} 62 | /> 63 | 64 | 65 |
66 | 67 |
68 | 69 | 70 | setData('password_confirmation', e.target.value)} 77 | /> 78 | 79 | 80 |
81 | 82 |
83 | 84 | Reset Password 85 | 86 |
87 |
88 |
89 | ); 90 | } 91 | -------------------------------------------------------------------------------- /resources/js/Pages/Auth/VerifyEmail.tsx: -------------------------------------------------------------------------------- 1 | import GuestLayout from '@/Layouts/GuestLayout'; 2 | import PrimaryButton from '@/Components/PrimaryButton'; 3 | import { Head, Link, useForm } from '@inertiajs/react'; 4 | import { FormEventHandler } from 'react'; 5 | 6 | export default function VerifyEmail({ status }: { status?: string }) { 7 | const { post, processing } = useForm({}); 8 | 9 | const submit: FormEventHandler = (e) => { 10 | e.preventDefault(); 11 | 12 | post(route('verification.send')); 13 | }; 14 | 15 | return ( 16 | 17 | 18 | 19 |
20 | Thanks for signing up! Before getting started, could you verify your email address by clicking on the 21 | link we just emailed to you? If you didn't receive the email, we will gladly send you another. 22 |
23 | 24 | {status === 'verification-link-sent' && ( 25 |
26 | A new verification link has been sent to the email address you provided during registration. 27 |
28 | )} 29 | 30 |
31 |
32 | Resend Verification Email 33 | 34 | 40 | Log Out 41 | 42 |
43 |
44 |
45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /resources/js/Pages/Dashboard.tsx: -------------------------------------------------------------------------------- 1 | import { Head } from '@inertiajs/react' 2 | import { PageProps } from '@/types' 3 | import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' 4 | import MonthlyRevenue from '@/Components/statistics/MonthlyRevenue' 5 | import UserEngagement from '@/Components/statistics/UserEngagement' 6 | import SalesChart from '@/Components/statistics/SalesChart' 7 | import TaskEmails from '@/Components/TaskEmails' 8 | 9 | export default function Dashboard({ auth, users }: PageProps) { 10 | return ( 11 | Dashboard} 14 | > 15 | 16 | 17 |
18 |
19 |
20 | 21 | 22 |
23 | 24 |
25 |
26 | 27 | 28 |
29 |
30 |
31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /resources/js/Pages/Profile/Edit.tsx: -------------------------------------------------------------------------------- 1 | import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' 2 | import DeleteUserForm from './Partials/DeleteUserForm' 3 | import UpdatePasswordForm from './Partials/UpdatePasswordForm' 4 | import UpdateProfileInformationForm from './Partials/UpdateProfileInformationForm' 5 | import { Head } from '@inertiajs/react' 6 | import { PageProps } from '@/types' 7 | 8 | export default function Edit({ 9 | auth, 10 | mustVerifyEmail, 11 | status 12 | }: PageProps<{ mustVerifyEmail: boolean; status?: string }>) { 13 | return ( 14 | 18 | Profile 19 | 20 | } 21 | > 22 | 23 | 24 |
25 |
26 |
27 | 32 |
33 | 34 |
35 | 36 |
37 | 38 |
39 | 40 |
41 |
42 |
43 |
44 | ) 45 | } 46 | -------------------------------------------------------------------------------- /resources/js/Pages/Profile/Partials/DeleteUserForm.tsx: -------------------------------------------------------------------------------- 1 | import { useRef, useState, FormEventHandler } from 'react'; 2 | import DangerButton from '@/Components/DangerButton'; 3 | import InputError from '@/Components/InputError'; 4 | import InputLabel from '@/Components/InputLabel'; 5 | import Modal from '@/Components/Modal'; 6 | import SecondaryButton from '@/Components/SecondaryButton'; 7 | import TextInput from '@/Components/TextInput'; 8 | import { useForm } from '@inertiajs/react'; 9 | 10 | export default function DeleteUserForm({ className = '' }: { className?: string }) { 11 | const [confirmingUserDeletion, setConfirmingUserDeletion] = useState(false); 12 | const passwordInput = useRef(null); 13 | 14 | const { 15 | data, 16 | setData, 17 | delete: destroy, 18 | processing, 19 | reset, 20 | errors, 21 | } = useForm({ 22 | password: '', 23 | }); 24 | 25 | const confirmUserDeletion = () => { 26 | setConfirmingUserDeletion(true); 27 | }; 28 | 29 | const deleteUser: FormEventHandler = (e) => { 30 | e.preventDefault(); 31 | 32 | destroy(route('profile.destroy'), { 33 | preserveScroll: true, 34 | onSuccess: () => closeModal(), 35 | onError: () => passwordInput.current?.focus(), 36 | onFinish: () => reset(), 37 | }); 38 | }; 39 | 40 | const closeModal = () => { 41 | setConfirmingUserDeletion(false); 42 | 43 | reset(); 44 | }; 45 | 46 | return ( 47 |
48 |
49 |

Delete Account

50 | 51 |

52 | Once your account is deleted, all of its resources and data will be permanently deleted. Before 53 | deleting your account, please download any data or information that you wish to retain. 54 |

55 |
56 | 57 | Delete Account 58 | 59 | 60 |
61 |

62 | Are you sure you want to delete your account? 63 |

64 | 65 |

66 | Once your account is deleted, all of its resources and data will be permanently deleted. Please 67 | enter your password to confirm you would like to permanently delete your account. 68 |

69 | 70 |
71 | 72 | 73 | setData('password', e.target.value)} 80 | className="mt-1 block w-3/4" 81 | isFocused 82 | placeholder="Password" 83 | /> 84 | 85 | 86 |
87 | 88 |
89 | Cancel 90 | 91 | 92 | Delete Account 93 | 94 |
95 |
96 |
97 |
98 | ); 99 | } 100 | -------------------------------------------------------------------------------- /resources/js/app.tsx: -------------------------------------------------------------------------------- 1 | import './bootstrap' 2 | import '../css/app.css' 3 | import { createRoot } from 'react-dom/client' 4 | import { createInertiaApp } from '@inertiajs/react' 5 | import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers' 6 | // import { createTheme, ThemeProvider } from '@mui/material/styles' 7 | // import useMediaQuery from '@mui/material/useMediaQuery' 8 | // import CssBaseline from '@mui/material/CssBaseline' 9 | import React from 'react' 10 | 11 | const appName = import.meta.env.VITE_APP_NAME || 'Laravel' 12 | 13 | // function AppWrapper({ App, props }: { App: any; props: any }) { 14 | // const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)') 15 | // const theme = React.useMemo( 16 | // () => 17 | // createTheme({ 18 | // palette: { 19 | // mode: prefersDarkMode ? 'dark' : 'light' 20 | // } 21 | // }), 22 | // [prefersDarkMode] 23 | // ) 24 | 25 | // return 26 | // } 27 | 28 | createInertiaApp({ 29 | title: title => `${title} - ${appName}`, 30 | resolve: name => 31 | resolvePageComponent( 32 | `./Pages/${name}.tsx`, 33 | import.meta.glob('./Pages/**/*.tsx') 34 | ), 35 | setup({ el, App, props }) { 36 | const root = createRoot(el) 37 | 38 | root.render() 39 | }, 40 | progress: { 41 | color: '#4B5563' 42 | } 43 | }) 44 | -------------------------------------------------------------------------------- /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 | 6 | import './echo' 7 | -------------------------------------------------------------------------------- /resources/js/echo.js: -------------------------------------------------------------------------------- 1 | import Echo from 'laravel-echo'; 2 | 3 | import Pusher from 'pusher-js'; 4 | window.Pusher = Pusher; 5 | 6 | window.Echo = new Echo({ 7 | broadcaster: 'reverb', 8 | key: import.meta.env.VITE_REVERB_APP_KEY, 9 | wsHost: import.meta.env.VITE_REVERB_HOST, 10 | wsPort: import.meta.env.VITE_REVERB_PORT ?? 80, 11 | wssPort: import.meta.env.VITE_REVERB_PORT ?? 443, 12 | forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https', 13 | enabledTransports: ['ws', 'wss'], 14 | }); 15 | -------------------------------------------------------------------------------- /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/menus.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | PiLayoutDuotone, 3 | PiFilesDuotone, 4 | PiUsersThreeDuotone, 5 | PiChartLineUpDuotone, 6 | PiWarehouseDuotone, 7 | PiUsersDuotone, 8 | PiHandTapDuotone, 9 | PiChartPieDuotone, 10 | PiTrafficSignalDuotone, 11 | PiMoneyWavyDuotone, 12 | PiDatabaseDuotone, 13 | PiNoteDuotone, 14 | PiQuestionDuotone, 15 | PiLifebuoyDuotone, 16 | PiMegaphoneDuotone 17 | } from 'react-icons/pi' 18 | 19 | type MenuItem = { 20 | label: string 21 | icon: React.JSX.Element | null 22 | href?: string | null 23 | } 24 | 25 | const menuItems: MenuItem[] = [ 26 | { 27 | label: 'Dashboard', 28 | icon: , 29 | href: '/dashboard' 30 | }, 31 | { 32 | label: 'Transactions', 33 | icon: , 34 | href: '/transactions' 35 | }, 36 | { 37 | label: 'Inventory', 38 | icon: , 39 | href: '/inventory' 40 | }, 41 | { 42 | label: 'Analytics', 43 | icon: , 44 | href: '/analytics' 45 | }, 46 | { 47 | label: 'separator', 48 | icon: null 49 | // href: undefined 50 | }, 51 | { 52 | label: 'Subscribers', 53 | icon: , 54 | href: '/subscribers' 55 | }, 56 | { 57 | label: 'Clients', 58 | icon: , 59 | href: '/clients' 60 | }, 61 | { 62 | label: 'User Engagements', 63 | icon: , 64 | href: '/clients' 65 | }, 66 | { 67 | label: 'separator', 68 | icon: null 69 | // href: undefined 70 | }, 71 | { 72 | label: 'Reports', 73 | icon: , 74 | href: '/reports' 75 | }, 76 | { 77 | label: 'Traffic Analysis', 78 | icon: , 79 | href: '/traffic-analysis' 80 | }, 81 | { 82 | label: 'Sales Data', 83 | icon: , 84 | href: '/sales-data' 85 | }, 86 | { 87 | label: 'separator', 88 | icon: null 89 | // href: undefined 90 | }, 91 | { 92 | label: 'System Maintenance', 93 | icon: , 94 | href: '/system-maintenance' 95 | }, 96 | { 97 | label: 'System Logs', 98 | icon: , 99 | href: '/system-logs' 100 | }, 101 | { 102 | label: 'separator', 103 | icon: null 104 | // href: undefined 105 | }, 106 | { 107 | label: 'Help Center', 108 | icon: , 109 | href: '/help-support' 110 | }, 111 | { 112 | label: 'FAQ', 113 | icon: , 114 | href: '/faq' 115 | }, 116 | { 117 | label: 'separator', 118 | icon: null 119 | // href: undefined 120 | }, 121 | { 122 | label: 'User Feedback', 123 | icon: , 124 | href: '/user-feedback' 125 | } 126 | ] 127 | 128 | export default menuItems 129 | -------------------------------------------------------------------------------- /resources/js/types/global.d.ts: -------------------------------------------------------------------------------- 1 | import { AxiosInstance } from 'axios'; 2 | import { route as ziggyRoute } from 'ziggy-js'; 3 | 4 | declare global { 5 | interface Window { 6 | axios: AxiosInstance; 7 | } 8 | 9 | var route: typeof ziggyRoute; 10 | } 11 | -------------------------------------------------------------------------------- /resources/js/types/index.d.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | id: number; 3 | name: string; 4 | email: string; 5 | task_checked_at: string; 6 | email_verified_at: string; 7 | } 8 | 9 | export interface DatabaseBackup { 10 | id: number; 11 | user_id: number; 12 | path: string; 13 | created_at: string; 14 | updated_at: string; 15 | } 16 | 17 | export type PageProps = Record> = T & { 18 | auth: { 19 | user: User; 20 | }; 21 | users: User[]; 22 | backups: DatabaseBackup[]; 23 | }; 24 | -------------------------------------------------------------------------------- /resources/js/types/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /resources/views/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{ config('app.name', 'Laravel') }} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | @routes 16 | @viteReactRefresh 17 | @vite(['resources/js/app.tsx', "resources/js/Pages/{$page['component']}.tsx"]) 18 | @inertiaHead 19 | 20 | 21 | @inertia 22 | 23 | 24 | -------------------------------------------------------------------------------- /routes/auth.php: -------------------------------------------------------------------------------- 1 | group(function () { 15 | // Route::get('register', [RegisteredUserController::class, 'create']) 16 | // ->name('register'); 17 | 18 | // Route::post('register', [RegisteredUserController::class, 'store']); 19 | 20 | // Route::get('login', [AuthenticatedSessionController::class, 'create']) 21 | // ->name('login'); 22 | 23 | // Route::post('login', [AuthenticatedSessionController::class, 'store']); 24 | 25 | // Route::get('forgot-password', [PasswordResetLinkController::class, 'create']) 26 | // ->name('password.request'); 27 | 28 | // Route::post('forgot-password', [PasswordResetLinkController::class, 'store']) 29 | // ->name('password.email'); 30 | 31 | // Route::get('reset-password/{token}', [NewPasswordController::class, 'create']) 32 | // ->name('password.reset'); 33 | 34 | // Route::post('reset-password', [NewPasswordController::class, 'store']) 35 | // ->name('password.store'); 36 | }); 37 | 38 | Route::middleware('auth')->group(function () { 39 | Route::get('verify-email', EmailVerificationPromptController::class) 40 | ->name('verification.notice'); 41 | 42 | Route::get('verify-email/{id}/{hash}', VerifyEmailController::class) 43 | ->middleware(['signed', 'throttle:6,1']) 44 | ->name('verification.verify'); 45 | 46 | Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store']) 47 | ->middleware('throttle:6,1') 48 | ->name('verification.send'); 49 | 50 | Route::get('confirm-password', [ConfirmablePasswordController::class, 'show']) 51 | ->name('password.confirm'); 52 | 53 | Route::post('confirm-password', [ConfirmablePasswordController::class, 'store']); 54 | 55 | Route::put('password', [PasswordController::class, 'update'])->name('password.update'); 56 | 57 | Route::post('logout', [AuthenticatedSessionController::class, 'destroy']) 58 | ->name('logout'); 59 | }); 60 | -------------------------------------------------------------------------------- /routes/channels.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 7 | }); 8 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 8 | })->purpose('Display an inspiring quote')->hourly(); 9 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | Route::has('login'), 15 | 'canRegister' => Route::has('register'), 16 | 'laravelVersion' => Application::VERSION, 17 | 'phpVersion' => PHP_VERSION, 18 | ]); 19 | }); 20 | 21 | Route::get('/dashboard', function () { 22 | $users = User::where('id', '!=', Auth::id())->simplePaginate(10); 23 | 24 | return Inertia::render('Dashboard', ['users' => $users]); 25 | })->middleware(['auth', 'verified'])->name('dashboard'); 26 | 27 | Route::middleware('auth')->group(function () { 28 | Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); 29 | Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); 30 | Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); 31 | 32 | Route::get('/system-maintenance', function () { 33 | return Inertia::render('BackupDatabase'); 34 | }); 35 | 36 | Route::post('/backup', [BackupController::class, 'backup']); 37 | 38 | Route::get('/download-backup/{path}', function($path) { 39 | return response()->download(storage_path("app/$path")); 40 | }); 41 | 42 | Route::get('/get-backups', function() { 43 | return DatabaseBackup::all(); 44 | }); 45 | 46 | Route::post('/delete-backup', [BackupController::class, 'deleteBackup']); 47 | }); 48 | 49 | require __DIR__.'/auth.php'; 50 | -------------------------------------------------------------------------------- /storage/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/storage/.DS_Store -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/storage/framework/.DS_Store -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | import forms from '@tailwindcss/forms' 2 | import defaultTheme from 'tailwindcss/defaultTheme' 3 | 4 | /** @type {import('tailwindcss').Config} */ 5 | export default { 6 | darkMode: 'media', 7 | content: [ 8 | './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php', 9 | './storage/framework/views/*.php', 10 | './resources/views/**/*.blade.php', 11 | './resources/js/**/*.tsx' 12 | ], 13 | 14 | theme: { 15 | container: { 16 | center: true, 17 | padding: '2rem', 18 | screens: { 19 | '2xl': '1400px' 20 | } 21 | }, 22 | extend: { 23 | colors: { 24 | 'gradient-start-light': '#cbd5e1', 25 | 'gradient-end-light': '#94a3b8', 26 | 'gradient-start-dark': '#4b5563', 27 | 'gradient-end-dark': '#1f2937', 28 | border: 'hsl(var(--border))', 29 | input: 'hsl(var(--input))', 30 | ring: 'hsl(var(--ring))', 31 | background: 'hsl(var(--background))', 32 | foreground: 'hsl(var(--foreground))', 33 | primary: { 34 | DEFAULT: 'hsl(var(--primary))', 35 | foreground: 'hsl(var(--primary-foreground))' 36 | }, 37 | secondary: { 38 | DEFAULT: 'hsl(var(--secondary))', 39 | foreground: 'hsl(var(--secondary-foreground))' 40 | }, 41 | destructive: { 42 | DEFAULT: 'hsl(var(--destructive))', 43 | foreground: 'hsl(var(--destructive-foreground))' 44 | }, 45 | muted: { 46 | DEFAULT: 'hsl(var(--muted))', 47 | foreground: 'hsl(var(--muted-foreground))' 48 | }, 49 | accent: { 50 | DEFAULT: 'hsl(var(--accent))', 51 | foreground: 'hsl(var(--accent-foreground))' 52 | }, 53 | popover: { 54 | DEFAULT: 'hsl(var(--popover))', 55 | foreground: 'hsl(var(--popover-foreground))' 56 | }, 57 | card: { 58 | DEFAULT: 'hsl(var(--card))', 59 | foreground: 'hsl(var(--card-foreground))' 60 | } 61 | }, 62 | borderRadius: { 63 | lg: `var(--radius)`, 64 | md: `calc(var(--radius) - 2px)`, 65 | sm: 'calc(var(--radius) - 4px)' 66 | }, 67 | fontFamily: { 68 | // sans: ['Inter', ...defaultTheme.fontFamily.sans], 69 | sans: ['"GeistVF", sans-serif'], 70 | mono: [ 71 | 'GeistMonoVF', 72 | 'JetBrainsMono', 73 | ...defaultTheme.fontFamily.mono 74 | ] 75 | }, 76 | keyframes: { 77 | 'accordion-down': { 78 | from: { height: 0 }, 79 | to: { height: 'var(--radix-accordion-content-height)' } 80 | }, 81 | 'accordion-up': { 82 | from: { height: 'var(--radix-accordion-content-height)' }, 83 | to: { height: 0 } 84 | } 85 | }, 86 | animation: { 87 | 'accordion-down': 'accordion-down 0.2s ease-out', 88 | 'accordion-up': 'accordion-up 0.2s ease-out' 89 | } 90 | } 91 | }, 92 | 93 | plugins: [forms, require('tailwindcss-animate')] 94 | } 95 | -------------------------------------------------------------------------------- /tests/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glennraya/backupdb/0ba1b2ad547ede4e2ec9b241b9d7c7f3305f304e/tests/.DS_Store -------------------------------------------------------------------------------- /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('login')); 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('correct password must be provided to update 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 | -------------------------------------------------------------------------------- /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/Feature/ProfileTest.php: -------------------------------------------------------------------------------- 1 | create(); 7 | 8 | $response = $this 9 | ->actingAs($user) 10 | ->get('/profile'); 11 | 12 | $response->assertOk(); 13 | }); 14 | 15 | test('profile information can be updated', function () { 16 | $user = User::factory()->create(); 17 | 18 | $response = $this 19 | ->actingAs($user) 20 | ->patch('/profile', [ 21 | 'name' => 'Test User', 22 | 'email' => 'test@example.com', 23 | ]); 24 | 25 | $response 26 | ->assertSessionHasNoErrors() 27 | ->assertRedirect('/profile'); 28 | 29 | $user->refresh(); 30 | 31 | $this->assertSame('Test User', $user->name); 32 | $this->assertSame('test@example.com', $user->email); 33 | $this->assertNull($user->email_verified_at); 34 | }); 35 | 36 | test('email verification status is unchanged when the email address is unchanged', function () { 37 | $user = User::factory()->create(); 38 | 39 | $response = $this 40 | ->actingAs($user) 41 | ->patch('/profile', [ 42 | 'name' => 'Test User', 43 | 'email' => $user->email, 44 | ]); 45 | 46 | $response 47 | ->assertSessionHasNoErrors() 48 | ->assertRedirect('/profile'); 49 | 50 | $this->assertNotNull($user->refresh()->email_verified_at); 51 | }); 52 | 53 | test('user can delete their account', function () { 54 | $user = User::factory()->create(); 55 | 56 | $response = $this 57 | ->actingAs($user) 58 | ->delete('/profile', [ 59 | 'password' => 'password', 60 | ]); 61 | 62 | $response 63 | ->assertSessionHasNoErrors() 64 | ->assertRedirect('/'); 65 | 66 | $this->assertGuest(); 67 | $this->assertNull($user->fresh()); 68 | }); 69 | 70 | test('correct password must be provided to delete account', function () { 71 | $user = User::factory()->create(); 72 | 73 | $response = $this 74 | ->actingAs($user) 75 | ->from('/profile') 76 | ->delete('/profile', [ 77 | 'password' => 'wrong-password', 78 | ]); 79 | 80 | $response 81 | ->assertSessionHasErrors('password') 82 | ->assertRedirect('/profile'); 83 | 84 | $this->assertNotNull($user->fresh()); 85 | }); 86 | -------------------------------------------------------------------------------- /tests/Pest.php: -------------------------------------------------------------------------------- 1 | in('Feature'); 18 | 19 | /* 20 | |-------------------------------------------------------------------------- 21 | | Expectations 22 | |-------------------------------------------------------------------------- 23 | | 24 | | When you're writing tests, you often need to check that values meet certain conditions. The 25 | | "expect()" function gives you access to a set of "expectations" methods that you can use 26 | | to assert different things. Of course, you may extend the Expectation API at any time. 27 | | 28 | */ 29 | 30 | expect()->extend('toBeOne', function () { 31 | return $this->toBe(1); 32 | }); 33 | 34 | /* 35 | |-------------------------------------------------------------------------- 36 | | Functions 37 | |-------------------------------------------------------------------------- 38 | | 39 | | While Pest is very powerful out-of-the-box, you may have some testing code specific to your 40 | | project that you don't want to repeat in every file. Here you can also expose helpers as 41 | | global functions to help you to reduce the number of lines of code in your test files. 42 | | 43 | */ 44 | 45 | function something() 46 | { 47 | // .. 48 | } 49 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | toBeTrue(); 5 | }); 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "jsx": "react-jsx", 7 | "strict": true, 8 | "isolatedModules": true, 9 | "target": "ESNext", 10 | "esModuleInterop": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "noEmit": true, 13 | "paths": { 14 | "@/*": ["./resources/js/*"], 15 | "ziggy-js": ["./vendor/tightenco/ziggy"] 16 | } 17 | }, 18 | "include": ["resources/js/**/*.ts", "resources/js/**/*.tsx", "resources/js/**/*.d.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import laravel from 'laravel-vite-plugin'; 3 | import react from '@vitejs/plugin-react'; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | laravel({ 8 | input: 'resources/js/app.tsx', 9 | refresh: true, 10 | }), 11 | react(), 12 | ], 13 | }); 14 | --------------------------------------------------------------------------------