├── .editorconfig ├── .env.example ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── _ide_helper.php ├── _ide_helper_models.php ├── app ├── Console │ └── Kernel.php ├── Exceptions │ └── Handler.php ├── Http │ ├── Controllers │ │ ├── Auth │ │ │ └── EmailVerificationController.php │ │ └── Controller.php │ ├── Kernel.php │ └── Middleware │ │ ├── Authenticate.php │ │ ├── EncryptCookies.php │ │ ├── PreventRequestsDuringMaintenance.php │ │ ├── RedirectIfAuthenticated.php │ │ ├── SetLocale.php │ │ ├── TrimStrings.php │ │ ├── TrustHosts.php │ │ ├── TrustProxies.php │ │ ├── ValidateSignature.php │ │ └── VerifyCsrfToken.php ├── Livewire │ ├── Admin │ │ ├── Dashboard.php │ │ ├── Example.php │ │ └── User │ │ │ ├── Create.php │ │ │ ├── Edit.php │ │ │ ├── Index.php │ │ │ └── Show.php │ ├── Auth │ │ ├── Login.php │ │ ├── Password │ │ │ ├── Confirm.php │ │ │ ├── Request.php │ │ │ └── Reset.php │ │ ├── PrivacyPolicy.php │ │ ├── Register.php │ │ ├── TermsOfService.php │ │ └── Verify.php │ ├── Example.php │ ├── Home.php │ ├── Layouts │ │ ├── AdminNavigation.php │ │ ├── AdminSidebar.php │ │ ├── AppNavigation.php │ │ └── MainNavigation.php │ └── User │ │ ├── Dashboard.php │ │ ├── Example.php │ │ ├── Profile.php │ │ └── Settings.php ├── Models │ └── User.php └── Providers │ ├── AppServiceProvider.php │ ├── AuthServiceProvider.php │ ├── BroadcastServiceProvider.php │ ├── EventServiceProvider.php │ └── RouteServiceProvider.php ├── artisan ├── bootstrap ├── app.php └── cache │ └── .gitignore ├── composer.json ├── composer.lock ├── config ├── app.php ├── auth.php ├── broadcasting.php ├── cache.php ├── cors.php ├── database.php ├── debugbar.php ├── filesystems.php ├── hashing.php ├── ide-helper.php ├── livewire.php ├── logging.php ├── mail.php ├── queue.php ├── sanctum.php ├── services.php ├── session.php ├── toaster.php ├── translation.php └── view.php ├── database ├── .gitignore ├── factories │ └── UserFactory.php ├── migrations │ ├── 2014_10_12_000000_create_users_table.php │ ├── 2014_10_12_100000_create_password_reset_tokens_table.php │ ├── 2019_08_19_000000_create_failed_jobs_table.php │ └── 2019_12_14_000001_create_personal_access_tokens_table.php └── seeders │ ├── DatabaseSeeder.php │ └── UserSeeder.php ├── lang ├── en.json ├── en │ ├── auth.php │ ├── http-statuses.php │ ├── pagination.php │ ├── passwords.php │ └── validation.php ├── id.json └── id │ ├── auth.php │ ├── http-statuses.php │ ├── pagination.php │ ├── passwords.php │ └── validation.php ├── package-lock.json ├── package.json ├── phpunit.xml ├── pint.json ├── postcss.config.js ├── public ├── .htaccess ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── index.php ├── lali.png ├── robots.txt └── site.webmanifest ├── resources ├── css │ └── app.css ├── js │ ├── app.js │ └── bootstrap.js ├── markdown │ ├── privacy-policy-en.md │ ├── privacy-policy-id.md │ ├── terms-of-service-en.md │ └── terms-of-service-id.md └── views │ ├── components │ ├── action-message.blade.php │ ├── app-card.blade.php │ ├── auth-card.blade.php │ ├── badge.blade.php │ ├── banner.blade.php │ ├── button-link.blade.php │ ├── button.blade.php │ ├── checkbox.blade.php │ ├── danger-button.blade.php │ ├── dialog-modal.blade.php │ ├── divider.blade.php │ ├── dropdown-button.blade.php │ ├── dropdown-link.blade.php │ ├── dropdown.blade.php │ ├── form-card.blade.php │ ├── icon-arrow-down.blade.php │ ├── icon-arrow-up.blade.php │ ├── icon-dashboard.blade.php │ ├── icon-data-not-found.blade.php │ ├── icon-disable.blade.php │ ├── icon-home.blade.php │ ├── icon-logout.blade.php │ ├── icon-profile.blade.php │ ├── icon-setting.blade.php │ ├── icon-sort-asc.blade.php │ ├── icon-sort-desc.blade.php │ ├── input-error.blade.php │ ├── label.blade.php │ ├── logo.blade.php │ ├── modal.blade.php │ ├── nav-dropdown.blade.php │ ├── nav-link.blade.php │ ├── responsive-nav-link.blade.php │ ├── secondary-button-link.blade.php │ ├── secondary-button.blade.php │ ├── select-input.blade.php │ ├── session-message.blade.php │ ├── stat-card.blade.php │ ├── table-action-button.blade.php │ ├── table-action-link.blade.php │ ├── table-body-not-found.blade.php │ ├── table-body-td.blade.php │ ├── table-body-tr.blade.php │ ├── table-body.blade.php │ ├── table-head-th.blade.php │ ├── table-head-tr.blade.php │ ├── table-head.blade.php │ ├── table.blade.php │ └── text-input.blade.php │ ├── errors │ ├── 401.blade.php │ ├── 402.blade.php │ ├── 403.blade.php │ ├── 404.blade.php │ ├── 419.blade.php │ ├── 429.blade.php │ ├── 500.blade.php │ ├── 503.blade.php │ └── minimal.blade.php │ ├── layouts │ ├── admin-navigation.blade.php │ ├── admin-sidebar.blade.php │ ├── admin.blade.php │ ├── app-navigation.blade.php │ ├── app.blade.php │ ├── auth.blade.php │ ├── main-navigation.blade.php │ ├── main.blade.php │ └── partials │ │ ├── admin-user-dropdown-navigation.blade.php │ │ ├── admin-user-dropdown-sidebar.blade.php │ │ ├── app-user-dropdown.blade.php │ │ ├── footer.blade.php │ │ └── main-user-dropdown.blade.php │ ├── livewire │ ├── admin │ │ ├── dashboard.blade.php │ │ ├── example.blade.php │ │ └── user │ │ │ ├── create.blade.php │ │ │ ├── edit.blade.php │ │ │ ├── index.blade.php │ │ │ ├── partials │ │ │ ├── bulk-actions.blade.php │ │ │ ├── user-delete-modal.blade.php │ │ │ └── user-table.blade.php │ │ │ └── show.blade.php │ ├── auth │ │ ├── login.blade.php │ │ ├── password │ │ │ ├── confirm.blade.php │ │ │ ├── request.blade.php │ │ │ └── reset.blade.php │ │ ├── privacy-policy.blade.php │ │ ├── register.blade.php │ │ ├── terms-of-service.blade.php │ │ └── verify.blade.php │ ├── example.blade.php │ ├── home.blade.php │ └── user │ │ ├── dashboard.blade.php │ │ ├── example.blade.php │ │ ├── partials │ │ ├── profile │ │ │ ├── delete-account.blade.php │ │ │ ├── update-password.blade.php │ │ │ └── update-profile.blade.php │ │ └── settings │ │ │ ├── change-language.blade.php │ │ │ └── terms-and-policy.blade.php │ │ ├── profile.blade.php │ │ └── settings.blade.php │ ├── template │ ├── admin.blade.php │ ├── app.blade.php │ └── main.blade.php │ └── vendor │ ├── livewire │ ├── custom-tailwind.blade.php │ └── simple-custom-tailwind.blade.php │ └── toaster │ └── hub.blade.php ├── routes ├── api.php ├── auth.php ├── channels.php ├── console.php └── web.php ├── storage ├── app │ ├── .gitignore │ └── public │ │ └── .gitignore ├── debugbar │ └── .gitignore ├── framework │ ├── .gitignore │ ├── cache │ │ ├── .gitignore │ │ └── data │ │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ ├── testing │ │ └── .gitignore │ └── views │ │ └── .gitignore └── logs │ └── .gitignore ├── tailwind.config.js ├── tests ├── CreatesApplication.php ├── Feature │ ├── Admin │ │ ├── DashboardTest.php │ │ ├── ExampleTest.php │ │ └── User │ │ │ ├── CreateTest.php │ │ │ ├── EditTest.php │ │ │ ├── IndexTest.php │ │ │ └── ShowTest.php │ ├── Auth │ │ ├── EmailVerificationTest.php │ │ ├── LoginTest.php │ │ ├── Password │ │ │ ├── ConfirmTest.php │ │ │ ├── RequestTest.php │ │ │ └── ResetTest.php │ │ ├── PrivacyPolicyTest.php │ │ ├── RegisterTest.php │ │ ├── TermsOfServiceTest.php │ │ └── VerifyTest.php │ ├── ExampleTest.php │ ├── HomeTest.php │ ├── Navigation │ │ ├── AdminNavigationTest.php │ │ ├── AdminSidebarTest.php │ │ ├── AppNavigationTest.php │ │ └── MainNavigationTest.php │ └── User │ │ ├── DashboardTest.php │ │ ├── ExampleTest.php │ │ ├── ProfileTest.php │ │ └── SettingsTest.php ├── Pest.php └── TestCase.php └── vite.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME="Lali" 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | DEBUGBAR_ENABLED=true 6 | APP_URL=http://localhost 7 | 8 | LOG_CHANNEL=stack 9 | LOG_DEPRECATIONS_CHANNEL=null 10 | LOG_LEVEL=debug 11 | 12 | DB_CONNECTION=mysql 13 | DB_HOST=127.0.0.1 14 | DB_PORT=3306 15 | DB_DATABASE=lali 16 | DB_USERNAME=root 17 | DB_PASSWORD= 18 | 19 | BROADCAST_DRIVER=log 20 | CACHE_DRIVER=file 21 | FILESYSTEM_DISK=local 22 | QUEUE_CONNECTION=sync 23 | SESSION_DRIVER=file 24 | SESSION_LIFETIME=120 25 | 26 | MEMCACHED_HOST=127.0.0.1 27 | 28 | REDIS_HOST=127.0.0.1 29 | REDIS_PASSWORD=null 30 | REDIS_PORT=6379 31 | 32 | MAIL_MAILER=smtp 33 | MAIL_HOST=localhost 34 | MAIL_PORT=1025 35 | MAIL_USERNAME=null 36 | MAIL_PASSWORD=null 37 | MAIL_ENCRYPTION=null 38 | MAIL_FROM_ADDRESS="hello@example.com" 39 | MAIL_FROM_NAME="${APP_NAME}" 40 | 41 | AWS_ACCESS_KEY_ID= 42 | AWS_SECRET_ACCESS_KEY= 43 | AWS_DEFAULT_REGION=us-east-1 44 | AWS_BUCKET= 45 | AWS_USE_PATH_STYLE_ENDPOINT=false 46 | 47 | PUSHER_APP_ID= 48 | PUSHER_APP_KEY= 49 | PUSHER_APP_SECRET= 50 | PUSHER_HOST= 51 | PUSHER_PORT=443 52 | PUSHER_SCHEME=https 53 | PUSHER_APP_CLUSTER=mt1 54 | 55 | VITE_APP_NAME="${APP_NAME}" 56 | VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}" 57 | VITE_PUSHER_HOST="${PUSHER_HOST}" 58 | VITE_PUSHER_PORT="${PUSHER_PORT}" 59 | VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" 60 | VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" 61 | -------------------------------------------------------------------------------- /.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 | .phpunit.result.cache 12 | Homestead.json 13 | Homestead.yaml 14 | auth.json 15 | npm-debug.log 16 | yarn-error.log 17 | /.fleet 18 | /.idea 19 | /.vscode 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Arif Budiman Arrosyid 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /_ide_helper_models.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | 12 | 13 | namespace App\Models{ 14 | /** 15 | * App\Models\User 16 | * 17 | * @property int $id 18 | * @property string $name 19 | * @property string $email 20 | * @property \Illuminate\Support\Carbon|null $email_verified_at 21 | * @property mixed $password 22 | * @property string|null $remember_token 23 | * @property \Illuminate\Support\Carbon|null $created_at 24 | * @property \Illuminate\Support\Carbon|null $updated_at 25 | * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications 26 | * @property-read int|null $notifications_count 27 | * @property-read \Illuminate\Database\Eloquent\Collection $tokens 28 | * @property-read int|null $tokens_count 29 | * @method static \Database\Factories\UserFactory factory($count = null, $state = []) 30 | * @method static \Illuminate\Database\Eloquent\Builder|User newModelQuery() 31 | * @method static \Illuminate\Database\Eloquent\Builder|User newQuery() 32 | * @method static \Illuminate\Database\Eloquent\Builder|User query() 33 | * @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedAt($value) 34 | * @method static \Illuminate\Database\Eloquent\Builder|User whereEmail($value) 35 | * @method static \Illuminate\Database\Eloquent\Builder|User whereEmailVerifiedAt($value) 36 | * @method static \Illuminate\Database\Eloquent\Builder|User whereId($value) 37 | * @method static \Illuminate\Database\Eloquent\Builder|User whereName($value) 38 | * @method static \Illuminate\Database\Eloquent\Builder|User wherePassword($value) 39 | * @method static \Illuminate\Database\Eloquent\Builder|User whereRememberToken($value) 40 | * @method static \Illuminate\Database\Eloquent\Builder|User whereUpdatedAt($value) 41 | * @property-read string $first_name 42 | * @method static Builder|User search($search) 43 | * @mixin \Eloquent 44 | */ 45 | class User extends \Eloquent implements \Illuminate\Contracts\Auth\MustVerifyEmail {} 46 | } 47 | 48 | -------------------------------------------------------------------------------- /app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | command('inspire')->hourly(); 16 | } 17 | 18 | /** 19 | * Register the commands for the application. 20 | */ 21 | protected function commands(): void 22 | { 23 | $this->load(__DIR__.'/Commands'); 24 | 25 | require base_path('routes/console.php'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Exceptions/Handler.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | protected $dontFlash = [ 16 | 'current_password', 17 | 'password', 18 | 'password_confirmation', 19 | ]; 20 | 21 | /** 22 | * Register the exception handling callbacks for the application. 23 | */ 24 | public function register(): void 25 | { 26 | $this->reportable(function (Throwable $e) { 27 | // 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/EmailVerificationController.php: -------------------------------------------------------------------------------- 1 | getKey())) { 17 | throw new AuthorizationException(); 18 | } 19 | 20 | if (! hash_equals((string) $hash, sha1(Auth::user()->getEmailForVerification()))) { 21 | throw new AuthorizationException(); 22 | } 23 | 24 | if (Auth::user()->hasVerifiedEmail()) { 25 | Toaster::success('Your email address already verified.'); 26 | 27 | return redirect(session('url.intended', route('user.dashboard'))); 28 | } 29 | 30 | if (Auth::user()->markEmailAsVerified()) { 31 | event(new Verified(Auth::user())); 32 | Toaster::success('Your email address has been verified.'); 33 | 34 | return redirect(session('url.intended', route('user.dashboard'))); 35 | } 36 | 37 | return redirect(route('user.dashboard')); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | protected $middleware = [ 17 | // \App\Http\Middleware\TrustHosts::class, 18 | \App\Http\Middleware\TrustProxies::class, 19 | \Illuminate\Http\Middleware\HandleCors::class, 20 | \App\Http\Middleware\PreventRequestsDuringMaintenance::class, 21 | \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, 22 | \App\Http\Middleware\TrimStrings::class, 23 | \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, 24 | ]; 25 | 26 | /** 27 | * The application's route middleware groups. 28 | * 29 | * @var array> 30 | */ 31 | protected $middlewareGroups = [ 32 | 'web' => [ 33 | \App\Http\Middleware\EncryptCookies::class, 34 | \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, 35 | \Illuminate\Session\Middleware\StartSession::class, 36 | \Illuminate\View\Middleware\ShareErrorsFromSession::class, 37 | \App\Http\Middleware\VerifyCsrfToken::class, 38 | \Illuminate\Routing\Middleware\SubstituteBindings::class, 39 | \App\Http\Middleware\SetLocale::class, 40 | ], 41 | 42 | 'api' => [ 43 | // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 44 | \Illuminate\Routing\Middleware\ThrottleRequests::class.':api', 45 | \Illuminate\Routing\Middleware\SubstituteBindings::class, 46 | ], 47 | ]; 48 | 49 | /** 50 | * The application's middleware aliases. 51 | * 52 | * Aliases may be used instead of class names to conveniently assign middleware to routes and groups. 53 | * 54 | * @var array 55 | */ 56 | protected $middlewareAliases = [ 57 | 'auth' => \App\Http\Middleware\Authenticate::class, 58 | 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 59 | 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class, 60 | 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 61 | 'can' => \Illuminate\Auth\Middleware\Authorize::class, 62 | 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 63 | 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, 64 | 'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class, 65 | 'signed' => \App\Http\Middleware\ValidateSignature::class, 66 | 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 67 | 'verified.email' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 68 | ]; 69 | } 70 | -------------------------------------------------------------------------------- /app/Http/Middleware/Authenticate.php: -------------------------------------------------------------------------------- 1 | expectsJson() ? null : route('login'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /app/Http/Middleware/PreventRequestsDuringMaintenance.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /app/Http/Middleware/RedirectIfAuthenticated.php: -------------------------------------------------------------------------------- 1 | check()) { 24 | return redirect(RouteServiceProvider::HOME); 25 | } 26 | } 27 | 28 | return $next($request); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Http/Middleware/SetLocale.php: -------------------------------------------------------------------------------- 1 | has('locale')) { 19 | app()->setLocale(session()->get('locale')); 20 | } else { 21 | app()->setLocale(config('app.locale')); 22 | } 23 | 24 | return $next($request); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrimStrings.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | 'current_password', 16 | 'password', 17 | 'password_confirmation', 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrustHosts.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | public function hosts(): array 15 | { 16 | return [ 17 | $this->allSubdomainsOfApplicationUrl(), 18 | ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrustProxies.php: -------------------------------------------------------------------------------- 1 | |string|null 14 | */ 15 | protected $proxies; 16 | 17 | /** 18 | * The headers that should be used to detect proxies. 19 | * 20 | * @var int 21 | */ 22 | protected $headers = 23 | Request::HEADER_X_FORWARDED_FOR | 24 | Request::HEADER_X_FORWARDED_HOST | 25 | Request::HEADER_X_FORWARDED_PORT | 26 | Request::HEADER_X_FORWARDED_PROTO | 27 | Request::HEADER_X_FORWARDED_AWS_ELB; 28 | } 29 | -------------------------------------------------------------------------------- /app/Http/Middleware/ValidateSignature.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 'fbclid', 16 | // 'utm_campaign', 17 | // 'utm_content', 18 | // 'utm_medium', 19 | // 'utm_source', 20 | // 'utm_term', 21 | ]; 22 | } 23 | -------------------------------------------------------------------------------- /app/Http/Middleware/VerifyCsrfToken.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /app/Livewire/Admin/Dashboard.php: -------------------------------------------------------------------------------- 1 | put('url.intended', route('admin.dashboard')); 18 | 19 | $latest_created_users = User::latest()->take(4)->get(); 20 | $latest_updated_users = User::whereColumn('created_at', '!=', 'updated_at')->latest('updated_at')->take(4)->get(); 21 | $total_users = User::count(); 22 | $total_verified_users = User::where('email_verified_at', '!=', null)->count(); 23 | $total_unverified_users = User::where('email_verified_at', null)->count(); 24 | $total_register_last_month = User::where('created_at', '>=', now()->subMonth())->count(); 25 | 26 | return view('livewire.admin.dashboard', compact( 27 | 'latest_created_users', 28 | 'latest_updated_users', 29 | 'total_users', 30 | 'total_verified_users', 31 | 'total_unverified_users', 32 | 'total_register_last_month' 33 | )); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/Livewire/Admin/Example.php: -------------------------------------------------------------------------------- 1 | put('url.intended', route('admin.example')); 17 | 18 | return view('livewire.admin.example'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Livewire/Admin/User/Create.php: -------------------------------------------------------------------------------- 1 | 'required|string|min:3|max:255|regex:/^[a-zA-Z\s]+$/', 24 | 'email' => 'required|email|unique:users,email', 25 | ]; 26 | } 27 | 28 | public function messages(): array 29 | { 30 | return [ 31 | 'name.regex' => __('The name may only contain letters and spaces.'), 32 | ]; 33 | } 34 | 35 | public function createUser(): void 36 | { 37 | $this->validate(); 38 | User::create([ 39 | 'name' => Str::title($this->name), 40 | 'email' => $this->email, 41 | 'password' => Hash::make('password'), 42 | ]); 43 | Toaster::success('User created.'); 44 | $this->redirect(session('url.intended', route('admin.users.index')), navigate: true); 45 | } 46 | 47 | public function cancelCreate(): void 48 | { 49 | $this->redirect(session('url.intended', route('admin.users.index')), navigate: true); 50 | } 51 | 52 | #[Layout('layouts.admin')] 53 | #[Title('Admin Users Create')] 54 | public function render(): View 55 | { 56 | return view('livewire.admin.user.create'); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/Livewire/Admin/User/Edit.php: -------------------------------------------------------------------------------- 1 | name = $user->name; 24 | $this->email = $user->email; 25 | } 26 | 27 | public function rules(): array 28 | { 29 | return [ 30 | 'name' => 'required|string|min:3|max:255|regex:/^[a-zA-Z\s]+$/', 31 | 'email' => 'required|email|unique:users,email,'.$this->user->id, 32 | ]; 33 | } 34 | 35 | public function messages(): array 36 | { 37 | return [ 38 | 'name.regex' => __('The name may only contain letters and spaces.'), 39 | ]; 40 | } 41 | 42 | public function updateUser(): void 43 | { 44 | $this->validate(); 45 | if ($this->email !== $this->user->email) { 46 | $this->user->timestamps = false; 47 | $this->user->forceFill([ 48 | 'email_verified_at' => null, 49 | ])->save(); 50 | } 51 | if ($this->name === $this->user->name && $this->email === $this->user->email) { 52 | Toaster::info('Nothing changed.'); 53 | 54 | return; 55 | } 56 | $this->user->timestamps = false; 57 | $this->user->update([ 58 | 'name' => Str::title($this->name), 59 | 'email' => $this->email, 60 | ]); 61 | Toaster::success('User updated.'); 62 | $this->redirect(session('url.intended', route('admin.users.show', $this->user)), navigate: true); 63 | } 64 | 65 | public function cancelEdit(): void 66 | { 67 | $this->redirect(session('url.intended', route('admin.users.show', $this->user)), navigate: true); 68 | } 69 | 70 | #[Layout('layouts.admin')] 71 | #[Title('Admin Users Edit')] 72 | public function render(): View 73 | { 74 | return view('livewire.admin.user.edit'); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/Livewire/Admin/User/Show.php: -------------------------------------------------------------------------------- 1 | put('url.intended', route('admin.users.show', $this->user)); 20 | 21 | return view('livewire.admin.user.show'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/Livewire/Auth/Login.php: -------------------------------------------------------------------------------- 1 | ['required', 'email'], 23 | 'password' => ['required'], 24 | ]; 25 | } 26 | 27 | public function login(): void 28 | { 29 | $this->validate(); 30 | if (! Auth::attempt(['email' => $this->email, 'password' => $this->password], $this->remember)) { 31 | $this->addError('email', __('auth.failed')); 32 | 33 | return; 34 | } 35 | $this->redirect(session('url.intended', route('user.dashboard')), navigate: true); 36 | } 37 | 38 | #[Layout('layouts.auth')] 39 | #[Title('Login')] 40 | public function render(): View 41 | { 42 | return view('livewire.auth.login'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/Livewire/Auth/Password/Confirm.php: -------------------------------------------------------------------------------- 1 | ['required', 'current_password'], 19 | ]; 20 | } 21 | 22 | public function confirmPassword(): void 23 | { 24 | $this->validate(); 25 | session()->put('auth.password_confirmed_at', time()); 26 | Toaster::success('Password confirmed.'); 27 | $this->redirect(session('url.intended', route('user.dashboard')), navigate: true); 28 | } 29 | 30 | #[Layout('layouts.auth')] 31 | #[Title('Confirm Password')] 32 | public function render(): View 33 | { 34 | if (session()->has('auth.password_confirmed_at')) { 35 | $this->redirect(session('url.intended', route('user.dashboard')), navigate: true); 36 | } 37 | 38 | return view('livewire.auth.password.confirm'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Livewire/Auth/Password/Request.php: -------------------------------------------------------------------------------- 1 | ['required', 'email'], 20 | ]; 21 | } 22 | 23 | public function sendResetPasswordLink(): void 24 | { 25 | $this->validate(); 26 | $response = Password::broker()->sendResetLink(['email' => $this->email]); 27 | if ($response == Password::RESET_LINK_SENT) { 28 | Toaster::success($response); 29 | 30 | return; 31 | } 32 | $this->addError('email', __($response)); 33 | } 34 | 35 | #[Layout('layouts.auth')] 36 | #[Title('Request Password Reset')] 37 | public function render(): View 38 | { 39 | return view('livewire.auth.password.request'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/Livewire/Auth/Password/Reset.php: -------------------------------------------------------------------------------- 1 | email = request()->query('email', ''); 30 | $this->token = $token; 31 | } 32 | 33 | public function rules(): array 34 | { 35 | return [ 36 | 'token' => ['required'], 37 | 'email' => ['required', 'email'], 38 | 'password' => ['required', 'string', PasswordRule::defaults()], 39 | 'password_confirmation' => ['required', 'same:password'], 40 | ]; 41 | } 42 | 43 | public function resetPassword(): void 44 | { 45 | $this->validate(); 46 | $response = Password::broker()->reset([ 47 | 'token' => $this->token, 48 | 'email' => $this->email, 49 | 'password' => $this->password, 50 | ], function ($user, $password) { 51 | $user->password = Hash::make($password); 52 | $user->setRememberToken(Str::random(60)); 53 | $user->save(); 54 | event(new PasswordReset($user)); 55 | Auth::guard()->login($user); 56 | }); 57 | if ($response == Password::PASSWORD_RESET) { 58 | Toaster::success($response); 59 | $this->redirect(route('user.dashboard'), navigate: true); 60 | } 61 | $this->addError('email', __($response)); 62 | } 63 | 64 | #[Layout('layouts.auth')] 65 | #[Title('Reset Password')] 66 | public function render(): View 67 | { 68 | return view('livewire.auth.password.reset'); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/Livewire/Auth/PrivacyPolicy.php: -------------------------------------------------------------------------------- 1 | put('url.intended', route('privacypolicy')); 18 | 19 | $locale = session('locale', app()->getLocale()); 20 | $privacyFile = resource_path("markdown/privacy-policy-$locale.md"); 21 | if (! file_exists($privacyFile)) { 22 | $privacyFile = resource_path('markdown/privacy-policy-en.md'); 23 | } 24 | $privacy = Str::markdown(file_get_contents($privacyFile)); 25 | 26 | return view('livewire.auth.privacy-policy', compact('privacy')); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Livewire/Auth/Register.php: -------------------------------------------------------------------------------- 1 | ['required', 'string', 'max:255', 'regex:/^[a-zA-Z\s]+$/'], 33 | 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 34 | 'password' => ['required', 'string', Password::defaults()], 35 | 'password_confirmation' => ['required', 'same:password'], 36 | 'terms_of_service_and_privacy_policy' => ['accepted'], 37 | ]; 38 | } 39 | 40 | public function messages(): array 41 | { 42 | return [ 43 | 'name.regex' => __('The name may only contain letters and spaces.'), 44 | ]; 45 | } 46 | 47 | public function register(): void 48 | { 49 | $this->validate(); 50 | $user = User::create([ 51 | 'email' => $this->email, 52 | 'name' => Str::title($this->name), 53 | 'password' => Hash::make($this->password), 54 | ]); 55 | event(new Registered($user)); 56 | Auth::login($user, true); 57 | $this->redirect(route('user.dashboard'), navigate: true); 58 | Toaster::success('A verification link has been sent to your email address.'); 59 | } 60 | 61 | #[Layout('layouts.auth')] 62 | #[Title('Register')] 63 | public function render(): View 64 | { 65 | return view('livewire.auth.register'); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/Livewire/Auth/TermsOfService.php: -------------------------------------------------------------------------------- 1 | put('url.intended', route('termsofservice')); 18 | 19 | $locale = session('locale', app()->getLocale()); 20 | $termsFile = resource_path("markdown/terms-of-service-$locale.md"); 21 | if (! file_exists($termsFile)) { 22 | $termsFile = resource_path('markdown/terms-of-service-en.md'); 23 | } 24 | $terms = Str::markdown(file_get_contents($termsFile)); 25 | 26 | return view('livewire.auth.terms-of-service', compact('terms')); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Livewire/Auth/Verify.php: -------------------------------------------------------------------------------- 1 | logout(); 17 | $this->redirect(session('url.intended', route('login')), navigate: true); 18 | if (session()->has('auth.password_confirmed_at')) { 19 | session()->forget('auth.password_confirmed_at'); 20 | } 21 | session()->invalidate(); 22 | session()->regenerateToken(); 23 | } 24 | 25 | public function resend(): void 26 | { 27 | Auth::user()->sendEmailVerificationNotification(); 28 | Toaster::success('A new verification link has been sent to the email address you provided in your profile settings.'); 29 | } 30 | 31 | #[Layout('layouts.auth')] 32 | #[Title('Verify')] 33 | public function render(): View 34 | { 35 | if (Auth::user()->hasVerifiedEmail()) { 36 | $this->redirect(session('url.intended', route('user.dashboard')), navigate: true); 37 | Toaster::success('Your email address already verified.'); 38 | } 39 | 40 | return view('livewire.auth.verify'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/Livewire/Example.php: -------------------------------------------------------------------------------- 1 | put('url.intended', route('example')); 17 | 18 | return view('livewire.example'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Livewire/Home.php: -------------------------------------------------------------------------------- 1 | put('url.intended', route('home')); 17 | 18 | return view('livewire.home'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Livewire/Layouts/AdminNavigation.php: -------------------------------------------------------------------------------- 1 | forget('auth.password_confirmed_at'); 14 | $this->redirect(route('user.dashboard'), navigate: true); 15 | } 16 | 17 | public function logout(): void 18 | { 19 | Auth::guard('web')->logout(); 20 | $this->redirect(session('url.intended', route('login')), navigate: true); 21 | if (session()->has('auth.password_confirmed_at')) { 22 | session()->forget('auth.password_confirmed_at'); 23 | } 24 | session()->invalidate(); 25 | session()->regenerateToken(); 26 | } 27 | 28 | public function render(): View 29 | { 30 | return view('layouts.admin-navigation'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Livewire/Layouts/AdminSidebar.php: -------------------------------------------------------------------------------- 1 | forget('auth.password_confirmed_at'); 14 | $this->redirect(route('user.dashboard'), navigate: true); 15 | } 16 | 17 | public function logout(): void 18 | { 19 | Auth::guard('web')->logout(); 20 | $this->redirect(session('url.intended', route('login')), navigate: true); 21 | if (session()->has('auth.password_confirmed_at')) { 22 | session()->forget('auth.password_confirmed_at'); 23 | } 24 | session()->invalidate(); 25 | session()->regenerateToken(); 26 | } 27 | 28 | public function render(): View 29 | { 30 | return view('layouts.admin-sidebar'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Livewire/Layouts/AppNavigation.php: -------------------------------------------------------------------------------- 1 | forget('auth.password_confirmed_at'); 15 | $this->redirect(session('url.intended', route('user.dashboard')), navigate: true); 16 | } 17 | 18 | public function logout(): void 19 | { 20 | Auth::guard('web')->logout(); 21 | $this->redirect(session('url.intended', route('login')), navigate: true); 22 | if (session()->has('auth.password_confirmed_at')) { 23 | session()->forget('auth.password_confirmed_at'); 24 | } 25 | session()->invalidate(); 26 | session()->regenerateToken(); 27 | } 28 | 29 | #[On('profileUpdated')] 30 | public function render(): View 31 | { 32 | return view('layouts.app-navigation'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/Livewire/Layouts/MainNavigation.php: -------------------------------------------------------------------------------- 1 | forget('auth.password_confirmed_at'); 14 | $this->redirect(session('url.intended', route('user.dashboard')), navigate: true); 15 | } 16 | 17 | public function logout(): void 18 | { 19 | Auth::guard('web')->logout(); 20 | $this->redirect(session('url.intended', route('login')), navigate: true); 21 | if (session()->has('auth.password_confirmed_at')) { 22 | session()->forget('auth.password_confirmed_at'); 23 | } 24 | session()->invalidate(); 25 | session()->regenerateToken(); 26 | } 27 | 28 | public function render(): View 29 | { 30 | return view('layouts.main-navigation'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Livewire/User/Dashboard.php: -------------------------------------------------------------------------------- 1 | put('url.intended', route('user.dashboard')); 15 | 16 | return view('livewire.user.dashboard'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Livewire/User/Example.php: -------------------------------------------------------------------------------- 1 | put('url.intended', route('user.example')); 15 | 16 | return view('livewire.user.example'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Livewire/User/Settings.php: -------------------------------------------------------------------------------- 1 | current_language = app()->getLocale(); 21 | $this->language = $this->current_language; 22 | $this->supported_languages = config('translation.supported_translations'); 23 | } 24 | 25 | public function updateLanguage(): void 26 | { 27 | $this->validate([ 28 | 'language' => 'required|in:'.implode(',', array_column($this->supported_languages, 'code')), 29 | ]); 30 | 31 | if ($this->language == $this->current_language) { 32 | Toaster::info('Nothing changed.'); 33 | } else { 34 | session(['locale' => $this->language]); 35 | app()->setLocale($this->language); 36 | Toaster::success('Language updated.'); 37 | $this->redirect(route('user.settings'), navigate: true); 38 | } 39 | } 40 | 41 | #[Title('User Settings')] 42 | public function render(): View 43 | { 44 | session()->put('url.intended', route('user.settings')); 45 | 46 | return view('livewire.user.settings'); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | protected $policies = [ 16 | // 17 | ]; 18 | 19 | /** 20 | * Register any authentication / authorization services. 21 | */ 22 | public function boot(): void 23 | { 24 | // 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | > 16 | */ 17 | protected $listen = [ 18 | Registered::class => [ 19 | SendEmailVerificationNotification::class, 20 | ], 21 | ]; 22 | 23 | /** 24 | * Register any events for your application. 25 | */ 26 | public function boot(): void 27 | { 28 | // 29 | } 30 | 31 | /** 32 | * Determine if events and listeners should be automatically discovered. 33 | */ 34 | public function shouldDiscoverEvents(): bool 35 | { 36 | return false; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | by($request->user()?->id ?: $request->ip()); 29 | }); 30 | 31 | $this->routes(function () { 32 | Route::middleware('api') 33 | ->prefix('api') 34 | ->group(base_path('routes/api.php')); 35 | 36 | Route::middleware('web') 37 | ->group(base_path('routes/web.php')); 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | make(Illuminate\Contracts\Console\Kernel::class); 34 | 35 | $status = $kernel->handle( 36 | $input = new Symfony\Component\Console\Input\ArgvInput, 37 | new Symfony\Component\Console\Output\ConsoleOutput 38 | ); 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Shutdown The Application 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Once Artisan has finished running, we will fire off the shutdown events 46 | | so that any final work may be done by the application before we shut 47 | | down the process. This is the last thing to happen to the request. 48 | | 49 | */ 50 | 51 | $kernel->terminate($input, $status); 52 | 53 | exit($status); 54 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | singleton( 30 | Illuminate\Contracts\Http\Kernel::class, 31 | App\Http\Kernel::class 32 | ); 33 | 34 | $app->singleton( 35 | Illuminate\Contracts\Console\Kernel::class, 36 | App\Console\Kernel::class 37 | ); 38 | 39 | $app->singleton( 40 | Illuminate\Contracts\Debug\ExceptionHandler::class, 41 | App\Exceptions\Handler::class 42 | ); 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Return The Application 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This script returns the application instance. The instance is given to 50 | | the calling script so we can separate the building of the instances 51 | | from the actual running of the application and sending responses. 52 | | 53 | */ 54 | 55 | return $app; 56 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "arifbudimanar/lali", 3 | "type": "project", 4 | "description": "The skeleton application for the Laravel starter project with TALL Stack.", 5 | "keywords": [ 6 | "laravel", 7 | "framework", 8 | "livewire" 9 | ], 10 | "license": "MIT", 11 | "require": { 12 | "php": "^8.2", 13 | "guzzlehttp/guzzle": "^7.8.1", 14 | "laravel/framework": "^10.38.0", 15 | "laravel/sanctum": "^3.3.2", 16 | "laravel/tinker": "^2.8.2", 17 | "livewire/livewire": "^3.3.2", 18 | "masmerise/livewire-toaster": "^2.0.3" 19 | }, 20 | "require-dev": { 21 | "barryvdh/laravel-debugbar": "^3.9.2", 22 | "barryvdh/laravel-ide-helper": "^2.13", 23 | "fakerphp/faker": "^1.23.0", 24 | "laravel-lang/common": "^4.1.1", 25 | "laravel/pint": "^1.13.7", 26 | "laravel/sail": "^1.26.3", 27 | "mockery/mockery": "^1.6.7", 28 | "nunomaduro/collision": "^7.10.0", 29 | "pestphp/pest": "^2.28.1", 30 | "phpunit/phpunit": "^10.5.3", 31 | "spatie/laravel-ignition": "^2.3.2" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "App\\": "app/", 36 | "Database\\Factories\\": "database/factories/", 37 | "Database\\Seeders\\": "database/seeders/" 38 | } 39 | }, 40 | "autoload-dev": { 41 | "psr-4": { 42 | "Tests\\": "tests/" 43 | } 44 | }, 45 | "scripts": { 46 | "post-autoload-dump": [ 47 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", 48 | "@php artisan package:discover --ansi" 49 | ], 50 | "post-update-cmd": [ 51 | "@php artisan vendor:publish --tag=laravel-assets --ansi --force", 52 | "@php artisan ide-helper:generate", 53 | "@php artisan ide-helper:models" 54 | ], 55 | "post-root-package-install": [ 56 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" 57 | ], 58 | "post-create-project-cmd": [ 59 | "@php artisan key:generate --ansi" 60 | ] 61 | }, 62 | "extra": { 63 | "laravel": { 64 | "dont-discover": [] 65 | } 66 | }, 67 | "config": { 68 | "optimize-autoloader": true, 69 | "preferred-install": "dist", 70 | "sort-packages": true, 71 | "allow-plugins": { 72 | "pestphp/pest-plugin": true, 73 | "php-http/discovery": true 74 | } 75 | }, 76 | "minimum-stability": "stable", 77 | "prefer-stable": true 78 | } 79 | -------------------------------------------------------------------------------- /config/broadcasting.php: -------------------------------------------------------------------------------- 1 | env('BROADCAST_DRIVER', '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 | 'pusher' => [ 34 | 'driver' => 'pusher', 35 | 'key' => env('PUSHER_APP_KEY'), 36 | 'secret' => env('PUSHER_APP_SECRET'), 37 | 'app_id' => env('PUSHER_APP_ID'), 38 | 'options' => [ 39 | 'cluster' => env('PUSHER_APP_CLUSTER'), 40 | 'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com', 41 | 'port' => env('PUSHER_PORT', 443), 42 | 'scheme' => env('PUSHER_SCHEME', 'https'), 43 | 'encrypted' => true, 44 | 'useTLS' => env('PUSHER_SCHEME', 'https') === 'https', 45 | ], 46 | 'client_options' => [ 47 | // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html 48 | ], 49 | ], 50 | 51 | 'ably' => [ 52 | 'driver' => 'ably', 53 | 'key' => env('ABLY_KEY'), 54 | ], 55 | 56 | 'redis' => [ 57 | 'driver' => 'redis', 58 | 'connection' => 'default', 59 | ], 60 | 61 | 'log' => [ 62 | 'driver' => 'log', 63 | ], 64 | 65 | 'null' => [ 66 | 'driver' => 'null', 67 | ], 68 | 69 | ], 70 | 71 | ]; 72 | -------------------------------------------------------------------------------- /config/cors.php: -------------------------------------------------------------------------------- 1 | ['api/*', 'sanctum/csrf-cookie'], 19 | 20 | 'allowed_methods' => ['*'], 21 | 22 | 'allowed_origins' => ['*'], 23 | 24 | 'allowed_origins_patterns' => [], 25 | 26 | 'allowed_headers' => ['*'], 27 | 28 | 'exposed_headers' => [], 29 | 30 | 'max_age' => 0, 31 | 32 | 'supports_credentials' => false, 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /config/filesystems.php: -------------------------------------------------------------------------------- 1 | env('FILESYSTEM_DISK', 'local'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Filesystem Disks 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may configure as many filesystem "disks" as you wish, and you 24 | | may even configure multiple disks of the same driver. Defaults have 25 | | been set up for each driver as an example of the required values. 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/hashing.php: -------------------------------------------------------------------------------- 1 | 'bcrypt', 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Bcrypt Options 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may specify the configuration options that should be used when 26 | | passwords are hashed using the Bcrypt algorithm. This will allow you 27 | | to control the amount of time it takes to hash the given password. 28 | | 29 | */ 30 | 31 | 'bcrypt' => [ 32 | 'rounds' => env('BCRYPT_ROUNDS', 12), 33 | 'verify' => true, 34 | ], 35 | 36 | /* 37 | |-------------------------------------------------------------------------- 38 | | Argon Options 39 | |-------------------------------------------------------------------------- 40 | | 41 | | Here you may specify the configuration options that should be used when 42 | | passwords are hashed using the Argon algorithm. These will allow you 43 | | to control the amount of time it takes to hash the given password. 44 | | 45 | */ 46 | 47 | 'argon' => [ 48 | 'memory' => 65536, 49 | 'threads' => 1, 50 | 'time' => 4, 51 | 'verify' => true, 52 | ], 53 | 54 | ]; 55 | -------------------------------------------------------------------------------- /config/sanctum.php: -------------------------------------------------------------------------------- 1 | explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( 19 | '%s%s', 20 | 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', 21 | Sanctum::currentApplicationUrlWithPort() 22 | ))), 23 | 24 | /* 25 | |-------------------------------------------------------------------------- 26 | | Sanctum Guards 27 | |-------------------------------------------------------------------------- 28 | | 29 | | This array contains the authentication guards that will be checked when 30 | | Sanctum is trying to authenticate a request. If none of these guards 31 | | are able to authenticate the request, Sanctum will use the bearer 32 | | token that's present on an incoming request for authentication. 33 | | 34 | */ 35 | 36 | 'guard' => ['web'], 37 | 38 | /* 39 | |-------------------------------------------------------------------------- 40 | | Expiration Minutes 41 | |-------------------------------------------------------------------------- 42 | | 43 | | This value controls the number of minutes until an issued token will be 44 | | considered expired. If this value is null, personal access tokens do 45 | | not expire. This won't tweak the lifetime of first-party sessions. 46 | | 47 | */ 48 | 49 | 'expiration' => null, 50 | 51 | /* 52 | |-------------------------------------------------------------------------- 53 | | Sanctum Middleware 54 | |-------------------------------------------------------------------------- 55 | | 56 | | When authenticating your first-party SPA with Sanctum you may need to 57 | | customize some of the middleware Sanctum uses while processing the 58 | | request. You may change the middleware listed below as required. 59 | | 60 | */ 61 | 62 | 'middleware' => [ 63 | 'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class, 64 | 'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class, 65 | ], 66 | 67 | ]; 68 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => env('MAILGUN_DOMAIN'), 19 | 'secret' => env('MAILGUN_SECRET'), 20 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), 21 | 'scheme' => 'https', 22 | ], 23 | 24 | 'postmark' => [ 25 | 'token' => env('POSTMARK_TOKEN'), 26 | ], 27 | 28 | 'ses' => [ 29 | 'key' => env('AWS_ACCESS_KEY_ID'), 30 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 31 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 32 | ], 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /config/toaster.php: -------------------------------------------------------------------------------- 1 | true, 13 | 14 | /** 15 | * The vertical alignment of the toast container. 16 | * 17 | * Supported: "bottom", "middle" or "top" 18 | */ 19 | 'alignment' => 'bottom', 20 | 21 | /** 22 | * Allow users to close toast messages prematurely. 23 | * 24 | * Supported: true | false 25 | */ 26 | 'closeable' => true, 27 | 28 | /** 29 | * The on-screen duration of each toast. 30 | * 31 | * Minimum: 3000 (in milliseconds) 32 | */ 33 | 'duration' => 5000, 34 | 35 | /** 36 | * The horizontal position of each toast. 37 | * 38 | * Supported: "center", "left" or "right" 39 | */ 40 | 'position' => 'right', 41 | 42 | /** 43 | * Whether messages passed as translation keys should be translated automatically. 44 | * 45 | * Supported: true | false 46 | */ 47 | 'translate' => true, 48 | ]; 49 | -------------------------------------------------------------------------------- /config/translation.php: -------------------------------------------------------------------------------- 1 | [ 17 | [ 18 | 'code' => 'en', 19 | 'name' => [ 20 | 'en' => 'English', 21 | 'id' => 'Inggris', 22 | ], 23 | ], 24 | [ 25 | 'code' => 'id', 26 | 'name' => [ 27 | 'en' => 'Indonesian', 28 | 'id' => 'Indonesia', 29 | ], 30 | ], 31 | ], 32 | ]; 33 | -------------------------------------------------------------------------------- /config/view.php: -------------------------------------------------------------------------------- 1 | [ 17 | resource_path('views'), 18 | ], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Compiled View Path 23 | |-------------------------------------------------------------------------- 24 | | 25 | | This option determines where all the compiled Blade templates will be 26 | | stored for your application. Typically, this is within the storage 27 | | directory. However, as usual, you are free to change this value. 28 | | 29 | */ 30 | 31 | 'compiled' => env( 32 | 'VIEW_COMPILED_PATH', 33 | realpath(storage_path('framework/views')) 34 | ), 35 | 36 | ]; 37 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class UserFactory extends Factory 12 | { 13 | /** 14 | * Define the model's default state. 15 | * 16 | * @return array 17 | */ 18 | public function definition(): array 19 | { 20 | return [ 21 | 'name' => fake()->name(), 22 | 'email' => fake()->unique()->safeEmail(), 23 | 'email_verified_at' => now(), 24 | 'password' => '$2y$12$UjSaTMmVv8JagyPC8jotKu18JoFN6TssUuFpBO14zTPBeChQI3bOa', 25 | 'remember_token' => Str::random(10), 26 | ]; 27 | } 28 | 29 | /** 30 | * Indicate that the model's email address should be unverified. 31 | */ 32 | public function unverified(): static 33 | { 34 | return $this->state(fn (array $attributes) => [ 35 | 'email_verified_at' => null, 36 | ]); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('name'); 17 | $table->string('email')->unique(); 18 | $table->timestamp('email_verified_at')->nullable(); 19 | $table->string('password'); 20 | $table->rememberToken(); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | */ 28 | public function down(): void 29 | { 30 | Schema::dropIfExists('users'); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php: -------------------------------------------------------------------------------- 1 | string('email')->primary(); 16 | $table->string('token'); 17 | $table->timestamp('created_at')->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::dropIfExists('password_reset_tokens'); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2019_08_19_000000_create_failed_jobs_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('uuid')->unique(); 17 | $table->text('connection'); 18 | $table->text('queue'); 19 | $table->longText('payload'); 20 | $table->longText('exception'); 21 | $table->timestamp('failed_at')->useCurrent(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | */ 28 | public function down(): void 29 | { 30 | Schema::dropIfExists('failed_jobs'); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->morphs('tokenable'); 17 | $table->string('name'); 18 | $table->string('token', 64)->unique(); 19 | $table->text('abilities')->nullable(); 20 | $table->timestamp('last_used_at')->nullable(); 21 | $table->timestamp('expires_at')->nullable(); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | */ 29 | public function down(): void 30 | { 31 | Schema::dropIfExists('personal_access_tokens'); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | create([ 17 | 'name' => 'User Example', 18 | 'email' => 'user.example@mail.com', 19 | 'created_at' => fake()->dateTimeBetween('-3 month', '-1 week'), 20 | 'updated_at' => fake()->dateTimeBetween('-1 week', 'now'), 21 | ]); 22 | 23 | User::factory()->create([ 24 | 'name' => 'Admin Example', 25 | 'email' => 'admin.example@mail.com', 26 | 'created_at' => fake()->dateTimeBetween('-3 month', '-1 week'), 27 | 'updated_at' => fake()->dateTimeBetween('-1 week', 'now'), 28 | ]); 29 | 30 | User::factory()->create([ 31 | 'name' => 'Arif Budiman Arrosyid', 32 | 'email' => 'arifbudimanarrosyid@gmail.com', 33 | 'created_at' => fake()->dateTimeBetween('-3 month', '-1 week'), 34 | 'updated_at' => fake()->dateTimeBetween('-1 week', 'now'), 35 | ]); 36 | 37 | $this->call(UserSeeder::class); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /database/seeders/UserSeeder.php: -------------------------------------------------------------------------------- 1 | create([ 17 | 'created_at' => fake()->dateTimeBetween('-3 month', '-1 week'), 18 | 'updated_at' => fake()->dateTimeBetween('-1 week', 'now'), 19 | ]); 20 | } 21 | 22 | for ($i = 0; $i < 200; $i++) { 23 | User::factory()->unverified()->create([ 24 | 'created_at' => fake()->dateTimeBetween('-3 month', '-1 week'), 25 | 'updated_at' => fake()->dateTimeBetween('-1 week', 'now'), 26 | ]); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lang/en/auth.php: -------------------------------------------------------------------------------- 1 | 'These credentials do not match our records.', 7 | 'password' => 'The password is incorrect.', 8 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 9 | ]; 10 | -------------------------------------------------------------------------------- /lang/en/http-statuses.php: -------------------------------------------------------------------------------- 1 | 'Unknown Error', 7 | '100' => 'Continue', 8 | '101' => 'Switching Protocols', 9 | '102' => 'Processing', 10 | '200' => 'OK', 11 | '201' => 'Created', 12 | '202' => 'Accepted', 13 | '203' => 'Non-Authoritative Information', 14 | '204' => 'No Content', 15 | '205' => 'Reset Content', 16 | '206' => 'Partial Content', 17 | '207' => 'Multi-Status', 18 | '208' => 'Already Reported', 19 | '226' => 'IM Used', 20 | '300' => 'Multiple Choices', 21 | '301' => 'Moved Permanently', 22 | '302' => 'Found', 23 | '303' => 'See Other', 24 | '304' => 'Not Modified', 25 | '305' => 'Use Proxy', 26 | '307' => 'Temporary Redirect', 27 | '308' => 'Permanent Redirect', 28 | '400' => 'Bad Request', 29 | '401' => 'Unauthorized', 30 | '402' => 'Payment Required', 31 | '403' => 'Forbidden', 32 | '404' => 'Not Found', 33 | '405' => 'Method Not Allowed', 34 | '406' => 'Not Acceptable', 35 | '407' => 'Proxy Authentication Required', 36 | '408' => 'Request Timeout', 37 | '409' => 'Conflict', 38 | '410' => 'Gone', 39 | '411' => 'Length Required', 40 | '412' => 'Precondition Failed', 41 | '413' => 'Payload Too Large', 42 | '414' => 'URI Too Long', 43 | '415' => 'Unsupported Media Type', 44 | '416' => 'Range Not Satisfiable', 45 | '417' => 'Expectation Failed', 46 | '418' => 'I\'m a teapot', 47 | '419' => 'Session Has Expired', 48 | '421' => 'Misdirected Request', 49 | '422' => 'Unprocessable Entity', 50 | '423' => 'Locked', 51 | '424' => 'Failed Dependency', 52 | '425' => 'Too Early', 53 | '426' => 'Upgrade Required', 54 | '428' => 'Precondition Required', 55 | '429' => 'Too Many Requests', 56 | '431' => 'Request Header Fields Too Large', 57 | '444' => 'Connection Closed Without Response', 58 | '449' => 'Retry With', 59 | '451' => 'Unavailable For Legal Reasons', 60 | '499' => 'Client Closed Request', 61 | '500' => 'Internal Server Error', 62 | '501' => 'Not Implemented', 63 | '502' => 'Bad Gateway', 64 | '503' => 'Maintenance Mode', 65 | '504' => 'Gateway Timeout', 66 | '505' => 'HTTP Version Not Supported', 67 | '506' => 'Variant Also Negotiates', 68 | '507' => 'Insufficient Storage', 69 | '508' => 'Loop Detected', 70 | '509' => 'Bandwidth Limit Exceeded', 71 | '510' => 'Not Extended', 72 | '511' => 'Network Authentication Required', 73 | '520' => 'Unknown Error', 74 | '521' => 'Web Server is Down', 75 | '522' => 'Connection Timed Out', 76 | '523' => 'Origin Is Unreachable', 77 | '524' => 'A Timeout Occurred', 78 | '525' => 'SSL Handshake Failed', 79 | '526' => 'Invalid SSL Certificate', 80 | '527' => 'Railgun Error', 81 | '598' => 'Network Read Timeout Error', 82 | '599' => 'Network Connect Timeout Error', 83 | 'unknownError' => 'Unknown Error', 84 | ]; 85 | -------------------------------------------------------------------------------- /lang/en/pagination.php: -------------------------------------------------------------------------------- 1 | 'Next »', 7 | // 'previous' => '« Previous', 8 | 'next' => 'Next', 9 | 'previous' => 'Previous', 10 | ]; 11 | -------------------------------------------------------------------------------- /lang/en/passwords.php: -------------------------------------------------------------------------------- 1 | 'Your password has been reset.', 7 | 'sent' => 'We have emailed your password reset link.', 8 | 'throttled' => 'Please wait before retrying.', 9 | 'token' => 'This password reset token is invalid.', 10 | 'user' => 'We can\'t find a user with that email address.', 11 | ]; 12 | -------------------------------------------------------------------------------- /lang/id/auth.php: -------------------------------------------------------------------------------- 1 | 'Identitas tersebut tidak cocok dengan data kami.', 7 | 'password' => 'Kata sandi salah.', 8 | 'throttle' => 'Terlalu banyak upaya masuk. Silahkan coba lagi dalam :seconds detik.', 9 | ]; 10 | -------------------------------------------------------------------------------- /lang/id/http-statuses.php: -------------------------------------------------------------------------------- 1 | 'Error tidak diketahui', 7 | '100' => 'Lanjut', 8 | '101' => 'Beralih protokol', 9 | '102' => 'Memproses', 10 | '200' => 'OKE', 11 | '201' => 'Dibuat', 12 | '202' => 'Diterima', 13 | '203' => 'Informasi Non-Otoritatif', 14 | '204' => 'Tidak ada konten', 15 | '205' => 'Setel ulang konten', 16 | '206' => 'Konten parsial', 17 | '207' => 'Status ganda', 18 | '208' => 'Sudah dilaporkan', 19 | '226' => 'IM Digunakan', 20 | '300' => 'Pilihan ganda', 21 | '301' => 'Pengalihan permanen', 22 | '302' => 'Ditemukan', 23 | '303' => 'Lihat lainnya', 24 | '304' => 'Tidak dimodifikasi', 25 | '305' => 'Memakai Proxy', 26 | '307' => 'Pengalihan temporer', 27 | '308' => 'Pengalihan permanen', 28 | '400' => 'Permintaan yang buruk', 29 | '401' => 'Tidak sah', 30 | '402' => 'Pembayaran diperlukan', 31 | '403' => 'Terlarang', 32 | '404' => 'Tidak ditemukan', 33 | '405' => 'Metode tidak diizinkan', 34 | '406' => 'Tidak dapat diterima', 35 | '407' => 'Otentikasi proksi diperlukan', 36 | '408' => 'Permintaan waktu habis', 37 | '409' => 'Konflik', 38 | '410' => 'Hilang', 39 | '411' => 'Panjang diperlukan', 40 | '412' => 'Prasyarat Gagal', 41 | '413' => 'Payload terlalu besar', 42 | '414' => 'URI terlalu panjang', 43 | '415' => 'Tipe media tidak mendukung', 44 | '416' => 'Rentang Tidak Memuaskan', 45 | '417' => 'Ekspetasi gagal', 46 | '418' => 'Saya teko', 47 | '419' => 'Sesi telah berakhir', 48 | '421' => 'Permintaan salah arah', 49 | '422' => 'Entitas yang tidak dapat diproses', 50 | '423' => 'Terkunci', 51 | '424' => 'Dependensi gagal', 52 | '425' => 'Terlalu dini', 53 | '426' => 'Diperlukan Peningkatan', 54 | '428' => 'Prasyarat diperlukan', 55 | '429' => 'Terlalu banyak permintaan', 56 | '431' => 'Bidang permintaan Header terlalu besar', 57 | '444' => 'Koneksi Ditutup Tanpa Respon', 58 | '449' => 'Coba lagi dengan', 59 | '451' => 'Tidak Tersedia Karena Alasan Hukum', 60 | '499' => 'Permintaan ditutup klien', 61 | '500' => 'Kesalahan dari dalam server', 62 | '501' => 'Tidak dilaksanakan', 63 | '502' => 'Gateway buruk', 64 | '503' => 'Mode pemeliharaan', 65 | '504' => 'Gateway waktu habis', 66 | '505' => 'Versi HTTP tidak mendukung', 67 | '506' => 'Varian Juga Nego', 68 | '507' => 'Penyimpanan tidak cukup', 69 | '508' => 'Terdeteksi perulangan', 70 | '509' => 'Batas Bandwidth terlampaui', 71 | '510' => 'Tidak diperpanjang', 72 | '511' => 'Otentikasi jaringan diperlukan', 73 | '520' => 'Error tidak diketahui', 74 | '521' => 'Web server sedang turun', 75 | '522' => 'Waktu koneksi berakhir', 76 | '523' => 'Asal tidak terjangkau', 77 | '524' => 'Terjadi Timeout', 78 | '525' => 'Jabat tangan SSL gagal', 79 | '526' => 'Sertifikat SSL tidak sah', 80 | '527' => 'Kesalahan Railgun', 81 | '598' => 'Network Read Timeout gagal', 82 | '599' => 'Network Connect Timeout gagal', 83 | 'unknownError' => 'Error tidak diketahui', 84 | ]; 85 | -------------------------------------------------------------------------------- /lang/id/pagination.php: -------------------------------------------------------------------------------- 1 | 'Berikutnya »', 7 | // 'previous' => '« Sebelumnya', 8 | 'next' => 'Berikutnya', 9 | 'previous' => 'Sebelumnya', 10 | ]; 11 | -------------------------------------------------------------------------------- /lang/id/passwords.php: -------------------------------------------------------------------------------- 1 | 'Kata sandi Anda sudah direset!', 7 | 'sent' => 'Kami sudah mengirim surel yang berisi tautan untuk mereset kata sandi Anda!', 8 | 'throttled' => 'Harap tunggu sebelum mencoba lagi.', 9 | 'token' => 'Token pengaturan ulang kata sandi tidak sah.', 10 | 'user' => 'Kami tidak dapat menemukan pengguna dengan alamat surel tersebut.', 11 | ]; 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build" 7 | }, 8 | "devDependencies": { 9 | "@tailwindcss/forms": "^0.5.7", 10 | "@tailwindcss/typography": "^0.5.10", 11 | "autoprefixer": "^10.4.16", 12 | "axios": "^1.6.2", 13 | "laravel-vite-plugin": "^1.0.0", 14 | "postcss": "^8.4.32", 15 | "tailwindcss": "^3.4.0", 16 | "vite": "^5.0.10" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /pint.json: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "laravel" 3 | } 4 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # 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/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifbudimanar/lali/8107b797148b43a62c41729e6e8662c971e1e769/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifbudimanar/lali/8107b797148b43a62c41729e6e8662c971e1e769/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifbudimanar/lali/8107b797148b43a62c41729e6e8662c971e1e769/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifbudimanar/lali/8107b797148b43a62c41729e6e8662c971e1e769/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifbudimanar/lali/8107b797148b43a62c41729e6e8662c971e1e769/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifbudimanar/lali/8107b797148b43a62c41729e6e8662c971e1e769/public/favicon.ico -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class); 50 | 51 | $response = $kernel->handle( 52 | $request = Request::capture() 53 | )->send(); 54 | 55 | $kernel->terminate($request, $response); 56 | -------------------------------------------------------------------------------- /public/lali.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifbudimanar/lali/8107b797148b43a62c41729e6e8662c971e1e769/public/lali.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Laravel Livewire Starter Project", 3 | "short_name": "Lali", 4 | "description": "The skeleton application for the Laravel starter project with TALL Stack.", 5 | "start_url": "/", 6 | "icons": [ 7 | { 8 | "src": "/android-chrome-192x192.png", 9 | "sizes": "192x192", 10 | "type": "image/png" 11 | }, 12 | { 13 | "src": "/android-chrome-512x512.png", 14 | "sizes": "512x512", 15 | "type": "image/png" 16 | } 17 | ], 18 | "theme_color": "#27272a", 19 | "background_color": "#27272a", 20 | "display": "standalone" 21 | } 22 | -------------------------------------------------------------------------------- /resources/css/app.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @media (prefers-color-scheme: dark) { 6 | ::-webkit-scrollbar { 7 | width: 10px; 8 | height: 10px; 9 | background-color: #27272a; 10 | } 11 | 12 | ::-webkit-scrollbar-thumb { 13 | background-color: #52525b; 14 | border-radius: 10px; 15 | } 16 | 17 | ::-webkit-scrollbar-thumb:hover { 18 | background-color: #312e81; 19 | } 20 | } 21 | 22 | @media (prefers-color-scheme: light) { 23 | ::-webkit-scrollbar { 24 | width: 10px; 25 | height: 10px; 26 | background-color: #ffffff; 27 | } 28 | 29 | ::-webkit-scrollbar-thumb { 30 | background-color: #d4d4d8; 31 | border-radius: 10px; 32 | } 33 | 34 | ::-webkit-scrollbar-thumb:hover { 35 | background-color: #c7d2fe; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /resources/js/app.js: -------------------------------------------------------------------------------- 1 | import "./bootstrap"; 2 | import "../../vendor/masmerise/livewire-toaster/resources/js"; 3 | -------------------------------------------------------------------------------- /resources/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * We'll load the axios HTTP library which allows us to easily issue requests 3 | * to our Laravel back-end. This library automatically handles sending the 4 | * CSRF token as a header based on the value of the "XSRF" token cookie. 5 | */ 6 | 7 | import axios from 'axios'; 8 | window.axios = axios; 9 | 10 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 11 | 12 | /** 13 | * Echo exposes an expressive API for subscribing to channels and listening 14 | * for events that are broadcast by Laravel. Echo and event broadcasting 15 | * allows your team to easily build robust real-time web applications. 16 | */ 17 | 18 | // import Echo from 'laravel-echo'; 19 | 20 | // import Pusher from 'pusher-js'; 21 | // window.Pusher = Pusher; 22 | 23 | // window.Echo = new Echo({ 24 | // broadcaster: 'pusher', 25 | // key: import.meta.env.VITE_PUSHER_APP_KEY, 26 | // cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER ?? 'mt1', 27 | // wsHost: import.meta.env.VITE_PUSHER_HOST ? import.meta.env.VITE_PUSHER_HOST : `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`, 28 | // wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80, 29 | // wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443, 30 | // forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https', 31 | // enabledTransports: ['ws', 'wss'], 32 | // }); 33 | -------------------------------------------------------------------------------- /resources/markdown/privacy-policy-en.md: -------------------------------------------------------------------------------- 1 | Please modify the following file to create a Markdown formatted document outlining the Privacy Policy for your application, taking into account the specified locale: 2 | 3 | File Path: 4 | 5 | ``` 6 | resources/markdown/privacy-policy-en.md 7 | ``` 8 | 9 | Please ensure that the document reflects the Privacy Policy in accordance with the locale mentioned in the file path. 10 | 11 | For guidance on using Markdown, you can refer to the [Markdown guide](https://www.markdownguide.org/cheat-sheet/) for further assistance. 12 | -------------------------------------------------------------------------------- /resources/markdown/privacy-policy-id.md: -------------------------------------------------------------------------------- 1 | Silahkan ubah file berikut untuk membuat dokumen berformat Markdown yang menguraikan Kebijakan Privasi untuk aplikasi Anda, dengan memperhatikan bahasa yang ditentukan: 2 | 3 | Lokasi File: 4 | 5 | ``` 6 | resources/markdown/privacy-policy-id.md 7 | ``` 8 | 9 | Silahkan pastikan bahwa dokumen tersebut mencerminkan Kebijakan Privasi sesuai dengan bahasa yang disebutkan di lokasi file. 10 | 11 | Untuk panduan menggunakan Markdown, Anda dapat merujuk ke [panduan Markdown](https://www.markdownguide.org/cheat-sheet/) untuk bantuan lebih lanjut. 12 | -------------------------------------------------------------------------------- /resources/markdown/terms-of-service-en.md: -------------------------------------------------------------------------------- 1 | Please modify the following file to create a Markdown formatted document outlining the Terms of Service for your application, taking into account the specified locale: 2 | 3 | File Path: 4 | 5 | ``` 6 | resources/markdown/terms-of-service-en.md 7 | ``` 8 | 9 | Please ensure that the document reflects the Terms of Service in accordance with the locale mentioned in the file path. 10 | 11 | For guidance on using Markdown, you can refer to the [Markdown guide](https://www.markdownguide.org/cheat-sheet/) for further assistance. 12 | -------------------------------------------------------------------------------- /resources/markdown/terms-of-service-id.md: -------------------------------------------------------------------------------- 1 | Silahkan ubah file berikut untuk membuat dokumen berformat Markdown yang menguraikan Syarat Layanan untuk aplikasi Anda, dengan memperhatikan bahasa yang ditentukan: 2 | 3 | Lokasi File: 4 | 5 | ``` 6 | resources/markdown/terms-of-service-id.md 7 | ``` 8 | 9 | Silahkan pastikan bahwa dokumen tersebut mencerminkan Syarat Layanan sesuai dengan bahasa yang disebutkan di lokasi file. 10 | 11 | Untuk panduan menggunakan Markdown, Anda dapat merujuk ke [panduan Markdown](https://www.markdownguide.org/cheat-sheet/) untuk bantuan lebih lanjut. 12 | -------------------------------------------------------------------------------- /resources/views/components/action-message.blade.php: -------------------------------------------------------------------------------- 1 | @props(['on']) 2 | 3 |
merge(['class' => 'text-sm text-zinc-600 dark:text-zinc-400']) }}> 10 | {{ $slot->isEmpty() ? 'Saved.' : $slot }} 11 |
12 | -------------------------------------------------------------------------------- /resources/views/components/app-card.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'maxWidth' => '7xl', 3 | 'title', 4 | 'description', 5 | ]) 6 | 7 | @php 8 | $maxWidthClasses = match ($maxWidth) { 9 | '7xl' => 'max-w-7xl', 10 | 'full' => 'w-full', 11 | default => $maxWidth, 12 | }; 13 | @endphp 14 | 15 |
16 |
17 |
18 |
19 | @isset($title) 20 |

21 | {{ $title }} 22 |

23 | @endisset 24 | 25 | @isset($description) 26 |
27 |

28 | {{ $description }} 29 |

30 |
31 | @endisset 32 |
33 | 34 | @isset($actions) 35 |
36 | {{ $actions }} 37 |
38 | @endisset 39 |
40 | 41 | @isset($content) 42 |
44 | {{ $content }} 45 |
46 | @endisset 47 |
48 |
49 | -------------------------------------------------------------------------------- /resources/views/components/auth-card.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'maxWidth' => 'md', 3 | 'title', 4 | ]) 5 | 6 | @php 7 | $maxWidthClasses = match ($maxWidth) { 8 | 'md' => 'sm:max-w-md', 9 | 'xl' => 'sm:max-w-xl lg:max-w-2xl', 10 | default => $maxWidth, 11 | }; 12 | @endphp 13 | 14 |
merge([ 16 | 'class' => 'flex flex-col items-center min-h-screen py-6 sm:justify-center', 17 | ]) }}> 18 | 19 | 20 | @isset($title) 21 |
22 |

{{ $title }}

23 |
24 | @endisset 25 | 26 |
28 | {{ $slot }} 29 |
30 |
31 | -------------------------------------------------------------------------------- /resources/views/components/badge.blade.php: -------------------------------------------------------------------------------- 1 | @props(['type' => 'info']) 2 | 3 | @php 4 | $typeClasses = match ($type) { 5 | 'info' => 'text-zinc-600 bg-zinc-200 dark:text-zinc-200 dark:bg-zinc-600', 6 | 'danger' => 'text-red-700 bg-red-200 dark:text-red-200 dark:bg-red-700', 7 | 'warning' => 'text-orange-700 bg-orange-200 dark:text-orange-200 dark:bg-orange-700', 8 | 'success' => 'text-indigo-700 bg-indigo-200 dark:text-indigo-200 dark:bg-indigo-700', 9 | default => $type, 10 | }; 11 | @endphp 12 | 13 | 14 | {{ $slot }} 15 | 16 | -------------------------------------------------------------------------------- /resources/views/components/banner.blade.php: -------------------------------------------------------------------------------- 1 | @props(['type' => 'info']) 2 | 3 | @php 4 | $typeClasses = match ($type) { 5 | 'info' => 'bg-zinc-100 dark:bg-zinc-900 text-zinc-700 dark:text-zinc-300', 6 | 'danger' => 'bg-red-100 dark:bg-red-900 text-red-700 dark:text-red-300', 7 | 'warning' => 'bg-orange-100 dark:bg-orange-900 text-orange-700 dark:text-orange-300', 8 | 'success' => 'bg-indigo-100 dark:bg-indigo-900 text-indigo-700 dark:text-indigo-300', 9 | default => $type, 10 | }; 11 | @endphp 12 | 13 |
14 |
15 | {{ $slot }} 16 |
17 |
18 | -------------------------------------------------------------------------------- /resources/views/components/button-link.blade.php: -------------------------------------------------------------------------------- 1 | merge([ 3 | 'class' => 'inline-block rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white hover:bg-indigo-500', 4 | ]) }}> 5 | {{ $slot }} 6 | 7 | -------------------------------------------------------------------------------- /resources/views/components/button.blade.php: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /resources/views/components/checkbox.blade.php: -------------------------------------------------------------------------------- 1 | merge([ 2 | 'class' => 3 | 'rounded dark:bg-zinc-900 border-zinc-300 dark:border-zinc-700 text-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-zinc-800', 4 | ]) !!}> 5 | -------------------------------------------------------------------------------- /resources/views/components/danger-button.blade.php: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /resources/views/components/dialog-modal.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'id' => null, 3 | 'maxWidth' => null, 4 | 'title', 5 | ]) 6 | 7 | 8 |
9 |
10 | {{ $title }} 11 |
12 | 13 |
14 | {{ $content }} 15 |
16 | 17 |
18 | {{ $footer }} 19 |
20 |
21 | 22 |
23 | -------------------------------------------------------------------------------- /resources/views/components/divider.blade.php: -------------------------------------------------------------------------------- 1 |
merge(['class' => 'border-t border-zinc-200 dark:border-zinc-600']) }}>
2 | -------------------------------------------------------------------------------- /resources/views/components/dropdown-button.blade.php: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /resources/views/components/dropdown-link.blade.php: -------------------------------------------------------------------------------- 1 | merge([ 3 | 'class' => 4 | 'w-full flex items-center px-4 py-2 text-left text-sm leading-5 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 focus:outline-none focus:bg-zinc-100 dark:focus:bg-zinc-800 transition duration-150 ease-in-out', 5 | ]) }}> 6 | 7 | @isset($icon) 8 |
9 | {{ $icon }} 10 |
11 | @endisset 12 | 13 | {{ $slot }} 14 |
15 | -------------------------------------------------------------------------------- /resources/views/components/dropdown.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'align' => 'right', 3 | 'width' => '48', 4 | ]) 5 | 6 | @php 7 | $alignmentClasses = match ($align) { 8 | 'left' => 'origin-top-left left-0', 9 | 'right' => 'origin-top-right right-0', 10 | 'center' => 'origin-top', 11 | 'top' => 'origin-top', 12 | 'none', 'false' => '', 13 | default => $align, 14 | }; 15 | 16 | $widthClasses = match ($width) { 17 | '48' => 'w-48', 18 | 'auto' => 'min-w-max', 19 | default => $width, 20 | }; 21 | @endphp 22 | 23 |
24 |
25 | {{ $trigger }} 26 |
27 | 28 | 38 |
39 | -------------------------------------------------------------------------------- /resources/views/components/form-card.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'maxWidth' => '7xl', 3 | 'formWidth' => 'lg', 4 | 'submit', 5 | 'title', 6 | 'description', 7 | ]) 8 | 9 | @php 10 | $maxWidthClasses = match ($maxWidth) { 11 | '7xl' => 'max-w-7xl', 12 | 'full' => 'w-full', 13 | default => $maxWidth, 14 | }; 15 | 16 | $formWidthClasses = match ($formWidth) { 17 | 'lg' => 'max-w-lg', 18 | '3xl' => 'max-w-3xl', 19 | '7xl' => 'max-w-7xl', 20 | 'full' => 'w-full', 21 | default => $formWidth, 22 | }; 23 | @endphp 24 | 25 |
26 |
27 | @isset($title) 28 |

{{ $title }}

29 | @endisset 30 | 31 | @isset($description) 32 |

33 | {{ $description }} 34 |

35 | @endisset 36 | 37 |
38 | {{ $form }} 39 | 40 | @isset($actions) 41 |
42 | {{ $actions }} 43 |
44 | @endisset 45 |
46 | 47 | @isset($content) 48 |
49 | {{ $content }} 50 |
51 | @endisset 52 |
53 |
54 | -------------------------------------------------------------------------------- /resources/views/components/icon-arrow-down.blade.php: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/views/components/icon-arrow-up.blade.php: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/views/components/icon-dashboard.blade.php: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /resources/views/components/icon-data-not-found.blade.php: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /resources/views/components/icon-disable.blade.php: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /resources/views/components/icon-home.blade.php: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /resources/views/components/icon-logout.blade.php: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /resources/views/components/icon-profile.blade.php: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /resources/views/components/icon-setting.blade.php: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/views/components/icon-sort-asc.blade.php: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/views/components/icon-sort-desc.blade.php: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/views/components/input-error.blade.php: -------------------------------------------------------------------------------- 1 | @props(['for']) 2 | 3 | @error($for) 4 |

merge(['class' => 'text-sm text-red-600 dark:text-red-400']) }}>{{ $message }}

5 | @enderror 6 | -------------------------------------------------------------------------------- /resources/views/components/label.blade.php: -------------------------------------------------------------------------------- 1 | @props(['value']) 2 | 3 | 9 | -------------------------------------------------------------------------------- /resources/views/components/logo.blade.php: -------------------------------------------------------------------------------- 1 | 2 | {{ config('app.name', 'Laravel') }} 3 | 4 | -------------------------------------------------------------------------------- /resources/views/components/modal.blade.php: -------------------------------------------------------------------------------- 1 | @props(['id', 'maxWidth']) 2 | 3 | @php 4 | $id = $id ?? md5($attributes->wire('model')); 5 | 6 | $maxWidth = [ 7 | 'sm' => 'sm:max-w-sm', 8 | 'md' => 'sm:max-w-md', 9 | 'lg' => 'sm:max-w-lg', 10 | 'xl' => 'sm:max-w-xl', 11 | '2xl' => 'sm:max-w-2xl', 12 | ][$maxWidth ?? '2xl']; 13 | @endphp 14 | 15 | 34 | -------------------------------------------------------------------------------- /resources/views/components/nav-dropdown.blade.php: -------------------------------------------------------------------------------- 1 | @props(['active', 'title']) 2 | 3 | @php 4 | $classes = $active ?? false ? 'block px-3 py-2 text-sm font-medium text-zinc-700 flex items-center text-left justify-between w-full dark:text-zinc-100 bg-zinc-100 dark:bg-zinc-900 rounded-md' : 'block px-3 py-2 flex items-center text-left justify-between w-full text-sm font-medium text-zinc-700 dark:text-zinc-100 rounded-md hover:bg-zinc-100 dark:hover:bg-zinc-900 hover:text-zinc-700 dark:hover:text-white'; 5 | @endphp 6 | 7 |
8 | 17 | 18 | 25 |
26 | -------------------------------------------------------------------------------- /resources/views/components/nav-link.blade.php: -------------------------------------------------------------------------------- 1 | @props(['active']) 2 | 3 | @php 4 | $classes = $active ?? false ? 'px-3 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-100 bg-zinc-100 dark:bg-zinc-900 rounded-md' : 'px-3 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-100 rounded-md hover:bg-zinc-100 dark:hover:bg-zinc-900 hover:text-zinc-700 dark:hover:text-white'; 5 | @endphp 6 | 7 | merge(['class' => $classes]) }}> 8 | {{ $slot }} 9 | 10 | -------------------------------------------------------------------------------- /resources/views/components/responsive-nav-link.blade.php: -------------------------------------------------------------------------------- 1 | @props(['active']) 2 | 3 | @php 4 | $classes = $active ?? false ? 'block px-3 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-100 bg-zinc-100 dark:bg-zinc-900 rounded-md' : 'block px-3 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-100 rounded-md hover:bg-zinc-100 dark:hover:bg-zinc-900 hover:text-zinc-700 dark:hover:text-white'; 5 | @endphp 6 | 7 | merge(['class' => $classes]) }}> 8 | {{ $slot }} 9 | 10 | -------------------------------------------------------------------------------- /resources/views/components/secondary-button-link.blade.php: -------------------------------------------------------------------------------- 1 | merge([ 3 | 'class' => 4 | 'inline-block rounded-md bg-white dark:bg-zinc-800 px-3 py-2 text-sm font-semibold dark:text-white hover:bg-zinc-100 dark:hover:bg-zinc-900', 5 | ]) }}> 6 | {{ $slot }} 7 | 8 | -------------------------------------------------------------------------------- /resources/views/components/secondary-button.blade.php: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /resources/views/components/select-input.blade.php: -------------------------------------------------------------------------------- 1 | @props(['disabled' => false]) 2 | 3 | 10 | -------------------------------------------------------------------------------- /resources/views/components/session-message.blade.php: -------------------------------------------------------------------------------- 1 | @props(['session']) 2 | 3 | @if (session($session)) 4 |
merge(['class' => 'text-sm text-zinc-600 dark:text-zinc-400']) }}> 6 | {{ session($session) }} 7 |
8 | @endif 9 | -------------------------------------------------------------------------------- /resources/views/components/stat-card.blade.php: -------------------------------------------------------------------------------- 1 | @props(['title', 'stats']) 2 | 3 |
4 |

5 | {{ $title }} 6 |

7 | 8 |

9 | {{ $stats }} 10 |

11 |
12 | -------------------------------------------------------------------------------- /resources/views/components/table-action-button.blade.php: -------------------------------------------------------------------------------- 1 | @props(['type' => 'info']) 2 | 3 | @php 4 | $typeClasses = match ($type) { 5 | 'info' => 'text-zinc-600 dark:text-zinc-400', 6 | 'danger' => 'text-red-600 dark:text-red-400', 7 | 'warning' => 'text-yellow-600 dark:text-yellow-400', 8 | 'success' => 'text-indigo-600 dark:text-indigo-400', 9 | default => $type, 10 | }; 11 | @endphp 12 | 13 | 18 | -------------------------------------------------------------------------------- /resources/views/components/table-action-link.blade.php: -------------------------------------------------------------------------------- 1 | @props(['type' => 'info']) 2 | 3 | @php 4 | $typeClasses = match ($type) { 5 | 'info' => 'text-zinc-600 dark:text-zinc-400', 6 | 'danger' => 'text-red-600 dark:text-red-400', 7 | 'warning' => 'text-yellow-600 dark:text-yellow-400', 8 | 'success' => 'text-indigo-600 dark:text-indigo-400', 9 | default => $type, 10 | }; 11 | @endphp 12 | 13 | merge([ 14 | 'class' => 'font-medium hover:underline ' . $typeClasses, 15 | ]) }}> 16 | {{ $slot }} 17 | 18 | -------------------------------------------------------------------------------- /resources/views/components/table-body-not-found.blade.php: -------------------------------------------------------------------------------- 1 | merge([ 2 | 'class' => 'px-6 py-4 space-x-2 whitespace-nowrap', 3 | ]) }}> 4 |
5 | 6 | 7 | {{ __('Data Not Found') }} 8 |
9 | 10 | -------------------------------------------------------------------------------- /resources/views/components/table-body-td.blade.php: -------------------------------------------------------------------------------- 1 | merge([ 2 | 'class' => 'px-6 py-4 space-x-2 whitespace-nowrap', 3 | ]) }}> 4 | {{ $slot }} 5 | 6 | -------------------------------------------------------------------------------- /resources/views/components/table-body-tr.blade.php: -------------------------------------------------------------------------------- 1 | merge([ 3 | 'class' => 'odd:bg-white odd:dark:bg-zinc-800 even:bg-zinc-100 even:dark:bg-zinc-900', 4 | ]) }}> 5 | {{ $slot }} 6 | 7 | -------------------------------------------------------------------------------- /resources/views/components/table-body.blade.php: -------------------------------------------------------------------------------- 1 | merge([ 2 | 'class' => '', 3 | ]) }}> 4 | {{ $slot }} 5 | 6 | -------------------------------------------------------------------------------- /resources/views/components/table-head-th.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | {{ $slot }} 4 |
5 | 6 | -------------------------------------------------------------------------------- /resources/views/components/table-head-tr.blade.php: -------------------------------------------------------------------------------- 1 | merge([ 2 | 'class' => '', 3 | ]) }}> 4 | {{ $slot }} 5 | 6 | -------------------------------------------------------------------------------- /resources/views/components/table-head.blade.php: -------------------------------------------------------------------------------- 1 | merge([ 3 | 'class' => 'text-zinc-600 uppercase bg-zinc-100 dark:bg-zinc-900 dark:text-zinc-400', 4 | ]) }}> 5 | 6 | {{ $slot }} 7 | 8 | 9 | -------------------------------------------------------------------------------- /resources/views/components/table.blade.php: -------------------------------------------------------------------------------- 1 | merge([ 2 | 'class' => 'w-full text-left text-zinc-500 dark:text-zinc-400', 3 | ]) }}> 4 | {{ $slot }} 5 |
6 | -------------------------------------------------------------------------------- /resources/views/components/text-input.blade.php: -------------------------------------------------------------------------------- 1 | @props(['disabled' => false]) 2 | 3 | merge([ 4 | 'class' => 5 | 'border-zinc-300 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md', 6 | ]) !!}> 7 | -------------------------------------------------------------------------------- /resources/views/errors/401.blade.php: -------------------------------------------------------------------------------- 1 | @extends('errors::minimal') 2 | 3 | @section('title', __('Unauthorized')) 4 | @section('code', '401') 5 | @section('message', __('Unauthorized')) 6 | -------------------------------------------------------------------------------- /resources/views/errors/402.blade.php: -------------------------------------------------------------------------------- 1 | @extends('errors::minimal') 2 | 3 | @section('title', __('Payment Required')) 4 | @section('code', '402') 5 | @section('message', __('Payment Required')) 6 | -------------------------------------------------------------------------------- /resources/views/errors/403.blade.php: -------------------------------------------------------------------------------- 1 | @extends('errors::minimal') 2 | 3 | @section('title', __('Forbidden')) 4 | @section('code', '403') 5 | @section('message', __($exception->getMessage() ?: 'Forbidden')) 6 | -------------------------------------------------------------------------------- /resources/views/errors/404.blade.php: -------------------------------------------------------------------------------- 1 | @extends('errors::minimal') 2 | 3 | @section('title', __('Not Found')) 4 | @section('code', '404') 5 | @section('message', __('Not Found')) 6 | -------------------------------------------------------------------------------- /resources/views/errors/419.blade.php: -------------------------------------------------------------------------------- 1 | @extends('errors::minimal') 2 | 3 | @section('title', __('Page Expired')) 4 | @section('code', '419') 5 | @section('message', __('Page Expired')) 6 | -------------------------------------------------------------------------------- /resources/views/errors/429.blade.php: -------------------------------------------------------------------------------- 1 | @extends('errors::minimal') 2 | 3 | @section('title', __('Too Many Requests')) 4 | @section('code', '429') 5 | @section('message', __('Too Many Requests')) 6 | -------------------------------------------------------------------------------- /resources/views/errors/500.blade.php: -------------------------------------------------------------------------------- 1 | @extends('errors::minimal') 2 | 3 | @section('title', __('Server Error')) 4 | @section('code', '500') 5 | @section('message', __('Server Error')) 6 | -------------------------------------------------------------------------------- /resources/views/errors/503.blade.php: -------------------------------------------------------------------------------- 1 | @extends('errors::minimal') 2 | 3 | @section('title', __('Service Unavailable')) 4 | @section('code', '503') 5 | @section('message', __('Service Unavailable')) 6 | -------------------------------------------------------------------------------- /resources/views/errors/minimal.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | @yield('title') | {{ config('app.name', 'Laravel') }} 14 | 15 | 16 | {{-- Scripts --}} 17 | @vite(['resources/css/app.css', 'resources/js/app.js']) 18 | 19 | {{-- Styles --}} 20 | @livewireStyles 21 | 22 | 23 | 24 |
25 |
26 |
27 |
28 | 29 | 30 |

31 | @yield('code') 32 |

33 | 34 |

35 | @yield('message') 36 |

37 | 38 | @if ($exception->getStatusCode() == 404) 39 |

40 | {{ __('Sorry, we can\'t find that page.') }} 41 |

42 | @endif 43 | 44 | @if ($exception->getStatusCode() == 503) 45 |

46 | {{ __('We are currently performing some maintenance. We will be back shortly.') }} 47 |

48 | @endif 49 | 50 | @unless ($exception->getStatusCode() == 503) 51 | 52 | {{ __('Home') }} 53 | 54 | @endunless 55 |
56 |
57 |
58 |
59 | 60 | @livewireScripts 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /resources/views/layouts/auth.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {{ $title ?? null ? __($title) . ' | ' . config('app.name', 'Laravel') : config('app.name', 'Laravel') }} 15 | 16 | 17 | {{-- Scripts --}} 18 | @vite(['resources/css/app.css', 'resources/js/app.js']) 19 | 20 | {{-- Styles --}} 21 | @livewireStyles 22 | 23 | 24 | 25 | 26 | 27 | {{-- Page Content --}} 28 |
29 | {{ $slot }} 30 |
31 | 32 | @stack('modals') 33 | 34 | @livewireScripts 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /resources/views/livewire/admin/example.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | {{ __('Admin Header Title') }} 5 |

6 |
7 | 8 | 9 | 10 | {{ __('Header Actions') }} 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | {{ __('Card Actions') }} 19 | 20 | 21 | 22 | 23 | {{ __('Card Content') }} 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{ __('Card Actions') }} 31 | 32 | 33 | 34 | 35 | {{ __('Card Title') }} 36 | 37 | 38 | 39 | {{ __('Card description.') }} 40 | 41 | 42 | 43 | {{ __('Card Content') }} 44 | 45 | 46 | 47 | 49 | 50 | {{-- Name --}} 51 |
52 | 53 | 55 | 56 |
57 | 58 | {{-- Email --}} 59 |
60 | 61 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | {{ __('Submit') }} 70 | 71 | 72 | 73 | {{ __('Cancel') }} 74 | 75 | 76 |
77 |
78 |
79 | -------------------------------------------------------------------------------- /resources/views/livewire/admin/user/create.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | 5 | {{ __('Users') }} 6 | 7 | / 8 | {{ __('Create') }} 9 |

10 |
11 | 12 |
13 | 15 | 16 | {{-- Name --}} 17 |
18 | 19 | 21 | 22 |
23 | 24 | {{-- Email --}} 25 |
26 | 27 | 29 | {{--

30 | {{ __('Please use a valid email address.') }} 31 |

--}} 32 | 33 |
34 |
35 | 36 | 37 | 38 | {{ __('Save') }} 39 | 40 | 41 | 42 | {{ __('Cancel') }} 43 | 44 | 45 |
46 |
47 |
48 | -------------------------------------------------------------------------------- /resources/views/livewire/admin/user/edit.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | 5 | {{ __('Users') }} 6 | 7 | / 8 | 9 | 10 | {{ Str::limit($user->name, 12, ' ...') }} 11 | 12 | 13 | 14 | / 15 | {{ __('Edit') }} 16 |

17 |
18 | 19 |
20 | 22 | 23 | {{-- Name --}} 24 |
25 | 26 | 28 | 29 |
30 | 31 | {{-- Email --}} 32 |
33 | 34 | 36 | 37 |
38 |
39 | 40 | 41 | 42 | {{ __('Save') }} 43 | 44 | 45 | 46 | {{ __('Cancel') }} 47 | 48 | 49 |
50 |
51 |
52 | -------------------------------------------------------------------------------- /resources/views/livewire/admin/user/partials/bulk-actions.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | {{ __('Bulk Actions') }} 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 23 | 24 | 25 | 26 | {{ __('Delete Selected') }} 27 | 28 | 29 | 30 | 31 | 33 | 35 | 36 | 37 | 38 | {{ __('Verify Selected') }} 39 | 40 | 41 | 42 | 43 | 45 | 47 | 48 | 49 | 50 | {{ __('Unverify Selected') }} 51 | 52 | 53 | 54 |
55 | -------------------------------------------------------------------------------- /resources/views/livewire/admin/user/partials/user-delete-modal.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | {{ __('Are you sure you want delete this user? Related data to this user will also be deleted. This action cannot be undone.') }} 5 |
6 | 7 | @if ($selected_user_delete) 8 |
9 | {{ $selected_user_delete->name }} 10 |
11 |
12 | {{ $selected_user_delete->email }} 13 |
14 | @if ($selected_user_delete == auth()->user()) 15 |
16 | {{ __('You are currently logged in as this user. Deleting this user will log you out, and you won\'t be able to log back in.') }} 17 |
18 | @endif 19 | @endif 20 |
21 | 22 | 23 | 24 | {{ __('Delete User') }} 25 | 26 | 27 | 28 | {{ __('Cancel') }} 29 | 30 | 31 |
32 | -------------------------------------------------------------------------------- /resources/views/livewire/auth/login.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | {{-- Email --}} 5 |
6 | 7 | 9 | 10 |
11 | 12 | {{-- Password --}} 13 |
14 | 15 | 17 |
18 | 19 | {{-- Remember me --}} 20 |
21 | 25 |
26 | 27 |
28 |
29 | 31 | {{ __('Not register yet?') }} 32 | 33 | @if (Route::has('password.request')) 34 | 36 | {{ __('Forgot your password?') }} 37 | 38 | @endif 39 |
40 | 41 | 42 | {{ __('Login') }} 43 | 44 |
45 |
46 |
47 |
48 | -------------------------------------------------------------------------------- /resources/views/livewire/auth/password/confirm.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | {{ __('Please confirm your password before continuing.') }} 5 |
6 | 7 |
8 | {{-- Password --}} 9 |
10 | 11 | 13 | 14 |
15 | 16 |
17 | 18 | {{ __('Confirm') }} 19 | 20 |
21 |
22 | 23 |
24 |
25 | -------------------------------------------------------------------------------- /resources/views/livewire/auth/password/request.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | {{ __('Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.') }} 5 |
6 | 7 |
8 | {{-- Email --}} 9 |
10 | 11 | 13 | 14 |
15 | 16 |
17 | 18 | {{ __('Send Password Reset Link') }} 19 | 20 |
21 |
22 | 23 |
24 |
25 | -------------------------------------------------------------------------------- /resources/views/livewire/auth/password/reset.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | {{-- Email --}} 5 |
6 | 7 | 9 | 10 |
11 | 12 | {{-- Password --}} 13 |
14 | 15 | 17 | 18 |
19 | 20 | {{-- Password confirmation --}} 21 |
22 | 23 | 26 | 27 |
28 | 29 |
30 | 31 | {{ __('Reset Password') }} 32 | 33 |
34 |
35 |
36 |
37 | -------------------------------------------------------------------------------- /resources/views/livewire/auth/privacy-policy.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | {!! $privacy !!} 5 |
6 |
7 |
8 | -------------------------------------------------------------------------------- /resources/views/livewire/auth/terms-of-service.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | {!! $terms !!} 5 |
6 |
7 |
8 | -------------------------------------------------------------------------------- /resources/views/livewire/auth/verify.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | {{ __('Before continuing, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }} 5 |
6 | 7 |
8 | 9 | {{ __('Resend Verification Email') }} 10 | 11 | 12 |
13 | 15 | {{ __('Edit Profile') }} 16 | 17 | 21 |
22 |
23 |
24 |
25 | -------------------------------------------------------------------------------- /resources/views/livewire/example.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | {{ __('Main Header Title') }} 5 |

6 |
7 | 8 | 9 | 10 | {{ __('Header Actions') }} 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | {{ __('Card Actions') }} 19 | 20 | 21 | 22 | 23 | {{ __('Card Content') }} 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{ __('Card Actions') }} 31 | 32 | 33 | 34 | 35 | {{ __('Card Title') }} 36 | 37 | 38 | 39 | {{ __('Card description.') }} 40 | 41 | 42 | 43 | {{ __('Card Content') }} 44 | 45 | 46 | 47 | 49 | 50 | {{-- Name --}} 51 |
52 | 53 | 55 | 56 |
57 | 58 | {{-- Email --}} 59 |
60 | 61 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | {{ __('Submit') }} 70 | 71 | 72 | 73 | {{ __('Cancel') }} 74 | 75 | 76 |
77 |
78 |
79 | -------------------------------------------------------------------------------- /resources/views/livewire/user/dashboard.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | {{ __('User Dashboard') }} 5 |

6 |
7 | 8 |
9 | {{-- Welcome app --}} 10 | 12 | 13 |
14 |
15 | -------------------------------------------------------------------------------- /resources/views/livewire/user/example.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | {{ __('App Header Title') }} 5 |

6 |
7 | 8 | 9 | 10 | {{ __('Header Actions') }} 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | {{ __('Card Actions') }} 19 | 20 | 21 | 22 | 23 | {{ __('Card Content') }} 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{ __('Card Actions') }} 31 | 32 | 33 | 34 | 35 | {{ __('Card Title') }} 36 | 37 | 38 | 39 | {{ __('Card description.') }} 40 | 41 | 42 | 43 | {{ __('Card Content') }} 44 | 45 | 46 | 47 | 49 | 50 | {{-- Name --}} 51 |
52 | 53 | 55 | 56 |
57 | 58 | {{-- Email --}} 59 |
60 | 61 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | {{ __('Submit') }} 70 | 71 | 72 | 73 | {{ __('Cancel') }} 74 | 75 | 76 |
77 |
78 |
79 | -------------------------------------------------------------------------------- /resources/views/livewire/user/partials/profile/delete-account.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | {{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.') }} 5 |
6 | 7 |
8 | 9 | {{ __('Delete Account') }} 10 | 11 |
12 | 13 | 14 | 15 |
16 | {{ __('Are you sure you want to delete your account? Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.') }} 17 |
18 | 19 | {{-- Password --}} 20 |
21 | 22 | 24 | 25 |
26 |
27 | 28 | 29 | 30 | {{ __('Delete Account') }} 31 | 32 | 33 | 35 | {{ __('Cancel') }} 36 | 37 | 38 |
39 |
40 |
41 | -------------------------------------------------------------------------------- /resources/views/livewire/user/partials/profile/update-password.blade.php: -------------------------------------------------------------------------------- 1 | 3 | 4 | {{-- Current password --}} 5 |
6 | 7 | 9 | 10 |
11 | 12 | {{-- New password --}} 13 |
14 | 15 | 17 | 18 |
19 | 20 | {{-- New password confirmation --}} 21 |
22 | 23 | 26 | 27 |
28 |
29 | 30 | 31 | 32 | {{ __('Save') }} 33 | 34 | 35 |
36 | -------------------------------------------------------------------------------- /resources/views/livewire/user/partials/profile/update-profile.blade.php: -------------------------------------------------------------------------------- 1 | 3 | 4 | {{-- Name --}} 5 |
6 | 7 | 9 | 10 |
11 | 12 | {{-- Email --}} 13 |
14 | 15 | 17 | 18 | 19 | @if ($user instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && !$user->hasVerifiedEmail()) 20 |
21 |

22 | {{ __('Your email address is unverified.') }} 23 | 24 | 29 |

30 | 31 | @if (session('resent')) 32 |

33 | {{ __('A new verification link has been sent to your email address.') }} 34 |

35 | @endif 36 |
37 | @endif 38 |
39 |
40 | 41 | 42 | 43 | {{ __('Save') }} 44 | 45 | 46 |
47 | -------------------------------------------------------------------------------- /resources/views/livewire/user/partials/settings/change-language.blade.php: -------------------------------------------------------------------------------- 1 | 3 | 4 | {{-- Language --}} 5 |
6 | 7 | 8 | @foreach ($supported_languages as $translation) 9 | 12 | @endforeach 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | {{ __('Save') }} 21 | 22 | 23 |
24 | -------------------------------------------------------------------------------- /resources/views/livewire/user/partials/settings/terms-and-policy.blade.php: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | {{ __('View Terms of Service') }} 6 | 7 | 8 | 9 | {{ __('View Privacy Policy') }} 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /resources/views/livewire/user/profile.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | {{ __('Profile') }} 5 |

6 |
7 | 8 |
9 | @include('livewire.user.partials.profile.update-profile') 10 | 11 | @include('livewire.user.partials.profile.update-password') 12 | 13 | @include('livewire.user.partials.profile.delete-account') 14 |
15 |
16 | -------------------------------------------------------------------------------- /resources/views/livewire/user/settings.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | {{ __('Settings') }} 5 |

6 |
7 | 8 |
9 | @include('livewire.user.partials.settings.change-language') 10 | 11 | @include('livewire.user.partials.settings.terms-and-policy') 12 |
13 |
14 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | get('/user', function (Request $request) { 18 | return $request->user(); 19 | }); 20 | -------------------------------------------------------------------------------- /routes/auth.php: -------------------------------------------------------------------------------- 1 | name('termsofservice'); 9 | Route::get('/privacy-policy', Auth\PrivacyPolicy::class) 10 | ->name('privacypolicy'); 11 | 12 | Route::middleware('auth')->group(function () { 13 | Route::get('/email/verify', Auth\Verify::class) 14 | ->middleware('throttle:6,1') 15 | ->name('verification.notice'); 16 | Route::get('/email/verify/{id}/{hash}', EmailVerificationController::class) 17 | ->middleware('signed') 18 | ->name('verification.verify'); 19 | Route::get('/password/confirm', Auth\Password\Confirm::class) 20 | ->name('password.confirm'); 21 | }); 22 | 23 | Route::middleware('guest')->group(function () { 24 | Route::get('/login', Auth\Login::class) 25 | ->name('login'); 26 | Route::get('/register', Auth\Register::class) 27 | ->name('register'); 28 | Route::get('/password/request', Auth\Password\Request::class) 29 | ->name('password.request'); 30 | Route::get('/password/reset/{token}', Auth\Password\Reset::class) 31 | ->name('password.reset'); 32 | }); 33 | -------------------------------------------------------------------------------- /routes/channels.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 18 | }); 19 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 19 | })->purpose('Display an inspiring quote'); 20 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | name('home'); 21 | Route::get('/example', Example::class)->name('example'); 22 | 23 | Route::fallback(function () { 24 | abort(404); 25 | }); 26 | 27 | Route::prefix('user') 28 | ->name('user.') 29 | ->middleware(['auth']) 30 | ->group(function () { 31 | Route::get('/profile', User\Profile::class)->name('profile'); 32 | Route::get('/settings', User\Settings::class)->name('settings'); 33 | 34 | Route::middleware(['verified.email']) 35 | ->group(function () { 36 | Route::get('/dashboard', User\Dashboard::class)->name('dashboard'); 37 | Route::get('/example', User\Example::class)->name('example'); 38 | }); 39 | }); 40 | 41 | Route::prefix('admin') 42 | ->name('admin.') 43 | ->middleware(['auth', 'verified.email', 'password.confirm']) 44 | ->group(function () { 45 | Route::get('/dashboard', Admin\Dashboard::class)->name('dashboard'); 46 | Route::get('/example', Admin\Example::class)->name('example'); 47 | 48 | Route::prefix('users') 49 | ->name('users.') 50 | ->group(function () { 51 | Route::get('/', Admin\User\Index::class)->name('index'); 52 | Route::get('/create', Admin\User\Create::class)->name('create'); 53 | Route::get('/{user}', Admin\User\Show::class)->name('show'); 54 | Route::get('/{user}/edit', Admin\User\Edit::class)->name('edit'); 55 | }); 56 | }); 57 | 58 | include __DIR__.'/auth.php'; 59 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/debugbar/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | compiled.php 2 | config.php 3 | down 4 | events.scanned.php 5 | maintenance.php 6 | routes.php 7 | routes.scanned.php 8 | schedule-* 9 | services.json 10 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | import defaultTheme from "tailwindcss/defaultTheme"; 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | export default { 5 | content: [ 6 | "./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php", 7 | "./storage/framework/views/*.php", 8 | "./resources/views/**/*.blade.php", 9 | "./vendor/masmerise/livewire-toaster/resources/views/*.blade.php", 10 | ], 11 | theme: { 12 | extend: { 13 | fontFamily: { 14 | sans: ["Inter", ...defaultTheme.fontFamily.sans], 15 | }, 16 | }, 17 | }, 18 | plugins: [ 19 | require("@tailwindcss/typography"), 20 | require("@tailwindcss/forms"), 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /tests/CreatesApplication.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 18 | 19 | return $app; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Feature/Admin/DashboardTest.php: -------------------------------------------------------------------------------- 1 | create(); 8 | 9 | $this->actingAs($user); 10 | 11 | Livewire::test(Dashboard::class) 12 | ->assertStatus(200); 13 | }); 14 | 15 | it('allows authenticated user with session auth.password_confirmed_at to access admin dashboard page', function () { 16 | $user = User::factory()->create(); 17 | 18 | session()->put('auth.password_confirmed_at', time()); 19 | 20 | $response = $this->actingAs($user)->get(route('admin.dashboard')); 21 | 22 | $response->assertStatus(200); 23 | }); 24 | 25 | it('redirect authenticated user without session auth.password_confirmed_at to access admin dashboard page', function () { 26 | $user = User::factory()->create(); 27 | 28 | $response = $this->actingAs($user)->get(route('admin.dashboard')); 29 | 30 | $response->assertRedirect(route('password.confirm')); 31 | 32 | $response->assertStatus(302); 33 | }); 34 | 35 | it('redirects unauthenticated user to login page when accessing admin dashboard page', function () { 36 | $response = $this->get(route('admin.dashboard')); 37 | 38 | $response->assertRedirect(route('login')); 39 | 40 | $response->assertStatus(302); 41 | }); 42 | -------------------------------------------------------------------------------- /tests/Feature/Admin/ExampleTest.php: -------------------------------------------------------------------------------- 1 | assertStatus(200); 8 | }); 9 | -------------------------------------------------------------------------------- /tests/Feature/Admin/User/CreateTest.php: -------------------------------------------------------------------------------- 1 | assertStatus(200); 9 | }); 10 | 11 | it('allows authenticated user with session auth.password_confirmed_at to access admin users create page', function () { 12 | $user = User::factory()->create(); 13 | 14 | session()->put('auth.password_confirmed_at', time()); 15 | 16 | $response = $this->actingAs($user)->get(route('admin.users.create')); 17 | 18 | $response->assertStatus(200); 19 | }); 20 | 21 | it('redirect authenticated user without session auth.password_confirmed_at to access admin users create page', function () { 22 | $user = User::factory()->create(); 23 | 24 | $response = $this->actingAs($user)->get(route('admin.users.create')); 25 | 26 | $response->assertRedirect(route('password.confirm')); 27 | 28 | $response->assertStatus(302); 29 | }); 30 | 31 | it('redirects unauthenticated user to login page when accessing admin users create page', function () { 32 | $response = $this->get(route('admin.users.create')); 33 | 34 | $response->assertRedirect(route('login')); 35 | 36 | $response->assertStatus(302); 37 | }); 38 | 39 | it('can create new user when provided valid data', function () { 40 | Livewire::test(Create::class) 41 | ->set('name', 'User Name') 42 | ->set('email', 'user1@mail.com') 43 | ->call('createUser') 44 | ->assertHasNoErrors() 45 | ->assertRedirect(route('admin.users.index')) 46 | ->assertStatus(200); 47 | 48 | $this->assertDatabaseHas('users', [ 49 | 'name' => 'User Name', 50 | 'email' => 'user1@mail.com', 51 | ]); 52 | }); 53 | 54 | it('can not create new user when provided invalid data', function () { 55 | Livewire::test(Create::class) 56 | ->set('name', 'User 1') 57 | ->set('email', 'user1') 58 | ->call('createUser') 59 | ->assertHasErrors(['email' => 'email']) 60 | ->assertStatus(200); 61 | 62 | $this->assertDatabaseMissing('users', [ 63 | 'name' => 'User 1', 64 | 'email' => 'user1', 65 | ]); 66 | }); 67 | -------------------------------------------------------------------------------- /tests/Feature/Admin/User/EditTest.php: -------------------------------------------------------------------------------- 1 | create(); 8 | 9 | Livewire::test(Edit::class, ['user' => $editUser]) 10 | ->assertStatus(200); 11 | }); 12 | 13 | it('allows authenticated user with session auth.password_confirmed_at to access admin users edit page', function () { 14 | $user = User::factory()->create(); 15 | 16 | $editUser = User::factory()->create(); 17 | 18 | session()->put('auth.password_confirmed_at', time()); 19 | 20 | $response = $this->actingAs($user)->get(route('admin.users.edit', $editUser)); 21 | 22 | $response->assertStatus(200); 23 | }); 24 | 25 | it('redirect authenticated user without session auth.password_confirmed_at to access admin users edit page', function () { 26 | $user = User::factory()->create(); 27 | 28 | $editUser = User::factory()->create(); 29 | 30 | $response = $this->actingAs($user)->get(route('admin.users.edit', $editUser)); 31 | 32 | $response->assertRedirect(route('password.confirm')); 33 | 34 | $response->assertStatus(302); 35 | }); 36 | 37 | it('redirects unauthenticated user to login page when accessing admin users edit page', function () { 38 | $editUser = User::factory()->create(); 39 | 40 | $response = $this->get(route('admin.users.edit', $editUser)); 41 | 42 | $response->assertRedirect(route('login')); 43 | 44 | $response->assertStatus(302); 45 | }); 46 | 47 | it('can update user when provided valid data', function () { 48 | $user = User::factory()->create(); 49 | 50 | $editUser = User::factory()->create(); 51 | 52 | $this->actingAs($user); 53 | 54 | Livewire::test(Edit::class, ['user' => $editUser]) 55 | ->set('name', 'User Name') 56 | ->set('email', 'new@email.com') 57 | ->call('updateUser') 58 | ->assertHasNoErrors() 59 | ->assertRedirect(route('admin.users.show', $editUser)) 60 | ->assertStatus(200); 61 | 62 | $this->assertDatabaseHas('users', [ 63 | 'id' => $editUser->id, 64 | 'name' => 'User Name', 65 | 'email' => 'new@email.com', 66 | ]); 67 | }); 68 | 69 | it('can not update user when provided invalid data', function () { 70 | $user = User::factory()->create(); 71 | 72 | $editUser = User::factory()->create(); 73 | 74 | $this->actingAs($user); 75 | 76 | Livewire::test(Edit::class, ['user' => $editUser]) 77 | ->set('name', 'User 1') 78 | ->set('email', 'invalidemail') 79 | ->call('updateUser') 80 | ->assertHasErrors(['email' => 'email']) 81 | ->assertStatus(200); 82 | 83 | $this->assertDatabaseHas('users', [ 84 | 'id' => $editUser->id, 85 | 'name' => $editUser->name, 86 | 'email' => $editUser->email, 87 | ]); 88 | }); 89 | -------------------------------------------------------------------------------- /tests/Feature/Admin/User/IndexTest.php: -------------------------------------------------------------------------------- 1 | assertStatus(200); 9 | }); 10 | 11 | it('allows authenticated user with session auth.password_confirmed_at to access admin users index page', function () { 12 | $user = User::factory()->create(); 13 | 14 | session()->put('auth.password_confirmed_at', time()); 15 | 16 | $response = $this->actingAs($user)->get(route('admin.users.index')); 17 | 18 | $response->assertStatus(200); 19 | }); 20 | 21 | it('redirect authenticated user without session auth.password_confirmed_at to access admin users index page', function () { 22 | $user = User::factory()->create(); 23 | 24 | $response = $this->actingAs($user)->get(route('admin.users.index')); 25 | 26 | $response->assertRedirect(route('password.confirm')); 27 | 28 | $response->assertStatus(302); 29 | }); 30 | 31 | it('redirects unauthenticated user to login page when accessing admin users index page', function () { 32 | $response = $this->get(route('admin.users.index')); 33 | 34 | $response->assertRedirect(route('login')); 35 | 36 | $response->assertStatus(302); 37 | }); 38 | 39 | it('shows 10 paginated user data', function () { 40 | $users = User::factory(20)->create(); 41 | 42 | Livewire::test(Index::class) 43 | ->set('paginate', 10) 44 | ->assertSee($users[0]->name) 45 | ->assertSee($users[9]->name) 46 | ->assertDontSee($users[10]->name) 47 | ->assertStatus(200); 48 | }); 49 | 50 | it('can search by name or email', function () { 51 | $users = User::factory(20)->create(); 52 | 53 | Livewire::test(Index::class) 54 | ->set('search', $users[0]->name) 55 | ->assertSee($users[0]->name) 56 | ->assertDontSee($users[1]->name) 57 | ->assertStatus(200); 58 | 59 | Livewire::test(Index::class) 60 | ->set('search', $users[0]->email) 61 | ->assertSee($users[0]->name) 62 | ->assertDontSee($users[1]->name) 63 | ->assertStatus(200); 64 | }); 65 | 66 | it('can delete user', function () { 67 | $user = User::factory()->create(); 68 | 69 | $this->actingAs($user); 70 | 71 | Livewire::test(Index::class) 72 | ->call('confirmUserDeletion', $user->id) 73 | ->call('deleteUser') 74 | ->assertStatus(200); 75 | 76 | $this->assertDatabaseMissing('users', [ 77 | 'id' => $user->id, 78 | ]); 79 | }); 80 | -------------------------------------------------------------------------------- /tests/Feature/Admin/User/ShowTest.php: -------------------------------------------------------------------------------- 1 | create(); 8 | 9 | Livewire::test(Show::class, ['user' => $showUser]) 10 | ->assertStatus(200); 11 | }); 12 | it('allows authenticated user with session auth.password_confirmed_at to access admin users show page', function () { 13 | $user = User::factory()->create(); 14 | 15 | $showUser = User::factory()->create(); 16 | 17 | session()->put('auth.password_confirmed_at', time()); 18 | 19 | $response = $this->actingAs($user)->get(route('admin.users.show', $showUser)); 20 | 21 | $response->assertStatus(200); 22 | }); 23 | 24 | it('redirect authenticated user without session auth.password_confirmed_at to access admin users show page', function () { 25 | $user = User::factory()->create(); 26 | 27 | $showUser = User::factory()->create(); 28 | 29 | $response = $this->actingAs($user)->get(route('admin.users.show', $showUser)); 30 | 31 | $response->assertRedirect(route('password.confirm')); 32 | 33 | $response->assertStatus(302); 34 | }); 35 | 36 | it('redirects unauthenticated user to login page when accessing admin users show page', function () { 37 | $showUser = User::factory()->create(); 38 | 39 | $response = $this->get(route('admin.users.show', $showUser)); 40 | 41 | $response->assertRedirect(route('login')); 42 | 43 | $response->assertStatus(302); 44 | }); 45 | -------------------------------------------------------------------------------- /tests/Feature/Auth/EmailVerificationTest.php: -------------------------------------------------------------------------------- 1 | unverified()->create(); 7 | 8 | $this->assertFalse($user->hasVerifiedEmail()); 9 | 10 | $verificationUrl = URL::temporarySignedRoute( 11 | 'verification.verify', 12 | now()->addMinutes(60), 13 | ['id' => $user->id, 'hash' => sha1($user->email)] 14 | ); 15 | 16 | $this->actingAs($user)->get($verificationUrl); 17 | $this->assertTrue($user->fresh()->hasVerifiedEmail()); 18 | }); 19 | 20 | it('can not verify with invalid hash', function () { 21 | $user = User::factory()->unverified()->create(); 22 | 23 | $verificationUrl = URL::temporarySignedRoute( 24 | 'verification.verify', 25 | now()->addMinutes(60), 26 | ['id' => $user->id, 'hash' => sha1('wrong-email')] 27 | ); 28 | 29 | $this->actingAs($user)->get($verificationUrl); 30 | 31 | $this->assertFalse($user->fresh()->hasVerifiedEmail()); 32 | }); 33 | -------------------------------------------------------------------------------- /tests/Feature/Auth/LoginTest.php: -------------------------------------------------------------------------------- 1 | assertStatus(200); 10 | }); 11 | 12 | it('redirects authenticated user to home page when accessing login page', function () { 13 | $user = User::factory()->create(); 14 | 15 | $response = $this->actingAs($user)->get(route('login')); 16 | 17 | $response->assertStatus(302); 18 | $response->assertRedirect(route('home')); 19 | }); 20 | 21 | it('allows users to log in with valid credentials', function () { 22 | $user = User::factory()->create(); 23 | 24 | Livewire::test(Login::class) 25 | ->set('email', $user->email) 26 | ->set('password', 'password') 27 | ->call('login') 28 | ->assertRedirect(route('user.dashboard')) 29 | ->assertStatus(200); 30 | 31 | $this->assertAuthenticatedAs($user); 32 | }); 33 | 34 | it('does not allow users to log in with invalid credentials', function () { 35 | $user = User::factory()->create(); 36 | 37 | Livewire::test(Login::class) 38 | ->set('email', $user->email) 39 | ->set('password', 'wrong-password') 40 | ->call('login'); 41 | 42 | $this->assertGuest(); 43 | }); 44 | -------------------------------------------------------------------------------- /tests/Feature/Auth/Password/ConfirmTest.php: -------------------------------------------------------------------------------- 1 | assertStatus(200); 10 | }); 11 | 12 | it('can confirm with valid password', function () { 13 | $user = User::factory()->create(); 14 | 15 | $this->actingAs($user); 16 | 17 | Livewire::test(Confirm::class) 18 | ->set('password', 'password') 19 | ->call('confirmPassword') 20 | ->assertHasNoErrors(); 21 | }); 22 | 23 | it('can not confirm with invalid password', function () { 24 | $user = User::factory()->create(); 25 | 26 | $this->actingAs($user); 27 | 28 | Livewire::test(Confirm::class) 29 | ->set('password', 'wrong-password') 30 | ->call('confirmPassword') 31 | ->assertHasErrors(); 32 | }); 33 | -------------------------------------------------------------------------------- /tests/Feature/Auth/Password/RequestTest.php: -------------------------------------------------------------------------------- 1 | assertStatus(200); 10 | }); 11 | 12 | it('must enter an email address', function () { 13 | Livewire::test(Request::class) 14 | ->call('sendResetPasswordLink') 15 | ->assertHasErrors(['email' => 'required']); 16 | }); 17 | 18 | it('must enter a valid email address', function () { 19 | Livewire::test(Request::class) 20 | ->set('email', 'invalid-email') 21 | ->call('sendResetPasswordLink') 22 | ->assertHasErrors(['email' => 'email']); 23 | }); 24 | 25 | it('sent an email when provide valid email', function () { 26 | $user = User::factory()->create(); 27 | Livewire::test(Request::class) 28 | ->set('email', $user->email) 29 | ->call('sendResetPasswordLink') 30 | ->assertHasNoErrors(); 31 | 32 | $this->assertDatabaseHas('password_reset_tokens', ['email' => $user->email]); 33 | }); 34 | -------------------------------------------------------------------------------- /tests/Feature/Auth/Password/ResetTest.php: -------------------------------------------------------------------------------- 1 | create(); 10 | 11 | $token = Str::random(16); 12 | 13 | DB::table('password_reset_tokens')->insert([ 14 | 'email' => $user->email, 15 | 'token' => Hash::make($token), 16 | 'created_at' => Carbon::now(), 17 | ]); 18 | 19 | Livewire::test(Reset::class, ['token' => $token]) 20 | ->assertSuccessful() 21 | ->assertStatus(200); 22 | }); 23 | 24 | it('can reset password', function () { 25 | $user = User::factory()->create(); 26 | 27 | $token = Str::random(16); 28 | 29 | DB::table('password_reset_tokens')->insert([ 30 | 'email' => $user->email, 31 | 'token' => Hash::make($token), 32 | 'created_at' => Carbon::now(), 33 | ]); 34 | 35 | Livewire::test(Reset::class, ['token' => $token]) 36 | ->set('email', $user->email) 37 | ->set('password', 'secret-password') 38 | ->set('password_confirmation', 'secret-password') 39 | ->call('resetPassword') 40 | ->assertStatus(200); 41 | 42 | $this->assertTrue(Auth::attempt([ 43 | 'email' => $user->email, 44 | 'password' => 'secret-password', 45 | ])); 46 | }); 47 | 48 | it('require token', function () { 49 | Livewire::test(Reset::class, ['token' => null]) 50 | ->call('resetPassword') 51 | ->assertHasErrors(['token' => 'required']); 52 | }); 53 | 54 | it('require email', function () { 55 | Livewire::test(Reset::class, ['token' => Str::random(16)]) 56 | ->call('resetPassword') 57 | ->assertHasErrors(['email' => 'required']); 58 | }); 59 | 60 | it('require valid email', function () { 61 | Livewire::test(Reset::class, ['token' => Str::random(16)]) 62 | ->set('email', 'invalid-email') 63 | ->call('resetPassword') 64 | ->assertHasErrors(['email' => 'email']); 65 | }); 66 | 67 | it('require password', function () { 68 | Livewire::test(Reset::class, ['token' => Str::random(16)]) 69 | ->set('password', '') 70 | ->call('resetPassword') 71 | ->assertHasErrors(['password' => 'required']); 72 | }); 73 | 74 | it('require match password confirmation', function () { 75 | Livewire::test(Reset::class, ['token' => Str::random(16)]) 76 | ->set('password', 'new-password') 77 | ->set('password_confirmation', 'invalid-new-password') 78 | ->call('resetPassword') 79 | ->assertHasErrors(['password_confirmation' => 'same:password']); 80 | }); 81 | -------------------------------------------------------------------------------- /tests/Feature/Auth/PrivacyPolicyTest.php: -------------------------------------------------------------------------------- 1 | assertStatus(200); 8 | }); 9 | -------------------------------------------------------------------------------- /tests/Feature/Auth/RegisterTest.php: -------------------------------------------------------------------------------- 1 | assertStatus(200); 10 | }); 11 | 12 | it('redirect authenticated user to home page when accessing register page', function () { 13 | $user = User::factory()->create(); 14 | 15 | $response = $this->actingAs($user)->get(route('register')); 16 | 17 | $response->assertStatus(302); 18 | $response->assertRedirect(route('home')); 19 | }); 20 | 21 | it('registers a new user with valid data', function () { 22 | Artisan::call('migrate:fresh'); 23 | Livewire::test(Register::class) 24 | ->set('name', 'New User') 25 | ->set('email', 'newuser@mail.com') 26 | ->set('password', 'password') 27 | ->set('password_confirmation', 'password') 28 | ->set('terms_of_service_and_privacy_policy', true) 29 | ->call('register') 30 | ->assertHasNoErrors() 31 | ->assertStatus(200); 32 | 33 | $this->assertDatabaseHas('users', [ 34 | 'name' => 'New User', 35 | 'email' => 'newuser@mail.com', 36 | ]); 37 | }); 38 | 39 | it('can not registers a new user with invalid data', function () { 40 | Artisan::call('migrate:fresh'); 41 | Livewire::test(Register::class) 42 | ->set('name', 'New User') 43 | ->set('email', 'newuser@mail.com') 44 | ->set('password', 'pass') 45 | ->set('password_confirmation', 'wrongpassword') 46 | ->set('terms_of_service_and_privacy_policy', true) 47 | ->call('register') 48 | ->assertHasErrors('password', 'password_confirmation') 49 | ->assertStatus(200); 50 | 51 | $this->assertDatabaseMissing('users', [ 52 | 'name' => 'New User', 53 | 'email' => 'newuser@mail.com', 54 | ]); 55 | }); 56 | -------------------------------------------------------------------------------- /tests/Feature/Auth/TermsOfServiceTest.php: -------------------------------------------------------------------------------- 1 | assertStatus(200); 8 | }); 9 | -------------------------------------------------------------------------------- /tests/Feature/Auth/VerifyTest.php: -------------------------------------------------------------------------------- 1 | create( 11 | ['email_verified_at' => null] 12 | ); 13 | 14 | $this->actingAs($user); 15 | 16 | Livewire::test(Verify::class) 17 | ->assertStatus(200); 18 | }); 19 | 20 | it('can resend verification email', function () { 21 | $user = User::factory()->create([ 22 | 'email_verified_at' => null, 23 | ]); 24 | 25 | Livewire::actingAs($user) 26 | ->test(Verify::class) 27 | ->call('resend') 28 | ->assertStatus(200); 29 | }); 30 | 31 | it('can verify', function () { 32 | $user = User::factory()->create([ 33 | 'email_verified_at' => null, 34 | ]); 35 | 36 | Auth::login($user); 37 | 38 | $url = URL::temporarySignedRoute('verification.verify', Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)), [ 39 | 'id' => $user->getKey(), 40 | 'hash' => sha1($user->getEmailForVerification()), 41 | ]); 42 | 43 | Livewire::actingAs($user) 44 | ->test(Verify::class) 45 | ->call('resend', $url) 46 | ->assertStatus(200); 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | assertStatus(200); 8 | }); 9 | -------------------------------------------------------------------------------- /tests/Feature/HomeTest.php: -------------------------------------------------------------------------------- 1 | assertStatus(200); 9 | }); 10 | -------------------------------------------------------------------------------- /tests/Feature/Navigation/AdminNavigationTest.php: -------------------------------------------------------------------------------- 1 | create(); 8 | 9 | $this->actingAs($user); 10 | 11 | Livewire::test(AdminNavigation::class) 12 | ->assertStatus(200); 13 | }); 14 | 15 | it('can logout an user', function () { 16 | $user = User::factory()->create(); 17 | 18 | $this->be($user); 19 | 20 | Livewire::test(AdminNavigation::class) 21 | ->call('logout') 22 | ->assertRedirect(route('login')) 23 | ->assertStatus(200); 24 | }); 25 | 26 | it('can disable admin mode', function () { 27 | $user = User::factory()->create(); 28 | 29 | $this->be($user); 30 | 31 | Livewire::test(AdminNavigation::class) 32 | ->call('disableAdminMode') 33 | ->assertRedirect(route('user.dashboard')) 34 | ->assertStatus(200); 35 | }); 36 | -------------------------------------------------------------------------------- /tests/Feature/Navigation/AdminSidebarTest.php: -------------------------------------------------------------------------------- 1 | create(); 8 | 9 | $this->be($user); 10 | 11 | Livewire::test(AdminSidebar::class) 12 | ->assertStatus(200); 13 | }); 14 | 15 | it('can logout an user', function () { 16 | $user = User::factory()->create(); 17 | 18 | $this->be($user); 19 | 20 | Livewire::test(AdminSidebar::class) 21 | ->call('logout') 22 | ->assertRedirect(route('login')) 23 | ->assertStatus(200); 24 | }); 25 | 26 | it('can disable admin mode', function () { 27 | $user = User::factory()->create(); 28 | 29 | $this->be($user); 30 | 31 | Livewire::test(AdminSidebar::class) 32 | ->call('disableAdminMode') 33 | ->assertRedirect(route('user.dashboard')) 34 | ->assertStatus(200); 35 | }); 36 | -------------------------------------------------------------------------------- /tests/Feature/Navigation/AppNavigationTest.php: -------------------------------------------------------------------------------- 1 | create(); 8 | 9 | $this->be($user); 10 | 11 | Livewire::test(AppNavigation::class) 12 | ->assertStatus(200); 13 | }); 14 | 15 | it('can logout an user', function () { 16 | $user = User::factory()->create(); 17 | 18 | $this->be($user); 19 | 20 | Livewire::test(AppNavigation::class) 21 | ->call('logout') 22 | ->assertRedirect(route('login')) 23 | ->assertStatus(200); 24 | }); 25 | 26 | it('can disable admin mode', function () { 27 | $user = User::factory()->create(); 28 | 29 | $this->be($user); 30 | 31 | Livewire::test(AppNavigation::class) 32 | ->call('disableAdminMode') 33 | ->assertRedirect(route('user.dashboard')) 34 | ->assertStatus(200); 35 | }); 36 | -------------------------------------------------------------------------------- /tests/Feature/Navigation/MainNavigationTest.php: -------------------------------------------------------------------------------- 1 | assertStatus(200); 9 | }); 10 | 11 | it('can logout an user', function () { 12 | $user = User::factory()->create(); 13 | $this->be($user); 14 | 15 | Livewire::test(MainNavigation::class) 16 | ->call('logout') 17 | ->assertRedirect(route('login')) 18 | ->assertStatus(200); 19 | }); 20 | 21 | it('can disable admin mode', function () { 22 | $user = User::factory()->create(); 23 | 24 | $this->be($user); 25 | 26 | Livewire::test(MainNavigation::class) 27 | ->call('disableAdminMode') 28 | ->assertRedirect(route('user.dashboard')) 29 | ->assertStatus(200); 30 | }); 31 | -------------------------------------------------------------------------------- /tests/Feature/User/DashboardTest.php: -------------------------------------------------------------------------------- 1 | create(); 9 | 10 | $this->actingAs($user); 11 | 12 | Livewire::test(Dashboard::class) 13 | ->assertStatus(200); 14 | }); 15 | 16 | it('allows authenticated user to access user dashboard page', function () { 17 | $user = User::factory()->create(); 18 | 19 | $response = $this->actingAs($user)->get(route('user.dashboard')); 20 | 21 | $response->assertStatus(200); 22 | }); 23 | 24 | it('redirects unauthenticated user to login page when accessing user dashboard page', function () { 25 | $response = $this->get(route('user.dashboard')); 26 | 27 | $response->assertRedirect(route('login')); 28 | 29 | $response->assertStatus(302); 30 | }); 31 | -------------------------------------------------------------------------------- /tests/Feature/User/ExampleTest.php: -------------------------------------------------------------------------------- 1 | assertStatus(200); 8 | }); 9 | -------------------------------------------------------------------------------- /tests/Feature/User/SettingsTest.php: -------------------------------------------------------------------------------- 1 | create(); 8 | 9 | $this->actingAs($user); 10 | 11 | Livewire::test(Settings::class) 12 | ->assertStatus(200); 13 | }); 14 | 15 | it('can change languages', function () { 16 | $user = User::factory()->create(); 17 | 18 | $this->actingAs($user); 19 | 20 | Livewire::test(Settings::class) 21 | ->set('language', 'id') 22 | ->call('updateLanguage') 23 | ->assertHasNoErrors() 24 | ->assertStatus(200); 25 | }); 26 | 27 | it('only allows supported languages to be selected', function () { 28 | $user = User::factory()->create(); 29 | 30 | $this->actingAs($user); 31 | 32 | Livewire::test(Settings::class) 33 | ->set('language', 'ar') 34 | ->call('updateLanguage') 35 | ->assertHasErrors(['language' => 'in']) 36 | ->assertStatus(200); 37 | }); 38 | -------------------------------------------------------------------------------- /tests/Pest.php: -------------------------------------------------------------------------------- 1 | in('Feature'); 21 | 22 | /* 23 | |-------------------------------------------------------------------------- 24 | | Expectations 25 | |-------------------------------------------------------------------------- 26 | | 27 | | When you're writing tests, you often need to check that values meet certain conditions. The 28 | | "expect()" function gives you access to a set of "expectations" methods that you can use 29 | | to assert different things. Of course, you may extend the Expectation API at any time. 30 | | 31 | */ 32 | 33 | expect()->extend('toBeOne', function () { 34 | return $this->toBe(1); 35 | }); 36 | 37 | /* 38 | |-------------------------------------------------------------------------- 39 | | Functions 40 | |-------------------------------------------------------------------------- 41 | | 42 | | While Pest is very powerful out-of-the-box, you may have some testing code specific to your 43 | | project that you don't want to repeat in every file. Here you can also expose helpers as 44 | | global functions to help you to reduce the number of lines of code in your test files. 45 | | 46 | */ 47 | 48 | function something() 49 | { 50 | // .. 51 | } 52 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 |