├── .editorconfig ├── .env.example ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── _TODO.txt ├── app ├── Filament │ └── Resources │ │ ├── CategoryResource.php │ │ ├── CategoryResource │ │ └── Pages │ │ │ ├── CreateCategory.php │ │ │ ├── EditCategory.php │ │ │ ├── ListCategories.php │ │ │ └── ViewCategory.php │ │ ├── CustomerResource.php │ │ ├── CustomerResource │ │ └── Pages │ │ │ ├── CreateCustomer.php │ │ │ ├── EditCustomer.php │ │ │ ├── ListCustomers.php │ │ │ └── ViewCustomer.php │ │ ├── ProductResource.php │ │ ├── ProductResource │ │ └── Pages │ │ │ ├── CreateProduct.php │ │ │ ├── EditProduct.php │ │ │ ├── ListProducts.php │ │ │ └── ViewProduct.php │ │ ├── PurchaseResource.php │ │ ├── PurchaseResource │ │ └── Pages │ │ │ ├── CreatePurchase.php │ │ │ ├── EditPurchase.php │ │ │ ├── ListPurchases.php │ │ │ └── ViewPurchase.php │ │ ├── SaleResource.php │ │ └── SaleResource │ │ └── Pages │ │ ├── CreateSale.php │ │ ├── EditSale.php │ │ ├── ListSales.php │ │ └── ViewSale.php ├── Http │ ├── Controllers │ │ ├── Auth │ │ │ ├── AuthenticatedSessionController.php │ │ │ ├── ConfirmablePasswordController.php │ │ │ ├── EmailVerificationNotificationController.php │ │ │ ├── EmailVerificationPromptController.php │ │ │ ├── NewPasswordController.php │ │ │ ├── PasswordController.php │ │ │ ├── PasswordResetLinkController.php │ │ │ ├── RegisteredUserController.php │ │ │ └── VerifyEmailController.php │ │ ├── Controller.php │ │ ├── ProfileController.php │ │ └── _SiteController.php │ └── Requests │ │ ├── Auth │ │ └── LoginRequest.php │ │ └── ProfileUpdateRequest.php ├── Models │ ├── Category.php │ ├── Customer.php │ ├── Product.php │ ├── Purchase.php │ ├── PurchaseDetails.php │ ├── Sale.php │ ├── SaleDetails.php │ └── User.php ├── Providers │ ├── AppServiceProvider.php │ └── Filament │ │ └── AdminPanelProvider.php └── View │ └── Components │ ├── AppLayout.php │ └── GuestLayout.php ├── artisan ├── bootstrap ├── app.php ├── cache │ ├── blade-icons.php │ ├── filament │ │ └── panels │ │ │ └── admin.php │ ├── pac8E8F.tmp │ ├── packages.php │ └── services.php └── providers.php ├── composer.json ├── composer.lock ├── config ├── app.php ├── auth.php ├── cache.php ├── database.php ├── filament.php ├── filesystems.php ├── logging.php ├── mail.php ├── queue.php ├── services.php └── session.php ├── database ├── factories │ └── UserFactory.php ├── migrations │ ├── 0001_01_01_000000_create_users_table.php │ ├── 0001_01_01_000001_create_cache_table.php │ ├── 0001_01_01_000002_create_jobs_table.php │ ├── 2024_10_30_081232_create_categories_table.php │ ├── 2024_10_30_081239_create_products_table.php │ ├── 2024_10_30_081306_create_customers_table.php │ ├── 2024_10_30_081322_create_purchases_table.php │ ├── 2024_10_30_081328_create_purchase_details_table.php │ ├── 2024_10_30_081336_create_sales_table.php │ └── 2024_10_30_081342_create_sale_details_table.php └── seeders │ ├── DatabaseSeeder.php │ └── _InitalSeeder.php ├── lang ├── en.json ├── en │ ├── actions.php │ ├── auth.php │ ├── http-statuses.php │ ├── pagination.php │ ├── passwords.php │ └── validation.php ├── es.json └── es │ ├── actions.php │ ├── auth.php │ ├── http-statuses.php │ ├── pagination.php │ ├── passwords.php │ └── validation.php ├── package-lock.json ├── package.json ├── phpunit.xml ├── postcss.config.js ├── public ├── .htaccess ├── css │ └── filament │ │ ├── filament │ │ └── app.css │ │ ├── forms │ │ └── forms.css │ │ └── support │ │ └── support.css ├── favicon.ico ├── index.php ├── js │ └── filament │ │ ├── filament │ │ ├── app.js │ │ └── echo.js │ │ ├── forms │ │ └── components │ │ │ ├── color-picker.js │ │ │ ├── date-time-picker.js │ │ │ ├── file-upload.js │ │ │ ├── key-value.js │ │ │ ├── markdown-editor.js │ │ │ ├── rich-editor.js │ │ │ ├── select.js │ │ │ ├── tags-input.js │ │ │ └── textarea.js │ │ ├── notifications │ │ └── notifications.js │ │ ├── support │ │ ├── async-alpine.js │ │ └── support.js │ │ ├── tables │ │ └── components │ │ │ └── table.js │ │ └── widgets │ │ └── components │ │ ├── chart.js │ │ └── stats-overview │ │ └── stat │ │ └── chart.js └── robots.txt ├── resources ├── css │ └── app.css ├── js │ ├── app.js │ └── bootstrap.js └── views │ ├── auth │ ├── confirm-password.blade.php │ ├── forgot-password.blade.php │ ├── login.blade.php │ ├── register.blade.php │ ├── reset-password.blade.php │ └── verify-email.blade.php │ ├── components │ ├── application-logo.blade.php │ ├── auth-session-status.blade.php │ ├── danger-button.blade.php │ ├── dropdown-link.blade.php │ ├── dropdown.blade.php │ ├── input-error.blade.php │ ├── input-label.blade.php │ ├── modal.blade.php │ ├── nav-link.blade.php │ ├── primary-button.blade.php │ ├── responsive-nav-link.blade.php │ ├── secondary-button.blade.php │ └── text-input.blade.php │ ├── dashboard.blade.php │ ├── index.blade.php │ ├── layouts │ ├── app.blade.php │ ├── guest.blade.php │ └── navigation.blade.php │ └── profile │ ├── edit.blade.php │ └── partials │ ├── delete-user-form.blade.php │ ├── update-password-form.blade.php │ └── update-profile-information-form.blade.php ├── routes ├── auth.php ├── console.php └── web.php ├── storage ├── app │ └── public │ │ └── products │ │ └── 01JBFGWYW9257R6094PGA99P3W.png └── logs │ └── laravel.log ├── tailwind.config.js ├── tests ├── Feature │ ├── Auth │ │ ├── AuthenticationTest.php │ │ ├── EmailVerificationTest.php │ │ ├── PasswordConfirmationTest.php │ │ ├── PasswordResetTest.php │ │ ├── PasswordUpdateTest.php │ │ └── RegistrationTest.php │ ├── ExampleTest.php │ └── ProfileTest.php ├── Pest.php ├── TestCase.php └── Unit │ └── ExampleTest.php └── 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=Laravel 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_TIMEZONE=UTC 6 | APP_URL=http://localhost 7 | 8 | APP_LOCALE=en 9 | APP_FALLBACK_LOCALE=en 10 | APP_FAKER_LOCALE=en_US 11 | 12 | APP_MAINTENANCE_DRIVER=file 13 | # APP_MAINTENANCE_STORE=database 14 | 15 | PHP_CLI_SERVER_WORKERS=4 16 | 17 | BCRYPT_ROUNDS=12 18 | 19 | LOG_CHANNEL=stack 20 | LOG_STACK=single 21 | LOG_DEPRECATIONS_CHANNEL=null 22 | LOG_LEVEL=debug 23 | 24 | DB_CONNECTION=mysql 25 | DB_HOST=127.0.0.1 26 | DB_PORT=3306 27 | DB_DATABASE=minipos 28 | DB_USERNAME=root 29 | DB_PASSWORD= 30 | 31 | SESSION_DRIVER=database 32 | SESSION_LIFETIME=120 33 | SESSION_ENCRYPT=false 34 | SESSION_PATH=/ 35 | SESSION_DOMAIN=null 36 | 37 | BROADCAST_CONNECTION=log 38 | FILESYSTEM_DISK=local 39 | QUEUE_CONNECTION=database 40 | 41 | CACHE_STORE=database 42 | CACHE_PREFIX= 43 | 44 | MEMCACHED_HOST=127.0.0.1 45 | 46 | REDIS_CLIENT=phpredis 47 | REDIS_HOST=127.0.0.1 48 | REDIS_PASSWORD=null 49 | REDIS_PORT=6379 50 | 51 | MAIL_MAILER=log 52 | MAIL_HOST=127.0.0.1 53 | MAIL_PORT=2525 54 | MAIL_USERNAME=null 55 | MAIL_PASSWORD=null 56 | MAIL_ENCRYPTION=null 57 | MAIL_FROM_ADDRESS="hello@example.com" 58 | MAIL_FROM_NAME="${APP_NAME}" 59 | 60 | AWS_ACCESS_KEY_ID= 61 | AWS_SECRET_ACCESS_KEY= 62 | AWS_DEFAULT_REGION=us-east-1 63 | AWS_BUCKET= 64 | AWS_USE_PATH_STYLE_ENDPOINT=false 65 | 66 | VITE_APP_NAME="${APP_NAME}" 67 | -------------------------------------------------------------------------------- /.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 | /storage/pail 8 | /vendor 9 | .env 10 | .env.backup 11 | .env.production 12 | .phpactor.json 13 | .phpunit.result.cache 14 | Homestead.json 15 | Homestead.yaml 16 | auth.json 17 | npm-debug.log 18 | yarn-error.log 19 | /.fleet 20 | /.idea 21 | /.vscode 22 | /.zed 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 JEBC - DeV 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # MiniPOS - Sistema de Punto de Venta 3 | 4 | Este es un sistema MiniPOS desarrollado en **Laravel 11** y **Filament PHP 3** con una base de datos **MySQL**. Diseñado para gestionar de forma eficaz categorías de productos, inventario, clientes, y ventas en un entorno de punto de venta. 5 | 6 | ## Características Principales 7 | 8 | ### Módulos: 9 | 10 | - **Categorías:** Gestión de categorías de productos para una organización ordenada y ágil. 11 | - **Productos:** Administración de productos, permitiendo agregar, editar, y eliminar productos con detalles completos de cada uno. 12 | - **Clientes:** Registro y gestión de clientes, mejorando el seguimiento de ventas y personalización de servicios. 13 | - **Compras y Detalles de Compras:** Registro detallado de compras realizadas, con desglose de productos y precios por unidad y cantidad. 14 | - **Ventas y Detalles de Ventas:** Seguimiento detallado de las ventas, incluidas las especificaciones de productos, precios y cantidades. 15 | 16 | ### Requisitos Previos 17 | 18 | 1. **Base de Datos MySQL:** Configurar la base de datos y las credenciales en el archivo `.env`. 19 | 2. **Link de almacenamiento:** Ejecute `php artisan storage:link` para crear el enlace simbólico al almacenamiento de archivos públicos. 20 | 3. **Configuración de Archivos Públicos:** Asegurarse de que `FILESYSTEM_DISK` esté configurado como `public` en el archivo `.env`. 21 | 22 | ### Instalación 23 | 24 | 1. **Clona el repositorio** 25 | ```bash 26 | git clone https://github.com/jebcdev/FilamentMiniPOS.git 27 | cd MiniPOS 28 | ``` 29 | 30 | 2. **Instala las dependencias** 31 | ```bash 32 | composer install 33 | ``` 34 | ```bash 35 | npm install 36 | ``` 37 | 38 | 3. **Configura el archivo `.env`** 39 | ```bash 40 | php artisan key:generate 41 | ``` 42 | Muy Importante Configurar el: 43 | ```bash 44 | APP_URL=http://minipos.test 45 | ``` 46 | Cambialo por tu URL 47 | 48 | 4. **Migraciones y Seeders** 49 | Ejecuta las migraciones para crear la estructura de la base de datos: 50 | ```bash 51 | php artisan migrate 52 | php artisan db:seed 53 | ``` 54 | 55 | 5. **Storage Link** 56 | Ejecuta el siguiente comando para crear el 57 | enlace simbolico para que funcione la 58 | visualizacion de las imágenes: 59 | ```bash 60 | php artisan storage:link --force 61 | ``` 62 | 63 | 6. **Inicia el servidor de desarrollo** 64 | ```bash 65 | php artisan serve 66 | ``` 67 | O accede a tu URL 68 | 69 | ### Créditos 70 | 71 | Desarrollado por { JEBC-Dev } en **Laravel 11** y **Filament PHP 3**. -------------------------------------------------------------------------------- /_TODO.txt: -------------------------------------------------------------------------------- 1 | - Category => Done 2 | - Product => Done 3 | - Customer => Done 4 | 5 | - Purchase => Done 6 | - PurchaseDetails => Done 7 | 8 | - Sale => Done 9 | - SaleDetails => Done 10 | 11 | 12 | 13 | php artisan storage:link 14 | 15 | FILESYSTEM_DISK=public -------------------------------------------------------------------------------- /app/Filament/Resources/CategoryResource.php: -------------------------------------------------------------------------------- 1 | schema([ 45 | Forms\Components\TextInput::make('name') 46 | ->label(__('Name')) 47 | ->required() 48 | ->maxLength(200), 49 | 50 | Forms\Components\MarkdownEditor::make('description') 51 | ->label(__('Description')) 52 | ->columnSpanFull(), 53 | 54 | 55 | ]); 56 | } 57 | 58 | public static function table(Table $table): Table 59 | { 60 | return $table 61 | ->columns([ 62 | Tables\Columns\TextColumn::make('name') 63 | ->label(__('Name')) 64 | ->searchable()->sortable(), 65 | 66 | Tables\Columns\TextColumn::make('description') 67 | ->label(__('Description')) 68 | ->searchable()->sortable(), 69 | 70 | 71 | Tables\Columns\TextColumn::make('created_at') 72 | ->dateTime() 73 | ->searchable()->sortable() 74 | ->toggleable(isToggledHiddenByDefault: true), 75 | 76 | 77 | Tables\Columns\TextColumn::make('updated_at') 78 | ->dateTime() 79 | ->searchable()->sortable() 80 | ->toggleable(isToggledHiddenByDefault: true), 81 | 82 | 83 | Tables\Columns\TextColumn::make('deleted_at') 84 | ->dateTime() 85 | ->searchable()->sortable() 86 | ->toggleable(isToggledHiddenByDefault: true), 87 | 88 | 89 | ]) 90 | ->filters([ 91 | Tables\Filters\TrashedFilter::make(), 92 | ]) 93 | ->actions( 94 | [ 95 | 96 | Tables\Actions\ActionGroup::make([ 97 | Tables\Actions\EditAction::make(), 98 | Tables\Actions\DeleteAction::make(), 99 | Tables\Actions\ViewAction::make(), 100 | ]), 101 | 102 | ], 103 | position: Tables\Enums\ActionsPosition::BeforeColumns 104 | ) 105 | ->bulkActions([ 106 | Tables\Actions\BulkActionGroup::make([ 107 | Tables\Actions\DeleteBulkAction::make(), 108 | Tables\Actions\ForceDeleteBulkAction::make(), 109 | Tables\Actions\RestoreBulkAction::make(), 110 | 111 | 112 | ]), 113 | 114 | 115 | ]); 116 | } 117 | 118 | public static function getRelations(): array 119 | { 120 | return [ 121 | // 122 | ]; 123 | } 124 | 125 | public static function getPages(): array 126 | { 127 | return [ 128 | 'index' => Pages\ListCategories::route('/'), 129 | 130 | 131 | 'create' => Pages\CreateCategory::route('/create'), 132 | 133 | 134 | 'view' => Pages\ViewCategory::route('/{record}'), 135 | 136 | 137 | 'edit' => Pages\EditCategory::route('/{record}/edit'), 138 | 139 | 140 | ]; 141 | } 142 | 143 | public static function getEloquentQuery(): Builder 144 | { 145 | return parent::getEloquentQuery() 146 | ->withoutGlobalScopes([ 147 | SoftDeletingScope::class, 148 | ]); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /app/Filament/Resources/CategoryResource/Pages/CreateCategory.php: -------------------------------------------------------------------------------- 1 | authenticate(); 28 | 29 | $request->session()->regenerate(); 30 | 31 | return redirect()->intended(route('dashboard', absolute: false)); 32 | } 33 | 34 | /** 35 | * Destroy an authenticated session. 36 | */ 37 | public function destroy(Request $request): RedirectResponse 38 | { 39 | Auth::guard('web')->logout(); 40 | 41 | $request->session()->invalidate(); 42 | 43 | $request->session()->regenerateToken(); 44 | 45 | return redirect('/'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ConfirmablePasswordController.php: -------------------------------------------------------------------------------- 1 | validate([ 28 | 'email' => $request->user()->email, 29 | 'password' => $request->password, 30 | ])) { 31 | throw ValidationException::withMessages([ 32 | 'password' => __('auth.password'), 33 | ]); 34 | } 35 | 36 | $request->session()->put('auth.password_confirmed_at', time()); 37 | 38 | return redirect()->intended(route('dashboard', absolute: false)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/EmailVerificationNotificationController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail()) { 17 | return redirect()->intended(route('dashboard', absolute: false)); 18 | } 19 | 20 | $request->user()->sendEmailVerificationNotification(); 21 | 22 | return back()->with('status', 'verification-link-sent'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/EmailVerificationPromptController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail() 18 | ? redirect()->intended(route('dashboard', absolute: false)) 19 | : view('auth.verify-email'); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/NewPasswordController.php: -------------------------------------------------------------------------------- 1 | $request]); 23 | } 24 | 25 | /** 26 | * Handle an incoming new password request. 27 | * 28 | * @throws \Illuminate\Validation\ValidationException 29 | */ 30 | public function store(Request $request): RedirectResponse 31 | { 32 | $request->validate([ 33 | 'token' => ['required'], 34 | 'email' => ['required', 'email'], 35 | 'password' => ['required', 'confirmed', Rules\Password::defaults()], 36 | ]); 37 | 38 | // Here we will attempt to reset the user's password. If it is successful we 39 | // will update the password on an actual user model and persist it to the 40 | // database. Otherwise we will parse the error and return the response. 41 | $status = Password::reset( 42 | $request->only('email', 'password', 'password_confirmation', 'token'), 43 | function ($user) use ($request) { 44 | $user->forceFill([ 45 | 'password' => Hash::make($request->password), 46 | 'remember_token' => Str::random(60), 47 | ])->save(); 48 | 49 | event(new PasswordReset($user)); 50 | } 51 | ); 52 | 53 | // If the password was successfully reset, we will redirect the user back to 54 | // the application's home authenticated view. If there is an error we can 55 | // redirect them back to where they came from with their error message. 56 | return $status == Password::PASSWORD_RESET 57 | ? redirect()->route('login')->with('status', __($status)) 58 | : back()->withInput($request->only('email')) 59 | ->withErrors(['email' => __($status)]); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/PasswordController.php: -------------------------------------------------------------------------------- 1 | validateWithBag('updatePassword', [ 19 | 'current_password' => ['required', 'current_password'], 20 | 'password' => ['required', Password::defaults(), 'confirmed'], 21 | ]); 22 | 23 | $request->user()->update([ 24 | 'password' => Hash::make($validated['password']), 25 | ]); 26 | 27 | return back()->with('status', 'password-updated'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/PasswordResetLinkController.php: -------------------------------------------------------------------------------- 1 | validate([ 29 | 'email' => ['required', 'email'], 30 | ]); 31 | 32 | // We will send the password reset link to this user. Once we have attempted 33 | // to send the link, we will examine the response then see the message we 34 | // need to show to the user. Finally, we'll send out a proper response. 35 | $status = Password::sendResetLink( 36 | $request->only('email') 37 | ); 38 | 39 | return $status == Password::RESET_LINK_SENT 40 | ? back()->with('status', __($status)) 41 | : back()->withInput($request->only('email')) 42 | ->withErrors(['email' => __($status)]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/RegisteredUserController.php: -------------------------------------------------------------------------------- 1 | validate([ 33 | 'name' => ['required', 'string', 'max:255'], 34 | 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], 35 | 'password' => ['required', 'confirmed', Rules\Password::defaults()], 36 | ]); 37 | 38 | $user = User::create([ 39 | 'name' => $request->name, 40 | 'email' => $request->email, 41 | 'password' => Hash::make($request->password), 42 | ]); 43 | 44 | event(new Registered($user)); 45 | 46 | Auth::login($user); 47 | 48 | return redirect(route('dashboard', absolute: false)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/VerifyEmailController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail()) { 18 | return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); 19 | } 20 | 21 | if ($request->user()->markEmailAsVerified()) { 22 | event(new Verified($request->user())); 23 | } 24 | 25 | return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | $request->user(), 21 | ]); 22 | } 23 | 24 | /** 25 | * Update the user's profile information. 26 | */ 27 | public function update(ProfileUpdateRequest $request): RedirectResponse 28 | { 29 | $request->user()->fill($request->validated()); 30 | 31 | if ($request->user()->isDirty('email')) { 32 | $request->user()->email_verified_at = null; 33 | } 34 | 35 | $request->user()->save(); 36 | 37 | return Redirect::route('profile.edit')->with('status', 'profile-updated'); 38 | } 39 | 40 | /** 41 | * Delete the user's account. 42 | */ 43 | public function destroy(Request $request): RedirectResponse 44 | { 45 | $request->validateWithBag('userDeletion', [ 46 | 'password' => ['required', 'current_password'], 47 | ]); 48 | 49 | $user = $request->user(); 50 | 51 | Auth::logout(); 52 | 53 | $user->delete(); 54 | 55 | $request->session()->invalidate(); 56 | $request->session()->regenerateToken(); 57 | 58 | return Redirect::to('/'); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/Http/Controllers/_SiteController.php: -------------------------------------------------------------------------------- 1 | json([ 16 | 'purchase' => $purchase, 17 | 'total' => $purchase->total, // Accedemos al accesor 'total' como propiedad 18 | ]); */ 19 | 20 | 21 | 22 | return to_route('filament.admin.pages.dashboard'); 23 | } 24 | 25 | public function dashboard() 26 | { 27 | return view('dashboard'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Http/Requests/Auth/LoginRequest.php: -------------------------------------------------------------------------------- 1 | 26 | */ 27 | public function rules(): array 28 | { 29 | return [ 30 | 'email' => ['required', 'string', 'email'], 31 | 'password' => ['required', 'string'], 32 | ]; 33 | } 34 | 35 | /** 36 | * Attempt to authenticate the request's credentials. 37 | * 38 | * @throws \Illuminate\Validation\ValidationException 39 | */ 40 | public function authenticate(): void 41 | { 42 | $this->ensureIsNotRateLimited(); 43 | 44 | if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) { 45 | RateLimiter::hit($this->throttleKey()); 46 | 47 | throw ValidationException::withMessages([ 48 | 'email' => trans('auth.failed'), 49 | ]); 50 | } 51 | 52 | RateLimiter::clear($this->throttleKey()); 53 | } 54 | 55 | /** 56 | * Ensure the login request is not rate limited. 57 | * 58 | * @throws \Illuminate\Validation\ValidationException 59 | */ 60 | public function ensureIsNotRateLimited(): void 61 | { 62 | if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { 63 | return; 64 | } 65 | 66 | event(new Lockout($this)); 67 | 68 | $seconds = RateLimiter::availableIn($this->throttleKey()); 69 | 70 | throw ValidationException::withMessages([ 71 | 'email' => trans('auth.throttle', [ 72 | 'seconds' => $seconds, 73 | 'minutes' => ceil($seconds / 60), 74 | ]), 75 | ]); 76 | } 77 | 78 | /** 79 | * Get the rate limiting throttle key for the request. 80 | */ 81 | public function throttleKey(): string 82 | { 83 | return Str::transliterate(Str::lower($this->string('email')).'|'.$this->ip()); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/Http/Requests/ProfileUpdateRequest.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | public function rules(): array 17 | { 18 | return [ 19 | 'name' => ['required', 'string', 'max:255'], 20 | 'email' => [ 21 | 'required', 22 | 'string', 23 | 'lowercase', 24 | 'email', 25 | 'max:255', 26 | Rule::unique(User::class)->ignore($this->user()->id), 27 | ], 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Models/Category.php: -------------------------------------------------------------------------------- 1 | hasMany(Product::class); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Models/Customer.php: -------------------------------------------------------------------------------- 1 | hasMany(Sale::class); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Models/Product.php: -------------------------------------------------------------------------------- 1 | 'array', 29 | ]; 30 | 31 | public function category():BelongsTo 32 | { 33 | return $this->belongsTo(Category::class); 34 | } 35 | 36 | public function purchaseDetails():HasMany 37 | { 38 | return $this->hasMany(PurchaseDetails::class); 39 | } 40 | 41 | public function saleDetails():HasMany 42 | { 43 | return $this->hasMany(SaleDetails::class); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Models/Purchase.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class); 25 | } 26 | 27 | public function purchaseDetails(): HasMany 28 | { 29 | return $this->hasMany(PurchaseDetails::class); 30 | } 31 | 32 | // Método para calcular el total de la compra 33 | public function getTotalAttribute(): float 34 | { 35 | // Sumar el total de cada detalle de la compra 36 | return $this->purchaseDetails->sum(fn($detail) => ($detail->quantity * $detail->purchase_price)); 37 | } 38 | 39 | protected static function boot() 40 | { 41 | parent::boot(); 42 | 43 | static::creating(function ($purchase) { 44 | if (!$purchase->user_id) { 45 | $purchase->user_id = Auth::id() ?? 1; 46 | } 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/Models/PurchaseDetails.php: -------------------------------------------------------------------------------- 1 | belongsTo(Purchase::class); 24 | } 25 | 26 | public function product(): BelongsTo 27 | { 28 | return $this->belongsTo(Product::class); 29 | } 30 | 31 | public function getTotalAttribute():float 32 | { 33 | return $this->quantity * $this->purchase_price; 34 | } 35 | 36 | protected static function booted(): void 37 | { 38 | static::creating(function ($purchaseDetails) { 39 | 40 | $product = $purchaseDetails->product; // Obtener el producto 41 | $product->stock += $purchaseDetails->quantity; // Sumar la cantidad de productos comprados al stock 42 | $product->save(); // Guardar el cambio en el producto 43 | 44 | 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/Models/Sale.php: -------------------------------------------------------------------------------- 1 | 'date', 24 | ]; 25 | 26 | public function user(): BelongsTo 27 | { 28 | return $this->belongsTo(User::class); 29 | } 30 | 31 | public function saleDetails(): HasMany 32 | { 33 | return $this->hasMany(SaleDetails::class); 34 | } 35 | 36 | // Método para calcular el total de la compra 37 | public function getTotalAttribute(): float 38 | { 39 | // Sumar el total de cada detalle de la compra 40 | return $this->saleDetails->sum(fn($detail) => ($detail->quantity * $detail->sale_price)); 41 | } 42 | 43 | protected static function boot() 44 | { 45 | parent::boot(); 46 | 47 | static::creating(function ($sale) { 48 | if (!$sale->user_id) { 49 | $sale->user_id = Auth::id() ?? 1; 50 | } 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/Models/SaleDetails.php: -------------------------------------------------------------------------------- 1 | belongsTo(Sale::class); 23 | } 24 | 25 | public function product():BelongsTo 26 | { 27 | return $this->belongsTo(Product::class); 28 | } 29 | 30 | public function getTotalAttribute():float 31 | { 32 | return $this->quantity * $this->sale_price; 33 | } 34 | 35 | protected static function booted(): void 36 | { 37 | static::creating(function ($saleDetails) { 38 | 39 | $product = $saleDetails->product; // Obtener el producto 40 | $product->stock -= $saleDetails->quantity; // Restar la cantidad de productos vendidos al stock 41 | $product->save(); // Guardar el cambio en el producto 42 | 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Models/User.php: -------------------------------------------------------------------------------- 1 | */ 15 | use HasFactory, Notifiable, SoftDeletes; 16 | 17 | /** 18 | * The attributes that are mass assignable. 19 | * 20 | * @var array 21 | */ 22 | protected $fillable = [ 23 | 'name', 24 | 'email', 25 | 'password', 26 | ]; 27 | 28 | /** 29 | * The attributes that should be hidden for serialization. 30 | * 31 | * @var array 32 | */ 33 | protected $hidden = [ 34 | 'password', 35 | 'remember_token', 36 | ]; 37 | 38 | /** 39 | * Get the attributes that should be cast. 40 | * 41 | * @return array 42 | */ 43 | protected function casts(): array 44 | { 45 | return [ 46 | 'email_verified_at' => 'datetime', 47 | 'password' => 'hashed', 48 | ]; 49 | } 50 | 51 | public function purchases(): HasMany 52 | { 53 | return $this->hasMany(Purchase::class); 54 | } 55 | 56 | public function sales(): HasMany 57 | { 58 | return $this->hasMany(Sale::class); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | default() 30 | ->id('admin') 31 | ->path('admin') 32 | ->login() 33 | ->colors([ 34 | 'primary' => Color::Cyan, 35 | ]) 36 | /* */ 37 | ->font('Poppins') 38 | ->favicon(asset('assets/img/logo.jpg')) 39 | ->darkMode(true) 40 | ->sidebarCollapsibleOnDesktop() 41 | 42 | // links de navegación en el menú 43 | ->navigationItems([ 44 | NavigationItem::make('WhatsApp') 45 | ->url('https://wa.me/573052850514', shouldOpenInNewTab: true) 46 | ->icon('heroicon-o-chat-bubble-bottom-center-text') 47 | ->group(__('External Links')) 48 | ->visible(function (): bool { 49 | return Auth::check(); 50 | }) 51 | ->sort(99), 52 | ]) 53 | 54 | ->userMenuItems([ //para el menu de usuario en la parte superior derecha 55 | MenuItem::make() 56 | ->label(__('Profile')) 57 | ->url('/profile') 58 | ->icon('heroicon-o-cog-6-tooth') 59 | ]) 60 | /* */ 61 | ->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources') 62 | ->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages') 63 | ->pages([ 64 | Pages\Dashboard::class, 65 | ]) 66 | ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets') 67 | ->widgets([ 68 | Widgets\AccountWidget::class, 69 | // Widgets\FilamentInfoWidget::class, 70 | ]) 71 | ->middleware([ 72 | EncryptCookies::class, 73 | AddQueuedCookiesToResponse::class, 74 | StartSession::class, 75 | AuthenticateSession::class, 76 | ShareErrorsFromSession::class, 77 | VerifyCsrfToken::class, 78 | SubstituteBindings::class, 79 | DisableBladeIconComponents::class, 80 | DispatchServingFilamentEvent::class, 81 | ]) 82 | ->authMiddleware([ 83 | Authenticate::class, 84 | ]); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/View/Components/AppLayout.php: -------------------------------------------------------------------------------- 1 | handleCommand(new ArgvInput); 14 | 15 | exit($status); 16 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | withRouting( 9 | web: __DIR__.'/../routes/web.php', 10 | commands: __DIR__.'/../routes/console.php', 11 | health: '/up', 12 | ) 13 | ->withMiddleware(function (Middleware $middleware) { 14 | // 15 | }) 16 | ->withExceptions(function (Exceptions $exceptions) { 17 | // 18 | })->create(); 19 | -------------------------------------------------------------------------------- /bootstrap/cache/filament/panels/admin.php: -------------------------------------------------------------------------------- 1 | 3 | array ( 4 | 'app.filament.resources.category-resource.pages.create-category' => 'App\\Filament\\Resources\\CategoryResource\\Pages\\CreateCategory', 5 | 'app.filament.resources.category-resource.pages.edit-category' => 'App\\Filament\\Resources\\CategoryResource\\Pages\\EditCategory', 6 | 'app.filament.resources.category-resource.pages.list-categories' => 'App\\Filament\\Resources\\CategoryResource\\Pages\\ListCategories', 7 | 'app.filament.resources.category-resource.pages.view-category' => 'App\\Filament\\Resources\\CategoryResource\\Pages\\ViewCategory', 8 | 'app.filament.resources.customer-resource.pages.create-customer' => 'App\\Filament\\Resources\\CustomerResource\\Pages\\CreateCustomer', 9 | 'app.filament.resources.customer-resource.pages.edit-customer' => 'App\\Filament\\Resources\\CustomerResource\\Pages\\EditCustomer', 10 | 'app.filament.resources.customer-resource.pages.list-customers' => 'App\\Filament\\Resources\\CustomerResource\\Pages\\ListCustomers', 11 | 'app.filament.resources.customer-resource.pages.view-customer' => 'App\\Filament\\Resources\\CustomerResource\\Pages\\ViewCustomer', 12 | 'app.filament.resources.product-resource.pages.create-product' => 'App\\Filament\\Resources\\ProductResource\\Pages\\CreateProduct', 13 | 'app.filament.resources.product-resource.pages.edit-product' => 'App\\Filament\\Resources\\ProductResource\\Pages\\EditProduct', 14 | 'app.filament.resources.product-resource.pages.list-products' => 'App\\Filament\\Resources\\ProductResource\\Pages\\ListProducts', 15 | 'app.filament.resources.product-resource.pages.view-product' => 'App\\Filament\\Resources\\ProductResource\\Pages\\ViewProduct', 16 | 'app.filament.resources.purchase-resource.pages.create-purchase' => 'App\\Filament\\Resources\\PurchaseResource\\Pages\\CreatePurchase', 17 | 'app.filament.resources.purchase-resource.pages.edit-purchase' => 'App\\Filament\\Resources\\PurchaseResource\\Pages\\EditPurchase', 18 | 'app.filament.resources.purchase-resource.pages.list-purchases' => 'App\\Filament\\Resources\\PurchaseResource\\Pages\\ListPurchases', 19 | 'app.filament.resources.purchase-resource.pages.view-purchase' => 'App\\Filament\\Resources\\PurchaseResource\\Pages\\ViewPurchase', 20 | 'app.filament.resources.sale-resource.pages.create-sale' => 'App\\Filament\\Resources\\SaleResource\\Pages\\CreateSale', 21 | 'app.filament.resources.sale-resource.pages.edit-sale' => 'App\\Filament\\Resources\\SaleResource\\Pages\\EditSale', 22 | 'app.filament.resources.sale-resource.pages.list-sales' => 'App\\Filament\\Resources\\SaleResource\\Pages\\ListSales', 23 | 'app.filament.resources.sale-resource.pages.view-sale' => 'App\\Filament\\Resources\\SaleResource\\Pages\\ViewSale', 24 | 'filament.pages.dashboard' => 'Filament\\Pages\\Dashboard', 25 | 'filament.widgets.account-widget' => 'Filament\\Widgets\\AccountWidget', 26 | 'filament.livewire.database-notifications' => 'Filament\\Livewire\\DatabaseNotifications', 27 | 'filament.pages.auth.edit-profile' => 'Filament\\Pages\\Auth\\EditProfile', 28 | 'filament.livewire.global-search' => 'Filament\\Livewire\\GlobalSearch', 29 | 'filament.livewire.notifications' => 'Filament\\Livewire\\Notifications', 30 | 'filament.pages.auth.login' => 'Filament\\Pages\\Auth\\Login', 31 | ), 32 | 'clusters' => 33 | array ( 34 | ), 35 | 'clusteredComponents' => 36 | array ( 37 | ), 38 | 'clusterDirectories' => 39 | array ( 40 | ), 41 | 'clusterNamespaces' => 42 | array ( 43 | ), 44 | 'pages' => 45 | array ( 46 | 0 => 'Filament\\Pages\\Dashboard', 47 | ), 48 | 'pageDirectories' => 49 | array ( 50 | 0 => 'D:\\laragon\\www\\MiniPOS\\app\\Filament/Pages', 51 | ), 52 | 'pageNamespaces' => 53 | array ( 54 | 0 => 'App\\Filament\\Pages', 55 | ), 56 | 'resources' => 57 | array ( 58 | 'D:\\laragon\\www\\MiniPOS\\app\\Filament\\Resources\\CategoryResource.php' => 'App\\Filament\\Resources\\CategoryResource', 59 | 'D:\\laragon\\www\\MiniPOS\\app\\Filament\\Resources\\CustomerResource.php' => 'App\\Filament\\Resources\\CustomerResource', 60 | 'D:\\laragon\\www\\MiniPOS\\app\\Filament\\Resources\\ProductResource.php' => 'App\\Filament\\Resources\\ProductResource', 61 | 'D:\\laragon\\www\\MiniPOS\\app\\Filament\\Resources\\PurchaseResource.php' => 'App\\Filament\\Resources\\PurchaseResource', 62 | 'D:\\laragon\\www\\MiniPOS\\app\\Filament\\Resources\\SaleResource.php' => 'App\\Filament\\Resources\\SaleResource', 63 | ), 64 | 'resourceDirectories' => 65 | array ( 66 | 0 => 'D:\\laragon\\www\\MiniPOS\\app\\Filament/Resources', 67 | ), 68 | 'resourceNamespaces' => 69 | array ( 70 | 0 => 'App\\Filament\\Resources', 71 | ), 72 | 'widgets' => 73 | array ( 74 | 0 => 'Filament\\Widgets\\AccountWidget', 75 | ), 76 | 'widgetDirectories' => 77 | array ( 78 | 0 => 'D:\\laragon\\www\\MiniPOS\\app\\Filament/Widgets', 79 | ), 80 | 'widgetNamespaces' => 81 | array ( 82 | 0 => 'App\\Filament\\Widgets', 83 | ), 84 | ); -------------------------------------------------------------------------------- /bootstrap/cache/pac8E8F.tmp: -------------------------------------------------------------------------------- 1 | 3 | array ( 4 | 'aliases' => 5 | array ( 6 | 'EloquentSerialize' => 'AnourValar\\EloquentSerialize\\Facades\\EloquentSerializeFacade', 7 | ), 8 | ), 9 | 'blade-ui-kit/blade-heroicons' => 10 | array ( 11 | 'providers' => 12 | array ( 13 | 0 => 'BladeUI\\Heroicons\\BladeHeroiconsServiceProvider', 14 | ), 15 | ), 16 | 'blade-ui-kit/blade-icons' => 17 | array ( 18 | 'providers' => 19 | array ( 20 | 0 => 'BladeUI\\Icons\\BladeIconsServiceProvider', 21 | ), 22 | ), 23 | 'filament/actions' => 24 | array ( 25 | 'providers' => 26 | array ( 27 | 0 => 'Filament\\Actions\\ActionsServiceProvider', 28 | ), 29 | ), 30 | 'filament/filament' => 31 | array ( 32 | 'providers' => 33 | array ( 34 | 0 => 'Filament\\FilamentServiceProvider', 35 | ), 36 | ), 37 | 'filament/forms' => 38 | array ( 39 | 'providers' => 40 | array ( 41 | 0 => 'Filament\\Forms\\FormsServiceProvider', 42 | ), 43 | ), 44 | 'filament/infolists' => 45 | array ( 46 | 'providers' => 47 | array ( 48 | 0 => 'Filament\\Infolists\\InfolistsServiceProvider', 49 | ), 50 | ), 51 | 'filament/notifications' => 52 | array ( 53 | 'providers' => 54 | array ( 55 | 0 => 'Filament\\Notifications\\NotificationsServiceProvider', 56 | ), 57 | ), 58 | 'filament/support' => 59 | array ( 60 | 'providers' => 61 | array ( 62 | 0 => 'Filament\\Support\\SupportServiceProvider', 63 | ), 64 | ), 65 | 'filament/tables' => 66 | array ( 67 | 'providers' => 68 | array ( 69 | 0 => 'Filament\\Tables\\TablesServiceProvider', 70 | ), 71 | ), 72 | 'filament/widgets' => 73 | array ( 74 | 'providers' => 75 | array ( 76 | 0 => 'Filament\\Widgets\\WidgetsServiceProvider', 77 | ), 78 | ), 79 | 'kirschbaum-development/eloquent-power-joins' => 80 | array ( 81 | 'providers' => 82 | array ( 83 | 0 => 'Kirschbaum\\PowerJoins\\PowerJoinsServiceProvider', 84 | ), 85 | ), 86 | 'laravel-lang/actions' => 87 | array ( 88 | 'providers' => 89 | array ( 90 | 0 => 'LaravelLang\\Actions\\ServiceProvider', 91 | ), 92 | ), 93 | 'laravel-lang/attributes' => 94 | array ( 95 | 'providers' => 96 | array ( 97 | 0 => 'LaravelLang\\Attributes\\ServiceProvider', 98 | ), 99 | ), 100 | 'laravel-lang/config' => 101 | array ( 102 | 'providers' => 103 | array ( 104 | 0 => 'LaravelLang\\Config\\ServiceProvider', 105 | ), 106 | ), 107 | 'laravel-lang/http-statuses' => 108 | array ( 109 | 'providers' => 110 | array ( 111 | 0 => 'LaravelLang\\HttpStatuses\\ServiceProvider', 112 | ), 113 | ), 114 | 'laravel-lang/lang' => 115 | array ( 116 | 'providers' => 117 | array ( 118 | 0 => 'LaravelLang\\Lang\\ServiceProvider', 119 | ), 120 | ), 121 | 'laravel-lang/locales' => 122 | array ( 123 | 'providers' => 124 | array ( 125 | 0 => 'LaravelLang\\Locales\\ServiceProvider', 126 | ), 127 | ), 128 | 'laravel-lang/models' => 129 | array ( 130 | 'providers' => 131 | array ( 132 | 0 => 'LaravelLang\\Models\\ServiceProvider', 133 | ), 134 | ), 135 | 'laravel-lang/publisher' => 136 | array ( 137 | 'providers' => 138 | array ( 139 | 0 => 'LaravelLang\\Publisher\\ServiceProvider', 140 | ), 141 | ), 142 | 'laravel-lang/routes' => 143 | array ( 144 | 'providers' => 145 | array ( 146 | 0 => 'LaravelLang\\Routes\\ServiceProvider', 147 | ), 148 | ), 149 | 'laravel/breeze' => 150 | array ( 151 | 'providers' => 152 | array ( 153 | 0 => 'Laravel\\Breeze\\BreezeServiceProvider', 154 | ), 155 | ), 156 | 'laravel/pail' => 157 | array ( 158 | 'providers' => 159 | array ( 160 | 0 => 'Laravel\\Pail\\PailServiceProvider', 161 | ), 162 | ), 163 | 'laravel/sail' => 164 | array ( 165 | 'providers' => 166 | array ( 167 | 0 => 'Laravel\\Sail\\SailServiceProvider', 168 | ), 169 | ), 170 | 'laravel/tinker' => 171 | array ( 172 | 'providers' => 173 | array ( 174 | 0 => 'Laravel\\Tinker\\TinkerServiceProvider', 175 | ), 176 | ), 177 | 'livewire/livewire' => 178 | array ( 179 | 'providers' => 180 | array ( 181 | 0 => 'Livewire\\LivewireServiceProvider', 182 | ), 183 | 'aliases' => 184 | array ( 185 | 'Livewire' => 'Livewire\\Livewire', 186 | ), 187 | ), 188 | 'nesbot/carbon' => 189 | array ( 190 | 'providers' => 191 | array ( 192 | 0 => 'Carbon\\Laravel\\ServiceProvider', 193 | ), 194 | ), 195 | 'nunomaduro/collision' => 196 | array ( 197 | 'providers' => 198 | array ( 199 | 0 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider', 200 | ), 201 | ), 202 | 'nunomaduro/termwind' => 203 | array ( 204 | 'providers' => 205 | array ( 206 | 0 => 'Termwind\\Laravel\\TermwindServiceProvider', 207 | ), 208 | ), 209 | 'pestphp/pest-plugin-laravel' => 210 | array ( 211 | 'providers' => 212 | array ( 213 | 0 => 'Pest\\Laravel\\PestServiceProvider', 214 | ), 215 | ), 216 | 'ryangjchandler/blade-capture-directive' => 217 | array ( 218 | 'providers' => 219 | array ( 220 | 0 => 'RyanChandler\\BladeCaptureDirective\\BladeCaptureDirectiveServiceProvider', 221 | ), 222 | 'aliases' => 223 | array ( 224 | 'BladeCaptureDirective' => 'RyanChandler\\BladeCaptureDirective\\Facades\\BladeCaptureDirective', 225 | ), 226 | ), 227 | ); -------------------------------------------------------------------------------- /bootstrap/cache/packages.php: -------------------------------------------------------------------------------- 1 | 3 | array ( 4 | 'aliases' => 5 | array ( 6 | 'EloquentSerialize' => 'AnourValar\\EloquentSerialize\\Facades\\EloquentSerializeFacade', 7 | ), 8 | ), 9 | 'blade-ui-kit/blade-heroicons' => 10 | array ( 11 | 'providers' => 12 | array ( 13 | 0 => 'BladeUI\\Heroicons\\BladeHeroiconsServiceProvider', 14 | ), 15 | ), 16 | 'blade-ui-kit/blade-icons' => 17 | array ( 18 | 'providers' => 19 | array ( 20 | 0 => 'BladeUI\\Icons\\BladeIconsServiceProvider', 21 | ), 22 | ), 23 | 'filament/actions' => 24 | array ( 25 | 'providers' => 26 | array ( 27 | 0 => 'Filament\\Actions\\ActionsServiceProvider', 28 | ), 29 | ), 30 | 'filament/filament' => 31 | array ( 32 | 'providers' => 33 | array ( 34 | 0 => 'Filament\\FilamentServiceProvider', 35 | ), 36 | ), 37 | 'filament/forms' => 38 | array ( 39 | 'providers' => 40 | array ( 41 | 0 => 'Filament\\Forms\\FormsServiceProvider', 42 | ), 43 | ), 44 | 'filament/infolists' => 45 | array ( 46 | 'providers' => 47 | array ( 48 | 0 => 'Filament\\Infolists\\InfolistsServiceProvider', 49 | ), 50 | ), 51 | 'filament/notifications' => 52 | array ( 53 | 'providers' => 54 | array ( 55 | 0 => 'Filament\\Notifications\\NotificationsServiceProvider', 56 | ), 57 | ), 58 | 'filament/support' => 59 | array ( 60 | 'providers' => 61 | array ( 62 | 0 => 'Filament\\Support\\SupportServiceProvider', 63 | ), 64 | ), 65 | 'filament/tables' => 66 | array ( 67 | 'providers' => 68 | array ( 69 | 0 => 'Filament\\Tables\\TablesServiceProvider', 70 | ), 71 | ), 72 | 'filament/widgets' => 73 | array ( 74 | 'providers' => 75 | array ( 76 | 0 => 'Filament\\Widgets\\WidgetsServiceProvider', 77 | ), 78 | ), 79 | 'kirschbaum-development/eloquent-power-joins' => 80 | array ( 81 | 'providers' => 82 | array ( 83 | 0 => 'Kirschbaum\\PowerJoins\\PowerJoinsServiceProvider', 84 | ), 85 | ), 86 | 'laravel-lang/actions' => 87 | array ( 88 | 'providers' => 89 | array ( 90 | 0 => 'LaravelLang\\Actions\\ServiceProvider', 91 | ), 92 | ), 93 | 'laravel-lang/attributes' => 94 | array ( 95 | 'providers' => 96 | array ( 97 | 0 => 'LaravelLang\\Attributes\\ServiceProvider', 98 | ), 99 | ), 100 | 'laravel-lang/config' => 101 | array ( 102 | 'providers' => 103 | array ( 104 | 0 => 'LaravelLang\\Config\\ServiceProvider', 105 | ), 106 | ), 107 | 'laravel-lang/http-statuses' => 108 | array ( 109 | 'providers' => 110 | array ( 111 | 0 => 'LaravelLang\\HttpStatuses\\ServiceProvider', 112 | ), 113 | ), 114 | 'laravel-lang/lang' => 115 | array ( 116 | 'providers' => 117 | array ( 118 | 0 => 'LaravelLang\\Lang\\ServiceProvider', 119 | ), 120 | ), 121 | 'laravel-lang/locales' => 122 | array ( 123 | 'providers' => 124 | array ( 125 | 0 => 'LaravelLang\\Locales\\ServiceProvider', 126 | ), 127 | ), 128 | 'laravel-lang/models' => 129 | array ( 130 | 'providers' => 131 | array ( 132 | 0 => 'LaravelLang\\Models\\ServiceProvider', 133 | ), 134 | ), 135 | 'laravel-lang/publisher' => 136 | array ( 137 | 'providers' => 138 | array ( 139 | 0 => 'LaravelLang\\Publisher\\ServiceProvider', 140 | ), 141 | ), 142 | 'laravel-lang/routes' => 143 | array ( 144 | 'providers' => 145 | array ( 146 | 0 => 'LaravelLang\\Routes\\ServiceProvider', 147 | ), 148 | ), 149 | 'laravel/breeze' => 150 | array ( 151 | 'providers' => 152 | array ( 153 | 0 => 'Laravel\\Breeze\\BreezeServiceProvider', 154 | ), 155 | ), 156 | 'laravel/pail' => 157 | array ( 158 | 'providers' => 159 | array ( 160 | 0 => 'Laravel\\Pail\\PailServiceProvider', 161 | ), 162 | ), 163 | 'laravel/sail' => 164 | array ( 165 | 'providers' => 166 | array ( 167 | 0 => 'Laravel\\Sail\\SailServiceProvider', 168 | ), 169 | ), 170 | 'laravel/tinker' => 171 | array ( 172 | 'providers' => 173 | array ( 174 | 0 => 'Laravel\\Tinker\\TinkerServiceProvider', 175 | ), 176 | ), 177 | 'livewire/livewire' => 178 | array ( 179 | 'providers' => 180 | array ( 181 | 0 => 'Livewire\\LivewireServiceProvider', 182 | ), 183 | 'aliases' => 184 | array ( 185 | 'Livewire' => 'Livewire\\Livewire', 186 | ), 187 | ), 188 | 'nesbot/carbon' => 189 | array ( 190 | 'providers' => 191 | array ( 192 | 0 => 'Carbon\\Laravel\\ServiceProvider', 193 | ), 194 | ), 195 | 'nunomaduro/collision' => 196 | array ( 197 | 'providers' => 198 | array ( 199 | 0 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider', 200 | ), 201 | ), 202 | 'nunomaduro/termwind' => 203 | array ( 204 | 'providers' => 205 | array ( 206 | 0 => 'Termwind\\Laravel\\TermwindServiceProvider', 207 | ), 208 | ), 209 | 'pestphp/pest-plugin-laravel' => 210 | array ( 211 | 'providers' => 212 | array ( 213 | 0 => 'Pest\\Laravel\\PestServiceProvider', 214 | ), 215 | ), 216 | 'ryangjchandler/blade-capture-directive' => 217 | array ( 218 | 'providers' => 219 | array ( 220 | 0 => 'RyanChandler\\BladeCaptureDirective\\BladeCaptureDirectiveServiceProvider', 221 | ), 222 | 'aliases' => 223 | array ( 224 | 'BladeCaptureDirective' => 'RyanChandler\\BladeCaptureDirective\\Facades\\BladeCaptureDirective', 225 | ), 226 | ), 227 | ); -------------------------------------------------------------------------------- /bootstrap/providers.php: -------------------------------------------------------------------------------- 1 | env('APP_NAME', 'Mini POS'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Application Environment 21 | |-------------------------------------------------------------------------- 22 | | 23 | | This value determines the "environment" your application is currently 24 | | running in. This may determine how you prefer to configure various 25 | | services the application utilizes. Set this in your ".env" file. 26 | | 27 | */ 28 | 29 | 'env' => env('APP_ENV', 'local'), 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Application Debug Mode 34 | |-------------------------------------------------------------------------- 35 | | 36 | | When your application is in debug mode, detailed error messages with 37 | | stack traces will be shown on every error that occurs within your 38 | | application. If disabled, a simple generic error page is shown. 39 | | 40 | */ 41 | 42 | 'debug' => (bool) env('APP_DEBUG', true), 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Application URL 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This URL is used by the console to properly generate URLs when using 50 | | the Artisan command line tool. You should set this to the root of 51 | | the application so that it's available within Artisan commands. 52 | | 53 | */ 54 | 55 | 'url' => env('APP_URL', 'http://minipos.test'), 56 | 57 | /* 58 | |-------------------------------------------------------------------------- 59 | | Application Timezone 60 | |-------------------------------------------------------------------------- 61 | | 62 | | Here you may specify the default timezone for your application, which 63 | | will be used by the PHP date and date-time functions. The timezone 64 | | is set to "UTC" by default as it is suitable for most use cases. 65 | | 66 | */ 67 | 68 | 'timezone' => env('APP_TIMEZONE', 'America/Bogota'), 69 | 70 | /* 71 | |-------------------------------------------------------------------------- 72 | | Application Locale Configuration 73 | |-------------------------------------------------------------------------- 74 | | 75 | | The application locale determines the default locale that will be used 76 | | by Laravel's translation / localization methods. This option can be 77 | | set to any locale for which you plan to have translation strings. 78 | | 79 | */ 80 | 81 | 'locale' => env('APP_LOCALE', 'es'), 82 | 83 | 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), 84 | 85 | 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), 86 | 87 | /* 88 | |-------------------------------------------------------------------------- 89 | | Encryption Key 90 | |-------------------------------------------------------------------------- 91 | | 92 | | This key is utilized by Laravel's encryption services and should be set 93 | | to a random, 32 character string to ensure that all encrypted values 94 | | are secure. You should do this prior to deploying the application. 95 | | 96 | */ 97 | 98 | 'cipher' => 'AES-256-CBC', 99 | 100 | 'key' => env('APP_KEY'), 101 | 102 | 'previous_keys' => [ 103 | ...array_filter( 104 | explode(',', env('APP_PREVIOUS_KEYS', '')) 105 | ), 106 | ], 107 | 108 | /* 109 | |-------------------------------------------------------------------------- 110 | | Maintenance Mode Driver 111 | |-------------------------------------------------------------------------- 112 | | 113 | | These configuration options determine the driver used to determine and 114 | | manage Laravel's "maintenance mode" status. The "cache" driver will 115 | | allow maintenance mode to be controlled across multiple machines. 116 | | 117 | | Supported drivers: "file", "cache" 118 | | 119 | */ 120 | 121 | 'maintenance' => [ 122 | 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), 123 | 'store' => env('APP_MAINTENANCE_STORE', 'database'), 124 | ], 125 | 126 | ]; 127 | -------------------------------------------------------------------------------- /config/auth.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'guard' => env('AUTH_GUARD', 'web'), 18 | 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), 19 | ], 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Authentication Guards 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Next, you may define every authentication guard for your application. 27 | | Of course, a great default configuration has been defined for you 28 | | which utilizes session storage plus the Eloquent user provider. 29 | | 30 | | All authentication guards have a user provider, which defines how the 31 | | users are actually retrieved out of your database or other storage 32 | | system used by the application. Typically, Eloquent is utilized. 33 | | 34 | | Supported: "session" 35 | | 36 | */ 37 | 38 | 'guards' => [ 39 | 'web' => [ 40 | 'driver' => 'session', 41 | 'provider' => 'users', 42 | ], 43 | ], 44 | 45 | /* 46 | |-------------------------------------------------------------------------- 47 | | User Providers 48 | |-------------------------------------------------------------------------- 49 | | 50 | | All authentication guards have a user provider, which defines how the 51 | | users are actually retrieved out of your database or other storage 52 | | system used by the application. Typically, Eloquent is utilized. 53 | | 54 | | If you have multiple user tables or models you may configure multiple 55 | | providers to represent the model / table. These providers may then 56 | | be assigned to any extra authentication guards you have defined. 57 | | 58 | | Supported: "database", "eloquent" 59 | | 60 | */ 61 | 62 | 'providers' => [ 63 | 'users' => [ 64 | 'driver' => 'eloquent', 65 | 'model' => env('AUTH_MODEL', App\Models\User::class), 66 | ], 67 | 68 | // 'users' => [ 69 | // 'driver' => 'database', 70 | // 'table' => 'users', 71 | // ], 72 | ], 73 | 74 | /* 75 | |-------------------------------------------------------------------------- 76 | | Resetting Passwords 77 | |-------------------------------------------------------------------------- 78 | | 79 | | These configuration options specify the behavior of Laravel's password 80 | | reset functionality, including the table utilized for token storage 81 | | and the user provider that is invoked to actually retrieve users. 82 | | 83 | | The expiry time is the number of minutes that each reset token will be 84 | | considered valid. This security feature keeps tokens short-lived so 85 | | they have less time to be guessed. You may change this as needed. 86 | | 87 | | The throttle setting is the number of seconds a user must wait before 88 | | generating more password reset tokens. This prevents the user from 89 | | quickly generating a very large amount of password reset tokens. 90 | | 91 | */ 92 | 93 | 'passwords' => [ 94 | 'users' => [ 95 | 'provider' => 'users', 96 | 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), 97 | 'expire' => 60, 98 | 'throttle' => 60, 99 | ], 100 | ], 101 | 102 | /* 103 | |-------------------------------------------------------------------------- 104 | | Password Confirmation Timeout 105 | |-------------------------------------------------------------------------- 106 | | 107 | | Here you may define the amount of seconds before a password confirmation 108 | | window expires and users are asked to re-enter their password via the 109 | | confirmation screen. By default, the timeout lasts for three hours. 110 | | 111 | */ 112 | 113 | 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), 114 | 115 | ]; 116 | -------------------------------------------------------------------------------- /config/cache.php: -------------------------------------------------------------------------------- 1 | env('CACHE_STORE', 'database'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Cache Stores 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the cache "stores" for your application as 26 | | well as their drivers. You may even define multiple stores for the 27 | | same cache driver to group types of items stored in your caches. 28 | | 29 | | Supported drivers: "array", "database", "file", "memcached", 30 | | "redis", "dynamodb", "octane", "null" 31 | | 32 | */ 33 | 34 | 'stores' => [ 35 | 36 | 'array' => [ 37 | 'driver' => 'array', 38 | 'serialize' => false, 39 | ], 40 | 41 | 'database' => [ 42 | 'driver' => 'database', 43 | 'connection' => env('DB_CACHE_CONNECTION'), 44 | 'table' => env('DB_CACHE_TABLE', 'cache'), 45 | 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), 46 | 'lock_table' => env('DB_CACHE_LOCK_TABLE'), 47 | ], 48 | 49 | 'file' => [ 50 | 'driver' => 'file', 51 | 'path' => storage_path('framework/cache/data'), 52 | 'lock_path' => storage_path('framework/cache/data'), 53 | ], 54 | 55 | 'memcached' => [ 56 | 'driver' => 'memcached', 57 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), 58 | 'sasl' => [ 59 | env('MEMCACHED_USERNAME'), 60 | env('MEMCACHED_PASSWORD'), 61 | ], 62 | 'options' => [ 63 | // Memcached::OPT_CONNECT_TIMEOUT => 2000, 64 | ], 65 | 'servers' => [ 66 | [ 67 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 68 | 'port' => env('MEMCACHED_PORT', 11211), 69 | 'weight' => 100, 70 | ], 71 | ], 72 | ], 73 | 74 | 'redis' => [ 75 | 'driver' => 'redis', 76 | 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), 77 | 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), 78 | ], 79 | 80 | 'dynamodb' => [ 81 | 'driver' => 'dynamodb', 82 | 'key' => env('AWS_ACCESS_KEY_ID'), 83 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 84 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 85 | 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), 86 | 'endpoint' => env('DYNAMODB_ENDPOINT'), 87 | ], 88 | 89 | 'octane' => [ 90 | 'driver' => 'octane', 91 | ], 92 | 93 | ], 94 | 95 | /* 96 | |-------------------------------------------------------------------------- 97 | | Cache Key Prefix 98 | |-------------------------------------------------------------------------- 99 | | 100 | | When utilizing the APC, database, memcached, Redis, and DynamoDB cache 101 | | stores, there might be other applications using the same cache. For 102 | | that reason, you may prefix every cache key to avoid collisions. 103 | | 104 | */ 105 | 106 | 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'), 107 | 108 | ]; 109 | -------------------------------------------------------------------------------- /config/filament.php: -------------------------------------------------------------------------------- 1 | [ 18 | 19 | // 'echo' => [ 20 | // 'broadcaster' => 'pusher', 21 | // 'key' => env('VITE_PUSHER_APP_KEY'), 22 | // 'cluster' => env('VITE_PUSHER_APP_CLUSTER'), 23 | // 'wsHost' => env('VITE_PUSHER_HOST'), 24 | // 'wsPort' => env('VITE_PUSHER_PORT'), 25 | // 'wssPort' => env('VITE_PUSHER_PORT'), 26 | // 'authEndpoint' => '/broadcasting/auth', 27 | // 'disableStats' => true, 28 | // 'encrypted' => true, 29 | // 'forceTLS' => true, 30 | // ], 31 | 32 | ], 33 | 34 | /* 35 | |-------------------------------------------------------------------------- 36 | | Default Filesystem Disk 37 | |-------------------------------------------------------------------------- 38 | | 39 | | This is the storage disk Filament will use to store files. You may use 40 | | any of the disks defined in the `config/filesystems.php`. 41 | | 42 | */ 43 | 44 | 'default_filesystem_disk' => env('FILAMENT_FILESYSTEM_DISK', 'public'), 45 | 46 | /* 47 | |-------------------------------------------------------------------------- 48 | | Assets Path 49 | |-------------------------------------------------------------------------- 50 | | 51 | | This is the directory where Filament's assets will be published to. It 52 | | is relative to the `public` directory of your Laravel application. 53 | | 54 | | After changing the path, you should run `php artisan filament:assets`. 55 | | 56 | */ 57 | 58 | 'assets_path' => null, 59 | 60 | /* 61 | |-------------------------------------------------------------------------- 62 | | Cache Path 63 | |-------------------------------------------------------------------------- 64 | | 65 | | This is the directory that Filament will use to store cache files that 66 | | are used to optimize the registration of components. 67 | | 68 | | After changing the path, you should run `php artisan filament:cache-components`. 69 | | 70 | */ 71 | 72 | 'cache_path' => base_path('bootstrap/cache/filament'), 73 | 74 | /* 75 | |-------------------------------------------------------------------------- 76 | | Livewire Loading Delay 77 | |-------------------------------------------------------------------------- 78 | | 79 | | This sets the delay before loading indicators appear. 80 | | 81 | | Setting this to 'none' makes indicators appear immediately, which can be 82 | | desirable for high-latency connections. Setting it to 'default' applies 83 | | Livewire's standard 200ms delay. 84 | | 85 | */ 86 | 87 | 'livewire_loading_delay' => 'default', 88 | 89 | ]; 90 | -------------------------------------------------------------------------------- /config/filesystems.php: -------------------------------------------------------------------------------- 1 | env('FILESYSTEM_DISK', 'local'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Filesystem Disks 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Below you may configure as many filesystem disks as necessary, and you 24 | | may even configure multiple disks for the same driver. Examples for 25 | | most supported storage drivers are configured here for reference. 26 | | 27 | | Supported drivers: "local", "ftp", "sftp", "s3" 28 | | 29 | */ 30 | 31 | 'disks' => [ 32 | 33 | 'local' => [ 34 | 'driver' => 'local', 35 | 'root' => storage_path('app/private'), 36 | 'serve' => true, 37 | 'throw' => false, 38 | ], 39 | 40 | 'public' => [ 41 | 'driver' => 'local', 42 | 'root' => storage_path('app/public'), 43 | 'url' => env('APP_URL').'/storage', 44 | 'visibility' => 'public', 45 | 'throw' => false, 46 | ], 47 | 48 | 's3' => [ 49 | 'driver' => 's3', 50 | 'key' => env('AWS_ACCESS_KEY_ID'), 51 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 52 | 'region' => env('AWS_DEFAULT_REGION'), 53 | 'bucket' => env('AWS_BUCKET'), 54 | 'url' => env('AWS_URL'), 55 | 'endpoint' => env('AWS_ENDPOINT'), 56 | 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), 57 | 'throw' => false, 58 | ], 59 | 60 | ], 61 | 62 | /* 63 | |-------------------------------------------------------------------------- 64 | | Symbolic Links 65 | |-------------------------------------------------------------------------- 66 | | 67 | | Here you may configure the symbolic links that will be created when the 68 | | `storage:link` Artisan command is executed. The array keys should be 69 | | the locations of the links and the values should be their targets. 70 | | 71 | */ 72 | 73 | 'links' => [ 74 | public_path('storage') => storage_path('app/public'), 75 | ], 76 | 77 | ]; 78 | -------------------------------------------------------------------------------- /config/logging.php: -------------------------------------------------------------------------------- 1 | env('LOG_CHANNEL', 'stack'), 22 | 23 | /* 24 | |-------------------------------------------------------------------------- 25 | | Deprecations Log Channel 26 | |-------------------------------------------------------------------------- 27 | | 28 | | This option controls the log channel that should be used to log warnings 29 | | regarding deprecated PHP and library features. This allows you to get 30 | | your application ready for upcoming major versions of dependencies. 31 | | 32 | */ 33 | 34 | 'deprecations' => [ 35 | 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), 36 | 'trace' => env('LOG_DEPRECATIONS_TRACE', false), 37 | ], 38 | 39 | /* 40 | |-------------------------------------------------------------------------- 41 | | Log Channels 42 | |-------------------------------------------------------------------------- 43 | | 44 | | Here you may configure the log channels for your application. Laravel 45 | | utilizes the Monolog PHP logging library, which includes a variety 46 | | of powerful log handlers and formatters that you're free to use. 47 | | 48 | | Available drivers: "single", "daily", "slack", "syslog", 49 | | "errorlog", "monolog", "custom", "stack" 50 | | 51 | */ 52 | 53 | 'channels' => [ 54 | 55 | 'stack' => [ 56 | 'driver' => 'stack', 57 | 'channels' => explode(',', env('LOG_STACK', 'single')), 58 | 'ignore_exceptions' => false, 59 | ], 60 | 61 | 'single' => [ 62 | 'driver' => 'single', 63 | 'path' => storage_path('logs/laravel.log'), 64 | 'level' => env('LOG_LEVEL', 'debug'), 65 | 'replace_placeholders' => true, 66 | ], 67 | 68 | 'daily' => [ 69 | 'driver' => 'daily', 70 | 'path' => storage_path('logs/laravel.log'), 71 | 'level' => env('LOG_LEVEL', 'debug'), 72 | 'days' => env('LOG_DAILY_DAYS', 14), 73 | 'replace_placeholders' => true, 74 | ], 75 | 76 | 'slack' => [ 77 | 'driver' => 'slack', 78 | 'url' => env('LOG_SLACK_WEBHOOK_URL'), 79 | 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), 80 | 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), 81 | 'level' => env('LOG_LEVEL', 'critical'), 82 | 'replace_placeholders' => true, 83 | ], 84 | 85 | 'papertrail' => [ 86 | 'driver' => 'monolog', 87 | 'level' => env('LOG_LEVEL', 'debug'), 88 | 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), 89 | 'handler_with' => [ 90 | 'host' => env('PAPERTRAIL_URL'), 91 | 'port' => env('PAPERTRAIL_PORT'), 92 | 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), 93 | ], 94 | 'processors' => [PsrLogMessageProcessor::class], 95 | ], 96 | 97 | 'stderr' => [ 98 | 'driver' => 'monolog', 99 | 'level' => env('LOG_LEVEL', 'debug'), 100 | 'handler' => StreamHandler::class, 101 | 'formatter' => env('LOG_STDERR_FORMATTER'), 102 | 'with' => [ 103 | 'stream' => 'php://stderr', 104 | ], 105 | 'processors' => [PsrLogMessageProcessor::class], 106 | ], 107 | 108 | 'syslog' => [ 109 | 'driver' => 'syslog', 110 | 'level' => env('LOG_LEVEL', 'debug'), 111 | 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), 112 | 'replace_placeholders' => true, 113 | ], 114 | 115 | 'errorlog' => [ 116 | 'driver' => 'errorlog', 117 | 'level' => env('LOG_LEVEL', 'debug'), 118 | 'replace_placeholders' => true, 119 | ], 120 | 121 | 'null' => [ 122 | 'driver' => 'monolog', 123 | 'handler' => NullHandler::class, 124 | ], 125 | 126 | 'emergency' => [ 127 | 'path' => storage_path('logs/laravel.log'), 128 | ], 129 | 130 | ], 131 | 132 | ]; 133 | -------------------------------------------------------------------------------- /config/mail.php: -------------------------------------------------------------------------------- 1 | env('MAIL_MAILER', 'log'), 18 | 19 | /* 20 | |-------------------------------------------------------------------------- 21 | | Mailer Configurations 22 | |-------------------------------------------------------------------------- 23 | | 24 | | Here you may configure all of the mailers used by your application plus 25 | | their respective settings. Several examples have been configured for 26 | | you and you are free to add your own as your application requires. 27 | | 28 | | Laravel supports a variety of mail "transport" drivers that can be used 29 | | when delivering an email. You may specify which one you're using for 30 | | your mailers below. You may also add additional mailers if needed. 31 | | 32 | | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", 33 | | "postmark", "resend", "log", "array", 34 | | "failover", "roundrobin" 35 | | 36 | */ 37 | 38 | 'mailers' => [ 39 | 40 | 'smtp' => [ 41 | 'transport' => 'smtp', 42 | 'url' => env('MAIL_URL'), 43 | 'host' => env('MAIL_HOST', '127.0.0.1'), 44 | 'port' => env('MAIL_PORT', 2525), 45 | 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 46 | 'username' => env('MAIL_USERNAME'), 47 | 'password' => env('MAIL_PASSWORD'), 48 | 'timeout' => null, 49 | 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url(env('APP_URL', 'http://localhost'), PHP_URL_HOST)), 50 | ], 51 | 52 | 'ses' => [ 53 | 'transport' => 'ses', 54 | ], 55 | 56 | 'postmark' => [ 57 | 'transport' => 'postmark', 58 | // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), 59 | // 'client' => [ 60 | // 'timeout' => 5, 61 | // ], 62 | ], 63 | 64 | 'resend' => [ 65 | 'transport' => 'resend', 66 | ], 67 | 68 | 'sendmail' => [ 69 | 'transport' => 'sendmail', 70 | 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), 71 | ], 72 | 73 | 'log' => [ 74 | 'transport' => 'log', 75 | 'channel' => env('MAIL_LOG_CHANNEL'), 76 | ], 77 | 78 | 'array' => [ 79 | 'transport' => 'array', 80 | ], 81 | 82 | 'failover' => [ 83 | 'transport' => 'failover', 84 | 'mailers' => [ 85 | 'smtp', 86 | 'log', 87 | ], 88 | ], 89 | 90 | 'roundrobin' => [ 91 | 'transport' => 'roundrobin', 92 | 'mailers' => [ 93 | 'ses', 94 | 'postmark', 95 | ], 96 | ], 97 | 98 | ], 99 | 100 | /* 101 | |-------------------------------------------------------------------------- 102 | | Global "From" Address 103 | |-------------------------------------------------------------------------- 104 | | 105 | | You may wish for all emails sent by your application to be sent from 106 | | the same address. Here you may specify a name and address that is 107 | | used globally for all emails that are sent by your application. 108 | | 109 | */ 110 | 111 | 'from' => [ 112 | 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), 113 | 'name' => env('MAIL_FROM_NAME', 'Example'), 114 | ], 115 | 116 | ]; 117 | -------------------------------------------------------------------------------- /config/queue.php: -------------------------------------------------------------------------------- 1 | env('QUEUE_CONNECTION', 'database'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Queue Connections 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may configure the connection options for every queue backend 24 | | used by your application. An example configuration is provided for 25 | | each backend supported by Laravel. You're also free to add more. 26 | | 27 | | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'sync' => [ 34 | 'driver' => 'sync', 35 | ], 36 | 37 | 'database' => [ 38 | 'driver' => 'database', 39 | 'connection' => env('DB_QUEUE_CONNECTION'), 40 | 'table' => env('DB_QUEUE_TABLE', 'jobs'), 41 | 'queue' => env('DB_QUEUE', 'default'), 42 | 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90), 43 | 'after_commit' => false, 44 | ], 45 | 46 | 'beanstalkd' => [ 47 | 'driver' => 'beanstalkd', 48 | 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), 49 | 'queue' => env('BEANSTALKD_QUEUE', 'default'), 50 | 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), 51 | 'block_for' => 0, 52 | 'after_commit' => false, 53 | ], 54 | 55 | 'sqs' => [ 56 | 'driver' => 'sqs', 57 | 'key' => env('AWS_ACCESS_KEY_ID'), 58 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 59 | 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), 60 | 'queue' => env('SQS_QUEUE', 'default'), 61 | 'suffix' => env('SQS_SUFFIX'), 62 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 63 | 'after_commit' => false, 64 | ], 65 | 66 | 'redis' => [ 67 | 'driver' => 'redis', 68 | 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), 69 | 'queue' => env('REDIS_QUEUE', 'default'), 70 | 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), 71 | 'block_for' => null, 72 | 'after_commit' => false, 73 | ], 74 | 75 | ], 76 | 77 | /* 78 | |-------------------------------------------------------------------------- 79 | | Job Batching 80 | |-------------------------------------------------------------------------- 81 | | 82 | | The following options configure the database and table that store job 83 | | batching information. These options can be updated to any database 84 | | connection and table which has been defined by your application. 85 | | 86 | */ 87 | 88 | 'batching' => [ 89 | 'database' => env('DB_CONNECTION', 'sqlite'), 90 | 'table' => 'job_batches', 91 | ], 92 | 93 | /* 94 | |-------------------------------------------------------------------------- 95 | | Failed Queue Jobs 96 | |-------------------------------------------------------------------------- 97 | | 98 | | These options configure the behavior of failed queue job logging so you 99 | | can control how and where failed jobs are stored. Laravel ships with 100 | | support for storing failed jobs in a simple file or in a database. 101 | | 102 | | Supported drivers: "database-uuids", "dynamodb", "file", "null" 103 | | 104 | */ 105 | 106 | 'failed' => [ 107 | 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), 108 | 'database' => env('DB_CONNECTION', 'sqlite'), 109 | 'table' => 'failed_jobs', 110 | ], 111 | 112 | ]; 113 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'token' => env('POSTMARK_TOKEN'), 19 | ], 20 | 21 | 'ses' => [ 22 | 'key' => env('AWS_ACCESS_KEY_ID'), 23 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 24 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 25 | ], 26 | 27 | 'resend' => [ 28 | 'key' => env('RESEND_KEY'), 29 | ], 30 | 31 | 'slack' => [ 32 | 'notifications' => [ 33 | 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), 34 | 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), 35 | ], 36 | ], 37 | 38 | ]; 39 | -------------------------------------------------------------------------------- /database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class UserFactory extends Factory 13 | { 14 | /** 15 | * The current password being used by the factory. 16 | */ 17 | protected static ?string $password; 18 | 19 | /** 20 | * Define the model's default state. 21 | * 22 | * @return array 23 | */ 24 | public function definition(): array 25 | { 26 | return [ 27 | 'name' => fake()->name(), 28 | 'email' => fake()->unique()->safeEmail(), 29 | 'email_verified_at' => now(), 30 | 'password' => static::$password ??= Hash::make('password'), 31 | 'remember_token' => Str::random(10), 32 | ]; 33 | } 34 | 35 | /** 36 | * Indicate that the model's email address should be unverified. 37 | */ 38 | public function unverified(): static 39 | { 40 | return $this->state(fn (array $attributes) => [ 41 | 'email_verified_at' => null, 42 | ]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /database/migrations/0001_01_01_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('name'); 17 | $table->string('email')->unique(); 18 | $table->timestamp('email_verified_at')->nullable(); 19 | $table->string('password'); 20 | $table->rememberToken(); 21 | $table->timestamps(); 22 | $table->softDeletes(); 23 | }); 24 | 25 | Schema::create('password_reset_tokens', function (Blueprint $table) { 26 | $table->string('email')->primary(); 27 | $table->string('token'); 28 | $table->timestamp('created_at')->nullable(); 29 | }); 30 | 31 | Schema::create('sessions', function (Blueprint $table) { 32 | $table->string('id')->primary(); 33 | $table->foreignId('user_id')->nullable()->index(); 34 | $table->string('ip_address', 45)->nullable(); 35 | $table->text('user_agent')->nullable(); 36 | $table->longText('payload'); 37 | $table->integer('last_activity')->index(); 38 | }); 39 | } 40 | 41 | /** 42 | * Reverse the migrations. 43 | */ 44 | public function down(): void 45 | { 46 | Schema::dropIfExists('users'); 47 | Schema::dropIfExists('password_reset_tokens'); 48 | Schema::dropIfExists('sessions'); 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /database/migrations/0001_01_01_000001_create_cache_table.php: -------------------------------------------------------------------------------- 1 | string('key')->primary(); 16 | $table->mediumText('value'); 17 | $table->integer('expiration'); 18 | }); 19 | 20 | Schema::create('cache_locks', function (Blueprint $table) { 21 | $table->string('key')->primary(); 22 | $table->string('owner'); 23 | $table->integer('expiration'); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | */ 30 | public function down(): void 31 | { 32 | Schema::dropIfExists('cache'); 33 | Schema::dropIfExists('cache_locks'); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /database/migrations/0001_01_01_000002_create_jobs_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('queue')->index(); 17 | $table->longText('payload'); 18 | $table->unsignedTinyInteger('attempts'); 19 | $table->unsignedInteger('reserved_at')->nullable(); 20 | $table->unsignedInteger('available_at'); 21 | $table->unsignedInteger('created_at'); 22 | }); 23 | 24 | Schema::create('job_batches', function (Blueprint $table) { 25 | $table->string('id')->primary(); 26 | $table->string('name'); 27 | $table->integer('total_jobs'); 28 | $table->integer('pending_jobs'); 29 | $table->integer('failed_jobs'); 30 | $table->longText('failed_job_ids'); 31 | $table->mediumText('options')->nullable(); 32 | $table->integer('cancelled_at')->nullable(); 33 | $table->integer('created_at'); 34 | $table->integer('finished_at')->nullable(); 35 | }); 36 | 37 | Schema::create('failed_jobs', function (Blueprint $table) { 38 | $table->id(); 39 | $table->string('uuid')->unique(); 40 | $table->text('connection'); 41 | $table->text('queue'); 42 | $table->longText('payload'); 43 | $table->longText('exception'); 44 | $table->timestamp('failed_at')->useCurrent(); 45 | }); 46 | } 47 | 48 | /** 49 | * Reverse the migrations. 50 | */ 51 | public function down(): void 52 | { 53 | Schema::dropIfExists('jobs'); 54 | Schema::dropIfExists('job_batches'); 55 | Schema::dropIfExists('failed_jobs'); 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /database/migrations/2024_10_30_081232_create_categories_table.php: -------------------------------------------------------------------------------- 1 | id(); 17 | 18 | $table->string('name',200)->unique(); 19 | $table->longText('description')->nullable(); 20 | 21 | /* */ 22 | $table->timestamps(); 23 | $table->softDeletes(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | */ 30 | public function down(): void 31 | { 32 | Schema::dropIfExists('categories'); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /database/migrations/2024_10_30_081239_create_products_table.php: -------------------------------------------------------------------------------- 1 | id(); 17 | 18 | $table->foreignId('category_id')->references('id')->on('categories')->constrained()->cascadeOnUpdate()->cascadeOnDelete(); 19 | 20 | $table->string('code',200)->unique(); 21 | $table->string('name',200)->unique(); 22 | $table->longText('description')->nullable(); 23 | $table->json('images')->nullable(); 24 | 25 | $table->integer('stock')->default(1); 26 | $table->integer('min_stock')->default(1); 27 | $table->integer('max_stock')->default(50); 28 | 29 | $table->decimal('purchase_price', 12, 2); 30 | $table->decimal('sale_price', 12, 2); 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | /* */ 39 | $table->timestamps(); 40 | $table->softDeletes(); 41 | }); 42 | } 43 | 44 | /** 45 | * Reverse the migrations. 46 | */ 47 | public function down(): void 48 | { 49 | Schema::dropIfExists('products'); 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /database/migrations/2024_10_30_081306_create_customers_table.php: -------------------------------------------------------------------------------- 1 | id(); 17 | 18 | $table->string('name'); 19 | $table->string('email')->unique(); 20 | $table->string('phone')->nullable(); 21 | $table->string('zip_code')->nullable(); 22 | $table->string('address')->nullable(); 23 | $table->string('city')->nullable(); 24 | $table->string('state')->nullable(); 25 | $table->string('country')->nullable(); 26 | 27 | 28 | 29 | /* */ 30 | $table->timestamps(); 31 | $table->softDeletes(); 32 | }); 33 | } 34 | 35 | /** 36 | * Reverse the migrations. 37 | */ 38 | public function down(): void 39 | { 40 | Schema::dropIfExists('customers'); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /database/migrations/2024_10_30_081322_create_purchases_table.php: -------------------------------------------------------------------------------- 1 | id(); 17 | 18 | $table->foreignId('user_id')->references('id')->on('users')->constrained()->cascadeOnUpdate()->cascadeOnDelete(); 19 | 20 | $table->date('date'); 21 | $table->string('purchase_number')->unique(); 22 | $table->longText('description'); 23 | 24 | 25 | 26 | 27 | /* */ 28 | $table->timestamps(); 29 | $table->softDeletes(); 30 | }); 31 | } 32 | 33 | /** 34 | * Reverse the migrations. 35 | */ 36 | public function down(): void 37 | { 38 | Schema::dropIfExists('purchases'); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /database/migrations/2024_10_30_081328_create_purchase_details_table.php: -------------------------------------------------------------------------------- 1 | id(); 17 | 18 | $table->foreignId('purchase_id')->references('id')->on('purchases')->constrained()->cascadeOnUpdate()->cascadeOnDelete(); 19 | 20 | $table->foreignId('product_id')->references('id')->on('products')->constrained()->cascadeOnUpdate()->cascadeOnDelete(); 21 | 22 | $table->integer('quantity')->default(1); 23 | $table->decimal('purchase_price', 12, 2); 24 | $table->decimal('sale_price', 12, 2); 25 | 26 | 27 | 28 | /* */ 29 | $table->timestamps(); 30 | $table->softDeletes(); 31 | }); 32 | } 33 | 34 | /** 35 | * Reverse the migrations. 36 | */ 37 | public function down(): void 38 | { 39 | Schema::dropIfExists('purchase_details'); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /database/migrations/2024_10_30_081336_create_sales_table.php: -------------------------------------------------------------------------------- 1 | id(); 17 | 18 | $table->foreignId('user_id')->references('id')->on('users')->constrained()->cascadeOnUpdate()->cascadeOnDelete(); 19 | 20 | $table->date('date'); 21 | $table->string('sale_number')->unique(); 22 | $table->longText('description'); 23 | 24 | 25 | 26 | 27 | /* */ 28 | $table->timestamps(); 29 | $table->softDeletes(); 30 | }); 31 | } 32 | 33 | /** 34 | * Reverse the migrations. 35 | */ 36 | public function down(): void 37 | { 38 | Schema::dropIfExists('sales'); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /database/migrations/2024_10_30_081342_create_sale_details_table.php: -------------------------------------------------------------------------------- 1 | id(); 17 | 18 | 19 | $table->foreignId('sale_id')->references('id')->on('sales')->constrained()->cascadeOnUpdate()->cascadeOnDelete(); 20 | 21 | $table->foreignId('product_id')->references('id')->on('products')->constrained()->cascadeOnUpdate()->cascadeOnDelete(); 22 | 23 | $table->integer('quantity')->default(1); 24 | $table->decimal('sale_price', 12, 2); 25 | 26 | 27 | /* */ 28 | $table->timestamps(); 29 | $table->softDeletes(); 30 | }); 31 | } 32 | 33 | /** 34 | * Reverse the migrations. 35 | */ 36 | public function down(): void 37 | { 38 | Schema::dropIfExists('sale_details'); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | call([ 18 | _InitalSeeder::class, 19 | ]); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lang/en/actions.php: -------------------------------------------------------------------------------- 1 | 'Accept', 7 | 'action' => 'Action', 8 | 'actions' => 'Actions', 9 | 'add' => 'Add', 10 | 'admin' => 'Admin', 11 | 'agree' => 'Agree', 12 | 'archive' => 'Archive', 13 | 'assign' => 'Assign', 14 | 'associate' => 'Associate', 15 | 'attach' => 'Attach', 16 | 'browse' => 'Browse', 17 | 'cancel' => 'Cancel', 18 | 'choose' => 'Choose', 19 | 'choose_file' => 'Choose File', 20 | 'choose_image' => 'Choose Image', 21 | 'click_to_copy' => 'Click to copy', 22 | 'close' => 'Close', 23 | 'collapse' => 'Collapse', 24 | 'collapse_all' => 'Collapse All', 25 | 'comment' => 'Comment', 26 | 'confirm' => 'Confirm', 27 | 'connect' => 'Connect', 28 | 'create' => 'Create', 29 | 'delete' => 'Delete', 30 | 'detach' => 'Detach', 31 | 'details' => 'Details', 32 | 'disable' => 'Disable', 33 | 'discard' => 'Discard', 34 | 'done' => 'Done', 35 | 'down' => 'Down', 36 | 'duplicate' => 'Duplicate', 37 | 'edit' => 'Edit', 38 | 'enable' => 'Enable', 39 | 'expand' => 'Expand', 40 | 'expand_all' => 'Expand All', 41 | 'explanation' => 'Explanation', 42 | 'export' => 'Export', 43 | 'file' => 'File', 44 | 'files' => 'Files', 45 | 'go_home' => 'Go Home', 46 | 'hide' => 'Hide', 47 | 'home' => 'Home', 48 | 'image' => 'Image', 49 | 'impersonate' => 'Impersonate', 50 | 'impersonation' => 'Impersonation', 51 | 'import' => 'Import', 52 | 'introduction' => 'Introduction', 53 | 'like' => 'Like', 54 | 'load' => 'Load', 55 | 'localize' => 'Localize', 56 | 'log_in' => 'Log In', 57 | 'log_out' => 'Log Out', 58 | 'named' => [ 59 | 'add' => 'Add :name', 60 | 'choose' => 'Choose :name', 61 | 'create' => 'Create :name', 62 | 'delete' => 'Delete :name', 63 | 'duplicate' => 'Duplicate :name', 64 | 'edit' => 'Edit :name', 65 | 'export' => 'Export :name', 66 | 'hide' => 'Hide :name', 67 | 'import' => 'Import :name', 68 | 'new' => 'New :name', 69 | 'restore' => 'Restore :name', 70 | 'save' => 'Save :name', 71 | 'search' => 'Search :name', 72 | 'show' => 'Show :name', 73 | 'update' => 'Update :name', 74 | 'view' => 'View :name', 75 | ], 76 | 'new' => 'New', 77 | 'no' => 'No', 78 | 'open' => 'Open', 79 | 'open_website' => 'Open on the website', 80 | 'preview' => 'Preview', 81 | 'price' => 'Price', 82 | 'record' => 'Record', 83 | 'restore' => 'Restore', 84 | 'save' => 'Save', 85 | 'save_and_close' => 'Save & Close', 86 | 'save_and_return' => 'Save & Return', 87 | 'search' => 'Search', 88 | 'select' => 'Select', 89 | 'select_all' => 'Select All', 90 | 'send' => 'Send', 91 | 'settings' => 'Settings', 92 | 'show' => 'Show', 93 | 'show_all' => 'Show All', 94 | 'sign_in' => 'Sign In', 95 | 'solve' => 'Solve', 96 | 'start' => 'Start', 97 | 'stop' => 'Stop', 98 | 'submit' => 'Submit', 99 | 'subscribe' => 'Subscribe', 100 | 'switch' => 'Switch', 101 | 'switch_to_role' => 'Switch To Role', 102 | 'tag' => 'Tag', 103 | 'tags' => 'Tags', 104 | 'target_link' => [ 105 | 'blank' => 'Open in a new window', 106 | 'parent' => 'Open in a parent frame', 107 | 'self' => 'Open in a current window', 108 | 'top' => 'Open in the topmost frame', 109 | ], 110 | 'translate' => 'Translate', 111 | 'translate_it' => 'Translate It', 112 | 'unpack' => 'Unpack', 113 | 'unsubscribe' => 'Unsubscribe', 114 | 'up' => 'Up', 115 | 'update' => 'Update', 116 | 'user' => 'User', 117 | 'view' => 'View', 118 | 'yes' => 'Yes', 119 | ]; 120 | -------------------------------------------------------------------------------- /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 | ]; 9 | -------------------------------------------------------------------------------- /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/es/actions.php: -------------------------------------------------------------------------------- 1 | 'Aceptar', 7 | 'action' => 'Acción', 8 | 'actions' => 'Acciones', 9 | 'add' => 'Agregar', 10 | 'admin' => 'Administrar', 11 | 'agree' => 'Aceptar', 12 | 'archive' => 'Archivar', 13 | 'assign' => 'Asignar', 14 | 'associate' => 'Asociar', 15 | 'attach' => 'Adjuntar', 16 | 'browse' => 'Navegar', 17 | 'cancel' => 'Cancelar', 18 | 'choose' => 'Elegir', 19 | 'choose_file' => 'Elegir archivo', 20 | 'choose_image' => 'Elegir Imagen', 21 | 'click_to_copy' => 'Haga clic para copiar', 22 | 'close' => 'Cerrar', 23 | 'collapse' => 'Colapsar', 24 | 'collapse_all' => 'Colapsar todo', 25 | 'comment' => 'Comentar', 26 | 'confirm' => 'Confirmar', 27 | 'connect' => 'Conectar', 28 | 'create' => 'Crear', 29 | 'delete' => 'Borrar', 30 | 'detach' => 'Desasociar', 31 | 'details' => 'Detalles', 32 | 'disable' => 'Desactivar', 33 | 'discard' => 'Descartar', 34 | 'done' => 'Hecho', 35 | 'down' => 'Abajo', 36 | 'duplicate' => 'Duplicar', 37 | 'edit' => 'Editar', 38 | 'enable' => 'Permitir', 39 | 'expand' => 'Expandir', 40 | 'expand_all' => 'Expandir todo', 41 | 'explanation' => 'Explicación', 42 | 'export' => 'Exportar', 43 | 'file' => 'Archivo', 44 | 'files' => 'Archivos', 45 | 'go_home' => 'Ir a Inicio', 46 | 'hide' => 'Ocultar', 47 | 'home' => 'Inicio', 48 | 'image' => 'Imagen', 49 | 'impersonate' => 'Suplantar', 50 | 'impersonation' => 'Suplantación', 51 | 'import' => 'Importar', 52 | 'introduction' => 'Introducción', 53 | 'like' => 'Me gusta', 54 | 'load' => 'Cargar', 55 | 'localize' => 'Localizar', 56 | 'log_in' => 'Acceder', 57 | 'log_out' => 'Cerrar sesión', 58 | 'named' => [ 59 | 'add' => 'Agregar :name', 60 | 'choose' => 'Elegir :name', 61 | 'create' => 'Crear :name', 62 | 'delete' => 'Eliminar :name', 63 | 'duplicate' => 'Duplicar :name', 64 | 'edit' => 'Editar :name', 65 | 'export' => 'Exportar :name', 66 | 'hide' => 'Ocultar :name', 67 | 'import' => 'Importar :name', 68 | 'new' => 'Nuevo :name', 69 | 'restore' => 'Restaurar :name', 70 | 'save' => 'Guardar :name', 71 | 'search' => 'Buscar :name', 72 | 'show' => 'Mostrar :name', 73 | 'update' => 'Actualizar :name', 74 | 'view' => 'Ver :name', 75 | ], 76 | 'new' => 'Nuevo', 77 | 'no' => 'No', 78 | 'open' => 'Abrir', 79 | 'open_website' => 'Abrir en el sitio web', 80 | 'preview' => 'Previsualizar', 81 | 'price' => 'Precio', 82 | 'record' => 'Registro', 83 | 'restore' => 'Restaurar', 84 | 'save' => 'Guardar', 85 | 'save_and_close' => 'Guardar y cerrar', 86 | 'save_and_return' => 'Guardar y volver', 87 | 'search' => 'Buscar', 88 | 'select' => 'Seleccionar', 89 | 'select_all' => 'Seleccionar todo', 90 | 'send' => 'Enviar', 91 | 'settings' => 'Ajustes', 92 | 'show' => 'Mostrar', 93 | 'show_all' => 'Mostrar todo', 94 | 'sign_in' => 'Iniciar sesión', 95 | 'solve' => 'Resolver', 96 | 'start' => 'Comenzar', 97 | 'stop' => 'Detener', 98 | 'submit' => 'Enviar', 99 | 'subscribe' => 'Suscribir', 100 | 'switch' => 'Cambiar', 101 | 'switch_to_role' => 'Cambiar de rol', 102 | 'tag' => 'Etiqueta', 103 | 'tags' => 'Etiquetas', 104 | 'target_link' => [ 105 | 'blank' => 'Abrir en una ventana nueva', 106 | 'parent' => 'Abrir en el marco principal', 107 | 'self' => 'Abrir en la ventana actual', 108 | 'top' => 'Abrir en el marco superior', 109 | ], 110 | 'translate' => 'Traducir', 111 | 'translate_it' => 'Traducirlo', 112 | 'unpack' => 'Desglosar', 113 | 'unsubscribe' => 'Darse de baja', 114 | 'up' => 'Arriba', 115 | 'update' => 'Actualizar', 116 | 'user' => 'Usuario', 117 | 'view' => 'Ver', 118 | 'yes' => 'Sí', 119 | ]; 120 | -------------------------------------------------------------------------------- /lang/es/auth.php: -------------------------------------------------------------------------------- 1 | 'Estas credenciales no coinciden con nuestros registros.', 7 | 'password' => 'La contraseña es incorrecta.', 8 | 'throttle' => 'Demasiados intentos de acceso. Por favor intente nuevamente en :seconds segundos.', 9 | ]; 10 | -------------------------------------------------------------------------------- /lang/es/http-statuses.php: -------------------------------------------------------------------------------- 1 | 'Error desconocido', 7 | '100' => 'Continuar', 8 | '101' => 'Protocolos de conmutación', 9 | '102' => 'Procesando', 10 | '200' => 'DE ACUERDO', 11 | '201' => 'Creado', 12 | '202' => 'Aceptado', 13 | '203' => 'Información no autorizada', 14 | '204' => 'Sin contenido', 15 | '205' => 'Restablecer contenido', 16 | '206' => 'Contenido parcial', 17 | '207' => 'Multiestado', 18 | '208' => 'Ya Reportado', 19 | '226' => 'IM usado', 20 | '300' => 'Múltiples opciones', 21 | '301' => 'Movido permanentemente', 22 | '302' => 'Encontrado', 23 | '303' => 'Ver otros', 24 | '304' => 'No modificado', 25 | '305' => 'Usa proxy', 26 | '307' => 'Redirección temporal', 27 | '308' => 'Redirección permanente', 28 | '400' => 'Solicitud incorrecta', 29 | '401' => 'No autorizado', 30 | '402' => 'Pago requerido', 31 | '403' => 'Prohibido', 32 | '404' => 'No encontrado', 33 | '405' => 'Método no permitido', 34 | '406' => 'Inaceptable', 35 | '407' => 'Se requiere autenticación proxy', 36 | '408' => 'Solicitud de tiempo de espera', 37 | '409' => 'Conflicto', 38 | '410' => 'Recurso no disponible', 39 | '411' => 'Longitud requerida', 40 | '412' => 'Error de condición previa', 41 | '413' => 'Solicitud demasiado grande', 42 | '414' => 'URI demasiado largo', 43 | '415' => 'Tipo de medio no admitido', 44 | '416' => 'Rango no satisfactorio', 45 | '417' => 'Expectativa fallida', 46 | '418' => 'Soy una tetera', 47 | '419' => 'La sesión ha expirado', 48 | '421' => 'Solicitud mal dirigida', 49 | '422' => 'Entidad no procesable', 50 | '423' => 'Bloqueado', 51 | '424' => 'Dependencia fallida', 52 | '425' => 'Demasiado temprano', 53 | '426' => 'Se requiere actualización', 54 | '428' => 'Precondición requerida', 55 | '429' => 'Demasiadas solicitudes', 56 | '431' => 'Campos de encabezado de solicitud demasiado grandes', 57 | '444' => 'Conexión cerrada sin respuesta', 58 | '449' => 'Reintentar con', 59 | '451' => 'No disponible por razones legales', 60 | '499' => 'Solicitud cerrada del cliente', 61 | '500' => 'Error interno del servidor', 62 | '501' => 'No se ha implementado', 63 | '502' => 'Mala puerta de enlace', 64 | '503' => 'Modo de mantenimiento', 65 | '504' => 'Tiempo de espera de puerta de enlace', 66 | '505' => 'Versión HTTP no compatible', 67 | '506' => 'Variante También Negocia', 68 | '507' => 'Espacio insuficiente', 69 | '508' => 'Bucle detectado', 70 | '509' => 'Límite de ancho de banda excedido', 71 | '510' => 'no extendido', 72 | '511' => 'Se requiere autenticación de red', 73 | '520' => 'Error desconocido', 74 | '521' => 'El servidor web está caído', 75 | '522' => 'Tiempo de conexión agotado', 76 | '523' => 'El origen es inalcanzable', 77 | '524' => 'Se produjo un tiempo de espera', 78 | '525' => 'Protocolo de enlace SSL fallido', 79 | '526' => 'Certificado SSL no válido', 80 | '527' => 'Error de cañón de riel', 81 | '598' => 'Error de tiempo de espera de lectura de red', 82 | '599' => 'Error de tiempo de espera de conexión de red', 83 | 'unknownError' => 'Error desconocido', 84 | ]; 85 | -------------------------------------------------------------------------------- /lang/es/pagination.php: -------------------------------------------------------------------------------- 1 | 'Siguiente »', 7 | 'previous' => '« Anterior', 8 | ]; 9 | -------------------------------------------------------------------------------- /lang/es/passwords.php: -------------------------------------------------------------------------------- 1 | 'Su contraseña ha sido restablecida.', 7 | 'sent' => 'Le hemos enviado por correo electrónico el enlace para restablecer su contraseña.', 8 | 'throttled' => 'Por favor espere antes de intentar de nuevo.', 9 | 'token' => 'El token de restablecimiento de contraseña es inválido.', 10 | 'user' => 'No encontramos ningún usuario con ese correo electrónico.', 11 | ]; 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "build": "vite build", 6 | "dev": "vite" 7 | }, 8 | "devDependencies": { 9 | "@tailwindcss/forms": "^0.5.2", 10 | "alpinejs": "^3.4.2", 11 | "autoprefixer": "^10.4.2", 12 | "axios": "^1.7.4", 13 | "concurrently": "^9.0.1", 14 | "laravel-vite-plugin": "^1.0", 15 | "postcss": "^8.4.31", 16 | "tailwindcss": "^3.1.0", 17 | "vite": "^5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | tests/Unit 10 | 11 | 12 | tests/Feature 13 | 14 | 15 | 16 | 17 | app 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # 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/css/filament/support/support.css: -------------------------------------------------------------------------------- 1 | .fi-pagination-items,.fi-pagination-overview,.fi-pagination-records-per-page-select:not(.fi-compact){display:none}@supports (container-type:inline-size){.fi-pagination{container-type:inline-size}@container (min-width: 28rem){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@container (min-width: 56rem){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}@supports not (container-type:inline-size){@media (min-width:640px){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@media (min-width:768px){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{background-color:#333;border-radius:4px;color:#fff;font-size:14px;line-height:1.4;outline:0;position:relative;transition-property:transform,visibility,opacity;white-space:normal}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{border-top-color:initial;border-width:8px 8px 0;bottom:-7px;left:0;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:initial;border-width:0 8px 8px;left:0;top:-7px;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-left-color:initial;border-width:8px 0 8px 8px;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{border-right-color:initial;border-width:8px 8px 8px 0;left:-7px;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{color:#333;height:16px;width:16px}.tippy-arrow:before{border-color:transparent;border-style:solid;content:"";position:absolute}.tippy-content{padding:5px 9px;position:relative;z-index:1}.tippy-box[data-theme~=light]{background-color:#fff;box-shadow:0 0 20px 4px #9aa1b126,0 4px 80px -8px #24282f40,0 4px 4px -2px #5b5e6926;color:#26323d}.tippy-box[data-theme~=light][data-placement^=top]>.tippy-arrow:before{border-top-color:#fff}.tippy-box[data-theme~=light][data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:#fff}.tippy-box[data-theme~=light][data-placement^=left]>.tippy-arrow:before{border-left-color:#fff}.tippy-box[data-theme~=light][data-placement^=right]>.tippy-arrow:before{border-right-color:#fff}.tippy-box[data-theme~=light]>.tippy-backdrop{background-color:#fff}.tippy-box[data-theme~=light]>.tippy-svg-arrow{fill:#fff}.fi-sortable-ghost{opacity:.3} -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jebcdev/FilamentMiniPOS/82a6632e9d0536a6c5a9362320b5bc84541ce655/public/favicon.ico -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | handleRequest(Request::capture()); 18 | -------------------------------------------------------------------------------- /public/js/filament/forms/components/key-value.js: -------------------------------------------------------------------------------- 1 | function r({state:o}){return{state:o,rows:[],shouldUpdateRows:!0,init:function(){this.updateRows(),this.rows.length<=0?this.rows.push({key:"",value:""}):this.updateState(),this.$watch("state",(t,e)=>{let s=i=>i===null?0:Array.isArray(i)?i.length:typeof i!="object"?0:Object.keys(i).length;s(t)===0&&s(e)===0||this.updateRows()})},addRow:function(){this.rows.push({key:"",value:""}),this.updateState()},deleteRow:function(t){this.rows.splice(t,1),this.rows.length<=0&&this.addRow(),this.updateState()},reorderRows:function(t){let e=Alpine.raw(this.rows);this.rows=[];let s=e.splice(t.oldIndex,1)[0];e.splice(t.newIndex,0,s),this.$nextTick(()=>{this.rows=e,this.updateState()})},updateRows:function(){if(!this.shouldUpdateRows){this.shouldUpdateRows=!0;return}let t=[];for(let[e,s]of Object.entries(this.state??{}))t.push({key:e,value:s});this.rows=t},updateState:function(){let t={};this.rows.forEach(e=>{e.key===""||e.key===null||(t[e.key]=e.value)}),this.shouldUpdateRows=!1,this.state=t}}}export{r as default}; 2 | -------------------------------------------------------------------------------- /public/js/filament/forms/components/tags-input.js: -------------------------------------------------------------------------------- 1 | function i({state:a,splitKeys:n}){return{newTag:"",state:a,createTag:function(){if(this.newTag=this.newTag.trim(),this.newTag!==""){if(this.state.includes(this.newTag)){this.newTag="";return}this.state.push(this.newTag),this.newTag=""}},deleteTag:function(t){this.state=this.state.filter(e=>e!==t)},reorderTags:function(t){let e=this.state.splice(t.oldIndex,1)[0];this.state.splice(t.newIndex,0,e),this.state=[...this.state]},input:{["x-on:blur"]:"createTag()",["x-model"]:"newTag",["x-on:keydown"](t){["Enter",...n].includes(t.key)&&(t.preventDefault(),t.stopPropagation(),this.createTag())},["x-on:paste"](){this.$nextTick(()=>{if(n.length===0){this.createTag();return}let t=n.map(e=>e.replace(/[/\-\\^$*+?.()|[\]{}]/g,"\\$&")).join("|");this.newTag.split(new RegExp(t,"g")).forEach(e=>{this.newTag=e,this.createTag()})})}}}}export{i as default}; 2 | -------------------------------------------------------------------------------- /public/js/filament/forms/components/textarea.js: -------------------------------------------------------------------------------- 1 | function r({initialHeight:t,shouldAutosize:i,state:s}){return{state:s,wrapperEl:null,init:function(){this.wrapperEl=this.$el.parentNode,this.setInitialHeight(),i?this.$watch("state",()=>{this.resize()}):this.setUpResizeObserver()},setInitialHeight:function(){this.$el.scrollHeight<=0||(this.wrapperEl.style.height=t+"rem")},resize:function(){if(this.setInitialHeight(),this.$el.scrollHeight<=0)return;let e=this.$el.scrollHeight+"px";this.wrapperEl.style.height!==e&&(this.wrapperEl.style.height=e)},setUpResizeObserver:function(){new ResizeObserver(()=>{this.wrapperEl.style.height=this.$el.style.height}).observe(this.$el)}}}export{r as default}; 2 | -------------------------------------------------------------------------------- /public/js/filament/support/async-alpine.js: -------------------------------------------------------------------------------- 1 | (()=>{(()=>{var d=Object.defineProperty,m=t=>d(t,"__esModule",{value:!0}),f=(t,e)=>{m(t);for(var i in e)d(t,i,{get:e[i],enumerable:!0})},o={};f(o,{eager:()=>g,event:()=>w,idle:()=>y,media:()=>b,visible:()=>E});var c=()=>!0,g=c,v=({component:t,argument:e})=>new Promise(i=>{if(e)window.addEventListener(e,()=>i(),{once:!0});else{let n=a=>{a.detail.id===t.id&&(window.removeEventListener("async-alpine:load",n),i())};window.addEventListener("async-alpine:load",n)}}),w=v,x=()=>new Promise(t=>{"requestIdleCallback"in window?window.requestIdleCallback(t):setTimeout(t,200)}),y=x,A=({argument:t})=>new Promise(e=>{if(!t)return console.log("Async Alpine: media strategy requires a media query. Treating as 'eager'"),e();let i=window.matchMedia(`(${t})`);i.matches?e():i.addEventListener("change",e,{once:!0})}),b=A,$=({component:t,argument:e})=>new Promise(i=>{let n=e||"0px 0px 0px 0px",a=new IntersectionObserver(r=>{r[0].isIntersecting&&(a.disconnect(),i())},{rootMargin:n});a.observe(t.el)}),E=$;function P(t){let e=q(t),i=u(e);return i.type==="method"?{type:"expression",operator:"&&",parameters:[i]}:i}function q(t){let e=/\s*([()])\s*|\s*(\|\||&&|\|)\s*|\s*((?:[^()&|]+\([^()]+\))|[^()&|]+)\s*/g,i=[],n;for(;(n=e.exec(t))!==null;){let[,a,r,s]=n;if(a!==void 0)i.push({type:"parenthesis",value:a});else if(r!==void 0)i.push({type:"operator",value:r==="|"?"&&":r});else{let p={type:"method",method:s.trim()};s.includes("(")&&(p.method=s.substring(0,s.indexOf("(")).trim(),p.argument=s.substring(s.indexOf("(")+1,s.indexOf(")"))),s.method==="immediate"&&(s.method="eager"),i.push(p)}}return i}function u(t){let e=h(t);for(;t.length>0&&(t[0].value==="&&"||t[0].value==="|"||t[0].value==="||");){let i=t.shift().value,n=h(t);e.type==="expression"&&e.operator===i?e.parameters.push(n):e={type:"expression",operator:i,parameters:[e,n]}}return e}function h(t){if(t[0].value==="("){t.shift();let e=u(t);return t[0].value===")"&&t.shift(),e}else return t.shift()}var _="__internal_",l={Alpine:null,_options:{prefix:"ax-",alpinePrefix:"x-",root:"load",inline:"load-src",defaultStrategy:"eager"},_alias:!1,_data:{},_realIndex:0,get _index(){return this._realIndex++},init(t,e={}){return this.Alpine=t,this._options={...this._options,...e},this},start(){return this._processInline(),this._setupComponents(),this._mutations(),this},data(t,e=!1){return this._data[t]={loaded:!1,download:e},this},url(t,e){!t||!e||(this._data[t]||this.data(t),this._data[t].download=()=>import(this._parseUrl(e)))},alias(t){this._alias=t},_processInline(){let t=document.querySelectorAll(`[${this._options.prefix}${this._options.inline}]`);for(let e of t)this._inlineElement(e)},_inlineElement(t){let e=t.getAttribute(`${this._options.alpinePrefix}data`),i=t.getAttribute(`${this._options.prefix}${this._options.inline}`);if(!e||!i)return;let n=this._parseName(e);this.url(n,i)},_setupComponents(){let t=document.querySelectorAll(`[${this._options.prefix}${this._options.root}]`);for(let e of t)this._setupComponent(e)},_setupComponent(t){let e=t.getAttribute(`${this._options.alpinePrefix}data`);t.setAttribute(`${this._options.alpinePrefix}ignore`,"");let i=this._parseName(e),n=t.getAttribute(`${this._options.prefix}${this._options.root}`)||this._options.defaultStrategy;this._componentStrategy({name:i,strategy:n,el:t,id:t.id||this._index})},async _componentStrategy(t){let e=P(t.strategy);await this._generateRequirements(t,e),await this._download(t.name),this._activate(t)},_generateRequirements(t,e){if(e.type==="expression"){if(e.operator==="&&")return Promise.all(e.parameters.map(i=>this._generateRequirements(t,i)));if(e.operator==="||")return Promise.any(e.parameters.map(i=>this._generateRequirements(t,i)))}return o[e.method]?o[e.method]({component:t,argument:e.argument}):!1},async _download(t){if(t.startsWith(_)||(this._handleAlias(t),!this._data[t]||this._data[t].loaded))return;let e=await this._getModule(t);this.Alpine.data(t,e),this._data[t].loaded=!0},async _getModule(t){if(!this._data[t])return;let e=await this._data[t].download(t);return typeof e=="function"?e:e[t]||e.default||Object.values(e)[0]||!1},_activate(t){this.Alpine.destroyTree(t.el),t.el.removeAttribute(`${this._options.alpinePrefix}ignore`),t.el._x_ignore=!1,this.Alpine.initTree(t.el)},_mutations(){new MutationObserver(t=>{for(let e of t)if(e.addedNodes)for(let i of e.addedNodes)i.nodeType===1&&(i.hasAttribute(`${this._options.prefix}${this._options.root}`)&&this._mutationEl(i),i.querySelectorAll(`[${this._options.prefix}${this._options.root}]`).forEach(n=>this._mutationEl(n)))}).observe(document,{attributes:!0,childList:!0,subtree:!0})},_mutationEl(t){t.hasAttribute(`${this._options.prefix}${this._options.inline}`)&&this._inlineElement(t),this._setupComponent(t)},_handleAlias(t){if(!(!this._alias||this._data[t])){if(typeof this._alias=="function"){this.data(t,this._alias);return}this.url(t,this._alias.replaceAll("[name]",t))}},_parseName(t){return(t||"").split(/[({]/g)[0]||`${_}${this._index}`},_parseUrl(t){return new RegExp("^(?:[a-z+]+:)?//","i").test(t)?t:new URL(t,document.baseURI).href}};document.addEventListener("alpine:init",()=>{window.AsyncAlpine=l,l.init(Alpine,window.AsyncAlpineOptions||{}),document.dispatchEvent(new CustomEvent("async-alpine:init")),l.start()})})();})(); 2 | -------------------------------------------------------------------------------- /public/js/filament/tables/components/table.js: -------------------------------------------------------------------------------- 1 | function n(){return{checkboxClickController:null,collapsedGroups:[],isLoading:!1,selectedRecords:[],shouldCheckUniqueSelection:!0,lastCheckedRecord:null,livewireId:null,init:function(){this.livewireId=this.$root.closest("[wire\\:id]").attributes["wire:id"].value,this.$wire.$on("deselectAllTableRecords",()=>this.deselectAllRecords()),this.$watch("selectedRecords",()=>{if(!this.shouldCheckUniqueSelection){this.shouldCheckUniqueSelection=!0;return}this.selectedRecords=[...new Set(this.selectedRecords)],this.shouldCheckUniqueSelection=!1}),this.$nextTick(()=>this.watchForCheckboxClicks()),Livewire.hook("element.init",({component:e})=>{e.id===this.livewireId&&this.watchForCheckboxClicks()})},mountAction:function(e,t=null){this.$wire.set("selectedTableRecords",this.selectedRecords,!1),this.$wire.mountTableAction(e,t)},mountBulkAction:function(e){this.$wire.set("selectedTableRecords",this.selectedRecords,!1),this.$wire.mountTableBulkAction(e)},toggleSelectRecordsOnPage:function(){let e=this.getRecordsOnPage();if(this.areRecordsSelected(e)){this.deselectRecords(e);return}this.selectRecords(e)},toggleSelectRecordsInGroup:async function(e){if(this.isLoading=!0,this.areRecordsSelected(this.getRecordsInGroupOnPage(e))){this.deselectRecords(await this.$wire.getGroupedSelectableTableRecordKeys(e));return}this.selectRecords(await this.$wire.getGroupedSelectableTableRecordKeys(e)),this.isLoading=!1},getRecordsInGroupOnPage:function(e){let t=[];for(let s of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])s.dataset.group===e&&t.push(s.value);return t},getRecordsOnPage:function(){let e=[];for(let t of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])e.push(t.value);return e},selectRecords:function(e){for(let t of e)this.isRecordSelected(t)||this.selectedRecords.push(t)},deselectRecords:function(e){for(let t of e){let s=this.selectedRecords.indexOf(t);s!==-1&&this.selectedRecords.splice(s,1)}},selectAllRecords:async function(){this.isLoading=!0,this.selectedRecords=await this.$wire.getAllSelectableTableRecordKeys(),this.isLoading=!1},deselectAllRecords:function(){this.selectedRecords=[]},isRecordSelected:function(e){return this.selectedRecords.includes(e)},areRecordsSelected:function(e){return e.every(t=>this.isRecordSelected(t))},toggleCollapseGroup:function(e){if(this.isGroupCollapsed(e)){this.collapsedGroups.splice(this.collapsedGroups.indexOf(e),1);return}this.collapsedGroups.push(e)},isGroupCollapsed:function(e){return this.collapsedGroups.includes(e)},resetCollapsedGroups:function(){this.collapsedGroups=[]},watchForCheckboxClicks:function(){this.checkboxClickController&&this.checkboxClickController.abort(),this.checkboxClickController=new AbortController;let{signal:e}=this.checkboxClickController;this.$root?.addEventListener("click",t=>t.target?.matches(".fi-ta-record-checkbox")&&this.handleCheckboxClick(t,t.target),{signal:e})},handleCheckboxClick:function(e,t){if(!this.lastChecked){this.lastChecked=t;return}if(e.shiftKey){let s=Array.from(this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[]);if(!s.includes(this.lastChecked)){this.lastChecked=t;return}let o=s.indexOf(this.lastChecked),r=s.indexOf(t),l=[o,r].sort((i,d)=>i-d),c=[];for(let i=l[0];i<=l[1];i++)s[i].checked=t.checked,c.push(s[i].value);t.checked?this.selectRecords(c):this.deselectRecords(c)}this.lastChecked=t}}}export{n as default}; 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /resources/css/app.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /resources/js/app.js: -------------------------------------------------------------------------------- 1 | import './bootstrap'; 2 | 3 | import Alpine from 'alpinejs'; 4 | 5 | window.Alpine = Alpine; 6 | 7 | Alpine.start(); 8 | -------------------------------------------------------------------------------- /resources/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | window.axios = axios; 3 | 4 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 5 | -------------------------------------------------------------------------------- /resources/views/auth/confirm-password.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | {{ __('This is a secure area of the application. Please confirm your password before continuing.') }} 4 |
5 | 6 |
7 | @csrf 8 | 9 | 10 |
11 | 12 | 13 | 17 | 18 | 19 |
20 | 21 |
22 | 23 | {{ __('Confirm') }} 24 | 25 |
26 |
27 |
28 | -------------------------------------------------------------------------------- /resources/views/auth/forgot-password.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | {{ __('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.') }} 4 |
5 | 6 | 7 | 8 | 9 |
10 | @csrf 11 | 12 | 13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 | 21 | {{ __('Email Password Reset Link') }} 22 | 23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /resources/views/auth/login.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | @csrf 7 | 8 | 9 |
10 | 11 | 12 | 13 |
14 | 15 | 16 |
17 | 18 | 19 | 23 | 24 | 25 |
26 | 27 | 28 |
29 | 33 |
34 | 35 |
36 | @if (Route::has('password.request')) 37 | 38 | {{ __('Forgot your password?') }} 39 | 40 | @endif 41 | 42 | 43 | {{ __('Log in') }} 44 | 45 |
46 |
47 |
48 | -------------------------------------------------------------------------------- /resources/views/auth/register.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | @csrf 4 | 5 | 6 |
7 | 8 | 9 | 10 |
11 | 12 | 13 |
14 | 15 | 16 | 17 |
18 | 19 | 20 |
21 | 22 | 23 | 27 | 28 | 29 |
30 | 31 | 32 |
33 | 34 | 35 | 38 | 39 | 40 |
41 | 42 |
43 | 44 | {{ __('Already registered?') }} 45 | 46 | 47 | 48 | {{ __('Register') }} 49 | 50 |
51 |
52 |
53 | -------------------------------------------------------------------------------- /resources/views/auth/reset-password.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | @csrf 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 |
14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 | 22 | 23 |
24 | 25 | 26 | 29 | 30 | 31 |
32 | 33 |
34 | 35 | {{ __('Reset Password') }} 36 | 37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /resources/views/auth/verify-email.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | {{ __('Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }} 4 |
5 | 6 | @if (session('status') == 'verification-link-sent') 7 |
8 | {{ __('A new verification link has been sent to the email address you provided during registration.') }} 9 |
10 | @endif 11 | 12 |
13 |
14 | @csrf 15 | 16 |
17 | 18 | {{ __('Resend Verification Email') }} 19 | 20 |
21 |
22 | 23 |
24 | @csrf 25 | 26 | 29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /resources/views/components/application-logo.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/views/components/auth-session-status.blade.php: -------------------------------------------------------------------------------- 1 | @props(['status']) 2 | 3 | @if ($status) 4 |
merge(['class' => 'font-medium text-sm text-green-600 dark:text-green-400']) }}> 5 | {{ $status }} 6 |
7 | @endif 8 | -------------------------------------------------------------------------------- /resources/views/components/danger-button.blade.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /resources/views/components/dropdown-link.blade.php: -------------------------------------------------------------------------------- 1 | merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800 transition duration-150 ease-in-out']) }}>{{ $slot }} 2 | -------------------------------------------------------------------------------- /resources/views/components/dropdown.blade.php: -------------------------------------------------------------------------------- 1 | @props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white dark:bg-gray-700']) 2 | 3 | @php 4 | $alignmentClasses = match ($align) { 5 | 'left' => 'ltr:origin-top-left rtl:origin-top-right start-0', 6 | 'top' => 'origin-top', 7 | default => 'ltr:origin-top-right rtl:origin-top-left end-0', 8 | }; 9 | 10 | $width = match ($width) { 11 | '48' => 'w-48', 12 | default => $width, 13 | }; 14 | @endphp 15 | 16 |
17 |
18 | {{ $trigger }} 19 |
20 | 21 | 35 |
36 | -------------------------------------------------------------------------------- /resources/views/components/input-error.blade.php: -------------------------------------------------------------------------------- 1 | @props(['messages']) 2 | 3 | @if ($messages) 4 |
    merge(['class' => 'text-sm text-red-600 dark:text-red-400 space-y-1']) }}> 5 | @foreach ((array) $messages as $message) 6 |
  • {{ $message }}
  • 7 | @endforeach 8 |
9 | @endif 10 | -------------------------------------------------------------------------------- /resources/views/components/input-label.blade.php: -------------------------------------------------------------------------------- 1 | @props(['value']) 2 | 3 | 6 | -------------------------------------------------------------------------------- /resources/views/components/modal.blade.php: -------------------------------------------------------------------------------- 1 | @props([ 2 | 'name', 3 | 'show' => false, 4 | 'maxWidth' => '2xl' 5 | ]) 6 | 7 | @php 8 | $maxWidth = [ 9 | 'sm' => 'sm:max-w-sm', 10 | 'md' => 'sm:max-w-md', 11 | 'lg' => 'sm:max-w-lg', 12 | 'xl' => 'sm:max-w-xl', 13 | '2xl' => 'sm:max-w-2xl', 14 | ][$maxWidth]; 15 | @endphp 16 | 17 |
52 |
63 |
64 |
65 | 66 |
76 | {{ $slot }} 77 |
78 |
79 | -------------------------------------------------------------------------------- /resources/views/components/nav-link.blade.php: -------------------------------------------------------------------------------- 1 | @props(['active']) 2 | 3 | @php 4 | $classes = ($active ?? false) 5 | ? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 dark:border-indigo-600 text-sm font-medium leading-5 text-gray-900 dark:text-gray-100 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out' 6 | : 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hover:border-gray-300 dark:hover:border-gray-700 focus:outline-none focus:text-gray-700 dark:focus:text-gray-300 focus:border-gray-300 dark:focus:border-gray-700 transition duration-150 ease-in-out'; 7 | @endphp 8 | 9 | merge(['class' => $classes]) }}> 10 | {{ $slot }} 11 | 12 | -------------------------------------------------------------------------------- /resources/views/components/primary-button.blade.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /resources/views/components/responsive-nav-link.blade.php: -------------------------------------------------------------------------------- 1 | @props(['active']) 2 | 3 | @php 4 | $classes = ($active ?? false) 5 | ? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 dark:border-indigo-600 text-start text-base font-medium text-indigo-700 dark:text-indigo-300 bg-indigo-50 dark:bg-indigo-900/50 focus:outline-none focus:text-indigo-800 dark:focus:text-indigo-200 focus:bg-indigo-100 dark:focus:bg-indigo-900 focus:border-indigo-700 dark:focus:border-indigo-300 transition duration-150 ease-in-out' 6 | : 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 hover:border-gray-300 dark:hover:border-gray-600 focus:outline-none focus:text-gray-800 dark:focus:text-gray-200 focus:bg-gray-50 dark:focus:bg-gray-700 focus:border-gray-300 dark:focus:border-gray-600 transition duration-150 ease-in-out'; 7 | @endphp 8 | 9 | merge(['class' => $classes]) }}> 10 | {{ $slot }} 11 | 12 | -------------------------------------------------------------------------------- /resources/views/components/secondary-button.blade.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /resources/views/components/text-input.blade.php: -------------------------------------------------------------------------------- 1 | @props(['disabled' => false]) 2 | 3 | merge(['class' => 'border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm']) }}> 4 | -------------------------------------------------------------------------------- /resources/views/dashboard.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

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

6 |
7 | 8 |
9 |
10 |
11 |
12 | 13 | 14 |
15 | 16 | {{ __('Dashboard') }} 17 | 18 | 19 | 20 | {{ __('Admin') }} 21 | 22 |
23 | 24 | 25 |
26 |
27 |
28 |
29 |
30 | -------------------------------------------------------------------------------- /resources/views/layouts/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ config('app.name', 'Laravel') }} 9 | 10 | 11 | 12 | 13 | 14 | 15 | @vite(['resources/css/app.css', 'resources/js/app.js']) 16 | 17 | 18 |
19 | @include('layouts.navigation') 20 | 21 | 22 | @isset($header) 23 |
24 |
25 | {{ $header }} 26 |
27 |
28 | @endisset 29 | 30 | 31 |
32 | {{ $slot }} 33 |
34 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /resources/views/layouts/guest.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ config('app.name', 'Laravel') }} 9 | 10 | 11 | 12 | 13 | 14 | 15 | @vite(['resources/css/app.css', 'resources/js/app.js']) 16 | 17 | 18 |
19 |
20 | 21 | 22 | 23 |
24 | 25 |
26 | {{ $slot }} 27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /resources/views/profile/edit.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

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

6 |
7 | 8 |
9 |
10 |
11 |
12 | @include('profile.partials.update-profile-information-form') 13 |
14 |
15 | 16 |
17 |
18 | @include('profile.partials.update-password-form') 19 |
20 |
21 | 22 |
23 |
24 | @include('profile.partials.delete-user-form') 25 |
26 |
27 |
28 |
29 |
30 | -------------------------------------------------------------------------------- /resources/views/profile/partials/delete-user-form.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | {{ __('Delete Account') }} 5 |

6 | 7 |

8 | {{ __('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.') }} 9 |

10 |
11 | 12 | {{ __('Delete Account') }} 16 | 17 | 18 |
19 | @csrf 20 | @method('delete') 21 | 22 |

23 | {{ __('Are you sure you want to delete your account?') }} 24 |

25 | 26 |

27 | {{ __('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.') }} 28 |

29 | 30 |
31 | 32 | 33 | 40 | 41 | 42 |
43 | 44 |
45 | 46 | {{ __('Cancel') }} 47 | 48 | 49 | 50 | {{ __('Delete Account') }} 51 | 52 |
53 |
54 |
55 |
56 | -------------------------------------------------------------------------------- /resources/views/profile/partials/update-password-form.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | {{ __('Update Password') }} 5 |

6 | 7 |

8 | {{ __('Ensure your account is using a long, random password to stay secure.') }} 9 |

10 |
11 | 12 |
13 | @csrf 14 | @method('put') 15 | 16 |
17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 | 25 | 26 |
27 | 28 |
29 | 30 | 31 | 32 |
33 | 34 |
35 | {{ __('Save') }} 36 | 37 | @if (session('status') === 'password-updated') 38 |

{{ __('Saved.') }}

45 | @endif 46 |
47 |
48 |
49 | -------------------------------------------------------------------------------- /resources/views/profile/partials/update-profile-information-form.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |

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

6 | 7 |

8 | {{ __("Update your account's profile information and email address.") }} 9 |

10 |
11 | 12 |
13 | @csrf 14 |
15 | 16 |
17 | @csrf 18 | @method('patch') 19 | 20 |
21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 | 29 | 30 | 31 | @if ($user instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && ! $user->hasVerifiedEmail()) 32 |
33 |

34 | {{ __('Your email address is unverified.') }} 35 | 36 | 39 |

40 | 41 | @if (session('status') === 'verification-link-sent') 42 |

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

45 | @endif 46 |
47 | @endif 48 |
49 | 50 |
51 | {{ __('Save') }} 52 | 53 | @if (session('status') === 'profile-updated') 54 |

{{ __('Saved.') }}

61 | @endif 62 |
63 |
64 |
65 | -------------------------------------------------------------------------------- /routes/auth.php: -------------------------------------------------------------------------------- 1 | group(function () { 15 | /* Route::get('register', [RegisteredUserController::class, 'create']) 16 | ->name('register'); 17 | 18 | Route::post('register', [RegisteredUserController::class, 'store']); */ 19 | 20 | Route::get('login', [AuthenticatedSessionController::class, 'create']) 21 | ->name('login'); 22 | 23 | Route::post('login', [AuthenticatedSessionController::class, 'store']); 24 | 25 | Route::get('forgot-password', [PasswordResetLinkController::class, 'create']) 26 | ->name('password.request'); 27 | 28 | Route::post('forgot-password', [PasswordResetLinkController::class, 'store']) 29 | ->name('password.email'); 30 | 31 | Route::get('reset-password/{token}', [NewPasswordController::class, 'create']) 32 | ->name('password.reset'); 33 | 34 | Route::post('reset-password', [NewPasswordController::class, 'store']) 35 | ->name('password.store'); 36 | }); 37 | 38 | Route::middleware('auth')->group(function () { 39 | Route::get('verify-email', EmailVerificationPromptController::class) 40 | ->name('verification.notice'); 41 | 42 | Route::get('verify-email/{id}/{hash}', VerifyEmailController::class) 43 | ->middleware(['signed', 'throttle:6,1']) 44 | ->name('verification.verify'); 45 | 46 | Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store']) 47 | ->middleware('throttle:6,1') 48 | ->name('verification.send'); 49 | 50 | Route::get('confirm-password', [ConfirmablePasswordController::class, 'show']) 51 | ->name('password.confirm'); 52 | 53 | Route::post('confirm-password', [ConfirmablePasswordController::class, 'store']); 54 | 55 | Route::put('password', [PasswordController::class, 'update'])->name('password.update'); 56 | 57 | Route::post('logout', [AuthenticatedSessionController::class, 'destroy']) 58 | ->name('logout'); 59 | }); 60 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 8 | })->purpose('Display an inspiring quote')->hourly(); 9 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | name('index'); 8 | 9 | 10 | Route::middleware('auth')->group(function () { 11 | 12 | Route::get('/dashboard', [_SiteController::class, 'dashboard'])->name('dashboard'); 13 | 14 | Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); 15 | Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); 16 | Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); 17 | }); 18 | 19 | require __DIR__ . '/auth.php'; 20 | -------------------------------------------------------------------------------- /storage/app/public/products/01JBFGWYW9257R6094PGA99P3W.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jebcdev/FilamentMiniPOS/82a6632e9d0536a6c5a9362320b5bc84541ce655/storage/app/public/products/01JBFGWYW9257R6094PGA99P3W.png -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | import defaultTheme from 'tailwindcss/defaultTheme'; 2 | import forms from '@tailwindcss/forms'; 3 | 4 | /** @type {import('tailwindcss').Config} */ 5 | export default { 6 | content: [ 7 | './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php', 8 | './storage/framework/views/*.php', 9 | './resources/views/**/*.blade.php', 10 | ], 11 | 12 | theme: { 13 | extend: { 14 | fontFamily: { 15 | sans: ['Figtree', ...defaultTheme.fontFamily.sans], 16 | }, 17 | }, 18 | }, 19 | 20 | plugins: [forms], 21 | }; 22 | -------------------------------------------------------------------------------- /tests/Feature/Auth/AuthenticationTest.php: -------------------------------------------------------------------------------- 1 | get('/login'); 7 | 8 | $response->assertStatus(200); 9 | }); 10 | 11 | test('users can authenticate using the login screen', function () { 12 | $user = User::factory()->create(); 13 | 14 | $response = $this->post('/login', [ 15 | 'email' => $user->email, 16 | 'password' => 'password', 17 | ]); 18 | 19 | $this->assertAuthenticated(); 20 | $response->assertRedirect(route('dashboard', absolute: false)); 21 | }); 22 | 23 | test('users can not authenticate with invalid password', function () { 24 | $user = User::factory()->create(); 25 | 26 | $this->post('/login', [ 27 | 'email' => $user->email, 28 | 'password' => 'wrong-password', 29 | ]); 30 | 31 | $this->assertGuest(); 32 | }); 33 | 34 | test('users can logout', function () { 35 | $user = User::factory()->create(); 36 | 37 | $response = $this->actingAs($user)->post('/logout'); 38 | 39 | $this->assertGuest(); 40 | $response->assertRedirect('/'); 41 | }); 42 | -------------------------------------------------------------------------------- /tests/Feature/Auth/EmailVerificationTest.php: -------------------------------------------------------------------------------- 1 | unverified()->create(); 10 | 11 | $response = $this->actingAs($user)->get('/verify-email'); 12 | 13 | $response->assertStatus(200); 14 | }); 15 | 16 | test('email can be verified', function () { 17 | $user = User::factory()->unverified()->create(); 18 | 19 | Event::fake(); 20 | 21 | $verificationUrl = URL::temporarySignedRoute( 22 | 'verification.verify', 23 | now()->addMinutes(60), 24 | ['id' => $user->id, 'hash' => sha1($user->email)] 25 | ); 26 | 27 | $response = $this->actingAs($user)->get($verificationUrl); 28 | 29 | Event::assertDispatched(Verified::class); 30 | expect($user->fresh()->hasVerifiedEmail())->toBeTrue(); 31 | $response->assertRedirect(route('dashboard', absolute: false).'?verified=1'); 32 | }); 33 | 34 | test('email is not verified with invalid hash', function () { 35 | $user = User::factory()->unverified()->create(); 36 | 37 | $verificationUrl = URL::temporarySignedRoute( 38 | 'verification.verify', 39 | now()->addMinutes(60), 40 | ['id' => $user->id, 'hash' => sha1('wrong-email')] 41 | ); 42 | 43 | $this->actingAs($user)->get($verificationUrl); 44 | 45 | expect($user->fresh()->hasVerifiedEmail())->toBeFalse(); 46 | }); 47 | -------------------------------------------------------------------------------- /tests/Feature/Auth/PasswordConfirmationTest.php: -------------------------------------------------------------------------------- 1 | create(); 7 | 8 | $response = $this->actingAs($user)->get('/confirm-password'); 9 | 10 | $response->assertStatus(200); 11 | }); 12 | 13 | test('password can be confirmed', function () { 14 | $user = User::factory()->create(); 15 | 16 | $response = $this->actingAs($user)->post('/confirm-password', [ 17 | 'password' => 'password', 18 | ]); 19 | 20 | $response->assertRedirect(); 21 | $response->assertSessionHasNoErrors(); 22 | }); 23 | 24 | test('password is not confirmed with invalid password', function () { 25 | $user = User::factory()->create(); 26 | 27 | $response = $this->actingAs($user)->post('/confirm-password', [ 28 | 'password' => 'wrong-password', 29 | ]); 30 | 31 | $response->assertSessionHasErrors(); 32 | }); 33 | -------------------------------------------------------------------------------- /tests/Feature/Auth/PasswordResetTest.php: -------------------------------------------------------------------------------- 1 | get('/forgot-password'); 9 | 10 | $response->assertStatus(200); 11 | }); 12 | 13 | test('reset password link can be requested', function () { 14 | Notification::fake(); 15 | 16 | $user = User::factory()->create(); 17 | 18 | $this->post('/forgot-password', ['email' => $user->email]); 19 | 20 | Notification::assertSentTo($user, ResetPassword::class); 21 | }); 22 | 23 | test('reset password screen can be rendered', function () { 24 | Notification::fake(); 25 | 26 | $user = User::factory()->create(); 27 | 28 | $this->post('/forgot-password', ['email' => $user->email]); 29 | 30 | Notification::assertSentTo($user, ResetPassword::class, function ($notification) { 31 | $response = $this->get('/reset-password/'.$notification->token); 32 | 33 | $response->assertStatus(200); 34 | 35 | return true; 36 | }); 37 | }); 38 | 39 | test('password can be reset with valid token', function () { 40 | Notification::fake(); 41 | 42 | $user = User::factory()->create(); 43 | 44 | $this->post('/forgot-password', ['email' => $user->email]); 45 | 46 | Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) { 47 | $response = $this->post('/reset-password', [ 48 | 'token' => $notification->token, 49 | 'email' => $user->email, 50 | 'password' => 'password', 51 | 'password_confirmation' => 'password', 52 | ]); 53 | 54 | $response 55 | ->assertSessionHasNoErrors() 56 | ->assertRedirect(route('login')); 57 | 58 | return true; 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /tests/Feature/Auth/PasswordUpdateTest.php: -------------------------------------------------------------------------------- 1 | create(); 8 | 9 | $response = $this 10 | ->actingAs($user) 11 | ->from('/profile') 12 | ->put('/password', [ 13 | 'current_password' => 'password', 14 | 'password' => 'new-password', 15 | 'password_confirmation' => 'new-password', 16 | ]); 17 | 18 | $response 19 | ->assertSessionHasNoErrors() 20 | ->assertRedirect('/profile'); 21 | 22 | $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); 23 | }); 24 | 25 | test('correct password must be provided to update password', function () { 26 | $user = User::factory()->create(); 27 | 28 | $response = $this 29 | ->actingAs($user) 30 | ->from('/profile') 31 | ->put('/password', [ 32 | 'current_password' => 'wrong-password', 33 | 'password' => 'new-password', 34 | 'password_confirmation' => 'new-password', 35 | ]); 36 | 37 | $response 38 | ->assertSessionHasErrorsIn('updatePassword', 'current_password') 39 | ->assertRedirect('/profile'); 40 | }); 41 | -------------------------------------------------------------------------------- /tests/Feature/Auth/RegistrationTest.php: -------------------------------------------------------------------------------- 1 | get('/register'); 5 | 6 | $response->assertStatus(200); 7 | }); 8 | 9 | test('new users can register', function () { 10 | $response = $this->post('/register', [ 11 | 'name' => 'Test User', 12 | 'email' => 'test@example.com', 13 | 'password' => 'password', 14 | 'password_confirmation' => 'password', 15 | ]); 16 | 17 | $this->assertAuthenticated(); 18 | $response->assertRedirect(route('dashboard', absolute: false)); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/'); 5 | 6 | $response->assertStatus(200); 7 | }); 8 | -------------------------------------------------------------------------------- /tests/Feature/ProfileTest.php: -------------------------------------------------------------------------------- 1 | create(); 7 | 8 | $response = $this 9 | ->actingAs($user) 10 | ->get('/profile'); 11 | 12 | $response->assertOk(); 13 | }); 14 | 15 | test('profile information can be updated', function () { 16 | $user = User::factory()->create(); 17 | 18 | $response = $this 19 | ->actingAs($user) 20 | ->patch('/profile', [ 21 | 'name' => 'Test User', 22 | 'email' => 'test@example.com', 23 | ]); 24 | 25 | $response 26 | ->assertSessionHasNoErrors() 27 | ->assertRedirect('/profile'); 28 | 29 | $user->refresh(); 30 | 31 | $this->assertSame('Test User', $user->name); 32 | $this->assertSame('test@example.com', $user->email); 33 | $this->assertNull($user->email_verified_at); 34 | }); 35 | 36 | test('email verification status is unchanged when the email address is unchanged', function () { 37 | $user = User::factory()->create(); 38 | 39 | $response = $this 40 | ->actingAs($user) 41 | ->patch('/profile', [ 42 | 'name' => 'Test User', 43 | 'email' => $user->email, 44 | ]); 45 | 46 | $response 47 | ->assertSessionHasNoErrors() 48 | ->assertRedirect('/profile'); 49 | 50 | $this->assertNotNull($user->refresh()->email_verified_at); 51 | }); 52 | 53 | test('user can delete their account', function () { 54 | $user = User::factory()->create(); 55 | 56 | $response = $this 57 | ->actingAs($user) 58 | ->delete('/profile', [ 59 | 'password' => 'password', 60 | ]); 61 | 62 | $response 63 | ->assertSessionHasNoErrors() 64 | ->assertRedirect('/'); 65 | 66 | $this->assertGuest(); 67 | $this->assertNull($user->fresh()); 68 | }); 69 | 70 | test('correct password must be provided to delete account', function () { 71 | $user = User::factory()->create(); 72 | 73 | $response = $this 74 | ->actingAs($user) 75 | ->from('/profile') 76 | ->delete('/profile', [ 77 | 'password' => 'wrong-password', 78 | ]); 79 | 80 | $response 81 | ->assertSessionHasErrorsIn('userDeletion', 'password') 82 | ->assertRedirect('/profile'); 83 | 84 | $this->assertNotNull($user->fresh()); 85 | }); 86 | -------------------------------------------------------------------------------- /tests/Pest.php: -------------------------------------------------------------------------------- 1 | extend(Tests\TestCase::class) 15 | ->use(Illuminate\Foundation\Testing\RefreshDatabase::class) 16 | ->in('Feature'); 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Expectations 21 | |-------------------------------------------------------------------------- 22 | | 23 | | When you're writing tests, you often need to check that values meet certain conditions. The 24 | | "expect()" function gives you access to a set of "expectations" methods that you can use 25 | | to assert different things. Of course, you may extend the Expectation API at any time. 26 | | 27 | */ 28 | 29 | expect()->extend('toBeOne', function () { 30 | return $this->toBe(1); 31 | }); 32 | 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | Functions 36 | |-------------------------------------------------------------------------- 37 | | 38 | | While Pest is very powerful out-of-the-box, you may have some testing code specific to your 39 | | project that you don't want to repeat in every file. Here you can also expose helpers as 40 | | global functions to help you to reduce the number of lines of code in your test files. 41 | | 42 | */ 43 | 44 | function something() 45 | { 46 | // .. 47 | } 48 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | toBeTrue(); 5 | }); 6 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import laravel from 'laravel-vite-plugin'; 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | laravel({ 7 | input: [ 8 | 'resources/css/app.css', 9 | 'resources/js/app.js', 10 | ], 11 | refresh: true, 12 | }), 13 | ], 14 | }); 15 | --------------------------------------------------------------------------------