diff --git a/README.md b/README.md index 75e21b4..006175e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,254 @@ -# DADProject +# DAD Project - Bisca Game Platform +## Overview + +The DAD project aims to develop and deploy the **Bisca Game Platform** as a web application. The platform is a distributed system that provides users with the ability to play the traditional Portuguese card game Bisca in single-player and multiplayer modes, manage their coins, and track their gaming statistics. + +The web client is implemented as a **Single-Page Application (SPA)** built with **Vue.js** and hosted on the designated internal school server. The backend system includes a database, a RESTful API server, and a WebSocket server for real-time multiplayer gameplay. + +## Project Architecture + +### Frontend +- **Technology**: Vue.js SPA +- **Purpose**: User interface for game play, user management, coin transactions, and statistics visualization +- **Features**: Real-time game updates via WebSockets, responsive design, player profiles, leaderboards + +### Backend +- **RESTful API Server**: Handles user authentication, coin transactions, game history, leaderboards, and administrative operations +- **WebSocket Server**: Manages real-time multiplayer game communication, ensuring the server acts as the authoritative source for game state +- **Database**: Persists user data, game results, transactions, and platform statistics + +## Constraints + +The project must adhere to the following mandatory constraints: + +1. **Single-Page Application (SPA)** - The application must be implemented exclusively as an SPA +2. **Vue.js Requirement** - The client application must be built with Vue.js +3. **Payment Gateway Integration** - All external coin purchases must be processed via the provided RESTful API (Payment Gateway Service) +4. **Deployment** - The application must be deployed on the designated internal school server and be accessible via a desktop web browser +5. **Backend Technologies** - Must be open-source or available as free external services +6. **Server Compatibility** - Backend technologies must be installable on or accessible from the designated server; if not supported, compliant alternatives must be selected +7. **Database Seeding** - The deployed application's database must be seeded with realistic data in both scale and content to simulate typical usage patterns + +## Core Features + +### 1. User Management +- **Registration**: Users register with email, nickname, name, password (minimum 3 characters), and optional photo/avatar +- **Authentication**: Email and password-based login +- **Profile Management**: View and update profile information, change password +- **Sign Out**: Users can end their session +- **Account Deletion**: Requires explicit confirmation; permanently forfeits all coins +- **Welcome Bonus**: New users receive 10 coins upon registration +- **Admin Accounts**: Created only by existing administrators; same profile capabilities as users but cannot delete their own accounts + +### 2. Coins and Transactions +- **Coin Balance**: Users can view their current coin balance +- **Coin Purchases**: External purchases via Payment Gateway Service +- **Conversion Rate**: €1 = 10 coins (purchases in €1 increments) +- **Coin Earning**: Players earn coins through gameplay and bonuses +- **Transaction History**: All coin-related transactions (bonuses, purchases, spending, earnings) are logged +- **History Access**: + - Players view only their own transaction history + - Administrators have read-only access to all players' histories + - Administrators cannot create, modify, or delete transactions + +### 3. Games and Matches + +#### Game Variants +- **Bisca de 3**: Three-card hands +- **Bisca de 9**: Nine-card hands + +#### Game Modes +- **Single-Player**: Against a bot (available to anonymous and registered users) +- **Multiplayer**: Against human opponents (available only to registered users) +- **Match**: Best-of series (up to 4 marks/wins) with two players + +#### Game Rules +- Games and matches are limited to two players +- The bot follows game rules and attempts to win when playing second; otherwise plays its lowest-value card +- Official Bisca rules apply (as defined in the "Bisca Game" documentation) + +#### Multiplayer Gameplay +- **Real-Time Communication**: WebSockets for live game updates +- **Server Authority**: The server is the source of truth for: + - Card dealing + - Turn order + - Valid moves + - Trick resolution + - Drawing from stock + - Scoring + - End-of-game/match conditions +- **Move Timer**: 20-second limit per move +- **Resignation**: + - Opponent awarded all remaining cards + - Game ends immediately + - If in a match, player forfeits all remaining games + - Scores computed accordingly +- **Persistent Results**: Game and match results with metadata are persisted for user history and administrative access + +### 4. Game History and Leaderboards + +#### Tracking +- **Scope**: Only multiplayer games and matches are tracked +- **Metadata**: Start/end timestamps, variant, outcome (win/loss/draw/forfeit), final points, marks (capote, bandeira), duration + +#### History Access +- **Players**: View only their own multiplayer history +- **Administrators**: Read-only access to all players' histories and detailed records +- **Detailed Records**: Visible only to administrators and participating players + +#### Leaderboards +- **Personal Leaderboards**: Visible only to the owner; summarize totals for game wins, match wins, capotes, and bandeiras (optionally segmented by variant) +- **Global Leaderboards**: Visible to all users including anonymous visitors; highlight top players by game and match wins, may include capotes and bandeiras +- **Tie-Breaking**: Earlier achievers rank higher in case of ties + +### 5. Administration + +#### Administrator Capabilities +- **Profile Management**: Same as regular users, except cannot delete their own accounts +- **Account Management**: Create new administrator accounts, remove any account except their own +- **Soft Deletion**: Player accounts with prior activity must be soft-deleted +- **User Monitoring**: View all users (players and administrators), block/unblock players +- **Read-Only Access**: + - All transactions + - All multiplayer games and matches + - Platform usage summaries and statistics + +#### Administrator Access Control +- Cannot play games or hold coins +- New administrator accounts can only be created by existing administrators + +### 6. Statistics and Reporting + +#### Anonymous Users and Players +- Access only generic, anonymized aggregates: + - Total registered players + - Games and matches played + - Recent activity summaries + +#### Administrators +- Full, non-anonymized access including: + - Purchases by period and player + - Game/match volumes + - User activity breakdowns + - Time-series data + +#### Visualization +- Statistics available in both visual (charts) and textual/tabular forms +- Privacy for non-admin viewers is preserved +- Exact metrics and visualizations are team-defined + +## Enhancement and Innovation Opportunities + +To distinguish this project, consider implementing one or more of the following: + +### Custom Features (Examples) +- Support for three or more players per game (optional extension while preserving two-player mode) +- Spectator mode for multiplayer games and matches +- Full playback of past games +- Custom card sets +- Extended bot capabilities (e.g., improved AI strategies) +- Integration of bots into multiplayer games + +### Architecture and Performance Enhancements (Examples) +- Queue server or worker for asynchronous, long-running operations +- Horizontal scaling of the WebSocket server +- Session recovery after WebSocket or node failures +- Cache server implementation +- NoSQL database for specific use cases +- Search engine for game history or statistics +- Asynchronous messaging broker (e.g., Kafka, RabbitMQ) +- Distributed components for improved scalability and resilience + +### Implementation Quality +- Use of technologies or techniques not covered in standard coursework +- Performance optimization with empirical benchmarks +- Improved architecture and code structure +- Enhanced implementation quality and documentation + +**Note**: Credit is proportional to demonstrated impact with preference given to distributed capabilities that improve architecture, scalability, or resilience. + +## Technology Stack + +### Frontend +- Vue.js (mandatory) +- Vite (build tool) +- ESLint (code quality) + +### Backend +- **API Server**: Open-source, student-selected technology +- **WebSocket Server**: Open-source, student-selected technology +- **Database**: Open-source, student-selected technology +- All must be compatible with the designated school server + +### Supported Variants +- Laravel (PHP) for REST API +- Node.js for WebSocket server + +## Project Structure + +``` +DADProject/ +├── api/ # Laravel REST API backend +├── frontend/ # Vue.js SPA +├── websockets/ # WebSocket server +└── README.md # This file +``` + +## Getting Started + +Refer to the individual `README.md` files in each directory for setup and deployment instructions: + +- [API Documentation](./api/README.md) +- [Frontend Documentation](./frontend/README.md) +- [WebSocket Server Documentation](./websockets/README.md) + +## Database Schema + +The application's database includes the following primary entities: + +- **Users**: Player and administrator accounts +- **Games**: Individual game records (both single-player and multiplayer) +- **Matches**: Match records (series of games) +- **Coin Transactions**: Purchase and spending history +- **Coin Transaction Types**: Transaction categorization +- **Coin Purchases**: External purchase records +- **Leaderboard Data**: Aggregated player statistics + +For detailed schema information, refer to the database migrations in the `api/database/migrations/` directory. + +## Key Business Rules + +1. **Coin System**: + - New users receive 10 welcome coins + - Conversion rate: €1 = 10 coins + - Coin purchases are made in €1 increments + - All transactions are logged and immutable (except soft-deleted accounts) + +2. **Game Rules**: + - Two-player games only (in standard mode) + - Bot cannot be played in multiplayer + - 20-second move timer in multiplayer + - Resignation forfeits remaining games in a match + +3. **Account Management**: + - Administrators cannot be created via public registration + - Administrators cannot delete their own accounts + - Account deletion requires explicit confirmation + - Soft deletion preserves historical data for deleted player accounts + +4. **Leaderboard Ranking**: + - Multiplayer games and matches only + - Ties resolved by earlier achievement date + +## Notes for Developers + +- The WebSocket server acts as the authoritative source for all game state in multiplayer games +- Ensure proper error handling and recovery for WebSocket disconnections +- All coin transactions must be atomic and immutable +- Database must be seeded with realistic, varied data +- Privacy and data protection must be maintained throughout + +## License + +This project is part of the EI – DAD 2025/26 course. diff --git a/api/database/database.sqlite b/api/database/database.sqlite index a1b672d..744c4d8 100644 Binary files a/api/database/database.sqlite and b/api/database/database.sqlite differ diff --git a/api/database/migrations/2025_11_08_200643_create_personal_access_tokens_table.php b/api/database/migrations/2025_10_30_140612_create_personal_access_tokens_table.php similarity index 100% rename from api/database/migrations/2025_11_08_200643_create_personal_access_tokens_table.php rename to api/database/migrations/2025_10_30_140612_create_personal_access_tokens_table.php diff --git a/api/database/migrations/2025_10_30_140658_initial.php b/api/database/migrations/2025_10_30_140658_initial.php new file mode 100644 index 0000000..1799b53 --- /dev/null +++ b/api/database/migrations/2025_10_30_140658_initial.php @@ -0,0 +1,258 @@ +enum('type', ['A', 'P'])->default('P'); + + // Nickname - must be unique + $table->string('nickname', 20)->nullable()->unique(); + + // User access is blocked + $table->boolean('blocked')->default(false); + + // User Photo/Avatar + $table->string('photo_avatar_filename')->nullable(); + + // Brain Coin Balance + $table->integer('coins_balance')->default(0); + + // Users can be deleted with "soft deletes" + $table->softDeletes(); + + // custom data + $table->json('custom')->nullable(); + }); + + // NOTE: Only multiplayer matches are registered + Schema::create('matches', function (Blueprint $table) { + $table->id(); + + // Type of games in the match (Bisca dos 3; Bisca dos 9) + $table->enum('type', ['3', '9'])->default('3'); + + // Player 1 (usually, creates the match) + $table->unsignedBigInteger('player1_user_id'); + $table->foreign('player1_user_id')->references('id')->on('users'); + + // Player 2 (usually, joins the match) + $table->unsignedBigInteger('player2_user_id'); + $table->foreign('player2_user_id')->references('id')->on('users'); + + // Winner (null when not ended) + $table->unsignedBigInteger('winner_user_id')->nullable(); + $table->foreign('winner_user_id')->references('id')->on('users'); + + // Loser (null when not ended) + $table->unsignedBigInteger('loser_user_id')->nullable(); + $table->foreign('loser_user_id')->references('id')->on('users'); + + // Match status + // Pending - Match is waiting for players + // Playing - Match is in progress + // Ended - Match is over + // Interrupted - Match was interrupted due to technical issues (not finished; no winner) + // Note: depending of the project's implementation, some status may never be used + $table->enum('status', ['Pending', 'Playing', 'Ended', 'Interrupted']); + + // The stake for this match + $table->integer('stake')->default(3); + + // Moment when the match began + $table->dateTime('began_at')->nullable(); + // Moment when the match ended + $table->dateTime('ended_at')->nullable(); + // Match total time (in seconds) = ended_at - began_at + $table->decimal('total_time', 8, 2)->nullable(); + + // After the match ended, the total marks for player 1 + $table->integer('player1_marks')->nullable(); + + // After the game ended, the total marks for player 2 + $table->integer('player2_marks')->nullable(); + + // After the match ended, the sum of points for player 1 (all games) + $table->integer('player1_points')->nullable(); + + // After the match ended, the sum of points for player 2 (all games) + $table->integer('player2_points')->nullable(); + + // custom data + $table->json('custom')->nullable(); + + }); + + // NOTE: Only multiplayer games are registered + Schema::create('games', function (Blueprint $table) { + $table->id(); + + // Type of game (Bisca dos 3; Bisca dos 9) + $table->enum('type', ['3', '9'])->default('3'); + + // Player 1 (usually, creates the game) + $table->unsignedBigInteger('player1_user_id'); + $table->foreign('player1_user_id')->references('id')->on('users'); + + // Player 2 (usually, joins the game) + $table->unsignedBigInteger('player2_user_id'); + $table->foreign('player2_user_id')->references('id')->on('users'); + + // True when the game has ended with a draw + $table->boolean('is_draw')->default(false); + + // Winner (null when not ended, or when a draw) + $table->unsignedBigInteger('winner_user_id')->nullable(); + $table->foreign('winner_user_id')->references('id')->on('users'); + + // Loser (null when not ended, or when a draw) + $table->unsignedBigInteger('loser_user_id')->nullable(); + $table->foreign('loser_user_id')->references('id')->on('users'); + + // Match (null for standalone games) + $table->unsignedBigInteger('match_id')->nullable(); + $table->foreign('match_id')->references('id')->on('matches'); + + // Game status: 'Pending', 'Playing', 'Ended', or 'Interrupted'. + // Pending: waiting for players + // Playing: in progress + // Ended: finished + // Interrupted: stopped due to technical issues (unfinished; no winner) + // Note: depending on the project’s implementation, some statuses may never be used. + $table->enum('status', ['Pending', 'Playing', 'Ended', 'Interrupted']); + + // Moment when the game began + $table->dateTime('began_at')->nullable(); + // Moment when the game ended + $table->dateTime('ended_at')->nullable(); + // Game total time (in seconds) = ended_at - began_at + $table->decimal('total_time', 8, 2)->nullable(); + + + // After the game ended, the total of points for player 1 + $table->integer('player1_points')->nullable(); + + // After the game ended, the total of points for player 2 + $table->integer('player2_points')->nullable(); + + // custom data + $table->json('custom')->nullable(); + }); + + Schema::create('coin_transaction_types', function (Blueprint $table) { + $table->id(); + $table->string('name', 40); + // Credit - increase coins + // Debit - decrease coins + $table->enum('type', ['C', 'D']); + // Transaction Types can be deleted with "soft deletes" + $table->softDeletes(); + + // custom data + $table->json('custom')->nullable(); + }); + + // Add default coin_transaction_types + DB::table('coin_transaction_types')->insert([ + ['name' => 'Bonus', 'type' => 'C'], // id = 1 + ['name' => 'Coin purchase', 'type' => 'C'], // id = 2 + ['name' => 'Game fee', 'type' => 'D'], // id = 3 + ['name' => 'Match stake', 'type' => 'D'], // id = 4 + ['name' => 'Game payout', 'type' => 'C'], // id = 5 + ['name' => 'Match payout', 'type' => 'C'], // id = 6 + ]); + + Schema::create('coin_transactions', function (Blueprint $table) { + $table->id(); + + // Datetime of the coin transaction + $table->dateTime('transaction_datetime'); + + // User associated with the transaction + $table->unsignedBigInteger('user_id'); + $table->foreign('user_id')->references('id')->on('users'); + + // Match associated with the transaction (optional - it can be null) + $table->unsignedBigInteger('match_id')->nullable(); + $table->foreign('match_id')->references('id')->on('matches'); + + // Game associated with the transaction (optional - it can be null) + $table->unsignedBigInteger('game_id')->nullable(); + $table->foreign('game_id')->references('id')->on('games'); + + // Type of the transaction + $table->unsignedBigInteger('coin_transaction_type_id'); + $table->foreign('coin_transaction_type_id')->references('id')->on('coin_transaction_types'); + + // Amount of the transaction (coins) + // Positive -> increments the total amount of brain coins (Credit) + // Negative -> decrements the total amount of brain coins (Debit) + $table->integer('coins'); + + // custom data + $table->json('custom')->nullable(); + }); + + Schema::create('coin_purchases', function (Blueprint $table) { + $table->id(); + + // Datetime of the coin purchase + $table->dateTime('purchase_datetime'); + + // User associated with the purchase + $table->unsignedBigInteger('user_id'); + $table->foreign('user_id')->references('id')->on('users'); + + // Coin Transaction associated with this purchase + // One to One relation (purchase must have a coin transaction) + $table->unsignedBigInteger('coin_transaction_id')->unique(); + $table->foreign('coin_transaction_id')->references('id')->on('coin_transactions'); + + // Amount of the purchase (real money in euros) + $table->decimal('euros', 8, 2); + + // Purchases will involve a payment with a type and a reference + // MBWAY - Phone number with 9 digits + // PAYPAL - eMail + // IBAN - bank transfer (2 letters + 23 digits) + // MB - Multibanco payment - entity number (5 digits) + Reference (9 digits)) + // VISA - Visa card number (16 digits) + $table->enum('payment_type', ['MBWAY', 'PAYPAL', 'IBAN', 'MB', 'VISA']); + $table->string('payment_reference', 30); + + // custom data + $table->json('custom')->nullable(); + }); + } + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::drop('coin_purchases'); + Schema::drop('coin_transactions'); + Schema::drop('coin_transaction_types'); + Schema::drop('games'); + Schema::drop('matches'); + + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('deleted_at'); + $table->dropColumn('custom'); + $table->dropColumn('coins_balance'); + $table->dropColumn('photo_avatar_filename'); + $table->dropColumn('blocked'); + $table->dropColumn('nickname'); + $table->dropColumn('type'); + }); + } +}; diff --git a/api/database/seeders/DatabaseSeeder.php b/api/database/seeders/DatabaseSeeder.php index 6b901f8..85bfc6e 100644 --- a/api/database/seeders/DatabaseSeeder.php +++ b/api/database/seeders/DatabaseSeeder.php @@ -5,21 +5,80 @@ namespace Database\Seeders; use App\Models\User; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; +use Illuminate\Support\Facades\DB; +use Carbon\Carbon; class DatabaseSeeder extends Seeder { use WithoutModelEvents; - /** - * Seed the application's database. - */ + public static $startDate; + public static $dbInsertBlockSize = 500; + + // public static $seedType = "small"; + //public static $seedType = "full"; + //public static $seedLanguage = "pt_PT"; + public static $seedLanguage = "en_US"; + public function run(): void { - // User::factory(10)->create(); + $this->command->info("-----------------------------------------------"); + $this->command->info("START of database seeder"); + $this->command->info("-----------------------------------------------"); - User::factory()->create([ - 'name' => 'Test User', - 'email' => 'test@example.com', - ]); + self::$startDate = Carbon::now()->subMonths(14); + self::$seedLanguage = $this->command->choice('What is the language for users\' names?', ['pt_PT', 'en_US'], 0); + + if (DB::getDriverName() === 'sqlite') { + DB::statement('PRAGMA foreign_keys = OFF'); + } else { + DB::statement('SET foreign_key_checks=0'); + // No permissions to change global setting. Change the session setting only + //DB::statement("SET @@global.time_zone = '+00:00'"); + DB::statement("SET time_zone = '+00:00'"); + } + + DB::table('users')->delete(); + DB::table('matches')->delete(); + DB::table('games')->delete(); + DB::table('coin_purchases')->delete(); + DB::table('coin_transactions')->delete(); + DB::table('coin_transaction_types')->delete(); + + if (DB::getDriverName() === 'sqlite') { + DB::statement("DELETE FROM sqlite_sequence WHERE name = 'users'"); + DB::statement("DELETE FROM sqlite_sequence WHERE name = 'matches'"); + DB::statement("DELETE FROM sqlite_sequence WHERE name = 'games'"); + DB::statement("DELETE FROM sqlite_sequence WHERE name = 'coin_purchases'"); + DB::statement("DELETE FROM sqlite_sequence WHERE name = 'coin_transactions'"); + DB::statement("DELETE FROM sqlite_sequence WHERE name = 'coin_transaction_types'"); + } else { + DB::statement('ALTER TABLE users AUTO_INCREMENT = 0'); + DB::statement('ALTER TABLE matches AUTO_INCREMENT = 0'); + DB::statement('ALTER TABLE games AUTO_INCREMENT = 0'); + DB::statement('ALTER TABLE coin_purchases AUTO_INCREMENT = 0'); + DB::statement('ALTER TABLE coin_transactions AUTO_INCREMENT = 0'); + DB::statement('ALTER TABLE coin_transaction_types AUTO_INCREMENT = 0'); + } + + $this->command->info("-----------------------------------------------"); + + $this->call(TransactionTypesSeeder::class); + $this->call(UsersSeeder::class); + $this->call(InitialTransactionsSeeder::class); + $this->call(GamesSeeder::class); + $this->call(GamesTransactionsSeeder::class); + + if (DB::getDriverName() === 'sqlite') { + DB::statement('PRAGMA foreign_keys = ON'); + } else { + DB::statement('SET foreign_key_checks=1'); + } + + + + $this->command->info("-----------------------------------------------"); + $this->command->info("END of database seeder"); + $this->command->info("-----------------------------------------------"); } } diff --git a/api/database/seeders/GamesSeeder.php b/api/database/seeders/GamesSeeder.php new file mode 100644 index 0000000..4241f47 --- /dev/null +++ b/api/database/seeders/GamesSeeder.php @@ -0,0 +1,214 @@ +count() + 1; + return (12 * 60 * 60) / $totalPlayers + rand(0, 2000); + } + + private function nextGameDateTime(&$d, $filteredPlayers, $withinSameMatch = false) { + if ($withinSameMatch) { + $deltaSegundos = rand(300, 900); + } else { + $deltaSegundos = $this->calculateRandomSeconds($filteredPlayers); + } + $d->addSeconds($deltaSegundos); + } + + public function run(): void + { + $this->command->info("Games seeder - Start"); + + $start = DB::table('users')->where('type', 'P')->min('created_at'); + + $allPlayers = DB::table('users')->where('type', 'P')->get(); + $sortedPlayers = $allPlayers->sortBy('created_at')->values(); + + $d = new \Carbon\Carbon($start); + $d = $d->addDay(); + $now = \Carbon\Carbon::now(); + $this->command->info("Starting to create games"); + + $games = []; + $matches = []; + + $i = 0; + + $filteredPlayers = null; + $filteredPlayersIds = null; + $nextCreatedAt = null; + + while ($d->lte($now)) { + $i++; + if (($filteredPlayers === null) || ($nextCreatedAt === null) || + ($d->gte($nextCreatedAt)) + ) { + $filteredPlayers = $allPlayers->filter(function ($value) use ($d) { + return $d->gt($value->created_at); + }); + $nextCreatedAtPlayer = $sortedPlayers->first(function ($value) use ($d) { + return $d->lte($value->created_at); + }); + $nextCreatedAt = $nextCreatedAtPlayer ? $nextCreatedAtPlayer->created_at : new \Carbon\Carbon('9999-12-31'); + $filteredPlayersIds = $filteredPlayers->pluck('id')->toArray(); + } + + // Only creates games or matches when there are enough players + if ($filteredPlayersIds === null || empty($filteredPlayersIds) || count($filteredPlayersIds) <2) { + $this->nextGameDateTime($d, $filteredPlayers); + continue; + } + + $userIdKeys = array_rand($filteredPlayersIds, 2); + $userIDPlayer1 = $filteredPlayersIds[$userIdKeys[0]]; + $userIDPlayer2 = $filteredPlayersIds[$userIdKeys[1]]; + + if ($userIDPlayer1 == $userIDPlayer2) { + $this->nextGameDateTime($d, $filteredPlayers); + continue; + } + $match = null; + if (rand(1, $this->ratioStandaloneToMatch) === 1) { + $match = $this->newMatch($filteredPlayers, $userIDPlayer1, $userIDPlayer2, $d); + $playersMarks = [0, 0]; + $playersPoints = [0, 0]; + while($playersMarks[0] < 4 && $playersMarks[1] <4) { + $newGame = $this->newGame($filteredPlayers, $match, $userIDPlayer1, $userIDPlayer2, $d); + $games[] = $newGame; + $playersPoints[0] += $newGame['player1_points']; + $playersPoints[1] += $newGame['player2_points']; + if ($newGame['player1_points'] > $newGame['player2_points']) { + if ($newGame['player1_points'] >= 120) { + $playersMarks[0]+= 4; + } elseif ($newGame['player1_points'] >= 91) { + $playersMarks[0] += 2; + } else { + $playersMarks[0]++; + } + } elseif ($newGame['player2_points'] > $newGame['player1_points']) { + if ($newGame['player2_points'] >= 120) { + $playersMarks[1]+= 4; + } elseif ($newGame['player2_points'] >= 91) { + $playersMarks[1] += 2; + } else { + $playersMarks[1]++; + } + } + } + $this->updateMatchWinner($match, $playersMarks[0], $playersMarks[1], $playersPoints[0], $playersPoints[1], $d); + $matches[] = $match; + } else { + $newGame = $this->newGame($filteredPlayers, $match, $userIDPlayer1, $userIDPlayer2, $d); + $games[] = $newGame; + } + + if ($i >= DatabaseSeeder::$dbInsertBlockSize) { + if (!empty($matches)) { + DB::table('matches')->insert($matches); + $this->command->info("Saved " . count($matches) . " matches at date " . $d->format('Y-m-d H:i:s')); + } + if (!empty($games)) { + DB::table('games')->insert($games); + $this->command->info("Saved " . count($games) . " games at date " . $d->format('Y-m-d H:i:s')); + } + $i = 0; + $games = []; + $matches = []; + } + //$this->nextGameDateTime($d, $filteredPlayers); + } + if (!empty($matches)) { + DB::table('matches')->insert($matches); + $this->command->info("Saved " . count($matches) . " matches at date " . $d->format('Y-m-d H:i:s')); + } + if (!empty($games)) { + DB::table('games')->insert($games); + $this->command->info("Saved " . count($games) . " games at date " . $d->format('Y-m-d H:i:s')); + } + $this->command->info("Games seeder - End"); + } + + private $matchID = 0; + private function newMatch($filteredPlayers,$user1, $user2, $d) + { + $this->matchID++; + $this->nextGameDateTime($d, $filteredPlayers); + return [ + 'id' => $this->matchID, + 'type' => random_int(1,2) == 1 ? '3' : '9', + 'player1_user_id' => $user1, + 'player2_user_id' => $user2, + 'winner_user_id' => null, + 'loser_user_id' => null, + 'status' => 'Ended', + 'stake' => random_int(1,4) > 1 ? 3 : random_int(4,100), + 'began_at' => $d->copy(), + 'ended_at' => null, + 'total_time' => null, + 'player1_marks' => null, + 'player2_marks' => null, + 'player1_points' => null, + 'player2_points' => null, + 'custom' => null + ]; + } + + private function updateMatchWinner(&$match, $player1Marks, $player2Marks, $totalPlayers1, $totalPlayers2, $d) + { + $match['player1_marks'] = $player1Marks; + $match['player2_marks'] = $player2Marks; + $match['player1_points'] = $totalPlayers1; + $match['player2_points'] = $totalPlayers2; + $match['ended_at'] = $d->copy(); + $match['total_time'] = $match['began_at']->diffInSeconds($match['ended_at']); + $match['winner_user_id'] = $player1Marks > $player2Marks ? $match['player1_user_id'] : ($player2Marks > $player1Marks ? $match['player2_user_id'] : null); + $match['loser_user_id'] = $player1Marks > $player2Marks ? $match['player2_user_id'] : ($player2Marks > $player1Marks ? $match['player1_user_id'] : null); + } + + private $gameID = 0; + private function newGame($filteredPlayers, $match, $user1, $user2, $d) + { + $this->gameID++; + $this->nextGameDateTime($d, $filteredPlayers, $match != null); + $begin_d = $d->copy(); + $pointsUser1 = 60; + $pointsUser2 = 60; + // if random == 1 it is a draw + if (random_int(1,30) > 1) { + $pointsUser1 = rand(0, 120); + $pointsUser2 = 120 - $pointsUser1; + } + $duration = random_int(200, 900); + $d->addSeconds($duration); + + return [ + 'id' => $this->gameID, + 'type' => $match ? $match['type'] : (random_int(1, 2) == 1 ? '3' : '9'), + 'match_id' => $match ? $match['id'] : null, + 'player1_user_id' => $user1, + 'player2_user_id' => $user2, + 'is_draw' => $pointsUser1 == $pointsUser2, + 'winner_user_id' => $pointsUser1 > $pointsUser2 ? $user1 : ($pointsUser2 > $pointsUser1 ? $user2 : null), + 'loser_user_id' => $pointsUser1 < $pointsUser2 ? $user1 : ($pointsUser2 < $pointsUser1 ? $user2 : null), + 'status' => 'Ended', + 'began_at' => $begin_d, + 'ended_at' => $d->copy(), + 'total_time' => $duration, + 'player1_points' => $pointsUser1, + 'player2_points' => $pointsUser2, + 'custom' => null + ]; + } +} diff --git a/api/database/seeders/GamesTransactionsSeeder.php b/api/database/seeders/GamesTransactionsSeeder.php new file mode 100644 index 0000000..78255e3 --- /dev/null +++ b/api/database/seeders/GamesTransactionsSeeder.php @@ -0,0 +1,265 @@ +command->info("Create game related Transactions"); + $this->faker = \Faker\Factory::create(DatabaseSeeder::$seedLanguage); + + $this->users = DB::table('users')->orderBy('id', 'asc')->get(); + + $games = DB::table('games')->whereNull('match_id')->orderBy('began_at', 'asc')->get(); + $matches = DB::table('matches')->orderBy('began_at', 'asc')->get(); + + $coin_purchases = []; + $coin_transactions = []; + $i = 0; + while($games->count() > 0 || $matches->count() > 0) { + $firstGame = $games->first(); + $firstMatch = $matches->first(); + $isGameFirst = false; + if ($firstGame && $firstMatch) { + $isGameFirst = $firstGame->began_at < $firstMatch->began_at; + } elseif ($firstGame && !$firstMatch) { + $isGameFirst = true; + } elseif (!$firstGame && $firstMatch) { + $isGameFirst = false; + } + if ($isGameFirst) { + $this->handleGame($coin_purchases, $coin_transactions, $firstGame); + $games->shift(); + } else { + $this->handleMatch($coin_purchases, $coin_transactions, $firstMatch); + $matches->shift(); + } + + $i++; + if ($i >= DatabaseSeeder::$dbInsertBlockSize) { + $this->command->info("Block of coin purchases and transactions"); + if (!empty($coin_transactions)) { + DB::table('coin_transactions')->insert($coin_transactions); + $this->command->info("Saved " . count($coin_transactions) . " coin transactions"); + } + if (!empty($coin_purchases)) { + DB::table('coin_purchases')->insert($coin_purchases); + $this->command->info("Saved " . count($coin_purchases) . " coin purchases"); + } + $i = 0; + $coin_purchases = []; + $coin_transactions = []; + } + } + $this->command->info("Last block of coin purchases and transactions"); + if (!empty($coin_transactions)) { + DB::table('coin_transactions')->insert($coin_transactions); + $this->command->info("Saved " . count($coin_transactions) . " coin transactions"); + } + if (!empty($coin_purchases)) { + DB::table('coin_purchases')->insert($coin_purchases); + $this->command->info("Saved " . count($coin_purchases) . " coin purchases"); + } + $this->updateAllUsersBalance(); + $this->command->info("Game related Transactions Created Successfully."); + } + + private function getTimeStampFromDB($strDateFromDB, $minusOneSecond = false) + { + if ($minusOneSecond) { + return date("Y-m-d H:i:s", strtotime($strDateFromDB . " - 1 second")); + } + return date("Y-m-d H:i:s", strtotime($strDateFromDB)); + } + + + private function getRandomPayment($user, &$type, &$ref): void + { + $type = $this->faker->randomElement(['MBWAY', 'PAYPAL', 'IBAN', 'MB', 'VISA']); + switch ($type) { + case 'MBWAY': + $ref = rand(910000000, 999999999); + break; + case 'PAYPAL': + $ref = $user->email; + break; + case 'IBAN': + $ref = 'PT' . rand(5000000000000, 9999999999999) . rand(1000000000, 9999999999); + break; + case 'MB': + $ref = rand(10000, 99999) . '-' . rand(100000000, 999999999); + break; + case 'VISA': + $ref = rand(40000000, 49999999) . rand(10000000, 99999999); + break; + } + } + + private $purchaseID = null; + private function addPurchase(&$coin_purchases, &$coin_transactions, &$user, $time, $eurosAmount) + { + $this->purchaseID++; + $coins = $eurosAmount * 10; + + $newCoinTransaction = $this->addCoinTransaction($coin_transactions, $user, $coins, 2, $time, null, null); + + $this->getRandomPayment($user, $paymentType, $paymentRef); + + $coin_purchases[] = [ + 'id' => $this->purchaseID, + 'purchase_datetime' => $time, + 'user_id' => $user->id, + 'coin_transaction_id' => $newCoinTransaction['id'], + 'euros' => $eurosAmount, + 'payment_type' => $paymentType, + 'payment_reference' => $paymentRef, + 'custom' => null, + ]; + } + + private $transactionID = 0; + private function addCoinTransaction(&$coin_transactions, &$user, $coins, $type, $time, $game_id = null, $match_id = null) + { + if ($this->transactionID == null) { + $this->transactionID = DB::table('coin_transactions')->max('id'); + } + + // Types of coin transactions: + // 1 - Bonus + // 2 - Coin purchase + // 3 - Game fee - requires game_id + // 4 - Match stake - requires match_id + // 5 - Game payout - requires game_id + // 6 - Match payout - requires match_id + if(($type == 3 || $type == 5) && $game_id === null) { + throw new \Exception("Game ID is required for coin transaction type $type"); + } + if(($type == 4 || $type == 6) && $match_id === null) { + throw new \Exception("Match ID is required for coin transaction type $type"); + } + $this->transactionID++; + + $coins = abs($coins); + if (in_array($type, [3, 4])) { + $coins *= -1; + } + + $newCoinTransaction = [ + 'id' => $this->transactionID, + 'transaction_datetime' => $time, + 'user_id' => $user->id, + 'match_id' => $match_id, // nullable + 'game_id' => $game_id, // nullable + 'coin_transaction_type_id' => $type, + 'coins' => $coins, + 'custom' => null, + ]; + $coin_transactions[] = $newCoinTransaction; + $user->coins_balance += $coins; + return $newCoinTransaction; + } + + private function calculateGamePayouts($game) + { + $payouts = [0, 0]; + if ($game->player1_points == $game->player2_points) { + $payouts = [1, 1]; + } elseif ($game->player1_points == 120) { + $payouts = [6, 0]; + } elseif ($game->player2_points == 120) { + $payouts = [0, 6]; + } elseif ($game->player1_points >= 91) { + $payouts = [4, 0]; + } elseif ($game->player2_points >= 91) { + $payouts = [0, 4]; + } elseif ($game->player1_points >= 61) { + $payouts = [3, 0]; + } elseif ($game->player2_points >= 61) { + $payouts = [0, 3]; + } else { // this condition should never be reached + $payouts = [0, 0]; + } + return $payouts; + } + + private function calculateMatchPayouts($match) + { + $payouts = [0, 0]; + if ($match->winner_user_id == $match->player1_user_id) { + $payouts = [$match->stake * 2 - 1, 0]; + } elseif ($match->winner_user_id == $match->player2_user_id) { + $payouts = [0, $match->stake * 2 - 1]; + } + return $payouts; + } + + private function handleGame(&$coin_purchases, &$coin_transactions, $game) + { + $player1 = $this->users->where('id', $game->player1_user_id)->first(); + $player2 = $this->users->where('id', $game->player2_user_id)->first(); + // not enough coins to play - must purchase before the game + $timeOneSecondBefore = $this->getTimeStampFromDB($game->began_at, true); + if ($player1->coins_balance < 2) { + $this->addPurchase($coin_purchases, $coin_transactions, $player1, $timeOneSecondBefore, random_int(1, 10)); + } + // not enough coins to play - must purchase before the game + if ($player2->coins_balance < 2) { + $this->addPurchase($coin_purchases, $coin_transactions, $player2, $timeOneSecondBefore, random_int(1, 10)); + } + $this->addCoinTransaction($coin_transactions, $player1, -2, 3, $this->getTimeStampFromDB($game->began_at, false), $game->id); + $this->addCoinTransaction($coin_transactions, $player2, -2, 3, $this->getTimeStampFromDB($game->began_at, false), $game->id); + $payouts = $this->calculateGamePayouts($game); + if ($payouts[0] > 0) { + $this->addCoinTransaction($coin_transactions, $player1, $payouts[0], 5, $this->getTimeStampFromDB($game->ended_at, false), $game->id); + } + if ($payouts[1] > 0) { + $this->addCoinTransaction($coin_transactions, $player2, $payouts[1], 5, $this->getTimeStampFromDB($game->ended_at, false), $game->id); + } + } + + private function handleMatch(&$coin_purchases, &$coin_transactions, $match) + { + $player1 = $this->users->where('id', $match->player1_user_id)->first(); + $player2 = $this->users->where('id', $match->player2_user_id)->first(); + // not enough coins to play - must purchase before the match + $timeOneSecondBefore = $this->getTimeStampFromDB($match->began_at, true); + if ($player1->coins_balance < $match->stake) { + $requiredCoins = $match->stake - $player1->coins_balance; + $euros = ceil(($requiredCoins + 3) / 10); + $this->addPurchase($coin_purchases, $coin_transactions, $player1, $timeOneSecondBefore, $euros); + } + // not enough coins to play - must purchase before the match + if ($player2->coins_balance < $match->stake) { + $requiredCoins = $match->stake - $player2->coins_balance; + $euros = ceil(($requiredCoins + 3) / 10); + $this->addPurchase($coin_purchases, $coin_transactions, $player2, $timeOneSecondBefore, $euros); + } + $this->addCoinTransaction($coin_transactions, $player1, $match->stake * -1, 4, $this->getTimeStampFromDB($match->began_at, false), null, $match->id); + $this->addCoinTransaction($coin_transactions, $player2, $match->stake * -1, 4, $this->getTimeStampFromDB($match->began_at, false), null, $match->id); + $payouts = $this->calculateMatchPayouts($match); + if ($payouts[0] > 0) { + $this->addCoinTransaction($coin_transactions, $player1, $payouts[0], 6, $this->getTimeStampFromDB($match->ended_at, false), null, $match->id); + } + if ($payouts[1] > 0) { + $this->addCoinTransaction($coin_transactions, $player2, $payouts[1], 6, $this->getTimeStampFromDB($match->ended_at, false), null, $match->id); + } + } + + private function updateAllUsersBalance() + { + $this->command->info("Update users balances..."); + foreach ($this->users as $user) { + DB::table('users')->where('id', $user->id)->update(['coins_balance' => $user->coins_balance]); + } + $this->command->info("All users balances updated."); + } +} diff --git a/api/database/seeders/InitialTransactionsSeeder.php b/api/database/seeders/InitialTransactionsSeeder.php new file mode 100644 index 0000000..574f5b5 --- /dev/null +++ b/api/database/seeders/InitialTransactionsSeeder.php @@ -0,0 +1,62 @@ +allPlayers = DB::table('users')->where('type', 'P')->get(); + $this->command->info("Create initial Bonuses transactions"); + + $i = $this->sizeOfSaveBlock; + $dataBlock = []; + foreach ($this->allPlayers as $player) { + if ($i <= 0) { + $this->saveDataBlock($dataBlock); + $i = $this->sizeOfSaveBlock; + $dataBlock = []; + } + $dataBlock[] = $this->createBonusTransaction($player); + $i--; + } + if (!empty($dataBlock)) { + $this->saveDataBlock($dataBlock); + } + $this->updateAllPlayerBalances(); + } + + private function saveDataBlock($dataBlock): void + { + $size = count($dataBlock); + DB::table('coin_transactions')->insert($dataBlock); + $this->command->info("Saved $size initial bonuses transactions"); + } + + private function createBonusTransaction($player): array + { + return [ + 'transaction_datetime' => $player->created_at, + 'user_id' => $player->id, + 'match_id' => null, + 'game_id' => null, + 'coin_transaction_type_id' => 1, + 'coins' => $this->valueOfInitialBonus, + 'custom' => null, + ]; + } + + private function updateAllPlayerBalances(): void + { + DB::table('users')->where('type', 'P')->update(['coins_balance' => $this->valueOfInitialBonus]); + $this->command->info("Updated all players initial Balance"); + } +} diff --git a/api/database/seeders/SeederUtils.php b/api/database/seeders/SeederUtils.php new file mode 100644 index 0000000..c5390c8 --- /dev/null +++ b/api/database/seeders/SeederUtils.php @@ -0,0 +1,149 @@ +integerAsStringWithPadding($this->mt_rand($min, $max), $length); + } + + public function randomDecimal(float $min, float $max, int $decimals = 2): float + { + $factor = pow(10, $decimals); + return mt_rand($min * $factor, $max * $factor) / $factor; + } + + + public function getFileNameFromString($str, $extension) + { + return str_replace(' ', '_', strtolower($this->stripAccents($str))) . ".$extension"; + } + + public function stripAccents($stripAccents) + { + $from = 'àáâãäçèéêëìíîïñòóôõöùúûüýÿÀÁÂÃÄÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ'; + $to = 'aaaaaceeeeiiiinooooouuuuyyAAAAACEEEEIIIINOOOOOUUUUY'; + $keys = array(); + $values = array(); + preg_match_all('/./u', $from, $keys); + preg_match_all('/./u', $to, $values); + $mapping = array_combine($keys[0], $values[0]); + return strtr($stripAccents, $mapping); + } + + public function strtr_utf8($str, $from, $to) + { + $keys = array(); + $values = array(); + preg_match_all('/./u', $from, $keys); + preg_match_all('/./u', $to, $values); + $mapping = array_combine($keys[0], $values[0]); + return strtr($str, $mapping); + } + + public function randomName($faker, &$gender, &$fullname, &$email, $allowRepeated = false) + { + $gender = $gender ?? $faker->randomElement(['male', 'female']); + $firstname = $faker->firstName($gender); + $lastname = $faker->lastName(); + $secondname = mt_rand(1, 3) == 2 ? "" : " " . $faker->firstName($gender); + $number_middlenames = $faker->randomElement([0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3]); + $middlenames = ""; + for ($i = 0; $i < $number_middlenames; $i++) { + $middlenames .= " " . $faker->lastName(); + } + $fullname = $firstname . $secondname . $middlenames . " " . $lastname; + $email = strtolower($this->stripAccents($firstname) . "." . $this->stripAccents($lastname) . "@mail.pt"); + if (!$allowRepeated) { + $i = 2; + while (in_array($email, self::$used_emails)) { + $email = strtolower($this->stripAccents($firstname) . "." . $this->stripAccents($lastname) . "." . $i ."@mail.pt"); + $i++; + if ($i > 10) { + $this->command->error("Repeated email - not possible to create a unique email for $fullname!"); + exit(1); + } + } + } + self::$used_emails[] = $email; + $gender = $gender == 'male' ? 'M' : 'F'; + } + + public function ramdomPaymentMethod($email, &$paymentType, &$paymentReference) + { + $paymentType = $this->faker->randomElement(['Visa', 'PayPal', 'MB WAY']); + $paymentReference = match ($paymentType) { + 'Visa' => mt_rand(4, 6) . + $this->faker->randomNumber($nbDigits = 8, $strict = true) . + $this->faker->randomNumber($nbDigits = 7, $strict = true), + 'PayPal' => $email, + 'MB WAY' => '9' . $this->faker->randomNumber($nbDigits = 8, $strict = true) + }; + } + + public function cleanStorageFolder($folder, $public = true) + { + $storagePath = $public ? storage_path("app/public/$folder") : storage_path("app/private/$folder"); + if (File::exists($storagePath)) { + File::deleteDirectory($storagePath); + } + if (!File::exists(storage_path("app"))) { + File::makeDirectory(storage_path("app")); + } + if ($public) { + if (!File::exists(storage_path("app/public"))) { + File::makeDirectory(storage_path("app/public")); + } + } else { + if (!File::exists(storage_path("app/private"))) { + File::makeDirectory(storage_path("app/private")); + } + } + File::makeDirectory($storagePath); + } + + + public function copyFileToStorage($originalFolder, $originalFileName, $folder, $id = null, $public = true) + { + $originalFullName = database_path("seeders/$originalFolder") . '/' . $originalFileName; + $storagePath = $public ? storage_path("app/public/$folder") : storage_path("app/private/$folder"); + $prefix = $id ? $this->integerAsStringWithPadding($id, 5) . '_' : ''; + $ext = pathinfo($originalFullName, PATHINFO_EXTENSION); + $newFileName = $prefix . $this->randomString(10) . '.' . $ext; + if (!File::exists($storagePath)) { + File::makeDirectory($storagePath); + } + if (File::exists($originalFullName)) { + File::copy($originalFullName, "$storagePath/$newFileName"); + return $newFileName; + } + return null; + } + + public function directCopyFileToStorage($originalFolder, $originalFileName, $folder, $public = true) + { + $originalFullName = database_path("seeders/$originalFolder") . '/' . $originalFileName; + $storagePath = $public ? storage_path("app/public/$folder") : storage_path("app/private/$folder"); + if (!File::exists($storagePath)) { + File::makeDirectory($storagePath); + } + File::copy($originalFullName, "$storagePath/$originalFileName"); + return $originalFileName; + } +} diff --git a/api/database/seeders/TransactionTypesSeeder.php b/api/database/seeders/TransactionTypesSeeder.php new file mode 100644 index 0000000..f4cc7cc --- /dev/null +++ b/api/database/seeders/TransactionTypesSeeder.php @@ -0,0 +1,30 @@ +command->line("Creating Coin Transaction Types."); + + // Add default coin_transaction_types + DB::table('coin_transaction_types')->insert([ + ['id' => 1, 'name' => 'Bonus', 'type' => 'C'], // id = 1 + ['id' => 2, 'name' => 'Coin purchase', 'type' => 'C'], // id = 2 + ['id' => 3, 'name' => 'Game fee', 'type' => 'D'], // id = 3 + ['id' => 4, 'name' => 'Match stake', 'type' => 'D'], // id = 4 + ['id' => 5, 'name' => 'Game payout', 'type' => 'C'], // id = 5 + ['id' => 6, 'name' => 'Match payout', 'type' => 'C'], // id = 6 + ]); + + $this->command->line("Coin Transaction Types Created Successfully."); + } +} diff --git a/api/database/seeders/UsersSeeder.php b/api/database/seeders/UsersSeeder.php new file mode 100644 index 0000000..3a66851 --- /dev/null +++ b/api/database/seeders/UsersSeeder.php @@ -0,0 +1,173 @@ + 'A', 'name' => 'First Administrator', 'email' => 'a1@mail.pt', 'gender' => 'M', 'softdelete' => false], + ['type' => 'A', 'name' => 'Second Administrator', 'email' => 'a2@mail.pt', 'gender' => 'F', 'softdelete' => false], + ['type' => 'A', 'name' => 'Third Administrator', 'email' => 'a3@mail.pt', 'gender' => 'M', 'softdelete' => false], + ['type' => 'A', 'name' => 'Forth Administrator', 'email' => 'a4@mail.pt', 'gender' => 'M', 'softdelete' => true], + + ['type' => 'P', 'name' => 'Player A', 'email' => 'pa@mail.pt', 'gender' => 'M', 'softdelete' => false], + ['type' => 'P', 'name' => 'Player B', 'email' => 'pb@mail.pt', 'gender' => 'M', 'softdelete' => false], + ['type' => 'P', 'name' => 'Player C', 'email' => 'pc@mail.pt', 'gender' => 'F', 'softdelete' => false], + ['type' => 'P', 'name' => 'Player D', 'email' => 'pd@mail.pt', 'gender' => 'M', 'softdelete' => true], + ['type' => 'P', 'name' => 'Player E', 'email' => 'pe@mail.pt', 'gender' => 'F', 'softdelete' => false], + ['type' => 'P', 'name' => 'Player F', 'email' => 'pf@mail.pt', 'gender' => 'M', 'softdelete' => true], + ]; + + public static $userTypes = [ + 'A' => 10, + 'P' => 500]; + + public function run(): void + { + $this->command->line("Creating Users."); + self::$hashPasword = bcrypt('123'); + + //$this->faker = Factory::create('pt_PT'); + $this->faker = Factory::create(DatabaseSeeder::$seedLanguage); + + $this->cleanStorageFolder('photos_avatars'); + + $this->addUsersToDatabase(); + $this->addPhotoFiles(); + DB::table('users')->where('type', 'A')->update(['nickname' => null]); + self::$dbUsers = DB::table('users')->orderBy('id')->get(); + $this->command->line("Users Created Successfully."); + } + + private function addUsersToDatabase() + { + $this->command->line("Adding users to the database"); + $usersAdded = Self::$fixedUsers; + foreach (self::$userTypes as $userType => $totalUsers) { + for ($i = 0; $i < $totalUsers; $i++) { + $gender = null; + $name = null; + $email = null; + $this->randomName($this->faker, $gender, $name, $email, false); + $usersAdded[] = [ + 'type' => $userType, + 'name' => $name, + 'email' => $email, + 'gender' => $gender, + 'softdelete' => random_int(1, 20) == 1, + ]; + } + } + + $createdDate = DatabaseSeeder::$startDate->copy()->addMinutes(mt_rand(20000, 100000)); + foreach ($usersAdded as $key => $user) { + $usersAdded[$key]['password'] = self::$hashPasword; + $createdDate = $createdDate->copy()->addMinutes(mt_rand(10, 60)); + $usersAdded[$key]['created_at'] = $createdDate; + $usersAdded[$key]['email_verified_at'] = $createdDate->copy()->addMinutes(mt_rand(1, 9)); + if (random_int(1, 7) > 1) { + $usersAdded[$key]['updated_at'] = $createdDate->copy()->addMinutes(mt_rand(10, 10000)); + } else { + $usersAdded[$key]['updated_at'] = $createdDate; + } + $usersAdded[$key]['nickname'] = ($user['gender'] == 'M' ? 'Mickey' : 'Minnie') . ($key + 1) ; + $usersAdded[$key]['blocked'] = false; + $usersAdded[$key]['photo_avatar_filename'] = null; + $usersAdded[$key]['coins_balance'] = 0; + $usersAdded[$key]['deleted_at'] = null; + if ($usersAdded[$key]['softdelete']) { + $usersAdded[$key]['deleted_at'] = $createdDate->copy()->addMinutes(mt_rand(10001, 100000)); + } + } + + $arrayToStore = []; + foreach ($usersAdded as $user) { + unset($user['gender']); + unset($user['softdelete']); + $arrayToStore[] = $user; + + if (count($arrayToStore) >= DatabaseSeeder::$dbInsertBlockSize) { + DB::table('users')->insert($arrayToStore); + $this->command->line("Created " . count($arrayToStore) . " users"); + $arrayToStore = []; + } + } + if (count($arrayToStore) >= 1) { + DB::table('users')->insert($arrayToStore); + $this->command->line("Created " . count($arrayToStore) . " users"); + } + + $this->command->line("Total users created: " . DB::table('users')->count()); + self::$dbUsers = DB::table('users')->get(); + + print_r(self::$dbUsers); + } + + private function addPhotoFiles() + { + $this->command->line("Copying users' photos"); + $this->fillPhotoFilesNames(); + $total = count($this->files_M) + count($this->files_F); + $sortedUsers = self::$dbUsers->sortBy(function (stdClass $user) { + if ($user->id <= 10 ) { + return $user->id; + } + return match($user->type) { + 'A' => 20 + $user->id, + 'P' => 1000 + $user->id, + default => ($user->id < 50) ? 2000 + $user->id : 3000 + mt_rand(0, 100000), + }; + }); + $i = 0; + foreach($sortedUsers as $user) { + $originalFilename = str_starts_with($user->nickname, 'Mickey') ? array_shift($this->files_M) : array_shift($this->files_F); + if (!$originalFilename) { + if ((count($this->files_M) == 0) && (count($this->files_F) == 0)) + break; + } + if ($originalFilename) { + $originalFilename = basename($originalFilename); + $newFileName = $this->copyFileToStorage('photos', $originalFilename, 'photos_avatars', $user->id); + $user->photo_avatar_filename = $newFileName; + DB::table('users')->where('id', $user->id)->update(['photo_avatar_filename' => $user->photo_avatar_filename]); + $i++; + if ($i % 10 == 0) { + $this->command->line("User photo $i/$total copied"); + } + } + } + $this->command->line("Total of $total user's photos were copied!"); + $this->directCopyFileToStorage('photos', 'anonymous.png', 'photos_avatars'); + $this->command->line("Image for user with no associated photo was copied!"); + } + + private function fillPhotoFilesNames() + { + $allFiles = collect(File::files(database_path('seeders/photos'))); + foreach ($allFiles as $f) { + if (strpos($f->getPathname(), 'm_')) { + $this->files_M[] = $f->getPathname(); + } else if (strpos($f->getPathname(), 'w_')) { + $this->files_F[] = $f->getPathname(); + } + } + shuffle($this->files_M); + shuffle($this->files_F); + } +} diff --git a/api/database/seeders/photos/anonymous.png b/api/database/seeders/photos/anonymous.png new file mode 100644 index 0000000..fed6cec Binary files /dev/null and b/api/database/seeders/photos/anonymous.png differ diff --git a/api/database/seeders/photos/m_01.jpeg b/api/database/seeders/photos/m_01.jpeg new file mode 100644 index 0000000..fd6395b Binary files /dev/null and b/api/database/seeders/photos/m_01.jpeg differ diff --git a/api/database/seeders/photos/m_02.jpeg b/api/database/seeders/photos/m_02.jpeg new file mode 100644 index 0000000..b94ea78 Binary files /dev/null and b/api/database/seeders/photos/m_02.jpeg differ diff --git a/api/database/seeders/photos/m_03.jpeg b/api/database/seeders/photos/m_03.jpeg new file mode 100644 index 0000000..c0fa8c0 Binary files /dev/null and b/api/database/seeders/photos/m_03.jpeg differ diff --git a/api/database/seeders/photos/m_04.jpeg b/api/database/seeders/photos/m_04.jpeg new file mode 100644 index 0000000..fb64619 Binary files /dev/null and b/api/database/seeders/photos/m_04.jpeg differ diff --git a/api/database/seeders/photos/m_05.jpeg b/api/database/seeders/photos/m_05.jpeg new file mode 100644 index 0000000..f6b6097 Binary files /dev/null and b/api/database/seeders/photos/m_05.jpeg differ diff --git a/api/database/seeders/photos/m_06.jpeg b/api/database/seeders/photos/m_06.jpeg new file mode 100644 index 0000000..f3a3774 Binary files /dev/null and b/api/database/seeders/photos/m_06.jpeg differ diff --git a/api/database/seeders/photos/m_07.jpeg b/api/database/seeders/photos/m_07.jpeg new file mode 100644 index 0000000..d624b77 Binary files /dev/null and b/api/database/seeders/photos/m_07.jpeg differ diff --git a/api/database/seeders/photos/m_08.jpeg b/api/database/seeders/photos/m_08.jpeg new file mode 100644 index 0000000..7e45ef4 Binary files /dev/null and b/api/database/seeders/photos/m_08.jpeg differ diff --git a/api/database/seeders/photos/m_09.jpeg b/api/database/seeders/photos/m_09.jpeg new file mode 100644 index 0000000..347ecc2 Binary files /dev/null and b/api/database/seeders/photos/m_09.jpeg differ diff --git a/api/database/seeders/photos/m_10.jpeg b/api/database/seeders/photos/m_10.jpeg new file mode 100644 index 0000000..497c02c Binary files /dev/null and b/api/database/seeders/photos/m_10.jpeg differ diff --git a/api/database/seeders/photos/m_11.jpeg b/api/database/seeders/photos/m_11.jpeg new file mode 100644 index 0000000..53763b1 Binary files /dev/null and b/api/database/seeders/photos/m_11.jpeg differ diff --git a/api/database/seeders/photos/m_12.jpeg b/api/database/seeders/photos/m_12.jpeg new file mode 100644 index 0000000..9515afd Binary files /dev/null and b/api/database/seeders/photos/m_12.jpeg differ diff --git a/api/database/seeders/photos/m_13.jpeg b/api/database/seeders/photos/m_13.jpeg new file mode 100644 index 0000000..0567449 Binary files /dev/null and b/api/database/seeders/photos/m_13.jpeg differ diff --git a/api/database/seeders/photos/m_14.jpeg b/api/database/seeders/photos/m_14.jpeg new file mode 100644 index 0000000..7adc2c1 Binary files /dev/null and b/api/database/seeders/photos/m_14.jpeg differ diff --git a/api/database/seeders/photos/m_15.jpeg b/api/database/seeders/photos/m_15.jpeg new file mode 100644 index 0000000..dabf6a0 Binary files /dev/null and b/api/database/seeders/photos/m_15.jpeg differ diff --git a/api/database/seeders/photos/m_16.jpeg b/api/database/seeders/photos/m_16.jpeg new file mode 100644 index 0000000..c28c46f Binary files /dev/null and b/api/database/seeders/photos/m_16.jpeg differ diff --git a/api/database/seeders/photos/m_17.jpeg b/api/database/seeders/photos/m_17.jpeg new file mode 100644 index 0000000..17ef464 Binary files /dev/null and b/api/database/seeders/photos/m_17.jpeg differ diff --git a/api/database/seeders/photos/m_18.jpeg b/api/database/seeders/photos/m_18.jpeg new file mode 100644 index 0000000..98de629 Binary files /dev/null and b/api/database/seeders/photos/m_18.jpeg differ diff --git a/api/database/seeders/photos/m_19.jpeg b/api/database/seeders/photos/m_19.jpeg new file mode 100644 index 0000000..4edb7a7 Binary files /dev/null and b/api/database/seeders/photos/m_19.jpeg differ diff --git a/api/database/seeders/photos/m_20.jpeg b/api/database/seeders/photos/m_20.jpeg new file mode 100644 index 0000000..cb4f7cd Binary files /dev/null and b/api/database/seeders/photos/m_20.jpeg differ diff --git a/api/database/seeders/photos/m_21.jpeg b/api/database/seeders/photos/m_21.jpeg new file mode 100644 index 0000000..e44bd88 Binary files /dev/null and b/api/database/seeders/photos/m_21.jpeg differ diff --git a/api/database/seeders/photos/m_22.jpeg b/api/database/seeders/photos/m_22.jpeg new file mode 100644 index 0000000..db6df47 Binary files /dev/null and b/api/database/seeders/photos/m_22.jpeg differ diff --git a/api/database/seeders/photos/m_23.jpeg b/api/database/seeders/photos/m_23.jpeg new file mode 100644 index 0000000..9b28e77 Binary files /dev/null and b/api/database/seeders/photos/m_23.jpeg differ diff --git a/api/database/seeders/photos/m_24.jpeg b/api/database/seeders/photos/m_24.jpeg new file mode 100644 index 0000000..43c177a Binary files /dev/null and b/api/database/seeders/photos/m_24.jpeg differ diff --git a/api/database/seeders/photos/m_25.jpeg b/api/database/seeders/photos/m_25.jpeg new file mode 100644 index 0000000..70109f4 Binary files /dev/null and b/api/database/seeders/photos/m_25.jpeg differ diff --git a/api/database/seeders/photos/m_26.jpeg b/api/database/seeders/photos/m_26.jpeg new file mode 100644 index 0000000..39bec8f Binary files /dev/null and b/api/database/seeders/photos/m_26.jpeg differ diff --git a/api/database/seeders/photos/m_27.jpeg b/api/database/seeders/photos/m_27.jpeg new file mode 100644 index 0000000..78e11e3 Binary files /dev/null and b/api/database/seeders/photos/m_27.jpeg differ diff --git a/api/database/seeders/photos/m_28.jpeg b/api/database/seeders/photos/m_28.jpeg new file mode 100644 index 0000000..09e918f Binary files /dev/null and b/api/database/seeders/photos/m_28.jpeg differ diff --git a/api/database/seeders/photos/m_29.jpeg b/api/database/seeders/photos/m_29.jpeg new file mode 100644 index 0000000..ba43928 Binary files /dev/null and b/api/database/seeders/photos/m_29.jpeg differ diff --git a/api/database/seeders/photos/m_30.jpeg b/api/database/seeders/photos/m_30.jpeg new file mode 100644 index 0000000..9ce804e Binary files /dev/null and b/api/database/seeders/photos/m_30.jpeg differ diff --git a/api/database/seeders/photos/m_31.jpeg b/api/database/seeders/photos/m_31.jpeg new file mode 100644 index 0000000..a9beb17 Binary files /dev/null and b/api/database/seeders/photos/m_31.jpeg differ diff --git a/api/database/seeders/photos/m_32.jpeg b/api/database/seeders/photos/m_32.jpeg new file mode 100644 index 0000000..89ba45c Binary files /dev/null and b/api/database/seeders/photos/m_32.jpeg differ diff --git a/api/database/seeders/photos/m_33.jpeg b/api/database/seeders/photos/m_33.jpeg new file mode 100644 index 0000000..d1f5ac9 Binary files /dev/null and b/api/database/seeders/photos/m_33.jpeg differ diff --git a/api/database/seeders/photos/m_34.jpeg b/api/database/seeders/photos/m_34.jpeg new file mode 100644 index 0000000..91480b3 Binary files /dev/null and b/api/database/seeders/photos/m_34.jpeg differ diff --git a/api/database/seeders/photos/m_35.jpeg b/api/database/seeders/photos/m_35.jpeg new file mode 100644 index 0000000..81ec983 Binary files /dev/null and b/api/database/seeders/photos/m_35.jpeg differ diff --git a/api/database/seeders/photos/m_36.jpeg b/api/database/seeders/photos/m_36.jpeg new file mode 100644 index 0000000..579c5e0 Binary files /dev/null and b/api/database/seeders/photos/m_36.jpeg differ diff --git a/api/database/seeders/photos/m_37.jpeg b/api/database/seeders/photos/m_37.jpeg new file mode 100644 index 0000000..ebc49e8 Binary files /dev/null and b/api/database/seeders/photos/m_37.jpeg differ diff --git a/api/database/seeders/photos/m_38.jpeg b/api/database/seeders/photos/m_38.jpeg new file mode 100644 index 0000000..886c8b6 Binary files /dev/null and b/api/database/seeders/photos/m_38.jpeg differ diff --git a/api/database/seeders/photos/m_39.jpeg b/api/database/seeders/photos/m_39.jpeg new file mode 100644 index 0000000..9207a23 Binary files /dev/null and b/api/database/seeders/photos/m_39.jpeg differ diff --git a/api/database/seeders/photos/m_40.jpeg b/api/database/seeders/photos/m_40.jpeg new file mode 100644 index 0000000..157364e Binary files /dev/null and b/api/database/seeders/photos/m_40.jpeg differ diff --git a/api/database/seeders/photos/m_41.jpeg b/api/database/seeders/photos/m_41.jpeg new file mode 100644 index 0000000..b23d43b Binary files /dev/null and b/api/database/seeders/photos/m_41.jpeg differ diff --git a/api/database/seeders/photos/m_42.jpeg b/api/database/seeders/photos/m_42.jpeg new file mode 100644 index 0000000..4244b04 Binary files /dev/null and b/api/database/seeders/photos/m_42.jpeg differ diff --git a/api/database/seeders/photos/m_43.jpeg b/api/database/seeders/photos/m_43.jpeg new file mode 100644 index 0000000..aeebd9e Binary files /dev/null and b/api/database/seeders/photos/m_43.jpeg differ diff --git a/api/database/seeders/photos/m_44.jpeg b/api/database/seeders/photos/m_44.jpeg new file mode 100644 index 0000000..c723993 Binary files /dev/null and b/api/database/seeders/photos/m_44.jpeg differ diff --git a/api/database/seeders/photos/m_45.jpeg b/api/database/seeders/photos/m_45.jpeg new file mode 100644 index 0000000..6be1ccc Binary files /dev/null and b/api/database/seeders/photos/m_45.jpeg differ diff --git a/api/database/seeders/photos/m_46.jpeg b/api/database/seeders/photos/m_46.jpeg new file mode 100644 index 0000000..9cd5b95 Binary files /dev/null and b/api/database/seeders/photos/m_46.jpeg differ diff --git a/api/database/seeders/photos/m_47.jpeg b/api/database/seeders/photos/m_47.jpeg new file mode 100644 index 0000000..95c7f33 Binary files /dev/null and b/api/database/seeders/photos/m_47.jpeg differ diff --git a/api/database/seeders/photos/m_48.jpeg b/api/database/seeders/photos/m_48.jpeg new file mode 100644 index 0000000..9ab2962 Binary files /dev/null and b/api/database/seeders/photos/m_48.jpeg differ diff --git a/api/database/seeders/photos/m_49.jpeg b/api/database/seeders/photos/m_49.jpeg new file mode 100644 index 0000000..9fd9ec5 Binary files /dev/null and b/api/database/seeders/photos/m_49.jpeg differ diff --git a/api/database/seeders/photos/m_50.jpeg b/api/database/seeders/photos/m_50.jpeg new file mode 100644 index 0000000..d7dcccc Binary files /dev/null and b/api/database/seeders/photos/m_50.jpeg differ diff --git a/api/database/seeders/photos/m_51.jpeg b/api/database/seeders/photos/m_51.jpeg new file mode 100644 index 0000000..9a88e6a Binary files /dev/null and b/api/database/seeders/photos/m_51.jpeg differ diff --git a/api/database/seeders/photos/m_52.jpeg b/api/database/seeders/photos/m_52.jpeg new file mode 100644 index 0000000..9f08611 Binary files /dev/null and b/api/database/seeders/photos/m_52.jpeg differ diff --git a/api/database/seeders/photos/m_53.jpeg b/api/database/seeders/photos/m_53.jpeg new file mode 100644 index 0000000..b16ee0f Binary files /dev/null and b/api/database/seeders/photos/m_53.jpeg differ diff --git a/api/database/seeders/photos/m_54.jpeg b/api/database/seeders/photos/m_54.jpeg new file mode 100644 index 0000000..b153c25 Binary files /dev/null and b/api/database/seeders/photos/m_54.jpeg differ diff --git a/api/database/seeders/photos/m_55.jpeg b/api/database/seeders/photos/m_55.jpeg new file mode 100644 index 0000000..5f53f7c Binary files /dev/null and b/api/database/seeders/photos/m_55.jpeg differ diff --git a/api/database/seeders/photos/m_56.jpeg b/api/database/seeders/photos/m_56.jpeg new file mode 100644 index 0000000..ac10698 Binary files /dev/null and b/api/database/seeders/photos/m_56.jpeg differ diff --git a/api/database/seeders/photos/m_57.jpeg b/api/database/seeders/photos/m_57.jpeg new file mode 100644 index 0000000..7212480 Binary files /dev/null and b/api/database/seeders/photos/m_57.jpeg differ diff --git a/api/database/seeders/photos/m_58.jpeg b/api/database/seeders/photos/m_58.jpeg new file mode 100644 index 0000000..d9212da Binary files /dev/null and b/api/database/seeders/photos/m_58.jpeg differ diff --git a/api/database/seeders/photos/m_59.jpeg b/api/database/seeders/photos/m_59.jpeg new file mode 100644 index 0000000..6b2300e Binary files /dev/null and b/api/database/seeders/photos/m_59.jpeg differ diff --git a/api/database/seeders/photos/m_60.jpeg b/api/database/seeders/photos/m_60.jpeg new file mode 100644 index 0000000..1a0e37d Binary files /dev/null and b/api/database/seeders/photos/m_60.jpeg differ diff --git a/api/database/seeders/photos/m_61.jpeg b/api/database/seeders/photos/m_61.jpeg new file mode 100644 index 0000000..c8aea54 Binary files /dev/null and b/api/database/seeders/photos/m_61.jpeg differ diff --git a/api/database/seeders/photos/m_62.jpeg b/api/database/seeders/photos/m_62.jpeg new file mode 100644 index 0000000..5b08a1f Binary files /dev/null and b/api/database/seeders/photos/m_62.jpeg differ diff --git a/api/database/seeders/photos/m_63.jpeg b/api/database/seeders/photos/m_63.jpeg new file mode 100644 index 0000000..d8977dc Binary files /dev/null and b/api/database/seeders/photos/m_63.jpeg differ diff --git a/api/database/seeders/photos/m_64.jpeg b/api/database/seeders/photos/m_64.jpeg new file mode 100644 index 0000000..6418138 Binary files /dev/null and b/api/database/seeders/photos/m_64.jpeg differ diff --git a/api/database/seeders/photos/w_01.jpeg b/api/database/seeders/photos/w_01.jpeg new file mode 100644 index 0000000..0798a45 Binary files /dev/null and b/api/database/seeders/photos/w_01.jpeg differ diff --git a/api/database/seeders/photos/w_02.jpeg b/api/database/seeders/photos/w_02.jpeg new file mode 100644 index 0000000..38bd492 Binary files /dev/null and b/api/database/seeders/photos/w_02.jpeg differ diff --git a/api/database/seeders/photos/w_03.jpeg b/api/database/seeders/photos/w_03.jpeg new file mode 100644 index 0000000..82a4ca3 Binary files /dev/null and b/api/database/seeders/photos/w_03.jpeg differ diff --git a/api/database/seeders/photos/w_04.jpeg b/api/database/seeders/photos/w_04.jpeg new file mode 100644 index 0000000..4ef7de5 Binary files /dev/null and b/api/database/seeders/photos/w_04.jpeg differ diff --git a/api/database/seeders/photos/w_05.jpeg b/api/database/seeders/photos/w_05.jpeg new file mode 100644 index 0000000..cc76589 Binary files /dev/null and b/api/database/seeders/photos/w_05.jpeg differ diff --git a/api/database/seeders/photos/w_06.jpeg b/api/database/seeders/photos/w_06.jpeg new file mode 100644 index 0000000..3768e8c Binary files /dev/null and b/api/database/seeders/photos/w_06.jpeg differ diff --git a/api/database/seeders/photos/w_07.jpeg b/api/database/seeders/photos/w_07.jpeg new file mode 100644 index 0000000..cc05ba2 Binary files /dev/null and b/api/database/seeders/photos/w_07.jpeg differ diff --git a/api/database/seeders/photos/w_08.jpeg b/api/database/seeders/photos/w_08.jpeg new file mode 100644 index 0000000..2f89150 Binary files /dev/null and b/api/database/seeders/photos/w_08.jpeg differ diff --git a/api/database/seeders/photos/w_09.jpeg b/api/database/seeders/photos/w_09.jpeg new file mode 100644 index 0000000..9568b14 Binary files /dev/null and b/api/database/seeders/photos/w_09.jpeg differ diff --git a/api/database/seeders/photos/w_10.jpeg b/api/database/seeders/photos/w_10.jpeg new file mode 100644 index 0000000..2cb68f7 Binary files /dev/null and b/api/database/seeders/photos/w_10.jpeg differ diff --git a/api/database/seeders/photos/w_11.jpeg b/api/database/seeders/photos/w_11.jpeg new file mode 100644 index 0000000..3d7890b Binary files /dev/null and b/api/database/seeders/photos/w_11.jpeg differ diff --git a/api/database/seeders/photos/w_12.jpeg b/api/database/seeders/photos/w_12.jpeg new file mode 100644 index 0000000..90f1eeb Binary files /dev/null and b/api/database/seeders/photos/w_12.jpeg differ diff --git a/api/database/seeders/photos/w_13.jpeg b/api/database/seeders/photos/w_13.jpeg new file mode 100644 index 0000000..92b1220 Binary files /dev/null and b/api/database/seeders/photos/w_13.jpeg differ diff --git a/api/database/seeders/photos/w_14.jpeg b/api/database/seeders/photos/w_14.jpeg new file mode 100644 index 0000000..d077678 Binary files /dev/null and b/api/database/seeders/photos/w_14.jpeg differ diff --git a/api/database/seeders/photos/w_15.jpeg b/api/database/seeders/photos/w_15.jpeg new file mode 100644 index 0000000..54547ef Binary files /dev/null and b/api/database/seeders/photos/w_15.jpeg differ diff --git a/api/database/seeders/photos/w_16.jpeg b/api/database/seeders/photos/w_16.jpeg new file mode 100644 index 0000000..51f464c Binary files /dev/null and b/api/database/seeders/photos/w_16.jpeg differ diff --git a/api/database/seeders/photos/w_17.jpeg b/api/database/seeders/photos/w_17.jpeg new file mode 100644 index 0000000..0e4f205 Binary files /dev/null and b/api/database/seeders/photos/w_17.jpeg differ diff --git a/api/database/seeders/photos/w_18.jpeg b/api/database/seeders/photos/w_18.jpeg new file mode 100644 index 0000000..00e4af9 Binary files /dev/null and b/api/database/seeders/photos/w_18.jpeg differ diff --git a/api/database/seeders/photos/w_19.jpeg b/api/database/seeders/photos/w_19.jpeg new file mode 100644 index 0000000..fabccc4 Binary files /dev/null and b/api/database/seeders/photos/w_19.jpeg differ diff --git a/api/database/seeders/photos/w_20.jpeg b/api/database/seeders/photos/w_20.jpeg new file mode 100644 index 0000000..9abb6e4 Binary files /dev/null and b/api/database/seeders/photos/w_20.jpeg differ diff --git a/api/database/seeders/photos/w_21.jpeg b/api/database/seeders/photos/w_21.jpeg new file mode 100644 index 0000000..3f21b06 Binary files /dev/null and b/api/database/seeders/photos/w_21.jpeg differ diff --git a/api/database/seeders/photos/w_22.jpeg b/api/database/seeders/photos/w_22.jpeg new file mode 100644 index 0000000..578a874 Binary files /dev/null and b/api/database/seeders/photos/w_22.jpeg differ diff --git a/api/database/seeders/photos/w_23.jpeg b/api/database/seeders/photos/w_23.jpeg new file mode 100644 index 0000000..309637a Binary files /dev/null and b/api/database/seeders/photos/w_23.jpeg differ diff --git a/api/database/seeders/photos/w_24.jpeg b/api/database/seeders/photos/w_24.jpeg new file mode 100644 index 0000000..3ece4e8 Binary files /dev/null and b/api/database/seeders/photos/w_24.jpeg differ diff --git a/api/database/seeders/photos/w_25.jpeg b/api/database/seeders/photos/w_25.jpeg new file mode 100644 index 0000000..61d3efc Binary files /dev/null and b/api/database/seeders/photos/w_25.jpeg differ diff --git a/api/database/seeders/photos/w_26.jpeg b/api/database/seeders/photos/w_26.jpeg new file mode 100644 index 0000000..b13453f Binary files /dev/null and b/api/database/seeders/photos/w_26.jpeg differ diff --git a/api/database/seeders/photos/w_27.jpeg b/api/database/seeders/photos/w_27.jpeg new file mode 100644 index 0000000..fdc8478 Binary files /dev/null and b/api/database/seeders/photos/w_27.jpeg differ diff --git a/api/database/seeders/photos/w_28.jpeg b/api/database/seeders/photos/w_28.jpeg new file mode 100644 index 0000000..6011230 Binary files /dev/null and b/api/database/seeders/photos/w_28.jpeg differ diff --git a/api/database/seeders/photos/w_29.jpeg b/api/database/seeders/photos/w_29.jpeg new file mode 100644 index 0000000..0e31419 Binary files /dev/null and b/api/database/seeders/photos/w_29.jpeg differ diff --git a/api/database/seeders/photos/w_30.jpeg b/api/database/seeders/photos/w_30.jpeg new file mode 100644 index 0000000..83fa490 Binary files /dev/null and b/api/database/seeders/photos/w_30.jpeg differ diff --git a/api/database/seeders/photos/w_31.jpeg b/api/database/seeders/photos/w_31.jpeg new file mode 100644 index 0000000..afd2823 Binary files /dev/null and b/api/database/seeders/photos/w_31.jpeg differ diff --git a/api/database/seeders/photos/w_32.jpeg b/api/database/seeders/photos/w_32.jpeg new file mode 100644 index 0000000..d73c313 Binary files /dev/null and b/api/database/seeders/photos/w_32.jpeg differ diff --git a/api/database/seeders/photos/w_33.jpeg b/api/database/seeders/photos/w_33.jpeg new file mode 100644 index 0000000..70f34c9 Binary files /dev/null and b/api/database/seeders/photos/w_33.jpeg differ diff --git a/api/database/seeders/photos/w_34.jpeg b/api/database/seeders/photos/w_34.jpeg new file mode 100644 index 0000000..9de8b2d Binary files /dev/null and b/api/database/seeders/photos/w_34.jpeg differ diff --git a/api/database/seeders/photos/w_35.jpeg b/api/database/seeders/photos/w_35.jpeg new file mode 100644 index 0000000..1cbe5ae Binary files /dev/null and b/api/database/seeders/photos/w_35.jpeg differ diff --git a/api/database/seeders/photos/w_36.jpeg b/api/database/seeders/photos/w_36.jpeg new file mode 100644 index 0000000..1871de5 Binary files /dev/null and b/api/database/seeders/photos/w_36.jpeg differ diff --git a/api/database/seeders/photos/w_37.jpeg b/api/database/seeders/photos/w_37.jpeg new file mode 100644 index 0000000..a32bd80 Binary files /dev/null and b/api/database/seeders/photos/w_37.jpeg differ diff --git a/api/database/seeders/photos/w_38.jpeg b/api/database/seeders/photos/w_38.jpeg new file mode 100644 index 0000000..caabfa0 Binary files /dev/null and b/api/database/seeders/photos/w_38.jpeg differ diff --git a/api/database/seeders/photos/w_39.jpeg b/api/database/seeders/photos/w_39.jpeg new file mode 100644 index 0000000..b172a3d Binary files /dev/null and b/api/database/seeders/photos/w_39.jpeg differ diff --git a/api/database/seeders/photos/w_40.jpeg b/api/database/seeders/photos/w_40.jpeg new file mode 100644 index 0000000..929abec Binary files /dev/null and b/api/database/seeders/photos/w_40.jpeg differ diff --git a/api/database/seeders/photos/w_41.jpeg b/api/database/seeders/photos/w_41.jpeg new file mode 100644 index 0000000..b7af1d6 Binary files /dev/null and b/api/database/seeders/photos/w_41.jpeg differ diff --git a/api/database/seeders/photos/w_42.jpeg b/api/database/seeders/photos/w_42.jpeg new file mode 100644 index 0000000..26591c1 Binary files /dev/null and b/api/database/seeders/photos/w_42.jpeg differ diff --git a/api/database/seeders/photos/w_43.jpeg b/api/database/seeders/photos/w_43.jpeg new file mode 100644 index 0000000..3299ff5 Binary files /dev/null and b/api/database/seeders/photos/w_43.jpeg differ diff --git a/api/database/seeders/photos/w_44.jpeg b/api/database/seeders/photos/w_44.jpeg new file mode 100644 index 0000000..7917382 Binary files /dev/null and b/api/database/seeders/photos/w_44.jpeg differ diff --git a/api/database/seeders/photos/w_45.jpeg b/api/database/seeders/photos/w_45.jpeg new file mode 100644 index 0000000..7cf9f48 Binary files /dev/null and b/api/database/seeders/photos/w_45.jpeg differ diff --git a/api/database/seeders/photos/w_46.jpeg b/api/database/seeders/photos/w_46.jpeg new file mode 100644 index 0000000..9b30170 Binary files /dev/null and b/api/database/seeders/photos/w_46.jpeg differ diff --git a/api/database/seeders/photos/w_47.jpeg b/api/database/seeders/photos/w_47.jpeg new file mode 100644 index 0000000..19aadc3 Binary files /dev/null and b/api/database/seeders/photos/w_47.jpeg differ diff --git a/api/database/seeders/photos/w_48.jpeg b/api/database/seeders/photos/w_48.jpeg new file mode 100644 index 0000000..37ad917 Binary files /dev/null and b/api/database/seeders/photos/w_48.jpeg differ diff --git a/api/database/seeders/photos/w_49.jpeg b/api/database/seeders/photos/w_49.jpeg new file mode 100644 index 0000000..2aa639c Binary files /dev/null and b/api/database/seeders/photos/w_49.jpeg differ diff --git a/api/database/seeders/photos/w_50.jpeg b/api/database/seeders/photos/w_50.jpeg new file mode 100644 index 0000000..db10a2d Binary files /dev/null and b/api/database/seeders/photos/w_50.jpeg differ diff --git a/api/database/seeders/photos/w_51.jpeg b/api/database/seeders/photos/w_51.jpeg new file mode 100644 index 0000000..58d648f Binary files /dev/null and b/api/database/seeders/photos/w_51.jpeg differ diff --git a/api/database/seeders/photos/w_52.jpeg b/api/database/seeders/photos/w_52.jpeg new file mode 100644 index 0000000..d746064 Binary files /dev/null and b/api/database/seeders/photos/w_52.jpeg differ diff --git a/api/database/seeders/photos/w_53.jpeg b/api/database/seeders/photos/w_53.jpeg new file mode 100644 index 0000000..1597d59 Binary files /dev/null and b/api/database/seeders/photos/w_53.jpeg differ diff --git a/api/database/seeders/photos/w_54.jpeg b/api/database/seeders/photos/w_54.jpeg new file mode 100644 index 0000000..8489fd5 Binary files /dev/null and b/api/database/seeders/photos/w_54.jpeg differ diff --git a/api/database/seeders/photos/w_55.jpeg b/api/database/seeders/photos/w_55.jpeg new file mode 100644 index 0000000..aa2aae6 Binary files /dev/null and b/api/database/seeders/photos/w_55.jpeg differ diff --git a/api/database/seeders/photos/w_56.jpeg b/api/database/seeders/photos/w_56.jpeg new file mode 100644 index 0000000..c35256b Binary files /dev/null and b/api/database/seeders/photos/w_56.jpeg differ diff --git a/api/database/seeders/photos/w_57.jpeg b/api/database/seeders/photos/w_57.jpeg new file mode 100644 index 0000000..5228815 Binary files /dev/null and b/api/database/seeders/photos/w_57.jpeg differ diff --git a/api/database/seeders/photos/w_58.jpeg b/api/database/seeders/photos/w_58.jpeg new file mode 100644 index 0000000..883107d Binary files /dev/null and b/api/database/seeders/photos/w_58.jpeg differ diff --git a/api/database/seeders/photos/w_59.jpeg b/api/database/seeders/photos/w_59.jpeg new file mode 100644 index 0000000..d8dab8b Binary files /dev/null and b/api/database/seeders/photos/w_59.jpeg differ diff --git a/api/database/seeders/photos/w_60.jpeg b/api/database/seeders/photos/w_60.jpeg new file mode 100644 index 0000000..778305f Binary files /dev/null and b/api/database/seeders/photos/w_60.jpeg differ diff --git a/api/database/seeders/photos/w_61.jpeg b/api/database/seeders/photos/w_61.jpeg new file mode 100644 index 0000000..f1dbc8f Binary files /dev/null and b/api/database/seeders/photos/w_61.jpeg differ diff --git a/api/database/seeders/photos/w_62.jpeg b/api/database/seeders/photos/w_62.jpeg new file mode 100644 index 0000000..1f4f11b Binary files /dev/null and b/api/database/seeders/photos/w_62.jpeg differ diff --git a/api/database/seeders/photos/w_63.jpeg b/api/database/seeders/photos/w_63.jpeg new file mode 100644 index 0000000..27302bb Binary files /dev/null and b/api/database/seeders/photos/w_63.jpeg differ diff --git a/api/database/seeders/photos/w_64.jpeg b/api/database/seeders/photos/w_64.jpeg new file mode 100644 index 0000000..cd598cf Binary files /dev/null and b/api/database/seeders/photos/w_64.jpeg differ diff --git a/api/package-lock.json b/api/package-lock.json new file mode 100644 index 0000000..f61e40e --- /dev/null +++ b/api/package-lock.json @@ -0,0 +1,2376 @@ +{ + "name": "api", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "@tailwindcss/vite": "^4.0.0", + "axios": "^1.11.0", + "concurrently": "^9.0.1", + "laravel-vite-plugin": "^2.0.0", + "tailwindcss": "^4.0.0", + "vite": "^7.0.7" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "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.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz", + "integrity": "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz", + "integrity": "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz", + "integrity": "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz", + "integrity": "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz", + "integrity": "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz", + "integrity": "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz", + "integrity": "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz", + "integrity": "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz", + "integrity": "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz", + "integrity": "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz", + "integrity": "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz", + "integrity": "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz", + "integrity": "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz", + "integrity": "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz", + "integrity": "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.2.tgz", + "integrity": "sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.2.tgz", + "integrity": "sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz", + "integrity": "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz", + "integrity": "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz", + "integrity": "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz", + "integrity": "sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.2.tgz", + "integrity": "sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz", + "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.6.1", + "lightningcss": "1.30.2", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.17" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz", + "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-x64": "4.1.17", + "@tailwindcss/oxide-freebsd-x64": "4.1.17", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-x64-musl": "4.1.17", + "@tailwindcss/oxide-wasm32-wasi": "4.1.17", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz", + "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", + "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz", + "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz", + "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz", + "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz", + "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz", + "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz", + "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz", + "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz", + "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==", + "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.6.0", + "@emnapi/runtime": "^1.6.0", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.0.7", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz", + "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz", + "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.17.tgz", + "integrity": "sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.17", + "@tailwindcss/oxide": "4.1.17", + "tailwindcss": "4.1.17" + }, + "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/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/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.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "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/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.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "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.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "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/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.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "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-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", + "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.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "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/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/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.53.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz", + "integrity": "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==", + "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.53.2", + "@rollup/rollup-android-arm64": "4.53.2", + "@rollup/rollup-darwin-arm64": "4.53.2", + "@rollup/rollup-darwin-x64": "4.53.2", + "@rollup/rollup-freebsd-arm64": "4.53.2", + "@rollup/rollup-freebsd-x64": "4.53.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.2", + "@rollup/rollup-linux-arm-musleabihf": "4.53.2", + "@rollup/rollup-linux-arm64-gnu": "4.53.2", + "@rollup/rollup-linux-arm64-musl": "4.53.2", + "@rollup/rollup-linux-loong64-gnu": "4.53.2", + "@rollup/rollup-linux-ppc64-gnu": "4.53.2", + "@rollup/rollup-linux-riscv64-gnu": "4.53.2", + "@rollup/rollup-linux-riscv64-musl": "4.53.2", + "@rollup/rollup-linux-s390x-gnu": "4.53.2", + "@rollup/rollup-linux-x64-gnu": "4.53.2", + "@rollup/rollup-linux-x64-musl": "4.53.2", + "@rollup/rollup-openharmony-arm64": "4.53.2", + "@rollup/rollup-win32-arm64-msvc": "4.53.2", + "@rollup/rollup-win32-ia32-msvc": "4.53.2", + "@rollup/rollup-win32-x64-gnu": "4.53.2", + "@rollup/rollup-win32-x64-msvc": "4.53.2", + "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/tailwindcss": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", + "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "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.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz", + "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", + "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.15" + }, + "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/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/frontend/src/components/c1.png b/frontend/src/components/c1.png new file mode 100644 index 0000000..b422124 Binary files /dev/null and b/frontend/src/components/c1.png differ diff --git a/frontend/src/components/c11.png b/frontend/src/components/c11.png new file mode 100644 index 0000000..bf342bc Binary files /dev/null and b/frontend/src/components/c11.png differ diff --git a/frontend/src/components/c12.png b/frontend/src/components/c12.png new file mode 100644 index 0000000..21839e6 Binary files /dev/null and b/frontend/src/components/c12.png differ diff --git a/frontend/src/components/c13.png b/frontend/src/components/c13.png new file mode 100644 index 0000000..1d3c468 Binary files /dev/null and b/frontend/src/components/c13.png differ diff --git a/frontend/src/components/c2.png b/frontend/src/components/c2.png new file mode 100644 index 0000000..75a014f Binary files /dev/null and b/frontend/src/components/c2.png differ diff --git a/frontend/src/components/c3.png b/frontend/src/components/c3.png new file mode 100644 index 0000000..8e74673 Binary files /dev/null and b/frontend/src/components/c3.png differ diff --git a/frontend/src/components/c4.png b/frontend/src/components/c4.png new file mode 100644 index 0000000..ceecbfe Binary files /dev/null and b/frontend/src/components/c4.png differ diff --git a/frontend/src/components/c5.png b/frontend/src/components/c5.png new file mode 100644 index 0000000..d923456 Binary files /dev/null and b/frontend/src/components/c5.png differ diff --git a/frontend/src/components/c6.png b/frontend/src/components/c6.png new file mode 100644 index 0000000..361643e Binary files /dev/null and b/frontend/src/components/c6.png differ diff --git a/frontend/src/components/c7.png b/frontend/src/components/c7.png new file mode 100644 index 0000000..19b89a2 Binary files /dev/null and b/frontend/src/components/c7.png differ diff --git a/frontend/src/components/e1.png b/frontend/src/components/e1.png new file mode 100644 index 0000000..fbc3a2d Binary files /dev/null and b/frontend/src/components/e1.png differ diff --git a/frontend/src/components/e11.png b/frontend/src/components/e11.png new file mode 100644 index 0000000..f539c19 Binary files /dev/null and b/frontend/src/components/e11.png differ diff --git a/frontend/src/components/e12.png b/frontend/src/components/e12.png new file mode 100644 index 0000000..7983d03 Binary files /dev/null and b/frontend/src/components/e12.png differ diff --git a/frontend/src/components/e13.png b/frontend/src/components/e13.png new file mode 100644 index 0000000..2edbbc1 Binary files /dev/null and b/frontend/src/components/e13.png differ diff --git a/frontend/src/components/e2.png b/frontend/src/components/e2.png new file mode 100644 index 0000000..1ce0ffe Binary files /dev/null and b/frontend/src/components/e2.png differ diff --git a/frontend/src/components/e3.png b/frontend/src/components/e3.png new file mode 100644 index 0000000..f9e06b4 Binary files /dev/null and b/frontend/src/components/e3.png differ diff --git a/frontend/src/components/e4.png b/frontend/src/components/e4.png new file mode 100644 index 0000000..95abe3e Binary files /dev/null and b/frontend/src/components/e4.png differ diff --git a/frontend/src/components/e5.png b/frontend/src/components/e5.png new file mode 100644 index 0000000..53a1aad Binary files /dev/null and b/frontend/src/components/e5.png differ diff --git a/frontend/src/components/e6.png b/frontend/src/components/e6.png new file mode 100644 index 0000000..40242a7 Binary files /dev/null and b/frontend/src/components/e6.png differ diff --git a/frontend/src/components/e7.png b/frontend/src/components/e7.png new file mode 100644 index 0000000..b9f1b93 Binary files /dev/null and b/frontend/src/components/e7.png differ diff --git a/frontend/src/components/o1.png b/frontend/src/components/o1.png new file mode 100644 index 0000000..79cd3b8 Binary files /dev/null and b/frontend/src/components/o1.png differ diff --git a/frontend/src/components/o11.png b/frontend/src/components/o11.png new file mode 100644 index 0000000..131a977 Binary files /dev/null and b/frontend/src/components/o11.png differ diff --git a/frontend/src/components/o12.png b/frontend/src/components/o12.png new file mode 100644 index 0000000..928f650 Binary files /dev/null and b/frontend/src/components/o12.png differ diff --git a/frontend/src/components/o13.png b/frontend/src/components/o13.png new file mode 100644 index 0000000..e21d6a0 Binary files /dev/null and b/frontend/src/components/o13.png differ diff --git a/frontend/src/components/o2.png b/frontend/src/components/o2.png new file mode 100644 index 0000000..4deee7c Binary files /dev/null and b/frontend/src/components/o2.png differ diff --git a/frontend/src/components/o3.png b/frontend/src/components/o3.png new file mode 100644 index 0000000..8ee0b4b Binary files /dev/null and b/frontend/src/components/o3.png differ diff --git a/frontend/src/components/o4.png b/frontend/src/components/o4.png new file mode 100644 index 0000000..70e82e8 Binary files /dev/null and b/frontend/src/components/o4.png differ diff --git a/frontend/src/components/o5.png b/frontend/src/components/o5.png new file mode 100644 index 0000000..bb92525 Binary files /dev/null and b/frontend/src/components/o5.png differ diff --git a/frontend/src/components/o6.png b/frontend/src/components/o6.png new file mode 100644 index 0000000..78a80ad Binary files /dev/null and b/frontend/src/components/o6.png differ diff --git a/frontend/src/components/o7.png b/frontend/src/components/o7.png new file mode 100644 index 0000000..6ad5f15 Binary files /dev/null and b/frontend/src/components/o7.png differ diff --git a/frontend/src/components/p1.png b/frontend/src/components/p1.png new file mode 100644 index 0000000..42bf5ec Binary files /dev/null and b/frontend/src/components/p1.png differ diff --git a/frontend/src/components/p11.png b/frontend/src/components/p11.png new file mode 100644 index 0000000..5e003be Binary files /dev/null and b/frontend/src/components/p11.png differ diff --git a/frontend/src/components/p12.png b/frontend/src/components/p12.png new file mode 100644 index 0000000..7be5f9a Binary files /dev/null and b/frontend/src/components/p12.png differ diff --git a/frontend/src/components/p13.png b/frontend/src/components/p13.png new file mode 100644 index 0000000..68e5774 Binary files /dev/null and b/frontend/src/components/p13.png differ diff --git a/frontend/src/components/p2.png b/frontend/src/components/p2.png new file mode 100644 index 0000000..291ed97 Binary files /dev/null and b/frontend/src/components/p2.png differ diff --git a/frontend/src/components/p3.png b/frontend/src/components/p3.png new file mode 100644 index 0000000..076ab31 Binary files /dev/null and b/frontend/src/components/p3.png differ diff --git a/frontend/src/components/p4.png b/frontend/src/components/p4.png new file mode 100644 index 0000000..8be9e08 Binary files /dev/null and b/frontend/src/components/p4.png differ diff --git a/frontend/src/components/p5.png b/frontend/src/components/p5.png new file mode 100644 index 0000000..bde9777 Binary files /dev/null and b/frontend/src/components/p5.png differ diff --git a/frontend/src/components/p6.png b/frontend/src/components/p6.png new file mode 100644 index 0000000..a9660a0 Binary files /dev/null and b/frontend/src/components/p6.png differ diff --git a/frontend/src/components/p7.png b/frontend/src/components/p7.png new file mode 100644 index 0000000..9d6b545 Binary files /dev/null and b/frontend/src/components/p7.png differ diff --git a/frontend/src/components/semFace.png b/frontend/src/components/semFace.png new file mode 100644 index 0000000..9357c1e Binary files /dev/null and b/frontend/src/components/semFace.png differ