commit 10dc34514759c94829630532623d6d039a3d8b7a Author: dyzcdn Date: Mon Dec 22 12:03:01 2025 +0700 Initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8f0de65 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[docker-compose.yml] +indent_size = 4 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0363714 --- /dev/null +++ b/.env.example @@ -0,0 +1,108 @@ +APP_NAME="DyDev APP" +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_URL=http://localhost:8000 + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database + +PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +# Database Configuration +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=dy_app_db +DB_USERNAME=root +DB_PASSWORD= + +# Session Configuration +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +# Broadcasting & Filesystem +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +# Cache Configuration +CACHE_STORE=database +# CACHE_PREFIX= + +# Memcached Configuration +MEMCACHED_HOST=127.0.0.1 + +# Redis Configuration +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +# Mail Configuration +MAIL_MAILER=smtp +# MAIL_SCHEME=null +MAIL_HOST=127.0.0.1 +MAIL_PORT=587 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=tls +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" + +# AWS Configuration +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +# VITE Configuration +VITE_APP_NAME="${APP_NAME}" + +# Social Authentication (OAuth) Configuration +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= +GITHUB_REDIRECT_URI=${APP_URL}/auth/github/callback + +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= +GOOGLE_REDIRECT_URI=${APP_URL}/auth/google/callback + +# CA ROOT +CA_ROOT_COUNTRY_NAME="ID" +CA_ROOT_ORGANIZATION_NAME="DyDev TrustLab OpenSource" +CA_ROOT_ORGANIZATIONAL_UNIT_NAME="www.dyzulk.com" +CA_ROOT_COMMON_NAME="DyzulkDev" + +# CA INTERMEDIATE 4096 +CA_4096_COUNTRY_NAME="ID" +CA_4096_ORGANIZATION_NAME="DyDev TrustLab" +CA_4096_ORGANIZATIONAL_UNIT_NAME="www.dyzulk.com" +CA_4096_COMMON_NAME="DyDev Infinity CA1" + +# CA INTERMEDIATE 2048 +CA_2048_COUNTRY_NAME="ID" +CA_2048_ORGANIZATION_NAME="Twinpath TrustLab" +CA_2048_ORGANIZATIONAL_UNIT_NAME="www.dyzulk.com" +CA_2048_COMMON_NAME="Twinpath Infinity CA2" + +# CA LEAF DEFAULT +CA_LEAF_DEFAULT_COUNTRY_NAME="ID" +CA_LEAF_DEFAULT_LOCALITY="Jakarta" +CA_LEAF_DEFAULT_STATE="DKI Jakarta" +CA_LEAF_DEFAULT_ORGANIZATION_NAME="MyLab Secured" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fcb21d3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +* text=auto eol=lf + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +CHANGELOG.md export-ignore +.styleci.yml export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1bb514b --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +/node_modules +/public/build +/public/hot +/public/storage +/storage/*.key +/storage/pail +/vendor +.env +.env.backup +.env.production +.phpactor.json +.phpunit.result.cache +.DS_Store +Thumbs.db +*.log +/.fleet +/.idea +/.nova +/.phpunit.cache +/.vscode +/.zed +/auth.json +Homestead.json +Homestead.yaml +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.phpunit.result.cache + +# SQLite +/database/*.sqlite +/database/*.sqlite-journal + +# Compiled assets +mix-manifest.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..50d6381 --- /dev/null +++ b/README.md @@ -0,0 +1,124 @@ +# Certificate Authority & API Management System + +A robust, modern platform for managing Root CAs, Intermediate CAs, and Leaf Certificates with an integrated API management system. Built on **Laravel 12**, **Tailwind CSS v4**, and **Alpine.js**. + +## πŸš€ Key Features + +* **CA Management**: Securely manage Root and Intermediate CAs. +* **Certificate Issuance**: Issue and manage Leaf certificates for users. +* **API Key System**: Advanced API key management with: + * **Regeneration**: Securely rotate keys with a single click. + * **Activity Tracking**: Real-time "Last Used" monitoring. + * **Public/Private Endpoints**: Documentation with interactive tabs and code snippets. +* **AJAX-Powered UI**: Zero-refresh search, pagination, and status toggles. +* **Dynamic Dashboard**: Real-time metrics, certificate issuance trends, and server latency monitoring. +* **Modern Interactive UI**: High-performance dashboard with vibrant metrics and dark mode support. + +## πŸ› οΈ Built With + +* **Laravel 12**: Secure and scalable backend framework. +* **Tailwind CSS v4**: Modern, utility-first styling. +* **Alpine.js**: Lightweight reactivity. +* **Chart.js**: Visual trend analysis. + +## 🚦 Quick Start + +### 1. Requirements +* **PHP 8.2+** with the following extensions: + * `openssl` (Required for SSL/TLS operations) + * `zip` (Required for certificate bundle downloads) + * `bcmath` (Required for large serial number handling) + * `mbstring`, `xml`, `curl`, `ctype`, `filter` (Standard Laravel requirements) +* **Node.js 18+** & NPM +* **OpenSSL CLI** (Ensure it is accessible in your system PATH) + +> [!NOTE] +> Default PHP installations on Windows (XAMPP/WAMP), Mac (Homebrew), and Linux (apt/yum) often vary. Please ensure the extensions above are enabled in your `php.ini`. + +### 2. Setup + +#### Option A: Terminal Access +```bash +# Clone and enter +git clone +cd app-tail + +# Install dependencies +composer install +npm install + +# Setup environment +cp .env.example .env +php artisan key:generate +``` + +#### Option B: Manual (No Terminal/Shared Hosting) +1. **Download**: Click the "Code" button on GitHub and select **Download ZIP**, then extract it to your local computer. +2. **Dependencies**: + * Run `composer install` and `npm run build` on your **local computer**. + * Upload the entire project folder to your server via FTP/File Manager, **including** the `vendor` and `public/build` directories. +3. **Environment**: + * Rename `.env.example` to `.env` using your hosting File Manager. + * **APP_KEY**: Since you cannot run `key:generate`, visit `yourdomain.com/key-gen.html` to generate a secure key, then paste it into the `APP_KEY=` field in your `.env`. + +### 3. Database & Migrations + +#### Option A: Terminal Access (Recommended) +```bash +php artisan migrate --seed +``` + +#### Option B: Manual Import (Shared Hosting) +If your hosting does not provide terminal access: +1. Create a new database via your hosting panel (e.g., cPanel MySQL Wizard). +2. Open **phpMyAdmin**. +3. Select your database and go to the **Import** tab. +4. Choose the file `database/install.sql` from this project and click **Go**. + * **Default Admin**: `admin@dyzulk.com` + * **Default Password**: `password` + +## πŸš€ Production Deployment + +### 1. Optimize Environment +Update your `.env` for production: +```env +APP_ENV=production +APP_DEBUG=false +APP_URL=https://yourdomain.com +``` + +### 2. Assets & Storage Link + +#### Terminal Method +```bash +npm run build +php artisan storage:link +php artisan optimize +``` + +#### Manual Method (No Terminal) +1. **Assets**: Ensure you have uploaded the `public/build` folder from your local machine after running `npm run build`. +2. **Storage Link**: Create a file named `link.php` in your `public/` directory with this content: + ```php + [!IMPORTANT] +> **Web Server Root**: Ensure your domain/subdomain points to the `/public` directory of this project, not the root folder. + +## πŸ“‘ API Endpoints + +### Public CA Certificates +`GET /api/public/ca-certificates` +Returns Root and Intermediate CA certificates in JSON format. + +### Authenticated Certificates +`GET /api/v1/certificates` +Retrieves user-specific leaf certificates. Requires `X-API-KEY` header. + +## πŸ“¦ License +Refer to the [LICENSE](LICENSE) file for details. diff --git a/app/Helpers/MenuHelper.php b/app/Helpers/MenuHelper.php new file mode 100644 index 0000000..6698ca5 --- /dev/null +++ b/app/Helpers/MenuHelper.php @@ -0,0 +1,223 @@ + 'Dashboard', + 'icon' => 'dashboard', + 'route_name' => 'dashboard', + ], + [ + 'name' => 'Certificate', + 'icon' => 'certificate', + 'subItems' => [ + ['name' => 'Certificate Management', 'route_name' => 'certificate.index', 'pro' => false], + ['name' => 'Create Certificate', 'route_name' => 'certificate.create', 'pro' => false], + ], + ], + [ + 'name' => 'Calendar', + 'icon' => 'calendar', + 'route_name' => 'calendar', + ], + [ + 'name' => 'Forms', + 'icon' => 'forms', + 'subItems' => [ + ['name' => 'Form Elements', 'route_name' => 'form-elements', 'pro' => false], + ], + ], + [ + 'name' => 'Tables', + 'icon' => 'tables', + 'subItems' => [ + ['name' => 'Basic Tables', 'route_name' => 'basic-tables', 'pro' => false] + ], + ], + [ + 'name' => 'API Keys', + 'icon' => 'api-key', + 'route_name' => 'api-keys.index', + ], + [ + 'name' => 'Pages', + 'icon' => 'pages', + 'subItems' => [ + ['name' => 'Blank Page', 'route_name' => 'blank', 'pro' => false], + ['name' => '404 Error', 'route_name' => 'error-404', 'pro' => false], + // ['name' => 'API Keys', 'route_name' => 'api-keys.index', 'pro' => false, 'new' => true] + ], + ], + ]; + } + + public static function getMyAccountItems() + { + return [ + [ + 'name' => 'User Profile', + 'icon' => 'user-profile', + 'route_name' => 'profile', + ], + [ + 'name' => 'Account Settings', + 'icon' => 'settings', + 'route_name' => 'settings', + ], + ]; + } + + public static function getOthersItems() + { + return [ + [ + 'name' => 'Charts', + 'icon' => 'charts', + 'subItems' => [ + ['name' => 'Line Chart', 'route_name' => 'line-chart', 'pro' => false], + ['name' => 'Bar Chart', 'route_name' => 'bar-chart', 'pro' => false] + ], + ], + [ + 'name' => 'UI Elements', + 'icon' => 'ui-elements', + 'subItems' => [ + ['name' => 'Alerts', 'route_name' => 'alerts', 'pro' => false], + ['name' => 'Avatar', 'route_name' => 'avatars', 'pro' => false], + ['name' => 'Badge', 'route_name' => 'badges', 'pro' => false], + ['name' => 'Buttons', 'route_name' => 'buttons', 'pro' => false], + ['name' => 'Images', 'route_name' => 'images', 'pro' => false], + ['name' => 'Videos', 'route_name' => 'videos', 'pro' => false], + ], + ], + [ + 'name' => 'Authentication', + 'icon' => 'authentication', + 'subItems' => [ + ['name' => 'Sign In', 'route_name' => 'signin', 'pro' => false], + ['name' => 'Sign Up', 'route_name' => 'signup', 'pro' => false], + ], + ], + ]; + } + + public static function getMenuGroups() + { + $groups = []; + + // Admin Menu + if (auth()->check() && auth()->user()->isAdmin()) { + $groups[] = [ + 'title' => 'Admin Management', + 'items' => [ + [ + 'name' => 'User Management', + 'icon' => 'users', + 'route_name' => 'admin.users.index', + ], + [ + 'name' => 'Root CA Management', + 'icon' => 'certificate', + 'route_name' => 'admin.root-ca.index', + ], + ] + ]; + } + + // Filter Main Items based on role + $mainItems = self::getMainNavItems(); + if (!auth()->check() || !auth()->user()->isAdmin()) { + $mainItems = array_values(array_filter($mainItems, function ($item) { + return in_array($item['name'], ['Dashboard', 'Certificate', 'API Keys']); + })); + } + + // Standard Menus + $groups[] = [ + 'title' => 'Menu', + 'items' => $mainItems + ]; + + $groups[] = [ + 'title' => 'My Account', + 'items' => self::getMyAccountItems() + ]; + + // Others - Admin Only + if (auth()->check() && auth()->user()->isAdmin()) { + $groups[] = [ + 'title' => 'Others', + 'items' => self::getOthersItems() + ]; + } + + return $groups; + } + + public static function isActive($item) + { + if (isset($item['route_name'])) { + return request()->routeIs($item['route_name']); + } + + if (isset($item['subItems'])) { + foreach ($item['subItems'] as $subItem) { + if (isset($subItem['route_name']) && request()->routeIs($subItem['route_name'])) { + return true; + } + } + } + + return false; + } + + public static function getIconSvg($iconName) + { + $icons = [ + + 'certificate' => '', + + 'dashboard' => '', + + 'settings' => '', + + 'ecommerce' => '', + + 'calendar' => '', + + 'user-profile' => '', + + 'task' => '', + + 'forms' => '', + + 'tables' => '', + + 'pages' => '', + + 'charts' => '', + + 'ui-elements' => '', + + 'authentication' => '', + + 'chat' => '', + + 'support-ticket' => '', + + 'users' => '', + + 'email' => '', + + 'api-key' => '', + + ]; + + return $icons[$iconName] ?? ''; + } +} diff --git a/app/Http/Controllers/Admin/RootCaController.php b/app/Http/Controllers/Admin/RootCaController.php new file mode 100644 index 0000000..6034f34 --- /dev/null +++ b/app/Http/Controllers/Admin/RootCaController.php @@ -0,0 +1,45 @@ +map(function($cert) { + $cert->status = ($cert->valid_to > now()) ? 'valid' : 'expired'; + return $cert; + }); + + return view('pages.admin.root-ca.index', [ + 'certificates' => $certificates, + 'title' => 'Root CA Management' + ]); + } + + public function renew(Request $request, CaCertificate $certificate) + { + $days = (int) $request->input('days', 3650); + + try { + $newData = app(\App\Services\OpenSslService::class)->renewCaCertificate($certificate, $days); + + $certificate->update([ + 'cert_content' => $newData['cert_content'], + 'serial_number' => $newData['serial_number'], + 'valid_from' => $newData['valid_from'], + 'valid_to' => $newData['valid_to'], + ]); + + return redirect()->back()->with('success', 'Certificate renewed successfully (Re-signed).'); + } catch (\Exception $e) { + return redirect()->back()->with('error', 'Renewal failed: ' . $e->getMessage()); + } + } +} diff --git a/app/Http/Controllers/Api/CertificateApiController.php b/app/Http/Controllers/Api/CertificateApiController.php new file mode 100644 index 0000000..33097e7 --- /dev/null +++ b/app/Http/Controllers/Api/CertificateApiController.php @@ -0,0 +1,47 @@ +get('authenticated_user'); + + $certificates = $user->certificates() + ->latest() + ->get([ + 'uuid', + 'common_name', + 'organization', + 'san', + 'valid_from', + 'valid_to', + 'cert_content', + 'key_content' + ]) + ->map(function ($cert) { + return [ + 'id' => $cert->uuid, + 'common_name' => $cert->common_name, + 'organization' => $cert->organization, + 'san' => $cert->san, + 'issued_at' => $cert->valid_from->toIso8601String(), + 'expires_at' => $cert->valid_to->toIso8601String(), + 'certificate' => $cert->cert_content, + 'private_key' => $cert->key_content, + ]; + }); + + return response()->json([ + 'success' => true, + 'data' => $certificates + ]); + } +} diff --git a/app/Http/Controllers/Api/PublicCaController.php b/app/Http/Controllers/Api/PublicCaController.php new file mode 100644 index 0000000..6da0ebe --- /dev/null +++ b/app/Http/Controllers/Api/PublicCaController.php @@ -0,0 +1,37 @@ +get(['common_name', 'ca_type', 'serial_number', 'valid_to', 'cert_content']) + ->map(function ($cert) { + return [ + 'name' => $cert->common_name, + 'type' => $cert->ca_type, + 'serial' => $cert->serial_number, + 'expires_at' => $cert->valid_to->toIso8601String(), + 'certificate' => $cert->cert_content, + ]; + }); + + return response()->json([ + 'success' => true, + 'data' => $certificates + ]); + } +} diff --git a/app/Http/Controllers/ApiKeyController.php b/app/Http/Controllers/ApiKeyController.php new file mode 100644 index 0000000..2bd0a97 --- /dev/null +++ b/app/Http/Controllers/ApiKeyController.php @@ -0,0 +1,121 @@ +query('search'); + $perPage = $request->query('per_page', 10); + + $query = Auth::user()->apiKeys()->latest(); + + if ($search) { + $query->where('name', 'like', '%' . $search . '%'); + } + + $apiKeys = $query->paginate($perPage)->withQueryString(); + + if ($request->ajax()) { + return view('api-keys.partials.table', compact('apiKeys'))->render(); + } + + return view('api-keys.index', compact('apiKeys')); + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|string|max:255', + ]); + + $key = ApiKey::generate(); + + Auth::user()->apiKeys()->create([ + 'name' => $request->name, + 'key' => $key, + ]); + + return back()->with('success', 'API Key generated successfully.') + ->with('generated_key', $key); + } + + public function destroy(ApiKey $apiKey) + { + if ($apiKey->user_id !== Auth::id()) { + abort(403); + } + + $apiKey->delete(); + + return back()->with('success', 'API Key deleted successfully.'); + } + + public function toggle(ApiKey $apiKey) + { + if ($apiKey->user_id !== Auth::id()) { + abort(403); + } + + $apiKey->update([ + 'is_active' => !$apiKey->is_active + ]); + + if (request()->wantsJson()) { + return response()->json(['success' => true, 'message' => 'API Key status updated successfully.']); + } + + return back()->with('success', 'API Key status updated successfully.'); + } + + public function regenerate(ApiKey $apiKey) + { + if ($apiKey->user_id !== Auth::id()) { + abort(403); + } + + $newKey = ApiKey::generate(); + + $apiKey->update([ + 'key' => $newKey, + 'last_used_at' => null, // Reset usage + ]); + + if (request()->wantsJson()) { + return response()->json([ + 'success' => true, + 'message' => 'API Key regenerated successfully.', + 'new_key' => $newKey + ]); + } + + return back()->with('success', 'API Key regenerated successfully.') + ->with('generated_key', $newKey); + } + + public function update(Request $request, ApiKey $apiKey) + { + if ($apiKey->user_id !== Auth::id()) { + abort(403); + } + + $request->validate([ + 'name' => 'required|string|max:255', + ]); + + $apiKey->update([ + 'name' => $request->name, + ]); + + if (request()->wantsJson()) { + return response()->json(['success' => true, 'message' => 'API Key renamed successfully.']); + } + + return back()->with('success', 'API Key renamed successfully.'); + } +} diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php new file mode 100644 index 0000000..8a257e0 --- /dev/null +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -0,0 +1,42 @@ + 'Forgot Password']); + } + + /** + * Send a reset link to the given user. + */ + public function sendResetLinkEmail(Request $request) + { + $request->validate(['email' => 'required|email']); + + // We will send the password reset link to this user. Once we have attempted + // to send the link, we will examine the response then see the message we + // need to show to the user. Finally, we'll send out a proper response. + $status = Password::sendResetLink( + $request->only('email') + ); + + if ($status === Password::RESET_LINK_SENT) { + return back()->with(['status' => __($status)]); + } + + throw ValidationException::withMessages([ + 'email' => [__($status)], + ]); + } +} diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php new file mode 100644 index 0000000..d71a9d0 --- /dev/null +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -0,0 +1,70 @@ +with( + ['token' => $token, 'email' => $request->email, 'title' => 'Reset Password'] + ); + } + + /** + * Reset the given user's password. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse + */ + public function reset(Request $request) + { + $request->validate([ + 'token' => 'required', + 'email' => 'required|email', + 'password' => 'required|confirmed|min:8', + ]); + + // Here we will attempt to reset the user's password. If it is successful we + // will update the password on an actual user model and persist it to the + // database. Otherwise we will parse the error and return the response. + $status = Password::reset( + $request->only('email', 'password', 'password_confirmation', 'token'), + function ($user, $password) { + $user->forceFill([ + 'password' => Hash::make($password) + ])->setRememberToken(Str::random(60)); + + $user->save(); + + event(new PasswordReset($user)); + } + ); + + // If the password was successfully reset, we will redirect the user back to + // the application's home authenticated view. If there is an error we can + // redirect them back to where they came from with their error message. + if ($status === Password::PASSWORD_RESET) { + return redirect()->route('signin')->with('status', __($status)); + } + + throw ValidationException::withMessages([ + 'email' => [__($status)], + ]); + } +} diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php new file mode 100644 index 0000000..5366b97 --- /dev/null +++ b/app/Http/Controllers/AuthController.php @@ -0,0 +1,412 @@ + 'Sign In']); + } + + public function authenticate(Request $request) + { + $credentials = $request->validate([ + 'email' => ['required', 'email'], + 'password' => ['required'], + ]); + + // Find user by email + $user = User::where('email', $credentials['email'])->first(); + + // Check if user exists and is social-only (no password set) + if ($user && !$user->password) { + return back()->withErrors([ + 'email' => 'This account was created using social login. Please sign in with ' . + ($user->google_id ? 'Google' : 'GitHub') . ' instead.', + ])->onlyInput('email'); + } + + $remember = $request->boolean('remember'); + + if (Auth::attempt($credentials, $remember)) { + $user = Auth::user(); + + + $this->recordLogin($user, 'credentials'); + + $request->session()->regenerate(); + return redirect()->intended('dashboard'); + } + + return back()->withErrors([ + 'email' => 'The provided credentials do not match our records.', + ])->onlyInput('email'); + } + + public function signup() + { + return view('pages.auth.signup', ['title' => 'Sign Up']); + } + + public function store(Request $request) + { + $validated = $request->validate([ + 'fname' => 'required|string|max:255', + 'lname' => 'nullable|string|max:255', + 'email' => 'required|string|email|max:255|unique:users', + 'password' => 'required|string|min:8', + 'terms' => 'required|accepted', + ]); + + $roleInfo = \App\Models\Role::where('name', 'customer')->first(); + + $user = User::create([ + 'first_name' => $validated['fname'], + 'last_name' => $validated['lname'], + 'email' => $validated['email'], + 'password' => Hash::make($validated['password']), + 'role_id' => $roleInfo ? $roleInfo->id : null, + ]); + + Auth::login($user); + + event(new Registered($user)); + + return redirect()->route('verification.notice'); + } + + /** + * Unified social redirect method + */ + public function socialRedirect($provider, $context) + { + // Store context in session for callback + session(['social_auth_context' => $context]); + + return Socialite::driver($provider)->redirect(); + } + + /** + * Unified social callback method + */ + public function socialCallback($provider) + { + if (request()->has('error')) { + \Log::info('Social auth error: ' . request()->get('error')); + return redirect()->route('signin')->with('error', 'Authentication failed or was cancelled.'); + } + + try { + $socialUser = Socialite::driver($provider)->user(); + \Log::info('Social user retrieved', ['provider' => $provider, 'email' => $socialUser->email]); + } catch (\Exception $e) { + \Log::error('Socialite error: ' . $e->getMessage()); + return redirect()->route('signin')->with('error', 'Failed to authenticate with ' . ucfirst($provider) . '.'); + } + + $context = session('social_auth_context', 'signin'); + \Log::info('Social auth context', ['context' => $context, 'is_authenticated' => Auth::check()]); + session()->forget('social_auth_context'); + + // Handle 'connect' context - user wants to link social account + if ($context === 'connect') { + if (!Auth::check()) { + \Log::warning('Connect attempt without authentication'); + return redirect()->route('signin')->with('error', 'Please sign in first to connect your social account.'); + } + \Log::info('Handling social connect from settings'); + return $this->connectSocialAccount($provider, $socialUser); + } + + // If user is already authenticated (shouldn't happen for signin/signup) + if (Auth::check()) { + \Log::info('User already authenticated, treating as connect'); + return $this->connectSocialAccount($provider, $socialUser); + } + + // Handle based on context + if ($context === 'signin') { + \Log::info('Handling social signin'); + return $this->handleSocialSignin($provider, $socialUser); + } else { + \Log::info('Handling social signup'); + return $this->handleSocialSignup($provider, $socialUser); + } + } + + /** + * Connect social account to existing authenticated user + */ + protected function connectSocialAccount($provider, $socialUser) + { + $user = Auth::user(); + + $updateData = [ + $provider . '_id' => $socialUser->id, + $provider . '_token' => $socialUser->token, + $provider . '_refresh_token' => $socialUser->refreshToken, + ]; + + // Ensure update persists + $user->forceFill($updateData)->save(); + + $this->recordLogin($user, $provider); + + \Log::info('Social account connected for user', ['user_id' => $user->id, 'provider' => $provider]); + + return redirect()->route('settings')->with('success', ucfirst($provider) . ' account connected successfully.'); + } + + /** + * Handle social signin (only for existing users) + */ + protected function handleSocialSignin($provider, $socialUser) + { + // Try to find user by provider ID first + $user = User::where($provider . '_id', $socialUser->id)->first(); + + // If user not found by ID + if (!$user) { + // Check if user exists by email just to provide a helpful error message + if ($socialUser->email && User::where('email', $socialUser->email)->exists()) { + $this->revokeSocialToken($provider, $socialUser->token); + return redirect()->route('signin')->with('error', "An account with this email exists but is not connected to " . ucfirst($provider) . ". Please log in with your password and connect your social account from Settings."); + } + + // Genuinely no account found + $this->revokeSocialToken($provider, $socialUser->token); + return redirect()->route('signin')->with('error', 'No account found with this ' . ucfirst($provider) . ' account. Please sign up first.'); + } + + // Proceed with login for connected user + + // Update login tracking + + + $this->recordLogin($user, $provider); + + Auth::login($user); + + return redirect()->route('dashboard'); + } + + /** + * Handle social signup (only for new users) + */ + protected function handleSocialSignup($provider, $socialUser) + { + // Check if user already exists by email + $existingUser = User::where('email', $socialUser->email)->first(); + + if ($existingUser) { + return redirect()->route('signup')->withErrors([ + 'email' => 'An account with this email already exists. Please sign in instead.', + ]); + } + + // Download and save avatar + $avatarPath = null; + if ($socialUser->avatar) { + $avatarPath = $this->downloadSocialAvatar($socialUser->avatar, $socialUser->email); + } + + // Parse name + $nameParts = explode(' ', $socialUser->name ?? $socialUser->email, 2); + $firstName = $nameParts[0]; + $lastName = $nameParts[1] ?? ''; + + // Store user data in session for password setup + session([ + 'needs_password_setup' => true, + 'social_signup_provider' => $provider, + 'social_signup_name' => $socialUser->name ?? $socialUser->email, + 'social_signup_email' => $socialUser->email, + 'social_signup_first_name' => $firstName, + 'social_signup_last_name' => $lastName, + 'social_signup_avatar' => $avatarPath ? asset('storage/' . $avatarPath) : null, + 'social_signup_avatar_path' => $avatarPath, + 'social_signup_provider_id' => $socialUser->id, + 'social_signup_provider_token' => $socialUser->token, + 'social_signup_provider_refresh_token' => $socialUser->refreshToken, + ]); + + \Log::info('Social signup - redirecting to password setup', ['email' => $socialUser->email]); + + // Redirect to password setup page + return redirect()->route('setup-password'); + } + + /** + * Download social avatar and save to storage + */ + protected function downloadSocialAvatar($avatarUrl, $userEmail) + { + try { + // Generate unique filename + $filename = 'avatars/' . Str::slug($userEmail) . '_' . time() . '.jpg'; + + // Download image content + $imageContent = file_get_contents($avatarUrl); + + if ($imageContent === false) { + return null; + } + + // Save to storage + Storage::disk('public')->put($filename, $imageContent); + + return $filename; + } catch (\Exception $e) { + // If download fails, return null (user will have default avatar) + \Log::error('Failed to download social avatar: ' . $e->getMessage()); + return null; + } + } + + /** + * Show password setup page for social signup users + */ + public function showPasswordSetup() + { + // Check if user has the session flag + if (!session('needs_password_setup')) { + return redirect()->route('dashboard'); + } + + return view('pages.auth.setup-password', ['title' => 'Setup Password']); + } + + /** + * Complete password setup for social signup users + */ + public function completePasswordSetup(Request $request) + { + // Validate session + if (!session('needs_password_setup')) { + return redirect()->route('dashboard'); + } + + // Validate input + $validated = $request->validate([ + 'password' => 'required|string|min:8|confirmed', + ]); + + // Get data from session + $provider = session('social_signup_provider'); + $email = session('social_signup_email'); + $firstName = session('social_signup_first_name'); + $lastName = session('social_signup_last_name'); + $avatarPath = session('social_signup_avatar_path'); + $providerId = session('social_signup_provider_id'); + $providerToken = session('social_signup_provider_token'); + $providerRefreshToken = session('social_signup_provider_refresh_token'); + + // Create user + $roleInfo = \App\Models\Role::where('name', 'customer')->first(); + + $user = User::create([ + 'first_name' => $firstName, + 'last_name' => $lastName, + 'email' => $email, + 'avatar' => $avatarPath, + 'password' => Hash::make($validated['password']), + 'role_id' => $roleInfo ? $roleInfo->id : null, + $provider . '_id' => $providerId, + $provider . '_token' => $providerToken, + $provider . '_refresh_token' => $providerRefreshToken, + ]); + + $this->recordLogin($user, $provider); + + // Clear session data + session()->forget([ + 'needs_password_setup', + 'social_signup_provider', + 'social_signup_name', + 'social_signup_email', + 'social_signup_first_name', + 'social_signup_last_name', + 'social_signup_avatar', + 'social_signup_avatar_path', + 'social_signup_provider_id', + 'social_signup_provider_token', + 'social_signup_provider_refresh_token', + ]); + + // Login user + Auth::login($user); + + \Log::info('Social signup completed with password setup', ['user_id' => $user->id]); + + return redirect()->route('dashboard')->with('success', 'Welcome! Your account has been created successfully.'); + } + + + public function logout(Request $request) + { + Auth::logout(); + $request->session()->invalidate(); + $request->session()->regenerateToken(); + return redirect()->route('signin'); + } + + /** + * Handle manual GET access to logout (e.g. typing in URL) + */ + public function logoutGet() + { + if (Auth::check()) { + return redirect()->route('dashboard')->with('error', 'For security reasons, you cannot logout by typing the URL. Please use the Logout button.'); + } + return redirect()->route('signin'); + } + + + + /** + * Record login history and enforce limits + */ + protected function recordLogin(User $user, $provider) + { + $loginAt = now(); + + LoginHistory::create([ + 'user_id' => $user->id, + 'ip_address' => request()->ip(), + 'user_agent' => request()->userAgent(), + 'provider' => $provider, + 'login_at' => $loginAt, + ]); + + // Cleanup old records (older than 1 month) + LoginHistory::where('user_id', $user->id) + ->where('login_at', '<', $loginAt->subMonth()) + ->delete(); + + // Cleanup excess records (keep only latest 10) + $idsToKeep = LoginHistory::where('user_id', $user->id) + ->latest('login_at') + ->take(10) + ->pluck('id'); + + LoginHistory::where('user_id', $user->id) + ->whereNotIn('id', $idsToKeep) + ->delete(); + } +} diff --git a/app/Http/Controllers/CertificateController.php b/app/Http/Controllers/CertificateController.php new file mode 100644 index 0000000..2b1e872 --- /dev/null +++ b/app/Http/Controllers/CertificateController.php @@ -0,0 +1,360 @@ +sslService = $sslService; + } + + public function index(Request $request) + { + $caReady = CaCertificate::where('ca_type', 'root')->exists() && + CaCertificate::where('ca_type', 'intermediate_2048')->exists() && + CaCertificate::where('ca_type', 'intermediate_4096')->exists(); + + $perPage = $request->input('per_page', 10); + $search = $request->input('search'); + + $query = Certificate::where('user_id', Auth::id()); + + if ($search) { + $query->where(function($q) use ($search) { + $q->where('common_name', 'like', "%{$search}%") + ->orWhere('serial_number', 'like', "%{$search}%") + ->orWhere('san', 'like', "%{$search}%"); + }); + } + + $certificates = $query->latest()->paginate($perPage)->withQueryString(); + + if ($request->ajax()) { + return view('pages.certificate.partials.table', [ + 'certificates' => $certificates, + ])->render(); + } + + return view('pages.certificate.index', [ + 'title' => 'Certificate Management', + 'caReady' => $caReady, + 'certificates' => $certificates, + 'perPage' => $perPage, + 'search' => $search, + 'defaults' => Config::get('openssl.ca_leaf_default') + ]); + } + + public function downloadCa($type) + { + // map legacy or simple type to specific ca_type + $caType = match($type) { + 'root' => 'root', + 'intermediate', 'int_4096' => 'intermediate_4096', + 'int_2048' => 'intermediate_2048', + default => $type + }; + + $ca = CaCertificate::where('ca_type', $caType)->firstOrFail(); + + $configKey = match($caType) { + 'root' => 'openssl.ca_root.organizationName', + 'intermediate_4096' => 'openssl.ca_4096.organizationName', + 'intermediate_2048' => 'openssl.ca_2048.organizationName', + default => 'app.name' + }; + + $orgName = config($configKey); + $brand = Str::slug($orgName, '_'); + $filename = "{$brand}_ca_{$type}.crt"; + + return response($ca->cert_content) + ->header('Content-Type', 'application/x-x509-ca-cert') + ->header('Content-Disposition', "attachment; filename={$filename}"); + } + + public function downloadCaBundle() + { + $root = CaCertificate::where('ca_type', 'root')->firstOrFail(); + $int2048 = CaCertificate::where('ca_type', 'intermediate_2048')->firstOrFail(); + $int4096 = CaCertificate::where('ca_type', 'intermediate_4096')->firstOrFail(); + + // Bundle includes all for convenience + $bundle = $int4096->cert_content . "\n" . $int2048->cert_content . "\n" . $root->cert_content; + + $brand = Str::slug(config('openssl.ca_root.organizationName'), '_'); + $filename = "{$brand}_ca-bundle.crt"; + + return response($bundle) + ->header('Content-Type', 'application/x-x509-ca-cert') + ->header('Content-Disposition', "attachment; filename={$filename}"); + } + + public function downloadCaAndroid() + { + $root = CaCertificate::where('ca_type', 'root')->firstOrFail(); + + // Convert PEM to DER + $certPem = $root->cert_content; + $begin = "-----BEGIN CERTIFICATE-----"; + $end = "-----END CERTIFICATE-----"; + $der = base64_decode($certPem); + + $brand = Str::slug(config('openssl.ca_root.organizationName'), '_'); + $filename = "{$brand}_root-ca.der"; + + return response($der) + ->header('Content-Type', 'application/pkix-cert') + ->header('Content-Disposition', "attachment; filename={$filename}"); + } + + public function downloadInstaller() + { + $appUrl = config('app.url'); + $brand = Str::slug(config('openssl.ca_root.organizationName'), '_'); + + $script = <<header('Content-Type', 'application/x-msdos-program') + ->header('Content-Disposition', "attachment; filename={$brand}_install_certs.bat"); + } + + public function create() + { + return view('pages.certificate.create', [ + 'title' => 'Generate Certificate', + 'defaults' => Config::get('openssl.ca_leaf_default') + ]); + } + + public function setupCa() + { + if (CaCertificate::count() > 0) { + return redirect()->route('certificate.index')->with('error', 'CA already initialized.'); + } + + if ($this->sslService->setupCa()) { + return redirect()->route('certificate.index')->with('success', 'Root and Intermediate CA successfully initialized.'); + } + + return redirect()->route('certificate.index')->with('error', 'Failed to initialize CA.'); + } + + public function generate(Request $request) + { + $validated = $request->validate([ + 'common_name' => 'required|string|max:255', + 'config_mode' => 'required|in:default,manual', + 'organization' => 'nullable|required_if:config_mode,manual|string|max:255', + 'locality' => 'nullable|required_if:config_mode,manual|string|max:255', + 'state' => 'nullable|required_if:config_mode,manual|string|max:255', + 'country' => 'nullable|required_if:config_mode,manual|string|size:2', + 'san' => 'nullable|string', + 'key_bits' => 'required|in:2048,4096', + ]); + + try { + // Apply defaults if mode is 'default' + if ($validated['config_mode'] === 'default') { + $defaults = Config::get('openssl.ca_leaf_default'); + $validated['organization'] = $defaults['organizationName']; + $validated['locality'] = $defaults['localityName']; + $validated['state'] = $defaults['stateOrProvinceName']; + $validated['country'] = $defaults['countryName']; + } + + $result = $this->sslService->generateLeaf($validated); + + Certificate::create([ + 'user_id' => Auth::id(), + 'common_name' => $validated['common_name'], + 'organization' => $validated['organization'], + 'locality' => $validated['locality'], + 'state' => $validated['state'], + 'country' => $validated['country'], + 'san' => $validated['san'], + 'key_bits' => $validated['key_bits'], + 'serial_number' => $this->sslService->formatSerialToHex($result['serial']), + 'cert_content' => $result['cert'], + 'key_content' => $result['key'], + 'csr_content' => $result['csr'], + 'valid_from' => $result['valid_from'] ?? null, + 'valid_to' => $result['valid_to'] ?? null, + ]); + + return redirect()->route('certificate.index')->with('success', 'Certificate generated successfully.'); + } catch (\Exception $e) { + return redirect()->back()->withInput()->with('error', $e->getMessage()); + } + } + public function regenerate(Certificate $certificate) + { + $this->authorizeOwner($certificate); + + try { + $data = [ + 'common_name' => $certificate->common_name, + 'organization' => $certificate->organization, + 'locality' => $certificate->locality, + 'state' => $certificate->state, + 'country' => $certificate->country, + 'san' => $certificate->san, + 'key_bits' => $certificate->key_bits, + ]; + + $result = $this->sslService->generateLeaf($data); + + // Update existing record with new content + $certificate->update([ + 'serial_number' => $this->sslService->formatSerialToHex($result['serial']), + 'cert_content' => $result['cert'], + 'key_content' => $result['key'], + 'csr_content' => $result['csr'], + 'valid_from' => $result['valid_from'] ?? null, + 'valid_to' => $result['valid_to'] ?? null, + 'created_at' => now(), // Refresh timestamp + ]); + + return redirect()->route('certificate.index')->with('success', 'Certificate regenerated successfully.'); + } catch (\Exception $e) { + return redirect()->route('certificate.index')->with('error', 'Failed to regenerate: ' . $e->getMessage()); + } + } + + + public function downloadZip(Certificate $certificate) + { + $this->authorizeOwner($certificate); + + $tempFile = tempnam(sys_get_temp_dir(), 'cert_zip'); + $zip = new ZipArchive; + + if ($zip->open($tempFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) { + return redirect()->back()->with('error', 'Failed to create ZIP archive.'); + } + + $filenameBase = preg_replace('/[^a-zA-Z0-9-_\.]/', '_', $certificate->common_name); + + $zip->addFromString("{$filenameBase}.pem", $certificate->cert_content); + $zip->addFromString("{$filenameBase}.key", $certificate->key_content); + if ($certificate->csr_content) { + $zip->addFromString("{$filenameBase}.csr", $certificate->csr_content); + } + $zip->close(); + + return response()->download($tempFile, "cert_{$filenameBase}.zip")->deleteFileAfterSend(true); + } + + public function downloadP12(Certificate $certificate) + { + $this->authorizeOwner($certificate); + + $password = '112133'; // Default password from user request + if (openssl_pkcs12_export($certificate->cert_content, $p12, $certificate->key_content, $password)) { + $filenameBase = preg_replace('/[^a-zA-Z0-9-_\.]/', '_', $certificate->common_name); + return response($p12) + ->header('Content-Type', 'application/x-pkcs12') + ->header('Content-Disposition', "attachment; filename={$filenameBase}.p12"); + } + + return redirect()->back()->with('error', 'Failed to generate P12 file.'); + } + + public function viewFile(Certificate $certificate, $type) + { + $this->authorizeOwner($certificate); + + $content = match($type) { + 'cert' => $certificate->cert_content, + 'key' => $certificate->key_content, + 'csr' => $certificate->csr_content, + default => abort(404) + }; + + return response($content)->header('Content-Type', 'text/plain'); + } + + public function delete(Certificate $certificate) + { + $this->authorizeOwner($certificate); + $certificate->delete(); + return redirect()->route('certificate.index')->with('success', 'Certificate deleted successfully.'); + } + + protected function authorizeOwner(Certificate $certificate) + { + if ($certificate->user_id !== Auth::id()) { + abort(403); + } + } +} diff --git a/app/Http/Controllers/ChartController.php b/app/Http/Controllers/ChartController.php new file mode 100644 index 0000000..5fae080 --- /dev/null +++ b/app/Http/Controllers/ChartController.php @@ -0,0 +1,18 @@ + 'Line Chart']); + } + + public function barChart() + { + return view('pages.chart.bar-chart', ['title' => 'Bar Chart']); + } +} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php new file mode 100644 index 0000000..8677cd5 --- /dev/null +++ b/app/Http/Controllers/Controller.php @@ -0,0 +1,8 @@ +apiKeys()->count(); + $totalCertificates = $user->certificates()->count(); + $activeCertificates = $user->certificates() + ->where('valid_to', '>', now()) + ->count(); + $expiringSoonCount = $user->certificates() + ->where('valid_to', '>', now()) + ->where('valid_to', '<=', now()->addDays(14)) + ->count(); + + // Recent Activity + $recentCertificates = $user->certificates() + ->latest() + ->limit(5) + ->get(); + + $recentApiActivity = $user->apiKeys() + ->whereNotNull('last_used_at') + ->orderBy('last_used_at', 'desc') + ->limit(5) + ->get(); + + // Chart Data: Certificates issued per month (last 6 months) + $months = []; + $issuanceData = []; + for ($i = 5; $i >= 0; $i--) { + $date = now()->subMonths($i); + $months[] = $date->format('M'); + $issuanceData[] = $user->certificates() + ->whereYear('created_at', $date->year) + ->whereMonth('created_at', $date->month) + ->count(); + } + + return view('pages.dashboard', compact( + 'totalApiKeys', + 'totalCertificates', + 'activeCertificates', + 'expiringSoonCount', + 'recentCertificates', + 'recentApiActivity', + 'months', + 'issuanceData' + )); + } +} diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php new file mode 100644 index 0000000..0b5194a --- /dev/null +++ b/app/Http/Controllers/PageController.php @@ -0,0 +1,29 @@ + 'Calendar']); + } + + + public function blank() + { + return view('pages.blank', ['title' => 'Blank']); + } + + public function error404() + { + return view('pages.errors.error-404', ['title' => 'Error 404']); + } + + public function php() + { + phpinfo(); + } +} diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php new file mode 100644 index 0000000..97dce4c --- /dev/null +++ b/app/Http/Controllers/ProfileController.php @@ -0,0 +1,60 @@ + 'User Profile', + 'user' => Auth::user(), + ]); + } + + /** + * Update the user's profile information. + */ + public function update(Request $request) + { + $user = Auth::user(); + + $validated = $request->validate([ + 'name' => 'sometimes|required|string|max:255', + 'first_name' => 'nullable|string|max:255', + 'last_name' => 'nullable|string|max:255', + 'email' => 'sometimes|required|email|max:255|unique:users,email,' . $user->id, + 'phone' => 'nullable|string|max:20', + 'bio' => 'nullable|string', + 'country' => 'nullable|string|max:255', + 'city_state' => 'nullable|string|max:255', + 'postal_code' => 'nullable|string|max:20', + 'tax_id' => 'nullable|string|max:50', + 'facebook' => 'nullable|url|max:255', + 'x_link' => 'nullable|url|max:255', + 'linkedin' => 'nullable|url|max:255', + 'instagram' => 'nullable|url|max:255', + 'avatar' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048', + ]); + + if ($request->hasFile('avatar')) { + // Delete old avatar if exists and it's not a default one + if ($user->avatar && Storage::disk('public')->exists($user->avatar)) { + Storage::disk('public')->delete($user->avatar); + } + $path = $request->file('avatar')->store('avatars', 'public'); + $validated['avatar'] = $path; + } + + $user->update($validated); + + return back()->with('success', 'Profile updated successfully.'); + } +} diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php new file mode 100644 index 0000000..80744b1 --- /dev/null +++ b/app/Http/Controllers/SettingsController.php @@ -0,0 +1,98 @@ + 'Account Settings', + 'user' => Auth::user()->load(['loginHistories' => fn($q) => $q->latest('login_at')]), + ]); + } + + /** + * Update user password. + */ + public function updatePassword(Request $request) + { + $validated = $request->validate([ + 'current_password' => ['required', 'current_password'], + 'password' => ['required', Password::defaults(), 'confirmed'], + ]); + + $request->user()->update([ + 'password' => Hash::make($validated['password']), + ]); + + return back()->with('success', 'Password updated successfully.'); + } + + + + /** + * Disconnect a social account. + */ + public function disconnectSocial(string $provider) + { + $user = Auth::user(); + + if (!in_array($provider, ['google', 'github'])) { + return back()->with('error', 'Invalid provider.'); + } + + // Revoke token on provider side if exists + $this->revokeSocialToken($provider, $user->{$provider . '_token'}); + + $user->update([ + "{$provider}_id" => null, + "{$provider}_token" => null, + "{$provider}_refresh_token" => null, + ]); + + return back()->with('success', ucfirst($provider) . ' account disconnected successfully.'); + } + + /** + * Delete user account. + */ + public function destroy(Request $request) + { + $request->validate([ + 'confirmation' => ['required', 'string', 'in:Yes I will delete my account'], + ]); + + $user = $request->user(); + + if ($user->isAdmin()) { + return back()->with('error', 'Administrator accounts cannot be deleted.'); + } + + // Revoke connected social tokens before deletion + foreach (['google', 'github'] as $provider) { + if ($user->{$provider . '_id'}) { + $this->revokeSocialToken($provider, $user->{$provider . '_token'}); + } + } + + Auth::logout(); + + $user->delete(); + + $request->session()->invalidate(); + $request->session()->regenerateToken(); + + return redirect()->route('signin')->with('success', 'Your account has been deleted.'); + } +} diff --git a/app/Http/Controllers/SidebarController.php b/app/Http/Controllers/SidebarController.php new file mode 100644 index 0000000..34fc9f8 --- /dev/null +++ b/app/Http/Controllers/SidebarController.php @@ -0,0 +1,183 @@ + 'Menu', + 'items' => [ + [ + 'icon' => 'grid-icon', + 'name' => 'Dashboard', + 'path' => '/dashboard', + ], + [ + 'icon' => 'bot-icon', + 'name' => 'AI Assistant', + 'new' => true, + 'subItems' => [ + ['name' => 'Text Generator', 'path' => '/text-generator'], + ['name' => 'Image Generator', 'path' => '/image-generator'], + ['name' => 'Code Generator', 'path' => '/code-generator'], + ['name' => 'Video Generator', 'path' => '/video-generator'], + ], + ], + [ + 'icon' => 'cart-icon', + 'name' => 'E-commerce', + 'new' => true, + 'subItems' => [ + ['name' => 'Products', 'path' => '/products-list'], + ['name' => 'Add Product', 'path' => '/add-product'], + ['name' => 'Billing', 'path' => '/billing'], + ['name' => 'Invoices', 'path' => '/invoices'], + ['name' => 'Single Invoice', 'path' => '/single-invoice'], + ['name' => 'Create Invoice', 'path' => '/create-invoice'], + ['name' => 'Transactions', 'path' => '/transactions'], + ['name' => 'Single Transaction', 'path' => '/single-transaction'], + ], + ], + [ + 'icon' => 'calendar-icon', + 'name' => 'Calendar', + 'path' => '/calendar', + ], + [ + 'icon' => 'user-circle-icon', + 'name' => 'User Profile', + 'path' => '/profile', + ], + [ + 'icon' => 'task-icon', + 'name' => 'Task', + 'subItems' => [ + ['name' => 'List', 'path' => '/task-list', 'pro' => false], + ['name' => 'Kanban', 'path' => '/task-kanban', 'pro' => false], + ], + ], + [ + 'icon' => 'list-icon', + 'name' => 'Forms', + 'subItems' => [ + ['name' => 'Form Elements', 'path' => '/form-elements', 'pro' => false], + ['name' => 'Form Layout', 'path' => '/form-layout', 'pro' => false], + ], + ], + [ + 'icon' => 'table-icon', + 'name' => 'Tables', + 'subItems' => [ + ['name' => 'Basic Tables', 'path' => '/basic-tables', 'pro' => false], + ['name' => 'Data Tables', 'path' => '/data-tables', 'pro' => false], + ], + ], + [ + 'icon' => 'page-icon', + 'name' => 'Pages', + 'subItems' => [ + ['name' => 'File Manager', 'path' => '/file-manager', 'pro' => false], + ['name' => 'Pricing Tables', 'path' => '/pricing-tables', 'pro' => false], + ['name' => 'Faqs', 'path' => '/faq', 'pro' => false], + ['name' => 'API Keys', 'path' => '/api-keys', 'new' => true], + ['name' => 'Integrations', 'path' => '/integrations', 'new' => true], + ['name' => 'Blank Page', 'path' => '/blank', 'pro' => false], + ['name' => '404 Error', 'path' => '/error-404', 'pro' => false], + ['name' => '500 Error', 'path' => '/error-500', 'pro' => false], + ['name' => '503 Error', 'path' => '/error-503', 'pro' => false], + ['name' => 'Coming Soon', 'path' => '/coming-soon', 'pro' => false], + ['name' => 'Maintenance', 'path' => '/maintenance', 'pro' => false], + ['name' => 'Success', 'path' => '/success', 'pro' => false], + ], + ], + ], + ], + [ + 'title' => 'Support', + 'items' => [ + [ + 'icon' => 'chat-icon', + 'name' => 'Chat', + 'path' => '/chat', + ], + [ + 'icon' => 'call-icon', + 'name' => 'Support Ticket', + 'new' => true, + 'subItems' => [ + ['name' => 'Ticket List', 'path' => '/support-tickets'], + ['name' => 'Ticket Reply', 'path' => '/support-ticket-reply'], + ], + ], + [ + 'icon' => 'mail-icon', + 'name' => 'Email', + 'subItems' => [ + ['name' => 'Inbox', 'path' => '/inbox', 'pro' => false], + ['name' => 'Details', 'path' => '/inbox-details', 'pro' => false], + ], + ], + ], + ], + [ + 'title' => 'Others', + 'items' => [ + [ + 'icon' => 'pie-chart-icon', + 'name' => 'Charts', + 'subItems' => [ + ['name' => 'Line Chart', 'path' => '/line-chart', 'pro' => false], + ['name' => 'Bar Chart', 'path' => '/bar-chart', 'pro' => false], + ['name' => 'Pie Chart', 'path' => '/pie-chart', 'pro' => false], + ], + ], + [ + 'icon' => 'box-cube-icon', + 'name' => 'UI Elements', + 'subItems' => [ + ['name' => 'Alerts', 'path' => '/alerts', 'pro' => false], + ['name' => 'Avatar', 'path' => '/avatars', 'pro' => false], + ['name' => 'Badge', 'path' => '/badge', 'pro' => false], + ['name' => 'Breadcrumb', 'path' => '/breadcrumb', 'pro' => false], + ['name' => 'Buttons', 'path' => '/buttons', 'pro' => false], + ['name' => 'Buttons Group', 'path' => '/buttons-group', 'pro' => false], + ['name' => 'Cards', 'path' => '/cards', 'pro' => false], + ['name' => 'Carousel', 'path' => '/carousel', 'pro' => false], + ['name' => 'Dropdowns', 'path' => '/dropdowns', 'pro' => false], + ['name' => 'Images', 'path' => '/image', 'pro' => false], + ['name' => 'Links', 'path' => '/links', 'pro' => false], + ['name' => 'List', 'path' => '/list', 'pro' => false], + ['name' => 'Modals', 'path' => '/modals', 'pro' => false], + ['name' => 'Notification', 'path' => '/notifications', 'pro' => false], + ['name' => 'Pagination', 'path' => '/pagination', 'pro' => false], + ['name' => 'Popovers', 'path' => '/popovers', 'pro' => false], + ['name' => 'Progressbar', 'path' => '/progress-bar', 'pro' => false], + ['name' => 'Ribbons', 'path' => '/ribbons', 'pro' => false], + ['name' => 'Spinners', 'path' => '/spinners', 'pro' => false], + ['name' => 'Tabs', 'path' => '/tabs', 'pro' => false], + ['name' => 'Tooltips', 'path' => '/tooltips', 'pro' => false], + ['name' => 'Videos', 'path' => '/videos', 'pro' => false], + ], + ], + [ + 'icon' => 'plug-in-icon', + 'name' => 'Authentication', + 'subItems' => [ + ['name' => 'Sign In', 'path' => '/signin', 'pro' => false], + ['name' => 'Sign Up', 'path' => '/signup', 'pro' => false], + ['name' => 'Reset Password', 'path' => '/reset-password', 'pro' => false], + ['name' => 'Two Step Verification', 'path' => '/two-step-verification', 'pro' => false], + ], + ], + ], + ], + ]; + + return view('components.sidebar', compact('menuGroups')); + } +} diff --git a/app/Http/Controllers/SuspendedController.php b/app/Http/Controllers/SuspendedController.php new file mode 100644 index 0000000..603ed37 --- /dev/null +++ b/app/Http/Controllers/SuspendedController.php @@ -0,0 +1,20 @@ +check() && !auth()->user()->isSuspended()) { + return redirect()->route('dashboard'); + } + + return view('pages.suspended', [ + 'title' => 'Account Suspended' + ]); + } +} diff --git a/app/Http/Controllers/UiController.php b/app/Http/Controllers/UiController.php new file mode 100644 index 0000000..67dfc42 --- /dev/null +++ b/app/Http/Controllers/UiController.php @@ -0,0 +1,48 @@ + 'Alerts']); + } + + public function avatars() + { + return view('pages.ui-elements.avatars', ['title' => 'Avatars']); + } + + public function badges() + { + return view('pages.ui-elements.badges', ['title' => 'Badges']); + } + + public function buttons() + { + return view('pages.ui-elements.buttons', ['title' => 'Buttons']); + } + + public function images() + { + return view('pages.ui-elements.images', ['title' => 'Images']); + } + + public function videos() + { + return view('pages.ui-elements.videos', ['title' => 'Videos']); + } + + public function formElements() + { + return view('pages.form.form-elements', ['title' => 'Form Elements']); + } + + public function basicTables() + { + return view('pages.tables.basic-tables', ['title' => 'Basic Tables']); + } +} diff --git a/app/Http/Controllers/UserManagementController.php b/app/Http/Controllers/UserManagementController.php new file mode 100644 index 0000000..6788813 --- /dev/null +++ b/app/Http/Controllers/UserManagementController.php @@ -0,0 +1,98 @@ +paginate(10); + + return view('pages.admin.users.index', [ + 'users' => $users + ]); + } + + public function destroy(User $user) + { + if ($user->id === auth()->id()) { + return back()->with('error', 'You cannot delete your own account.'); + } + + // Revoke Google Token + if ($user->google_token) { + $this->revokeSocialToken('google', $user->google_token); + } + + // Revoke GitHub Token + if ($user->github_token) { + $this->revokeSocialToken('github', $user->github_token); + } + + $user->delete(); + + return back()->with('success', 'User deleted successfully.'); + } + + public function sendResetLink(User $user) + { + $token = \Illuminate\Support\Facades\Password::createToken($user); + $user->sendPasswordResetNotification($token); + + return back()->with('success', 'Password reset link sent to ' . $user->email); + } + + public function toggleStatus(User $user) + { + if ($user->id === auth()->id()) { + return back()->with('error', 'You cannot change your own status.'); + } + + $newStatus = $user->status === 'suspended' ? 'active' : 'suspended'; + $user->update(['status' => $newStatus]); + + $message = $newStatus === 'suspended' + ? 'User account has been suspended.' + : 'User account has been activated.'; + + return back()->with('success', $message); + } + + public function sendVerification(User $user) + { + if ($user->hasVerifiedEmail()) { + return back()->with('message', 'User is already verified.'); + } + + $user->sendEmailVerificationNotification(); + + return back()->with('success', 'Verification link sent to ' . $user->email); + } + + public function updateEmail(Request $request, User $user) + { + if ($user->id === auth()->id()) { + return back()->with('error', 'You cannot update your own email from here.'); + } + + $validated = $request->validate([ + 'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email,' . $user->id], + ]); + + $user->forceFill([ + 'email' => $validated['email'], + 'email_verified_at' => null, // Reset verification status + ])->save(); + + return back()->with('success', 'User email updated. Verification status has been reset.'); + } +} diff --git a/app/Http/Controllers/VerificationController.php b/app/Http/Controllers/VerificationController.php new file mode 100644 index 0000000..823e63f --- /dev/null +++ b/app/Http/Controllers/VerificationController.php @@ -0,0 +1,66 @@ +user()->hasVerifiedEmail()) { + return redirect()->route('dashboard'); + } + + return view('pages.auth.verify-email', ['title' => 'Verify Email']); + } + + /** + * Mark the authenticated user's email address as verified. + */ + public function verify(Request $request, $id, $hash) + { + $user = \App\Models\User::findOrFail($id); + + if (! hash_equals((string) $hash, sha1($user->getEmailForVerification()))) { + abort(403); + } + + if ($user->hasVerifiedEmail()) { + return $this->redirectAfterVerification('Email already verified.'); + } + + if ($user->markEmailAsVerified()) { + event(new \Illuminate\Auth\Events\Verified($user)); + } + + return $this->redirectAfterVerification('Email verified successfully!'); + } + + private function redirectAfterVerification($message) + { + if (auth()->check()) { + return redirect()->route('dashboard')->with('success', $message); + } + + return redirect()->route('signin')->with('success', $message . ' You can now login.'); + } + + /** + * Resend the email verification notification. + */ + public function resend(Request $request) + { + if ($request->user()->hasVerifiedEmail()) { + return redirect()->route('dashboard'); + } + + $request->user()->sendEmailVerificationNotification(); + + return back()->with('success', 'Verification link sent!'); + } +} diff --git a/app/Http/Middleware/CheckApiKey.php b/app/Http/Middleware/CheckApiKey.php new file mode 100644 index 0000000..3992507 --- /dev/null +++ b/app/Http/Middleware/CheckApiKey.php @@ -0,0 +1,48 @@ +header('X-API-KEY'); + + if (!$keyString) { + return response()->json([ + 'success' => false, + 'message' => 'API Key is missing. Please provide it in the X-API-KEY header.' + ], 401); + } + + $apiKey = ApiKey::where('key', $keyString)->first(); + + if (!$apiKey || !$apiKey->is_active) { + return response()->json([ + 'success' => false, + 'message' => 'Invalid or inactive API Key.' + ], 401); + } + + // Update last used timestamp + $apiKey->update(['last_used_at' => now()]); + + // Put the user in the request context + $request->merge(['authenticated_user' => $apiKey->user]); + + // Alternatively, if we want to use Auth facade, we can manually log in the user for this request + // \Auth::login($apiKey->user); + + return $next($request); + } +} diff --git a/app/Http/Middleware/EnsureUserIsActive.php b/app/Http/Middleware/EnsureUserIsActive.php new file mode 100644 index 0000000..c4384b4 --- /dev/null +++ b/app/Http/Middleware/EnsureUserIsActive.php @@ -0,0 +1,29 @@ +isSuspended()) { + // Allow access to suspended page and logout + if ($request->routeIs('suspended') || $request->routeIs('logout')) { + return $next($request); + } + + // Redirect everything else to suspended page + return redirect()->route('suspended'); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/EnsureUserIsAdmin.php b/app/Http/Middleware/EnsureUserIsAdmin.php new file mode 100644 index 0000000..35bb65a --- /dev/null +++ b/app/Http/Middleware/EnsureUserIsAdmin.php @@ -0,0 +1,24 @@ +user() || ! $request->user()->isAdmin()) { + return redirect()->route('dashboard')->with('error', 'Administrator privileges required.'); + } + + return $next($request); + } +} diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php new file mode 100644 index 0000000..9fcfdbc --- /dev/null +++ b/app/Models/ApiKey.php @@ -0,0 +1,39 @@ + 'datetime', + 'is_active' => 'boolean', + ]; + + public function user() + { + return $this->belongsTo(User::class); + } + + public static function generate() + { + do { + $key = 'dvp_' . Str::random(60); + } while (static::where('key', $key)->exists()); + + return $key; + } +} diff --git a/app/Models/CaCertificate.php b/app/Models/CaCertificate.php new file mode 100644 index 0000000..3865822 --- /dev/null +++ b/app/Models/CaCertificate.php @@ -0,0 +1,35 @@ + 'datetime', + 'valid_to' => 'datetime', + ]; + + protected static function boot() + { + parent::boot(); + static::creating(function ($model) { + $model->uuid = (string) Str::uuid(); + }); + } +} diff --git a/app/Models/Certificate.php b/app/Models/Certificate.php new file mode 100644 index 0000000..ec55cdd --- /dev/null +++ b/app/Models/Certificate.php @@ -0,0 +1,35 @@ + 'datetime', + 'valid_to' => 'datetime', + ]; + + protected static function boot() + { + parent::boot(); + static::creating(function ($model) { + $model->uuid = (string) Str::uuid(); + }); + } + + public function user() + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Models/LoginHistory.php b/app/Models/LoginHistory.php new file mode 100644 index 0000000..8578258 --- /dev/null +++ b/app/Models/LoginHistory.php @@ -0,0 +1,25 @@ + 'datetime', + ]; + + public function user() + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Models/Role.php b/app/Models/Role.php new file mode 100644 index 0000000..058de53 --- /dev/null +++ b/app/Models/Role.php @@ -0,0 +1,21 @@ +hasMany(User::class); + } +} diff --git a/app/Models/User.php b/app/Models/User.php new file mode 100644 index 0000000..dcdf41a --- /dev/null +++ b/app/Models/User.php @@ -0,0 +1,155 @@ + */ + use HasFactory, Notifiable; + + /** + * The primary key type. + * + * @var string + */ + protected $keyType = 'string'; + + /** + * Indicates if the IDs are auto-incrementing. + * + * @var bool + */ + public $incrementing = false; + + /** + * Boot function from Laravel. + */ + protected static function booted() + { + static::creating(function ($user) { + if (empty($user->id)) { + do { + $id = \Illuminate\Support\Str::random(32); + } while (self::where('id', $id)->exists()); + + $user->id = $id; + } + }); + } + + /** + * The attributes that are mass assignable. + * + * @var list + */ + protected $fillable = [ + 'role_id', + 'email', + 'password', + 'avatar', + 'first_name', + 'last_name', + 'phone', + 'bio', + 'status', + 'country', + 'city_state', + 'postal_code', + 'tax_id', + 'facebook', + 'x_link', + 'linkedin', + 'instagram', + 'google_id', + 'google_token', + 'google_refresh_token', + 'github_id', + 'github_token', + 'github_refresh_token', + 'last_login_at', + 'last_login_provider', + 'last_login_ip', + 'last_login_user_agent', + ]; + + /** + * The attributes that should be hidden for serialization. + * + * @var list + */ + protected $hidden = [ + 'password', + 'remember_token', + ]; + + /** + * Get the attributes that should be cast. + * + * @return array + */ + protected function casts(): array + { + return [ + 'email_verified_at' => 'datetime', + 'password' => 'hashed', + ]; + } + + /** + * Get the user's full name. + * + * @return string + */ + public function getNameAttribute(): string + { + return trim($this->first_name . ' ' . ($this->last_name ?? '')); + } + + public function loginHistories() + { + return $this->hasMany(LoginHistory::class); + } + + public function role() + { + return $this->belongsTo(Role::class); + } + + /** + * Check if user is admin + */ + public function isAdmin(): bool + { + return $this->role && $this->role->name === 'admin'; + } + + /** + * Check if user is customer + */ + public function isCustomer(): bool + { + return $this->role && $this->role->name === 'customer'; + } + + /** + * Check if user is suspended + */ + public function isSuspended(): bool + { + return $this->status === 'suspended'; + } + public function apiKeys() + { + return $this->hasMany(ApiKey::class); + } + + public function certificates() + { + return $this->hasMany(Certificate::class); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php new file mode 100644 index 0000000..452e6b6 --- /dev/null +++ b/app/Providers/AppServiceProvider.php @@ -0,0 +1,24 @@ + 0) { + return false; + } + + $rootConfig = Config::get('openssl.ca_root'); + $int4096Config = Config::get('openssl.ca_4096'); + $int2048Config = Config::get('openssl.ca_2048'); + + // Create a basic temporary openssl config for CA extensions + $configContent = "[req]\ndistinguished_name = req\n[v3_ca]\nsubjectKeyIdentifier = hash\nauthorityKeyIdentifier = keyid:always,issuer\nbasicConstraints = critical, CA:true\nkeyUsage = critical, digitalSignature, cRLSign, keyCertSign"; + $configFile = tempnam(sys_get_temp_dir(), 'ca_conf_'); + file_put_contents($configFile, $configContent); + + try { + // Root CA (4096-bit) + $rootKey = openssl_pkey_new([ + 'private_key_bits' => 4096, + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + 'config' => $configFile + ]); + if (!$rootKey) throw new \Exception('Failed to generate Root Key: ' . openssl_error_string()); + + $rootCsr = openssl_csr_new($rootConfig, $rootKey, ['digest_alg' => 'sha256', 'config' => $configFile]); + if (!$rootCsr) throw new \Exception('Failed to generate Root CSR: ' . openssl_error_string()); + + // Generate a random serial + $serial = $this->generateSerialNumber(); + + $rootCert = openssl_csr_sign($rootCsr, null, $rootKey, 10950, [ + 'digest_alg' => 'sha256', + 'x509_extensions' => 'v3_ca', + 'config' => $configFile, + ], $serial); + if (!$rootCert) throw new \Exception('Failed to sign Root Cert: ' . openssl_error_string()); + + if (!openssl_x509_export($rootCert, $rootCertPem)) throw new \Exception('Failed to export Root Cert'); + if (!openssl_pkey_export($rootKey, $rootKeyPem, null, ['config' => $configFile])) throw new \Exception('Failed to export Root Key'); + + $rootDetails = openssl_x509_parse($rootCertPem); + + // Prefer serialNumberHex if available (PHP 8.0+) + $serialHex = isset($rootDetails['serialNumberHex']) + ? $this->formatHex($rootDetails['serialNumberHex']) + : $this->formatSerialToHex($rootDetails['serialNumber']); + + CaCertificate::create([ + 'ca_type' => 'root', + 'cert_content' => $rootCertPem, + 'key_content' => $rootKeyPem, + 'serial_number' => $serialHex, + 'common_name' => $rootDetails['subject']['CN'] ?? 'Root CA', + 'organization' => $rootDetails['subject']['O'] ?? null, + 'valid_from' => date('Y-m-d H:i:s', $rootDetails['validFrom_time_t']), + 'valid_to' => date('Y-m-d H:i:s', $rootDetails['validTo_time_t']), + ]); + + // Intermediate CA 4096-bit + $int4096Key = openssl_pkey_new([ + 'private_key_bits' => 4096, + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + 'config' => $configFile + ]); + if (!$int4096Key) throw new \Exception('Failed to generate Int-4096 Key: ' . openssl_error_string()); + + $int4096Csr = openssl_csr_new($int4096Config, $int4096Key, ['digest_alg' => 'sha256', 'config' => $configFile]); + if (!$int4096Csr) throw new \Exception('Failed to generate Int-4096 CSR: ' . openssl_error_string()); + + $int4096Cert = openssl_csr_sign($int4096Csr, $rootCert, $rootKey, 10950, [ + 'digest_alg' => 'sha256', + 'x509_extensions' => 'v3_ca', + 'config' => $configFile, + ], $this->generateSerialNumber()); + if (!$int4096Cert) throw new \Exception('Failed to sign Int-4096 Cert: ' . openssl_error_string()); + + if (!openssl_x509_export($int4096Cert, $int4096CertPem)) throw new \Exception('Failed to export Int-4096 Cert'); + if (!openssl_pkey_export($int4096Key, $int4096KeyPem, null, ['config' => $configFile])) throw new \Exception('Failed to export Int-4096 Key'); + + $int4096Details = openssl_x509_parse($int4096CertPem); + $serialHex4096 = isset($int4096Details['serialNumberHex']) + ? $this->formatHex($int4096Details['serialNumberHex']) + : $this->formatSerialToHex($int4096Details['serialNumber']); + + CaCertificate::create([ + 'ca_type' => 'intermediate_4096', + 'cert_content' => $int4096CertPem, + 'key_content' => $int4096KeyPem, + 'serial_number' => $serialHex4096, + 'common_name' => $int4096Details['subject']['CN'] ?? 'Intermediate CA 4096', + 'organization' => $int4096Details['subject']['O'] ?? null, + 'valid_from' => date('Y-m-d H:i:s', $int4096Details['validFrom_time_t']), + 'valid_to' => date('Y-m-d H:i:s', $int4096Details['validTo_time_t']), + ]); + + // Intermediate CA 2048-bit + $int2048Key = openssl_pkey_new([ + 'private_key_bits' => 2048, + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + 'config' => $configFile + ]); + if (!$int2048Key) throw new \Exception('Failed to generate Int-2048 Key: ' . openssl_error_string()); + + $int2048Csr = openssl_csr_new($int2048Config, $int2048Key, ['digest_alg' => 'sha256', 'config' => $configFile]); + if (!$int2048Csr) throw new \Exception('Failed to generate Int-2048 CSR: ' . openssl_error_string()); + + $int2048Cert = openssl_csr_sign($int2048Csr, $rootCert, $rootKey, 10950, [ + 'digest_alg' => 'sha256', + 'x509_extensions' => 'v3_ca', + 'config' => $configFile, + ], $this->generateSerialNumber()); + if (!$int2048Cert) throw new \Exception('Failed to sign Int-2048 Cert: ' . openssl_error_string()); + + if (!openssl_x509_export($int2048Cert, $int2048CertPem)) throw new \Exception('Failed to export Int-2048 Cert'); + if (!openssl_pkey_export($int2048Key, $int2048KeyPem, null, ['config' => $configFile])) throw new \Exception('Failed to export Int-2048 Key'); + + $int2048Details = openssl_x509_parse($int2048CertPem); + $serialHex2048 = isset($int2048Details['serialNumberHex']) + ? $this->formatHex($int2048Details['serialNumberHex']) + : $this->formatSerialToHex($int2048Details['serialNumber']); + + CaCertificate::create([ + 'ca_type' => 'intermediate_2048', + 'cert_content' => $int2048CertPem, + 'key_content' => $int2048KeyPem, + 'serial_number' => $serialHex2048, + 'common_name' => $int2048Details['subject']['CN'] ?? 'Intermediate CA 2048', + 'organization' => $int2048Details['subject']['O'] ?? null, + 'valid_from' => date('Y-m-d H:i:s', $int2048Details['validFrom_time_t']), + 'valid_to' => date('Y-m-d H:i:s', $int2048Details['validTo_time_t']), + ]); + + return true; + } finally { + if (file_exists($configFile)) unlink($configFile); + } + } + + /** + * Generate a domain certificate (Leaf). + */ + public function generateLeaf($data) + { + $keyBits = $data['key_bits'] ?? 2048; + $issuerType = (int)$keyBits === 4096 ? 'intermediate_4096' : 'intermediate_2048'; + + $intermediate = CaCertificate::where('ca_type', $issuerType)->first(); + if (!$intermediate) { + throw new \Exception("Intermediate CA ({$issuerType}) not found. Please setup CA first."); + } + + $dn = [ + "countryName" => $data['country'], + "stateOrProvinceName" => $data['state'], + "localityName" => $data['locality'], + "organizationName" => $data['organization'], + "commonName" => $data['common_name'] + ]; + + $cn = $data['common_name']; + $userSan = $data['san'] ?? ''; + + // Parse user input: split by comma, trim, filter empty + $entries = array_filter(array_map('trim', explode(',', $userSan))); + + // Always include CN as the first DNS entry + array_unshift($entries, $cn); + + $sanArray = array_unique(array_map(function($entry) { + if (str_starts_with($entry, 'IP:') || str_starts_with($entry, 'DNS:')) { + return $entry; + } + return filter_var($entry, FILTER_VALIDATE_IP) ? "IP:$entry" : "DNS:$entry"; + }, $entries)); + + $sanString = implode(', ', $sanArray); + + $configFile = null; + try { + $configContent = "[req]\ndistinguished_name = req\nreq_extensions = v3_req\nprompt = no\n[req_distinguished_name]\nCN = $cn\n[v3_req]\nsubjectAltName = $sanString"; + $configFile = tempnam(sys_get_temp_dir(), 'openssl_'); + file_put_contents($configFile, $configContent); + + $keyBits = $data['key_bits'] ?? 2048; + $privKey = openssl_pkey_new([ + 'private_key_bits' => (int)$keyBits, + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + 'config' => $configFile + ]); + if (!$privKey) throw new \Exception('Failed to generate Private Key: ' . openssl_error_string()); + + \Log::debug("Generating Leaf with SAN: " . $sanString); + + $csr = openssl_csr_new($dn, $privKey, [ + 'digest_alg' => 'sha256', + 'req_extensions' => 'v3_req', + 'config' => $configFile + ]); + if (!$csr) { + $err = openssl_error_string(); + \Log::error("CSR Creation Failed: " . $err); + throw new \Exception('Failed to generate CSR: ' . $err); + } + + $serial = $this->generateSerialNumber(); + \Log::debug("Signing CSR with serial: " . $serial); + + $cert = openssl_csr_sign($csr, $intermediate->cert_content, $intermediate->key_content, 365, [ + 'digest_alg' => 'sha256', + 'x509_extensions' => 'v3_req', + 'config' => $configFile, + ], $serial); + + if (!$cert) { + $err = openssl_error_string(); + \Log::error("Certificate Signing Failed: " . $err); + throw new \Exception('Failed to sign Certificate: ' . $err); + } + + // Verification: check if serial was actually applied + $certInfo = openssl_x509_parse($cert); + $actualSerialHex = isset($certInfo['serialNumberHex']) + ? $this->formatHex($certInfo['serialNumberHex']) + : $this->formatSerialToHex($certInfo['serialNumber']); + + \Log::debug("Certificate signed. Embedded Serial: " . $actualSerialHex); + + if (!openssl_x509_export($cert, $certPem)) throw new \Exception('Failed to export Certificate'); + if (!openssl_pkey_export($privKey, $keyPem, null, ['config' => $configFile])) throw new \Exception('Failed to export Private Key'); + if (!openssl_csr_export($csr, $csrPem)) throw new \Exception('Failed to export CSR'); + + return [ + 'cert' => $certPem, + 'key' => $keyPem, + 'csr' => $csrPem, + 'serial' => $actualSerialHex, + 'valid_from' => isset($certInfo['validFrom_time_t']) ? date('Y-m-d H:i:s', $certInfo['validFrom_time_t']) : null, + 'valid_to' => isset($certInfo['validTo_time_t']) ? date('Y-m-d H:i:s', $certInfo['validTo_time_t']) : null, + ]; + } finally { + if ($configFile && file_exists($configFile)) { + unlink($configFile); + } + } + } + + /** + * Generate a unique serial number. + * Generates a large random integer for the serial number. + */ + /** + * Generate a unique serial number. + * Generates a 20-byte (160-bit) positive integer string for X.509 compliance. + */ + /** + * Generate a unique serial number. + * Note: strictly returns an int because openssl_csr_sign() in PHP requires type int. + * This limits us to 64-bit entropy (PHP_INT_MAX), enabling approx 9e18 combinations. + * While X.509 uses 160-bit, PHP's native extension wrapper limits this. + */ + protected function generateSerialNumber(): int + { + try { + return random_int(1, PHP_INT_MAX); + } catch (\Exception $e) { + return time(); + } + } + + /** + * Format a hex string (from serialNumberHex). + */ + public function formatHex($hex) + { + // Capitalize and add colons + $hex = strtoupper($hex); + return implode(':', str_split($hex, 2)); + } + + /** + * Renew (Re-sign) a CA Certificate. + */ + public function renewCaCertificate(CaCertificate $cert, int $days) + { + $configFile = null; + try { + // 1. Prepare Config + $configContent = "[req]\ndistinguished_name = req\n[v3_ca]\nsubjectKeyIdentifier = hash\nauthorityKeyIdentifier = keyid:always,issuer\nbasicConstraints = critical, CA:true\nkeyUsage = critical, digitalSignature, cRLSign, keyCertSign"; + $configFile = tempnam(sys_get_temp_dir(), 'renew_ca_'); + file_put_contents($configFile, $configContent); + + // 2. Get Private Key + $privKey = openssl_pkey_get_private($cert->key_content); + if (!$privKey) throw new \Exception('Failed to load Private Key'); + + // 3. Get Subject DN from existing Cert + $certInfo = openssl_x509_parse($cert->cert_content); + $dn = $certInfo['subject']; + + // Reconstruct DN array for openssl_csr_new if needed, + // but we can just use the existing subject. + // CAUTION: openssl_csr_new expects proper array keys (e.g. commonName, organizationName) + // openssl_x509_parse returns abbreviated keys (CN, O, C, etc.) often. + // We need to map them back or pass the DN info carefully. + + $dnMap = [ + 'CN' => 'commonName', + 'O' => 'organizationName', + 'OU' => 'organizationalUnitName', + 'C' => 'countryName', + 'ST' => 'stateOrProvinceName', + 'L' => 'localityName', + 'emailAddress' => 'emailAddress' + ]; + + $newDn = []; + foreach ($dn as $key => $value) { + if (isset($dnMap[$key])) { + $newDn[$dnMap[$key]] = $value; + } + } + + // 4. Generate New CSR + // Note: We use the SAME private key + $csr = openssl_csr_new($newDn, $privKey, ['digest_alg' => 'sha256', 'config' => $configFile]); + if (!$csr) throw new \Exception('Failed to generate Renewal CSR: ' . openssl_error_string()); + + // 5. Determine Signer (Issuer) + $issuerCert = null; + $issuerKey = null; + + if ($cert->ca_type === 'root') { + // Root signs itself + $issuerCert = null; + $issuerKey = $privKey; + } else { + // Intermediate is signed by Root + $root = CaCertificate::where('ca_type', 'root')->first(); + if (!$root) throw new \Exception('Root CA not found for signing intermediate renewal.'); + $issuerCert = $root->cert_content; + $issuerKey = openssl_pkey_get_private($root->key_content); + } + + // 6. Sign CSR + $serial = $this->generateSerialNumber(); + $newCert = openssl_csr_sign($csr, $issuerCert, $issuerKey, $days, [ + 'digest_alg' => 'sha256', + 'x509_extensions' => 'v3_ca', + 'config' => $configFile, + ], $serial); + + if (!$newCert) throw new \Exception('Failed to sign Renewal Cert: ' . openssl_error_string()); + + // 7. Export + if (!openssl_x509_export($newCert, $newCertPem)) throw new \Exception('Failed to export Renewal Cert'); + + // 8. Parse new details + $newInfo = openssl_x509_parse($newCertPem); + $newSerialHex = isset($newInfo['serialNumberHex']) + ? $this->formatHex($newInfo['serialNumberHex']) + : $this->formatSerialToHex($newInfo['serialNumber']); + + return [ + 'cert_content' => $newCertPem, + 'serial_number' => $newSerialHex, + 'valid_from' => date('Y-m-d H:i:s', $newInfo['validFrom_time_t']), + 'valid_to' => date('Y-m-d H:i:s', $newInfo['validTo_time_t']), + ]; + + } finally { + if ($configFile && file_exists($configFile)) unlink($configFile); + } + } + + /** + * Fallback format a decimal serial number to hex string. + */ + public function formatSerialToHex($decimal) + { + // Sanitize input: remove anything that isn't a digit or minus sign + // Note: serial numbers shouldn't be negative here normally, but just cleaning string + $cleaned = preg_replace('/[^0-9]/', '', (string)$decimal); + + if ($cleaned === '') { + $cleaned = '0'; + } + + // Support large integers that might be strings (bcmath based conversion) + if (function_exists('bcdiv')) { + $hex = ''; + $value = $cleaned; + + // Basic check if it's a valid BCMath number + if (!preg_match('/^\d+$/', $value)) { + $value = '0'; + } + + // Loop while value > 0 + while (bccomp($value, '0') > 0) { + $mod = bcmod($value, '16'); + $hex = dechex((int)$mod) . $hex; + $value = bcdiv($value, '16', 0); + } + $hex = $hex ?: '0'; + } else { + // Fallback for smaller integers if bcmath missing + $hex = dechex((int)$cleaned); + } + + // Ensure even length + if (strlen($hex) % 2 !== 0) { + $hex = '0' . $hex; + } + return strtoupper(implode(':', str_split($hex, 2))); + } +} diff --git a/app/Traits/RevokesSocialTokens.php b/app/Traits/RevokesSocialTokens.php new file mode 100644 index 0000000..f98f03a --- /dev/null +++ b/app/Traits/RevokesSocialTokens.php @@ -0,0 +1,33 @@ + $token, + ]); + } elseif ($provider === 'github') { + $clientId = config('services.github.client_id'); + $clientSecret = config('services.github.client_secret'); + + Http::withBasicAuth($clientId, $clientSecret) + ->delete("https://api.github.com/applications/{$clientId}/grant", [ + 'access_token' => $token, + ]); + } + \Log::info("Revoked {$provider} token."); + } catch (\Exception $e) { + \Log::error("Failed to revoke {$provider} token: " . $e->getMessage()); + } + } +} diff --git a/app/View/Components/CalenderArea.php b/app/View/Components/CalenderArea.php new file mode 100644 index 0000000..f2765f6 --- /dev/null +++ b/app/View/Components/CalenderArea.php @@ -0,0 +1,26 @@ +isOpen = $isOpen; + $this->showCloseButton = $showCloseButton; + $this->isFullscreen = $isFullscreen; + $this->modalId = $modalId ?? 'modal-' . uniqid(); + } + + /** + * Get the view / contents that represent the component. + */ + public function render(): View|Closure|string + { + return view('components.ui.modal'); + } +} diff --git a/app/View/Components/ui/YoutubeEmbed.php b/app/View/Components/ui/YoutubeEmbed.php new file mode 100644 index 0000000..5f4652f --- /dev/null +++ b/app/View/Components/ui/YoutubeEmbed.php @@ -0,0 +1,26 @@ +handleCommand(new ArgvInput); + +exit($status); diff --git a/bootstrap/app.php b/bootstrap/app.php new file mode 100644 index 0000000..776f14a --- /dev/null +++ b/bootstrap/app.php @@ -0,0 +1,26 @@ +withRouting( + web: __DIR__.'/../routes/web.php', + commands: __DIR__.'/../routes/console.php', + health: '/up', + ) + ->withMiddleware(function (Middleware $middleware): void { + $middleware->redirectTo( + guests: '/signin', + users: '/dashboard' + ); + + $middleware->alias([ + 'admin' => \App\Http\Middleware\EnsureUserIsAdmin::class, + 'api_key' => \App\Http\Middleware\CheckApiKey::class, + ]); + }) + ->withExceptions(function (Exceptions $exceptions): void { + // + })->create(); diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/bootstrap/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/bootstrap/providers.php b/bootstrap/providers.php new file mode 100644 index 0000000..38b258d --- /dev/null +++ b/bootstrap/providers.php @@ -0,0 +1,5 @@ +=5.0.0" + }, + "require-dev": { + "doctrine/dbal": "^4.0.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2024-02-09T16:56:22+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" + }, + "time": "2024-07-08T12:26:09+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.1.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2025-08-10T19:31:58+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "8c784d071debd117328803d86b2097615b457500" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", + "reference": "8c784d071debd117328803d86b2097615b457500", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2024-10-09T13:47:03+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2025-03-06T22:45:56+00:00" + }, + { + "name": "firebase/php-jwt", + "version": "v6.11.1", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" + }, + "time": "2025-04-09T20:32:01+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "symfony/http-foundation": "^4.4|^5.4|^6|^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barryvdh", + "email": "barryvdh@gmail.com" + } + ], + "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", + "homepage": "https://github.com/fruitcake/php-cors", + "keywords": [ + "cors", + "laravel", + "symfony" + ], + "support": { + "issues": "https://github.com/fruitcake/php-cors/issues", + "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2023-10-12T05:21:21+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:45:45+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.10.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "MΓ‘rk SΓ‘gi-KazΓ‘r", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2025-08-23T22:36:01+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "481557b130ef3790cf82b713667b43030dc9c957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.3.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2025-08-22T14:34:08+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "21dc724a0583619cd1652f673303492272778051" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "MΓ‘rk SΓ‘gi-KazΓ‘r", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "MΓ‘rk SΓ‘gi-KazΓ‘r", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.8.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2025-08-23T21:21:41+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.5", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25", + "uri-template/tests": "1.0.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\UriTemplate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "A polyfill class for uri_template of PHP", + "keywords": [ + "guzzlehttp", + "uri-template" + ], + "support": { + "issues": "https://github.com/guzzle/uri-template/issues", + "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", + "type": "tidelift" + } + ], + "time": "2025-08-22T14:27:06+00:00" + }, + { + "name": "laravel/framework", + "version": "v12.26.4", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "085a367a32ba86fcfa647bfc796098ae6f795b09" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/085a367a32ba86fcfa647bfc796098ae6f795b09", + "reference": "085a367a32ba86fcfa647bfc796098ae6f795b09", + "shasum": "" + }, + "require": { + "brick/math": "^0.11|^0.12|^0.13", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.4", + "egulias/email-validator": "^3.2.1|^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.3", + "guzzlehttp/guzzle": "^7.8.2", + "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.3.0", + "laravel/serializable-closure": "^1.3|^2.0", + "league/commonmark": "^2.7", + "league/flysystem": "^3.25.1", + "league/flysystem-local": "^3.25.1", + "league/uri": "^7.5.1", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^3.8.4", + "nunomaduro/termwind": "^2.0", + "php": "^8.2", + "psr/container": "^1.1.1|^2.0.1", + "psr/log": "^1.0|^2.0|^3.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "ramsey/uuid": "^4.7", + "symfony/console": "^7.2.0", + "symfony/error-handler": "^7.2.0", + "symfony/finder": "^7.2.0", + "symfony/http-foundation": "^7.2.0", + "symfony/http-kernel": "^7.2.0", + "symfony/mailer": "^7.2.0", + "symfony/mime": "^7.2.0", + "symfony/polyfill-php83": "^1.33", + "symfony/polyfill-php84": "^1.33", + "symfony/polyfill-php85": "^1.33", + "symfony/process": "^7.2.0", + "symfony/routing": "^7.2.0", + "symfony/uid": "^7.2.0", + "symfony/var-dumper": "^7.2.0", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", + "vlucas/phpdotenv": "^5.6.1", + "voku/portable-ascii": "^2.0.2" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "psr/log-implementation": "1.0|2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0" + }, + "replace": { + "illuminate/auth": "self.version", + "illuminate/broadcasting": "self.version", + "illuminate/bus": "self.version", + "illuminate/cache": "self.version", + "illuminate/collections": "self.version", + "illuminate/concurrency": "self.version", + "illuminate/conditionable": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/contracts": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/filesystem": "self.version", + "illuminate/hashing": "self.version", + "illuminate/http": "self.version", + "illuminate/log": "self.version", + "illuminate/macroable": "self.version", + "illuminate/mail": "self.version", + "illuminate/notifications": "self.version", + "illuminate/pagination": "self.version", + "illuminate/pipeline": "self.version", + "illuminate/process": "self.version", + "illuminate/queue": "self.version", + "illuminate/redis": "self.version", + "illuminate/routing": "self.version", + "illuminate/session": "self.version", + "illuminate/support": "self.version", + "illuminate/testing": "self.version", + "illuminate/translation": "self.version", + "illuminate/validation": "self.version", + "illuminate/view": "self.version", + "spatie/once": "*" + }, + "require-dev": { + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.322.9", + "ext-gmp": "*", + "fakerphp/faker": "^1.24", + "guzzlehttp/promises": "^2.0.3", + "guzzlehttp/psr7": "^2.4", + "laravel/pint": "^1.18", + "league/flysystem-aws-s3-v3": "^3.25.1", + "league/flysystem-ftp": "^3.25.1", + "league/flysystem-path-prefixing": "^3.25.1", + "league/flysystem-read-only": "^3.25.1", + "league/flysystem-sftp-v3": "^3.25.1", + "mockery/mockery": "^1.6.10", + "orchestra/testbench-core": "^10.6.3", + "pda/pheanstalk": "^5.0.6|^7.0.0", + "php-http/discovery": "^1.15", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1", + "predis/predis": "^2.3|^3.0", + "resend/resend-php": "^0.10.0", + "symfony/cache": "^7.2.0", + "symfony/http-client": "^7.2.0", + "symfony/psr-http-message-bridge": "^7.2.0", + "symfony/translation": "^7.2.0" + }, + "suggest": { + "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).", + "brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).", + "ext-apcu": "Required to use the APC cache driver.", + "ext-fileinfo": "Required to use the Filesystem class.", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "ext-memcached": "Required to use the memcache cache driver.", + "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", + "ext-pdo": "Required to use all database features.", + "ext-posix": "Required to use all features of the queue worker.", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).", + "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "filp/whoops": "Required for friendly error pages in development (^2.14.3).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).", + "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", + "mockery/mockery": "Required to use mocking (^1.6).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", + "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", + "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).", + "predis/predis": "Required to use the predis connector (^2.3|^3.0).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^7.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Collections/functions.php", + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", + "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Log/functions.php", + "src/Illuminate/Support/functions.php", + "src/Illuminate/Support/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/", + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Laravel Framework.", + "homepage": "https://laravel.com", + "keywords": [ + "framework", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-08-29T14:15:53+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.3.6", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "86a8b692e8661d0fb308cec64f3d176821323077" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/86a8b692e8661d0fb308cec64f3d176821323077", + "reference": "86a8b692e8661d0fb308cec64f3d176821323077", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "ext-mbstring": "*", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "illuminate/collections": "^10.0|^11.0|^12.0", + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3|^3.4", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.3.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.3.6" + }, + "time": "2025-07-07T14:17:42+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v2.0.4", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b352cf0534aa1ae6b4d825d1e762e35d43f8a841", + "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0|^11.0|^12.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2025-03-19T13:51:03+00:00" + }, + { + "name": "laravel/socialite", + "version": "v5.24.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/socialite.git", + "reference": "1d19358c28e8951dde6e36603b89d8f09e6cfbfd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/socialite/zipball/1d19358c28e8951dde6e36603b89d8f09e6cfbfd", + "reference": "1d19358c28e8951dde6e36603b89d8f09e6cfbfd", + "shasum": "" + }, + "require": { + "ext-json": "*", + "firebase/php-jwt": "^6.4", + "guzzlehttp/guzzle": "^6.0|^7.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "league/oauth1-client": "^1.11", + "php": "^7.2|^8.0", + "phpseclib/phpseclib": "^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^4.18|^5.20|^6.47|^7.55|^8.36|^9.15|^10.8", + "phpstan/phpstan": "^1.12.23", + "phpunit/phpunit": "^8.0|^9.3|^10.4|^11.5|^12.0" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Socialite": "Laravel\\Socialite\\Facades\\Socialite" + }, + "providers": [ + "Laravel\\Socialite\\SocialiteServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Socialite\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.", + "homepage": "https://laravel.com", + "keywords": [ + "laravel", + "oauth" + ], + "support": { + "issues": "https://github.com/laravel/socialite/issues", + "source": "https://github.com/laravel/socialite" + }, + "time": "2025-12-09T15:37:06+00:00" + }, + { + "name": "laravel/tinker", + "version": "v2.10.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/22177cc71807d38f2810c6204d8f7183d88a57d3", + "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "php": "^7.2.5|^8.0", + "psy/psysh": "^0.11.1|^0.12.0", + "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.8|^9.3.3|^10.0" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0)." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v2.10.1" + }, + "time": "2025-01-27T14:24:01+00:00" + }, + { + "name": "league/commonmark", + "version": "2.7.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "10732241927d3971d28e7ea7b5712721fa2296ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/10732241927d3971d28e7ea7b5712721fa2296ca", + "reference": "10732241927d3971d28e7ea7b5712721fa2296ca", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.31.1", + "commonmark/commonmark.js": "0.31.1", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4 || ^2.0", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0 | ^7.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2025-07-20T12:47:49+00:00" + }, + { + "name": "league/config", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2022-12-11T20:36:23+00:00" + }, + { + "name": "league/flysystem", + "version": "3.30.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "2203e3151755d874bb2943649dae1eb8533ac93e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/2203e3151755d874bb2943649dae1eb8533ac93e", + "reference": "2203e3151755d874bb2943649dae1eb8533ac93e", + "shasum": "" + }, + "require": { + "league/flysystem-local": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "phpseclib/phpseclib": "3.0.15", + "symfony/http-client": "<5.2" + }, + "require-dev": { + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", + "aws/aws-sdk-php": "^3.295.10", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-mongodb": "^1.3|^2", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "guzzlehttp/psr7": "^2.6", + "microsoft/azure-storage-blob": "^1.1", + "mongodb/mongodb": "^1.2|^2", + "phpseclib/phpseclib": "^3.0.36", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", + "sabre/dav": "^4.6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "File storage abstraction for PHP", + "keywords": [ + "WebDAV", + "aws", + "cloud", + "file", + "files", + "filesystem", + "filesystems", + "ftp", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/3.30.0" + }, + "time": "2025-06-25T13:29:59+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.30.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "6691915f77c7fb69adfb87dcd550052dc184ee10" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/6691915f77c7fb69adfb87dcd550052dc184ee10", + "reference": "6691915f77c7fb69adfb87dcd550052dc184ee10", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Local\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-local/tree/3.30.0" + }, + "time": "2025-05-21T10:34:19+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.16.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2024-09-21T08:32:55+00:00" + }, + { + "name": "league/oauth1-client", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth1-client.git", + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/f9c94b088837eb1aae1ad7c4f23eb65cc6993055", + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "guzzlehttp/guzzle": "^6.0|^7.0", + "guzzlehttp/psr7": "^1.7|^2.0", + "php": ">=7.1||>=8.0" + }, + "require-dev": { + "ext-simplexml": "*", + "friendsofphp/php-cs-fixer": "^2.17", + "mockery/mockery": "^1.3.3", + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5||9.5" + }, + "suggest": { + "ext-simplexml": "For decoding XML-based responses." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev", + "dev-develop": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth1\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Corlett", + "email": "bencorlett@me.com", + "homepage": "http://www.webcomm.com.au", + "role": "Developer" + } + ], + "description": "OAuth 1.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "bitbucket", + "identity", + "idp", + "oauth", + "oauth1", + "single sign on", + "trello", + "tumblr", + "twitter" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth1-client/issues", + "source": "https://github.com/thephpleague/oauth1-client/tree/v1.11.0" + }, + "time": "2024-12-10T19:59:05+00:00" + }, + { + "name": "league/uri", + "version": "7.5.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "81fb5145d2644324614cc532b28efd0215bda430" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", + "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.5", + "php": "^8.1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.5.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:40:02+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.5.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-factory": "^1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common interfaces and classes for URI representation and interaction", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:18:47+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.9.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.9.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2025-03-24T10:02:05+00:00" + }, + { + "name": "nesbot/carbon", + "version": "3.10.2", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24", + "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24", + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "<100.0", + "ext-json": "*", + "php": "^8.1", + "psr/clock": "^1.0", + "symfony/clock": "^6.3.12 || ^7.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^3.6.3 || ^4.0", + "doctrine/orm": "^2.15.2 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.75.0", + "kylekatarnls/multi-tester": "^2.5.3", + "phpmd/phpmd": "^2.15.0", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.17", + "phpunit/phpunit": "^10.5.46", + "squizlabs/php_codesniffer": "^3.13.0" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/CarbonPHP/carbon/issues", + "source": "https://github.com/CarbonPHP/carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2025-08-02T09:36:06+00:00" + }, + { + "name": "nette/schema", + "version": "v1.3.2", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d", + "shasum": "" + }, + "require": { + "nette/utils": "^4.0", + "php": "8.1 - 8.4" + }, + "require-dev": { + "nette/tester": "^2.5.2", + "phpstan/phpstan-nette": "^1.0", + "tracy/tracy": "^2.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "πŸ“ Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.3.2" + }, + "time": "2024-10-06T23:10:23+00:00" + }, + { + "name": "nette/utils", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede", + "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede", + "shasum": "" + }, + "require": { + "php": "8.0 - 8.5" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "^1.2", + "nette/tester": "^2.5", + "phpstan/phpstan-nette": "^2.0@stable", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "πŸ›  Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.0.8" + }, + "time": "2025-08-06T21:43:34+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.6.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" + }, + "time": "2025-08-13T20:13:15+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "dfa08f390e509967a15c22493dc0bac5733d9123" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/dfa08f390e509967a15c22493dc0bac5733d9123", + "reference": "dfa08f390e509967a15c22493dc0bac5733d9123", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.2.6" + }, + "require-dev": { + "illuminate/console": "^11.44.7", + "laravel/pint": "^1.22.0", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0 || ^3.8.2", + "phpstan/phpstan": "^1.12.25", + "phpstan/phpstan-strict-rules": "^1.6.2", + "symfony/var-dumper": "^7.2.6", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Its like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v2.3.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2025-05-08T08:14:37+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "shasum": "" + }, + "require": { + "php": "^8" + }, + "require-dev": { + "infection/infection": "^0", + "nikic/php-fuzzer": "^0", + "phpunit/phpunit": "^9|^10|^11", + "vimeo/psalm": "^4|^5|^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2025-09-24T15:06:41+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.4", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2025-08-21T11:53:16+00:00" + }, + { + "name": "phpseclib/phpseclib", + "version": "3.0.48", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "64065a5679c50acb886e82c07aa139b0f757bb89" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/64065a5679c50acb886e82c07aa139b0f757bb89", + "reference": "64065a5679c50acb886e82c07aa139b0f757bb89", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1|^2|^3", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-dom": "Install the DOM extension to load XML formatted public keys.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib3\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-JΓΌrgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.48" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2025-12-15T11:51:42+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "psy/psysh", + "version": "v0.12.10", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "6e80abe6f2257121f1eb9a4c55bf29d921025b22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/6e80abe6f2257121f1eb9a4c55bf29d921025b22", + "reference": "6e80abe6f2257121f1eb9a4c55bf29d921025b22", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^5.0 || ^4.0", + "php": "^8.0 || ^7.4", + "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2" + }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-main": "0.12.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "https://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.12.10" + }, + "time": "2025-08-04T12:39:37+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.1.1" + }, + "time": "2025-03-22T05:38:12+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.9.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.25", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.9.0" + }, + "time": "2025-06-25T14:20:11+00:00" + }, + { + "name": "symfony/clock", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/clock": "^1.0", + "symfony/polyfill-php83": "^1.28" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/console", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-25T06:35:40+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-FranΓ§ois Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/0b31a944fcd8759ae294da4d2808cbc53aebd0c3", + "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^6.4|^7.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/webpack-encore-bundle": "^1.0|^2.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-07T08:17:57+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-13T11:49:31+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T13:41:35+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7475561ec27020196c49bb7c4f178d33d7d3dc00", + "reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + }, + "require-dev": { + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4.12|^7.1.5", + "symfony/clock": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-20T08:04:18+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/72c304de37e1a1cec6d5d12b81187ebd4850a17b", + "reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^7.3", + "symfony/http-foundation": "^7.3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<6.4", + "symfony/cache": "<6.4", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<6.4", + "symfony/form": "<6.4", + "symfony/http-client": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4", + "symfony/translation": "<6.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<6.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.4", + "twig/twig": "<3.12" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^6.4|^7.0", + "symfony/clock": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/css-selector": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^7.1", + "symfony/routing": "^6.4|^7.0", + "symfony/serializer": "^7.1", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0", + "symfony/var-exporter": "^6.4|^7.0", + "twig/twig": "^3.12" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-29T08:23:45+00:00" + }, + { + "name": "symfony/mailer", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/a32f3f45f1990db8c4341d5122a7d3a381c7e575", + "reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.2", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/mime": "^7.2", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/twig-bridge": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-13T11:49:31+00:00" + }, + { + "name": "symfony/mime", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/e0a0f859148daf1edf6c60b398eb40bfc96697d1", + "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<6.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T13:41:35+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-27T09:58:17+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-10T14:38:51+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-23T08:48:59+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-02T08:10:11+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-08T02:45:35+00:00" + }, + { + "name": "symfony/polyfill-php84", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-24T13:30:11+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-23T16:12:55+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "GrΓ©goire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/process", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "32241012d521e2e8a9d713adb0812bb773b907f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", + "reference": "32241012d521e2e8a9d713adb0812bb773b907f1", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-18T09:42:54+00:00" + }, + { + "name": "symfony/routing", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/7614b8ca5fa89b9cd233e21b627bfc5774f586e4", + "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T11:36:08+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-25T09:37:31+00:00" + }, + { + "name": "symfony/string", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-25T06:35:40+00:00" + }, + { + "name": "symfony/translation", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "e0837b4cbcef63c754d89a4806575cada743a38d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/e0837b4cbcef63c754d89a4806575cada743a38d", + "reference": "e0837b4cbcef63c754d89a4806575cada743a38d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "nikic/php-parser": "<5.0", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<6.4", + "symfony/yaml": "<6.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-01T21:02:37+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-27T08:32:26+00:00" + }, + { + "name": "symfony/uid", + "version": "v7.3.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/a69f69f3159b852651a6bf45a9fdd149520525bb", + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "GrΓ©goire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v7.3.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-27T19:55:54+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/34d8d4c4b9597347306d1ec8eb4e1319b1e6986f", + "reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "twig/twig": "^3.12" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-13T11:49:31+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "0d72ac1c00084279c1816675284073c5a337c20d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0d72ac1c00084279c1816675284073c5a337c20d", + "reference": "0d72ac1c00084279c1816675284073c5a337c20d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^7.4 || ^8.0", + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^8.5.21 || ^9.5.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", + "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "support": { + "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.3.0" + }, + "time": "2024-12-21T16:25:41+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.6.2", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af", + "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.3", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-filter": "*", + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "5.6-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2025-04-30T23:37:27+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "https://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2024-11-21T01:49:47+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "packages-dev": [ + { + "name": "brianium/paratest", + "version": "v7.12.0", + "source": { + "type": "git", + "url": "https://github.com/paratestphp/paratest.git", + "reference": "6a34ddb12a3bd5bd07d831ce95f111087f3bcbd8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/6a34ddb12a3bd5bd07d831ce95f111087f3bcbd8", + "reference": "6a34ddb12a3bd5bd07d831ce95f111087f3bcbd8", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "fidry/cpu-core-counter": "^1.3.0", + "jean85/pretty-package-versions": "^2.1.1", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "phpunit/php-code-coverage": "^12.3.2", + "phpunit/php-file-iterator": "^6", + "phpunit/php-timer": "^8", + "phpunit/phpunit": "^12.3.6", + "sebastian/environment": "^8.0.3", + "symfony/console": "^6.4.20 || ^7.3.2", + "symfony/process": "^6.4.20 || ^7.3.0" + }, + "require-dev": { + "doctrine/coding-standard": "^13.0.1", + "ext-pcntl": "*", + "ext-pcov": "*", + "ext-posix": "*", + "phpstan/phpstan": "^2.1.22", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.7", + "phpstan/phpstan-strict-rules": "^2.0.6", + "squizlabs/php_codesniffer": "^3.13.2", + "symfony/filesystem": "^6.4.13 || ^7.3.2" + }, + "bin": [ + "bin/paratest", + "bin/paratest_for_phpstorm" + ], + "type": "library", + "autoload": { + "psr-4": { + "ParaTest\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" + }, + { + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" + } + ], + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", + "keywords": [ + "concurrent", + "parallel", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v7.12.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2025-08-29T05:28:31+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=13" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12 || ^13", + "phpstan/phpstan": "1.4.10 || 2.1.11", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + }, + "time": "2025-04-07T20:06:18+00:00" + }, + { + "name": "fakerphp/faker", + "version": "v1.24.1", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "FranΓ§ois Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" + }, + "time": "2024-11-21T13:46:39+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "ThΓ©o FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2025-08-14T07:29:31+00:00" + }, + { + "name": "filp/whoops", + "version": "2.18.4", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.18.4" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-08-08T12:00:00+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" + }, + "time": "2025-04-30T06:54:44+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.1.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^7.5|^8.5|^9.6", + "rector/rector": "^2.0", + "vimeo/psalm": "^4.3 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1" + }, + "time": "2025-03-19T14:43:43+00:00" + }, + { + "name": "laravel/pail", + "version": "v1.2.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/pail.git", + "reference": "8cc3d575c1f0e57eeb923f366a37528c50d2385a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pail/zipball/8cc3d575c1f0e57eeb923f366a37528c50d2385a", + "reference": "8cc3d575c1f0e57eeb923f366a37528c50d2385a", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/console": "^10.24|^11.0|^12.0", + "illuminate/contracts": "^10.24|^11.0|^12.0", + "illuminate/log": "^10.24|^11.0|^12.0", + "illuminate/process": "^10.24|^11.0|^12.0", + "illuminate/support": "^10.24|^11.0|^12.0", + "nunomaduro/termwind": "^1.15|^2.0", + "php": "^8.2", + "symfony/console": "^6.0|^7.0" + }, + "require-dev": { + "laravel/framework": "^10.24|^11.0|^12.0", + "laravel/pint": "^1.13", + "orchestra/testbench-core": "^8.13|^9.0|^10.0", + "pestphp/pest": "^2.20|^3.0", + "pestphp/pest-plugin-type-coverage": "^2.3|^3.0", + "phpstan/phpstan": "^1.12.27", + "symfony/var-dumper": "^6.3|^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Pail\\PailServiceProvider" + ] + }, + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Pail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Easily delve into your Laravel application's log files directly from the command line.", + "homepage": "https://github.com/laravel/pail", + "keywords": [ + "dev", + "laravel", + "logs", + "php", + "tail" + ], + "support": { + "issues": "https://github.com/laravel/pail/issues", + "source": "https://github.com/laravel/pail" + }, + "time": "2025-06-05T13:55:57+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.24.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/0345f3b05f136801af8c339f9d16ef29e6b4df8a", + "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.2.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.82.2", + "illuminate/view": "^11.45.1", + "larastan/larastan": "^3.5.0", + "laravel-zero/framework": "^11.45.0", + "mockery/mockery": "^1.6.12", + "nunomaduro/termwind": "^2.3.1", + "pestphp/pest": "^2.36.0" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "files": [ + "overrides/Runner/Parallel/ProcessFactory.php" + ], + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2025-07-10T18:09:32+00:00" + }, + { + "name": "laravel/sail", + "version": "v1.45.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/sail.git", + "reference": "019a2933ff4a9199f098d4259713f9bc266a874e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sail/zipball/019a2933ff4a9199f098d4259713f9bc266a874e", + "reference": "019a2933ff4a9199f098d4259713f9bc266a874e", + "shasum": "" + }, + "require": { + "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0", + "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0", + "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0", + "php": "^8.0", + "symfony/console": "^6.0|^7.0", + "symfony/yaml": "^6.0|^7.0" + }, + "require-dev": { + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", + "phpstan/phpstan": "^1.10" + }, + "bin": [ + "bin/sail" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sail\\SailServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Docker files for running a basic Laravel application.", + "keywords": [ + "docker", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/sail/issues", + "source": "https://github.com/laravel/sail" + }, + "time": "2025-08-25T19:28:31+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "PΓ‘draic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v8.8.2", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/60207965f9b7b7a4ce15a0f75d57f9dadb105bdb", + "reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.18.1", + "nunomaduro/termwind": "^2.3.1", + "php": "^8.2.0", + "symfony/console": "^7.3.0" + }, + "conflict": { + "laravel/framework": "<11.44.2 || >=13.0.0", + "phpunit/phpunit": "<11.5.15 || >=13.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.8.3", + "larastan/larastan": "^3.4.2", + "laravel/framework": "^11.44.2 || ^12.18", + "laravel/pint": "^1.22.1", + "laravel/sail": "^1.43.1", + "laravel/sanctum": "^4.1.1", + "laravel/tinker": "^2.10.1", + "orchestra/testbench-core": "^9.12.0 || ^10.4", + "pestphp/pest": "^3.8.2", + "sebastian/environment": "^7.2.1 || ^8.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + }, + "branch-alias": { + "dev-8.x": "8.x-dev" + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "dev", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2025-06-25T02:12:12+00:00" + }, + { + "name": "pestphp/pest", + "version": "v4.0.4", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest.git", + "reference": "47fb1d77631d608022cc7af96cac90ac741c8394" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest/zipball/47fb1d77631d608022cc7af96cac90ac741c8394", + "reference": "47fb1d77631d608022cc7af96cac90ac741c8394", + "shasum": "" + }, + "require": { + "brianium/paratest": "^7.11.2", + "nunomaduro/collision": "^8.8.2", + "nunomaduro/termwind": "^2.3.1", + "pestphp/pest-plugin": "^4.0.0", + "pestphp/pest-plugin-arch": "^4.0.0", + "pestphp/pest-plugin-mutate": "^4.0.1", + "pestphp/pest-plugin-profanity": "^4.0.1", + "php": "^8.3.0", + "phpunit/phpunit": "^12.3.7", + "symfony/process": "^7.3.0" + }, + "conflict": { + "filp/whoops": "<2.18.3", + "phpunit/phpunit": ">12.3.7", + "sebastian/exporter": "<7.0.0", + "webmozart/assert": "<1.11.0" + }, + "require-dev": { + "pestphp/pest-dev-tools": "^4.0.0", + "pestphp/pest-plugin-browser": "^4.0.2", + "pestphp/pest-plugin-type-coverage": "^4.0.2", + "psy/psysh": "^0.12.10" + }, + "bin": [ + "bin/pest" + ], + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Mutate\\Plugins\\Mutate", + "Pest\\Plugins\\Configuration", + "Pest\\Plugins\\Bail", + "Pest\\Plugins\\Cache", + "Pest\\Plugins\\Coverage", + "Pest\\Plugins\\Init", + "Pest\\Plugins\\Environment", + "Pest\\Plugins\\Help", + "Pest\\Plugins\\Memory", + "Pest\\Plugins\\Only", + "Pest\\Plugins\\Printer", + "Pest\\Plugins\\ProcessIsolation", + "Pest\\Plugins\\Profile", + "Pest\\Plugins\\Retry", + "Pest\\Plugins\\Snapshot", + "Pest\\Plugins\\Verbose", + "Pest\\Plugins\\Version", + "Pest\\Plugins\\Shard", + "Pest\\Plugins\\Parallel" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "files": [ + "src/Functions.php", + "src/Pest.php" + ], + "psr-4": { + "Pest\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "The elegant PHP Testing Framework.", + "keywords": [ + "framework", + "pest", + "php", + "test", + "testing", + "unit" + ], + "support": { + "issues": "https://github.com/pestphp/pest/issues", + "source": "https://github.com/pestphp/pest/tree/v4.0.4" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2025-08-28T18:19:42+00:00" + }, + { + "name": "pestphp/pest-plugin", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin.git", + "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/9d4b93d7f73d3f9c3189bb22c220fef271cdf568", + "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0.0", + "composer-runtime-api": "^2.2.2", + "php": "^8.3" + }, + "conflict": { + "pestphp/pest": "<4.0.0" + }, + "require-dev": { + "composer/composer": "^2.8.10", + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Pest\\Plugin\\Manager" + }, + "autoload": { + "psr-4": { + "Pest\\Plugin\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest plugin manager", + "keywords": [ + "framework", + "manager", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin/tree/v4.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2025-08-20T12:35:58+00:00" + }, + { + "name": "pestphp/pest-plugin-arch", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-arch.git", + "reference": "25bb17e37920ccc35cbbcda3b00d596aadf3e58d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/25bb17e37920ccc35cbbcda3b00d596aadf3e58d", + "reference": "25bb17e37920ccc35cbbcda3b00d596aadf3e58d", + "shasum": "" + }, + "require": { + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3", + "ta-tikoma/phpunit-architecture-test": "^0.8.5" + }, + "require-dev": { + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Arch\\Plugin" + ] + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Pest\\Arch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Arch plugin for Pest PHP.", + "keywords": [ + "arch", + "architecture", + "framework", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-arch/tree/v4.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2025-08-20T13:10:51+00:00" + }, + { + "name": "pestphp/pest-plugin-laravel", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-laravel.git", + "reference": "e12a07046b826a40b1c8632fd7b80d6b8d7b628e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-laravel/zipball/e12a07046b826a40b1c8632fd7b80d6b8d7b628e", + "reference": "e12a07046b826a40b1c8632fd7b80d6b8d7b628e", + "shasum": "" + }, + "require": { + "laravel/framework": "^11.45.2|^12.25.0", + "pestphp/pest": "^4.0.0", + "php": "^8.3.0" + }, + "require-dev": { + "laravel/dusk": "^8.3.3", + "orchestra/testbench": "^9.13.0|^10.5.0", + "pestphp/pest-dev-tools": "^4.0.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Laravel\\Plugin" + ] + }, + "laravel": { + "providers": [ + "Pest\\Laravel\\PestServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Pest\\Laravel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest Laravel Plugin", + "keywords": [ + "framework", + "laravel", + "pest", + "php", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-laravel/tree/v4.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2025-08-20T12:46:37+00:00" + }, + { + "name": "pestphp/pest-plugin-mutate", + "version": "v4.0.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-mutate.git", + "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-mutate/zipball/d9b32b60b2385e1688a68cc227594738ec26d96c", + "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.6.1", + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3", + "psr/simple-cache": "^3.0.0" + }, + "require-dev": { + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0", + "pestphp/pest-plugin-type-coverage": "^4.0.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Pest\\Mutate\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + }, + { + "name": "Sandro Gehri", + "email": "sandrogehri@gmail.com" + } + ], + "description": "Mutates your code to find untested cases", + "keywords": [ + "framework", + "mutate", + "mutation", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-mutate/tree/v4.0.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/gehrisandro", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2025-08-21T20:19:25+00:00" + }, + { + "name": "pestphp/pest-plugin-profanity", + "version": "v4.0.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-profanity.git", + "reference": "823d5d8ae07a265c70f5e1a9ce50639543b0bf11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-profanity/zipball/823d5d8ae07a265c70f5e1a9ce50639543b0bf11", + "reference": "823d5d8ae07a265c70f5e1a9ce50639543b0bf11", + "shasum": "" + }, + "require": { + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3" + }, + "require-dev": { + "faissaloux/pest-plugin-inside": "^1.9", + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Profanity\\Plugin" + ] + } + }, + "autoload": { + "psr-4": { + "Pest\\Profanity\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest Profanity Plugin", + "keywords": [ + "framework", + "pest", + "php", + "plugin", + "profanity", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-profanity/tree/v4.0.1" + }, + "time": "2025-08-20T12:58:03+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.6.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94f8051919d1b0369a6bcc7931d679a511c03fe9", + "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.3" + }, + "time": "2025-08-01T19:43:32+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.18|^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" + }, + "time": "2024-11-09T15:12:26+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", + "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0" + }, + "time": "2025-08-30T15:50:23+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "12.3.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "7ad0e9bdc72b147600badccd694a2e57ffc9297a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7ad0e9bdc72b147600badccd694a2e57ffc9297a", + "reference": "7ad0e9bdc72b147600badccd694a2e57ffc9297a", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.4.0", + "php": ">=8.3", + "phpunit/php-file-iterator": "^6.0", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.0", + "sebastian/lines-of-code": "^4.0", + "sebastian/version": "^6.0", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "12.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2025-08-29T11:32:44+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "961bc913d42fe24a257bfff826a5068079ac7782" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/961bc913d42fe24a257bfff826a5068079ac7782", + "reference": "961bc913d42fe24a257bfff826a5068079ac7782", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:37+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:58+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:59:16+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "8.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:59:38+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "12.3.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "b8fa997c49682979ad6bfaa0d7fb25f54954965e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b8fa997c49682979ad6bfaa0d7fb25f54954965e", + "reference": "b8fa997c49682979ad6bfaa0d7fb25f54954965e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.3", + "phpunit/php-code-coverage": "^12.3.3", + "phpunit/php-file-iterator": "^6.0.0", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.0.0", + "sebastian/comparator": "^7.1.3", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.0.3", + "sebastian/exporter": "^7.0.0", + "sebastian/global-state": "^8.0.0", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/type": "^6.0.3", + "sebastian/version": "^6.0.0", + "staabm/side-effects-detector": "^1.0.5" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "12.3-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.7" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2025-08-28T05:15:46+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/6d584c727d9114bcdc14c86711cd1cad51778e7c", + "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:53:50+00:00" + }, + { + "name": "sebastian/comparator", + "version": "7.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "dc904b4bb3ab070865fa4068cd84f3da8b945148" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/dc904b4bb3ab070865fa4068cd84f3da8b945148", + "reference": "dc904b4bb3ab070865fa4068cd84f3da8b945148", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.2" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2025-08-20T11:27:00+00:00" + }, + { + "name": "sebastian/complexity", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:25+00:00" + }, + { + "name": "sebastian/diff", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:46+00:00" + }, + { + "name": "sebastian/environment", + "version": "8.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/24a711b5c916efc6d6e62aa65aa2ec98fef77f68", + "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/8.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" + } + ], + "time": "2025-08-12T14:11:56+00:00" + }, + { + "name": "sebastian/exporter", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "76432aafc58d50691a00d86d0632f1217a47b688" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/76432aafc58d50691a00d86d0632f1217a47b688", + "reference": "76432aafc58d50691a00d86d0632f1217a47b688", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:56:42+00:00" + }, + { + "name": "sebastian/global-state", + "version": "8.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "ef1377171613d09edd25b7816f05be8313f9115d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d", + "reference": "ef1377171613d09edd25b7816f05be8313f9115d", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" + } + ], + "time": "2025-08-29T11:29:25+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:57:28+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:57:48+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:17+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-13T04:44:59+00:00" + }, + { + "name": "sebastian/type", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d", + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/6.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" + } + ], + "time": "2025-08-09T06:57:12+00:00" + }, + { + "name": "sebastian/version", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T05:00:38+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "symfony/yaml", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "d4f4a66866fe2451f61296924767280ab5732d9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/d4f4a66866fe2451f61296924767280ab5732d9d", + "reference": "d4f4a66866fe2451f61296924767280ab5732d9d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-27T11:34:33+00:00" + }, + { + "name": "ta-tikoma/phpunit-architecture-test", + "version": "0.8.5", + "source": { + "type": "git", + "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git", + "reference": "cf6fb197b676ba716837c886baca842e4db29005" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/cf6fb197b676ba716837c886baca842e4db29005", + "reference": "cf6fb197b676ba716837c886baca842e4db29005", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18.0 || ^5.0.0", + "php": "^8.1.0", + "phpdocumentor/reflection-docblock": "^5.3.0", + "phpunit/phpunit": "^10.5.5 || ^11.0.0 || ^12.0.0", + "symfony/finder": "^6.4.0 || ^7.0.0" + }, + "require-dev": { + "laravel/pint": "^1.13.7", + "phpstan/phpstan": "^1.10.52" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPUnit\\Architecture\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ni Shi", + "email": "futik0ma011@gmail.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Methods for testing application architecture", + "keywords": [ + "architecture", + "phpunit", + "stucture", + "test", + "testing" + ], + "support": { + "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues", + "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.5" + }, + "time": "2025-04-20T20:23:40+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": "^8.2" + }, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/config/app.php b/config/app.php new file mode 100644 index 0000000..423eed5 --- /dev/null +++ b/config/app.php @@ -0,0 +1,126 @@ + env('APP_NAME', 'Laravel'), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services the application utilizes. Set this in your ".env" file. + | + */ + + 'env' => env('APP_ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Application Debug Mode + |-------------------------------------------------------------------------- + | + | When your application is in debug mode, detailed error messages with + | stack traces will be shown on every error that occurs within your + | application. If disabled, a simple generic error page is shown. + | + */ + + 'debug' => (bool) env('APP_DEBUG', false), + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | the application so that it's available within Artisan commands. + | + */ + + 'url' => env('APP_URL', 'http://localhost'), + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. The timezone + | is set to "UTC" by default as it is suitable for most use cases. + | + */ + + 'timezone' => 'UTC', + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by Laravel's translation / localization methods. This option can be + | set to any locale for which you plan to have translation strings. + | + */ + + 'locale' => env('APP_LOCALE', 'en'), + + 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), + + 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is utilized by Laravel's encryption services and should be set + | to a random, 32 character string to ensure that all encrypted values + | are secure. You should do this prior to deploying the application. + | + */ + + 'cipher' => 'AES-256-CBC', + + 'key' => env('APP_KEY'), + + 'previous_keys' => [ + ...array_filter( + explode(',', (string) env('APP_PREVIOUS_KEYS', '')) + ), + ], + + /* + |-------------------------------------------------------------------------- + | Maintenance Mode Driver + |-------------------------------------------------------------------------- + | + | These configuration options determine the driver used to determine and + | manage Laravel's "maintenance mode" status. The "cache" driver will + | allow maintenance mode to be controlled across multiple machines. + | + | Supported drivers: "file", "cache" + | + */ + + 'maintenance' => [ + 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), + 'store' => env('APP_MAINTENANCE_STORE', 'database'), + ], + +]; diff --git a/config/auth.php b/config/auth.php new file mode 100644 index 0000000..7d1eb0d --- /dev/null +++ b/config/auth.php @@ -0,0 +1,115 @@ + [ + 'guard' => env('AUTH_GUARD', 'web'), + 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), + ], + + /* + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | which utilizes session storage plus the Eloquent user provider. + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | Supported: "session" + | + */ + + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + ], + ], + + /* + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | If you have multiple user tables or models you may configure multiple + | providers to represent the model / table. These providers may then + | be assigned to any extra authentication guards you have defined. + | + | Supported: "database", "eloquent" + | + */ + + 'providers' => [ + 'users' => [ + 'driver' => 'eloquent', + 'model' => env('AUTH_MODEL', App\Models\User::class), + ], + + // 'users' => [ + // 'driver' => 'database', + // 'table' => 'users', + // ], + ], + + /* + |-------------------------------------------------------------------------- + | Resetting Passwords + |-------------------------------------------------------------------------- + | + | These configuration options specify the behavior of Laravel's password + | reset functionality, including the table utilized for token storage + | and the user provider that is invoked to actually retrieve users. + | + | The expiry time is the number of minutes that each reset token will be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + | The throttle setting is the number of seconds a user must wait before + | generating more password reset tokens. This prevents the user from + | quickly generating a very large amount of password reset tokens. + | + */ + + 'passwords' => [ + 'users' => [ + 'provider' => 'users', + 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), + 'expire' => 60, + 'throttle' => 60, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Password Confirmation Timeout + |-------------------------------------------------------------------------- + | + | Here you may define the number of seconds before a password confirmation + | window expires and users are asked to re-enter their password via the + | confirmation screen. By default, the timeout lasts for three hours. + | + */ + + 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), + +]; diff --git a/config/cache.php b/config/cache.php new file mode 100644 index 0000000..c2d927d --- /dev/null +++ b/config/cache.php @@ -0,0 +1,108 @@ + env('CACHE_STORE', 'database'), + + /* + |-------------------------------------------------------------------------- + | Cache Stores + |-------------------------------------------------------------------------- + | + | Here you may define all of the cache "stores" for your application as + | well as their drivers. You may even define multiple stores for the + | same cache driver to group types of items stored in your caches. + | + | Supported drivers: "array", "database", "file", "memcached", + | "redis", "dynamodb", "octane", "null" + | + */ + + 'stores' => [ + + 'array' => [ + 'driver' => 'array', + 'serialize' => false, + ], + + 'database' => [ + 'driver' => 'database', + 'connection' => env('DB_CACHE_CONNECTION'), + 'table' => env('DB_CACHE_TABLE', 'cache'), + 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), + 'lock_table' => env('DB_CACHE_LOCK_TABLE'), + ], + + 'file' => [ + 'driver' => 'file', + 'path' => storage_path('framework/cache/data'), + 'lock_path' => storage_path('framework/cache/data'), + ], + + 'memcached' => [ + 'driver' => 'memcached', + 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), + 'sasl' => [ + env('MEMCACHED_USERNAME'), + env('MEMCACHED_PASSWORD'), + ], + 'options' => [ + // Memcached::OPT_CONNECT_TIMEOUT => 2000, + ], + 'servers' => [ + [ + 'host' => env('MEMCACHED_HOST', '127.0.0.1'), + 'port' => env('MEMCACHED_PORT', 11211), + 'weight' => 100, + ], + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), + 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), + ], + + 'dynamodb' => [ + 'driver' => 'dynamodb', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), + 'endpoint' => env('DYNAMODB_ENDPOINT'), + ], + + 'octane' => [ + 'driver' => 'octane', + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing the APC, database, memcached, Redis, and DynamoDB cache + | stores, there might be other applications using the same cache. For + | that reason, you may prefix every cache key to avoid collisions. + | + */ + + 'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'), + +]; diff --git a/config/database.php b/config/database.php new file mode 100644 index 0000000..5b318f5 --- /dev/null +++ b/config/database.php @@ -0,0 +1,174 @@ + env('DB_CONNECTION', 'sqlite'), + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Below are all of the database connections defined for your application. + | An example configuration is provided for each database system which + | is supported by Laravel. You're free to add / remove connections. + | + */ + + 'connections' => [ + + 'sqlite' => [ + 'driver' => 'sqlite', + 'url' => env('DB_URL'), + 'database' => env('DB_DATABASE', database_path('database.sqlite')), + 'prefix' => '', + 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), + 'busy_timeout' => null, + 'journal_mode' => null, + 'synchronous' => null, + ], + + 'mysql' => [ + 'driver' => 'mysql', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'mariadb' => [ + 'driver' => 'mariadb', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'pgsql' => [ + 'driver' => 'pgsql', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '5432'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => '', + 'prefix_indexes' => true, + 'search_path' => 'public', + 'sslmode' => 'prefer', + ], + + 'sqlsrv' => [ + 'driver' => 'sqlsrv', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT', '1433'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => '', + 'prefix_indexes' => true, + // 'encrypt' => env('DB_ENCRYPT', 'yes'), + // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'), + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run on the database. + | + */ + + 'migrations' => [ + 'table' => 'migrations', + 'update_date_on_publish' => true, + ], + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer body of commands than a typical key-value system + | such as Memcached. You may define your connection settings here. + | + */ + + 'redis' => [ + + 'client' => env('REDIS_CLIENT', 'phpredis'), + + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'), + 'persistent' => env('REDIS_PERSISTENT', false), + ], + + 'default' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), + ], + + 'cache' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_CACHE_DB', '1'), + ], + + ], + +]; diff --git a/config/filesystems.php b/config/filesystems.php new file mode 100644 index 0000000..3d671bd --- /dev/null +++ b/config/filesystems.php @@ -0,0 +1,80 @@ + env('FILESYSTEM_DISK', 'local'), + + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Below you may configure as many filesystem disks as necessary, and you + | may even configure multiple disks for the same driver. Examples for + | most supported storage drivers are configured here for reference. + | + | Supported drivers: "local", "ftp", "sftp", "s3" + | + */ + + 'disks' => [ + + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app/private'), + 'serve' => true, + 'throw' => false, + 'report' => false, + ], + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'public', + 'throw' => false, + 'report' => false, + ], + + 's3' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION'), + 'bucket' => env('AWS_BUCKET'), + 'url' => env('AWS_URL'), + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), + 'throw' => false, + 'report' => false, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Symbolic Links + |-------------------------------------------------------------------------- + | + | Here you may configure the symbolic links that will be created when the + | `storage:link` Artisan command is executed. The array keys should be + | the locations of the links and the values should be their targets. + | + */ + + 'links' => [ + public_path('storage') => storage_path('app/public'), + ], + +]; diff --git a/config/logging.php b/config/logging.php new file mode 100644 index 0000000..9e998a4 --- /dev/null +++ b/config/logging.php @@ -0,0 +1,132 @@ + env('LOG_CHANNEL', 'stack'), + + /* + |-------------------------------------------------------------------------- + | Deprecations Log Channel + |-------------------------------------------------------------------------- + | + | This option controls the log channel that should be used to log warnings + | regarding deprecated PHP and library features. This allows you to get + | your application ready for upcoming major versions of dependencies. + | + */ + + 'deprecations' => [ + 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), + 'trace' => env('LOG_DEPRECATIONS_TRACE', false), + ], + + /* + |-------------------------------------------------------------------------- + | Log Channels + |-------------------------------------------------------------------------- + | + | Here you may configure the log channels for your application. Laravel + | utilizes the Monolog PHP logging library, which includes a variety + | of powerful log handlers and formatters that you're free to use. + | + | Available drivers: "single", "daily", "slack", "syslog", + | "errorlog", "monolog", "custom", "stack" + | + */ + + 'channels' => [ + + 'stack' => [ + 'driver' => 'stack', + 'channels' => explode(',', (string) env('LOG_STACK', 'single')), + 'ignore_exceptions' => false, + ], + + 'single' => [ + 'driver' => 'single', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'daily' => [ + 'driver' => 'daily', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'days' => env('LOG_DAILY_DAYS', 14), + 'replace_placeholders' => true, + ], + + 'slack' => [ + 'driver' => 'slack', + 'url' => env('LOG_SLACK_WEBHOOK_URL'), + 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), + 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), + 'level' => env('LOG_LEVEL', 'critical'), + 'replace_placeholders' => true, + ], + + 'papertrail' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), + 'handler_with' => [ + 'host' => env('PAPERTRAIL_URL'), + 'port' => env('PAPERTRAIL_PORT'), + 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), + ], + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'stderr' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => StreamHandler::class, + 'handler_with' => [ + 'stream' => 'php://stderr', + ], + 'formatter' => env('LOG_STDERR_FORMATTER'), + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'syslog' => [ + 'driver' => 'syslog', + 'level' => env('LOG_LEVEL', 'debug'), + 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), + 'replace_placeholders' => true, + ], + + 'errorlog' => [ + 'driver' => 'errorlog', + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'null' => [ + 'driver' => 'monolog', + 'handler' => NullHandler::class, + ], + + 'emergency' => [ + 'path' => storage_path('logs/laravel.log'), + ], + + ], + +]; diff --git a/config/mail.php b/config/mail.php new file mode 100644 index 0000000..385fd44 --- /dev/null +++ b/config/mail.php @@ -0,0 +1,119 @@ + env('MAIL_MAILER', 'log'), + + /* + |-------------------------------------------------------------------------- + | Mailer Configurations + |-------------------------------------------------------------------------- + | + | Here you may configure all of the mailers used by your application plus + | their respective settings. Several examples have been configured for + | you and you are free to add your own as your application requires. + | + | Laravel supports a variety of mail "transport" drivers that can be used + | when delivering an email. You may specify which one you're using for + | your mailers below. You may also add additional mailers if needed. + | + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "resend", "log", "array", + | "failover", "roundrobin" + | + */ + + 'mailers' => [ + + 'smtp' => [ + 'transport' => 'smtp', + 'scheme' => env('MAIL_SCHEME'), + 'url' => env('MAIL_URL'), + 'host' => env('MAIL_HOST', '127.0.0.1'), + 'port' => env('MAIL_PORT', 2525), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'timeout' => null, + 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)), + ], + + 'ses' => [ + 'transport' => 'ses', + ], + + 'postmark' => [ + 'transport' => 'postmark', + // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), + // 'client' => [ + // 'timeout' => 5, + // ], + ], + + 'resend' => [ + 'transport' => 'resend', + ], + + 'sendmail' => [ + 'transport' => 'sendmail', + 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), + ], + + 'log' => [ + 'transport' => 'log', + 'channel' => env('MAIL_LOG_CHANNEL'), + ], + + 'array' => [ + 'transport' => 'array', + ], + + 'failover' => [ + 'transport' => 'failover', + 'mailers' => [ + 'smtp', + 'log', + ], + 'retry_after' => 60, + ], + + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', + ], + 'retry_after' => 60, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all emails sent by your application to be sent from + | the same address. Here you may specify a name and address that is + | used globally for all emails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_FROM_NAME', 'Example'), + ], + +]; diff --git a/config/openssl.php b/config/openssl.php new file mode 100644 index 0000000..ada2da4 --- /dev/null +++ b/config/openssl.php @@ -0,0 +1,31 @@ + [ + 'countryName' => env('CA_ROOT_COUNTRY_NAME'), + 'organizationName' => env('CA_ROOT_ORGANIZATION_NAME'), + 'organizationalUnitName' => env('CA_ROOT_ORGANIZATIONAL_UNIT_NAME'), + 'commonName' => env('CA_ROOT_COMMON_NAME'), + ], + 'ca_4096' => [ + 'countryName' => env('CA_4096_COUNTRY_NAME'), + 'organizationName' => env('CA_4096_ORGANIZATION_NAME'), + 'organizationalUnitName' => env('CA_4096_ORGANIZATIONAL_UNIT_NAME'), + 'commonName' => env('CA_4096_COMMON_NAME'), + ], + 'ca_2048' => [ + 'countryName' => env('CA_2048_COUNTRY_NAME'), + 'organizationName' => env('CA_2048_ORGANIZATION_NAME'), + 'organizationalUnitName' => env('CA_2048_ORGANIZATIONAL_UNIT_NAME'), + 'commonName' => env('CA_2048_COMMON_NAME'), + ], + 'ca_leaf_default' => [ + 'countryName' => env('CA_LEAF_DEFAULT_COUNTRY_NAME'), + 'localityName' => env('CA_LEAF_DEFAULT_LOCALITY'), + 'stateOrProvinceName' => env('CA_LEAF_DEFAULT_STATE'), + 'organizationName' => env('CA_LEAF_DEFAULT_ORGANIZATION_NAME'), + 'commonName' => env('CA_LEAF_DEFAULT_COMMON_NAME'), + ], +]; + + diff --git a/config/queue.php b/config/queue.php new file mode 100644 index 0000000..116bd8d --- /dev/null +++ b/config/queue.php @@ -0,0 +1,112 @@ + env('QUEUE_CONNECTION', 'database'), + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection options for every queue backend + | used by your application. An example configuration is provided for + | each backend supported by Laravel. You're also free to add more. + | + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" + | + */ + + 'connections' => [ + + 'sync' => [ + 'driver' => 'sync', + ], + + 'database' => [ + 'driver' => 'database', + 'connection' => env('DB_QUEUE_CONNECTION'), + 'table' => env('DB_QUEUE_TABLE', 'jobs'), + 'queue' => env('DB_QUEUE', 'default'), + 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90), + 'after_commit' => false, + ], + + 'beanstalkd' => [ + 'driver' => 'beanstalkd', + 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), + 'queue' => env('BEANSTALKD_QUEUE', 'default'), + 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), + 'block_for' => 0, + 'after_commit' => false, + ], + + 'sqs' => [ + 'driver' => 'sqs', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), + 'queue' => env('SQS_QUEUE', 'default'), + 'suffix' => env('SQS_SUFFIX'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'after_commit' => false, + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), + 'block_for' => null, + 'after_commit' => false, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Job Batching + |-------------------------------------------------------------------------- + | + | The following options configure the database and table that store job + | batching information. These options can be updated to any database + | connection and table which has been defined by your application. + | + */ + + 'batching' => [ + 'database' => env('DB_CONNECTION', 'sqlite'), + 'table' => 'job_batches', + ], + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control how and where failed jobs are stored. Laravel ships with + | support for storing failed jobs in a simple file or in a database. + | + | Supported drivers: "database-uuids", "dynamodb", "file", "null" + | + */ + + 'failed' => [ + 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), + 'database' => env('DB_CONNECTION', 'sqlite'), + 'table' => 'failed_jobs', + ], + +]; diff --git a/config/services.php b/config/services.php new file mode 100644 index 0000000..262aabe --- /dev/null +++ b/config/services.php @@ -0,0 +1,50 @@ + [ + 'token' => env('POSTMARK_TOKEN'), + ], + + 'resend' => [ + 'key' => env('RESEND_KEY'), + ], + + 'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + ], + + 'slack' => [ + 'notifications' => [ + 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), + 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), + ], + ], + + 'github' => [ + 'client_id' => env('GITHUB_CLIENT_ID'), + 'client_secret' => env('GITHUB_CLIENT_SECRET'), + 'redirect' => env('GITHUB_REDIRECT_URI'), + ], + + 'google' => [ + 'client_id' => env('GOOGLE_CLIENT_ID'), + 'client_secret' => env('GOOGLE_CLIENT_SECRET'), + 'redirect' => env('GOOGLE_REDIRECT_URI'), + ], + +]; diff --git a/config/session.php b/config/session.php new file mode 100644 index 0000000..f715097 --- /dev/null +++ b/config/session.php @@ -0,0 +1,217 @@ + env('SESSION_DRIVER', 'database'), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle before it expires. If you want them + | to expire immediately when the browser is closed then you may + | indicate that via the expire_on_close configuration option. + | + */ + + 'lifetime' => (int) env('SESSION_LIFETIME', 120), + + 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false), + + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it's stored. All encryption is performed + | automatically by Laravel and you may use the session like normal. + | + */ + + 'encrypt' => env('SESSION_ENCRYPT', false), + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When utilizing the "file" session driver, the session files are placed + | on disk. The default storage location is defined here; however, you + | are free to provide another location where they should be stored. + | + */ + + 'files' => storage_path('framework/sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | + */ + + 'connection' => env('SESSION_CONNECTION'), + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table to + | be used to store sessions. Of course, a sensible default is defined + | for you; however, you're welcome to change this to another table. + | + */ + + 'table' => env('SESSION_TABLE', 'sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + | + | When using one of the framework's cache driven session backends, you may + | define the cache store which should be used to store the session data + | between requests. This must match one of your defined cache stores. + | + | Affects: "dynamodb", "memcached", "redis" + | + */ + + 'store' => env('SESSION_STORE'), + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | + */ + + 'lottery' => [2, 100], + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the session cookie that is created by + | the framework. Typically, you should not need to change this value + | since doing so does not grant a meaningful security improvement. + | + */ + + 'cookie' => env( + 'SESSION_COOKIE', + Str::slug(env('APP_NAME', 'laravel')).'-session' + ), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application, but you're free to change this when necessary. + | + */ + + 'path' => env('SESSION_PATH', '/'), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | This value determines the domain and subdomains the session cookie is + | available to. By default, the cookie will be available to the root + | domain and all subdomains. Typically, this shouldn't be changed. + | + */ + + 'domain' => env('SESSION_DOMAIN'), + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + | + | By setting this option to true, session cookies will only be sent back + | to the server if the browser has a HTTPS connection. This will keep + | the cookie from being sent to you when it can't be done securely. + | + */ + + 'secure' => env('SESSION_SECURE_COOKIE'), + + /* + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + | + | Setting this value to true will prevent JavaScript from accessing the + | value of the cookie and the cookie will only be accessible through + | the HTTP protocol. It's unlikely you should disable this option. + | + */ + + 'http_only' => env('SESSION_HTTP_ONLY', true), + + /* + |-------------------------------------------------------------------------- + | Same-Site Cookies + |-------------------------------------------------------------------------- + | + | This option determines how your cookies behave when cross-site requests + | take place, and can be used to mitigate CSRF attacks. By default, we + | will set this value to "lax" to permit secure cross-site requests. + | + | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value + | + | Supported: "lax", "strict", "none", null + | + */ + + 'same_site' => env('SESSION_SAME_SITE', 'lax'), + + /* + |-------------------------------------------------------------------------- + | Partitioned Cookies + |-------------------------------------------------------------------------- + | + | Setting this value to true will tie the cookie to the top-level site for + | a cross-site context. Partitioned cookies are accepted by the browser + | when flagged "secure" and the Same-Site attribute is set to "none". + | + */ + + 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), + +]; diff --git a/database/.gitignore b/database/.gitignore new file mode 100644 index 0000000..9b19b93 --- /dev/null +++ b/database/.gitignore @@ -0,0 +1 @@ +*.sqlite* diff --git a/database/factories/ApiKeyFactory.php b/database/factories/ApiKeyFactory.php new file mode 100644 index 0000000..5bee8bb --- /dev/null +++ b/database/factories/ApiKeyFactory.php @@ -0,0 +1,31 @@ + $this->faker->words(3, true), + 'key' => Str::random(64), + 'last_used_at' => null, + ]; + } +} diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php new file mode 100644 index 0000000..d03e102 --- /dev/null +++ b/database/factories/UserFactory.php @@ -0,0 +1,45 @@ + + */ +class UserFactory extends Factory +{ + /** + * The current password being used by the factory. + */ + protected static ?string $password; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'first_name' => fake()->firstName(), + 'last_name' => fake()->lastName(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => static::$password ??= Hash::make('password'), + 'remember_token' => Str::random(10), + ]; + } + + /** + * Indicate that the model's email address should be unverified. + */ + public function unverified(): static + { + return $this->state(fn (array $attributes) => [ + 'email_verified_at' => null, + ]); + } +} diff --git a/database/install.sql b/database/install.sql new file mode 100644 index 0000000..199302b --- /dev/null +++ b/database/install.sql @@ -0,0 +1,158 @@ +-- Database Export for Direct Import (e.g., cPanel/phpMyAdmin) +-- Includes Schema and Initial Data (Roles & Admin User) + +-- Table structure for `roles` +CREATE TABLE `roles` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `label` varchar(255) NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `roles_name_unique` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +INSERT INTO `roles` (`id`, `name`, `label`, `created_at`, `updated_at`) VALUES +(1, 'admin', 'Administrator', NOW(), NOW()), +(2, 'customer', 'Customer', NOW(), NOW()); + +-- Table structure for `users` +CREATE TABLE `users` ( + `id` varchar(32) NOT NULL, + `first_name` varchar(255) NOT NULL, + `last_name` varchar(255) DEFAULT NULL, + `email` varchar(255) NOT NULL, + `email_verified_at` timestamp NULL DEFAULT NULL, + `password` varchar(255) DEFAULT NULL, + `status` varchar(255) NOT NULL DEFAULT 'active', + `phone` varchar(255) DEFAULT NULL, + `bio` text DEFAULT NULL, + `avatar` varchar(255) DEFAULT NULL, + `country` varchar(255) DEFAULT NULL, + `city_state` varchar(255) DEFAULT NULL, + `postal_code` varchar(255) DEFAULT NULL, + `tax_id` varchar(255) DEFAULT NULL, + `facebook` varchar(255) DEFAULT NULL, + `x_link` varchar(255) DEFAULT NULL, + `linkedin` varchar(255) DEFAULT NULL, + `instagram` varchar(255) DEFAULT NULL, + `google_id` varchar(255) DEFAULT NULL, + `google_token` text DEFAULT NULL, + `google_refresh_token` text DEFAULT NULL, + `github_id` varchar(255) DEFAULT NULL, + `github_token` text DEFAULT NULL, + `github_refresh_token` text DEFAULT NULL, + `remember_token` varchar(100) DEFAULT NULL, + `role_id` bigint(20) unsigned DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `users_email_unique` (`email`), + KEY `users_role_id_foreign` (`role_id`), + CONSTRAINT `users_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Admin User (Password: password) +INSERT INTO `users` (`id`, `first_name`, `last_name`, `email`, `password`, `role_id`, `status`, `created_at`, `updated_at`) VALUES +('ADMIN000000000000000000000000001', 'Admin', 'User', 'admin@dyzulk.com', '$2y$12$R.SjA7Gk/9l7HlA.zC6iGOJbA5HkXfLrTYDR.SjA7Gk/9l7HlA.zC6iG', 1, 'active', NOW(), NOW()); + +-- Table structure for `api_keys` +CREATE TABLE `api_keys` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `user_id` varchar(32) NOT NULL, + `name` varchar(255) NOT NULL, + `key` varchar(64) NOT NULL, + `is_active` tinyint(1) NOT NULL DEFAULT 1, + `last_used_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `api_keys_key_unique` (`key`), + KEY `api_keys_user_id_foreign` (`user_id`), + CONSTRAINT `api_keys_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Table structure for `ca_certificates` +CREATE TABLE `ca_certificates` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `uuid` char(36) NOT NULL, + `ca_type` varchar(255) NOT NULL, + `cert_content` text NOT NULL, + `key_content` text NOT NULL, + `serial_number` varchar(255) DEFAULT NULL, + `common_name` varchar(255) DEFAULT NULL, + `organization` varchar(255) DEFAULT NULL, + `valid_from` datetime DEFAULT NULL, + `valid_to` datetime DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `ca_certificates_uuid_unique` (`uuid`), + UNIQUE KEY `ca_certificates_ca_type_unique` (`ca_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Table structure for `certificates` +CREATE TABLE `certificates` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `uuid` char(36) NOT NULL, + `user_id` varchar(32) NOT NULL, + `common_name` varchar(255) NOT NULL, + `organization` varchar(255) DEFAULT NULL, + `locality` varchar(255) DEFAULT NULL, + `state` varchar(255) DEFAULT NULL, + `country` varchar(10) DEFAULT NULL, + `san` text DEFAULT NULL, + `key_bits` int(11) NOT NULL DEFAULT 2048, + `serial_number` varchar(255) NOT NULL, + `cert_content` text NOT NULL, + `key_content` text NOT NULL, + `csr_content` text DEFAULT NULL, + `valid_from` timestamp NULL DEFAULT NULL, + `valid_to` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `certificates_uuid_unique` (`uuid`), + UNIQUE KEY `certificates_serial_number_unique` (`serial_number`), + KEY `certificates_user_id_foreign` (`user_id`), + CONSTRAINT `certificates_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Table structure for `cache`, `jobs`, `sessions`, etc. +CREATE TABLE `cache` ( + `key` varchar(255) NOT NULL, + `value` mediumtext NOT NULL, + `expiration` int(11) NOT NULL, + PRIMARY KEY (`key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE `sessions` ( + `id` varchar(255) NOT NULL, + `user_id` varchar(32) DEFAULT NULL, + `ip_address` varchar(45) DEFAULT NULL, + `user_agent` text DEFAULT NULL, + `payload` longtext NOT NULL, + `last_activity` int(11) NOT NULL, + PRIMARY KEY (`id`), + KEY `sessions_user_id_index` (`user_id`), + KEY `sessions_last_activity_index` (`last_activity`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE `migrations` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `migration` varchar(255) NOT NULL, + `batch` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES +(1,'0000_00_00_000000_create_roles_table',1), +(2,'0001_01_01_000000_create_users_table',1), +(3,'0001_01_01_000001_create_cache_table',1), +(4,'0001_01_01_000002_create_jobs_table',1), +(5,'2025_12_21_051706_create_ca_certificates_table',1), +(6,'2025_12_21_051735_create_certificates_table',1), +(7,'2025_12_21_161950_create_login_histories_table',1), +(8,'2025_12_22_012656_add_status_to_users_table',1), +(9,'2025_12_22_025212_create_api_keys_table',1), +(10,'2025_12_22_030724_add_is_active_to_api_keys_table',1); diff --git a/database/migrations/0000_00_00_000000_create_roles_table.php b/database/migrations/0000_00_00_000000_create_roles_table.php new file mode 100644 index 0000000..9e65cda --- /dev/null +++ b/database/migrations/0000_00_00_000000_create_roles_table.php @@ -0,0 +1,29 @@ +id(); + $table->string('name')->unique(); + $table->string('label'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('roles'); + } +}; diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php new file mode 100644 index 0000000..081addd --- /dev/null +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -0,0 +1,83 @@ +string('id', 32)->primary(); + + // Personal Information + $table->string('first_name'); + $table->string('last_name')->nullable(); + $table->string('email')->unique(); + $table->timestamp('email_verified_at')->nullable(); + $table->string('password')->nullable(); + $table->string('phone')->nullable(); + $table->text('bio')->nullable(); + + // Avatar + $table->string('avatar')->nullable(); + + // Address Information + $table->string('country')->nullable(); + $table->string('city_state')->nullable(); + $table->string('postal_code')->nullable(); + $table->string('tax_id')->nullable(); + + // Social Links + $table->string('facebook')->nullable(); + $table->string('x_link')->nullable(); + $table->string('linkedin')->nullable(); + $table->string('instagram')->nullable(); + + // Social Auth - Google + $table->string('google_id')->nullable(); + $table->text('google_token')->nullable(); + $table->text('google_refresh_token')->nullable(); + + // Social Auth - GitHub + $table->string('github_id')->nullable(); + $table->text('github_token')->nullable(); + $table->text('github_refresh_token')->nullable(); + + + + $table->rememberToken(); + $table->foreignId('role_id')->nullable()->constrained('roles')->onDelete('set null'); + $table->timestamps(); + }); + + Schema::create('password_reset_tokens', function (Blueprint $table) { + $table->string('email')->primary(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + + Schema::create('sessions', function (Blueprint $table) { + $table->string('id')->primary(); + $table->string('user_id', 32)->nullable()->index(); + $table->string('ip_address', 45)->nullable(); + $table->text('user_agent')->nullable(); + $table->longText('payload'); + $table->integer('last_activity')->index(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('users'); + Schema::dropIfExists('password_reset_tokens'); + Schema::dropIfExists('sessions'); + } +}; diff --git a/database/migrations/0001_01_01_000001_create_cache_table.php b/database/migrations/0001_01_01_000001_create_cache_table.php new file mode 100644 index 0000000..b9c106b --- /dev/null +++ b/database/migrations/0001_01_01_000001_create_cache_table.php @@ -0,0 +1,35 @@ +string('key')->primary(); + $table->mediumText('value'); + $table->integer('expiration'); + }); + + Schema::create('cache_locks', function (Blueprint $table) { + $table->string('key')->primary(); + $table->string('owner'); + $table->integer('expiration'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('cache'); + Schema::dropIfExists('cache_locks'); + } +}; diff --git a/database/migrations/0001_01_01_000002_create_jobs_table.php b/database/migrations/0001_01_01_000002_create_jobs_table.php new file mode 100644 index 0000000..425e705 --- /dev/null +++ b/database/migrations/0001_01_01_000002_create_jobs_table.php @@ -0,0 +1,57 @@ +id(); + $table->string('queue')->index(); + $table->longText('payload'); + $table->unsignedTinyInteger('attempts'); + $table->unsignedInteger('reserved_at')->nullable(); + $table->unsignedInteger('available_at'); + $table->unsignedInteger('created_at'); + }); + + Schema::create('job_batches', function (Blueprint $table) { + $table->string('id')->primary(); + $table->string('name'); + $table->integer('total_jobs'); + $table->integer('pending_jobs'); + $table->integer('failed_jobs'); + $table->longText('failed_job_ids'); + $table->mediumText('options')->nullable(); + $table->integer('cancelled_at')->nullable(); + $table->integer('created_at'); + $table->integer('finished_at')->nullable(); + }); + + Schema::create('failed_jobs', function (Blueprint $table) { + $table->id(); + $table->string('uuid')->unique(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('jobs'); + Schema::dropIfExists('job_batches'); + Schema::dropIfExists('failed_jobs'); + } +}; diff --git a/database/migrations/2025_12_21_051706_create_ca_certificates_table.php b/database/migrations/2025_12_21_051706_create_ca_certificates_table.php new file mode 100644 index 0000000..2e788fa --- /dev/null +++ b/database/migrations/2025_12_21_051706_create_ca_certificates_table.php @@ -0,0 +1,36 @@ +id(); + $table->uuid('uuid')->unique(); + $table->string('ca_type')->unique(); + $table->text('cert_content'); + $table->text('key_content'); + $table->string('serial_number')->nullable(); + $table->string('common_name')->nullable(); + $table->string('organization')->nullable(); + $table->dateTime('valid_from')->nullable(); + $table->dateTime('valid_to')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('ca_certificates'); + } +}; diff --git a/database/migrations/2025_12_21_051735_create_certificates_table.php b/database/migrations/2025_12_21_051735_create_certificates_table.php new file mode 100644 index 0000000..8a22cdd --- /dev/null +++ b/database/migrations/2025_12_21_051735_create_certificates_table.php @@ -0,0 +1,43 @@ +id(); + $table->uuid('uuid')->unique(); + $table->string('user_id', 32); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->string('common_name'); + $table->string('organization')->nullable(); + $table->string('locality')->nullable(); + $table->string('state')->nullable(); + $table->string('country', 10)->nullable(); + $table->text('san')->nullable(); + $table->integer('key_bits')->default(2048); + $table->string('serial_number')->unique(); + $table->text('cert_content'); + $table->text('key_content'); + $table->text('csr_content')->nullable(); + $table->timestamp('valid_from')->nullable(); + $table->timestamp('valid_to')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('certificates'); + } +}; diff --git a/database/migrations/2025_12_21_161950_create_login_histories_table.php b/database/migrations/2025_12_21_161950_create_login_histories_table.php new file mode 100644 index 0000000..ffb48fe --- /dev/null +++ b/database/migrations/2025_12_21_161950_create_login_histories_table.php @@ -0,0 +1,34 @@ +id(); + $table->string('user_id'); + $table->string('ip_address')->nullable(); + $table->string('user_agent')->nullable(); + $table->string('provider')->default('credentials'); + $table->timestamp('login_at')->useCurrent(); + $table->timestamps(); + + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('login_histories'); + } +}; diff --git a/database/migrations/2025_12_22_012656_add_status_to_users_table.php b/database/migrations/2025_12_22_012656_add_status_to_users_table.php new file mode 100644 index 0000000..75c8c53 --- /dev/null +++ b/database/migrations/2025_12_22_012656_add_status_to_users_table.php @@ -0,0 +1,28 @@ +string('status')->default('active')->after('password'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('status'); + }); + } +}; diff --git a/database/migrations/2025_12_22_025212_create_api_keys_table.php b/database/migrations/2025_12_22_025212_create_api_keys_table.php new file mode 100644 index 0000000..7e6064d --- /dev/null +++ b/database/migrations/2025_12_22_025212_create_api_keys_table.php @@ -0,0 +1,31 @@ +id(); + $table->foreignUuid('user_id')->constrained()->cascadeOnDelete(); + $table->string('name'); + $table->string('key', 64)->unique(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('api_keys'); + } +}; diff --git a/database/migrations/2025_12_22_030724_add_is_active_to_api_keys_table.php b/database/migrations/2025_12_22_030724_add_is_active_to_api_keys_table.php new file mode 100644 index 0000000..e89eead --- /dev/null +++ b/database/migrations/2025_12_22_030724_add_is_active_to_api_keys_table.php @@ -0,0 +1,28 @@ +boolean('is_active')->default(true)->after('key'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('api_keys', function (Blueprint $table) { + $table->dropColumn('is_active'); + }); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000..ae106b0 --- /dev/null +++ b/database/seeders/DatabaseSeeder.php @@ -0,0 +1,62 @@ + 'admin'], + ['label' => 'Administrator'] + ); + + $customerRole = \App\Models\Role::firstOrCreate( + ['name' => 'customer'], + ['label' => 'Customer'] + ); + + // Create Admin User + // Create Admin User + User::firstOrCreate( + ['email' => 'admin@dyzulk.com'], + [ + 'first_name' => 'Admin', + 'last_name' => 'User', + 'password' => \Illuminate\Support\Facades\Hash::make('password'), + 'role_id' => $adminRole->id, + 'email_verified_at' => now(), + ] + ); + + // Create Regular User + // Create Regular User + User::firstOrCreate( + ['email' => 'test@example.com'], + [ + 'first_name' => 'Test', + 'last_name' => 'User', + 'password' => \Illuminate\Support\Facades\Hash::make('password'), + 'role_id' => $customerRole->id, + ] + ); + + // Create specific test user for password reset + // User::firstOrCreate( + // ['email' => 'santulitam2024@gmail.com'], + // [ + // 'first_name' => 'Santul', + // 'last_name' => 'Itam', + // 'password' => \Illuminate\Support\Facades\Hash::make('password'), + // 'role_id' => $customerRole->id, + // ] + // ); + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..88e5786 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2667 @@ +{ + "name": "app", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@floating-ui/dom": "^1.7.4", + "@fullcalendar/core": "^6.1.19", + "@fullcalendar/daygrid": "^6.1.19", + "@fullcalendar/interaction": "^6.1.19", + "@fullcalendar/list": "^6.1.19", + "@fullcalendar/timegrid": "^6.1.19", + "@popperjs/core": "^2.11.8", + "alpinejs": "^3.14.9", + "apexcharts": "^5.3.5", + "flatpickr": "^4.6.13", + "jsvectormap": "^1.7.0", + "prismjs": "^1.30.0", + "swiper": "^12.0.3" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.1.12", + "axios": "^1.11.0", + "concurrently": "^9.0.1", + "laravel-vite-plugin": "^2.0.0", + "tailwindcss": "^4.1.12", + "vite": "^7.0.4" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@fullcalendar/core": { + "version": "6.1.19", + "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.19.tgz", + "integrity": "sha512-z0aVlO5e4Wah6p6mouM0UEqtRf1MZZPt4mwzEyU6kusaNL+dlWQgAasF2cK23hwT4cmxkEmr4inULXgpyeExdQ==", + "license": "MIT", + "dependencies": { + "preact": "~10.12.1" + } + }, + "node_modules/@fullcalendar/daygrid": { + "version": "6.1.19", + "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.19.tgz", + "integrity": "sha512-IAAfnMICnVWPjpT4zi87i3FEw0xxSza0avqY/HedKEz+l5MTBYvCDPOWDATpzXoLut3aACsjktIyw9thvIcRYQ==", + "license": "MIT", + "peerDependencies": { + "@fullcalendar/core": "~6.1.19" + } + }, + "node_modules/@fullcalendar/interaction": { + "version": "6.1.19", + "resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.19.tgz", + "integrity": "sha512-GOciy79xe8JMVp+1evAU3ytdwN/7tv35t5i1vFkifiuWcQMLC/JnLg/RA2s4sYmQwoYhTw/p4GLcP0gO5B3X5w==", + "license": "MIT", + "peerDependencies": { + "@fullcalendar/core": "~6.1.19" + } + }, + "node_modules/@fullcalendar/list": { + "version": "6.1.19", + "resolved": "https://registry.npmjs.org/@fullcalendar/list/-/list-6.1.19.tgz", + "integrity": "sha512-knZHpAVF0LbzZpSJSUmLUUzF0XlU/MRGK+Py2s0/mP93bCtno1k2L3XPs/kzh528hSjehwLm89RgKTSfW1P6cA==", + "license": "MIT", + "peerDependencies": { + "@fullcalendar/core": "~6.1.19" + } + }, + "node_modules/@fullcalendar/timegrid": { + "version": "6.1.19", + "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.19.tgz", + "integrity": "sha512-OuzpUueyO9wB5OZ8rs7TWIoqvu4v3yEqdDxZ2VcsMldCpYJRiOe7yHWKr4ap5Tb0fs7Rjbserc/b6Nt7ol6BRg==", + "license": "MIT", + "dependencies": { + "@fullcalendar/daygrid": "~6.1.19" + }, + "peerDependencies": { + "@fullcalendar/core": "~6.1.19" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.49.0.tgz", + "integrity": "sha512-rlKIeL854Ed0e09QGYFlmDNbka6I3EQFw7iZuugQjMb11KMpJCLPFL4ZPbMfaEhLADEL1yx0oujGkBQ7+qW3eA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.49.0.tgz", + "integrity": "sha512-cqPpZdKUSQYRtLLr6R4X3sD4jCBO1zUmeo3qrWBCqYIeH8Q3KRL4F3V7XJ2Rm8/RJOQBZuqzQGWPjjvFUcYa/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.49.0.tgz", + "integrity": "sha512-99kMMSMQT7got6iYX3yyIiJfFndpojBmkHfTc1rIje8VbjhmqBXE+nb7ZZP3A5skLyujvT0eIUCUsxAe6NjWbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.49.0.tgz", + "integrity": "sha512-y8cXoD3wdWUDpjOLMKLx6l+NFz3NlkWKcBCBfttUn+VGSfgsQ5o/yDUGtzE9HvsodkP0+16N0P4Ty1VuhtRUGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.49.0.tgz", + "integrity": "sha512-3mY5Pr7qv4GS4ZvWoSP8zha8YoiqrU+e0ViPvB549jvliBbdNLrg2ywPGkgLC3cmvN8ya3za+Q2xVyT6z+vZqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.49.0.tgz", + "integrity": "sha512-C9KzzOAQU5gU4kG8DTk+tjdKjpWhVWd5uVkinCwwFub2m7cDYLOdtXoMrExfeBmeRy9kBQMkiyJ+HULyF1yj9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.49.0.tgz", + "integrity": "sha512-OVSQgEZDVLnTbMq5NBs6xkmz3AADByCWI4RdKSFNlDsYXdFtlxS59J+w+LippJe8KcmeSSM3ba+GlsM9+WwC1w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.49.0.tgz", + "integrity": "sha512-ZnfSFA7fDUHNa4P3VwAcfaBLakCbYaxCk0jUnS3dTou9P95kwoOLAMlT3WmEJDBCSrOEFFV0Y1HXiwfLYJuLlA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.49.0.tgz", + "integrity": "sha512-Z81u+gfrobVK2iV7GqZCBfEB1y6+I61AH466lNK+xy1jfqFLiQ9Qv716WUM5fxFrYxwC7ziVdZRU9qvGHkYIJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.49.0.tgz", + "integrity": "sha512-zoAwS0KCXSnTp9NH/h9aamBAIve0DXeYpll85shf9NJ0URjSTzzS+Z9evmolN+ICfD3v8skKUPyk2PO0uGdFqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.49.0.tgz", + "integrity": "sha512-2QyUyQQ1ZtwZGiq0nvODL+vLJBtciItC3/5cYN8ncDQcv5avrt2MbKt1XU/vFAJlLta5KujqyHdYtdag4YEjYQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.49.0.tgz", + "integrity": "sha512-k9aEmOWt+mrMuD3skjVJSSxHckJp+SiFzFG+v8JLXbc/xi9hv2icSkR3U7uQzqy+/QbbYY7iNB9eDTwrELo14g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.49.0.tgz", + "integrity": "sha512-rDKRFFIWJ/zJn6uk2IdYLc09Z7zkE5IFIOWqpuU0o6ZpHcdniAyWkwSUWE/Z25N/wNDmFHHMzin84qW7Wzkjsw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.49.0.tgz", + "integrity": "sha512-FkkhIY/hYFVnOzz1WeV3S9Bd1h0hda/gRqvZCMpHWDHdiIHn6pqsY3b5eSbvGccWHMQ1uUzgZTKS4oGpykf8Tw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.49.0.tgz", + "integrity": "sha512-gRf5c+A7QiOG3UwLyOOtyJMD31JJhMjBvpfhAitPAoqZFcOeK3Kc1Veg1z/trmt+2P6F/biT02fU19GGTS529A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.49.0.tgz", + "integrity": "sha512-BR7+blScdLW1h/2hB/2oXM+dhTmpW3rQt1DeSiCP9mc2NMMkqVgjIN3DDsNpKmezffGC9R8XKVOLmBkRUcK/sA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.49.0.tgz", + "integrity": "sha512-hDMOAe+6nX3V5ei1I7Au3wcr9h3ktKzDvF2ne5ovX8RZiAHEtX1A5SNNk4zt1Qt77CmnbqT+upb/umzoPMWiPg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.49.0.tgz", + "integrity": "sha512-wkNRzfiIGaElC9kXUT+HLx17z7D0jl+9tGYRKwd8r7cUqTL7GYAvgUY++U2hK6Ar7z5Z6IRRoWC8kQxpmM7TDA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.49.0.tgz", + "integrity": "sha512-gq5aW/SyNpjp71AAzroH37DtINDcX1Qw2iv9Chyz49ZgdOP3NV8QCyKZUrGsYX9Yyggj5soFiRCgsL3HwD8TdA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.49.0.tgz", + "integrity": "sha512-gEtqFbzmZLFk2xKh7g0Rlo8xzho8KrEFEkzvHbfUGkrgXOpZ4XagQ6n+wIZFNh1nTb8UD16J4nFSFKXYgnbdBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@svgdotjs/svg.draggable.js": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@svgdotjs/svg.draggable.js/-/svg.draggable.js-3.0.6.tgz", + "integrity": "sha512-7iJFm9lL3C40HQcqzEfezK2l+dW2CpoVY3b77KQGqc8GXWa6LhhmX5Ckv7alQfUXBuZbjpICZ+Dvq1czlGx7gA==", + "license": "MIT", + "peerDependencies": { + "@svgdotjs/svg.js": "^3.2.4" + } + }, + "node_modules/@svgdotjs/svg.filter.js": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@svgdotjs/svg.filter.js/-/svg.filter.js-3.0.9.tgz", + "integrity": "sha512-/69XMRCDoam2HgC4ldHIaDgeQf1ViHIsa0Ld4uWgiXtZ+E24DWHe/9Ib6kbNiZ7WRIdlVokUDR1Fg0kjIpkfbw==", + "license": "MIT", + "dependencies": { + "@svgdotjs/svg.js": "^3.2.4" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@svgdotjs/svg.js": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@svgdotjs/svg.js/-/svg.js-3.2.5.tgz", + "integrity": "sha512-/VNHWYhNu+BS7ktbYoVGrCmsXDh+chFMaONMwGNdIBcFHrWqk2jY8fNyr3DLdtQUIalvkPfM554ZSFa3dm3nxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Fuzzyma" + } + }, + "node_modules/@svgdotjs/svg.resize.js": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@svgdotjs/svg.resize.js/-/svg.resize.js-2.0.5.tgz", + "integrity": "sha512-4heRW4B1QrJeENfi7326lUPYBCevj78FJs8kfeDxn5st0IYPIRXoTtOSYvTzFWgaWWXd3YCDE6ao4fmv91RthA==", + "license": "MIT", + "engines": { + "node": ">= 14.18" + }, + "peerDependencies": { + "@svgdotjs/svg.js": "^3.2.4", + "@svgdotjs/svg.select.js": "^4.0.1" + } + }, + "node_modules/@svgdotjs/svg.select.js": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@svgdotjs/svg.select.js/-/svg.select.js-4.0.3.tgz", + "integrity": "sha512-qkMgso1sd2hXKd1FZ1weO7ANq12sNmQJeGDjs46QwDVsxSRcHmvWKL2NDF7Yimpwf3sl5esOLkPqtV2bQ3v/Jg==", + "license": "MIT", + "engines": { + "node": ">= 14.18" + }, + "peerDependencies": { + "@svgdotjs/svg.js": "^3.2.4" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.12.tgz", + "integrity": "sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.5.1", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.12" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.12.tgz", + "integrity": "sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.12", + "@tailwindcss/oxide-darwin-arm64": "4.1.12", + "@tailwindcss/oxide-darwin-x64": "4.1.12", + "@tailwindcss/oxide-freebsd-x64": "4.1.12", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.12", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.12", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.12", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.12", + "@tailwindcss/oxide-linux-x64-musl": "4.1.12", + "@tailwindcss/oxide-wasm32-wasi": "4.1.12", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.12", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.12" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.12.tgz", + "integrity": "sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.12.tgz", + "integrity": "sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.12.tgz", + "integrity": "sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.12.tgz", + "integrity": "sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.12.tgz", + "integrity": "sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.12.tgz", + "integrity": "sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.12.tgz", + "integrity": "sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.12.tgz", + "integrity": "sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.12.tgz", + "integrity": "sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.12.tgz", + "integrity": "sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.5", + "@emnapi/runtime": "^1.4.5", + "@emnapi/wasi-threads": "^1.0.4", + "@napi-rs/wasm-runtime": "^0.2.12", + "@tybys/wasm-util": "^0.10.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.12.tgz", + "integrity": "sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.12.tgz", + "integrity": "sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.12.tgz", + "integrity": "sha512-4pt0AMFDx7gzIrAOIYgYP0KCBuKWqyW8ayrdiLEjoJTT4pKTjrzG/e4uzWtTLDziC+66R9wbUqZBccJalSE5vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.12", + "@tailwindcss/oxide": "4.1.12", + "tailwindcss": "4.1.12" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", + "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.1.5" + } + }, + "node_modules/@vue/shared": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", + "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==", + "license": "MIT" + }, + "node_modules/@yr/monotone-cubic-spline": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz", + "integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==", + "license": "MIT" + }, + "node_modules/alpinejs": { + "version": "3.14.9", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.14.9.tgz", + "integrity": "sha512-gqSOhTEyryU9FhviNqiHBHzgjkvtukq9tevew29fTj+ofZtfsYriw4zPirHHOAy9bw8QoL3WGhyk7QqCh5AYlw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "~3.1.1" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/apexcharts": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-5.3.5.tgz", + "integrity": "sha512-I04DY/WBZbJgJD2uixeV5EzyiL+J5LgKQXEu8rctqAwyRmKv44aDVeofJoLdTJe3ao4r2KEQfCgtVzXn6pqirg==", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "@svgdotjs/svg.draggable.js": "^3.0.4", + "@svgdotjs/svg.filter.js": "^3.0.8", + "@svgdotjs/svg.js": "^3.2.4", + "@svgdotjs/svg.resize.js": "^2.0.2", + "@svgdotjs/svg.select.js": "^4.0.1", + "@yr/monotone-cubic-spline": "^1.0.3" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concurrently": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/flatpickr": { + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz", + "integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==", + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/jsvectormap": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/jsvectormap/-/jsvectormap-1.7.0.tgz", + "integrity": "sha512-8VmL3Uuen08Es9xb2N6Wdc32TrQDGPXYCIdTB126jAhTJsYd/4r4Mc63VQA3qHxG0p4zeCI8sFO5XRsdjljMJg==", + "license": "MIT" + }, + "node_modules/laravel-vite-plugin": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-2.0.1.tgz", + "integrity": "sha512-zQuvzWfUKQu9oNVi1o0RZAJCwhGsdhx4NEOyrVQwJHaWDseGP9tl7XUPLY2T8Cj6+IrZ6lmyxlR1KC8unf3RLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "vite-plugin-full-reload": "^1.1.0" + }, + "bin": { + "clean-orphaned-assets": "bin/clean.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^7.0.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/magic-string": { + "version": "0.30.18", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/preact": { + "version": "10.12.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz", + "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.49.0.tgz", + "integrity": "sha512-3IVq0cGJ6H7fKXXEdVt+RcYvRCt8beYY9K1760wGQwSAHZcS9eot1zDG5axUbcp/kWRi5zKIIDX8MoKv/TzvZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.49.0", + "@rollup/rollup-android-arm64": "4.49.0", + "@rollup/rollup-darwin-arm64": "4.49.0", + "@rollup/rollup-darwin-x64": "4.49.0", + "@rollup/rollup-freebsd-arm64": "4.49.0", + "@rollup/rollup-freebsd-x64": "4.49.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.49.0", + "@rollup/rollup-linux-arm-musleabihf": "4.49.0", + "@rollup/rollup-linux-arm64-gnu": "4.49.0", + "@rollup/rollup-linux-arm64-musl": "4.49.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.49.0", + "@rollup/rollup-linux-ppc64-gnu": "4.49.0", + "@rollup/rollup-linux-riscv64-gnu": "4.49.0", + "@rollup/rollup-linux-riscv64-musl": "4.49.0", + "@rollup/rollup-linux-s390x-gnu": "4.49.0", + "@rollup/rollup-linux-x64-gnu": "4.49.0", + "@rollup/rollup-linux-x64-musl": "4.49.0", + "@rollup/rollup-win32-arm64-msvc": "4.49.0", + "@rollup/rollup-win32-ia32-msvc": "4.49.0", + "@rollup/rollup-win32-x64-msvc": "4.49.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/swiper": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-12.0.3.tgz", + "integrity": "sha512-BHd6U1VPEIksrXlyXjMmRWO0onmdNPaTAFduzqR3pgjvi7KfmUCAm/0cj49u2D7B0zNjMw02TSeXfinC1hDCXg==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/swiperjs" + }, + { + "type": "open_collective", + "url": "http://opencollective.com/swiper" + } + ], + "license": "MIT", + "engines": { + "node": ">= 4.7.0" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.12.tgz", + "integrity": "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/vite": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz", + "integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plugin-full-reload": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.2.0.tgz", + "integrity": "sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "picomatch": "^2.3.1" + } + }, + "node_modules/vite-plugin-full-reload/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..52c31ba --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://json.schemastore.org/package.json", + "private": true, + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.1.12", + "axios": "^1.11.0", + "concurrently": "^9.0.1", + "laravel-vite-plugin": "^2.0.0", + "tailwindcss": "^4.1.12", + "vite": "^7.0.4" + }, + "dependencies": { + "@floating-ui/dom": "^1.7.4", + "@fullcalendar/core": "^6.1.19", + "@fullcalendar/daygrid": "^6.1.19", + "@fullcalendar/interaction": "^6.1.19", + "@fullcalendar/list": "^6.1.19", + "@fullcalendar/timegrid": "^6.1.19", + "@popperjs/core": "^2.11.8", + "alpinejs": "^3.14.9", + "apexcharts": "^5.3.5", + "flatpickr": "^4.6.13", + "jsvectormap": "^1.7.0", + "prismjs": "^1.30.0", + "swiper": "^12.0.3" + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..5fd5bcf --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,34 @@ + + + + + tests/Unit + + + tests/Feature + + + + + app + + + + + + + + + + + + + + + + + diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..b574a59 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,25 @@ + + + Options -MultiViews -Indexes + + + RewriteEngine On + + # Handle Authorization Header + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # Handle X-XSRF-Token Header + RewriteCond %{HTTP:x-xsrf-token} . + RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}] + + # Redirect Trailing Slashes If Not A Folder... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} (.+)/$ + RewriteRule ^ %1 [L,R=301] + + # Send Requests To Front Controller... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + diff --git a/public/dashboard-apa-aja/ecommerce.blade.php b/public/dashboard-apa-aja/ecommerce.blade.php new file mode 100644 index 0000000..d429fbf --- /dev/null +++ b/public/dashboard-apa-aja/ecommerce.blade.php @@ -0,0 +1,25 @@ +@extends('layouts.app') + +@section('content') +
+
+ + +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+@endsection diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..082320a Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/images/ai/img-lg.png b/public/images/ai/img-lg.png new file mode 100644 index 0000000..640ae41 Binary files /dev/null and b/public/images/ai/img-lg.png differ diff --git a/public/images/ai/img-md-1.png b/public/images/ai/img-md-1.png new file mode 100644 index 0000000..2982d78 Binary files /dev/null and b/public/images/ai/img-md-1.png differ diff --git a/public/images/ai/img-md-2.png b/public/images/ai/img-md-2.png new file mode 100644 index 0000000..e5cd3e2 Binary files /dev/null and b/public/images/ai/img-md-2.png differ diff --git a/public/images/ai/video-thumb.png b/public/images/ai/video-thumb.png new file mode 100644 index 0000000..b8b63ef Binary files /dev/null and b/public/images/ai/video-thumb.png differ diff --git a/public/images/brand/brand-01.svg b/public/images/brand/brand-01.svg new file mode 100644 index 0000000..7321fbf --- /dev/null +++ b/public/images/brand/brand-01.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/brand/brand-02.svg b/public/images/brand/brand-02.svg new file mode 100644 index 0000000..14da422 --- /dev/null +++ b/public/images/brand/brand-02.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/images/brand/brand-03.svg b/public/images/brand/brand-03.svg new file mode 100644 index 0000000..8d29afa --- /dev/null +++ b/public/images/brand/brand-03.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/brand/brand-04.svg b/public/images/brand/brand-04.svg new file mode 100644 index 0000000..837a4d4 --- /dev/null +++ b/public/images/brand/brand-04.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/brand/brand-05.svg b/public/images/brand/brand-05.svg new file mode 100644 index 0000000..7044f46 --- /dev/null +++ b/public/images/brand/brand-05.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/brand/brand-06.svg b/public/images/brand/brand-06.svg new file mode 100644 index 0000000..78c5d01 --- /dev/null +++ b/public/images/brand/brand-06.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/brand/brand-07.svg b/public/images/brand/brand-07.svg new file mode 100644 index 0000000..5abb368 --- /dev/null +++ b/public/images/brand/brand-07.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/brand/brand-08.svg b/public/images/brand/brand-08.svg new file mode 100644 index 0000000..71bc1e2 --- /dev/null +++ b/public/images/brand/brand-08.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/brand/brand-09.svg b/public/images/brand/brand-09.svg new file mode 100644 index 0000000..1330ba2 --- /dev/null +++ b/public/images/brand/brand-09.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/brand/brand-10.svg b/public/images/brand/brand-10.svg new file mode 100644 index 0000000..60308dd --- /dev/null +++ b/public/images/brand/brand-10.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/brand/brand-11.svg b/public/images/brand/brand-11.svg new file mode 100644 index 0000000..b316bb4 --- /dev/null +++ b/public/images/brand/brand-11.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/brand/brand-12.svg b/public/images/brand/brand-12.svg new file mode 100644 index 0000000..8396a56 --- /dev/null +++ b/public/images/brand/brand-12.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/brand/brand-13.svg b/public/images/brand/brand-13.svg new file mode 100644 index 0000000..dd53f79 --- /dev/null +++ b/public/images/brand/brand-13.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/brand/brand-14.svg b/public/images/brand/brand-14.svg new file mode 100644 index 0000000..381d72d --- /dev/null +++ b/public/images/brand/brand-14.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/brand/brand-15.svg b/public/images/brand/brand-15.svg new file mode 100644 index 0000000..dfde3dd --- /dev/null +++ b/public/images/brand/brand-15.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/cards/card-01.jpg b/public/images/cards/card-01.jpg new file mode 100644 index 0000000..6ca5220 Binary files /dev/null and b/public/images/cards/card-01.jpg differ diff --git a/public/images/cards/card-01.png b/public/images/cards/card-01.png new file mode 100644 index 0000000..50136d2 Binary files /dev/null and b/public/images/cards/card-01.png differ diff --git a/public/images/cards/card-02.jpg b/public/images/cards/card-02.jpg new file mode 100644 index 0000000..4529105 Binary files /dev/null and b/public/images/cards/card-02.jpg differ diff --git a/public/images/cards/card-02.png b/public/images/cards/card-02.png new file mode 100644 index 0000000..29e3c40 Binary files /dev/null and b/public/images/cards/card-02.png differ diff --git a/public/images/cards/card-03.jpg b/public/images/cards/card-03.jpg new file mode 100644 index 0000000..dea4663 Binary files /dev/null and b/public/images/cards/card-03.jpg differ diff --git a/public/images/cards/card-03.png b/public/images/cards/card-03.png new file mode 100644 index 0000000..b8c8ed0 Binary files /dev/null and b/public/images/cards/card-03.png differ diff --git a/public/images/carousel/carousel-01.png b/public/images/carousel/carousel-01.png new file mode 100644 index 0000000..0c738c8 Binary files /dev/null and b/public/images/carousel/carousel-01.png differ diff --git a/public/images/carousel/carousel-02.png b/public/images/carousel/carousel-02.png new file mode 100644 index 0000000..963ca5f Binary files /dev/null and b/public/images/carousel/carousel-02.png differ diff --git a/public/images/carousel/carousel-03.png b/public/images/carousel/carousel-03.png new file mode 100644 index 0000000..d744261 Binary files /dev/null and b/public/images/carousel/carousel-03.png differ diff --git a/public/images/carousel/carousel-04.png b/public/images/carousel/carousel-04.png new file mode 100644 index 0000000..58d393d Binary files /dev/null and b/public/images/carousel/carousel-04.png differ diff --git a/public/images/chat/chat.jpg b/public/images/chat/chat.jpg new file mode 100644 index 0000000..7ad98e5 Binary files /dev/null and b/public/images/chat/chat.jpg differ diff --git a/public/images/country/country-01.svg b/public/images/country/country-01.svg new file mode 100644 index 0000000..4c14b12 --- /dev/null +++ b/public/images/country/country-01.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/images/country/country-02.svg b/public/images/country/country-02.svg new file mode 100644 index 0000000..52f57c7 --- /dev/null +++ b/public/images/country/country-02.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/country/country-03.svg b/public/images/country/country-03.svg new file mode 100644 index 0000000..e435fab --- /dev/null +++ b/public/images/country/country-03.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/public/images/country/country-04.svg b/public/images/country/country-04.svg new file mode 100644 index 0000000..93b49b6 --- /dev/null +++ b/public/images/country/country-04.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/country/country-05.svg b/public/images/country/country-05.svg new file mode 100644 index 0000000..5aa26b5 --- /dev/null +++ b/public/images/country/country-05.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/country/country-06.svg b/public/images/country/country-06.svg new file mode 100644 index 0000000..730e2e6 --- /dev/null +++ b/public/images/country/country-06.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/country/country-07.svg b/public/images/country/country-07.svg new file mode 100644 index 0000000..ce770d4 --- /dev/null +++ b/public/images/country/country-07.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/country/country-08.svg b/public/images/country/country-08.svg new file mode 100644 index 0000000..c652b95 --- /dev/null +++ b/public/images/country/country-08.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/error/404-dark.svg b/public/images/error/404-dark.svg new file mode 100644 index 0000000..4d14ec9 --- /dev/null +++ b/public/images/error/404-dark.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/error/404.svg b/public/images/error/404.svg new file mode 100644 index 0000000..ff8b8a2 --- /dev/null +++ b/public/images/error/404.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/error/500-dark.svg b/public/images/error/500-dark.svg new file mode 100644 index 0000000..c5ac764 --- /dev/null +++ b/public/images/error/500-dark.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/error/500.svg b/public/images/error/500.svg new file mode 100644 index 0000000..82f5159 --- /dev/null +++ b/public/images/error/500.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/error/503-dark.svg b/public/images/error/503-dark.svg new file mode 100644 index 0000000..8df2a94 --- /dev/null +++ b/public/images/error/503-dark.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/error/503.svg b/public/images/error/503.svg new file mode 100644 index 0000000..a27a714 --- /dev/null +++ b/public/images/error/503.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/error/maintenance-dark.svg b/public/images/error/maintenance-dark.svg new file mode 100644 index 0000000..e2a4499 --- /dev/null +++ b/public/images/error/maintenance-dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/error/maintenance.svg b/public/images/error/maintenance.svg new file mode 100644 index 0000000..859d817 --- /dev/null +++ b/public/images/error/maintenance.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/error/success-dark.svg b/public/images/error/success-dark.svg new file mode 100644 index 0000000..b57643f --- /dev/null +++ b/public/images/error/success-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/error/success.svg b/public/images/error/success.svg new file mode 100644 index 0000000..91e1a76 --- /dev/null +++ b/public/images/error/success.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/grid-image/image-01.png b/public/images/grid-image/image-01.png new file mode 100644 index 0000000..b00223b Binary files /dev/null and b/public/images/grid-image/image-01.png differ diff --git a/public/images/grid-image/image-02.png b/public/images/grid-image/image-02.png new file mode 100644 index 0000000..e1e9fb2 Binary files /dev/null and b/public/images/grid-image/image-02.png differ diff --git a/public/images/grid-image/image-03.png b/public/images/grid-image/image-03.png new file mode 100644 index 0000000..9fe184a Binary files /dev/null and b/public/images/grid-image/image-03.png differ diff --git a/public/images/grid-image/image-04.png b/public/images/grid-image/image-04.png new file mode 100644 index 0000000..0020a65 Binary files /dev/null and b/public/images/grid-image/image-04.png differ diff --git a/public/images/grid-image/image-05.png b/public/images/grid-image/image-05.png new file mode 100644 index 0000000..9ee8797 Binary files /dev/null and b/public/images/grid-image/image-05.png differ diff --git a/public/images/grid-image/image-06.png b/public/images/grid-image/image-06.png new file mode 100644 index 0000000..d7e3668 Binary files /dev/null and b/public/images/grid-image/image-06.png differ diff --git a/public/images/icons/file-image-dark.svg b/public/images/icons/file-image-dark.svg new file mode 100644 index 0000000..ff2d6df --- /dev/null +++ b/public/images/icons/file-image-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/icons/file-image.svg b/public/images/icons/file-image.svg new file mode 100644 index 0000000..0303d63 --- /dev/null +++ b/public/images/icons/file-image.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/icons/file-pdf-dark.svg b/public/images/icons/file-pdf-dark.svg new file mode 100644 index 0000000..8fc5a47 --- /dev/null +++ b/public/images/icons/file-pdf-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/icons/file-pdf.svg b/public/images/icons/file-pdf.svg new file mode 100644 index 0000000..a525a26 --- /dev/null +++ b/public/images/icons/file-pdf.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/icons/file-video-dark.svg b/public/images/icons/file-video-dark.svg new file mode 100644 index 0000000..9415c3d --- /dev/null +++ b/public/images/icons/file-video-dark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/icons/file-video.svg b/public/images/icons/file-video.svg new file mode 100644 index 0000000..49732ca --- /dev/null +++ b/public/images/icons/file-video.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/logistics/avatar-1.png b/public/images/logistics/avatar-1.png new file mode 100644 index 0000000..367dc95 Binary files /dev/null and b/public/images/logistics/avatar-1.png differ diff --git a/public/images/logistics/truck.png b/public/images/logistics/truck.png new file mode 100644 index 0000000..d674eb7 Binary files /dev/null and b/public/images/logistics/truck.png differ diff --git a/public/images/logo/auth-logo.svg b/public/images/logo/auth-logo.svg new file mode 100644 index 0000000..eb11cc7 --- /dev/null +++ b/public/images/logo/auth-logo.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/logo/logo-dark.svg b/public/images/logo/logo-dark.svg new file mode 100644 index 0000000..4b94dac --- /dev/null +++ b/public/images/logo/logo-dark.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/logo/logo-icon.svg b/public/images/logo/logo-icon.svg new file mode 100644 index 0000000..11d52ca --- /dev/null +++ b/public/images/logo/logo-icon.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/logo/logo.svg b/public/images/logo/logo.svg new file mode 100644 index 0000000..758dedd --- /dev/null +++ b/public/images/logo/logo.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/product/product-01.jpg b/public/images/product/product-01.jpg new file mode 100644 index 0000000..7ffb1e6 Binary files /dev/null and b/public/images/product/product-01.jpg differ diff --git a/public/images/product/product-02.jpg b/public/images/product/product-02.jpg new file mode 100644 index 0000000..db30a9a Binary files /dev/null and b/public/images/product/product-02.jpg differ diff --git a/public/images/product/product-03.jpg b/public/images/product/product-03.jpg new file mode 100644 index 0000000..95fd8d4 Binary files /dev/null and b/public/images/product/product-03.jpg differ diff --git a/public/images/product/product-04.jpg b/public/images/product/product-04.jpg new file mode 100644 index 0000000..131a9f5 Binary files /dev/null and b/public/images/product/product-04.jpg differ diff --git a/public/images/product/product-05.jpg b/public/images/product/product-05.jpg new file mode 100644 index 0000000..1ad17a9 Binary files /dev/null and b/public/images/product/product-05.jpg differ diff --git a/public/images/shape/grid-01.svg b/public/images/shape/grid-01.svg new file mode 100644 index 0000000..6490367 --- /dev/null +++ b/public/images/shape/grid-01.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/support/user-1.jpg b/public/images/support/user-1.jpg new file mode 100644 index 0000000..5d50135 Binary files /dev/null and b/public/images/support/user-1.jpg differ diff --git a/public/images/support/user-2.jpg b/public/images/support/user-2.jpg new file mode 100644 index 0000000..baf5105 Binary files /dev/null and b/public/images/support/user-2.jpg differ diff --git a/public/images/task/google-drive.svg b/public/images/task/google-drive.svg new file mode 100644 index 0000000..2bdb033 --- /dev/null +++ b/public/images/task/google-drive.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/task/pdf.svg b/public/images/task/pdf.svg new file mode 100644 index 0000000..4390c8d --- /dev/null +++ b/public/images/task/pdf.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/images/task/task.jpg b/public/images/task/task.jpg new file mode 100644 index 0000000..5e6add1 Binary files /dev/null and b/public/images/task/task.jpg differ diff --git a/public/images/task/task.png b/public/images/task/task.png new file mode 100644 index 0000000..0031cdb Binary files /dev/null and b/public/images/task/task.png differ diff --git a/public/images/user/owner.jpg b/public/images/user/owner.jpg new file mode 100644 index 0000000..55985fe Binary files /dev/null and b/public/images/user/owner.jpg differ diff --git a/public/images/user/owner.png b/public/images/user/owner.png new file mode 100644 index 0000000..810cf07 Binary files /dev/null and b/public/images/user/owner.png differ diff --git a/public/images/user/owner.webp b/public/images/user/owner.webp new file mode 100644 index 0000000..b5c7d44 Binary files /dev/null and b/public/images/user/owner.webp differ diff --git a/public/images/user/user-01.jpg b/public/images/user/user-01.jpg new file mode 100644 index 0000000..4877840 Binary files /dev/null and b/public/images/user/user-01.jpg differ diff --git a/public/images/user/user-02.jpg b/public/images/user/user-02.jpg new file mode 100644 index 0000000..fe64d3e Binary files /dev/null and b/public/images/user/user-02.jpg differ diff --git a/public/images/user/user-03.jpg b/public/images/user/user-03.jpg new file mode 100644 index 0000000..7a8647a Binary files /dev/null and b/public/images/user/user-03.jpg differ diff --git a/public/images/user/user-04.jpg b/public/images/user/user-04.jpg new file mode 100644 index 0000000..0522414 Binary files /dev/null and b/public/images/user/user-04.jpg differ diff --git a/public/images/user/user-05.jpg b/public/images/user/user-05.jpg new file mode 100644 index 0000000..c0b7ddb Binary files /dev/null and b/public/images/user/user-05.jpg differ diff --git a/public/images/user/user-06.jpg b/public/images/user/user-06.jpg new file mode 100644 index 0000000..d80d37e Binary files /dev/null and b/public/images/user/user-06.jpg differ diff --git a/public/images/user/user-07.jpg b/public/images/user/user-07.jpg new file mode 100644 index 0000000..7e8f58d Binary files /dev/null and b/public/images/user/user-07.jpg differ diff --git a/public/images/user/user-08.jpg b/public/images/user/user-08.jpg new file mode 100644 index 0000000..86d14ef Binary files /dev/null and b/public/images/user/user-08.jpg differ diff --git a/public/images/user/user-09.jpg b/public/images/user/user-09.jpg new file mode 100644 index 0000000..35942ed Binary files /dev/null and b/public/images/user/user-09.jpg differ diff --git a/public/images/user/user-10.jpg b/public/images/user/user-10.jpg new file mode 100644 index 0000000..e330208 Binary files /dev/null and b/public/images/user/user-10.jpg differ diff --git a/public/images/user/user-11.jpg b/public/images/user/user-11.jpg new file mode 100644 index 0000000..7aa2dd9 Binary files /dev/null and b/public/images/user/user-11.jpg differ diff --git a/public/images/user/user-12.jpg b/public/images/user/user-12.jpg new file mode 100644 index 0000000..2b9065c Binary files /dev/null and b/public/images/user/user-12.jpg differ diff --git a/public/images/user/user-13.jpg b/public/images/user/user-13.jpg new file mode 100644 index 0000000..7657777 Binary files /dev/null and b/public/images/user/user-13.jpg differ diff --git a/public/images/user/user-14.jpg b/public/images/user/user-14.jpg new file mode 100644 index 0000000..28ef7a9 Binary files /dev/null and b/public/images/user/user-14.jpg differ diff --git a/public/images/user/user-15.jpg b/public/images/user/user-15.jpg new file mode 100644 index 0000000..e39fb30 Binary files /dev/null and b/public/images/user/user-15.jpg differ diff --git a/public/images/user/user-16.jpg b/public/images/user/user-16.jpg new file mode 100644 index 0000000..f23e96c Binary files /dev/null and b/public/images/user/user-16.jpg differ diff --git a/public/images/user/user-17.jpg b/public/images/user/user-17.jpg new file mode 100644 index 0000000..4effac3 Binary files /dev/null and b/public/images/user/user-17.jpg differ diff --git a/public/images/user/user-18.jpg b/public/images/user/user-18.jpg new file mode 100644 index 0000000..4660835 Binary files /dev/null and b/public/images/user/user-18.jpg differ diff --git a/public/images/user/user-19.jpg b/public/images/user/user-19.jpg new file mode 100644 index 0000000..b8d2ae9 Binary files /dev/null and b/public/images/user/user-19.jpg differ diff --git a/public/images/user/user-20.jpg b/public/images/user/user-20.jpg new file mode 100644 index 0000000..6acae07 Binary files /dev/null and b/public/images/user/user-20.jpg differ diff --git a/public/images/user/user-21.jpg b/public/images/user/user-21.jpg new file mode 100644 index 0000000..d86084c Binary files /dev/null and b/public/images/user/user-21.jpg differ diff --git a/public/images/user/user-22.jpg b/public/images/user/user-22.jpg new file mode 100644 index 0000000..9cf7e13 Binary files /dev/null and b/public/images/user/user-22.jpg differ diff --git a/public/images/user/user-23.jpg b/public/images/user/user-23.jpg new file mode 100644 index 0000000..b79ffec Binary files /dev/null and b/public/images/user/user-23.jpg differ diff --git a/public/images/user/user-24.jpg b/public/images/user/user-24.jpg new file mode 100644 index 0000000..ee09128 Binary files /dev/null and b/public/images/user/user-24.jpg differ diff --git a/public/images/user/user-25.jpg b/public/images/user/user-25.jpg new file mode 100644 index 0000000..e930892 Binary files /dev/null and b/public/images/user/user-25.jpg differ diff --git a/public/images/user/user-26.jpg b/public/images/user/user-26.jpg new file mode 100644 index 0000000..c4934b4 Binary files /dev/null and b/public/images/user/user-26.jpg differ diff --git a/public/images/user/user-27.jpg b/public/images/user/user-27.jpg new file mode 100644 index 0000000..2a08ebe Binary files /dev/null and b/public/images/user/user-27.jpg differ diff --git a/public/images/user/user-28.jpg b/public/images/user/user-28.jpg new file mode 100644 index 0000000..82cf2d1 Binary files /dev/null and b/public/images/user/user-28.jpg differ diff --git a/public/images/user/user-29.jpg b/public/images/user/user-29.jpg new file mode 100644 index 0000000..1a1fa49 Binary files /dev/null and b/public/images/user/user-29.jpg differ diff --git a/public/images/user/user-30.jpg b/public/images/user/user-30.jpg new file mode 100644 index 0000000..512e8dd Binary files /dev/null and b/public/images/user/user-30.jpg differ diff --git a/public/images/user/user-31.jpg b/public/images/user/user-31.jpg new file mode 100644 index 0000000..0144b84 Binary files /dev/null and b/public/images/user/user-31.jpg differ diff --git a/public/images/user/user-32.jpg b/public/images/user/user-32.jpg new file mode 100644 index 0000000..712e336 Binary files /dev/null and b/public/images/user/user-32.jpg differ diff --git a/public/images/user/user-33.jpg b/public/images/user/user-33.jpg new file mode 100644 index 0000000..1f7796b Binary files /dev/null and b/public/images/user/user-33.jpg differ diff --git a/public/images/user/user-34.jpg b/public/images/user/user-34.jpg new file mode 100644 index 0000000..40202f6 Binary files /dev/null and b/public/images/user/user-34.jpg differ diff --git a/public/images/user/user-35.jpg b/public/images/user/user-35.jpg new file mode 100644 index 0000000..b78921a Binary files /dev/null and b/public/images/user/user-35.jpg differ diff --git a/public/images/user/user-36.jpg b/public/images/user/user-36.jpg new file mode 100644 index 0000000..a023f2f Binary files /dev/null and b/public/images/user/user-36.jpg differ diff --git a/public/images/user/user-37.jpg b/public/images/user/user-37.jpg new file mode 100644 index 0000000..6d3186e Binary files /dev/null and b/public/images/user/user-37.jpg differ diff --git a/public/images/video-thumb/thumb-16.png b/public/images/video-thumb/thumb-16.png new file mode 100644 index 0000000..1f12017 Binary files /dev/null and b/public/images/video-thumb/thumb-16.png differ diff --git a/public/images/video-thumb/youtube-icon-84.svg b/public/images/video-thumb/youtube-icon-84.svg new file mode 100644 index 0000000..5a9478a --- /dev/null +++ b/public/images/video-thumb/youtube-icon-84.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..ee8f07e --- /dev/null +++ b/public/index.php @@ -0,0 +1,20 @@ +handleRequest(Request::capture()); diff --git a/public/key-gen.html b/public/key-gen.html new file mode 100644 index 0000000..eaa350e --- /dev/null +++ b/public/key-gen.html @@ -0,0 +1,131 @@ + + + + + + Laravel Key Generator | DyDev Admin + + + + + + +
+ +
+ + + + + + + + +
+ + +
+
+

+ Key Generator +

+ +
+ +

+ Generate a secure 32-byte APP_KEY for your .env file. +

+ +
+
+
+ +
+ + +
+ +
+

How to use:

+
    +
  1. Copy the generated key above.
  2. +
  3. Open your .env file on your server.
  4. +
  5. Find APP_KEY= and paste the key.
  6. +
  7. Save the file.
  8. +
+
+
+ +

+ © 2025 - DyDev TrustLab +

+
+ + diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..eb05362 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/resources/css/app.css b/resources/css/app.css new file mode 100644 index 0000000..e7b4ef9 --- /dev/null +++ b/resources/css/app.css @@ -0,0 +1,876 @@ +@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap') layer(base); +@import 'prismjs/themes/prism.min.css'; +@import 'tailwindcss'; + + +@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php'; +@source '../../storage/framework/views/*.php'; +@source '../**/*.blade.php'; +@source '../**/*.js'; + + +@custom-variant dark (&:is(.dark *)); + +@theme { + --font-*: initial; + --font-outfit: Outfit, sans-serif; + + --breakpoint-*: initial; + --breakpoint-2xsm: 375px; + --breakpoint-xsm: 425px; + --breakpoint-3xl: 2000px; + --breakpoint-sm: 640px; + --breakpoint-md: 768px; + --breakpoint-lg: 1024px; + --breakpoint-xl: 1280px; + --breakpoint-2xl: 1536px; + + --text-title-2xl: 72px; + --text-title-2xl--line-height: 90px; + --text-title-xl: 60px; + --text-title-xl--line-height: 72px; + --text-title-lg: 48px; + --text-title-lg--line-height: 60px; + --text-title-md: 36px; + --text-title-md--line-height: 44px; + --text-title-sm: 30px; + --text-title-sm--line-height: 38px; + --text-theme-xl: 20px; + --text-theme-xl--line-height: 30px; + --text-theme-sm: 14px; + --text-theme-sm--line-height: 20px; + --text-theme-xs: 12px; + --text-theme-xs--line-height: 18px; + + --color-current: currentColor; + --color-transparent: transparent; + --color-white: #ffffff; + --color-black: #101828; + + --color-brand-25: #f2f7ff; + --color-brand-50: #ecf3ff; + --color-brand-100: #dde9ff; + --color-brand-200: #c2d6ff; + --color-brand-300: #9cb9ff; + --color-brand-400: #7592ff; + --color-brand-500: #465fff; + --color-brand-600: #3641f5; + --color-brand-700: #2a31d8; + --color-brand-800: #252dae; + --color-brand-900: #262e89; + --color-brand-950: #161950; + + --color-blue-light-25: #f5fbff; + --color-blue-light-50: #f0f9ff; + --color-blue-light-100: #e0f2fe; + --color-blue-light-200: #b9e6fe; + --color-blue-light-300: #7cd4fd; + --color-blue-light-400: #36bffa; + --color-blue-light-500: #0ba5ec; + --color-blue-light-600: #0086c9; + --color-blue-light-700: #026aa2; + --color-blue-light-800: #065986; + --color-blue-light-900: #0b4a6f; + --color-blue-light-950: #062c41; + + --color-gray-25: #fcfcfd; + --color-gray-50: #f9fafb; + --color-gray-100: #f2f4f7; + --color-gray-200: #e4e7ec; + --color-gray-300: #d0d5dd; + --color-gray-400: #98a2b3; + --color-gray-500: #667085; + --color-gray-600: #475467; + --color-gray-700: #344054; + --color-gray-800: #1d2939; + --color-gray-900: #101828; + --color-gray-950: #0c111d; + --color-gray-dark: #1a2231; + + --color-orange-25: #fffaf5; + --color-orange-50: #fff6ed; + --color-orange-100: #ffead5; + --color-orange-200: #fddcab; + --color-orange-300: #feb273; + --color-orange-400: #fd853a; + --color-orange-500: #fb6514; + --color-orange-600: #ec4a0a; + --color-orange-700: #c4320a; + --color-orange-800: #9c2a10; + --color-orange-900: #7e2410; + --color-orange-950: #511c10; + + --color-success-25: #f6fef9; + --color-success-50: #ecfdf3; + --color-success-100: #d1fadf; + --color-success-200: #a6f4c5; + --color-success-300: #6ce9a6; + --color-success-400: #32d583; + --color-success-500: #12b76a; + --color-success-600: #039855; + --color-success-700: #027a48; + --color-success-800: #05603a; + --color-success-900: #054f31; + --color-success-950: #053321; + + --color-error-25: #fffbfa; + --color-error-50: #fef3f2; + --color-error-100: #fee4e2; + --color-error-200: #fecdca; + --color-error-300: #fda29b; + --color-error-400: #f97066; + --color-error-500: #f04438; + --color-error-600: #d92d20; + --color-error-700: #b42318; + --color-error-800: #912018; + --color-error-900: #7a271a; + --color-error-950: #55160c; + + --color-warning-25: #fffcf5; + --color-warning-50: #fffaeb; + --color-warning-100: #fef0c7; + --color-warning-200: #fedf89; + --color-warning-300: #fec84b; + --color-warning-400: #fdb022; + --color-warning-500: #f79009; + --color-warning-600: #dc6803; + --color-warning-700: #b54708; + --color-warning-800: #93370d; + --color-warning-900: #7a2e0e; + --color-warning-950: #4e1d09; + + --color-theme-pink-500: #ee46bc; + + --color-theme-purple-500: #7a5af8; + + --shadow-theme-md: 0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06); + --shadow-theme-lg: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), + 0px 4px 6px -2px rgba(16, 24, 40, 0.03); + --shadow-theme-sm: 0px 1px 3px 0px rgba(16, 24, 40, 0.1), 0px 1px 2px 0px rgba(16, 24, 40, 0.06); + --shadow-theme-xs: 0px 1px 2px 0px rgba(16, 24, 40, 0.05); + --shadow-theme-xl: 0px 20px 24px -4px rgba(16, 24, 40, 0.08), + 0px 8px 8px -4px rgba(16, 24, 40, 0.03); + --shadow-datepicker: -5px 0 0 #262d3c, 5px 0 0 #262d3c; + --shadow-focus-ring: 0px 0px 0px 4px rgba(70, 95, 255, 0.12); + --shadow-slider-navigation: 0px 1px 2px 0px rgba(16, 24, 40, 0.1), + 0px 1px 3px 0px rgba(16, 24, 40, 0.1); + --shadow-tooltip: 0px 4px 6px -2px rgba(16, 24, 40, 0.05), + -8px 0px 20px 8px rgba(16, 24, 40, 0.05); + + --drop-shadow-4xl: 0 35px 35px rgba(0, 0, 0, 0.25), 0 45px 65px rgba(0, 0, 0, 0.15); + + --z-index-1: 1; + --z-index-9: 9; + --z-index-99: 99; + --z-index-999: 999; + --z-index-9999: 9999; + --z-index-99999: 99999; + --z-index-999999: 999999; +} + +/* + The default border color has changed to `currentColor` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. +*/ +@layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } + button:not(:disabled), + [role='button']:not(:disabled) { + cursor: pointer; + } + body { + @apply relative font-normal font-outfit z-1 bg-gray-50; + } +} + +@utility menu-item { + @apply relative flex items-center w-full gap-3 px-3 py-2 font-medium rounded-lg text-theme-sm; +} + +@utility menu-item-active { + @apply bg-brand-50 text-brand-500 dark:bg-brand-500/[0.12] dark:text-brand-400; +} + +@utility menu-item-inactive { + @apply text-gray-700 hover:bg-gray-100 group-hover:text-gray-700 dark:text-gray-300 dark:hover:bg-white/5 dark:hover:text-gray-300; +} + +@utility menu-item-icon { + @apply text-gray-500 group-hover:text-gray-700 dark:text-gray-400; +} + +@utility menu-item-icon-active { + @apply text-brand-500 dark:text-brand-400; +} + +@utility menu-item-icon-inactive { + @apply text-gray-500 group-hover:text-gray-700 dark:text-gray-400 dark:group-hover:text-gray-300; +} + +@utility menu-item-arrow { + @apply relative; +} + +@utility menu-item-arrow-active { + @apply rotate-180 text-brand-500 dark:text-brand-400; +} + +@utility menu-item-arrow-inactive { + @apply text-gray-500 group-hover:text-gray-700 dark:text-gray-400 dark:group-hover:text-gray-300; +} + +@utility menu-dropdown-item { + @apply relative flex items-center gap-3 rounded-lg px-3 py-2.5 text-theme-sm font-medium; +} + +@utility menu-dropdown-item-active { + @apply bg-brand-50 text-brand-500 dark:bg-brand-500/[0.12] dark:text-brand-400; +} + +@utility menu-dropdown-item-inactive { + @apply text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-white/5; +} + +@utility menu-dropdown-item { + @apply text-theme-sm relative flex items-center gap-3 rounded-lg px-3 py-2.5 font-medium; +} + +@utility menu-dropdown-item-active { + @apply bg-brand-50 text-brand-500 dark:bg-brand-500/[0.12] dark:text-brand-400; +} + +@utility menu-dropdown-item-inactive { + @apply text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-white/5; +} + +@utility menu-dropdown-badge { + @apply text-success-600 dark:text-success-500 block rounded-full px-2.5 py-0.5 text-xs font-medium uppercase; +} + +@utility menu-dropdown-badge-active { + @apply bg-success-100 dark:bg-success-500/20; +} + +@utility menu-dropdown-badge-inactive { + @apply bg-success-50 group-hover:bg-success-100 dark:bg-success-500/15 dark:group-hover:bg-success-500/20; +} + +@utility menu-dropdown-badge-pro { + @apply text-brand-600 dark:text-brand-500 block rounded-full px-2.5 py-0.5 text-xs font-medium uppercase; +} + +@utility menu-dropdown-badge-pro-active { + @apply bg-brand-100 dark:bg-brand-500/20; +} + +@utility menu-dropdown-badge-pro-inactive { + @apply bg-brand-50 group-hover:bg-brand-100 dark:bg-brand-500/15 dark:group-hover:bg-brand-500/20; +} + +@utility no-scrollbar { + /* Chrome, Safari and Opera */ + &::-webkit-scrollbar { + display: none; + } + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + +@utility custom-scrollbar { + &::-webkit-scrollbar { + @apply size-1.5; + } + + &::-webkit-scrollbar-track { + @apply rounded-full; + } + + &::-webkit-scrollbar-thumb { + @apply bg-gray-200 rounded-full dark:bg-gray-700; + } +} +.dark .custom-scrollbar::-webkit-scrollbar-thumb { + background-color: #344054; +} +@layer utilities { + /* For Remove Date Icon */ + input[type='date']::-webkit-inner-spin-button, + input[type='time']::-webkit-inner-spin-button, + input[type='date']::-webkit-calendar-picker-indicator, + input[type='time']::-webkit-calendar-picker-indicator { + display: none; + -webkit-appearance: none; + } +} + +.sidebar:hover { + width: 290px; +} +.sidebar:hover .logo { + display: block; +} +.sidebar:hover .logo-icon { + display: none; +} +.sidebar:hover .sidebar-header { + justify-content: space-between; +} +.sidebar:hover .menu-group-title { + display: block; +} +.sidebar:hover .menu-group-icon { + display: none; +} + +.sidebar:hover .menu-item-text { + display: inline; +} + +.sidebar:hover .menu-item-arrow { + display: block; +} + +.sidebar:hover .menu-dropdown { + display: flex; +} + +.tableCheckbox:checked ~ span span { + @apply opacity-100; +} +.tableCheckbox:checked ~ span { + @apply border-brand-500 bg-brand-500; +} + +/* third-party libraries CSS */ +.apexcharts-legend-text { + @apply !text-gray-700 dark:!text-gray-400; +} + +.apexcharts-text { + @apply !fill-gray-700 dark:!fill-gray-400; +} + +.apexcharts-tooltip.apexcharts-theme-light { + @apply gap-1 !rounded-lg !border-gray-200 p-3 !shadow-theme-sm dark:!border-gray-800 dark:!bg-gray-900; +} + +/* .apexcharts-tooltip-marker { + @apply !mr-1.5 !h-1.5 !w-1.5; +} */ +.apexcharts-legend-text { + @apply !pl-5 !text-gray-700 dark:!text-gray-400; +} +.apexcharts-tooltip-series-group { + @apply !p-0; +} +.apexcharts-tooltip-y-group { + @apply !p-0; +} +.apexcharts-tooltip-title { + @apply !mb-0 !border-b-0 !bg-transparent !p-0 !text-[10px] !leading-4 !text-gray-800 dark:!text-white/90; +} +.apexcharts-tooltip-text { + @apply !text-theme-xs !text-gray-700 dark:!text-white/90; +} +.apexcharts-tooltip-text-y-value { + @apply !font-medium; +} + +.apexcharts-gridline { + @apply !stroke-gray-100 dark:!stroke-gray-800; +} +#chartTwo .apexcharts-datalabels-group { + @apply !-translate-y-24; +} + +#chartSeven .apexcharts-datalabels-group .apexcharts-text, +#chartTwo .apexcharts-datalabels-group .apexcharts-text, +#chartThirteen .apexcharts-datalabels-group .apexcharts-text, +#chartTwelve .apexcharts-datalabels-group .apexcharts-text { + @apply !fill-gray-800 !font-semibold dark:!fill-white/90; +} + +#chartSixteen .apexcharts-legend { + @apply !p-0 !pl-6; +} + +.jvm-container { + @apply !bg-gray-50 dark:!bg-gray-900; +} +.jvm-region.jvm-element { + @apply !fill-gray-300 hover:!fill-brand-500 dark:!fill-gray-700 dark:hover:!fill-brand-500; +} +.jvm-marker.jvm-element { + @apply !stroke-gray-200 dark:!stroke-gray-800; +} + +.stocks-slider-outer .swiper-button-next:after, +.stocks-slider-outer .swiper-button-prev:after { + @apply hidden; +} + +.stocks-slider-outer .swiper-button-next, +.stocks-slider-outer .swiper-button-prev { + @apply static! mt-0 h-8 w-9 rounded-full border border-gray-200 !text-gray-700 transition hover:bg-gray-100 dark:border-gray-800 dark:bg-gray-800 dark:text-gray-400!; +} + +.stocks-slider-outer .swiper-button-next.swiper-button-disabled, +.stocks-slider-outer .swiper-button-prev.swiper-button-disabled { + @apply bg-white opacity-50 dark:bg-gray-900; +} + +.stocks-slider-outer .swiper-button-next svg, +.stocks-slider-outer .swiper-button-prev svg { + @apply !h-auto !w-auto; + fill: none; +} + +.flatpickr-wrapper { + @apply w-full; +} +.flatpickr-calendar { + @apply mt-2 !rounded-xl bg-black !p-5 !border !border-transparent dark:!border-white/5 !text-gray-500 dark:!bg-gray-dark dark:!text-gray-400 dark:!shadow-theme-xl 2xsm:!w-auto; +} +.flatpickr-time input { + background-color: #f9fafb !important; +} +.flatpickr-months .flatpickr-prev-month:hover svg, +.flatpickr-months .flatpickr-next-month:hover svg { + @apply stroke-brand-500; +} +.flatpickr-calendar.arrowTop:before, +.flatpickr-calendar.arrowTop:after { + @apply hidden; +} +.flatpickr-current-month .cur-month, +.flatpickr-current-month input.cur-year { + @apply !h-auto !pt-0 !text-lg !font-medium !text-gray-800 dark:!text-white/90; +} + +.flatpickr-prev-month, +.flatpickr-next-month { + @apply !p-0; +} + +.flatpickr-weekdays { + @apply h-auto mt-6 mb-4; +} + +.flatpickr-weekday { + @apply !text-theme-sm !font-medium !text-gray-500 dark:!text-gray-400; +} + +.flatpickr-day { + @apply !flex !items-center !text-theme-sm !font-medium !text-gray-800 dark:!text-white/90 dark:hover:!border-gray-300 dark:hover:!bg-gray-900; +} +.flatpickr-day.nextMonthDay, +.flatpickr-day.prevMonthDay { + @apply !text-gray-400; +} +.flatpickr-months .flatpickr-prev-month, +.flatpickr-months .flatpickr-next-month { + @apply !top-7 dark:!fill-white dark:!text-white; +} +.flatpickr-months .flatpickr-prev-month.flatpickr-prev-month, +.flatpickr-months .flatpickr-next-month.flatpickr-prev-month { + @apply !left-7; +} +.flatpickr-months .flatpickr-prev-month.flatpickr-next-month, +.flatpickr-months .flatpickr-next-month.flatpickr-next-month { + @apply !right-7; +} +span.flatpickr-weekday, +.flatpickr-months .flatpickr-month { + @apply dark:!fill-white dark:!text-white; +} +.flatpickr-day.inRange { + box-shadow: + -5px 0 0 #f9fafb, + 5px 0 0 #f9fafb !important; + @apply dark:!shadow-datepicker; +} +.flatpickr-day.inRange, +.flatpickr-day.prevMonthDay.inRange, +.flatpickr-day.nextMonthDay.inRange, +.flatpickr-day.today.inRange, +.flatpickr-day.prevMonthDay.today.inRange, +.flatpickr-day.nextMonthDay.today.inRange, +.flatpickr-day:hover, +.flatpickr-day.prevMonthDay:hover, +.flatpickr-day.nextMonthDay:hover, +.flatpickr-day:focus, +.flatpickr-day.prevMonthDay:focus, +.flatpickr-day.nextMonthDay:focus { + @apply !border-gray-50 !bg-gray-50 dark:!border-0 dark:!border-white/5 dark:!bg-white/5; +} +.flatpickr-day.selected, +.flatpickr-day.startRange, +.flatpickr-day.selected, +.flatpickr-day.endRange { + @apply !text-white dark:!text-white; +} +.flatpickr-day.selected, +.flatpickr-day.startRange, +.flatpickr-day.endRange, +.flatpickr-day.selected.inRange, +.flatpickr-day.startRange.inRange, +.flatpickr-day.endRange.inRange, +.flatpickr-day.selected:focus, +.flatpickr-day.startRange:focus, +.flatpickr-day.endRange:focus, +.flatpickr-day.selected:hover, +.flatpickr-day.startRange:hover, +.flatpickr-day.endRange:hover, +.flatpickr-day.selected.prevMonthDay, +.flatpickr-day.startRange.prevMonthDay, +.flatpickr-day.endRange.prevMonthDay, +.flatpickr-day.selected.nextMonthDay, +.flatpickr-day.startRange.nextMonthDay, +.flatpickr-day.endRange.nextMonthDay { + background: #465fff; + @apply !border-brand-500 !bg-brand-500 hover:!border-brand-500 hover:!bg-brand-500; +} +.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n + 1)), +.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n + 1)), +.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n + 1)) { + box-shadow: -10px 0 0 #465fff; +} + +.flatpickr-months .flatpickr-prev-month svg, +.flatpickr-months .flatpickr-next-month svg, +.flatpickr-months .flatpickr-prev-month, +.flatpickr-months .flatpickr-next-month { + @apply hover:!fill-none; +} +.flatpickr-months .flatpickr-prev-month:hover svg, +.flatpickr-months .flatpickr-next-month:hover svg { + fill: none !important; +} + +.flatpickr-calendar.static { + @apply right-0; +} +.flatpickr-calendar.hasTime { + width: 300px !important; +} +.flatpickr-calendar.hasTime .flatpickr-time { + border: transparent !important; +} +.fc .fc-view-harness { + @apply max-w-full overflow-x-auto custom-scrollbar; +} +.fc-dayGridMonth-view.fc-view.fc-daygrid { + @apply min-w-[718px]; +} +.fc .fc-scrollgrid-section > * { + border-right-width: 0; + border-bottom-width: 0; +} +.fc .fc-scrollgrid { + border-left-width: 0; +} +.fc .fc-toolbar.fc-header-toolbar { + @apply flex-col gap-4 px-6 pt-6 sm:flex-row; +} +.fc-button-group { + @apply gap-2; +} +.fc-button-group .fc-button { + @apply flex h-10 w-10 items-center justify-center !rounded-lg border border-gray-200 bg-transparent hover:border-gray-200 hover:bg-gray-50 focus:shadow-none active:!border-gray-200 active:!bg-transparent active:!shadow-none dark:border-gray-800 dark:hover:border-gray-800 dark:hover:bg-gray-900 dark:active:!border-gray-800; +} + +.fc-button-group .fc-button.fc-prev-button:before { + @apply inline-block mt-1; + content: url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M16.0068 6L9.75684 12.25L16.0068 18.5' stroke='%23344054' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A"); +} +.fc-button-group .fc-button.fc-next-button:before { + @apply inline-block mt-1; + content: url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M9.50684 19L15.7568 12.75L9.50684 6.5' stroke='%23344054' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A"); +} +.dark .fc-button-group .fc-button.fc-prev-button:before { + content: url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M16.0068 6L9.75684 12.25L16.0068 18.5' stroke='%2398A2B3' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A"); +} +.dark .fc-button-group .fc-button.fc-next-button:before { + content: url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M9.50684 19L15.7568 12.75L9.50684 6.5' stroke='%2398A2B3' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A"); +} +.fc-button-group .fc-button .fc-icon { + @apply hidden; +} +.fc-addEventButton-button { + @apply !rounded-lg !border-0 !bg-brand-500 !px-4 !py-2.5 !text-sm !font-medium hover:!bg-brand-600 focus:!shadow-none; +} +.fc-toolbar-title { + @apply !text-lg !font-medium text-gray-800 dark:text-white/90; +} +.fc-header-toolbar.fc-toolbar .fc-toolbar-chunk:last-child { + @apply rounded-lg bg-gray-100 p-0.5 dark:bg-gray-900; +} +.fc-header-toolbar.fc-toolbar .fc-toolbar-chunk:last-child .fc-button { + @apply !h-auto !w-auto rounded-md !border-0 bg-transparent !px-5 !py-2 text-sm font-medium text-gray-500 hover:text-gray-700 focus:!shadow-none dark:text-gray-400; +} +.fc-header-toolbar.fc-toolbar .fc-toolbar-chunk:last-child .fc-button.fc-button-active { + @apply text-gray-900 bg-white dark:bg-gray-800 dark:text-white; +} +.fc-theme-standard th { + @apply !border-x-0 border-t !border-gray-200 bg-gray-50 !text-left dark:!border-gray-800 dark:bg-gray-900; +} +.fc-theme-standard td, +.fc-theme-standard .fc-scrollgrid { + @apply !border-gray-200 dark:!border-gray-800; +} +.fc .fc-col-header-cell-cushion { + @apply !px-5 !py-4 text-sm font-medium uppercase text-gray-400; +} +.fc .fc-daygrid-day.fc-day-today { + @apply bg-transparent; +} +.fc .fc-daygrid-day { + @apply p-2; +} +.fc .fc-daygrid-day.fc-day-today .fc-scrollgrid-sync-inner { + @apply rounded-sm bg-gray-100 dark:bg-white/[0.03]; +} +.fc .fc-daygrid-day-number { + @apply !p-3 text-sm font-medium text-gray-700 dark:text-gray-400; +} +.fc .fc-daygrid-day-top { + @apply flex-row!; +} +.fc .fc-day-other .fc-daygrid-day-top { + opacity: 1; +} +.fc .fc-day-other .fc-daygrid-day-top .fc-daygrid-day-number { + @apply text-gray-400 dark:text-white/30; +} +.event-fc-color { + @apply rounded-lg py-2.5 pl-4 pr-3; +} +.event-fc-color .fc-event-title { + @apply p-0 text-sm font-normal text-gray-700; +} +.fc-daygrid-event-dot { + @apply w-1 h-5 ml-0 mr-3 border-none rounded-sm; +} +.fc-event { + @apply focus:shadow-none; +} +.fc-daygrid-event.fc-event-start { + @apply !ml-3; +} +.event-fc-color.fc-bg-success { + @apply border-success-50 bg-success-50; +} +.event-fc-color.fc-bg-danger { + @apply border-error-50 bg-error-50; +} +.event-fc-color.fc-bg-primary { + @apply border-brand-50 bg-brand-50; +} +.event-fc-color.fc-bg-warning { + @apply border-orange-50 bg-orange-50; +} +.event-fc-color.fc-bg-success .fc-daygrid-event-dot { + @apply bg-success-500; +} +.event-fc-color.fc-bg-danger .fc-daygrid-event-dot { + @apply bg-error-500; +} +.event-fc-color.fc-bg-primary .fc-daygrid-event-dot { + @apply bg-brand-500; +} +.event-fc-color.fc-bg-warning .fc-daygrid-event-dot { + @apply bg-orange-500; +} +.fc-direction-ltr .fc-timegrid-slot-label-frame { + @apply px-3 py-1.5 text-left text-sm font-medium text-gray-500 dark:text-gray-400; +} +.fc .fc-timegrid-axis-cushion { + @apply text-sm font-medium text-gray-500 dark:text-gray-400; +} + +.input-date-icon::-webkit-inner-spin-button, +.input-date-icon::-webkit-calendar-picker-indicator { + opacity: 0; + -webkit-appearance: none; +} + +.swiper-button-prev svg, +.swiper-button-next svg { + @apply !h-auto w-auto!; +} + +.carouselTwo .swiper-button-next:after, +.carouselTwo .swiper-button-prev:after, +.carouselFour .swiper-button-next:after, +.carouselFour .swiper-button-prev:after { + @apply hidden; +} +.carouselTwo .swiper-button-next.swiper-button-disabled, +.carouselTwo .swiper-button-prev.swiper-button-disabled, +.carouselFour .swiper-button-next.swiper-button-disabled, +.carouselFour .swiper-button-prev.swiper-button-disabled { + @apply bg-white/60 opacity-100!; +} +.carouselTwo .swiper-button-next, +.carouselTwo .swiper-button-prev, +.carouselFour .swiper-button-next, +.carouselFour .swiper-button-prev { + @apply h-10 w-10 rounded-full border-[0.5px] border-white/10 bg-white/90 !text-gray-700 shadow-slider-navigation backdrop-blur-[10px]; +} + +.carouselTwo .swiper-button-prev, +.carouselFour .swiper-button-prev { + @apply !left-3 sm:!left-4; +} + +.carouselTwo .swiper-button-next, +.carouselFour .swiper-button-next { + @apply !right-3 sm:!right-4; +} + +.carouselThree .swiper-pagination, +.carouselFour .swiper-pagination { + @apply !bottom-3 !left-1/2 inline-flex !w-auto -translate-x-1/2 items-center gap-1.5 rounded-[40px] border-[0.5px] border-white/10 bg-white/60 px-2 py-1.5 shadow-slider-navigation backdrop-blur-[10px] sm:!bottom-5; +} + +.carouselThree .swiper-pagination-bullet, +.carouselFour .swiper-pagination-bullet { + @apply !m-0 h-2.5 w-2.5 bg-white opacity-100 shadow-theme-xs duration-200 ease-in-out; +} + +.carouselThree .swiper-pagination-bullet-active, +.carouselFour .swiper-pagination-bullet-active { + @apply w-6.5 rounded-xl; +} + +.form-check-input:checked ~ span { + @apply border-[6px] border-brand-500 dark:border-brand-500; +} + +.taskCheckbox:checked ~ .box span { + @apply opacity-100; +} +.taskCheckbox:checked ~ p { + @apply text-gray-400 line-through; +} +.taskCheckbox:checked ~ .box { + @apply border-brand-500 bg-brand-500 dark:border-brand-500; +} + +.task { + transition: all 0.2s ease; /* Smooth transition for visual effects */ +} + +.task.is-dragging { + border-radius: 0.75rem; + box-shadow: + 0px 1px 3px 0px rgba(16, 24, 40, 0.1), + 0px 1px 2px 0px rgba(16, 24, 40, 0.06); + opacity: 0.8; + cursor: grabbing; /* Changes the cursor to indicate dragging */ +} + + +.custom-calendar .fc-h-event { + background-color: #0000; + border: none; + color: black; +} + +.simplebar-scrollbar:before { + @apply !bg-gray-200 !rounded-full dark:!bg-gray-700 !opacity-100; +} + +.dark .simplebar-scrollbar::before { + @apply !bg-gray-700; +} + +.simplebar-scrollbar.simplebar-visible:before { + @apply opacity-100; +} + +.social-button { + @apply flex h-11 w-11 items-center justify-center gap-2 rounded-full border border-gray-300 bg-white text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200; +} + +.edit-button { + @apply flex w-full items-center justify-center gap-2 rounded-full border border-gray-300 bg-white px-4 py-3 text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200 lg:inline-flex lg:w-auto; +} + +/* Code Editor */ +code[class*='language-'], +pre[class*='language-'] { + text-shadow: none; + color: #344054; + overflow: hidden !important; + font-size: 14px; + margin: 0; + padding: 0; + white-space: pre-wrap; + word-wrap: break-word; + text-align: justify; +} + +.dark code[class*='language-'], +.dark pre[class*='language-'] { + text-shadow: none; + color: #98a2b3; +} +.language-html { + background-color: #ffffff !important; +} +.dark .language-html { + background-color: #ffffff00 !important; +} +.token { + text-shadow: none; + font-size: 14px; +} +.token.doctype-tag, +.token.name { + color: #018001; +} +.token.tag { + color: #267f99; +} +.token.selector { + color: #267f99; +} +.token.property { + color: #0070c1; +} +.token.language-css { + color: #1b00ff; +} +.token.attr-name { + color: #98a2b3; +} +.token.attr-value { + color: #a31615; +} +.token.punctuation { + color: #344054; +} +.dark .token.punctuation { + color: #98a2b3; +} +.custom-datepicker .flatpickr-calendar.static.open { + left: 0; + right: auto; +} + +.swiper-button-prev, .swiper-button-next { + svg { + fill: none !important; + } +} diff --git a/resources/js/app.js b/resources/js/app.js new file mode 100644 index 0000000..cb520ed --- /dev/null +++ b/resources/js/app.js @@ -0,0 +1,51 @@ +import './bootstrap'; +import Alpine from 'alpinejs'; +import ApexCharts from 'apexcharts'; + +// flatpickr +import flatpickr from 'flatpickr'; +import 'flatpickr/dist/flatpickr.min.css'; +// FullCalendar +import { Calendar } from '@fullcalendar/core'; + + + +window.Alpine = Alpine; +window.ApexCharts = ApexCharts; +window.flatpickr = flatpickr; +window.FullCalendar = Calendar; + +Alpine.start(); + +// Initialize components on DOM ready +document.addEventListener('DOMContentLoaded', () => { + // Map imports + if (document.querySelector('#mapOne')) { + import('./components/map').then(module => module.initMap()); + } + + // Chart imports + if (document.querySelector('#chartOne')) { + import('./components/chart/chart-1').then(module => module.initChartOne()); + } + if (document.querySelector('#chartTwo')) { + import('./components/chart/chart-2').then(module => module.initChartTwo()); + } + if (document.querySelector('#chartThree')) { + import('./components/chart/chart-3').then(module => module.initChartThree()); + } + if (document.querySelector('#chartSix')) { + import('./components/chart/chart-6').then(module => module.initChartSix()); + } + if (document.querySelector('#chartEight')) { + import('./components/chart/chart-8').then(module => module.initChartEight()); + } + if (document.querySelector('#chartThirteen')) { + import('./components/chart/chart-13').then(module => module.initChartThirteen()); + } + + // Calendar init + if (document.querySelector('#calendar')) { + import('./components/calendar-init').then(module => module.calendarInit()); + } +}); diff --git a/resources/js/bootstrap.js b/resources/js/bootstrap.js new file mode 100644 index 0000000..5f1390b --- /dev/null +++ b/resources/js/bootstrap.js @@ -0,0 +1,4 @@ +import axios from 'axios'; +window.axios = axios; + +window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; diff --git a/resources/js/components/calendar-init.js b/resources/js/components/calendar-init.js new file mode 100644 index 0000000..c071a22 --- /dev/null +++ b/resources/js/components/calendar-init.js @@ -0,0 +1,352 @@ + + +import { Calendar } from "@fullcalendar/core"; +import dayGridPlugin from "@fullcalendar/daygrid"; +import listPlugin from "@fullcalendar/list"; +import timeGridPlugin from "@fullcalendar/timegrid"; +import interactionPlugin from "@fullcalendar/interaction"; + +export function calendarInit() { + const calendarWrapper = document.querySelector("#calendar"); + + if (calendarWrapper) { + // Calendar Date variable + const newDate = new Date(); + const getDynamicMonth = () => { + const month = newDate.getMonth() + 1; + return month < 10 ? `0${month}` : `${month}`; + }; + + // Calendar Modal Elements + const getModalTitleEl = document.querySelector("#event-title"); + const getModalStartDateEl = document.querySelector("#event-start-date"); + const getModalEndDateEl = document.querySelector("#event-end-date"); + const getModalAddBtnEl = document.querySelector(".btn-add-event"); + const getModalUpdateBtnEl = document.querySelector(".btn-update-event"); + const getModalHeaderEl = document.querySelector("#eventModalLabel"); + + const calendarsEvents = { + Danger: "danger", + Success: "success", + Primary: "primary", + Warning: "warning", + }; + + // Calendar Elements and options + const calendarEl = document.querySelector("#calendar"); + + const calendarHeaderToolbar = { + left: "prev,next addEventButton", + center: "title", + right: "dayGridMonth,timeGridWeek,timeGridDay", + }; + + const calendarEventsList = [ + { + id: "1", + title: "Event Conf.", + start: `${newDate.getFullYear()}-${getDynamicMonth()}-01`, + extendedProps: { calendar: "Danger" }, + }, + { + id: "2", + title: "Seminar #4", + start: `${newDate.getFullYear()}-${getDynamicMonth()}-07`, + end: `${newDate.getFullYear()}-${getDynamicMonth()}-10`, + extendedProps: { calendar: "Success" }, + }, + { + id: "3", + title: "Meeting #5", + start: `${newDate.getFullYear()}-${getDynamicMonth()}-09T16:00:00`, + extendedProps: { calendar: "Primary" }, + }, + { + id: "4", + title: "Submission #1", + start: `${newDate.getFullYear()}-${getDynamicMonth()}-16T16:00:00`, + extendedProps: { calendar: "Warning" }, + }, + { + id: "5", + title: "Seminar #6", + start: `${newDate.getFullYear()}-${getDynamicMonth()}-11`, + end: `${newDate.getFullYear()}-${getDynamicMonth()}-13`, + extendedProps: { calendar: "Danger" }, + }, + ]; + + // Modal Functions + const openModal = () => { + const modal = document.getElementById("eventModal"); + if (modal) { + modal.style.display = "flex"; + document.body.style.overflow = "hidden"; // Prevent background scroll + } + }; + + const closeModal = () => { + const modal = document.getElementById("eventModal"); + if (modal) { + modal.style.display = "none"; + document.body.style.overflow = ""; // Restore scroll + } + resetModalFields(); + }; + + // Reset modal fields + function resetModalFields() { + if (getModalTitleEl) getModalTitleEl.value = ""; + if (getModalStartDateEl) getModalStartDateEl.value = ""; + if (getModalEndDateEl) getModalEndDateEl.value = ""; + + const getModalIfCheckedRadioBtnEl = document.querySelector( + 'input[name="event-level"]:checked' + ); + if (getModalIfCheckedRadioBtnEl) { + getModalIfCheckedRadioBtnEl.checked = false; + } + } + + // Calendar Select function (when user clicks/drags on calendar) + const calendarSelect = (info) => { + resetModalFields(); + + // Update modal header + if (getModalHeaderEl) { + getModalHeaderEl.textContent = "Add Event"; + } + + // Show Add button, hide Update button + if (getModalAddBtnEl) getModalAddBtnEl.style.display = "flex"; + if (getModalUpdateBtnEl) getModalUpdateBtnEl.style.display = "none"; + + // Set dates from selection + if (getModalStartDateEl) getModalStartDateEl.value = info.startStr; + if (getModalEndDateEl) { + getModalEndDateEl.value = info.endStr || info.startStr; + } + + openModal(); + }; + + // Calendar AddEvent button click + const calendarAddEvent = () => { + resetModalFields(); + + // Update modal header + if (getModalHeaderEl) { + getModalHeaderEl.textContent = "Add Event"; + } + + // Show Add button, hide Update button + if (getModalAddBtnEl) getModalAddBtnEl.style.display = "flex"; + if (getModalUpdateBtnEl) getModalUpdateBtnEl.style.display = "none"; + + // Set default start date to today + const currentDate = new Date(); + const yyyy = currentDate.getFullYear(); + const mm = String(currentDate.getMonth() + 1).padStart(2, "0"); + const dd = String(currentDate.getDate()).padStart(2, "0"); + const combineDate = `${yyyy}-${mm}-${dd}`; + + if (getModalStartDateEl) getModalStartDateEl.value = combineDate; + + openModal(); + }; + + // Calendar Event Click function (when user clicks existing event) + const calendarEventClick = (info) => { + const eventObj = info.event; + + if (eventObj.url) { + window.open(eventObj.url); + info.jsEvent.preventDefault(); + } else { + resetModalFields(); + + // Update modal header + if (getModalHeaderEl) { + getModalHeaderEl.textContent = "Edit Event"; + } + + // Get event details + const getModalEventId = eventObj.id; + const getModalEventLevel = eventObj.extendedProps.calendar; + + // Set form values + if (getModalTitleEl) getModalTitleEl.value = eventObj.title; + if (getModalStartDateEl) { + getModalStartDateEl.value = eventObj.startStr.split("T")[0]; + } + if (getModalEndDateEl) { + getModalEndDateEl.value = eventObj.endStr + ? eventObj.endStr.split("T")[0] + : ""; + } + + // Check the correct radio button + const getModalCheckedRadioBtnEl = document.querySelector( + `input[value="${getModalEventLevel}"]` + ); + if (getModalCheckedRadioBtnEl) { + getModalCheckedRadioBtnEl.checked = true; + } + + // Store event ID for update + if (getModalUpdateBtnEl) { + getModalUpdateBtnEl.dataset.fcEventPublicId = getModalEventId; + } + + // Hide Add button, show Update button + if (getModalAddBtnEl) getModalAddBtnEl.style.display = "none"; + if (getModalUpdateBtnEl) getModalUpdateBtnEl.style.display = "flex"; + + openModal(); + } + }; + + // Initialize Calendar + const calendar = new Calendar(calendarEl, { + plugins: [dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin], + selectable: true, + initialView: "dayGridMonth", + initialDate: `${newDate.getFullYear()}-${getDynamicMonth()}-07`, + headerToolbar: calendarHeaderToolbar, + events: calendarEventsList, + select: calendarSelect, + eventClick: calendarEventClick, + displayEventTime: false, // Hide time display + customButtons: { + addEventButton: { + text: "Add Event +", + click: calendarAddEvent, + }, + }, + // eventClassNames({ event: calendarEvent }) { + // const getColorValue = + // calendarsEvents[calendarEvent._def.extendedProps.calendar]; + // return [`event-fc-color`, `fc-bg-${getColorValue}`]; + // }, + // Optional: Custom event content without time + eventContent(eventInfo) { + const colorClass = `fc-bg-${eventInfo.event.extendedProps.calendar.toLowerCase()}` + return { + html: ` +
+
+
${eventInfo.timeText}
+
${eventInfo.event.title}
+
+ `, + } + }, + }); + + // Update Calendar Event + // if (getModalUpdateBtnEl) { + // getModalUpdateBtnEl.addEventListener("click", () => { + // const getPublicID = getModalUpdateBtnEl.dataset.fcEventPublicId; + // const getTitleUpdatedValue = getModalTitleEl.value; + // const setModalStartDateValue = getModalStartDateEl.value; + // const setModalEndDateValue = getModalEndDateEl.value; + // const getEvent = calendar.getEventById(getPublicID); + // const getModalUpdatedCheckedRadioBtnEl = document.querySelector( + // 'input[name="event-level"]:checked' + // ); + + // const getModalUpdatedCheckedRadioBtnValue = + // getModalUpdatedCheckedRadioBtnEl + // ? getModalUpdatedCheckedRadioBtnEl.value + // : ""; + + // if (getEvent) { + // getEvent.setProp("title", getTitleUpdatedValue); + // getEvent.setDates(setModalStartDateValue, setModalEndDateValue); + // getEvent.setExtendedProp("calendar", getModalUpdatedCheckedRadioBtnValue); + // } + + // closeModal(); + // }); + // } + if (getModalUpdateBtnEl) { + getModalUpdateBtnEl.addEventListener("click", () => { + const getPublicID = getModalUpdateBtnEl.dataset.fcEventPublicId; + const getTitleUpdatedValue = getModalTitleEl.value; + const setModalStartDateValue = getModalStartDateEl.value; + const setModalEndDateValue = getModalEndDateEl.value; + const getEvent = calendar.getEventById(getPublicID); + const getModalUpdatedCheckedRadioBtnEl = document.querySelector( + 'input[name="event-level"]:checked' + ); + + const getModalUpdatedCheckedRadioBtnValue = + getModalUpdatedCheckedRadioBtnEl + ? getModalUpdatedCheckedRadioBtnEl.value + : ""; + + if (getEvent) { + // Remove the old event + getEvent.remove(); + + // Add updated event with all properties + calendar.addEvent({ + id: getPublicID, + title: getTitleUpdatedValue, + start: setModalStartDateValue, + end: setModalEndDateValue, + allDay: true, + extendedProps: { calendar: getModalUpdatedCheckedRadioBtnValue }, + }); + } + + closeModal(); + }); + } + + // Add Calendar Event + if (getModalAddBtnEl) { + getModalAddBtnEl.addEventListener("click", () => { + const getModalCheckedRadioBtnEl = document.querySelector( + 'input[name="event-level"]:checked' + ); + + const getTitleValue = getModalTitleEl.value; + const setModalStartDateValue = getModalStartDateEl.value; + const setModalEndDateValue = getModalEndDateEl.value; + const getModalCheckedRadioBtnValue = getModalCheckedRadioBtnEl + ? getModalCheckedRadioBtnEl.value + : ""; + + calendar.addEvent({ + id: Date.now().toString(), + title: getTitleValue, + start: setModalStartDateValue, + end: setModalEndDateValue, + allDay: true, + extendedProps: { calendar: getModalCheckedRadioBtnValue }, + }); + + closeModal(); + }); + } + + // Render Calendar + calendar.render(); + + // Close modal event listeners + document.querySelectorAll(".modal-close-btn").forEach((btn) => { + btn.addEventListener("click", closeModal); + }); + + // Close when clicking outside modal + window.addEventListener("click", (event) => { + const modal = document.getElementById("eventModal"); + if (event.target === modal) { + closeModal(); + } + }); + } +} + +export default calendarInit; diff --git a/resources/js/components/chart/chart-1.js b/resources/js/components/chart/chart-1.js new file mode 100644 index 0000000..a3342f4 --- /dev/null +++ b/resources/js/components/chart/chart-1.js @@ -0,0 +1,100 @@ + + +export const initChartOne = () => { + const chartElement = document.querySelector('#chartOne'); + if (!chartElement) return; + + const chartOneOptions = { + series: [{ + name: "Sales", + data: [168, 385, 201, 298, 187, 195, 291, 110, 215, 390, 280, 112], + },], + colors: ["#465fff"], + chart: { + fontFamily: "Outfit, sans-serif", + type: "bar", + height: 180, + toolbar: { + show: false, + }, + }, + plotOptions: { + bar: { + horizontal: false, + columnWidth: "39%", + borderRadius: 5, + borderRadiusApplication: "end", + }, + }, + dataLabels: { + enabled: false, + }, + stroke: { + show: true, + width: 4, + colors: ["transparent"], + }, + xaxis: { + categories: [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ], + axisBorder: { + show: false, + }, + axisTicks: { + show: false, + }, + }, + legend: { + show: true, + position: "top", + horizontalAlign: "left", + fontFamily: "Outfit", + markers: { + radius: 99, + }, + }, + yaxis: { + title: false, + }, + grid: { + yaxis: { + lines: { + show: true, + }, + }, + }, + fill: { + opacity: 1, + }, + + tooltip: { + x: { + show: false, + }, + y: { + formatter: function (val) { + return val; + }, + }, + }, + }; + + const chart = new ApexCharts(chartElement, chartOneOptions); + chart.render(); + + return chart; +}; + +export default initChartOne; diff --git a/resources/js/components/chart/chart-13.js b/resources/js/components/chart/chart-13.js new file mode 100644 index 0000000..9c46684 --- /dev/null +++ b/resources/js/components/chart/chart-13.js @@ -0,0 +1,332 @@ + +export function initChartThirteen() { + const chartThirteenEl = document.querySelector("#chartThirteen"); + if (chartThirteenEl) { + const data = [ + [1746153600000, 30.95], + [1746240000000, 31.34], + [1746326400000, 31.18], + [1746412800000, 31.05], + [1746672000000, 31.0], + [1746758400000, 30.95], + [1746844800000, 31.24], + [1746931200000, 31.29], + [1747017600000, 31.85], + [1747276800000, 31.86], + [1747363200000, 32.28], + [1747449600000, 32.1], + [1747536000000, 32.65], + [1747622400000, 32.21], + [1747881600000, 32.35], + [1747968000000, 32.44], + [1748054400000, 32.46], + [1748140800000, 32.86], + [1748227200000, 32.75], + [1748572800000, 32.54], + [1748659200000, 32.33], + [1748745600000, 32.97], + [1748832000000, 33.41], + [1749091200000, 33.27], + [1749177600000, 33.27], + [1749264000000, 32.89], + [1749350400000, 33.1], + [1749436800000, 33.73], + [1749696000000, 33.22], + [1749782400000, 31.99], + [1749868800000, 32.41], + [1749955200000, 33.05], + [1750041600000, 33.64], + [1750300800000, 33.56], + [1750387200000, 34.22], + [1750473600000, 33.77], + [1750560000000, 34.17], + [1750646400000, 33.82], + [1750905600000, 34.51], + [1750992000000, 33.16], + [1751078400000, 33.56], + [1751164800000, 33.71], + [1751251200000, 33.81], + [1751506800000, 34.4], + [1751593200000, 34.63], + [1751679600000, 34.46], + [1751766000000, 34.48], + [1751852400000, 34.31], + [1752111600000, 34.7], + [1752198000000, 34.31], + [1752284400000, 33.46], + [1752370800000, 33.59], + [1752716400000, 33.22], + [1752802800000, 32.61], + [1752889200000, 33.01], + [1752975600000, 33.55], + [1753062000000, 33.18], + [1753321200000, 32.84], + [1753407600000, 33.84], + [1753494000000, 33.39], + [1753580400000, 32.91], + [1753666800000, 33.06], + [1753926000000, 32.62], + [1754012400000, 32.4], + [1754098800000, 33.13], + [1754185200000, 33.26], + [1754271600000, 33.58], + [1754530800000, 33.55], + [1754617200000, 33.77], + [1754703600000, 33.76], + [1754790000000, 33.32], + [1754876400000, 32.61], + [1755135600000, 32.52], + [1755222000000, 32.67], + [1755308400000, 32.52], + [1755394800000, 31.92], + [1755481200000, 32.2], + [1755740400000, 32.23], + [1755826800000, 32.33], + [1755913200000, 32.36], + [1755999600000, 32.01], + [1756086000000, 31.31], + [1756345200000, 32.01], + [1756431600000, 32.01], + [1756518000000, 32.18], + [1756604400000, 31.54], + [1756690800000, 31.6], + [1757036400000, 32.05], + [1757122800000, 31.29], + [1757209200000, 31.05], + [1757295600000, 29.82], + [1757554800000, 30.31], + [1757641200000, 30.7], + [1757727600000, 31.69], + [1757814000000, 31.32], + [1757900400000, 31.65], + [1758159600000, 31.13], + [1758246000000, 31.77], + [1758332400000, 31.79], + [1758418800000, 31.67], + [1758505200000, 32.39], + [1758764400000, 32.63], + [1758850800000, 32.89], + [1758937200000, 31.99], + [1759023600000, 31.23], + [1759110000000, 31.57], + [1759369200000, 30.84], + [1759455600000, 31.07], + [1759542000000, 31.41], + [1759628400000, 31.17], + [1759714800000, 32.37], + [1759974000000, 32.19], + [1760060400000, 32.51], + [1760233200000, 32.53], + [1760319600000, 31.37], + [1760578800000, 30.43], + [1760665200000, 30.44], + [1760751600000, 30.2], + [1760838000000, 30.14], + [1760924400000, 30.65], + [1761183600000, 30.4], + [1761270000000, 30.65], + [1761356400000, 31.43], + [1761442800000, 31.89], + [1761529200000, 31.38], + [1761788400000, 30.64], + [1761874800000, 30.02], + [1761961200000, 30.33], + [1762047600000, 30.95], + [1762134000000, 31.89], + [1762393200000, 31.01], + [1762479600000, 30.88], + [1762566000000, 30.69], + [1762652400000, 30.58], + [1762738800000, 32.02], + [1762998000000, 32.14], + [1763084400000, 32.37], + [1763170800000, 32.51], + [1763257200000, 32.65], + [1763343600000, 32.64], + [1763602800000, 32.27], + [1763689200000, 32.1], + [1763775600000, 32.91], + [1763862000000, 33.65], + [1763948400000, 33.8], + [1764207600000, 33.92], + [1764294000000, 33.75], + [1764380400000, 33.84], + [1764466800000, 33.5], + [1764553200000, 32.26], + [1764812400000, 32.32], + [1764898800000, 32.06], + [1764985200000, 31.96], + [1765071600000, 31.46], + [1765158000000, 31.27], + [1765503600000, 31.43], + [1765590000000, 32.26], + [1765676400000, 32.79], + [1765762800000, 32.46], + [1766022000000, 32.13], + [1766108400000, 32.43], + [1766194800000, 32.42], + [1766281200000, 32.81], + [1766367600000, 33.34], + [1766626800000, 33.41], + [1766713200000, 32.57], + [1766799600000, 33.12], + [1766886000000, 34.53], + [1766972400000, 33.83], + [1767231600000, 33.41], + [1767318000000, 32.9], + [1767404400000, 32.53], + [1767490800000, 32.8], + [1767577200000, 32.44], + [1767836400000, 32.62], + [1767922800000, 32.57], + [1768009200000, 32.6], + [1768095600000, 32.68], + [1768182000000, 32.47], + [1768441200000, 32.23], + [1768527600000, 31.68], + [1768614000000, 31.51], + [1768700400000, 31.78], + [1768786800000, 31.94], + [1769046000000, 32.33], + [1769132400000, 33.24], + [1769218800000, 33.44], + [1769305200000, 33.48], + [1769391600000, 33.24], + [1769650800000, 33.49], + [1769737200000, 33.31], + [1769823600000, 33.36], + [1769910000000, 33.4], + [1769996400000, 34.01], + [1770432000000, 34.02], + [1770518400000, 34.36], + [1770604800000, 34.39], + [1770864000000, 34.24], + [1770950400000, 34.39], + [1771036800000, 33.47], + [1771123200000, 32.98], + [1771209600000, 32.9], + [1771468800000, 32.7], + [1771555200000, 32.54], + [1771641600000, 32.23], + [1771728000000, 32.64], + [1771814400000, 32.65], + [1772073600000, 32.92], + [1772160000000, 32.64], + [1772246400000, 32.84], + [1772419200000, 33.4], + [1772678400000, 33.3], + [1772764800000, 33.18], + [1772851200000, 33.88], + [1772937600000, 34.09], + [1773024000000, 34.61], + [1773283200000, 34.7], + [1773369600000, 35.3], + [1773456000000, 35.4], + [1773542400000, 35.14], + [1773628800000, 35.48], + [1773888000000, 35.75], + [1773974400000, 35.54], + [1774060800000, 35.96], + [1774147200000, 35.53], + [1774233600000, 37.56], + [1774492800000, 37.42], + [1774579200000, 37.49], + [1774665600000, 38.09], + [1774752000000, 37.87], + ]; + + const chartOptions = { + series: [ + { + name: "Portfolio Performance", + data: data, + }, + ], + legend: { + show: false, + position: "top", + horizontalAlign: "left", + }, + colors: ["#465FFF"], + chart: { + fontFamily: "Outfit, sans-serif", + height: 335, + id: "area-datetime", + type: "area", + toolbar: { + show: false, + }, + }, + + stroke: { + curve: "straight", + width: ["1", "1"], + }, + + dataLabels: { + enabled: false, + }, + + markers: { + size: 0, + }, + + labels: { + show: false, + position: "top", + }, + + xaxis: { + type: "datetime", + tickAmount: 10, + axisBorder: { + show: false, + }, + axisTicks: { + show: false, + }, + tooltip: false, + }, + + tooltip: { + x: { + format: "dd MMM yyyy", + }, + }, + + fill: { + gradient: { + enabled: true, + opacityFrom: 0.55, + opacityTo: 0, + }, + }, + + grid: { + xaxis: { + lines: { + show: false, + }, + }, + yaxis: { + lines: { + show: true, + }, + }, + }, + + yaxis: { + title: { + style: { + fontSize: "0px", + }, + }, + }, + }; + const chartThirteen = new ApexCharts(chartThirteenEl, chartOptions); + chartThirteen.render(); + return chartThirteen; + } +} + +export default initChartThirteen; diff --git a/resources/js/components/chart/chart-2.js b/resources/js/components/chart/chart-2.js new file mode 100644 index 0000000..3ea1425 --- /dev/null +++ b/resources/js/components/chart/chart-2.js @@ -0,0 +1,61 @@ + +export const initChartTwo = () => { + const chartElement = document.querySelector('#chartTwo'); + + if (chartElement) { + const chartTwoOptions = { + series: [75.55], + colors: ["#465FFF"], + chart: { + fontFamily: "Outfit, sans-serif", + type: "radialBar", + height: 330, + sparkline: { + enabled: true, + }, + }, + plotOptions: { + radialBar: { + startAngle: -90, + endAngle: 90, + hollow: { + size: "80%", + }, + track: { + background: "#E4E7EC", + strokeWidth: "100%", + margin: 5, // margin is in pixels + }, + dataLabels: { + name: { + show: false, + }, + value: { + fontSize: "36px", + fontWeight: "600", + offsetY: 60, + color: "#1D2939", + formatter: function (val) { + return val + "%"; + }, + }, + }, + }, + }, + fill: { + type: "solid", + colors: ["#465FFF"], + }, + stroke: { + lineCap: "round", + }, + labels: ["Progress"], + }; + + const chart = new ApexCharts(chartElement, chartTwoOptions); + chart.render(); + return chart; + } +} + +export default initChartTwo; diff --git a/resources/js/components/chart/chart-3.js b/resources/js/components/chart/chart-3.js new file mode 100644 index 0000000..ed19072 --- /dev/null +++ b/resources/js/components/chart/chart-3.js @@ -0,0 +1,107 @@ + +export const initChartThree = () => { + const chartElement = document.querySelector('#chartThree'); + + if (chartElement) { + const chartThreeOptions = { + series: [{ + name: "Sales", + data: [180, 190, 170, 160, 175, 165, 170, 205, 230, 210, 240, 235], + }, + { + name: "Revenue", + data: [40, 30, 50, 40, 55, 40, 70, 100, 110, 120, 150, 140], + }, + ], + legend: { + show: false, + position: "top", + horizontalAlign: "left", + }, + colors: ["#465FFF", "#9CB9FF"], + chart: { + fontFamily: "Outfit, sans-serif", + height: 310, + type: "area", + toolbar: { + show: false, + }, + }, + fill: { + gradient: { + enabled: true, + opacityFrom: 0.55, + opacityTo: 0, + }, + }, + stroke: { + curve: "straight", + width: ["2", "2"], + }, + markers: { + size: 0, + }, + labels: { + show: false, + position: "top", + }, + grid: { + xaxis: { + lines: { + show: false, + }, + }, + yaxis: { + lines: { + show: true, + }, + }, + }, + dataLabels: { + enabled: false, + }, + tooltip: { + x: { + format: "dd MMM yyyy", + }, + }, + xaxis: { + type: "category", + categories: [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ], + axisBorder: { + show: false, + }, + axisTicks: { + show: false, + }, + tooltip: false, + }, + yaxis: { + title: { + style: { + fontSize: "0px", + }, + }, + }, + }; + + const chart = new ApexCharts(chartElement, chartThreeOptions); + chart.render(); + return chart; + } +} + +export default initChartThree; diff --git a/resources/js/components/chart/chart-6.js b/resources/js/components/chart/chart-6.js new file mode 100644 index 0000000..e829b17 --- /dev/null +++ b/resources/js/components/chart/chart-6.js @@ -0,0 +1,107 @@ + + +export function initChartSix() { + const chartSixEl = document.querySelector('#chartSix'); + if (chartSixEl) { + const chartSixOptions = { + series: [ + { + name: "Direct", + data: [44, 55, 41, 67, 22, 43, 55, 41], + }, + { + name: "Referral", + data: [13, 23, 20, 8, 13, 27, 13, 23], + }, + { + name: "Organic Search", + data: [11, 17, 15, 15, 21, 14, 18, 20], + }, + { + name: "Social", + data: [21, 7, 25, 13, 22, 8, 18, 20], + }, + ], + colors: ["#2a31d8", "#465fff", "#7592ff", "#c2d6ff"], + chart: { + fontFamily: "Outfit, sans-serif", + type: "bar", + stacked: true, + height: 315, + toolbar: { + show: false, + }, + zoom: { + enabled: false, + }, + }, + plotOptions: { + bar: { + horizontal: false, + columnWidth: "39%", + borderRadius: 10, + borderRadiusApplication: "end", + borderRadiusWhenStacked: "last", + }, + }, + dataLabels: { + enabled: false, + }, + xaxis: { + categories: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug"], + axisBorder: { + show: false, + }, + axisTicks: { + show: false, + }, + }, + legend: { + show: true, + position: "top", + horizontalAlign: "left", + fontFamily: "Outfit", + fontSize: "14px", + fontWeight: 400, + markers: { + size: 5, + shape: "circle", + radius: 999, + strokeWidth: 0, + }, + itemMargin: { + horizontal: 10, + vertical: 0, + }, + }, + yaxis: { + title: false, + }, + grid: { + yaxis: { + lines: { + show: true, + }, + }, + }, + fill: { + opacity: 1, + }, + + tooltip: { + x: { + show: false, + }, + y: { + formatter: function (val) { + return val; + }, + }, + }, + }; + + const chartSix = new ApexCharts(chartSixEl, chartSixOptions); + chartSix.render(); + return chartSix; + } +} diff --git a/resources/js/components/chart/chart-8.js b/resources/js/components/chart/chart-8.js new file mode 100644 index 0000000..14f8efa --- /dev/null +++ b/resources/js/components/chart/chart-8.js @@ -0,0 +1,108 @@ + +export function initChartEight() { + const chartEightEl = document.querySelector('#chartEight'); + if (chartEightEl) { + const chartEightOptions = { + series: [ + { + name: "Sales", + data: [180, 190, 170, 160, 175, 165, 170, 205, 230, 210, 240, 235], + }, + { + name: "Revenue", + data: [40, 30, 50, 40, 55, 40, 70, 100, 110, 120, 150, 140], + }, + ], + legend: { + show: false, + position: "top", + horizontalAlign: "left", + }, + colors: ["#465FFF", "#9CB9FF"], + chart: { + fontFamily: "Outfit, sans-serif", + height: 310, + type: "area", + toolbar: { + show: false, + }, + }, + fill: { + gradient: { + enabled: true, + opacityFrom: 0.55, + opacityTo: 0, + }, + }, + stroke: { + curve: "smooth", + width: ["2", "2"], + }, + + markers: { + size: 0, + }, + labels: { + show: false, + position: "top", + }, + grid: { + xaxis: { + lines: { + show: false, + }, + }, + yaxis: { + lines: { + show: true, + }, + }, + }, + dataLabels: { + enabled: false, + }, + tooltip: { + x: { + format: "dd MMM yyyy", + }, + }, + xaxis: { + type: "category", + categories: [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ], + axisBorder: { + show: false, + }, + axisTicks: { + show: false, + }, + tooltip: false, + }, + yaxis: { + title: { + style: { + fontSize: "0px", + }, + }, + }, + }; + + const chartEight = new ApexCharts(chartEightEl, chartEightOptions); + chartEight.render(); + + return chartEight; + } +} +export default initChartEight; diff --git a/resources/js/components/map.js b/resources/js/components/map.js new file mode 100644 index 0000000..efad7cf --- /dev/null +++ b/resources/js/components/map.js @@ -0,0 +1,63 @@ +import jsVectorMap from 'jsvectormap'; +import 'jsvectormap/dist/maps/world'; +import 'jsvectormap/dist/jsvectormap.min.css'; + +export const initMap = () => { + const mapSelectorOne = document.querySelectorAll('#mapOne'); + + if (mapSelectorOne.length) { + const mapOne = new jsVectorMap({ + selector: "#mapOne", + map: "world", + zoomButtons: false, + regionStyle: { + initial: { + fontFamily: "Outfit", + fill: "#D9D9D9", + }, + hover: { + fillOpacity: 1, + fill: "#465fff", + }, + }, + markers: [ + { + name: "Egypt", + coords: [26.8206, 30.8025], + }, + { + name: "United Kingdom", + coords: [55.3781, 3.436], + }, + { + name: "United States", + coords: [37.0902, -95.7129], + }, + ], + + markerStyle: { + initial: { + strokeWidth: 1, + fill: "#465fff", + fillOpacity: 1, + r: 4, + }, + hover: { + fill: "#465fff", + fillOpacity: 1, + }, + selected: {}, + selectedHover: {}, + }, + + onRegionTooltipShow: function (event, tooltip, code) { + tooltip.text( + tooltip.text() + (code === "EG" ? " (Hello Russia)" : ""), + true // This second parameter enables HTML + ); + }, + }); + } +}; + +export default initMap; diff --git a/resources/views/api-keys/index.blade.php b/resources/views/api-keys/index.blade.php new file mode 100644 index 0000000..a1097a9 --- /dev/null +++ b/resources/views/api-keys/index.blade.php @@ -0,0 +1,161 @@ +@extends('layouts.app') + +@section('title', 'API Keys') + +@section('content') +
+
+
+

API Keys

+

API keys are used to authenticate requests to the DyDev APP API

+
+ +
+ + @if (session('success')) +
+ {{ session('success') }} +
+ @endif + + @if (session('generated_key')) +
+

New API Key Generated

+

Please copy this key immediately. You won't be able to see + it again!

+
+ + {{ session('generated_key') }} + + +
+
+ @endif + +
+
+
+ Show + + entries +
+ +
+
+ +
+ +
+
+
+ +
+
+ + + + +
+ @include('api-keys.partials.table') +
+ + @include('api-keys.partials.api-docs') +
+ + @include('api-keys.partials.generate-modal') + @include('api-keys.partials.edit-modal') + @include('api-keys.partials.delete-modal') + @include('api-keys.partials.regenerate-modal') + + @push('scripts') + + @endpush +@endsection diff --git a/resources/views/api-keys/partials/api-docs.blade.php b/resources/views/api-keys/partials/api-docs.blade.php new file mode 100644 index 0000000..6532c56 --- /dev/null +++ b/resources/views/api-keys/partials/api-docs.blade.php @@ -0,0 +1,100 @@ +
+
+
+
+

+ + + + API Documentation +

+

Learn how to integrate DyDev APP API into your application.

+
+ + +
+ + +
+
+
+ +
+ +
+

Language

+
+ + + + +
+
+ + +
+
+
+

+ + +

+ +
+ +
+
+
+ +
+
+ + + +

+ Security Note: Your API Key should never be shared or exposed in client-side code (browsers). Always use environment variables and call these endpoints from your backend servers. +

+
+
+
+
+
+
diff --git a/resources/views/api-keys/partials/delete-modal.blade.php b/resources/views/api-keys/partials/delete-modal.blade.php new file mode 100644 index 0000000..a99d355 --- /dev/null +++ b/resources/views/api-keys/partials/delete-modal.blade.php @@ -0,0 +1,43 @@ +
+ +
+
+

+ Delete API Key +

+

+ Are you sure you want to delete this API key? This action cannot be undone. +

+
+ +
+
+ + + +
+

+ Any applications using the key will stop working immediately. +

+
+ +
+ @csrf + @method('DELETE') +
+ + +
+
+
+
+
diff --git a/resources/views/api-keys/partials/edit-modal.blade.php b/resources/views/api-keys/partials/edit-modal.blade.php new file mode 100644 index 0000000..3681e2a --- /dev/null +++ b/resources/views/api-keys/partials/edit-modal.blade.php @@ -0,0 +1,71 @@ +
+ +
+
+

+ Edit API Key +

+

+ Update the name of your API key to keep your applications organized. +

+
+ +
+ @csrf + @method('PUT') +
+
+
+ + +
+
+
+ +
+ + +
+
+
+ + +
+
diff --git a/resources/views/api-keys/partials/generate-modal.blade.php b/resources/views/api-keys/partials/generate-modal.blade.php new file mode 100644 index 0000000..da216fa --- /dev/null +++ b/resources/views/api-keys/partials/generate-modal.blade.php @@ -0,0 +1,42 @@ + +
+
+

+ Generate API Key +

+

+ To enable secure access to the web services, your app requires an API key with permissions for resources. +

+
+ +
+ @csrf +
+
+
+ + +

+ Naming your application makes it easier to recognize your API key in the future. +

+
+
+
+ +
+ + +
+
+
+
diff --git a/resources/views/api-keys/partials/regenerate-modal.blade.php b/resources/views/api-keys/partials/regenerate-modal.blade.php new file mode 100644 index 0000000..89aab74 --- /dev/null +++ b/resources/views/api-keys/partials/regenerate-modal.blade.php @@ -0,0 +1,123 @@ +
+ + +
+ + + + + + +
+
+
+ diff --git a/resources/views/api-keys/partials/table.blade.php b/resources/views/api-keys/partials/table.blade.php new file mode 100644 index 0000000..2a8242d --- /dev/null +++ b/resources/views/api-keys/partials/table.blade.php @@ -0,0 +1,98 @@ +
+ + + + + + + + + + + + + @forelse($apiKeys as $key) + + + + + + + + + @empty + + + + @endforelse + +
NameStatusCreatedLast usedDisable/EnableAction
+
+
+
+
+ +
+ + +
+
+
+ + + + {{ $key->created_at->format('M d, Y') }} + + {{ $key->last_used_at ? $key->last_used_at->format('m/d/Y, h:i:s A') : 'Never' }} + + + +
+ + +
+
+ @if(request('search')) + No API keys found matching "{{ request('search') }}". + @else + No API keys found. Generate one to get started. + @endif +
+
+ +@if($apiKeys->hasPages()) +
+ {{ $apiKeys->links() }} +
+@endif diff --git a/resources/views/components/calender-area.blade.php b/resources/views/components/calender-area.blade.php new file mode 100644 index 0000000..106db82 --- /dev/null +++ b/resources/views/components/calender-area.blade.php @@ -0,0 +1,159 @@ + +
+
+
+
+
+
+ + + +
+ diff --git a/resources/views/components/common/common-grid-shape.blade.php b/resources/views/components/common/common-grid-shape.blade.php new file mode 100644 index 0000000..8392fe6 --- /dev/null +++ b/resources/views/components/common/common-grid-shape.blade.php @@ -0,0 +1,8 @@ +
+
+ grid +
+
+ grid +
+
diff --git a/resources/views/components/common/component-card.blade.php b/resources/views/components/common/component-card.blade.php new file mode 100644 index 0000000..1aef72a --- /dev/null +++ b/resources/views/components/common/component-card.blade.php @@ -0,0 +1,29 @@ +@props([ + 'title', + 'desc' => '', +]) + +
merge(['class' => 'rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]']) }}> + +
+ @isset($header) + {{ $header }} + @else +

+ {{ $title }} +

+ @if($desc) +

+ {{ $desc }} +

+ @endif + @endisset +
+ + +
+
+ {{ $slot }} +
+
+
\ No newline at end of file diff --git a/resources/views/components/common/dropdown-menu.blade.php b/resources/views/components/common/dropdown-menu.blade.php new file mode 100644 index 0000000..38c1f2f --- /dev/null +++ b/resources/views/components/common/dropdown-menu.blade.php @@ -0,0 +1,22 @@ +@props(['items' => ['View More','Delete']]) +
+ + +
+ @forelse($items as $item) + + @empty + {{ $slot }} + @endforelse +
+
\ No newline at end of file diff --git a/resources/views/components/common/page-breadcrumb.blade.php b/resources/views/components/common/page-breadcrumb.blade.php new file mode 100644 index 0000000..4cc7a85 --- /dev/null +++ b/resources/views/components/common/page-breadcrumb.blade.php @@ -0,0 +1,38 @@ +@props(['pageTitle' => 'Page']) + +
+

+ {{ $pageTitle }} +

+ +
diff --git a/resources/views/components/common/preloader.blade.php b/resources/views/components/common/preloader.blade.php new file mode 100644 index 0000000..bbd9d34 --- /dev/null +++ b/resources/views/components/common/preloader.blade.php @@ -0,0 +1,9 @@ +
+
+
diff --git a/resources/views/components/common/table-dropdown.blade.php b/resources/views/components/common/table-dropdown.blade.php new file mode 100644 index 0000000..3dc582e --- /dev/null +++ b/resources/views/components/common/table-dropdown.blade.php @@ -0,0 +1,39 @@ +
+
+ {{ $button }} +
+ +
+
+ +
+
+
diff --git a/resources/views/components/common/theme-toggle.blade.php b/resources/views/components/common/theme-toggle.blade.php new file mode 100644 index 0000000..789b99d --- /dev/null +++ b/resources/views/components/common/theme-toggle.blade.php @@ -0,0 +1,30 @@ + diff --git a/resources/views/components/ecommerce/customer-demographic.blade.php b/resources/views/components/ecommerce/customer-demographic.blade.php new file mode 100644 index 0000000..47c297b --- /dev/null +++ b/resources/views/components/ecommerce/customer-demographic.blade.php @@ -0,0 +1,73 @@ +@props(['countries' => []]) + +@php + $defaultCountries = [ + [ + 'name' => 'USA', + 'flag' => './images/country/country-01.svg', + 'customers' => '2,379', + 'percentage' => 79 + ], + [ + 'name' => 'France', + 'flag' => './images/country/country-02.svg', + 'customers' => '589', + 'percentage' => 23 + ], + ]; + + $countriesList = !empty($countries) ? $countries : $defaultCountries; +@endphp + +
+
+
+

+ Customers Demographic +

+

+ Number of customer based on country +

+
+ + + + +
+ +
+
+
+ +
+ @foreach($countriesList as $country) +
+
+
+ {{ strtolower($country['name']) }} +
+
+

+ {{ $country['name'] }} +

+ + {{ $country['customers'] }} Customers + +
+
+ +
+
+
+
+

+ {{ $country['percentage'] }}% +

+
+
+ @endforeach +
+
diff --git a/resources/views/components/ecommerce/ecommerce-metrics.blade.php b/resources/views/components/ecommerce/ecommerce-metrics.blade.php new file mode 100644 index 0000000..c96ebc2 --- /dev/null +++ b/resources/views/components/ecommerce/ecommerce-metrics.blade.php @@ -0,0 +1,107 @@ +
+
+
+ + + +
+ +
+
+ Customers +

3,782

+
+ + + + + + + 11.01% + +
+
+ +
+
+ + + +
+ +
+
+ Orders +

5,359

+
+ + + + + + + 9.05% + +
+
+
\ No newline at end of file diff --git a/resources/views/components/ecommerce/monthly-sale.blade.php b/resources/views/components/ecommerce/monthly-sale.blade.php new file mode 100644 index 0000000..a6259f1 --- /dev/null +++ b/resources/views/components/ecommerce/monthly-sale.blade.php @@ -0,0 +1,18 @@ +
+
+

+ Monthly Sales +

+ + + + +
+ +
+
+
+
+ + diff --git a/resources/views/components/ecommerce/monthly-target.blade.php b/resources/views/components/ecommerce/monthly-target.blade.php new file mode 100644 index 0000000..9499fd8 --- /dev/null +++ b/resources/views/components/ecommerce/monthly-target.blade.php @@ -0,0 +1,81 @@ +
+
+
+
+

+ Monthly Target +

+

+ Target you’ve set for each month +

+
+ + + + +
+
+ {{-- Chart --}} +
+ +10% +
+

+ You earn $3287 today, it's higher than last month. Keep up your good work! +

+
+ +
+
+

+ Target +

+

+ $20K + + + +

+
+ +
+ +
+

+ Revenue +

+

+ $20K + + + +

+
+ +
+ +
+

+ Today +

+

+ $20K + + + +

+
+
+
+ diff --git a/resources/views/components/ecommerce/recent-orders.blade.php b/resources/views/components/ecommerce/recent-orders.blade.php new file mode 100644 index 0000000..a2bacab --- /dev/null +++ b/resources/views/components/ecommerce/recent-orders.blade.php @@ -0,0 +1,137 @@ +@props(['products' => []]) + +@php + $defaultProducts = [ + [ + 'name' => 'Macbook pro 13"', + 'variants' => 2, + 'image' => '/images/product/product-01.jpg', + 'category' => 'Laptop', + 'price' => '$2399.00', + 'status' => 'Delivered', + ], + [ + 'name' => 'Apple Watch Ultra', + 'variants' => 1, + 'image' => '/images/product/product-02.jpg', + 'category' => 'Watch', + 'price' => '$879.00', + 'status' => 'Pending', + ], + [ + 'name' => 'iPhone 15 Pro Max', + 'variants' => 2, + 'image' => '/images/product/product-03.jpg', + 'category' => 'SmartPhone', + 'price' => '$1869.00', + 'status' => 'Delivered', + ], + [ + 'name' => 'iPad Pro 3rd Gen', + 'variants' => 2, + 'image' => '/images/product/product-04.jpg', + 'category' => 'Electronics', + 'price' => '$1699.00', + 'status' => 'Canceled', + ], + [ + 'name' => 'Airpods Pro 2nd Gen', + 'variants' => 1, + 'image' => '/images/product/product-05.jpg', + 'category' => 'Accessories', + 'price' => '$240.00', + 'status' => 'Delivered', + ], + ]; + + $productsList = !empty($products) ? $products : $defaultProducts; + + // Helper function for status classes + $getStatusClasses = function($status) { + $baseClasses = 'rounded-full px-2 py-0.5 text-theme-xs font-medium'; + + return match($status) { + 'Delivered' => $baseClasses . ' bg-success-50 text-success-600 dark:bg-success-500/15 dark:text-success-500', + 'Pending' => $baseClasses . ' bg-warning-50 text-warning-600 dark:bg-warning-500/15 dark:text-orange-400', + 'Canceled' => $baseClasses . ' bg-error-50 text-error-600 dark:bg-error-500/15 dark:text-error-500', + default => $baseClasses . ' bg-gray-50 text-gray-600 dark:bg-gray-500/15 dark:text-gray-400', + }; + }; +@endphp + +
+
+
+

Recent Orders

+
+ +
+ + + +
+
+ +
+ + + + + + + + + + + @foreach($productsList as $product) + + + + + + + @endforeach + +
+

Products

+
+

Category

+
+

Price

+
+

Status

+
+
+
+ {{ $product['name'] }} +
+
+

+ {{ $product['name'] }} +

+ + {{ $product['variants'] }} Variants + +
+
+
+

{{ $product['category'] }}

+
+

{{ $product['price'] }}

+
+ + {{ $product['status'] }} + +
+
+
\ No newline at end of file diff --git a/resources/views/components/ecommerce/statistics-chart.blade.php b/resources/views/components/ecommerce/statistics-chart.blade.php new file mode 100644 index 0000000..9b54919 --- /dev/null +++ b/resources/views/components/ecommerce/statistics-chart.blade.php @@ -0,0 +1,40 @@ +
+
+
+

+ Statistics +

+

+ Target you’ve set for each month +

+
+ +
+
+ + @php + $options = [ + ['value' => 'overview', 'label' => 'Overview'], + ['value' => 'sales', 'label' => 'Sales'], + ['value' => 'revenue', 'label' => 'Revenue'], + ]; + @endphp + + @foreach ($options as $option) + + @endforeach +
+
+
+
+
+
+
+ diff --git a/resources/views/components/form/date-picker.blade.php b/resources/views/components/form/date-picker.blade.php new file mode 100644 index 0000000..164102b --- /dev/null +++ b/resources/views/components/form/date-picker.blade.php @@ -0,0 +1,60 @@ +@props([ + 'id' => 'datepicker-' . uniqid(), + 'mode' => 'single', // 'single', 'multiple', 'range', 'time' + 'defaultDate' => null, + 'label' => null, + 'placeholder' => 'Select date', + 'name' => null, + 'dateFormat' => 'Y-m-d', +]) + +
{ + this.$dispatch('date-change', { + selectedDates, + dateStr, + instance + }); + } + }); + }); + }, + destroy() { + if (this.flatpickrInstance) { + this.flatpickrInstance.destroy(); + this.flatpickrInstance = null; + } + } +}" x-init="init()" x-destroy="destroy()"> + @if($label) + + @endif + +
+ + + + + + +
+
diff --git a/resources/views/components/form/form-elements/checkbox-component.blade.php b/resources/views/components/form/form-elements/checkbox-component.blade.php new file mode 100644 index 0000000..79ec6c2 --- /dev/null +++ b/resources/views/components/form/form-elements/checkbox-component.blade.php @@ -0,0 +1,69 @@ + +
+
+ +
+ +
+ +
+ +
+ +
+
+
diff --git a/resources/views/components/form/form-elements/default-inputs.blade.php b/resources/views/components/form/form-elements/default-inputs.blade.php new file mode 100644 index 0000000..0dcc15a --- /dev/null +++ b/resources/views/components/form/form-elements/default-inputs.blade.php @@ -0,0 +1,133 @@ + + +
+ + +
+ + +
+ + +
+ + +
+ +
+ + + + + + +
+
+ + +
+ +
+ + + + + + + + + + +
+
+ + +
+ + + +
+ + +
+ +
+ + + + + + +
+
+ + +
+ + +
+ + + + + + + + +
+
+
diff --git a/resources/views/components/form/form-elements/dropzone.blade.php b/resources/views/components/form/form-elements/dropzone.blade.php new file mode 100644 index 0000000..15efbf0 --- /dev/null +++ b/resources/views/components/form/form-elements/dropzone.blade.php @@ -0,0 +1,119 @@ + + +
+
+ + + +
+ +
+
+ + + +
+
+ + +

+ Drag & Drop Files Here + Drop Files Here +

+ + + Drag and drop your PNG, JPG, WebP, SVG images here or browse + + + + Browse File + +
+
+ + +
+
Uploaded Files:
+
    + +
+
+
+
diff --git a/resources/views/components/form/form-elements/file-input-example.blade.php b/resources/views/components/form/form-elements/file-input-example.blade.php new file mode 100644 index 0000000..5321989 --- /dev/null +++ b/resources/views/components/form/form-elements/file-input-example.blade.php @@ -0,0 +1,10 @@ + + +
+ + +
+
diff --git a/resources/views/components/form/form-elements/input-group.blade.php b/resources/views/components/form/form-elements/input-group.blade.php new file mode 100644 index 0000000..613883a --- /dev/null +++ b/resources/views/components/form/form-elements/input-group.blade.php @@ -0,0 +1,144 @@ + + +
+ +
+ + + + + + +
+
+ + +
+ +
+
+ +
+ + + +
+
+ +
+
+ + +
+ +
+
+ +
+ + + +
+
+ +
+
+ + +
+ +
+ + http:// + + +
+
+ + +
+ +
+ + +
+
+
diff --git a/resources/views/components/form/form-elements/input-states.blade.php b/resources/views/components/form/form-elements/input-states.blade.php new file mode 100644 index 0000000..c0c6241 --- /dev/null +++ b/resources/views/components/form/form-elements/input-states.blade.php @@ -0,0 +1,61 @@ + +
+ +
+ +
+ + + + + + +
+ +

+ This is an error message. +

+
+ + +
+ +
+ + + + + + +
+ +

+ This is an success message. +

+
+ + +
+ + +
+
+
diff --git a/resources/views/components/form/form-elements/radio-buttons.blade.php b/resources/views/components/form/form-elements/radio-buttons.blade.php new file mode 100644 index 0000000..f3852eb --- /dev/null +++ b/resources/views/components/form/form-elements/radio-buttons.blade.php @@ -0,0 +1,53 @@ + +
+
+ +
+ +
+ +
+ +
+ +
+
+
diff --git a/resources/views/components/form/form-elements/select-inputs.blade.php b/resources/views/components/form/form-elements/select-inputs.blade.php new file mode 100644 index 0000000..454bb98 --- /dev/null +++ b/resources/views/components/form/form-elements/select-inputs.blade.php @@ -0,0 +1,36 @@ + +
+ +
+ + + + + + +
+
+ + {{-- multiple select --}} + +
diff --git a/resources/views/components/form/form-elements/text-area-inputs.blade.php b/resources/views/components/form/form-elements/text-area-inputs.blade.php new file mode 100644 index 0000000..bc309e7 --- /dev/null +++ b/resources/views/components/form/form-elements/text-area-inputs.blade.php @@ -0,0 +1,31 @@ + + +
+ + +
+ + +
+ + +
+ + +
+ + +

+ Please enter a message in the textarea. +

+
+
diff --git a/resources/views/components/form/form-elements/toggle-switch.blade.php b/resources/views/components/form/form-elements/toggle-switch.blade.php new file mode 100644 index 0000000..9e729dc --- /dev/null +++ b/resources/views/components/form/form-elements/toggle-switch.blade.php @@ -0,0 +1,110 @@ + + +
+
+ +
+ +
+ +
+ +
+ +
+
+ + +
+
+ +
+ +
+ +
+ +
+ +
+
+
diff --git a/resources/views/components/form/input/radio.blade.php b/resources/views/components/form/input/radio.blade.php new file mode 100644 index 0000000..3c476ba --- /dev/null +++ b/resources/views/components/form/input/radio.blade.php @@ -0,0 +1,43 @@ +@props([ + 'id', + 'name', + 'value', + 'checked' => false, + 'label', + 'disabled' => false, +]) + + \ No newline at end of file diff --git a/resources/views/components/form/select/multiple-select.blade.php b/resources/views/components/form/select/multiple-select.blade.php new file mode 100644 index 0000000..495edf5 --- /dev/null +++ b/resources/views/components/form/select/multiple-select.blade.php @@ -0,0 +1,80 @@ +
+ + +
+ + + + +
+ +
+ + + + + Select options... + +
+ + +
+ + + +
+
+ + +
+
+ +
+
+
+
diff --git a/resources/views/components/header/notification-dropdown.blade.php b/resources/views/components/header/notification-dropdown.blade.php new file mode 100644 index 0000000..6e8f198 --- /dev/null +++ b/resources/views/components/header/notification-dropdown.blade.php @@ -0,0 +1,222 @@ +{{-- Notification Dropdown Component --}} +
+ + + + + + +
diff --git a/resources/views/components/header/user-dropdown.blade.php b/resources/views/components/header/user-dropdown.blade.php new file mode 100644 index 0000000..6e4be8e --- /dev/null +++ b/resources/views/components/header/user-dropdown.blade.php @@ -0,0 +1,129 @@ +
+ + + + + + +
diff --git a/resources/views/components/profile/address-card.blade.php b/resources/views/components/profile/address-card.blade.php new file mode 100644 index 0000000..7c4a801 --- /dev/null +++ b/resources/views/components/profile/address-card.blade.php @@ -0,0 +1,56 @@ +@props(['user']) + +
+
+
+
+

Address

+ +
+
+

Country

+

{{ $user->country ?? 'Not set' }}

+
+ +
+

City/State

+

+ {{ $user->city_state ?? 'Not set' }} +

+
+ +
+

+ Postal Code +

+

{{ $user->postal_code ?? 'Not set' }}

+
+ +
+

TAX ID

+

{{ $user->tax_id ?? 'Not set' }}

+
+
+
+ + +
+
+ + +
+ diff --git a/resources/views/components/profile/address-modal.blade.php b/resources/views/components/profile/address-modal.blade.php new file mode 100644 index 0000000..02a29a4 --- /dev/null +++ b/resources/views/components/profile/address-modal.blade.php @@ -0,0 +1,66 @@ +@props(['user', 'show']) + + +
+
+

+ Edit Address +

+

+ Update your details to keep your profile up-to-date. +

+
+
+ @csrf + @method('PATCH') + + +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+ + +
+
+
+
diff --git a/resources/views/components/profile/avatar-modal.blade.php b/resources/views/components/profile/avatar-modal.blade.php new file mode 100644 index 0000000..747118a --- /dev/null +++ b/resources/views/components/profile/avatar-modal.blade.php @@ -0,0 +1,120 @@ +@props(['user', 'show']) + + +
+
+

+ Update Profile Picture +

+

+ Upload a new photo and crop it to fit. +

+
+ + +
+ + + + + +

+ Drag and drop your image here, or +

+ + +
+ + +
+
+ +
+
+ +
+ + +
+
+
diff --git a/resources/views/components/profile/edit-modal.blade.php b/resources/views/components/profile/edit-modal.blade.php new file mode 100644 index 0000000..dc75813 --- /dev/null +++ b/resources/views/components/profile/edit-modal.blade.php @@ -0,0 +1,119 @@ +@props(['user', 'show']) + + +
+
+

+ Edit Personal Information +

+

+ Update your details to keep your profile up-to-date. +

+
+
+ @csrf + @method('PATCH') + + +
+
+
+ Social Links +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+
+ Personal Information +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+
+ + +
+
+
+
diff --git a/resources/views/components/profile/personal-info-card.blade.php b/resources/views/components/profile/personal-info-card.blade.php new file mode 100644 index 0000000..7d15251 --- /dev/null +++ b/resources/views/components/profile/personal-info-card.blade.php @@ -0,0 +1,67 @@ +@props(['user']) + +
+
+
+
+

+ Personal Information +

+ +
+
+

First Name

+

{{ $user->first_name ?? 'Not set' }}

+
+ +
+

Last Name

+

{{ $user->last_name ?? 'Not set' }}

+
+ +
+

+ Email address +

+

+ {{ $user->email }} + @if($user->hasVerifiedEmail()) + + Verified + + @else + + Unverified + + @endif +

+
+ +
+

Phone

+

{{ $user->phone ?? 'Not set' }}

+
+ +
+

Bio

+

{{ $user->bio ?? 'Not set' }}

+
+
+
+ + +
+
+
diff --git a/resources/views/components/profile/profile-card.blade.php b/resources/views/components/profile/profile-card.blade.php new file mode 100644 index 0000000..f7ffa01 --- /dev/null +++ b/resources/views/components/profile/profile-card.blade.php @@ -0,0 +1,96 @@ +@props(['user']) + +
+
+
+
+
+ user + +
+
+

+ {{ $user->name }} +

+
+

+ {{ $user->bio ?? 'No bio available' }} +

+ +

+ {{ $user->city_state ?? '' }}{{ $user->country ? ', ' . $user->country : '' }} +

+
+
+
+ + + + + + + +
+
+ + +
+
+ + + + + + +
diff --git a/resources/views/components/settings/delete-account-modal.blade.php b/resources/views/components/settings/delete-account-modal.blade.php new file mode 100644 index 0000000..821c91a --- /dev/null +++ b/resources/views/components/settings/delete-account-modal.blade.php @@ -0,0 +1,35 @@ +@props(['show', 'deleteConfirmation']) + + +
+
+
+ +
+

Delete Account

+
+

+ This action is irreversible. All your data will be permanently removed. + To confirm, please type Yes I will delete my account below. +

+ +
+ @csrf + @method('DELETE') +
+ +
+ +
+ + +
+
+
+
diff --git a/resources/views/components/settings/password-modal.blade.php b/resources/views/components/settings/password-modal.blade.php new file mode 100644 index 0000000..67db365 --- /dev/null +++ b/resources/views/components/settings/password-modal.blade.php @@ -0,0 +1,21 @@ +@props(['show']) + + +
+
+ +
+

Update Password?

+

+ Are you sure you want to change your password? You will need to use the new password for your next login. +

+
+ + +
+
+
diff --git a/resources/views/components/settings/social-modal.blade.php b/resources/views/components/settings/social-modal.blade.php new file mode 100644 index 0000000..cdb5cfb --- /dev/null +++ b/resources/views/components/settings/social-modal.blade.php @@ -0,0 +1,24 @@ +@props(['show', 'socialProvider', 'submitAction']) + + +
+
+ +
+

+ ? +

+

+ Are you sure you want to your account? +

+
+ + +
+
+
diff --git a/resources/views/components/tables/basic-tables/basic-tables-five.blade.php b/resources/views/components/tables/basic-tables/basic-tables-five.blade.php new file mode 100644 index 0000000..c247b14 --- /dev/null +++ b/resources/views/components/tables/basic-tables/basic-tables-five.blade.php @@ -0,0 +1,197 @@ + +@php + $orders = [ + [ + 'product' => 'TailGrids', + 'category' => 'UI Kit', + 'countryFlag' => '/images/country/country-01.svg', + 'country' => 'USA', + 'cr' => 'Dashboard', + 'value' => '$12,499', + ], + [ + 'product' => 'GrayGrids', + 'category' => 'Templates', + 'countryFlag' => '/images/country/country-03.svg', + 'country' => 'UK', + 'cr' => 'Dashboard', + 'value' => '$5,498', + ], + [ + 'product' => 'Uideck', + 'category' => 'Templates', + 'countryFlag' => '/images/country/country-04.svg', + 'country' => 'Canada', + 'cr' => 'Dashboard', + 'value' => '$4,521', + ], + [ + 'product' => 'FormBold', + 'category' => 'SaaS', + 'countryFlag' => '/images/country/country-05.svg', + 'country' => 'Australia', + 'cr' => 'Dashboard', + 'value' => '$13,843', + ], + [ + 'product' => 'NextAdmin', + 'category' => 'Dashboard', + 'countryFlag' => '/images/country/country-06.svg', + 'country' => 'Germany', + 'cr' => 'Dashboard', + 'value' => '$7,523', + ], + [ + 'product' => 'Form Builder', + 'category' => 'SaaS', + 'countryFlag' => '/images/country/country-07.svg', + 'country' => 'France', + 'cr' => 'Dashboard', + 'value' => '$1,377', + ], + [ + 'product' => 'AyroUI', + 'category' => 'UI Kit', + 'countryFlag' => '/images/country/country-08.svg', + 'country' => 'Japan', + 'cr' => 'Dashboard', + 'value' => '$599,00', + ], + ]; +@endphp + +
+
+
+

Recent Orders

+
+ +
+ + + +
+
+ +
+ + + + + + + + + + + + + + + + @foreach ($orders as $order) + + + + + + + + @endforeach + + +
+
+

Products

+
+
+
+

Category

+
+
+
+

Country

+
+
+
+

CR

+
+
+
+

Value

+
+
+
+

+ {{ $order['product'] }} +

+
+
+
+

{{ $order['category'] }}

+
+
+
+
+ {{ $order['country'] }} +
+
+
+
+

+ {{ $order['cr'] }} +

+
+
+
+

{{ $order['value'] }}

+
+
+
+
\ No newline at end of file diff --git a/resources/views/components/tables/basic-tables/basic-tables-four.blade.php b/resources/views/components/tables/basic-tables/basic-tables-four.blade.php new file mode 100644 index 0000000..dba3463 --- /dev/null +++ b/resources/views/components/tables/basic-tables/basic-tables-four.blade.php @@ -0,0 +1,143 @@ +@php + $campaigns = [ + [ + 'creator' => ['name' => 'Wilson Gouse', 'imageUrl' => '/images/user/user-01.jpg'], + 'brand' => ['name' => 'Brand 1', 'logo' => '/images/brand/brand-01.svg'], + 'title' => 'Grow your brand by...', + 'type' => 'Ads campaign', + 'status' => 'Success', + ], + [ + 'creator' => ['name' => 'Terry Franci', 'imageUrl' => '/images/user/user-02.jpg'], + 'brand' => ['name' => 'Brand 2', 'logo' => '/images/brand/brand-02.svg'], + 'title' => 'Make Better Ideas...', + 'type' => 'Ads campaign', + 'status' => 'Pending', + ], + [ + 'creator' => ['name' => 'Alena Franci', 'imageUrl' => '/images/user/user-03.jpg'], + 'brand' => ['name' => 'Brand 3', 'logo' => '/images/brand/brand-03.svg'], + 'title' => 'Increase your website tra...', + 'type' => 'Ads campaign', + 'status' => 'Success', + ], + [ + 'creator' => ['name' => 'Jocelyn Kenter', 'imageUrl' => '/images/user/user-04.jpg'], + 'brand' => ['name' => 'Brand 4', 'logo' => '/images/brand/brand-04.svg'], + 'title' => 'Digital Marketing that...', + 'type' => 'Ads campaign', + 'status' => 'Failed', + ], + [ + 'creator' => ['name' => 'Brandon Philips', 'imageUrl' => '/images/user/user-05.jpg'], + 'brand' => ['name' => 'Brand 2', 'logo' => '/images/brand/brand-02.svg'], + 'title' => 'Self branding', + 'type' => 'Ads campaign', + 'status' => 'Success', + ], + [ + 'creator' => ['name' => 'James Lipshutz', 'imageUrl' => '/images/user/user-06.jpg'], + 'brand' => ['name' => 'Brand 3', 'logo' => '/images/brand/brand-03.svg'], + 'title' => 'Increase your website tra...', + 'type' => 'Ads campaign', + 'status' => 'Success', + ], + ]; + + function getStatusClass($status) { + $baseClasses = 'rounded-full px-2 text-theme-xs font-medium'; + switch ($status) { + case 'Success': + return "$baseClasses bg-success-50 text-success-600 dark:bg-success-500/15 dark:text-success-500"; + case 'Pending': + return "$baseClasses bg-warning-50 text-warning-600 dark:bg-warning-500/15 dark:text-orange-400"; + case 'Failed': + return "$baseClasses bg-error-50 text-error-600 dark:bg-error-500/15 dark:text-error-500"; + default: + return $baseClasses; + } + } +@endphp + +
+
+
+

Featured Campaigns

+
+ +
+
+ +
+ + + + + + + + + + + + + + @foreach ($campaigns as $campaign) + + + + + + @endforeach + + +
+
+

Creator

+
+
+
+

Campaign

+
+
+
+

Status

+
+
+
+
+ {{ $campaign['creator']['name'] }} +
+
+

+ {{ $campaign['creator']['name'] }} +

+
+
+
+
+
+
+ {{ $campaign['brand']['name'] }} +
+
+

+ {{ $campaign['title'] }} +

+ + {{ $campaign['type'] }} + +
+
+
+
+
+ + {{ $campaign['status'] }} + +
+
+
+
\ No newline at end of file diff --git a/resources/views/components/tables/basic-tables/basic-tables-one.blade.php b/resources/views/components/tables/basic-tables/basic-tables-one.blade.php new file mode 100644 index 0000000..b08f36a --- /dev/null +++ b/resources/views/components/tables/basic-tables/basic-tables-one.blade.php @@ -0,0 +1,170 @@ +
+
+
+ + + + + + + + + + + + + +
+

+ User +

+
+

+ Project Name +

+
+

+ Team +

+
+

+ Status +

+
+

+ Budget +

+
+
+
+
\ No newline at end of file diff --git a/resources/views/components/tables/basic-tables/basic-tables-three.blade.php b/resources/views/components/tables/basic-tables/basic-tables-three.blade.php new file mode 100644 index 0000000..321ede9 --- /dev/null +++ b/resources/views/components/tables/basic-tables/basic-tables-three.blade.php @@ -0,0 +1,313 @@ +
+
+ +
+
+

Latest Transactions

+
+
+
+
+ + +
+
+
+
+ + +
+
+ + + + + + + + + + + + + + +
NameDatePriceCategoryStatus + Actions +
+
+
+ + +
+
+ + + + Page of + + + + + +
+
+
+
\ No newline at end of file diff --git a/resources/views/components/tables/basic-tables/basic-tables-two.blade.php b/resources/views/components/tables/basic-tables/basic-tables-two.blade.php new file mode 100644 index 0000000..2bb0c61 --- /dev/null +++ b/resources/views/components/tables/basic-tables/basic-tables-two.blade.php @@ -0,0 +1,207 @@ +
+
+ +
+
+

+ Recent Orders +

+
+
+ + +
+
+ + +
+ + + + + + + + + + + + + + + +
+
+
+ + + +
+ Deal ID +
+
CustomerProduct/ServiceDeal ValueClose DateStatusAction
+
+
+
\ No newline at end of file diff --git a/resources/views/components/ui/alert.blade.php b/resources/views/components/ui/alert.blade.php new file mode 100644 index 0000000..10e4b75 --- /dev/null +++ b/resources/views/components/ui/alert.blade.php @@ -0,0 +1,82 @@ +{{-- resources/views/components/alert.blade.php --}} + +@props([ + 'variant' => 'info', + 'title' => '', + 'message' => '', + 'showLink' => false, + 'linkHref' => '#', + 'linkText' => 'Learn more' +]) + +@php + $variantClasses = [ + 'success' => [ + 'container' => 'border-green-500 bg-green-50 dark:border-green-500/30 dark:bg-green-500/15', + 'icon' => 'text-green-500', + ], + 'error' => [ + 'container' => 'border-red-500 bg-red-50 dark:border-red-500/30 dark:bg-red-500/15', + 'icon' => 'text-red-500', + ], + 'warning' => [ + 'container' => 'border-yellow-500 bg-yellow-50 dark:border-yellow-500/30 dark:bg-yellow-500/15', + 'icon' => 'text-yellow-500', + ], + 'info' => [ + 'container' => 'border-blue-500 bg-blue-50 dark:border-blue-500/30 dark:bg-blue-500/15', + 'icon' => 'text-blue-500', + ], + ]; + + $icons = [ + 'success' => ' + + ', + 'error' => ' + + ', + 'warning' => ' + + ', + 'info' => ' + + ', + ]; + + $containerClass = $variantClasses[$variant]['container'] ?? $variantClasses['info']['container']; + $iconClass = $variantClasses[$variant]['icon'] ?? $variantClasses['info']['icon']; + $icon = $icons[$variant] ?? $icons['info']; +@endphp + +
+
+
+ {!! $icon !!} +
+ +
+ @if($title) +

+ {{ $title }} +

+ @endif + + @if($message) +

{{ $message }}

+ @endif + + @if($showLink) + + {{ $linkText }} + + @endif + + {{-- Slot for custom content --}} + {{ $slot }} +
+
+
\ No newline at end of file diff --git a/resources/views/components/ui/avatar.blade.php b/resources/views/components/ui/avatar.blade.php new file mode 100644 index 0000000..9fcbaa2 --- /dev/null +++ b/resources/views/components/ui/avatar.blade.php @@ -0,0 +1,49 @@ + +@props([ + 'src' => '', + 'alt' => 'User Avatar', + 'size' => 'medium', + 'status' => 'none', +]) + +@php + $sizeClasses = [ + 'xsmall' => 'h-6 w-6 max-w-6', + 'small' => 'h-8 w-8 max-w-8', + 'medium' => 'h-10 w-10 max-w-10', + 'large' => 'h-12 w-12 max-w-12', + 'xlarge' => 'h-14 w-14 max-w-14', + 'xxlarge' => 'h-16 w-16 max-w-16', + ]; + + $statusSizeClasses = [ + 'xsmall' => 'h-1.5 w-1.5 max-w-1.5', + 'small' => 'h-2 w-2 max-w-2', + 'medium' => 'h-2.5 w-2.5 max-w-2.5', + 'large' => 'h-3 w-3 max-w-3', + 'xlarge' => 'h-3.5 w-3.5 max-w-3.5', + 'xxlarge' => 'h-4 w-4 max-w-4', + ]; + + $statusColorClasses = [ + 'online' => 'bg-green-500', + 'offline' => 'bg-red-400', + 'busy' => 'bg-yellow-500', + ]; + + $sizeClass = $sizeClasses[$size] ?? $sizeClasses['medium']; + $statusSizeClass = $statusSizeClasses[$size] ?? $statusSizeClasses['medium']; + $statusColorClass = $statusColorClasses[$status] ?? ''; +@endphp + +
+ {{ $alt }} + + @if($status !== 'none') + + @endif +
\ No newline at end of file diff --git a/resources/views/components/ui/badge.blade.php b/resources/views/components/ui/badge.blade.php new file mode 100644 index 0000000..c2bd54c --- /dev/null +++ b/resources/views/components/ui/badge.blade.php @@ -0,0 +1,53 @@ + +@props([ + 'variant' => 'light', + 'size' => 'md', + 'color' => 'primary', + 'startIcon' => null, + 'endIcon' => null, +]) + +@php + $baseStyles = 'inline-flex items-center px-2.5 py-0.5 justify-center gap-1 rounded-full font-medium capitalize'; + + $sizeStyles = [ + 'sm' => 'text-xs', + 'md' => 'text-sm', + ]; + + $variants = [ + 'light' => [ + 'primary' => 'bg-blue-50 text-blue-500 dark:bg-blue-500/15 dark:text-blue-400', + 'success' => 'bg-green-50 text-green-600 dark:bg-green-500/15 dark:text-green-500', + 'error' => 'bg-red-50 text-red-600 dark:bg-red-500/15 dark:text-red-500', + 'warning' => 'bg-yellow-50 text-yellow-600 dark:bg-yellow-500/15 dark:text-orange-400', + 'info' => 'bg-sky-50 text-sky-500 dark:bg-sky-500/15 dark:text-sky-500', + 'light' => 'bg-gray-100 text-gray-700 dark:bg-white/5 dark:text-white/80', + 'dark' => 'bg-gray-500 text-white dark:bg-white/5 dark:text-white', + ], + 'solid' => [ + 'primary' => 'bg-blue-500 text-white dark:text-white', + 'success' => 'bg-green-500 text-white dark:text-white', + 'error' => 'bg-red-500 text-white dark:text-white', + 'warning' => 'bg-yellow-500 text-white dark:text-white', + 'info' => 'bg-sky-500 text-white dark:text-white', + 'light' => 'bg-gray-400 dark:bg-white/5 text-white dark:text-white/80', + 'dark' => 'bg-gray-700 text-white dark:text-white', + ], + ]; + + $sizeClass = $sizeStyles[$size] ?? $sizeStyles['md']; + $colorStyles = $variants[$variant][$color] ?? $variants['light']['primary']; +@endphp + + + @if($startIcon) + {!! $startIcon !!} + @endif + + {{ $slot }} + + @if($endIcon) + {!! $endIcon !!} + @endif + diff --git a/resources/views/components/ui/button.blade.php b/resources/views/components/ui/button.blade.php new file mode 100644 index 0000000..2838e10 --- /dev/null +++ b/resources/views/components/ui/button.blade.php @@ -0,0 +1,61 @@ +@props([ + 'size' => 'md', + 'variant' => 'primary', + 'startIcon' => null, + 'endIcon' => null, + 'className' => '', + 'disabled' => false, +]) + +@php + // Base classes + $base = 'inline-flex items-center justify-center font-medium gap-2 rounded-lg transition'; + + // Size map + $sizeMap = [ + 'sm' => 'px-4 py-3 text-sm', + 'md' => 'px-5 py-3.5 text-sm', + ]; + $sizeClass = $sizeMap[$size] ?? $sizeMap['md']; + + // Variant map + $variantMap = [ + 'primary' => 'bg-brand-500 text-white shadow-theme-xs hover:bg-brand-600 disabled:bg-brand-300', + 'outline' => 'bg-white text-gray-700 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-400 dark:ring-gray-700 dark:hover:bg-white/[0.03] dark:hover:text-gray-300', + ]; + $variantClass = $variantMap[$variant] ?? $variantMap['primary']; + + // disabled classes + $disabledClass = $disabled ? 'cursor-not-allowed opacity-50' : ''; + + // final classes (merge user className too) + $classes = trim("{$base} {$sizeClass} {$variantClass} {$className} {$disabledClass}"); +@endphp + + diff --git a/resources/views/components/ui/flash-message.blade.php b/resources/views/components/ui/flash-message.blade.php new file mode 100644 index 0000000..ba2e3b5 --- /dev/null +++ b/resources/views/components/ui/flash-message.blade.php @@ -0,0 +1,92 @@ +@if (session()->has('success') || session()->has('error') || session()->has('warning') || session()->has('info') || session()->has('status')) + @php + $variant = 'info'; + $message = ''; + $title = ''; + + if (session()->has('success')) { + $variant = 'success'; + $message = session('success'); + $title = 'Success'; + } elseif (session()->has('error')) { + $variant = 'error'; + $message = session('error'); + $title = 'Error'; + } elseif (session()->has('warning')) { + $variant = 'warning'; + $message = session('warning'); + $title = 'Warning'; + } elseif (session()->has('info')) { + $variant = 'info'; + $message = session('info'); + $title = 'Information'; + } elseif (session()->has('status')) { + $variant = 'success'; // Default status to success/info style + $message = session('status'); + $title = 'Notification'; + } + + // Copy styles from ui.alert + $variantClasses = [ + 'success' => [ + 'container' => 'border-green-500 bg-green-50 dark:border-green-500/30 dark:bg-green-500/15', + 'icon' => 'text-green-500', + ], + 'error' => [ + 'container' => 'border-red-500 bg-red-50 dark:border-red-500/30 dark:bg-red-500/15', + 'icon' => 'text-red-500', + ], + 'warning' => [ + 'container' => 'border-yellow-500 bg-yellow-50 dark:border-yellow-500/30 dark:bg-yellow-500/15', + 'icon' => 'text-yellow-500', + ], + 'info' => [ + 'container' => 'border-blue-500 bg-blue-50 dark:border-blue-500/30 dark:bg-blue-500/15', + 'icon' => 'text-blue-500', + ], + ]; + + $icons = [ + 'success' => '', + 'error' => '', + 'warning' => '', + 'info' => '', + ]; + + $containerClass = $variantClasses[$variant]['container'] ?? $variantClasses['info']['container']; + $iconClass = $variantClasses[$variant]['icon'] ?? $variantClasses['info']['icon']; + $icon = $icons[$variant] ?? $icons['info']; + @endphp + + +@endif diff --git a/resources/views/components/ui/modal.blade.php b/resources/views/components/ui/modal.blade.php new file mode 100644 index 0000000..9ec1fb9 --- /dev/null +++ b/resources/views/components/ui/modal.blade.php @@ -0,0 +1,78 @@ +@props([ + 'isOpen' => false, + 'showCloseButton' => true, + 'containerClass' => 'max-w-[500px]', +]) + +
merge([ + 'x-data' => '{ + open: ' . ($isOpen ? 'true' : 'false') . ', + init() { + this.$watch(\'open\', value => { + if (value) { + document.body.style.overflow = \'hidden\'; + } else { + document.body.style.overflow = \'unset\'; + } + }); + } + }' +]) }} +x-modelable="open" +x-show="open" +x-cloak +@keydown.escape.window="open = false" +class="relative z-99999" aria-labelledby="modal-title" role="dialog" aria-modal="true"> + + + + + +
+
+ + +
+ + + @if ($showCloseButton) + + @endif + + +
+ {{ $slot }} +
+
+
+
+
+ + diff --git a/resources/views/components/ui/youtube-embed.blade.php b/resources/views/components/ui/youtube-embed.blade.php new file mode 100644 index 0000000..2aab363 --- /dev/null +++ b/resources/views/components/ui/youtube-embed.blade.php @@ -0,0 +1,29 @@ + +@props([ + 'videoId' => '', + 'aspectRatio' => '16:9', + 'title' => 'YouTube video', + 'className' => '' +]) + +@php + $aspectRatioClasses = [ + '16:9' => 'aspect-video', + '4:3' => 'aspect-4/3', + '21:9' => 'aspect-21/9', + '1:1' => 'aspect-square', + ]; + + $aspectRatioClass = $aspectRatioClasses[$aspectRatio] ?? $aspectRatioClasses['16:9']; +@endphp + +
+ +
\ No newline at end of file diff --git a/resources/views/errors/403.blade.php b/resources/views/errors/403.blade.php new file mode 100644 index 0000000..a4ded46 --- /dev/null +++ b/resources/views/errors/403.blade.php @@ -0,0 +1,33 @@ +@extends('layouts.fullscreen-layout') + +@section('content') +@php + $currentYear = date('Y'); +@endphp +
+ {{-- common grid shape --}} + + +
+

+ FORBIDDEN +

+ + 403 + + +

+ You don't have permission to access this page. +

+ + + Back to Home Page + +
+ +

+ © {{ $currentYear }} - {{ config('app.name') }} +

+
+@endsection diff --git a/resources/views/errors/404.blade.php b/resources/views/errors/404.blade.php new file mode 100644 index 0000000..accaf0e --- /dev/null +++ b/resources/views/errors/404.blade.php @@ -0,0 +1,33 @@ +@extends('layouts.fullscreen-layout') + +@section('content') +@php + $currentYear = date('Y'); +@endphp +
+ {{-- common grid shape --}} + + +
+

+ ERROR +

+ + 404 + + +

+ We can't seem to find the page you are looking for! +

+ + + Back to Home Page + +
+ +

+ © {{ $currentYear }} - {{ config('app.name') }} +

+
+@endsection diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php new file mode 100644 index 0000000..c00747b --- /dev/null +++ b/resources/views/errors/500.blade.php @@ -0,0 +1,30 @@ +@extends('layouts.fullscreen-layout') + +@section('content') +@php + $currentYear = date('Y'); +@endphp +
+ +
+

+ ERROR +

+ + 500 + + +

+ Something went wrong on our end. Please try again later. +

+ + + Back to Home Page + +
+

+ © {{ $currentYear }} - {{ config('app.name') }} +

+
+@endsection diff --git a/resources/views/errors/503.blade.php b/resources/views/errors/503.blade.php new file mode 100644 index 0000000..48c6ce0 --- /dev/null +++ b/resources/views/errors/503.blade.php @@ -0,0 +1,30 @@ +@extends('layouts.fullscreen-layout') + +@section('content') +@php + $currentYear = date('Y'); +@endphp +
+ +
+

+ MAINTENANCE +

+ + 503 + + +

+ The service is currently unavailable. We are performing maintenance. +

+ + + Refresh Page + +
+

+ © {{ $currentYear }} - {{ config('app.name') }} +

+
+@endsection diff --git a/resources/views/layouts/app-header.blade.php b/resources/views/layouts/app-header.blade.php new file mode 100644 index 0000000..d5c6f5a --- /dev/null +++ b/resources/views/layouts/app-header.blade.php @@ -0,0 +1,123 @@ +
+
+
+ + + + + + + + + + Logo + + + + + + + + +
+ + +
+
+ + + + + +
+ + + +
+
+
diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php new file mode 100644 index 0000000..7e38075 --- /dev/null +++ b/resources/views/layouts/app.blade.php @@ -0,0 +1,144 @@ + + + + + + + + + {{ $title ?? 'Dashboard' }} | {{ config('app.name') }} + + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + + {{-- --}} + + + + + + + + + + + + + + + {{-- Flash Message Component --}} + + + {{-- preloader --}} + + {{-- preloader end --}} + +
+ @include('layouts.backdrop') + @include('layouts.sidebar') + +
+ + @include('layouts.app-header') + +
+ @yield('content') +
+
+ +
+ + + +@stack('scripts') + + diff --git a/resources/views/layouts/backdrop.blade.php b/resources/views/layouts/backdrop.blade.php new file mode 100644 index 0000000..5cd9728 --- /dev/null +++ b/resources/views/layouts/backdrop.blade.php @@ -0,0 +1,12 @@ +{{--
+sidebarToggle ? 'block xl:hidden' : 'hidden' +
--}} + +
diff --git a/resources/views/layouts/fullscreen-layout.blade.php b/resources/views/layouts/fullscreen-layout.blade.php new file mode 100644 index 0000000..0bb341a --- /dev/null +++ b/resources/views/layouts/fullscreen-layout.blade.php @@ -0,0 +1,120 @@ + + + + + + + + + {{ $title ?? 'Dashboard' }} | {{ config('app.name') }} + + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + + {{-- --}} + + + + + + + + + + + {{-- Flash Message Component --}} + + + {{-- preloader --}} + + {{-- preloader end --}} + + @yield('content') + + + +@stack('scripts') + + diff --git a/resources/views/layouts/sidebar-widget.blade.php b/resources/views/layouts/sidebar-widget.blade.php new file mode 100644 index 0000000..409257e --- /dev/null +++ b/resources/views/layouts/sidebar-widget.blade.php @@ -0,0 +1,12 @@ +
+

+ #1 Tailwind CSS Dashboard +

+

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

+ + Purchase Plan + +
diff --git a/resources/views/layouts/sidebar.blade.php b/resources/views/layouts/sidebar.blade.php new file mode 100644 index 0000000..c1a6458 --- /dev/null +++ b/resources/views/layouts/sidebar.blade.php @@ -0,0 +1,217 @@ + +@php + use App\Helpers\MenuHelper; + $menuGroups = MenuHelper::getMenuGroups(); + + // Get current path + $currentPath = request()->path(); +@endphp + + + + +
diff --git a/resources/views/pages/admin/root-ca/index.blade.php b/resources/views/pages/admin/root-ca/index.blade.php new file mode 100644 index 0000000..c2f5d75 --- /dev/null +++ b/resources/views/pages/admin/root-ca/index.blade.php @@ -0,0 +1,72 @@ +@extends('layouts.app') + +@section('content') +
+
+

Root CA Management

+

Manage your Root and Intermediate Certificates.

+
+
+ +
+
+ + + + + + + + + + + + + + @forelse($certificates as $cert) + + + + + + + + + + @empty + + + + @endforelse + +
TypeCommon NameSerial NumberValid FromValid ToStatusActions
+ {{ ucfirst(str_replace('_', ' ', $cert->ca_type)) }} + + {{ $cert->common_name }} + + {{ $cert->serial_number }} + + {{ \Carbon\Carbon::parse($cert->valid_from)->format('Y-m-d H:i') }} + + {{ \Carbon\Carbon::parse($cert->valid_to)->format('Y-m-d H:i') }} + + @if($cert->status === 'valid') + Valid + @else + Expired + @endif + +
+ @csrf + + +
+
+ No Root CA certificates found. + (Run Setup first) +
+
+
+@endsection diff --git a/resources/views/pages/admin/users/index.blade.php b/resources/views/pages/admin/users/index.blade.php new file mode 100644 index 0000000..f2d88fc --- /dev/null +++ b/resources/views/pages/admin/users/index.blade.php @@ -0,0 +1,213 @@ +@extends('layouts.app') + +@section('content') +
+ +
+

+ User Management +

+ + +
+ + +
+
+ + + + + + + + + + + + @foreach ($users as $user) + + + + + + + + @endforeach + +
+

+ User +

+
+

+ Role +

+
+

+ Joined Date +

+
+

+ Status +

+
+

+ Actions +

+
+
+
+ @if($user->avatar) + {{ $user->name }} + @else + {{ substr($user->first_name ?? $user->name, 0, 2) }} + @endif +
+
+ + {{ $user->name ?? $user->first_name . ' ' . $user->last_name }} + + + {{ $user->email }} + +
+
+
+ + {{ $user->role ? ucfirst($user->role->name) : 'User' }} + + +

+ {{ $user->created_at->format('M d, Y') }} + {{ $user->created_at->diffForHumans() }} +

+
+
+ + {{ ucfirst($user->status) }} + + + {{ $user->hasVerifiedEmail() ? 'Verified' : 'Unverified' }} + +
+
+
+ @if($user->id !== auth()->id()) +
+ @csrf + @method('PATCH') + +
+ + + @endif + + @if(!$user->hasVerifiedEmail()) +
+ @csrf + +
+ @endif + +
+ @csrf + +
+ + @if($user->id !== auth()->id()) +
+ @csrf + @method('DELETE') + +
+ @endif +
+
+
+ + + @if($users->hasPages()) +
+ {{ $users->links() }} +
+ @endif +
+ + + +
+@endsection diff --git a/resources/views/pages/auth/forgot-password.blade.php b/resources/views/pages/auth/forgot-password.blade.php new file mode 100644 index 0000000..42a95ce --- /dev/null +++ b/resources/views/pages/auth/forgot-password.blade.php @@ -0,0 +1,97 @@ +@extends('layouts.fullscreen-layout') + +@section('content') +
+
+ +
+ +
+
+
+

+ Forgot Password +

+

+ Enter your email address below. We'll look for your account and send you a password reset email. +

+
+ + @if (session('status')) + + @endif + +
+ @csrf +
+ +
+ + + @error('email') +

{{ $message }}

+ @enderror +
+ + +
+ +
+
+
+ + +
+
+
+ + + +
+ +
+
+
+@endsection diff --git a/resources/views/pages/auth/reset-password.blade.php b/resources/views/pages/auth/reset-password.blade.php new file mode 100644 index 0000000..a7ee39e --- /dev/null +++ b/resources/views/pages/auth/reset-password.blade.php @@ -0,0 +1,129 @@ +@extends('layouts.fullscreen-layout') + +@section('content') +
+
+ +
+ +
+
+
+

+ Reset Password +

+

+ Enter your new password details below. +

+
+ +
+ @csrf + + +
+ +
+ + + @error('email') +

{{ $message }}

+ @enderror +
+ + +
+ +
+ + + + + + + + + +
+ @error('password') +

{{ $message }}

+ @enderror +
+ + +
+ +
+ +
+
+ + +
+ +
+
+
+
+
+
+ + + +
+ +
+
+
+@endsection diff --git a/resources/views/pages/auth/setup-password.blade.php b/resources/views/pages/auth/setup-password.blade.php new file mode 100644 index 0000000..12c7e3f --- /dev/null +++ b/resources/views/pages/auth/setup-password.blade.php @@ -0,0 +1,142 @@ +@extends('layouts.fullscreen-layout') + +@section('content') +
+
+ +
+
+
+
+

+ Complete Your Registration +

+

+ Set a password to secure your account and enable email login. +

+
+ + +
+
+
+ avatar +
+
+

{{ session('social_signup_name') }}

+

{{ session('social_signup_email') }}

+ + via {{ ucfirst(session('social_signup_provider', 'social')) }} + +
+
+
+ + @if ($errors->any()) +
+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif + +
+ @csrf +
+ + + +
+ +
+ + + + + + + + + +
+
+ + +
+ +
+ + + + + + + + + +
+
+ + +
+

+ Why set a password?
+ This allows you to sign in with your email and password in the future, even if you don't want to use {{ ucfirst(session('social_signup_provider', 'social')) }} login. +

+
+ + +
+ +
+
+
+
+
+
+ + + + +
+ +
+
+
+@endsection diff --git a/resources/views/pages/auth/signin.blade.php b/resources/views/pages/auth/signin.blade.php new file mode 100644 index 0000000..b4c8432 --- /dev/null +++ b/resources/views/pages/auth/signin.blade.php @@ -0,0 +1,173 @@ +@extends('layouts.fullscreen-layout') + +@section('content') +
+
+ +
+ +
+
+
+

+ Sign In +

+

+ Enter your email and password to sign in! +

+
+ +
+
+ + +
+
+
+
+
+
+ Or +
+
+
+ @csrf +
+ +
+ + + @error('email') +

{{ $message }}

+ @enderror +
+ +
+ +
+ + + + + + + + + +
+
+ +
+
+ +
+ + Forgot password? + +
+ +
+ +
+
+
+
+

+ Don't have an account? + Sign Up +

+
+
+
+
+
+ + + +
+ +
+
+
+@endsection diff --git a/resources/views/pages/auth/signup.blade.php b/resources/views/pages/auth/signup.blade.php new file mode 100644 index 0000000..5e8b3ba --- /dev/null +++ b/resources/views/pages/auth/signup.blade.php @@ -0,0 +1,193 @@ +@extends('layouts.fullscreen-layout') + +@section('content') +
+
+ +
+ +
+
+

+ Sign Up +

+

+ Enter your email and password to sign up! +

+
+ +
+
+ + +
+
+
+
+
+
+ Or +
+
+
+ @csrf +
+
+ +
+ + + @error('fname') +

{{ $message }}

+ @enderror +
+ +
+ + + @error('lname') +

{{ $message }}

+ @enderror +
+
+ +
+ + + @error('email') +

{{ $message }}

+ @enderror +
+ +
+ +
+ + @error('password') +

{{ $message }}

+ @enderror + + + + + + + + +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+

+ Already have an account? + Sign In +

+
+
+
+
+ + +
+ +
+
+
+@endsection diff --git a/resources/views/pages/auth/verify-email.blade.php b/resources/views/pages/auth/verify-email.blade.php new file mode 100644 index 0000000..a70e219 --- /dev/null +++ b/resources/views/pages/auth/verify-email.blade.php @@ -0,0 +1,66 @@ +@extends('layouts.fullscreen-layout') + +@section('content') +
+
+
+
+
+

+ Verify Your Email +

+ +
+ + + + + +

+ 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. +

+
+ + @if (session('success')) +
+ {{ session('success') }} +
+ @endif + +
+ @csrf + +
+ +
+ @csrf + +
+
+
+
+ + +
+
+@endsection diff --git a/resources/views/pages/blank.blade.php b/resources/views/pages/blank.blade.php new file mode 100644 index 0000000..28b5e56 --- /dev/null +++ b/resources/views/pages/blank.blade.php @@ -0,0 +1,17 @@ +@extends('layouts.app') + +@section('content') + +
+
+

+ Card Title Here +

+ +

+ Start putting content on grids or panels, you can also use different combinations of + grids.Please check out the dashboard and other pages +

+
+
+@endsection diff --git a/resources/views/pages/calender.blade.php b/resources/views/pages/calender.blade.php new file mode 100644 index 0000000..10ef5fa --- /dev/null +++ b/resources/views/pages/calender.blade.php @@ -0,0 +1,6 @@ +@extends('layouts.app') + +@section('content') + + +@endsection diff --git a/resources/views/pages/certificate/create.blade.php b/resources/views/pages/certificate/create.blade.php new file mode 100644 index 0000000..98e1490 --- /dev/null +++ b/resources/views/pages/certificate/create.blade.php @@ -0,0 +1,160 @@ +@extends('layouts.app') + +@section('content') + + +
+ @if(session('error')) + + @endif +
+ +
+ + +
+

Generate SSL Certificate

+

Create a new self-signed certificate using your Local CA. This will be signed by your private root authority.

+
+
+ +
+ @csrf + + +
+ +
+ + + +
+
+ + +
+
+ + +

Please enter a valid domain name containing at least one dot.

+

The primary Fully Qualified Domain Name (FQDN) or IP Address to be secured.

+
+ +
+ + +

Optional comma-separated list of additional domains or IPs.

+
+
+ + +
+ +

Distinguished Name Attributes

+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+ +
+ + +
+
+ + +
+ + Cancel + + +
+
+
+
+@endsection diff --git a/resources/views/pages/certificate/index.blade.php b/resources/views/pages/certificate/index.blade.php new file mode 100644 index 0000000..9371c09 --- /dev/null +++ b/resources/views/pages/certificate/index.blade.php @@ -0,0 +1,210 @@ +@extends('layouts.app') + +@section('content') +
+
+
+

+ Certificate Management +

+ +
+
+ @if($caReady) + + @endif +
+
+ + @if(!$caReady) +
+
+
+ +
+
+

Setup Required

+

Root CA and Intermediate CA have not been initialized in the database yet.

+ + @if(Auth::user()->isAdmin()) +
+ @csrf + +
+ @else +
+ Action Required: Please contact your administrator to initialize the Root CA. +
+ @endif +
+
+
+ @endif + +
+ + +

+ + SSL Certificates List +

+
+ +
+
+ Show + + entries +
+ +
+ +
+ + +
+
+
+ +
+ @include('pages.certificate.partials.table') +
+
+ + @if($caReady) + + +

+ + Download Root CA Certificates +

+
+

These are the authority certificates used to sign your SSLs. Install them on your machine/browser to trust your generated certificates.

+ + +
+ @endif + + @include('pages.certificate.partials.view-modal') +
+ + @include('pages.certificate.partials.create-modal') +@endsection diff --git a/resources/views/pages/certificate/partials/create-modal.blade.php b/resources/views/pages/certificate/partials/create-modal.blade.php new file mode 100644 index 0000000..e24fe1b --- /dev/null +++ b/resources/views/pages/certificate/partials/create-modal.blade.php @@ -0,0 +1,137 @@ + +
+
+

Generate SSL Certificate

+

Create a new self-signed certificate using your Local CA. This will be signed by your private root authority.

+
+ +
+ @csrf + + +
+ +
+ + + +
+
+ + +
+
+ + +

Please enter a valid domain name containing at least one dot.

+

The primary Fully Qualified Domain Name (FQDN) or IP Address to be secured.

+
+ +
+ + +

Optional comma-separated list of additional domains or IPs.

+
+
+ + +
+ +

Distinguished Name Attributes

+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+ +
+ + +
+
+ + +
+ + +
+
+
+
diff --git a/resources/views/pages/certificate/partials/table.blade.php b/resources/views/pages/certificate/partials/table.blade.php new file mode 100644 index 0000000..7bec928 --- /dev/null +++ b/resources/views/pages/certificate/partials/table.blade.php @@ -0,0 +1,172 @@ +
+ + + + + + + + + + + + + + @forelse ($certificates as $cert) + + + + + + + + + + @empty + + + + @endforelse + + {{-- Filler rows to maintain professional layout height --}} + @php + $currentCount = $certificates->count(); + $fillCount = 5 - ($currentCount ?: 1); + @endphp + @for($i = 0; $i < $fillCount; $i++) + + + + @endfor + +
NoCommon Name (CN)Serial NumberStatusActions
+ {{ ($certificates->currentPage() - 1) * $certificates->perPage() + $loop->iteration }} + +
{{ $cert->common_name }}
+
{{ $cert->organization }}
+
+ @php + $isValid = $cert->valid_to && \Carbon\Carbon::parse($cert->valid_to)->isFuture(); + $statusClass = $isValid ? 'bg-success-50 text-success-700 dark:bg-success-900/30 dark:text-success-300' : 'bg-error-50 text-error-700 dark:bg-error-900/30 dark:text-error-300'; + $statusText = $isValid ? 'Valid' : 'Expired'; + if(!$cert->valid_to) { + $statusClass = 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400'; + $statusText = 'Unknown'; + } + @endphp + + {{ $statusText }} + + +
+ +
+ +
+ + + +
+
+ + +
+ + +
+ + +
+ +
+
+ @csrf + +
+
+ @csrf + @method('DELETE') + +
+
+
+
+
+
+ +

No certificates found matching your criteria.

+
+
 
+
+ +@if ($certificates->hasPages()) +
+ {{ $certificates->links() }} +
+@endif diff --git a/resources/views/pages/certificate/partials/view-modal.blade.php b/resources/views/pages/certificate/partials/view-modal.blade.php new file mode 100644 index 0000000..c8b4c50 --- /dev/null +++ b/resources/views/pages/certificate/partials/view-modal.blade.php @@ -0,0 +1,42 @@ + +
+ +
+
+

Certificate Content

+

View and copy the raw content below.

+
+ +
+ + + Raw View + + + +
+
+ + +
+ +
+ +
+ + +
+

+            
+
+
+
diff --git a/resources/views/pages/chart/bar-chart.blade.php b/resources/views/pages/chart/bar-chart.blade.php new file mode 100644 index 0000000..6bff80a --- /dev/null +++ b/resources/views/pages/chart/bar-chart.blade.php @@ -0,0 +1,22 @@ +@extends('layouts.app') + +@section('content') + +
+ + +
+
+
+ +
+ + + +
+
+
+ +
+
+@endsection diff --git a/resources/views/pages/chart/line-chart.blade.php b/resources/views/pages/chart/line-chart.blade.php new file mode 100644 index 0000000..0477230 --- /dev/null +++ b/resources/views/pages/chart/line-chart.blade.php @@ -0,0 +1,30 @@ +@extends('layouts.app') + +@section('content') + +
+ + +
+
+
+ +
+ + + +
+
+
+ +
+ + + +
+
+
+ +
+
+@endsection diff --git a/resources/views/pages/dashboard.blade.php b/resources/views/pages/dashboard.blade.php new file mode 100644 index 0000000..f09f946 --- /dev/null +++ b/resources/views/pages/dashboard.blade.php @@ -0,0 +1,209 @@ +@extends('layouts.app') + +@section('content') +
+ +
+
+

Dashboard Oversight

+

Total control over your security infrastructure and API integrations.

+
+
+ + + Manage Keys + +
+
+ + +
+ +
+
+ + + +
+
+ Total Certificates + {{ $totalCertificates }} + + + + + {{ $activeCertificates }} Active Now + +
+
+ + +
+
+ + + +
+
+ Manageable API Keys + {{ $totalApiKeys }} + + Latest usage: {{ $recentApiActivity->first()->last_used_at?->diffForHumans() ?? 'None' }} + +
+
+ + +
+
+ + + +
+
+ Expiring Soon + {{ $expiringSoonCount }} + + Action required within 14 days + +
+
+ + +
+
+ + + +
+
+ Node Status + Operational + + Latency: + +
+
+
+ + +
+ +
+
+

Certificate Issuance Trends

+ Last 6 Months +
+ +
+ @foreach($issuanceData as $index => $count) +
+
+ @php + $max = max($issuanceData) ?: 1; + $percentage = ($count / $max) * 100; + @endphp +
+
+ {{ $count }} +
+
+
+ {{ $months[$index] }} +
+ @endforeach +
+
+ + +
+

Recent API Activity

+
+ @forelse($recentApiActivity as $activity) +
+
+ + + +
+
+

{{ $activity->name }}

+

Used {{ $activity->last_used_at->diffForHumans() }}

+
+
+ @empty +
+

No recent activity detected.

+
+ @endforelse +
+
+ + +
+
+

Recently Issued Certificates

+ View All +
+
+ + + + + + + + + + + + @forelse($recentCertificates as $cert) + + + + + + + + @empty + + + + @endforelse + +
Common NameOrganizationIssued AtExpiresStatus
{{ $cert->common_name }}{{ $cert->organization }}{{ $cert->created_at->format('M d, Y') }}{{ $cert->valid_to->format('M d, Y') }} + @if($cert->valid_to > now()) + Valid + @else + Expired + @endif +
No certificates found.
+
+
+
+
+@endsection diff --git a/resources/views/pages/errors/error-404.blade.php b/resources/views/pages/errors/error-404.blade.php new file mode 100644 index 0000000..accaf0e --- /dev/null +++ b/resources/views/pages/errors/error-404.blade.php @@ -0,0 +1,33 @@ +@extends('layouts.fullscreen-layout') + +@section('content') +@php + $currentYear = date('Y'); +@endphp +
+ {{-- common grid shape --}} + + +
+

+ ERROR +

+ + 404 + + +

+ We can't seem to find the page you are looking for! +

+ + + Back to Home Page + +
+ +

+ © {{ $currentYear }} - {{ config('app.name') }} +

+
+@endsection diff --git a/resources/views/pages/form/form-elements.blade.php b/resources/views/pages/form/form-elements.blade.php new file mode 100644 index 0000000..7169379 --- /dev/null +++ b/resources/views/pages/form/form-elements.blade.php @@ -0,0 +1,21 @@ +@extends('layouts.app') + +@section('content') + +
+
+ + + + +
+
+ + + + + + +
+
+@endsection diff --git a/resources/views/pages/profile.blade.php b/resources/views/pages/profile.blade.php new file mode 100644 index 0000000..0793b2a --- /dev/null +++ b/resources/views/pages/profile.blade.php @@ -0,0 +1,11 @@ +@extends('layouts.app') + +@section('content') + +
+

Profile

+ + + +
+@endsection diff --git a/resources/views/pages/settings.blade.php b/resources/views/pages/settings.blade.php new file mode 100644 index 0000000..9679999 --- /dev/null +++ b/resources/views/pages/settings.blade.php @@ -0,0 +1,517 @@ +@extends('layouts.app') + +@section('content') +
+ + +
+

+ Account Settings +

+ + +
+ +
+ +
+
+
+ +
+ Settings Section + +
+ + +
+
+
+ + +
+ + + + +
+
+
+

+ Profile Information +

+

+ Update your personal details and avatar. +

+
+ +
+ +
+
+ +
+
+ user + +
+ +
+ + +
+
+

Full Name

+

{{ $user->name }}

+
+
+

Email Address

+

+ {{ $user->email }} + @if($user->hasVerifiedEmail()) + + Verified + + @else + + Unverified + + @endif +

+
+
+

Phone Number

+

{{ $user->phone ?? 'Not set' }}

+
+
+

Bio

+

{{ $user->bio ?? 'Not set' }}

+
+
+
+
+
+ + +
+
+

+ Change Password +

+

+ Update your password to keep your account secure. +

+
+ +
+
+ @csrf + +
+ +
+ + +
+ @error('current_password') +

{{ $message }}

+ @enderror +
+ + +
+ +
+ + +
+ @error('password') +

{{ $message }}

+ @enderror +
+ + +
+ +
+ + +
+
+ +
+ +
+
+
+
+ + +
+
+

+ Connected Accounts +

+

+ Manage your linked social accounts. +

+
+ +
+ +
+
+
+ +
+
+

Google

+

+ {{ $user->google_id ? 'Connected' : 'Not connected' }} +

+
+
+ @if ($user->google_id) +
+ @csrf + @method('DELETE') + +
+ @else + + Connect + + @endif +
+ + +
+
+
+ +
+
+

GitHub

+

+ {{ $user->github_id ? 'Connected' : 'Not connected' }} +

+
+
+ @if ($user->github_id) +
+ @csrf + @method('DELETE') + +
+ @else + + Connect + + @endif +
+
+
+ + +
+
+

+ Account Access +

+

+ Recent login activity on your account (Max 10 records, last 1 month). +

+
+ +
+ @forelse($user->loginHistories as $history) +
+
+
+ + @if($history->provider === 'google') + + @elseif($history->provider === 'github') + + @else + + @endif + +
+
+
+

+ {{ $history->login_at->format('d M Y, H:i') }} +

+ + ({{ $history->login_at->diffForHumans() }}) + +
+ @if($history->ip_address) +
+ + {{ $history->ip_address }} + + @if($history->user_agent) + + Login on {{ Str::limit($history->user_agent, 40) }} + + @endif +
+ @endif +
+
+
+ + {{ ucfirst($history->provider) }} + +
+
+ @empty +

No login history recorded yet.

+ @endforelse +
+
+ + +
+
+

+ Danger Zone +

+

+ Irreversible actions for your account. +

+
+ +
+
+
+

Delete Account

+

+ Once you delete your account, there is no going back. Please be certain. +

+
+ +
+
+
+
+
+ + + + + + + + + +
+@endsection diff --git a/resources/views/pages/suspended.blade.php b/resources/views/pages/suspended.blade.php new file mode 100644 index 0000000..6bce1f0 --- /dev/null +++ b/resources/views/pages/suspended.blade.php @@ -0,0 +1,59 @@ +@extends('layouts.fullscreen-layout') + +@section('content') +
+
+
+
+
+

+ Account Suspended +

+ +
+ + + + + + + +

+ Your account has been suspended by the administrator. +

+

+ You cannot access the dashboard or perform any actions. +

+

+ If you believe this is a mistake, please contact support. +

+
+ +
+ @csrf + +
+
+
+
+ + +
+
+@endsection diff --git a/resources/views/pages/tables/basic-tables.blade.php b/resources/views/pages/tables/basic-tables.blade.php new file mode 100644 index 0000000..a8b644e --- /dev/null +++ b/resources/views/pages/tables/basic-tables.blade.php @@ -0,0 +1,22 @@ +@extends('layouts.app') + +@section('content') + +
+ + + + + + + + + + + + + + + +
+@endsection diff --git a/resources/views/pages/ui-elements/alerts.blade.php b/resources/views/pages/ui-elements/alerts.blade.php new file mode 100644 index 0000000..1a0d406 --- /dev/null +++ b/resources/views/pages/ui-elements/alerts.blade.php @@ -0,0 +1,174 @@ +@extends('layouts.app') + +@section('content') + + +
+ {{-- Success Alert --}} + +
+ + + +
+
+ + {{-- Warning Alert --}} + +
+ + + +
+
+ + {{-- Error Alert --}} + +
+ + + +
+
+ + {{-- Info Alert --}} + +
+ + + +
+
+ + {{-- Additional Examples --}} + +
+ {{-- With Slot Content --}} + +

+ This alert uses custom slot content + instead of the message prop. +

+
    +
  • You can add any HTML content
  • +
  • Including lists and formatting
  • +
  • Perfect for complex messages
  • +
+ + + {{-- Minimal Alert --}} + + + {{-- Alert with Long Message --}} + +
+
+ + {{-- Interactive Demo --}} + +
+ {{-- Payment Success --}} + +

+ Your payment of $99.00 has been processed successfully. +

+
+

Order ID: #TAILADMIN-0014

+

Transaction ID: TXN-1234567890

+
+ + View Order Details + + + + {{-- Account Warning --}} + + + {{-- Validation Error --}} + +
    +
  • Email field is required
  • +
  • Password must be at least 8 characters
  • +
  • Please accept the terms and conditions
  • +
+ + + {{-- System Info --}} + +
+
+
+@endsection diff --git a/resources/views/pages/ui-elements/avatars.blade.php b/resources/views/pages/ui-elements/avatars.blade.php new file mode 100644 index 0000000..0006b1a --- /dev/null +++ b/resources/views/pages/ui-elements/avatars.blade.php @@ -0,0 +1,63 @@ +@extends('layouts.app') + +@section('content') + + + @php + $avatarSrc = asset('images/user/user-01.jpg'); + $sizes = ['xsmall', 'small', 'medium', 'large', 'xlarge', 'xxlarge']; + @endphp + +
+ {{-- Default Avatar --}} + +
+ @foreach($sizes as $size) + + @endforeach +
+
+ + {{-- Avatar with Online Indicator --}} + +
+ @foreach($sizes as $size) + + @endforeach +
+
+ + {{-- Avatar with Offline Indicator --}} + +
+ @foreach($sizes as $size) + + @endforeach +
+
+ + {{-- Avatar with Busy Indicator --}} + +
+ @foreach($sizes as $size) + + @endforeach +
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/pages/ui-elements/badges.blade.php b/resources/views/pages/ui-elements/badges.blade.php new file mode 100644 index 0000000..e53fd76 --- /dev/null +++ b/resources/views/pages/ui-elements/badges.blade.php @@ -0,0 +1,76 @@ +@extends('layouts.app') + +@section('content') + + + @php + use Illuminate\Support\HtmlString; + $colors = ['primary', 'success', 'error', 'warning', 'info', 'light', 'dark']; + + $plusIcon = new HtmlString(' + + '); + @endphp + +
+ +
+ @foreach ($colors as $color) + + {{ $color }} + + @endforeach +
+
+ + +
+ @foreach ($colors as $color) + + {{ $color }} + + @endforeach +
+
+ + +
+ @foreach ($colors as $color) + + {{ $color }} + + @endforeach +
+
+ + +
+ @foreach ($colors as $color) + + {{ $color }} + + @endforeach +
+
+ + +
+ @foreach ($colors as $color) + + {{ $color }} + + @endforeach +
+
+ + +
+ @foreach ($colors as $color) + + {{ $color }} + + @endforeach +
+
+
+@endsection diff --git a/resources/views/pages/ui-elements/buttons.blade.php b/resources/views/pages/ui-elements/buttons.blade.php new file mode 100644 index 0000000..2b306a1 --- /dev/null +++ b/resources/views/pages/ui-elements/buttons.blade.php @@ -0,0 +1,77 @@ +@extends('layouts.app') + +@php + use Illuminate\Support\HtmlString; + + // Page title + $currentPageTitle = 'Buttons'; + + // Define BoxIcon once as an HtmlString (so it won’t get escaped) + $BoxIcon = new HtmlString(' + + + + '); +@endphp + +@section('content') + {{-- Page Breadcrumb --}} + + +
+ + {{-- Primary Buttons --}} + +
+ Button Text + Button Text +
+
+ + {{-- Primary Button with Left Icon --}} + +
+ Button Text + Button Text +
+
+ + {{-- Primary Button with Right Icon --}} + +
+ Button Text + Button Text +
+
+ + {{-- Outline Buttons --}} + +
+ Button Text + Button Text +
+
+ + {{-- Outline Button with Left Icon --}} + +
+ Button Text + Button Text +
+
+ + {{-- Outline Button with Right Icon --}} + +
+ Button Text + Button Text +
+
+ +
+@endsection diff --git a/resources/views/pages/ui-elements/images.blade.php b/resources/views/pages/ui-elements/images.blade.php new file mode 100644 index 0000000..5d2ae4d --- /dev/null +++ b/resources/views/pages/ui-elements/images.blade.php @@ -0,0 +1,62 @@ +@extends('layouts.app') + +@php + $images = [ + [ + 'src' => asset('images/grid-image/image-04.png'), + 'alt' => 'Grid image 1', + ], + [ + 'src' => asset('images/grid-image/image-05.png'), + 'alt' => 'Grid image 2', + ], + [ + 'src' => asset('images/grid-image/image-06.png'), + 'alt' => 'Grid image 3', + ], + ]; +@endphp + +@section('content') + {{-- Page Breadcrumb --}} + + +
+ + +
+
+ Cover +
+
+
+
+ + +
+
+ grid +
+ +
+ grid +
+
+
+ + +
+ @foreach ($images as $image) +
+ {{ $image['alt'] }} +
+ @endforeach +
+
+ +
+@endsection diff --git a/resources/views/pages/ui-elements/videos.blade.php b/resources/views/pages/ui-elements/videos.blade.php new file mode 100644 index 0000000..11299dc --- /dev/null +++ b/resources/views/pages/ui-elements/videos.blade.php @@ -0,0 +1,30 @@ + +@extends('layouts.app') + +@section('content') + {{-- Page Breadcrumb --}} + + +
+ +
+ + + + + + + +
+ +
+ + + + + + +
+ +
+@endsection diff --git a/routes/console.php b/routes/console.php new file mode 100644 index 0000000..3c9adf1 --- /dev/null +++ b/routes/console.php @@ -0,0 +1,8 @@ +comment(Inspiring::quote()); +})->purpose('Display an inspiring quote'); diff --git a/routes/web.php b/routes/web.php new file mode 100644 index 0000000..55da8d0 --- /dev/null +++ b/routes/web.php @@ -0,0 +1,178 @@ +name('api.public.ca-certificates'); + +// Authenticated API Routes (v1) +Route::middleware('api_key')->prefix('api/v1')->group(function () { + Route::get('/certificates', [\App\Http\Controllers\Api\CertificateApiController::class, 'index'])->name('api.v1.certificates.index'); +}); + +Route::get('/ping', function () { + return response()->noContent(); +}); + +// authentication pages +Route::middleware('guest')->group(function () { + Route::get('/', [AuthController::class, 'signin'])->name('home'); + Route::get('/signin', [AuthController::class, 'signin'])->name('signin'); + Route::post('/signin', [AuthController::class, 'authenticate']); + Route::get('/signup', [AuthController::class, 'signup'])->name('signup'); + Route::post('/signup', [AuthController::class, 'store']); + + // Password Setup (for social signup) + Route::get('/setup-password', [AuthController::class, 'showPasswordSetup'])->name('setup-password'); + Route::post('/setup-password', [AuthController::class, 'completePasswordSetup']); + + // Forgot Password + // Forgot Password + Route::get('/forgot-password', [ForgotPasswordController::class, 'showLinkRequestForm'])->name('password.request'); + Route::post('/forgot-password', [ForgotPasswordController::class, 'sendResetLinkEmail'])->name('password.email'); + + Route::get('/reset-password', function () { + return redirect()->route('password.request'); + })->name('password.reset.missing_token'); + + Route::get('/reset-password/{token}', [ResetPasswordController::class, 'showResetForm'])->name('password.reset'); + Route::post('/reset-password', [ResetPasswordController::class, 'reset'])->name('password.update'); +}); + +// Social Auth Redirects (context-aware) +Route::get('/auth/{provider}/redirect/{context}', [AuthController::class, 'socialRedirect']) + ->name('auth.social') + ->where('provider', 'github|google') + ->where('context', 'signin|signup|connect'); + +// Social Auth Callbacks +Route::get('/auth/{provider}/callback', [AuthController::class, 'socialCallback']) + ->name('auth.social.callback') + ->where('provider', 'github|google'); + +// Public Certificate Routes +Route::prefix('certificate')->name('certificate.')->group(function () { + Route::get('/download-ca/{type}', [CertificateController::class, 'downloadCa'])->name('download-ca'); + Route::get('/download-ca-bundle', [CertificateController::class, 'downloadCaBundle'])->name('download-ca-bundle'); + Route::get('/download-ca-android', [CertificateController::class, 'downloadCaAndroid'])->name('download-ca-android'); + Route::get('/download-installer', [CertificateController::class, 'downloadInstaller'])->name('download-installer'); +}); + +// Email Verification (Public/Signed) +Route::get('/email/verify/{id}/{hash}', [App\Http\Controllers\VerificationController::class, 'verify']) + ->middleware(['signed', 'throttle:6,1']) + ->name('verification.verify'); + +// Authenticated Routes +Route::middleware(['auth', \App\Http\Middleware\EnsureUserIsActive::class])->group(function () { + // Email Verification Routes + Route::get('/email/verify', [App\Http\Controllers\VerificationController::class, 'show'])->name('verification.notice'); + + Route::post('/email/verification-notification', [App\Http\Controllers\VerificationController::class, 'resend'])->middleware(['throttle:6,1'])->name('verification.send'); + + // Suspended Page + Route::get('/suspended', [\App\Http\Controllers\SuspendedController::class, 'index'])->name('suspended'); + + // Logout + Route::get('/logout', [AuthController::class, 'logoutGet'])->name('logout.get'); + Route::post('/logout', [AuthController::class, 'logout'])->name('logout'); + + // Authenticated & Verified Routes + Route::middleware('verified')->group(function () { + // dashboard pages + Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard'); + + // Certificate routes + Route::prefix('certificate')->name('certificate.')->group(function () { + Route::get('/', [CertificateController::class, 'index'])->name('index'); + Route::get('/create', [CertificateController::class, 'create'])->name('create'); + Route::post('/generate', [CertificateController::class, 'generate'])->name('generate'); + // Setup CA moved to admin routes + + Route::post('/regenerate/{certificate:uuid}', [CertificateController::class, 'regenerate'])->name('regenerate'); + Route::get('/download-zip/{certificate:uuid}', [CertificateController::class, 'downloadZip'])->name('download-zip'); + Route::get('/download-p12/{certificate:uuid}', [CertificateController::class, 'downloadP12'])->name('download-p12'); + Route::get('/view/{certificate:uuid}/{type}', [CertificateController::class, 'viewFile'])->name('view'); + Route::delete('/{certificate:uuid}', [CertificateController::class, 'delete'])->name('delete'); + }); + + + + // Admin Only Pages (No Prefix) + Route::middleware('admin')->group(function () { + // calender pages + Route::get('/calendar', [PageController::class, 'calendar'])->name('calendar'); + + // form pages + Route::get('/form-elements', [UiController::class, 'formElements'])->name('form-elements'); + + // tables pages + Route::get('/basic-tables', [UiController::class, 'basicTables'])->name('basic-tables'); + + // pages + Route::get('/blank', [PageController::class, 'blank'])->name('blank'); + + // chart pages + Route::get('/line-chart', [ChartController::class, 'lineChart'])->name('line-chart'); + Route::get('/bar-chart', [ChartController::class, 'barChart'])->name('bar-chart'); + + // ui elements pages + Route::get('/alerts', [UiController::class, 'alerts'])->name('alerts'); + Route::get('/avatars', [UiController::class, 'avatars'])->name('avatars'); + Route::get('/badge', [UiController::class, 'badges'])->name('badges'); + Route::get('/buttons', [UiController::class, 'buttons'])->name('buttons'); + Route::get('/image', [UiController::class, 'images'])->name('images'); + Route::get('/videos', [UiController::class, 'videos'])->name('videos'); + }); + + // profile pages + Route::get('/profile', [ProfileController::class, 'index'])->name('profile'); + Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); + + // account settings + Route::get('/settings', [SettingsController::class, 'index'])->name('settings'); + Route::post('/settings/password', [SettingsController::class, 'updatePassword'])->name('settings.password'); + Route::delete('/settings/social/{provider}', [SettingsController::class, 'disconnectSocial'])->name('settings.social.disconnect'); + Route::delete('/settings', [SettingsController::class, 'destroy'])->name('settings.destroy'); + + // API Keys + Route::resource('api-keys', \App\Http\Controllers\ApiKeyController::class)->only(['index', 'store', 'destroy', 'update']); + Route::patch('/api-keys/{apiKey}/toggle', [\App\Http\Controllers\ApiKeyController::class, 'toggle'])->name('api-keys.toggle'); + Route::post('/api-keys/{apiKey}/regenerate', [\App\Http\Controllers\ApiKeyController::class, 'regenerate'])->name('api-keys.regenerate'); + + // Admin Pages + Route::middleware('admin')->prefix('admin')->name('admin.')->group(function () { + Route::get('/users', [App\Http\Controllers\UserManagementController::class, 'index'])->name('users.index'); + Route::patch('/users/{user}/toggle-status', [App\Http\Controllers\UserManagementController::class, 'toggleStatus'])->name('users.toggle-status'); + Route::delete('/users/{user}', [App\Http\Controllers\UserManagementController::class, 'destroy'])->name('users.destroy'); + Route::post('/users/{user}/send-reset-link', [App\Http\Controllers\UserManagementController::class, 'sendResetLink'])->name('users.send-reset-link'); + Route::post('/users/{user}/send-verification', [App\Http\Controllers\UserManagementController::class, 'sendVerification'])->name('users.send-verification'); + Route::patch('/users/{user}/update-email', [App\Http\Controllers\UserManagementController::class, 'updateEmail'])->name('users.update-email'); + + // Root CA Management + Route::get('/root-ca', [RootCaController::class, 'index'])->name('root-ca.index'); + Route::post('/root-ca/{certificate}/renew', [RootCaController::class, 'renew'])->name('root-ca.renew'); + + // Setup Route (Admin Only) + Route::post('/setup-ca', [CertificateController::class, 'setupCa'])->name('setup-ca'); + }); + }); +}); + +// Public / Error Pages +Route::get('/error-404', [PageController::class, 'error404'])->name('error-404'); + +Route::get('/php', [PageController::class, 'php']); + diff --git a/storage/app/.gitignore b/storage/app/.gitignore new file mode 100644 index 0000000..fedb287 --- /dev/null +++ b/storage/app/.gitignore @@ -0,0 +1,4 @@ +* +!private/ +!public/ +!.gitignore diff --git a/storage/app/private/.gitignore b/storage/app/private/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/app/private/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/app/public/.gitignore b/storage/app/public/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/app/public/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/.gitignore b/storage/framework/.gitignore new file mode 100644 index 0000000..05c4471 --- /dev/null +++ b/storage/framework/.gitignore @@ -0,0 +1,9 @@ +compiled.php +config.php +down +events.scanned.php +maintenance.php +routes.php +routes.scanned.php +schedule-* +services.json diff --git a/storage/framework/cache/.gitignore b/storage/framework/cache/.gitignore new file mode 100644 index 0000000..01e4a6c --- /dev/null +++ b/storage/framework/cache/.gitignore @@ -0,0 +1,3 @@ +* +!data/ +!.gitignore diff --git a/storage/framework/cache/data/.gitignore b/storage/framework/cache/data/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/cache/data/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/sessions/.gitignore b/storage/framework/sessions/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/sessions/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/testing/.gitignore b/storage/framework/testing/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/testing/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/views/.gitignore b/storage/framework/views/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/views/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/logs/.gitignore b/storage/logs/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/logs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/Feature/ApiKeyTest.php b/tests/Feature/ApiKeyTest.php new file mode 100644 index 0000000..cffe1bd --- /dev/null +++ b/tests/Feature/ApiKeyTest.php @@ -0,0 +1,99 @@ +create(); + + $response = $this->actingAs($user)->get(route('api-keys.index')); + + $response->assertStatus(200); + $response->assertSee('API Keys'); + } + + public function test_user_can_generate_api_key() + { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->post(route('api-keys.store'), [ + 'name' => 'Test Key', + ]); + + $response->assertRedirect(); + $this->assertDatabaseHas('api_keys', [ + 'user_id' => $user->id, + 'name' => 'Test Key', + ]); + + $apiKey = ApiKey::where('user_id', $user->id)->first(); + $this->assertNotNull($apiKey->key); + // $this->assertEquals(64, strlen($apiKey->key)); // Length might vary slightly if we use random(60) + 'dvp_' = 64. + $this->assertTrue(str_starts_with($apiKey->key, 'dvp_')); + } + + public function test_user_can_toggle_api_key_status() + { + $user = User::factory()->create(); + $apiKey = ApiKey::factory()->create([ + 'user_id' => $user->id, + 'is_active' => true, + ]); + + $response = $this->actingAs($user)->patch(route('api-keys.toggle', $apiKey)); + + $response->assertRedirect(); + $this->assertDatabaseHas('api_keys', [ + 'id' => $apiKey->id, + 'is_active' => false, + ]); + + // Toggle back + $response = $this->actingAs($user)->patch(route('api-keys.toggle', $apiKey)); + $this->assertDatabaseHas('api_keys', [ + 'id' => $apiKey->id, + 'is_active' => true, + ]); + } + + public function test_user_can_delete_api_key() + { + $user = User::factory()->create(); + $apiKey = ApiKey::factory()->create([ + 'user_id' => $user->id, + ]); + + $response = $this->actingAs($user)->delete(route('api-keys.destroy', $apiKey)); + + $response->assertRedirect(); + $this->assertDatabaseMissing('api_keys', [ + 'id' => $apiKey->id, + ]); + } + + public function test_user_cannot_delete_others_api_key() + { + $user = User::factory()->create(); + $otherUser = User::factory()->create(); + $apiKey = ApiKey::factory()->create([ + 'user_id' => $otherUser->id, + ]); + + $response = $this->actingAs($user)->delete(route('api-keys.destroy', $apiKey)); + + $response->assertStatus(403); + $this->assertDatabaseHas('api_keys', [ + 'id' => $apiKey->id, + ]); + } +} diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php new file mode 100644 index 0000000..8fdc86b --- /dev/null +++ b/tests/Feature/ExampleTest.php @@ -0,0 +1,7 @@ +get('/'); + + $response->assertStatus(200); +}); diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..60f04a4 --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,47 @@ +extend(Tests\TestCase::class) + // ->use(Illuminate\Foundation\Testing\RefreshDatabase::class) + ->in('Feature'); + +/* +|-------------------------------------------------------------------------- +| Expectations +|-------------------------------------------------------------------------- +| +| When you're writing tests, you often need to check that values meet certain conditions. The +| "expect()" function gives you access to a set of "expectations" methods that you can use +| to assert different things. Of course, you may extend the Expectation API at any time. +| +*/ + +expect()->extend('toBeOne', function () { + return $this->toBe(1); +}); + +/* +|-------------------------------------------------------------------------- +| Functions +|-------------------------------------------------------------------------- +| +| While Pest is very powerful out-of-the-box, you may have some testing code specific to your +| project that you don't want to repeat in every file. Here you can also expose helpers as +| global functions to help you to reduce the number of lines of code in your test files. +| +*/ + +function something() +{ + // .. +} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..fe1ffc2 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,10 @@ +toBeTrue(); +}); diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..29fbfe9 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,13 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import tailwindcss from '@tailwindcss/vite'; + +export default defineConfig({ + plugins: [ + laravel({ + input: ['resources/css/app.css', 'resources/js/app.js'], + refresh: true, + }), + tailwindcss(), + ], +});