diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000000000000000000000000000000000..8f0de65c560259bd171d746d12aa187f666893a3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[docker-compose.yml] +indent_size = 4 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000000000000000000000000000000000000..35db1ddf0e04fb9f138a372b17d0bfc8480bef38 --- /dev/null +++ b/.env.example @@ -0,0 +1,65 @@ +APP_NAME=Laravel +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_URL=http://localhost + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database + +PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=sqlite +# DB_HOST=127.0.0.1 +# DB_PORT=3306 +# DB_DATABASE=laravel +# DB_USERNAME=root +# DB_PASSWORD= + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=database +# CACHE_PREFIX= + +MEMCACHED_HOST=127.0.0.1 + +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=log +MAIL_SCHEME=null +MAIL_HOST=127.0.0.1 +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +VITE_APP_NAME="${APP_NAME}" diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..77c3019a2ba765fae7c781c7f51336d76c0ab18e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,35 +1,28 @@ -*.7z filter=lfs diff=lfs merge=lfs -text -*.arrow filter=lfs diff=lfs merge=lfs -text -*.bin filter=lfs diff=lfs merge=lfs -text -*.bz2 filter=lfs diff=lfs merge=lfs -text -*.ckpt filter=lfs diff=lfs merge=lfs -text -*.ftz filter=lfs diff=lfs merge=lfs -text -*.gz filter=lfs diff=lfs merge=lfs -text -*.h5 filter=lfs diff=lfs merge=lfs -text -*.joblib filter=lfs diff=lfs merge=lfs -text -*.lfs.* filter=lfs diff=lfs merge=lfs -text -*.mlmodel filter=lfs diff=lfs merge=lfs -text -*.model filter=lfs diff=lfs merge=lfs -text -*.msgpack filter=lfs diff=lfs merge=lfs -text -*.npy filter=lfs diff=lfs merge=lfs -text -*.npz filter=lfs diff=lfs merge=lfs -text -*.onnx filter=lfs diff=lfs merge=lfs -text -*.ot filter=lfs diff=lfs merge=lfs -text -*.parquet filter=lfs diff=lfs merge=lfs -text -*.pb filter=lfs diff=lfs merge=lfs -text -*.pickle filter=lfs diff=lfs merge=lfs -text -*.pkl filter=lfs diff=lfs merge=lfs -text -*.pt filter=lfs diff=lfs merge=lfs -text -*.pth filter=lfs diff=lfs merge=lfs -text -*.rar filter=lfs diff=lfs merge=lfs -text -*.safetensors filter=lfs diff=lfs merge=lfs -text -saved_model/**/* filter=lfs diff=lfs merge=lfs -text -*.tar.* filter=lfs diff=lfs merge=lfs -text -*.tar filter=lfs diff=lfs merge=lfs -text -*.tflite filter=lfs diff=lfs merge=lfs -text -*.tgz filter=lfs diff=lfs merge=lfs -text -*.wasm filter=lfs diff=lfs merge=lfs -text -*.xz filter=lfs diff=lfs merge=lfs -text -*.zip filter=lfs diff=lfs merge=lfs -text -*.zst filter=lfs diff=lfs merge=lfs -text -*tfevents* filter=lfs diff=lfs merge=lfs -text +* text=auto eol=lf + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +CHANGELOG.md export-ignore +.styleci.yml export-ignore +storage/app/public/payment_slips/Gdx6m0rw4cUSUAufq3NTNAUWyQSKjRRg7eLmIZEA.gif filter=lfs diff=lfs merge=lfs -text +storage/app/public/payment_slips/seALBjryd95AWErPzTvLGVfMOdvwDQVYK62vIJ5D.png filter=lfs diff=lfs merge=lfs -text +storage/app/public/payment_slips/YaS0Sd9UUJBNmekUXcgndzeVeGfXGMiTOnxnvEYj.jpg filter=lfs diff=lfs merge=lfs -text +storage/app/public/products/3Qk5m64c3TAqttQLi39MPeukQNG2rSEhZ8g1l9qI.jpg filter=lfs diff=lfs merge=lfs -text +storage/app/public/products/6CSKo9nXd4Xj61MEEmXeH3LO0jcVh1HCqowzQoBk.png filter=lfs diff=lfs merge=lfs -text +storage/app/public/products/cxSKE9LV3Zg1KzpaIFm43T9fCL3UngNSeJevsthU.jpg filter=lfs diff=lfs merge=lfs -text +storage/app/public/products/D3419EtxxLBwPIZQtYa3FfDXpVrQNQOHtdEfOK8U.png filter=lfs diff=lfs merge=lfs -text +storage/app/public/products/HdmkT7kc8AjFLIbZMseuNMLuo4MwnbWXzERl2hQQ.png filter=lfs diff=lfs merge=lfs -text +storage/app/public/products/HxgaBcFWUAVlXmPH4wsasrVE3NDzHqApCC4EJe76.png filter=lfs diff=lfs merge=lfs -text +storage/app/public/products/I1X5iyRSD2GD1Vi6dwbE37rhzgPFFQf80JDmpkFQ.jpg filter=lfs diff=lfs merge=lfs -text +storage/app/public/products/iG3yc6NCOj5bWJhVk4JLIUJeg42lmfWyLcPuHQdX.png filter=lfs diff=lfs merge=lfs -text +storage/app/public/products/Keuu1FkTKIBfFghhZqn7Ly5uw33Yetqk0DlhjDjN.jpg filter=lfs diff=lfs merge=lfs -text +storage/app/public/products/KOzqgZj7XUkMt2uPDYRXXKFVT1gmfRSh9RwB4Qnp.png filter=lfs diff=lfs merge=lfs -text +storage/app/public/products/p1IZEaJITbWLSg0A2mS4dg0zfmNzQbsoFk6vEDvy.png filter=lfs diff=lfs merge=lfs -text +storage/app/public/products/XYixdNVbWsh0WDYV9YiNN99CwXp6nMcoIdp07Gt7.jpg filter=lfs diff=lfs merge=lfs -text +storage/app/public/products/YBE7KBU74W2XxWwQFDLC8qqH3abpRM9WEFco8tt2.jpg filter=lfs diff=lfs merge=lfs -text +storage/app/public/products/Zl5tjkxHxKwEE69XgnJSUJgqNH0iQ32TWYv9fb8o.png filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d11a3d934d1fdf24e49e138ebe9b0bbab4543a17 --- /dev/null +++ b/.gitignore @@ -0,0 +1,57 @@ +/.phpunit.cache +/node_modules +/public/build +/public/hot +/public/storage +/storage/*.key +/storage/pail +/storage/logs +/vendor +.env +.env.backup +.env.production +.env.local +.env.*.local +.phpactor.json +.phpunit.result.cache +Homestead.json +Homestead.yaml +npm-debug.log +yarn-error.log +/auth.json +/.fleet +/.idea +/.nova +/.vscode +/.zed + +# Database files +/database/database.sqlite +*.sqlite +*.db + +# Log files +*.log + +# Cache and session files +/bootstrap/cache/* +/storage/framework/cache/* +/storage/framework/sessions/* +/storage/framework/views/* + +# Sensitive configuration +/config/database.php.backup +/config/app.php.backup + +# IDE and editor files +.DS_Store +Thumbs.db + +# Temporary files +*.tmp +*.temp +*.swp +*.swo + +# Kiro IDE files (optional - you might want to keep these) +# .kiro/ diff --git a/.kiro/specs/category-search-fix/design.md b/.kiro/specs/category-search-fix/design.md new file mode 100644 index 0000000000000000000000000000000000000000..ffecbb7cc6a3b334b2af771002d30cacad348a18 --- /dev/null +++ b/.kiro/specs/category-search-fix/design.md @@ -0,0 +1,172 @@ +# Design Document + +## Overview + +This design addresses the category selection functionality on the my-orders page where users can click "All Categories" to open a dropdown/modal and select a specific category. The current implementation opens a modal but doesn't update the "All Categories" button to reflect the selected category. This design will transform the category button to show the selected category's icon and name while maintaining the dropdown functionality. + +## Architecture + +The solution involves modifying the existing category selection system to: + +1. **Button State Management**: Track the currently selected category and update the button appearance +2. **Visual Transformation**: Replace the "All Categories" button content with the selected category's icon and name +3. **Interaction Preservation**: Maintain the existing modal/dropdown functionality for category selection +4. **Filter Integration**: Ensure the visual changes work seamlessly with the existing filter system + +## Components and Interfaces + +### 1. Category Button Component + +**Current Structure:** +```html +
+
+ +
+

All Categories

+
{{ $totalCategories }}
+
Browse
+
+``` + +**Enhanced Structure:** +- Add dynamic content areas that can be updated via JavaScript +- Maintain the same onclick functionality +- Add data attributes to track selected category state + +### 2. Category Selection System + +**Current Flow:** +1. User clicks "All Categories" → `showAllCategories()` → Opens modal +2. User selects category → `selectCategory(category)` → Closes modal + `setFilter(category)` + +**Enhanced Flow:** +1. User clicks category button → `showAllCategories()` → Opens modal +2. User selects category → `selectCategory(category)` → Closes modal + `setFilter(category)` + `updateCategoryButton(category)` + +### 3. Category Configuration + +**Game Category Mapping:** +```javascript +const categoryConfigs = { + 'all': { + icon: 'fas fa-th-large', + color: 'from-purple-600 to-blue-600', + name: 'All Categories', + badge: 'Browse' + }, + 'Genshin': { + icon: 'fas fa-star', + color: 'from-yellow-400 to-orange-500', + name: 'Genshin Impact', + badge: 'Orders' + }, + 'Starrail': { + icon: 'fas fa-rocket', + color: 'from-purple-500 to-pink-500', + name: 'Honkai: Star Rail', + badge: 'Orders' + }, + 'WutheringWave': { + icon: 'fas fa-water', + color: 'from-cyan-400 to-blue-500', + name: 'Wuthering Waves', + badge: 'Orders' + } +}; +``` + +## Data Models + +### Category State +```javascript +{ + selectedCategory: string, // Current selected category key + categoryConfig: object, // Configuration for the selected category + orderCount: number // Number of orders for this category +} +``` + +### Button Update Data +```javascript +{ + icon: string, // Font Awesome icon class + color: string, // Tailwind gradient classes + name: string, // Display name + count: number, // Order count + badge: string // Badge text +} +``` + +## Error Handling + +### Invalid Category Selection +- **Issue**: User selects a category that doesn't exist or has no orders +- **Solution**: Fall back to "All Categories" state and show appropriate message + +### Missing Category Configuration +- **Issue**: A game category exists in orders but not in configuration +- **Solution**: Use default configuration with generic game icon and original game name + +### Button Update Failures +- **Issue**: JavaScript fails to update button elements +- **Solution**: Graceful degradation - filter still works, button shows original state + +## Testing Strategy + +### Unit Tests +1. **Category Configuration Tests** + - Verify all game categories have proper configuration + - Test fallback configuration for unknown games + - Validate icon and color mappings + +2. **Button Update Tests** + - Test button transformation for each category + - Verify icon, name, and color updates + - Test reset to "All Categories" state + +3. **Integration Tests** + - Test category selection flow end-to-end + - Verify filter functionality works with button updates + - Test modal interaction with button state + +### Visual Tests +1. **Button Appearance** + - Verify correct icon displays for each category + - Check color gradients apply correctly + - Ensure text and count update properly + +2. **Interaction Tests** + - Confirm button remains clickable after transformation + - Test modal opens correctly from transformed button + - Verify hover states work on transformed button + +### Edge Cases +1. **No Orders Scenario** + - Test behavior when no orders exist for a category + - Verify button handles zero count gracefully + +2. **Multiple Rapid Selections** + - Test rapid category switching + - Ensure button updates don't conflict + +3. **Browser Compatibility** + - Test in different browsers + - Verify CSS transitions work consistently + +## Implementation Notes + +### CSS Considerations +- Maintain existing hover and active states +- Ensure smooth transitions between category states +- Preserve responsive design for different screen sizes + +### JavaScript Architecture +- Extend existing `selectCategory()` function +- Add new `updateCategoryButton()` function +- Maintain compatibility with existing filter system + +### Performance +- Minimize DOM manipulations during button updates +- Cache category configurations for quick access +- Ensure smooth animations don't impact performance \ No newline at end of file diff --git a/.kiro/specs/category-search-fix/requirements.md b/.kiro/specs/category-search-fix/requirements.md new file mode 100644 index 0000000000000000000000000000000000000000..103223c1acafa384f4f141538d9e3a9b6cfe8c1c --- /dev/null +++ b/.kiro/specs/category-search-fix/requirements.md @@ -0,0 +1,43 @@ +# Requirements Document + +## Introduction + +This feature addresses the category selection functionality on the my-orders page. Currently, when users click "All Categories" and select a specific category from the dropdown, the search interface does not properly update to reflect the selected category. This fix will ensure that category selection works correctly and updates the search display appropriately. + +## Requirements + +### Requirement 1 + +**User Story:** As a user browsing my orders, I want to select a specific category from the "All Categories" dropdown, so that the search interface updates to show my selected category. + +#### Acceptance Criteria + +1. WHEN a user clicks on "All Categories" dropdown THEN the system SHALL display a list of available categories +2. WHEN a user selects a specific category from the dropdown THEN the system SHALL update the search interface to display the selected category name +3. WHEN a category is selected THEN the system SHALL maintain all other search interface elements unchanged +4. WHEN a category is selected THEN the system SHALL filter the displayed orders to match the selected category + +### Requirement 2 + +**User Story:** As a user, I want the category button to transform into the selected category, so that I can clearly see which category is active. + +#### Acceptance Criteria + +1. WHEN a category is selected THEN the system SHALL replace the "All Categories" button with the selected category's button +2. WHEN a category is selected THEN the system SHALL display the selected category's specific icon and name on the button +3. WHEN no specific category is selected THEN the system SHALL display "All Categories" as the default button with a generic categories icon +4. WHEN the dropdown is open THEN the system SHALL highlight the currently selected category option +5. WHEN a category is selected THEN the system SHALL close the dropdown automatically +6. WHEN a game category is selected THEN the system SHALL transform the button to show that game's icon and category name as the search filter button +7. WHEN the transformed category button is clicked THEN the system SHALL open the dropdown menu to allow selecting a different category + +### Requirement 3 + +**User Story:** As a user, I want the category search to work seamlessly with other search functionality, so that I can combine category filtering with other search criteria. + +#### Acceptance Criteria + +1. WHEN a category is selected AND other search terms are present THEN the system SHALL apply both filters simultaneously +2. WHEN a category is selected THEN the system SHALL preserve any existing search query text +3. WHEN the search is cleared THEN the system SHALL reset the category selection to "All Categories" +4. WHEN a new category is selected THEN the system SHALL immediately update the search results without requiring additional user action \ No newline at end of file diff --git a/.kiro/specs/category-search-fix/tasks.md b/.kiro/specs/category-search-fix/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..baf3d11af70fb9189ae4aee0a1aa691392c1f31d --- /dev/null +++ b/.kiro/specs/category-search-fix/tasks.md @@ -0,0 +1,43 @@ +# Implementation Plan + +- [x] 1. Create category configuration system + + + - Add JavaScript object with category configurations (icon, color, name, badge) + - Include configurations for existing games (Genshin, Starrail, WutheringWave) + - Add fallback configuration for unknown categories + - _Requirements: 2.1, 2.3, 2.6_ + +- [x] 2. Implement button update functionality + + + - Create `updateCategoryButton()` function to transform the category button + - Update button icon, name, color, and count based on selected category + - Ensure smooth CSS transitions during button transformation + - _Requirements: 2.1, 2.2, 2.6_ + +- [x] 3. Enhance category selection flow + + + - Modify `selectCategory()` function to call button update after filter change + - Ensure button updates when "All Categories" is selected (reset to default) + - Maintain existing modal closing and filter setting functionality + - _Requirements: 1.1, 1.2, 2.7_ + +- [x] 4. Add category count calculation + + + - Create function to calculate order count for each category + - Update button count display when category is selected + - Handle zero count scenarios gracefully + - _Requirements: 1.4, 2.1_ + +- [x] 5. Test and validate functionality + + + + - Test category selection and button transformation for all game categories + - Verify "All Categories" reset functionality works correctly + - Ensure existing filter and modal functionality remains intact + - Test responsive behavior and CSS transitions + - _Requirements: 1.1, 1.2, 1.3, 1.4, 2.1, 2.2, 2.4, 2.5, 2.6, 2.7, 3.1, 3.2, 3.3, 3.4_ \ No newline at end of file diff --git a/.kiro/specs/console-interface-fix/design.md b/.kiro/specs/console-interface-fix/design.md new file mode 100644 index 0000000000000000000000000000000000000000..8661f91d4b7412e797312dd5c3febc6da49329a7 --- /dev/null +++ b/.kiro/specs/console-interface-fix/design.md @@ -0,0 +1,161 @@ +# Console Interface Fix Design + +## Overview + +The Luna Console interface needs to be redesigned to fix visual glitches, improve functionality, and ensure consistent styling with the overall site theme. The design will focus on creating a clean, professional administrative terminal that integrates seamlessly with the existing glassmorphic design system. + +## Architecture + +### Component Structure +``` +Console Interface +├── Overlay Container (backdrop) +├── Console Window +│ ├── Header Section +│ │ ├── Icon + Title +│ │ └── Close Button +│ ├── Output Area (scrollable) +│ └── Input Section +│ ├── Prompt Indicator +│ └── Input Field +└── Animation System +``` + +### State Management +- Console visibility state (open/closed) +- Input state (active/processing/disabled) +- Output buffer for command results +- Animation states for smooth transitions + +## Components and Interfaces + +### Console Container +- **Purpose**: Main wrapper with backdrop and positioning +- **Styling**: Full-screen overlay with blur backdrop +- **Behavior**: Centers console window, handles click-outside-to-close + +### Console Window +- **Dimensions**: Fixed size (900px × 600px) with responsive adjustments +- **Styling**: Glassmorphic design with consistent border radius and shadows +- **Layout**: Flexbox with header, content, and input sections + +### Header Section +- **Height**: 70px fixed +- **Content**: Icon, title, subtitle, and close button +- **Styling**: Gradient background matching site theme +- **Interactive**: Close button with hover effects + +### Output Area +- **Height**: Flexible (450px default) +- **Scrolling**: Auto-scroll to bottom on new content +- **Styling**: Dark background with proper text contrast +- **Content**: Monospace font for terminal-like appearance + +### Input Section +- **Height**: 80px fixed +- **Layout**: Prompt symbol + input field +- **Styling**: Consistent with site's input styling +- **Behavior**: Focus management and command processing + +## Data Models + +### Console State +```typescript +interface ConsoleState { + isVisible: boolean; + isProcessing: boolean; + outputLines: string[]; + currentInput: string; + animationState: 'entering' | 'visible' | 'exiting' | 'hidden'; +} +``` + +### Console Configuration +```typescript +interface ConsoleConfig { + dimensions: { + width: number; + height: number; + headerHeight: number; + inputHeight: number; + }; + styling: { + borderRadius: string; + backdropBlur: string; + backgroundColor: string; + }; + animations: { + enterDuration: number; + exitDuration: number; + typingSpeed: number; + }; +} +``` + +## Error Handling + +### Visual Error States +- **Invalid Commands**: Display error messages in red text +- **Connection Issues**: Show appropriate status indicators +- **Processing Errors**: Clear error messaging with recovery options + +### Fallback Behaviors +- **Animation Failures**: Graceful degradation to instant show/hide +- **Styling Issues**: Fallback to basic styling if glassmorphic effects fail +- **Input Problems**: Basic text input as fallback + +## Testing Strategy + +### Visual Testing +- Cross-browser compatibility testing +- Responsive design testing on various screen sizes +- Animation performance testing +- Color contrast and accessibility testing + +### Functional Testing +- Console open/close functionality +- Input field behavior and command processing +- Scroll behavior in output area +- Keyboard navigation and shortcuts + +### Integration Testing +- Integration with existing authentication system +- Compatibility with site's existing CSS framework +- Performance impact on page load and interactions + +## Implementation Notes + +### CSS Improvements +- Fix any conflicting styles causing visual artifacts +- Ensure proper z-index layering +- Optimize animations for smooth performance +- Implement proper responsive breakpoints + +### JavaScript Enhancements +- Improve event handling for better reliability +- Add proper error handling for edge cases +- Optimize animation timing and easing +- Implement proper cleanup on component unmount + +### Accessibility Considerations +- Proper ARIA labels for screen readers +- Keyboard navigation support +- High contrast mode compatibility +- Focus management for modal behavior + +## Design Decisions + +### Styling Approach +- **Glassmorphic Theme**: Maintain consistency with site's existing design +- **Monospace Typography**: Use system monospace fonts for terminal feel +- **Color Scheme**: Dark theme with white/gray text for readability + +### Animation Strategy +- **Smooth Transitions**: Use CSS transitions with appropriate easing +- **Performance**: Prefer CSS animations over JavaScript for better performance +- **Accessibility**: Respect user's motion preferences + +### Responsive Design +- **Desktop First**: Optimize for desktop admin usage +- **Mobile Adaptation**: Scale appropriately for smaller screens +- **Touch Friendly**: Ensure interactive elements are appropriately sized \ No newline at end of file diff --git a/.kiro/specs/console-interface-fix/requirements.md b/.kiro/specs/console-interface-fix/requirements.md new file mode 100644 index 0000000000000000000000000000000000000000..06596c49471264e8ac4b016e01d057b72a84706f --- /dev/null +++ b/.kiro/specs/console-interface-fix/requirements.md @@ -0,0 +1,60 @@ +# Console Interface Fix Requirements + +## Introduction + +The Luna Console interface currently has visual and functional issues that need to be addressed. The console appears to have layout problems, styling inconsistencies, and potentially broken functionality that affects the user experience when accessing the administrative terminal. + +## Requirements + +### Requirement 1 + +**User Story:** As an administrator, I want the Luna Console to display properly without visual glitches, so that I can access administrative functions reliably. + +#### Acceptance Criteria + +1. WHEN the console is opened THEN the interface SHALL display without visual artifacts or layout issues +2. WHEN the console header is rendered THEN it SHALL show the correct title "Luna Console" and subtitle "Administrative Terminal" with proper styling +3. WHEN the console window appears THEN it SHALL have consistent glassmorphic styling matching the site's design theme +4. WHEN the console is displayed THEN all text SHALL be clearly readable with proper contrast + +### Requirement 2 + +**User Story:** As an administrator, I want the console close button to work properly, so that I can dismiss the console when needed. + +#### Acceptance Criteria + +1. WHEN I click the close button (X) THEN the console SHALL close immediately +2. WHEN the close button is hovered THEN it SHALL show appropriate hover effects +3. WHEN the console is open THEN the close button SHALL be clearly visible and accessible + +### Requirement 3 + +**User Story:** As an administrator, I want the console input area to function correctly, so that I can enter commands without issues. + +#### Acceptance Criteria + +1. WHEN the console is open THEN the input field SHALL be properly positioned and styled +2. WHEN I type in the console input THEN the text SHALL appear clearly without visual artifacts +3. WHEN the console is in processing state THEN it SHALL show appropriate loading indicators +4. WHEN commands are entered THEN the console output SHALL display properly formatted text + +### Requirement 4 + +**User Story:** As an administrator, I want the console animations to work smoothly, so that the interface feels polished and professional. + +#### Acceptance Criteria + +1. WHEN the console opens THEN it SHALL animate smoothly into view +2. WHEN the console closes THEN it SHALL animate smoothly out of view +3. WHEN console text is being typed THEN animations SHALL be smooth and not cause visual glitches +4. WHEN the console state changes THEN transitions SHALL be fluid and professional + +### Requirement 5 + +**User Story:** As an administrator, I want the console to be responsive, so that it works properly on different screen sizes. + +#### Acceptance Criteria + +1. WHEN viewed on desktop THEN the console SHALL display at appropriate size with proper proportions +2. WHEN viewed on mobile devices THEN the console SHALL adapt to smaller screens appropriately +3. WHEN the browser window is resized THEN the console SHALL maintain proper layout and functionality \ No newline at end of file diff --git a/.kiro/specs/console-interface-fix/tasks.md b/.kiro/specs/console-interface-fix/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..2ba768ba45e8f0902aba6f654d015c9018095b69 --- /dev/null +++ b/.kiro/specs/console-interface-fix/tasks.md @@ -0,0 +1,62 @@ +# Console Interface Fix Implementation Plan + +- [x] 1. Analyze and fix console CSS styling issues + + + + - Identify conflicting CSS rules causing visual artifacts + - Fix glassmorphic styling inconsistencies + - Ensure proper z-index layering for modal behavior + + + + - _Requirements: 1.1, 1.3_ + +- [ ] 2. Fix console header layout and styling + - Correct header section positioning and dimensions + - Fix icon and title alignment issues + - Ensure proper gradient background rendering + - Style close button with appropriate hover effects + - _Requirements: 1.2, 2.2_ + +- [ ] 3. Repair console output area functionality + - Fix output area scrolling behavior + - Ensure proper text rendering without artifacts + - Implement auto-scroll to bottom for new content + - Fix monospace font rendering and line spacing + - _Requirements: 3.4, 1.4_ + +- [ ] 4. Fix console input section styling and behavior + - Repair input field positioning and styling + - Fix prompt indicator alignment + - Ensure proper focus states and cursor visibility + - Fix input text rendering and character spacing + - _Requirements: 3.1, 3.2_ + +- [ ] 5. Optimize console animations and transitions + - Fix jerky or broken opening/closing animations + - Smooth out typing animations and state transitions + - Ensure consistent animation timing and easing + - Fix any animation-related visual glitches + - _Requirements: 4.1, 4.2, 4.3, 4.4_ + +- [ ] 6. Implement responsive design fixes + - Fix console sizing issues on different screen sizes + - Ensure proper mobile responsiveness + - Fix layout issues when browser window is resized + - Implement appropriate breakpoints for different devices + - _Requirements: 5.1, 5.2, 5.3_ + +- [ ] 7. Fix console close button functionality + - Ensure close button click events work properly + - Fix any event propagation issues + - Implement proper cleanup when console closes + - Test close functionality across different browsers + - _Requirements: 2.1, 2.3_ + +- [ ] 8. Test and validate console interface fixes + - Test console functionality across different browsers + - Validate visual consistency with site theme + - Test responsive behavior on various screen sizes + - Verify all animations work smoothly + - _Requirements: 1.1, 2.1, 3.1, 4.1, 5.1_ \ No newline at end of file diff --git a/.kiro/specs/dashboard-analytics-enhancement/design.md b/.kiro/specs/dashboard-analytics-enhancement/design.md new file mode 100644 index 0000000000000000000000000000000000000000..c2c8e818cb7246fc32a1b4862bc0d0173a3dc7ab --- /dev/null +++ b/.kiro/specs/dashboard-analytics-enhancement/design.md @@ -0,0 +1,148 @@ +# Dashboard Analytics Enhancement - Design Document + +## Overview + +The enhanced dashboard will feature a modern glassmorphic design with real-time analytics, interactive charts, and comprehensive business metrics. The system will use Chart.js for visualizations, WebSockets for real-time updates, and a responsive grid layout optimized for all devices. + +## Architecture + +### Frontend Components +- **Dashboard Controller**: Main dashboard view with real-time data binding +- **Chart Components**: Reusable chart widgets (Line, Bar, Doughnut, Area) +- **Metric Cards**: Glassmorphic cards displaying KPIs with animations +- **Time Filter**: Interactive period selector (Daily/Weekly/Monthly) +- **Real-time Updates**: WebSocket integration for live data + +### Backend Services +- **Analytics Service**: Calculates revenue, profit, and sales metrics +- **Chart Data API**: Provides formatted data for different chart types +- **Real-time Service**: WebSocket server for live updates +- **Caching Layer**: Redis for performance optimization + +### Database Schema +- **analytics_snapshots**: Hourly/daily aggregated data +- **revenue_tracking**: Real-time revenue calculations +- **profit_margins**: Product-specific profit calculations + +## Components and Interfaces + +### 1. Dashboard Layout +``` +┌─────────────────────────────────────────────────────────┐ +│ Header: Time Filter (Daily/Weekly/Monthly) │ +├─────────────────────────────────────────────────────────┤ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ +│ │Revenue │ │ Profit │ │ Orders │ │ Customers│ │ +│ │ Card │ │ Card │ │ Card │ │ Card │ │ +│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ +├─────────────────────────────────────────────────────────┤ +│ ┌─────────────────────┐ ┌─────────────────────────────┐ │ +│ │ Revenue Chart │ │ Profit Trend Chart │ │ +│ │ (Line/Area) │ │ (Line/Bar) │ │ +│ └─────────────────────┘ └─────────────────────────────┘ │ +├─────────────────────────────────────────────────────────┤ +│ ┌─────────────────────┐ ┌─────────────────────────────┐ │ +│ │ Top Products Chart │ │ Customer Analytics │ │ +│ │ (Doughnut/Bar) │ │ (Mixed Chart) │ │ +│ └─────────────────────┘ └─────────────────────────────┘ │ +└─────────────────────────────────────────────────────────┘ +``` + +### 2. Metric Cards Design +- **Glassmorphic Background**: `bg-white/5 backdrop-blur-2xl` +- **Animated Numbers**: CountUp.js for smooth number transitions +- **Trend Indicators**: Up/down arrows with color coding +- **Glow Effects**: Subtle shadows and text glows + +### 3. Chart Configuration +- **Chart.js Integration**: Modern, interactive charts +- **Glassmorphic Styling**: Transparent backgrounds, subtle borders +- **Color Scheme**: + - Revenue: Emerald gradient (`from-emerald-400 to-emerald-600`) + - Profit: Purple gradient (`from-purple-400 to-purple-600`) + - Orders: Blue gradient (`from-blue-400 to-blue-600`) + - Customers: Orange gradient (`from-orange-400 to-orange-600`) + +### 4. Real-time Updates +- **WebSocket Connection**: Live data streaming +- **Smooth Animations**: Chart data updates with transitions +- **Performance Optimization**: Throttled updates (max 1/second) + +## Data Models + +### Analytics Snapshot +```php +class AnalyticsSnapshot extends Model +{ + protected $fillable = [ + 'date', + 'hour', + 'revenue', + 'profit', + 'orders_count', + 'customers_count', + 'avg_order_value', + 'conversion_rate' + ]; + + protected $casts = [ + 'date' => 'date', + 'revenue' => 'decimal:2', + 'profit' => 'decimal:2', + 'avg_order_value' => 'decimal:2', + 'conversion_rate' => 'decimal:4' + ]; +} +``` + +### Revenue Tracking +```php +class RevenueTracking extends Model +{ + protected $fillable = [ + 'order_id', + 'amount', + 'profit_margin', + 'created_at' + ]; + + protected $casts = [ + 'amount' => 'decimal:2', + 'profit_margin' => 'decimal:4' + ]; +} +``` + +## Error Handling + +### Chart Loading States +- **Loading Skeletons**: Animated placeholders while data loads +- **Error States**: Graceful fallbacks for failed API calls +- **Retry Mechanisms**: Automatic retry for failed WebSocket connections + +### Data Validation +- **Input Sanitization**: All user inputs validated and sanitized +- **Date Range Validation**: Ensure valid date ranges for filtering +- **Performance Limits**: Prevent excessive data requests + +## Testing Strategy + +### Unit Tests +- **Analytics Service**: Test revenue/profit calculations +- **Chart Data API**: Verify data formatting and aggregation +- **Real-time Service**: Test WebSocket connections and updates + +### Integration Tests +- **Dashboard Loading**: Test complete dashboard initialization +- **Time Filter**: Verify period switching functionality +- **Real-time Updates**: Test live data streaming + +### Performance Tests +- **Chart Rendering**: Measure chart load times with large datasets +- **WebSocket Performance**: Test concurrent connection limits +- **Database Queries**: Optimize analytics query performance + +### Browser Tests +- **Cross-browser Compatibility**: Test on Chrome, Firefox, Safari, Edge +- **Responsive Design**: Verify layout on different screen sizes +- **Chart Interactions**: Test hover, click, and zoom functionality \ No newline at end of file diff --git a/.kiro/specs/dashboard-analytics-enhancement/requirements.md b/.kiro/specs/dashboard-analytics-enhancement/requirements.md new file mode 100644 index 0000000000000000000000000000000000000000..f86503db67957f64c0782d8dec29f210270e2d0c --- /dev/null +++ b/.kiro/specs/dashboard-analytics-enhancement/requirements.md @@ -0,0 +1,73 @@ +# Dashboard Analytics Enhancement - Requirements Document + +## Introduction + +The current dashboard needs a complete overhaul with real-time analytics, revenue tracking, profit analysis, and interactive charts. This feature will provide comprehensive business insights with time-based filtering (daily, weekly, monthly) and modern glassmorphic design to match the existing UI aesthetic. + +## Requirements + +### Requirement 1 + +**User Story:** As an admin, I want to see real-time revenue analytics, so that I can monitor business performance instantly. + +#### Acceptance Criteria + +1. WHEN I access the dashboard THEN the system SHALL display current day revenue in real-time +2. WHEN revenue data changes THEN the dashboard SHALL update automatically without page refresh +3. WHEN viewing revenue metrics THEN the system SHALL show percentage change from previous period +4. WHEN displaying revenue THEN the system SHALL format currency properly with ฿ symbol + +### Requirement 2 + +**User Story:** As an admin, I want interactive time-based filtering, so that I can analyze performance across different periods. + +#### Acceptance Criteria + +1. WHEN I select daily view THEN the system SHALL show hourly revenue breakdown for today +2. WHEN I select weekly view THEN the system SHALL show daily revenue for the current week +3. WHEN I select monthly view THEN the system SHALL show daily revenue for the current month +4. WHEN switching time periods THEN charts SHALL animate smoothly to new data + +### Requirement 3 + +**User Story:** As an admin, I want profit analysis with visual charts, so that I can understand business profitability trends. + +#### Acceptance Criteria + +1. WHEN viewing profit metrics THEN the system SHALL calculate profit as revenue minus costs +2. WHEN displaying profit charts THEN the system SHALL use different colors for profit vs revenue +3. WHEN showing profit trends THEN the system SHALL indicate positive/negative changes clearly +4. WHEN viewing profit data THEN the system SHALL show profit margins as percentages + +### Requirement 4 + +**User Story:** As an admin, I want comprehensive sales analytics, so that I can track product performance and customer behavior. + +#### Acceptance Criteria + +1. WHEN viewing sales data THEN the system SHALL show top-selling products with quantities +2. WHEN displaying customer metrics THEN the system SHALL show new vs returning customers +3. WHEN viewing order analytics THEN the system SHALL show average order value trends +4. WHEN showing product performance THEN the system SHALL display conversion rates + +### Requirement 5 + +**User Story:** As an admin, I want a modern glassmorphic dashboard design, so that the interface matches the existing UI aesthetic. + +#### Acceptance Criteria + +1. WHEN viewing dashboard cards THEN they SHALL use glassmorphic design with backdrop blur +2. WHEN displaying charts THEN they SHALL have transparent backgrounds with subtle borders +3. WHEN showing metrics THEN they SHALL use consistent color scheme with glowing effects +4. WHEN interacting with elements THEN they SHALL have smooth hover animations + +### Requirement 6 + +**User Story:** As an admin, I want responsive dashboard layout, so that I can access analytics on different devices. + +#### Acceptance Criteria + +1. WHEN viewing on desktop THEN the dashboard SHALL display in multi-column grid layout +2. WHEN viewing on tablet THEN the dashboard SHALL adapt to smaller screen with proper spacing +3. WHEN viewing on mobile THEN the dashboard SHALL stack cards vertically for optimal viewing +4. WHEN resizing browser THEN charts SHALL resize smoothly to fit available space \ No newline at end of file diff --git a/.kiro/specs/dashboard-analytics-enhancement/tasks.md b/.kiro/specs/dashboard-analytics-enhancement/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..967e8463fac8aec6d725ae5a73014a0732c10f17 --- /dev/null +++ b/.kiro/specs/dashboard-analytics-enhancement/tasks.md @@ -0,0 +1,124 @@ +# Implementation Plan + +- [x] 1. Set up database structure and analytics models + + + - Create analytics_snapshots migration with date, hour, revenue, profit, orders_count, customers_count columns + - Create revenue_tracking migration with order_id, amount, profit_margin, timestamps + - Implement AnalyticsSnapshot model with proper casts and relationships + - Implement RevenueTracking model with decimal casting for financial data + - _Requirements: 1.1, 3.1, 4.1_ + + + +- [ ] 2. Create analytics service for data calculations + - Implement AnalyticsService class with revenue calculation methods + - Create profit calculation logic (revenue - costs) with margin percentages + - Build aggregation methods for daily, weekly, monthly data grouping + + + + - Add real-time data update methods for live dashboard updates + - _Requirements: 1.1, 1.2, 3.1, 3.2, 3.3_ + +- [ ] 3. Build chart data API endpoints + - Create DashboardController with analytics data endpoints + + + - Implement /api/dashboard/revenue endpoint with time period filtering + - Create /api/dashboard/profit endpoint with trend calculations + - Build /api/dashboard/sales endpoint with product performance data + - Add /api/dashboard/customers endpoint with new vs returning metrics + - _Requirements: 2.1, 2.2, 2.3, 4.1, 4.2, 4.3_ + + + +- [ ] 4. Install and configure Chart.js with glassmorphic styling + - Install Chart.js via npm and configure webpack compilation + - Create base chart configuration with glassmorphic theme (transparent backgrounds, subtle borders) + - Implement color scheme constants (emerald for revenue, purple for profit, blue for orders, orange for customers) + + + + - Configure chart animations and smooth transitions for data updates + - _Requirements: 2.4, 5.1, 5.2, 5.3_ + +- [x] 5. Create glassmorphic metric cards component + + + + - Build MetricCard blade component with glassmorphic styling (bg-white/5 backdrop-blur-2xl) + - Implement animated number display using CountUp.js for smooth transitions + - Add trend indicators with up/down arrows and color-coded percentage changes + - Create glow effects with text shadows and subtle box shadows + - _Requirements: 1.3, 3.3, 5.1, 5.3, 5.4_ + +- [ ] 6. Implement time period filter component + - Create TimeFilter blade component with Daily/Weekly/Monthly buttons + - Add active state styling with glassmorphic selected appearance + - Implement JavaScript for period switching with smooth chart transitions + - Connect filter to chart data updates via AJAX calls + - _Requirements: 2.1, 2.2, 2.3, 2.4_ + +- [ ] 7. Build main dashboard layout with responsive grid + - Create enhanced dashboard.blade.php with responsive CSS Grid layout + - Implement 4-column metric cards row for desktop, 2-column for tablet, 1-column for mobile + - Add 2-column chart grid for desktop, single column for mobile + - Ensure proper spacing and glassmorphic card styling throughout + - _Requirements: 5.1, 5.2, 6.1, 6.2, 6.3_ + +- [ ] 8. Create revenue chart component + - Build RevenueChart JavaScript class using Chart.js Line/Area chart + - Implement data fetching from /api/dashboard/revenue endpoint + - Add smooth animations and hover effects with glassmorphic tooltips + - Configure responsive chart sizing that adapts to container width + - _Requirements: 1.1, 1.2, 2.1, 2.4, 6.4_ + +- [ ] 9. Implement profit trend chart + - Create ProfitChart JavaScript class with Line/Bar chart combination + - Add profit vs revenue comparison with different colored datasets + - Implement profit margin percentage display in chart tooltips + - Configure positive/negative profit indication with green/red colors + - _Requirements: 3.1, 3.2, 3.3, 2.4_ + +- [ ] 10. Build top products performance chart + - Create ProductsChart JavaScript class using Doughnut/Bar chart + - Implement data fetching from sales endpoint with product quantities + - Add interactive hover effects showing product details + - Configure chart to show top 10 products with "Others" category for remaining + - _Requirements: 4.1, 4.4, 5.3, 5.4_ + +- [ ] 11. Create customer analytics chart + - Build CustomerChart JavaScript class with mixed chart type (Line + Bar) + - Implement new vs returning customers visualization + - Add average order value trend line overlay + - Configure conversion rate display with percentage formatting + - _Requirements: 4.2, 4.3, 4.4_ + +- [ ] 12. Implement real-time WebSocket updates + - Install and configure Laravel WebSockets or Pusher for real-time updates + - Create WebSocket events for revenue, profit, and order updates + - Implement JavaScript WebSocket client with automatic reconnection + - Add throttled update mechanism (max 1 update per second) for performance + - _Requirements: 1.2, 1.3, 5.4_ + +- [ ] 13. Add loading states and error handling + - Create animated skeleton loaders for charts while data is loading + - Implement error states with retry buttons for failed API calls + - Add graceful fallbacks when WebSocket connection fails + - Create user-friendly error messages with glassmorphic styling + - _Requirements: 5.4, 6.4_ + +- [ ] 14. Optimize performance and add caching + - Implement Redis caching for analytics data with 5-minute TTL + - Add database query optimization with proper indexes + - Configure chart data pagination for large datasets + - Implement lazy loading for charts that are not immediately visible + - _Requirements: 1.2, 6.4_ + +- [ ] 15. Create comprehensive test suite + - Write unit tests for AnalyticsService revenue and profit calculations + - Create integration tests for dashboard API endpoints + - Add browser tests for chart interactions and responsive design + - Implement performance tests for WebSocket connections and chart rendering + - _Requirements: 1.1, 2.4, 3.1, 4.1, 6.1, 6.2, 6.3_ \ No newline at end of file diff --git a/.kiro/specs/dashboard-time-filter-fix/design.md b/.kiro/specs/dashboard-time-filter-fix/design.md new file mode 100644 index 0000000000000000000000000000000000000000..a2819acd64300bfdda9c7403a17748687006288a --- /dev/null +++ b/.kiro/specs/dashboard-time-filter-fix/design.md @@ -0,0 +1,220 @@ +# Dashboard Time Filter Fix - Design Document + +## Overview + +This design addresses the dashboard time filtering functionality and chart rendering issues. The solution involves updating the time filter component, enhancing the JavaScript dashboard manager, and ensuring proper API data flow for different time periods. + +## Architecture + +### Component Structure +``` +Dashboard View +├── Time Filter Component (x-time-filter) +├── Metric Cards (with period-aware comparisons) +├── Chart Containers (with proper data binding) +└── Dashboard Manager (Alpine.js component) +``` + +### Data Flow +``` +User Clicks Filter → Dashboard Manager → API Request → Data Processing → Chart Update → Metric Card Update +``` + +## Components and Interfaces + +### 1. Time Filter Component Enhancement + +**File:** `resources/views/components/time-filter.blade.php` + +**Current Issues:** +- Filter buttons don't properly communicate with dashboard +- No visual feedback for active state +- Missing loading states + +**Design Solution:** +- Add Alpine.js integration with dashboard manager +- Implement proper active state styling +- Add loading indicators during data fetch +- Use event dispatching for filter changes + +**Interface:** +```php +
+ + + +
+``` + +### 2. Dashboard Manager JavaScript Enhancement + +**File:** `resources/views/dashboard.blade.php` (JavaScript section) + +**Current Issues:** +- Charts not initializing properly +- No period-aware data fetching +- Missing chart update functionality + +**Design Solution:** +- Fix chart initialization sequence +- Implement period-aware API calls +- Add chart update methods for filter changes +- Proper error handling and loading states + +**Key Methods:** +```javascript +dashboardManager() { + return { + currentPeriod: 'daily', + loading: false, + charts: {}, + + changeFilter(period) { + this.currentPeriod = period; + this.refreshDashboard(); + }, + + async refreshDashboard() { + this.loading = true; + await this.updateCharts(); + await this.updateMetrics(); + this.loading = false; + } + } +} +``` + +### 3. API Controller Enhancement + +**File:** `app/Http/Controllers/Api/DashboardController.php` + +**Current Issues:** +- Period parameter not properly handled +- Chart data format inconsistent +- Missing proper date range calculations + +**Design Solution:** +- Enhance period handling logic +- Standardize chart data format +- Implement proper date range calculations for each period +- Add data aggregation for weekly/monthly views + +**Data Format:** +```php +// Daily: Hourly data points (0-23) +// Weekly: Daily data points (7 days) +// Monthly: Daily data points (30/31 days) +[ + 'chart_data' => [ + ['label' => '00:00', 'revenue' => 1000, 'profit' => 300, 'orders' => 5], + // ... more data points + ], + 'summary' => [ + 'current' => 5000, + 'previous' => 4500, + 'percentage_change' => 11.11, + 'trend' => 'up' + ] +] +``` + +### 4. Analytics Service Enhancement + +**File:** `app/Services/AnalyticsService.php` + +**Current Issues:** +- getAggregatedData method needs period-specific logic +- Comparison calculations need previous period data +- Chart data formatting inconsistent + +**Design Solution:** +- Implement period-specific data aggregation +- Add previous period comparison methods +- Standardize data formatting across all methods +- Optimize database queries for different time ranges + +## Data Models + +### Chart Data Structure +```php +interface ChartDataPoint { + label: string; // "00:00", "Monday", "Jan 15" + revenue: number; + profit: number; + orders: number; + customers: number; +} +``` + +### Metric Summary Structure +```php +interface MetricSummary { + current: number; + previous: number; + percentage_change: number; + trend: 'up' | 'down' | 'neutral'; + period: 'daily' | 'weekly' | 'monthly'; +} +``` + +## Error Handling + +### Frontend Error Handling +- Chart initialization failures → Show error message in chart container +- API request failures → Display "Unable to load data" message +- Network timeouts → Retry mechanism with exponential backoff + +### Backend Error Handling +- Database query failures → Return empty data with success: false +- Invalid period parameters → Default to 'daily' with warning +- Missing data → Return zeros with appropriate messaging + +## Testing Strategy + +### Unit Tests +- AnalyticsService period calculations +- API controller response formats +- Chart data transformation methods + +### Integration Tests +- Full dashboard load with different periods +- Filter switching functionality +- API endpoint responses for all periods + +### Manual Testing Scenarios +1. Load dashboard → Verify daily view works +2. Click Weekly → Verify charts update with weekly data +3. Click Monthly → Verify charts update with monthly data +4. Refresh page → Verify default daily view loads +5. Network offline → Verify error handling + +## Implementation Priority + +### Phase 1: Core Functionality +1. Fix chart initialization +2. Implement basic period switching +3. Update API endpoints for proper period handling + +### Phase 2: Enhanced Features +1. Add loading states and animations +2. Implement proper error handling +3. Add URL state management + +### Phase 3: Polish +1. Smooth transitions between periods +2. Advanced error recovery +3. Performance optimizations + +## Performance Considerations + +- Cache API responses for 30 seconds to reduce server load +- Lazy load charts only when visible +- Debounce filter changes to prevent rapid API calls +- Use efficient database queries with proper indexing + +## Security Considerations + +- Validate period parameters on backend +- Sanitize date inputs to prevent SQL injection +- Rate limit API endpoints to prevent abuse +- Ensure proper authentication for dashboard access \ No newline at end of file diff --git a/.kiro/specs/dashboard-time-filter-fix/requirements.md b/.kiro/specs/dashboard-time-filter-fix/requirements.md new file mode 100644 index 0000000000000000000000000000000000000000..6fe6c13f6e840547605f6e094805fae8f64ba446 --- /dev/null +++ b/.kiro/specs/dashboard-time-filter-fix/requirements.md @@ -0,0 +1,55 @@ +# Dashboard Time Filter Fix - Requirements Document + +## Introduction + +The analytics dashboard currently has non-functional time period filtering and chart display issues. Users need to be able to switch between daily, weekly, and monthly views with properly working charts that display real data for each time period. + +## Requirements + +### Requirement 1 + +**User Story:** As a dashboard user, I want to switch between daily, weekly, and monthly time periods, so that I can analyze data across different timeframes. + +#### Acceptance Criteria + +1. WHEN user clicks "Daily" filter THEN dashboard SHALL display hourly data for the current day +2. WHEN user clicks "Weekly" filter THEN dashboard SHALL display daily data for the current week +3. WHEN user clicks "Monthly" filter THEN dashboard SHALL display daily data for the current month +4. WHEN time period changes THEN all charts SHALL update to reflect the selected timeframe +5. WHEN time period changes THEN metric cards SHALL show comparison data from previous period (previous day/week/month) + +### Requirement 2 + +**User Story:** As a dashboard user, I want to see working charts with real data, so that I can visualize my store's performance. + +#### Acceptance Criteria + +1. WHEN dashboard loads THEN revenue chart SHALL display with real data points +2. WHEN dashboard loads THEN profit chart SHALL show revenue vs profit comparison +3. WHEN dashboard loads THEN products chart SHALL display top products by revenue +4. WHEN dashboard loads THEN customers chart SHALL show customer activity trends +5. WHEN time period changes THEN all charts SHALL re-render with appropriate data + +### Requirement 3 + +**User Story:** As a dashboard user, I want metric cards to show meaningful comparisons, so that I can understand performance trends. + +#### Acceptance Criteria + +1. WHEN viewing daily data THEN metric cards SHALL compare with previous day +2. WHEN viewing weekly data THEN metric cards SHALL compare with previous week +3. WHEN viewing monthly data THEN metric cards SHALL compare with previous month +4. WHEN comparison data is available THEN cards SHALL show percentage change and trend direction +5. WHEN no comparison data exists THEN cards SHALL show "No previous data" message + +### Requirement 4 + +**User Story:** As a dashboard user, I want the time filter UI to be intuitive and responsive, so that I can easily switch between time periods. + +#### Acceptance Criteria + +1. WHEN dashboard loads THEN "Daily" filter SHALL be selected by default +2. WHEN user clicks a time filter THEN it SHALL become visually active +3. WHEN time filter is clicked THEN loading state SHALL be shown during data fetch +4. WHEN data loading completes THEN loading state SHALL be removed +5. WHEN filter changes THEN URL SHALL update to reflect current selection (optional) \ No newline at end of file diff --git a/.kiro/specs/dashboard-time-filter-fix/tasks.md b/.kiro/specs/dashboard-time-filter-fix/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..f444891e95123f4b8ef17b1f682594c322d1de53 --- /dev/null +++ b/.kiro/specs/dashboard-time-filter-fix/tasks.md @@ -0,0 +1,43 @@ +# Implementation Plan + +- [ ] 1. Fix time filter component integration + - Update time-filter component to properly communicate with dashboard manager + - Add Alpine.js event dispatching for filter changes + - Implement active state styling and loading indicators + - _Requirements: 4.1, 4.2, 4.3_ + +- [ ] 2. Enhance dashboard JavaScript manager + - Fix chart initialization sequence and error handling + - Implement changeFilter method to handle period switching + - Add refreshDashboard method to update all components + - Create proper loading states during data fetching + - _Requirements: 1.4, 2.5, 4.4_ + +- [ ] 3. Update API controller period handling + - Enhance period parameter processing in all dashboard API methods + - Implement proper date range calculations for daily/weekly/monthly + - Standardize chart data format across all endpoints + - Add previous period comparison data to all responses + - _Requirements: 1.1, 1.2, 1.3, 3.1, 3.2, 3.3_ + +- [ ] 4. Fix analytics service data aggregation + - Update getAggregatedData method with period-specific logic + - Implement previous period calculation methods + - Add proper data formatting for different time ranges + - Optimize database queries for weekly and monthly aggregation + - _Requirements: 1.1, 1.2, 1.3, 3.1, 3.2, 3.3_ + +- [ ] 5. Update metric cards for period-aware comparisons + - Modify dashboard view to pass period-specific comparison data + - Update metric card component to handle different comparison types + - Add proper messaging for missing comparison data + - Implement percentage change calculations for all periods + - _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5_ + +- [ ] 6. Test and validate all functionality + - Test daily, weekly, and monthly filter switching + - Verify all charts render correctly with real data + - Validate metric card comparisons for all periods + - Test error handling and loading states + - Ensure proper default state (daily) on page load + - _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 2.1, 2.2, 2.3, 2.4, 2.5, 3.1, 3.2, 3.3, 3.4, 3.5, 4.1, 4.2, 4.3, 4.4, 4.5_ \ No newline at end of file diff --git a/.kiro/specs/search-glassmorphism-fix/design.md b/.kiro/specs/search-glassmorphism-fix/design.md new file mode 100644 index 0000000000000000000000000000000000000000..d3b66ac92809be72606449cb3fd98c2bb3f8c450 --- /dev/null +++ b/.kiro/specs/search-glassmorphism-fix/design.md @@ -0,0 +1,161 @@ +# Search Results Glassmorphism Fix - Design Document + +## Overview + +This design addresses the glassmorphism implementation issues in the search results dropdown by improving background opacity, text contrast, positioning, and visual hierarchy. The solution maintains the modern aesthetic while ensuring excellent readability and user experience. + +## Architecture + +The search results dropdown is implemented using Alpine.js with Tailwind CSS classes. The main components that need improvement are: + +1. **Main Container**: The dropdown wrapper with glassmorphic background +2. **Content Sections**: Game categories and products sections +3. **Individual Items**: Game and product result items +4. **Hover Preview**: Product detail popup on hover + +## Components and Interfaces + +### 1. Search Results Container +**Current Issues:** +- `bg-black/30` is too transparent (30% opacity) +- `border-white/10` is barely visible (10% opacity) +- Positioning offset `translateX(-1.5rem)` causes misalignment + +**Design Solution:** +- Increase background opacity to `bg-black/70` for better frosted glass effect +- Enhance border visibility to `border-white/20` +- Adjust positioning to `translateX(-0.75rem)` for better alignment +- Add CSS backdrop-filter for enhanced blur effect + +### 2. Text Color Hierarchy +**Current Issues:** +- Loading text `text-white/60` has poor contrast +- Section headers `text-purple-300` and `text-green-300` are too dim +- Product descriptions `text-white/60` are hard to read +- Secondary text `text-white/50` lacks visibility + +**Design Solution:** +```css +Loading State: text-white/80 (improved from 60%) +Section Headers: text-purple-200, text-green-200 (brighter variants) +Primary Text: text-white (full opacity for names) +Secondary Text: text-white/70 (improved from 50%) +Description Text: text-white/80 (improved from 60%) +``` + +### 3. Visual Hierarchy Improvements +**Current Issues:** +- Small icons and images reduce visual impact +- Weak hover states provide poor feedback +- Insufficient padding creates cramped appearance + +**Design Solution:** +- Increase section header icons from `w-3 h-3` to `w-4 h-4` +- Enhance product images from `w-8 h-8` to `w-10 h-10` +- Improve hover backgrounds from `hover:bg-white/5` to `hover:bg-white/10` +- Increase padding on product items from `p-2` to `p-3` + +### 4. Border and Divider Enhancement +**Current Issues:** +- `divide-white/10` dividers are barely visible +- Item borders `border-white/5` provide no visual separation + +**Design Solution:** +- Strengthen dividers to `divide-white/20` +- Improve item borders to `border-white/10` with hover state `hover:border-white/20` + +## Data Models + +No data model changes required. The component works with existing search API response structure: + +```javascript +{ + games: [ + { + name: string, + slug: string, + description: string, + icon: string, + gradient: string, + category: string, + popularity: string, + count: number + } + ], + products: [ + { + id: number, + name: string, + slug: string, + game_slug: string, + price: number, + image: string, + game: string, + amount: number, + stock_color: string + } + ] +} +``` + +## Error Handling + +The existing error handling remains unchanged: +- Network errors fall back to empty results +- Image loading errors use placeholder images +- Missing data properties are handled gracefully + +## Testing Strategy + +### Visual Testing +1. **Contrast Testing**: Verify all text meets WCAG AA contrast requirements +2. **Responsive Testing**: Ensure dropdown works across different screen sizes +3. **Browser Testing**: Test glassmorphism effects across modern browsers + +### Functional Testing +1. **Positioning**: Verify dropdown aligns properly with search input +2. **Hover States**: Test all interactive elements provide clear feedback +3. **Keyboard Navigation**: Ensure accessibility is maintained + +### Performance Testing +1. **Backdrop Blur**: Verify enhanced blur effects don't impact performance +2. **Animation Smoothness**: Test transition animations remain smooth + +## Implementation Approach + +### Phase 1: Core Glassmorphism +- Update main container background and border opacity +- Adjust positioning for proper alignment +- Add enhanced backdrop-filter CSS + +### Phase 2: Text Contrast +- Improve all text color values for better readability +- Enhance section headers and primary text +- Strengthen secondary text visibility + +### Phase 3: Visual Polish +- Increase icon and image sizes +- Improve hover states and transitions +- Enhance borders and dividers + +### Phase 4: Testing & Refinement +- Cross-browser testing +- Accessibility validation +- Performance optimization if needed + +## CSS Implementation Details + +### Enhanced Backdrop Filter +```css +backdrop-filter: blur(20px) saturate(180%); +-webkit-backdrop-filter: blur(20px) saturate(180%); +``` + +### Improved Container Styling +```css +background: rgba(0, 0, 0, 0.7); +border: 1px solid rgba(255, 255, 255, 0.2); +box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); +``` + +This design ensures the search results dropdown maintains its modern glassmorphic aesthetic while providing excellent readability and user experience. \ No newline at end of file diff --git a/.kiro/specs/search-glassmorphism-fix/requirements.md b/.kiro/specs/search-glassmorphism-fix/requirements.md new file mode 100644 index 0000000000000000000000000000000000000000..ece86f6fe3c1004c9758cd4a1225cb5f89f8cf3e --- /dev/null +++ b/.kiro/specs/search-glassmorphism-fix/requirements.md @@ -0,0 +1,50 @@ +# Search Results Glassmorphism Fix - Requirements Document + +## Introduction + +The current search results dropdown has poor glassmorphism implementation with excessive transparency that makes text hard to read and lacks proper frosted glass aesthetics. This feature will improve the visual design and readability of the search results while maintaining the modern glassmorphic design language. + +## Requirements + +### Requirement 1 + +**User Story:** As a user, I want the search results dropdown to have proper frosted glass appearance, so that I can easily read the content without strain. + +#### Acceptance Criteria + +1. WHEN the search dropdown appears THEN the background SHALL have appropriate opacity (not too transparent) +2. WHEN viewing the search results THEN the backdrop blur SHALL create a proper frosted glass effect +3. WHEN text is displayed in the dropdown THEN it SHALL have sufficient contrast for readability +4. WHEN the dropdown is visible THEN borders SHALL be clearly defined with appropriate opacity + +### Requirement 2 + +**User Story:** As a user, I want the search results to be properly positioned relative to the search bar, so that the interface looks aligned and professional. + +#### Acceptance Criteria + +1. WHEN the search dropdown opens THEN it SHALL align properly with the search input field +2. WHEN viewing the dropdown THEN the positioning SHALL not cause layout shifts or misalignment +3. WHEN the dropdown is displayed THEN it SHALL maintain consistent spacing from the search bar + +### Requirement 3 + +**User Story:** As a user, I want improved text colors and contrast in search results, so that I can easily distinguish between different elements and read all information clearly. + +#### Acceptance Criteria + +1. WHEN viewing game categories THEN section headers SHALL have high contrast colors +2. WHEN viewing product information THEN product names SHALL be clearly readable +3. WHEN viewing metadata THEN secondary text SHALL have appropriate contrast while maintaining hierarchy +4. WHEN hovering over items THEN color changes SHALL provide clear visual feedback + +### Requirement 4 + +**User Story:** As a user, I want consistent visual hierarchy in search results, so that I can quickly scan and find relevant information. + +#### Acceptance Criteria + +1. WHEN viewing search results THEN different content types SHALL be visually distinct +2. WHEN scanning results THEN important information SHALL stand out through proper typography +3. WHEN viewing product images THEN they SHALL be appropriately sized and styled +4. WHEN interacting with results THEN hover states SHALL provide clear feedback \ No newline at end of file diff --git a/.kiro/specs/search-glassmorphism-fix/tasks.md b/.kiro/specs/search-glassmorphism-fix/tasks.md new file mode 100644 index 0000000000000000000000000000000000000000..927ec1e8fe32b446749b1b09da9f4450e47ac9f8 --- /dev/null +++ b/.kiro/specs/search-glassmorphism-fix/tasks.md @@ -0,0 +1,36 @@ +# Implementation Plan + +- [ ] 1. Enhance main search results container glassmorphism + - Update background opacity from `bg-black/30` to `bg-black/70` for better frosted glass effect + - Improve border visibility from `border-white/10` to `border-white/20` + - Adjust positioning from `translateX(-1.5rem)` to `translateX(-0.75rem)` for proper alignment + - Add enhanced backdrop-filter CSS for better blur effect + - _Requirements: 1.1, 1.2, 1.3, 1.4, 2.1, 2.2_ + +- [ ] 2. Improve text colors and contrast throughout search results + - Enhance loading state text from `text-white/60` to `text-white/80` + - Brighten section headers from `text-purple-300` to `text-purple-200` and `text-green-300` to `text-green-200` + - Improve secondary text visibility from `text-white/50` to `text-white/70` + - Enhance description text from `text-white/60` to `text-white/80` + - _Requirements: 3.1, 3.2, 3.3, 3.4_ + +- [ ] 3. Enhance visual hierarchy and interactive elements + - Increase section header icons from `w-3 h-3` to `w-4 h-4` with proper margin adjustments + - Improve product images from `w-8 h-8` to `w-10 h-10` with better border styling + - Strengthen hover backgrounds from `hover:bg-white/5` to `hover:bg-white/10` + - Increase product item padding from `p-2` to `p-3` for better spacing + - _Requirements: 4.1, 4.2, 4.3, 4.4_ + +- [ ] 4. Strengthen borders and dividers for better visual separation + - Improve main dividers from `divide-white/10` to `divide-white/20` + - Enhance item borders from `border-white/5` to `border-white/10` + - Add better hover border states `hover:border-white/20` + - Update product image borders from `border-white/10` to `border-white/20` + - _Requirements: 4.1, 4.2_ + +- [ ] 5. Enhance hover preview popup styling + - Improve popup background from `bg-black/95` to `bg-black/90` with enhanced backdrop-filter + - Strengthen popup borders from `border-white/20` to `border-white/30` + - Improve internal text colors and contrast for better readability + - Add proper backdrop-filter CSS for consistent glassmorphism + - _Requirements: 1.1, 1.3, 3.1, 3.2_ \ No newline at end of file diff --git a/README.md b/README.md index 7be5fc7f47d5db027d120b8024982df93db95b74..6851ea97b198f81475fb0fb4ddf49b66820c94f6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,61 @@ ---- -license: mit ---- +

Laravel Logo

+ +

+Build Status +Total Downloads +Latest Stable Version +License +

+ +## About Laravel + +Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: + +- [Simple, fast routing engine](https://laravel.com/docs/routing). +- [Powerful dependency injection container](https://laravel.com/docs/container). +- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage. +- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent). +- Database agnostic [schema migrations](https://laravel.com/docs/migrations). +- [Robust background job processing](https://laravel.com/docs/queues). +- [Real-time event broadcasting](https://laravel.com/docs/broadcasting). + +Laravel is accessible, powerful, and provides tools required for large, robust applications. + +## Learning Laravel + +Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. + +You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch. + +If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. + +## Laravel Sponsors + +We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com). + +### Premium Partners + +- **[Vehikl](https://vehikl.com/)** +- **[Tighten Co.](https://tighten.co)** +- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)** +- **[64 Robots](https://64robots.com)** +- **[Curotec](https://www.curotec.com/services/technologies/laravel/)** +- **[DevSquad](https://devsquad.com/hire-laravel-developers)** +- **[Redberry](https://redberry.international/laravel-development/)** +- **[Active Logic](https://activelogic.com)** + +## Contributing + +Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). + +## Code of Conduct + +In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). + +## Security Vulnerabilities + +If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. + +## License + +The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). diff --git a/README_HF.md b/README_HF.md new file mode 100644 index 0000000000000000000000000000000000000000..bdfc341b7933a88a62ac367c8cc04e58a5abd159 --- /dev/null +++ b/README_HF.md @@ -0,0 +1,85 @@ +# Laravel E-commerce Dashboard + +A modern Laravel-based e-commerce platform with an advanced analytics dashboard featuring real-time metrics, interactive charts, and comprehensive product management. + +## Features + +### 🎯 Analytics Dashboard +- **Real-time Metrics**: Revenue, orders, customers, and product statistics +- **Interactive Charts**: Revenue trends, top products, and purchase patterns +- **Time Period Filtering**: Daily, weekly, and monthly views +- **Product Trends**: Shows what products people buy at different time periods +- **Glassmorphism UI**: Modern, beautiful interface with smooth animations + +### 🛍️ E-commerce Features +- Product catalog with categories +- Order management system +- Customer tracking +- Search functionality with glassmorphism effects +- Responsive design + +### 📊 Advanced Analytics +- Revenue tracking with period comparisons +- Product performance metrics +- Customer behavior analysis +- Purchase trend visualization +- Detailed modal views with comprehensive data + +## Technology Stack + +- **Backend**: Laravel 10+ with PHP 8.1+ +- **Frontend**: Blade templates with Alpine.js +- **Styling**: Tailwind CSS with custom glassmorphism effects +- **Charts**: Chart.js for interactive visualizations +- **Database**: MySQL/SQLite support +- **Build Tools**: Vite for asset compilation + +## Key Components + +### Dashboard Features +- Time-sensitive metric cards +- Interactive revenue and product charts +- Product purchase trends by time period +- Modal details with comprehensive analytics +- Auto-refreshing data every 30 seconds + +### UI/UX Highlights +- Glassmorphism design language +- Smooth transitions and animations +- Responsive grid layouts +- Custom time filter buttons with natural glow effects +- Enhanced contrast for better readability + +## Installation + +1. Clone the repository +2. Install dependencies: `composer install && npm install` +3. Set up environment: `cp .env.example .env` +4. Generate key: `php artisan key:generate` +5. Run migrations: `php artisan migrate` +6. Build assets: `npm run build` +7. Start server: `php artisan serve` + +## API Endpoints + +- `/api/dashboard/summary` - Overall dashboard metrics +- `/api/dashboard/revenue` - Revenue data with time filtering +- `/api/dashboard/sales` - Sales and product performance +- `/api/dashboard/customers` - Customer analytics +- `/api/dashboard/product-trends` - Product purchase trends by period + +## Recent Enhancements + +- Fixed color contrast issues in metric cards +- Enhanced time filter button styling with natural glow effects +- Added product purchase trends chart showing buying patterns +- Improved glassmorphism effects throughout the interface +- Optimized database queries for better performance + +## Screenshots + +The dashboard features a modern dark theme with glassmorphism effects, interactive charts, and comprehensive analytics views. + +## License + +This project is open source and available under the MIT License. \ No newline at end of file diff --git a/app/Console/Commands/MigrateExistingCustomGames.php b/app/Console/Commands/MigrateExistingCustomGames.php new file mode 100644 index 0000000000000000000000000000000000000000..1fd79199b43bebb02fabfef8612e9f5aa2f285fa --- /dev/null +++ b/app/Console/Commands/MigrateExistingCustomGames.php @@ -0,0 +1,108 @@ +info('Starting migration of existing custom games...'); + + // Get all unique custom game categories from existing products + $existingCustomGames = Product::select('game') + ->distinct() + ->whereNotNull('game') + ->whereNotIn('game', ['Genshin', 'Starrail', 'WutheringWave', 'ZenlessZoneZero', 'Arknights', 'AzurLane']) + ->pluck('game'); + + if ($existingCustomGames->isEmpty()) { + $this->info('No existing custom games found to migrate.'); + return; + } + + $this->info("Found {$existingCustomGames->count()} custom games to migrate:"); + + $customGameColors = [ + 'from-red-500 to-pink-500', + 'from-orange-500 to-red-500', + 'from-yellow-500 to-orange-500', + 'from-green-500 to-teal-500', + 'from-teal-500 to-cyan-500', + 'from-blue-500 to-indigo-500', + 'from-indigo-500 to-purple-500', + 'from-purple-500 to-pink-500', + 'from-pink-500 to-rose-500', + 'from-emerald-500 to-green-500', + 'from-cyan-500 to-blue-500', + 'from-violet-500 to-purple-500' + ]; + + $customGameIcons = [ + 'fas fa-gamepad', + 'fas fa-dice', + 'fas fa-chess', + 'fas fa-puzzle-piece', + 'fas fa-trophy', + 'fas fa-crown', + 'fas fa-gem', + 'fas fa-fire', + 'fas fa-bolt', + 'fas fa-magic', + 'fas fa-dragon', + 'fas fa-shield', + 'fas fa-sword', + 'fas fa-heart', + 'fas fa-star', + 'fas fa-moon', + 'fas fa-sun', + 'fas fa-leaf', + 'fas fa-snowflake', + 'fas fa-mountain' + ]; + + foreach ($existingCustomGames as $gameName) { + // Check if it already exists in custom_games table + $existingCustomGame = CustomGame::where('name', $gameName)->first(); + + if (!$existingCustomGame) { + // Generate consistent styling based on game name + $hash = crc32($gameName); + $colorIndex = abs($hash) % count($customGameColors); + $iconIndex = abs($hash >> 8) % count($customGameIcons); + + CustomGame::create([ + 'name' => $gameName, + 'icon' => $customGameIcons[$iconIndex], + 'color_gradient' => $customGameColors[$colorIndex] + ]); + + $this->info("✓ Migrated: {$gameName}"); + } else { + $this->info("- Already exists: {$gameName}"); + } + } + + $this->info('Migration completed successfully!'); + } +} diff --git a/app/Console/Commands/PopulateAnalyticsData.php b/app/Console/Commands/PopulateAnalyticsData.php new file mode 100644 index 0000000000000000000000000000000000000000..dd86e4e3c4544726e6ee00b6515f6d611b7d736f --- /dev/null +++ b/app/Console/Commands/PopulateAnalyticsData.php @@ -0,0 +1,59 @@ +info('Populating analytics data from existing orders...'); + + // Get all orders and create revenue tracking entries + $orders = Order::where('status', '!=', 'cancelled')->get(); + + foreach ($orders as $order) { + // Parse cart data if it exists + $cartData = json_decode($order->cart_data, true) ?? []; + + if (!empty($cartData)) { + foreach ($cartData as $item) { + RevenueTracking::updateOrCreate([ + 'order_id' => $order->id, + 'product_name' => $item['name'] ?? 'Unknown Product' + ], [ + 'amount' => $item['price'] ?? 0, + 'cost' => ($item['price'] ?? 0) * 0.7, // 70% cost, 30% profit + 'profit_margin' => 0.30, + 'quantity' => $item['quantity'] ?? 1, + 'created_at' => $order->created_at, + 'updated_at' => $order->updated_at + ]); + } + } else { + // If no cart data, create a single entry for the order + RevenueTracking::updateOrCreate([ + 'order_id' => $order->id, + 'product_name' => 'Order #' . $order->id + ], [ + 'amount' => $order->total_amount, + 'cost' => $order->total_amount * 0.7, + 'profit_margin' => 0.30, + 'quantity' => 1, + 'created_at' => $order->created_at, + 'updated_at' => $order->updated_at + ]); + } + } + + $this->info('Analytics data populated successfully!'); + $this->info('Total revenue tracking entries: ' . RevenueTracking::count()); + } +} \ No newline at end of file diff --git a/app/Console/Commands/TestDashboard.php b/app/Console/Commands/TestDashboard.php new file mode 100644 index 0000000000000000000000000000000000000000..05e3136e80afdf47cf807daf7e3c19cd89d3b19a --- /dev/null +++ b/app/Console/Commands/TestDashboard.php @@ -0,0 +1,57 @@ +info('Testing Analytics Service...'); + + try { + $analyticsService = app(AnalyticsService::class); + + $this->info('Testing getTodayRevenue...'); + $todayRevenue = $analyticsService->getTodayRevenue(); + $this->info('Today Revenue: ' . json_encode($todayRevenue)); + + $this->info('Testing getProfitData...'); + $profitData = $analyticsService->getProfitData('today'); + $this->info('Profit Data: ' . json_encode($profitData)); + + $this->info('Testing getCustomerAnalytics...'); + $customerAnalytics = $analyticsService->getCustomerAnalytics(); + $this->info('Customer Analytics: ' . json_encode($customerAnalytics)); + + $this->info('Testing getOrderStats...'); + $orderStats = $analyticsService->getOrderStats(); + $this->info('Order Stats: ' . json_encode($orderStats)); + + $this->info('✅ All tests passed!'); + + } catch (\Exception $e) { + $this->error('❌ Error: ' . $e->getMessage()); + $this->error('File: ' . $e->getFile() . ':' . $e->getLine()); + } + } +} diff --git a/app/Http/Controllers/Api/DashboardController.php b/app/Http/Controllers/Api/DashboardController.php new file mode 100644 index 0000000000000000000000000000000000000000..5546f614eeb5c3b9690145035e86737f90a3082a --- /dev/null +++ b/app/Http/Controllers/Api/DashboardController.php @@ -0,0 +1,263 @@ +analyticsService = $analyticsService; + } + + /** + * Get revenue data with time period filtering + */ + public function revenue(Request $request): JsonResponse + { + $period = $request->get('period', 'daily'); + $date = $request->get('date') ? Carbon::parse($request->get('date')) : Carbon::now(); + + $data = $this->analyticsService->getAggregatedData($period, $date); + $todayRevenue = $this->analyticsService->getTodayRevenue(); + + return response()->json([ + 'success' => true, + 'data' => [ + 'chart_data' => $data, + 'summary' => $todayRevenue, + 'period' => $period, + 'date' => $date->format('Y-m-d') + ] + ]); + } + + /** + * Get profit data with trend calculations + */ + public function profit(Request $request): JsonResponse + { + $period = $request->get('period', 'today'); + $date = $request->get('date') ? Carbon::parse($request->get('date')) : Carbon::now(); + + $profitData = $this->analyticsService->getProfitData($period); + $chartData = $this->analyticsService->getAggregatedData($period === 'today' ? 'daily' : $period, $date); + + // Format chart data for profit visualization + $formattedChartData = array_map(function ($item) { + return [ + 'label' => $item['hour'] ?? $item['day_name'] ?? $item['day'] ?? $item['date'], + 'revenue' => $item['revenue'], + 'profit' => $item['profit'], + 'profit_margin' => $item['revenue'] > 0 ? (($item['profit'] / $item['revenue']) * 100) : 0 + ]; + }, $chartData); + + return response()->json([ + 'success' => true, + 'data' => [ + 'chart_data' => $formattedChartData, + 'summary' => $profitData, + 'period' => $period, + 'date' => $date->format('Y-m-d') + ] + ]); + } + + /** + * Get sales analytics with product performance + */ + public function sales(Request $request): JsonResponse + { + $limit = $request->get('limit', 10); + + $topProducts = $this->analyticsService->getTopProducts($limit); + $period = $request->get('period', 'daily'); + $date = $request->get('date') ? Carbon::parse($request->get('date')) : Carbon::now(); + + $salesData = $this->analyticsService->getAggregatedData($period, $date); + + // Format sales chart data + $formattedSalesData = array_map(function ($item) { + return [ + 'label' => $item['hour'] ?? $item['day_name'] ?? $item['day'] ?? $item['date'], + 'orders' => $item['orders'], + 'revenue' => $item['revenue'], + 'avg_order_value' => $item['orders'] > 0 ? ($item['revenue'] / $item['orders']) : 0 + ]; + }, $salesData); + + return response()->json([ + 'success' => true, + 'data' => [ + 'top_products' => $topProducts, + 'sales_chart' => $formattedSalesData, + 'period' => $period, + 'date' => $date->format('Y-m-d') + ] + ]); + } + + /** + * Get customer analytics with new vs returning metrics + */ + public function customers(Request $request): JsonResponse + { + $customerAnalytics = $this->analyticsService->getCustomerAnalytics(); + $period = $request->get('period', 'daily'); + $date = $request->get('date') ? Carbon::parse($request->get('date')) : Carbon::now(); + + $customerData = $this->analyticsService->getAggregatedData($period, $date); + + // Format customer chart data + $formattedCustomerData = array_map(function ($item) { + return [ + 'label' => $item['hour'] ?? $item['day_name'] ?? $item['day'] ?? $item['date'], + 'customers' => $item['customers'], + 'orders' => $item['orders'], + 'conversion_rate' => $item['customers'] > 0 ? (($item['orders'] / $item['customers']) * 100) : 0 + ]; + }, $customerData); + + return response()->json([ + 'success' => true, + 'data' => [ + 'analytics' => $customerAnalytics, + 'chart_data' => $formattedCustomerData, + 'period' => $period, + 'date' => $date->format('Y-m-d') + ] + ]); + } + + /** + * Get all dashboard metrics summary with period support + */ + public function summary(Request $request): JsonResponse + { + $period = $request->get('period', 'daily'); + $date = $request->get('date') ? Carbon::parse($request->get('date')) : Carbon::now(); + + // Get period-specific data + $revenueData = $this->getRevenueForPeriod($period, $date); + $profitData = $this->analyticsService->getProfitData($period); + $customerAnalytics = $this->analyticsService->getCustomerAnalytics(); + $orderStats = $this->getOrdersForPeriod($period, $date); + $productStats = $this->analyticsService->getProductStats(); + $topProducts = $this->analyticsService->getTopProducts(5); + + return response()->json([ + 'success' => true, + 'data' => [ + 'revenue' => $revenueData, + 'profit' => $profitData, + 'orders' => $orderStats, + 'customers' => $customerAnalytics, + 'products' => $productStats, + 'top_products' => $topProducts, + 'period' => $period, + 'date' => $date->format('Y-m-d'), + 'last_updated' => Carbon::now()->toISOString() + ] + ]); + } + + /** + * Get revenue data for specific period + */ + private function getRevenueForPeriod(string $period, Carbon $date): array + { + switch ($period) { + case 'weekly': + $current = \App\Models\Order::whereBetween('created_at', [ + $date->copy()->startOfWeek(), + $date->copy()->endOfWeek() + ])->sum('total_amount'); + + $previous = \App\Models\Order::whereBetween('created_at', [ + $date->copy()->subWeek()->startOfWeek(), + $date->copy()->subWeek()->endOfWeek() + ])->sum('total_amount'); + break; + + case 'monthly': + $current = \App\Models\Order::whereBetween('created_at', [ + $date->copy()->startOfMonth(), + $date->copy()->endOfMonth() + ])->sum('total_amount'); + + $previous = \App\Models\Order::whereBetween('created_at', [ + $date->copy()->subMonth()->startOfMonth(), + $date->copy()->subMonth()->endOfMonth() + ])->sum('total_amount'); + break; + + default: // daily + $current = \App\Models\Order::whereDate('created_at', $date)->sum('total_amount'); + $previous = \App\Models\Order::whereDate('created_at', $date->copy()->subDay())->sum('total_amount'); + break; + } + + $percentageChange = $previous > 0 ? (($current - $previous) / $previous) * 100 : 0; + + return [ + 'current' => $current, + 'previous' => $previous, + 'percentage_change' => round($percentageChange, 2), + 'trend' => $percentageChange >= 0 ? 'up' : 'down' + ]; + } + + /** + * Get orders data for specific period + */ + private function getOrdersForPeriod(string $period, Carbon $date): array + { + switch ($period) { + case 'weekly': + $current = \App\Models\Order::whereBetween('created_at', [ + $date->copy()->startOfWeek(), + $date->copy()->endOfWeek() + ])->count(); + + $previous = \App\Models\Order::whereBetween('created_at', [ + $date->copy()->subWeek()->startOfWeek(), + $date->copy()->subWeek()->endOfWeek() + ])->count(); + break; + + case 'monthly': + $current = \App\Models\Order::whereBetween('created_at', [ + $date->copy()->startOfMonth(), + $date->copy()->endOfMonth() + ])->count(); + + $previous = \App\Models\Order::whereBetween('created_at', [ + $date->copy()->subMonth()->startOfMonth(), + $date->copy()->subMonth()->endOfMonth() + ])->count(); + break; + + default: // daily + $current = \App\Models\Order::whereDate('created_at', $date)->count(); + $previous = \App\Models\Order::whereDate('created_at', $date->copy()->subDay())->count(); + break; + } + + $percentageChange = $previous > 0 ? (($current - $previous) / $previous) * 100 : 0; + + return [ + 'current' => $current, + 'previous' => $previous, + 'percentage_change' => round($percentageChange, 2), + 'trend' => $percentageChange >= 0 ? 'up' : 'down' + ]; + } +} diff --git a/app/Http/Controllers/Auth/AuthenticatedSessionController.php b/app/Http/Controllers/Auth/AuthenticatedSessionController.php new file mode 100644 index 0000000000000000000000000000000000000000..613bcd9d935fca7b0e7451060d8dd640f77c2509 --- /dev/null +++ b/app/Http/Controllers/Auth/AuthenticatedSessionController.php @@ -0,0 +1,47 @@ +authenticate(); + + $request->session()->regenerate(); + + return redirect()->intended(route('dashboard', absolute: false)); + } + + /** + * Destroy an authenticated session. + */ + public function destroy(Request $request): RedirectResponse + { + Auth::guard('web')->logout(); + + $request->session()->invalidate(); + + $request->session()->regenerateToken(); + + return redirect('/'); + } +} diff --git a/app/Http/Controllers/Auth/ConfirmablePasswordController.php b/app/Http/Controllers/Auth/ConfirmablePasswordController.php new file mode 100644 index 0000000000000000000000000000000000000000..712394a5a377259f398070884d4c15e9bd7a575c --- /dev/null +++ b/app/Http/Controllers/Auth/ConfirmablePasswordController.php @@ -0,0 +1,40 @@ +validate([ + 'email' => $request->user()->email, + 'password' => $request->password, + ])) { + throw ValidationException::withMessages([ + 'password' => __('auth.password'), + ]); + } + + $request->session()->put('auth.password_confirmed_at', time()); + + return redirect()->intended(route('dashboard', absolute: false)); + } +} diff --git a/app/Http/Controllers/Auth/EmailVerificationNotificationController.php b/app/Http/Controllers/Auth/EmailVerificationNotificationController.php new file mode 100644 index 0000000000000000000000000000000000000000..f64fa9ba79891e709d812c99dcc4440ed237993d --- /dev/null +++ b/app/Http/Controllers/Auth/EmailVerificationNotificationController.php @@ -0,0 +1,24 @@ +user()->hasVerifiedEmail()) { + return redirect()->intended(route('dashboard', absolute: false)); + } + + $request->user()->sendEmailVerificationNotification(); + + return back()->with('status', 'verification-link-sent'); + } +} diff --git a/app/Http/Controllers/Auth/EmailVerificationPromptController.php b/app/Http/Controllers/Auth/EmailVerificationPromptController.php new file mode 100644 index 0000000000000000000000000000000000000000..ee3cb6facd7f33c1c5e2a2943915c1c1a203b1f0 --- /dev/null +++ b/app/Http/Controllers/Auth/EmailVerificationPromptController.php @@ -0,0 +1,21 @@ +user()->hasVerifiedEmail() + ? redirect()->intended(route('dashboard', absolute: false)) + : view('auth.verify-email'); + } +} diff --git a/app/Http/Controllers/Auth/NewPasswordController.php b/app/Http/Controllers/Auth/NewPasswordController.php new file mode 100644 index 0000000000000000000000000000000000000000..e8368bd22a9771696ef87574dc25914eb2c0a39d --- /dev/null +++ b/app/Http/Controllers/Auth/NewPasswordController.php @@ -0,0 +1,62 @@ + $request]); + } + + /** + * Handle an incoming new password request. + * + * @throws \Illuminate\Validation\ValidationException + */ + public function store(Request $request): RedirectResponse + { + $request->validate([ + 'token' => ['required'], + 'email' => ['required', 'email'], + 'password' => ['required', 'confirmed', Rules\Password::defaults()], + ]); + + // Here we will attempt to reset the user's password. If it is successful we + // will update the password on an actual user model and persist it to the + // database. Otherwise we will parse the error and return the response. + $status = Password::reset( + $request->only('email', 'password', 'password_confirmation', 'token'), + function (User $user) use ($request) { + $user->forceFill([ + 'password' => Hash::make($request->password), + 'remember_token' => Str::random(60), + ])->save(); + + event(new PasswordReset($user)); + } + ); + + // If the password was successfully reset, we will redirect the user back to + // the application's home authenticated view. If there is an error we can + // redirect them back to where they came from with their error message. + return $status == Password::PASSWORD_RESET + ? redirect()->route('login')->with('status', __($status)) + : back()->withInput($request->only('email')) + ->withErrors(['email' => __($status)]); + } +} diff --git a/app/Http/Controllers/Auth/PasswordController.php b/app/Http/Controllers/Auth/PasswordController.php new file mode 100644 index 0000000000000000000000000000000000000000..69164091a61710120417c27d90793a6bb2074e4d --- /dev/null +++ b/app/Http/Controllers/Auth/PasswordController.php @@ -0,0 +1,29 @@ +validateWithBag('updatePassword', [ + 'current_password' => ['required', 'current_password'], + 'password' => ['required', Password::defaults(), 'confirmed'], + ]); + + $request->user()->update([ + 'password' => Hash::make($validated['password']), + ]); + + return back()->with('status', 'password-updated'); + } +} diff --git a/app/Http/Controllers/Auth/PasswordResetLinkController.php b/app/Http/Controllers/Auth/PasswordResetLinkController.php new file mode 100644 index 0000000000000000000000000000000000000000..bf1ebfa7882b546604645fbef3a780c55f0660d7 --- /dev/null +++ b/app/Http/Controllers/Auth/PasswordResetLinkController.php @@ -0,0 +1,44 @@ +validate([ + 'email' => ['required', 'email'], + ]); + + // We will send the password reset link to this user. Once we have attempted + // to send the link, we will examine the response then see the message we + // need to show to the user. Finally, we'll send out a proper response. + $status = Password::sendResetLink( + $request->only('email') + ); + + return $status == Password::RESET_LINK_SENT + ? back()->with('status', __($status)) + : back()->withInput($request->only('email')) + ->withErrors(['email' => __($status)]); + } +} diff --git a/app/Http/Controllers/Auth/RegisteredUserController.php b/app/Http/Controllers/Auth/RegisteredUserController.php new file mode 100644 index 0000000000000000000000000000000000000000..0739e2e87680d6fa82b0f4a46791d3bf2aa278fd --- /dev/null +++ b/app/Http/Controllers/Auth/RegisteredUserController.php @@ -0,0 +1,50 @@ +validate([ + 'name' => ['required', 'string', 'max:255'], + 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], + 'password' => ['required', 'confirmed', Rules\Password::defaults()], + ]); + + $user = User::create([ + 'name' => $request->name, + 'email' => $request->email, + 'password' => Hash::make($request->password), + ]); + + event(new Registered($user)); + + Auth::login($user); + + return redirect(route('dashboard', absolute: false)); + } +} diff --git a/app/Http/Controllers/Auth/VerifyEmailController.php b/app/Http/Controllers/Auth/VerifyEmailController.php new file mode 100644 index 0000000000000000000000000000000000000000..784765e3a5961b9293b74158d8c270088ef4738a --- /dev/null +++ b/app/Http/Controllers/Auth/VerifyEmailController.php @@ -0,0 +1,27 @@ +user()->hasVerifiedEmail()) { + return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); + } + + if ($request->user()->markEmailAsVerified()) { + event(new Verified($request->user())); + } + + return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); + } +} diff --git a/app/Http/Controllers/CartController.php b/app/Http/Controllers/CartController.php new file mode 100644 index 0000000000000000000000000000000000000000..3610d01f9d87aa393ca71ea31b304e03532b1f3e --- /dev/null +++ b/app/Http/Controllers/CartController.php @@ -0,0 +1,255 @@ +get('cart', []); + return view('cart', compact('cart')); + } + + public function add(Request $request, $id) + { + $product = Product::findOrFail($id); + $quantity = (int) $request->input('quantity', 1); + + // Check if product has enough stock + if ($product->Amount < $quantity) { + if ($request->ajax()) { + return response()->json([ + 'success' => false, + 'message' => 'Insufficient stock. Available: ' . $product->Amount + ]); + } + return back()->with('error', 'Insufficient stock. Available: ' . $product->Amount); + } + + $cart = session()->get('cart', []); + + if (isset($cart[$id])) { + $newQuantity = $cart[$id]['quantity'] + $quantity; + if ($newQuantity > $product->Amount) { + if ($request->ajax()) { + return response()->json([ + 'success' => false, + 'message' => 'Cannot add more items. Stock limit: ' . $product->Amount + ]); + } + return back()->with('error', 'Cannot add more items. Stock limit: ' . $product->Amount); + } + $cart[$id]['quantity'] = $newQuantity; + } else { + $cart[$id] = [ + 'id' => $product->id, + 'name' => $product->name, + 'image' => $product->image, + 'price' => $product->price, + 'quantity' => $quantity, + 'game' => $product->game, + ]; + } + + session()->put('cart', $cart); + + // Return JSON response for AJAX requests + if ($request->ajax()) { + return response()->json([ + 'success' => true, + 'message' => 'Item added to cart successfully', + 'cart_count' => array_sum(array_column($cart, 'quantity')) + ]); + } + + return redirect()->route('cart.index')->with('success', 'เพิ่มสินค้าลงตะกร้าเรียบร้อยแล้ว'); + } + + public function remove($id) + { + $cart = session()->get('cart', []); + + if (isset($cart[$id])) { + unset($cart[$id]); + session()->put('cart', $cart); + } + + return redirect()->route('cart.index')->with('success', 'ลบสินค้าออกจากตะกร้าเรียบร้อยแล้ว'); + } + public function checkout(Request $request) + { + $request->validate([ + 'customer_name' => 'required|string|max:255', + 'phone' => 'required|string|max:20', + 'payment_slip' => 'required|image|mimes:jpeg,png,jpg,gif|max:10240', // 10MB limit + ]); + + try { + $path = $request->file('payment_slip')->store('payment_slips', 'public'); + + // Get cart data + $cart = session()->get('cart', []); + + if (empty($cart)) { + return back()->with('error', 'Your cart is empty'); + } + + // Calculate total amount and validate stock + $totalAmount = 0; + foreach ($cart as $item) { + $product = Product::find($item['id']); + if (!$product) { + return back()->with('error', "Product {$item['name']} no longer exists"); + } + if ($product->Amount < $item['quantity']) { + return back()->with('error', "Insufficient stock for {$product->name}. Available: {$product->Amount}, Requested: {$item['quantity']}"); + } + $totalAmount += $item['price'] * $item['quantity']; + } + + // Create order + $order = Order::create([ + 'customer_name' => $request->customer_name, + 'phone' => $request->phone, + 'payment_slip_path' => $path, + 'cart_data' => json_encode($cart), + 'total_amount' => $totalAmount, + 'status' => 'pending', + ]); + + // Update product stock + foreach ($cart as $item) { + $product = Product::find($item['id']); + if ($product) { + $product->Amount = max(0, $product->Amount - $item['quantity']); + $product->save(); + } + } + + // Clear cart + session()->forget('cart'); + + return redirect()->route('cart.index')->with('success', 'Thank you for your order! We have received your order successfully.'); + + } catch (\Exception $e) { + return back()->with('error', 'Failed to process order: ' . $e->getMessage()); + } + } + public function order() + { + $orders = Order::latest()->get(); // ดึงทั้งหมด เรียงล่าสุดก่อน + return view('order', compact('orders')); + } + public function deleteOrder($id) + { + try { + $order = Order::findOrFail($id); + + // Delete payment slip file if it exists + if ($order->payment_slip_path && Storage::disk('public')->exists($order->payment_slip_path)) { + Storage::disk('public')->delete($order->payment_slip_path); + } + + $order->delete(); + + return back()->with('success', 'Order deleted successfully'); + } catch (\Exception $e) { + return back()->with('error', 'Failed to delete order: ' . $e->getMessage()); + } + } + + public function show($id) + { + try { + $order = Order::findOrFail($id); + return view('order-detail', compact('order')); + } catch (\Exception $e) { + return redirect()->route('orders.index')->with('error', 'Order not found'); + } + } + + public function completeOrder($id) + { + try { + $order = Order::findOrFail($id); + + // Update order status to completed + $order->status = 'completed'; + $order->save(); + + return redirect()->route('orders.index')->with('success', 'Order #' . str_pad($order->id, 4, '0', STR_PAD_LEFT) . ' has been ACCEPTED successfully!'); + } catch (\Exception $e) { + return redirect()->route('orders.index')->with('error', 'Failed to accept order: ' . $e->getMessage()); + } + } + + public function rejectOrder($id) + { + try { + $order = Order::findOrFail($id); + + // Update order status to cancelled (rejected) + $order->status = 'cancelled'; + $order->save(); + + return redirect()->route('orders.index')->with('success', 'Order #' . str_pad($order->id, 4, '0', STR_PAD_LEFT) . ' has been REJECTED. Customer will see it as cancelled.'); + } catch (\Exception $e) { + return redirect()->route('orders.index')->with('error', 'Failed to reject order: ' . $e->getMessage()); + } + } + + public function customerOrders(Request $request) + { + // For now, we'll show all orders since we don't have user authentication for customers + // In a real app, you'd filter by authenticated customer + $orders = Order::latest()->get(); + + // If it's an AJAX request for load more functionality + if ($request->ajax()) { + $page = $request->get('page', 1); + $perPage = 3; + $offset = ($page - 1) * $perPage; + $status = $request->get('status'); + $game = $request->get('game'); + + // Filter orders based on status + $filteredOrders = $orders; + + if ($status && $status !== 'recently') { + $filteredOrders = $orders->where('status', $status); + } + + // Filter by game if specified + if ($game && $game !== 'all') { + $filteredOrders = $filteredOrders->filter(function($order) use ($game) { + $items = json_decode($order->cart_data, true); + if (is_array($items)) { + foreach ($items as $item) { + $product = \App\Models\Product::find($item['id'] ?? null); + if ($product && $product->game === $game) { + return true; + } + } + } + return false; + }); + } + + $paginatedOrders = $filteredOrders->slice($offset, $perPage); + $hasMore = $filteredOrders->count() > ($offset + $perPage); + + return response()->json([ + 'orders' => $paginatedOrders->values(), + 'hasMore' => $hasMore, + 'currentPage' => $page + ]); + } + + return view('customer-orders', compact('orders')); + } +} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php new file mode 100644 index 0000000000000000000000000000000000000000..8677cd5cabb06901eac3d8f9a500094d2c1dab45 --- /dev/null +++ b/app/Http/Controllers/Controller.php @@ -0,0 +1,8 @@ +analyticsService = $analyticsService; + } + + public function index() + { + try { + $todayRevenue = $this->analyticsService->getTodayRevenue(); + $profitData = $this->analyticsService->getProfitData('today'); + $customerAnalytics = $this->analyticsService->getCustomerAnalytics(); + $orderStats = $this->analyticsService->getOrderStats(); + } catch (\Exception $e) { + // Fallback values if service fails + $todayRevenue = ['current' => 0, 'previous' => 0, 'percentage_change' => 0, 'trend' => 'up']; + $profitData = ['profit_margin' => 0, 'percentage_change' => 0, 'trend' => 'up']; + $customerAnalytics = ['total_customers' => 0, 'active_today' => 0]; + $orderStats = ['current' => 0, 'previous' => 0, 'percentage_change' => 0, 'trend' => 'up']; + } + + return view('dashboard', compact( + 'todayRevenue', + 'profitData', + 'customerAnalytics', + 'orderStats' + )); + } +} diff --git a/app/Http/Controllers/ProductController.php b/app/Http/Controllers/ProductController.php new file mode 100644 index 0000000000000000000000000000000000000000..cce3d5901823c782c3f337d49175a42c23b4a7a6 --- /dev/null +++ b/app/Http/Controllers/ProductController.php @@ -0,0 +1,411 @@ +validate([ + 'name' => 'required', + 'price' => 'required|numeric', + 'description' => 'required', + 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', + 'Amount' => 'required|numeric', + 'game' => 'nullable|string' + ]); + + $product = new Product($request->only(['name', 'price', 'description', 'Amount', 'game'])); + + if ($request->hasFile('image')) { + $imagePath = $request->file('image')->store('products', 'public'); + $product->image = $imagePath; + } + + $product->save(); + + return redirect()->back()->with('success', 'Product created successfully'); + } + public function show($category, $product_name) + { + $product = Product::where('game', $category) + ->where('slug', $product_name) + ->firstOrFail(); + return view('show', compact('product')); + } + + public function categoryProducts($category) + { + $products = Product::where('game', $category)->get(); + + // Get all custom games for navigation + $customGames = CustomGame::orderBy('name')->get(); + + return view('category_products', compact('products', 'category', 'customGames')); + } + + public function edit($id) + { + $product = Product::findOrFail($id); + return response()->json($product); + } + public function listOFproduct(Request $request) + { + $query = Product::query(); + + // Game filter + if ($request->filled('game')) { + $query->where('game', $request->input('game')); + } + + // Stock filter + if ($request->filled('stock')) { + $stock = $request->input('stock'); + switch ($stock) { + case 'in_stock': + $query->where('Amount', '>', 5); + break; + case 'low_stock': + $query->whereBetween('Amount', [1, 5]); + break; + case 'out_of_stock': + $query->where('Amount', 0); + break; + } + } + + // Search filter + if ($request->filled('search')) { + $search = $request->input('search'); + $query->where(function($q) use ($search) { + $q->where('name', 'like', '%' . $search . '%') + ->orWhere('description', 'like', '%' . $search . '%'); + }); + } + + $products = $query->orderBy('created_at', 'desc')->get(); + + // Get all custom games for the view + $customGames = CustomGame::orderBy('name')->get(); + + return view('table_product', compact('products', 'customGames')); + } + public function update(Request $request, $id) + { + $request->validate([ + 'name' => 'required', + 'price' => 'required|numeric', + 'description' => 'required', + 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', + 'Amount' => 'required|numeric', + 'game' => 'nullable|string' + ]); + + $product = Product::findOrFail($id); + $product->update($request->only(['name', 'price', 'description', 'Amount', 'game'])); + + if ($request->hasFile('image')) { + // Delete old image if exists + if ($product->image && Storage::disk('public')->exists($product->image)) { + Storage::disk('public')->delete($product->image); + } + + $imagePath = $request->file('image')->store('products', 'public'); + $product->image = $imagePath; + $product->save(); + } + + return redirect()->back()->with('success', 'Product updated successfully'); + } + + public function destroy($id) + { + try { + $product = Product::findOrFail($id); + + // Delete image file if exists + if ($product->image && Storage::disk('public')->exists($product->image)) { + Storage::disk('public')->delete($product->image); + } + + $product->delete(); + + return back()->with('success', 'Product deleted successfully'); + } catch (\Exception $e) { + return back()->with('error', 'Failed to delete product: ' . $e->getMessage()); + } + } + + public function categories() + { + // Get count of products for each category + $categoryCounts = [ + 'Genshin' => Product::where('game', 'Genshin')->count(), + 'Starrail' => Product::where('game', 'Starrail')->count(), + 'WutheringWave' => Product::where('game', 'WutheringWave')->count(), + ]; + + // Get all custom games + $customGames = CustomGame::orderBy('name')->get(); + + return view('categories', compact('categoryCounts', 'customGames')); + } + + public function getByCategory($game) + { + $products = Product::where('game', $game)->get(); + + // Add sales count to each product - only count completed orders + $productsWithSales = $products->map(function ($product) { + $salesCount = \App\Models\Order::where('cart_data', 'LIKE', '%"product_id":' . $product->id . '%') + ->where('status', 'completed') + ->count(); + $product->sales_count = $salesCount; + return $product; + }); + + return response()->json($productsWithSales); + } + + public function storeCustomGame(Request $request) + { + try { + $request->validate([ + 'name' => 'required|string|max:50|unique:custom_games,name', + 'icon' => 'nullable|string', + 'color_gradient' => 'nullable|string' + ]); + + $customGame = CustomGame::create([ + 'name' => $request->name, + 'icon' => $request->icon ?? 'fas fa-gamepad', + 'color_gradient' => $request->color_gradient ?? 'from-purple-500 to-blue-500' + ]); + + return response()->json([ + 'success' => true, + 'message' => "Custom game '{$request->name}' has been created successfully!", + 'data' => $customGame + ]); + + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => 'Failed to create custom game: ' . $e->getMessage() + ], 500); + } + } + + public function deleteCustomGame($gameName) + { + try { + // Check if there are any products using this custom game + $productsCount = Product::where('game', $gameName)->count(); + + if ($productsCount > 0) { + return response()->json([ + 'success' => false, + 'message' => "Cannot delete '{$gameName}' because {$productsCount} product(s) are still using this category. Please reassign or delete those products first." + ], 400); + } + + // Delete from database + $customGame = CustomGame::where('name', $gameName)->first(); + if ($customGame) { + $customGame->delete(); + } + + return response()->json([ + 'success' => true, + 'message' => "Custom game '{$gameName}' has been deleted successfully!" + ]); + + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => 'Failed to delete custom game: ' . $e->getMessage() + ], 500); + } + } + + public function search(Request $request) + { + $query = $request->get('q', ''); + + if (strlen($query) < 2) { + return response()->json(['games' => [], 'products' => []]); + } + + // Search games with rich data including descriptions and gradients + $mainGames = [ + [ + 'name' => 'Genshin Impact', + 'slug' => 'Genshin', + 'icon' => 'fas fa-star', + 'count' => Product::where('game', 'Genshin')->count(), + 'description' => 'Discover premium digital content for your favorite games', + 'gradient' => 'from-yellow-500 to-orange-500', + 'logo' => asset('images/games/genshin-logo.png'), // You can add actual logos later + 'category' => 'RPG', + 'popularity' => 'Popular' + ], + [ + 'name' => 'Honkai: Star Rail', + 'slug' => 'Starrail', + 'icon' => 'fas fa-rocket', + 'count' => Product::where('game', 'Starrail')->count(), + 'description' => 'Space fantasy RPG with strategic combat', + 'gradient' => 'from-purple-500 to-pink-500', + 'logo' => asset('images/games/starrail-logo.png'), + 'category' => 'RPG', + 'popularity' => 'Featured' + ], + [ + 'name' => 'Wuthering Waves', + 'slug' => 'WutheringWave', + 'icon' => 'fas fa-wave-square', + 'count' => Product::where('game', 'WutheringWave')->count(), + 'description' => 'Open-world action RPG with stunning visuals', + 'gradient' => 'from-cyan-500 to-blue-500', + 'logo' => asset('images/games/wuthering-logo.png'), + 'category' => 'Action RPG', + 'popularity' => 'New' + ], + [ + 'name' => 'Zenless Zone Zero', + 'slug' => 'ZenlessZoneZero', + 'icon' => 'fas fa-city', + 'count' => Product::where('game', 'ZenlessZoneZero')->count(), + 'description' => 'Urban fantasy action game', + 'gradient' => 'from-red-500 to-pink-500', + 'logo' => asset('images/games/zenless-logo.png'), + 'category' => 'Action', + 'popularity' => 'Trending' + ], + [ + 'name' => 'Arknights', + 'slug' => 'Arknights', + 'icon' => 'fas fa-chess-knight', + 'count' => Product::where('game', 'Arknights')->count(), + 'description' => 'Strategic tower defense with anime characters', + 'gradient' => 'from-indigo-500 to-purple-500', + 'logo' => asset('images/games/arknights-logo.png'), + 'category' => 'Strategy', + 'popularity' => 'Classic' + ], + [ + 'name' => 'Azur Lane', + 'slug' => 'AzurLane', + 'icon' => 'fas fa-ship', + 'count' => Product::where('game', 'AzurLane')->count(), + 'description' => 'Naval warfare with anthropomorphic ships', + 'gradient' => 'from-blue-500 to-teal-500', + 'logo' => asset('images/games/azurlane-logo.png'), + 'category' => 'Strategy', + 'popularity' => 'Popular' + ], + ]; + + // Add custom games with their stored styling + $customGames = CustomGame::all()->map(function($game) { + return [ + 'name' => $game->name, + 'slug' => $game->name, + 'icon' => $game->icon, + 'count' => Product::where('game', $game->name)->count(), + 'description' => 'Custom game category with unique items', + 'gradient' => $game->color_gradient, + 'logo' => asset('images/games/custom-logo.png'), + 'category' => 'Custom', + 'popularity' => 'Special' + ]; + }); + + $allGames = collect($mainGames)->concat($customGames); + + // Filter games by query + $matchingGames = $allGames->filter(function($game) use ($query) { + return stripos($game['name'], $query) !== false; + })->take(5)->values(); + + // Search products with enhanced data including real sales count + $products = Product::where('name', 'like', "%{$query}%") + ->orWhere('description', 'like', "%{$query}%") + ->orWhere('game', 'like', "%{$query}%") + ->take(8) + ->get() + ->map(function($product) { + // Calculate real sales count from completed orders + $salesCount = \App\Models\Order::where('status', 'completed') + ->where('cart_data', 'LIKE', '%"id":' . $product->id . '%') + ->get() + ->sum(function($order) use ($product) { + $cartData = json_decode($order->cart_data, true); + $quantity = 0; + if (is_array($cartData)) { + foreach ($cartData as $item) { + if (isset($item['id']) && $item['id'] == $product->id) { + $quantity += $item['quantity'] ?? 1; + } + } + } + return $quantity; + }); + + // Calculate average rating from orders (mock for now, can be enhanced later) + $rating = $salesCount > 0 ? min(5.0, 3.5 + ($salesCount / 100)) : 4.0; + + return [ + 'id' => $product->id, + 'name' => $product->name, + 'slug' => $product->slug ?? strtolower(str_replace(' ', '-', $product->name)), + 'game' => $product->game, + 'game_slug' => $product->game, + 'price' => number_format($product->price, 2), + 'original_price' => $product->price, + 'image' => $product->image ? asset($product->image) : 'https://via.placeholder.com/150x150/374151/ffffff?text=No+Image', + 'description' => $product->description, + 'short_description' => strlen($product->description) > 100 ? substr($product->description, 0, 100) . '...' : $product->description, + 'amount' => $product->Amount, + 'stock_status' => $product->Amount > 10 ? 'In Stock (' . $product->Amount . ')' : + ($product->Amount > 0 ? 'Low Stock (' . $product->Amount . ')' : 'Out of Stock'), + 'stock_color' => $product->Amount > 10 ? 'text-green-400' : + ($product->Amount > 0 ? 'text-yellow-400' : 'text-red-400'), + 'stock_bg' => $product->Amount > 10 ? 'bg-green-500/20' : + ($product->Amount > 0 ? 'bg-yellow-500/20' : 'bg-red-500/20'), + 'created_at' => $product->created_at->format('M d, Y'), + 'is_new' => $product->created_at->diffInDays(now()) <= 7, + 'rating' => round($rating, 1), + 'sales_count' => $salesCount, + 'total_revenue' => $salesCount * $product->price, + 'availability' => $product->Amount > 0 ? 'Available' : 'Sold Out', + 'category_icon' => $this->getGameIcon($product->game), + 'popularity_score' => $salesCount > 50 ? 'Hot' : ($salesCount > 20 ? 'Popular' : 'New') + ]; + }); + + return response()->json([ + 'games' => $matchingGames, + 'products' => $products + ]); + } + + private function getGameIcon($game) + { + $icons = [ + 'Genshin' => 'fas fa-star', + 'Starrail' => 'fas fa-rocket', + 'WutheringWave' => 'fas fa-wave-square', + 'ZenlessZoneZero' => 'fas fa-city', + 'Arknights' => 'fas fa-chess-knight', + 'AzurLane' => 'fas fa-ship', + ]; + + return $icons[$game] ?? 'fas fa-gamepad'; + } +} diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php new file mode 100644 index 0000000000000000000000000000000000000000..a48eb8d829dc3b348d094c108fcda9219c5c4ee7 --- /dev/null +++ b/app/Http/Controllers/ProfileController.php @@ -0,0 +1,60 @@ + $request->user(), + ]); + } + + /** + * Update the user's profile information. + */ + public function update(ProfileUpdateRequest $request): RedirectResponse + { + $request->user()->fill($request->validated()); + + if ($request->user()->isDirty('email')) { + $request->user()->email_verified_at = null; + } + + $request->user()->save(); + + return Redirect::route('profile.edit')->with('status', 'profile-updated'); + } + + /** + * Delete the user's account. + */ + public function destroy(Request $request): RedirectResponse + { + $request->validateWithBag('userDeletion', [ + 'password' => ['required', 'current_password'], + ]); + + $user = $request->user(); + + Auth::logout(); + + $user->delete(); + + $request->session()->invalidate(); + $request->session()->regenerateToken(); + + return Redirect::to('/'); + } +} diff --git a/app/Http/Requests/Auth/LoginRequest.php b/app/Http/Requests/Auth/LoginRequest.php new file mode 100644 index 0000000000000000000000000000000000000000..25746424580a1206e6995f034ecbf8b9cc7cd607 --- /dev/null +++ b/app/Http/Requests/Auth/LoginRequest.php @@ -0,0 +1,85 @@ +|string> + */ + public function rules(): array + { + return [ + 'email' => ['required', 'string', 'email'], + 'password' => ['required', 'string'], + ]; + } + + /** + * Attempt to authenticate the request's credentials. + * + * @throws \Illuminate\Validation\ValidationException + */ + public function authenticate(): void + { + $this->ensureIsNotRateLimited(); + + if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) { + RateLimiter::hit($this->throttleKey()); + + throw ValidationException::withMessages([ + 'email' => trans('auth.failed'), + ]); + } + + RateLimiter::clear($this->throttleKey()); + } + + /** + * Ensure the login request is not rate limited. + * + * @throws \Illuminate\Validation\ValidationException + */ + public function ensureIsNotRateLimited(): void + { + if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { + return; + } + + event(new Lockout($this)); + + $seconds = RateLimiter::availableIn($this->throttleKey()); + + throw ValidationException::withMessages([ + 'email' => trans('auth.throttle', [ + 'seconds' => $seconds, + 'minutes' => ceil($seconds / 60), + ]), + ]); + } + + /** + * Get the rate limiting throttle key for the request. + */ + public function throttleKey(): string + { + return Str::transliterate(Str::lower($this->string('email')).'|'.$this->ip()); + } +} diff --git a/app/Http/Requests/ProfileUpdateRequest.php b/app/Http/Requests/ProfileUpdateRequest.php new file mode 100644 index 0000000000000000000000000000000000000000..3622a8f37c2accfa385618240df70ac74fea342a --- /dev/null +++ b/app/Http/Requests/ProfileUpdateRequest.php @@ -0,0 +1,30 @@ +|string> + */ + public function rules(): array + { + return [ + 'name' => ['required', 'string', 'max:255'], + 'email' => [ + 'required', + 'string', + 'lowercase', + 'email', + 'max:255', + Rule::unique(User::class)->ignore($this->user()->id), + ], + ]; + } +} diff --git a/app/Models/AnalyticsSnapshot.php b/app/Models/AnalyticsSnapshot.php new file mode 100644 index 0000000000000000000000000000000000000000..83c53cf024cdf1bf65e03bbf813d0f76bde7f038 --- /dev/null +++ b/app/Models/AnalyticsSnapshot.php @@ -0,0 +1,63 @@ + 'date', + 'revenue' => 'decimal:2', + 'profit' => 'decimal:2', + 'avg_order_value' => 'decimal:2', + 'conversion_rate' => 'decimal:4' + ]; + + // Scopes for different time periods + public function scopeDaily($query, Carbon $date) + { + return $query->where('date', $date->format('Y-m-d')) + ->whereNotNull('hour') + ->orderBy('hour'); + } + + public function scopeWeekly($query, Carbon $startDate) + { + return $query->whereBetween('date', [ + $startDate->format('Y-m-d'), + $startDate->copy()->addDays(6)->format('Y-m-d') + ]) + ->whereNull('hour') + ->orderBy('date'); + } + + public function scopeMonthly($query, Carbon $date) + { + return $query->whereYear('date', $date->year) + ->whereMonth('date', $date->month) + ->whereNull('hour') + ->orderBy('date'); + } + + // Helper method to get profit margin percentage + public function getProfitMarginAttribute() + { + if ($this->revenue > 0) { + return ($this->profit / $this->revenue) * 100; + } + return 0; + } +} diff --git a/app/Models/CustomGame.php b/app/Models/CustomGame.php new file mode 100644 index 0000000000000000000000000000000000000000..f076bb8a678f628f3ef633cdddb9c51b737a3392 --- /dev/null +++ b/app/Models/CustomGame.php @@ -0,0 +1,14 @@ +slug)) { + $product->slug = Str::slug($product->name); + } + }); + + static::updating(function ($product) { + if ($product->isDirty('name') && empty($product->slug)) { + $product->slug = Str::slug($product->name); + } + }); + } + + public function getImageAttribute($value) + { + if (!$value) { + return null; + } + + // If it's already a full URL, return as is + if (str_starts_with($value, 'http')) { + return $value; + } + + // If it starts with 'storage/', it's already formatted + if (str_starts_with($value, 'storage/')) { + return asset($value); + } + + // Otherwise, prepend 'storage/' + return asset('storage/' . $value); + } +} diff --git a/app/Models/RevenueTracking.php b/app/Models/RevenueTracking.php new file mode 100644 index 0000000000000000000000000000000000000000..06b6f0065e5fa2d6d3a7ddd0c30d0c206e999a7f --- /dev/null +++ b/app/Models/RevenueTracking.php @@ -0,0 +1,59 @@ + 'decimal:2', + 'cost' => 'decimal:2', + 'profit_margin' => 'decimal:4' + ]; + + // Relationships + public function order(): BelongsTo + { + return $this->belongsTo(Order::class); + } + + // Calculate profit amount + public function getProfitAttribute() + { + return $this->amount - $this->cost; + } + + // Calculate profit margin percentage + public function getProfitMarginPercentageAttribute() + { + if ($this->amount > 0) { + return (($this->amount - $this->cost) / $this->amount) * 100; + } + return 0; + } + + // Scope for today's revenue + public function scopeToday($query) + { + return $query->whereDate('created_at', today()); + } + + // Scope for date range + public function scopeDateRange($query, $startDate, $endDate) + { + return $query->whereBetween('created_at', [$startDate, $endDate]); + } +} diff --git a/app/Models/User.php b/app/Models/User.php new file mode 100644 index 0000000000000000000000000000000000000000..749c7b77d9befa8c8726db91a4ba38ff777cc8ff --- /dev/null +++ b/app/Models/User.php @@ -0,0 +1,48 @@ + */ + use HasFactory, Notifiable; + + /** + * The attributes that are mass assignable. + * + * @var list + */ + protected $fillable = [ + 'name', + 'email', + 'password', + ]; + + /** + * The attributes that should be hidden for serialization. + * + * @var list + */ + protected $hidden = [ + 'password', + 'remember_token', + ]; + + /** + * Get the attributes that should be cast. + * + * @return array + */ + protected function casts(): array + { + return [ + 'email_verified_at' => 'datetime', + 'password' => 'hashed', + ]; + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php new file mode 100644 index 0000000000000000000000000000000000000000..5ff12f26d8afa457444042d0b5593d52492791a7 --- /dev/null +++ b/app/Providers/AppServiceProvider.php @@ -0,0 +1,28 @@ +header('x-forwarded-proto') === 'https') { + URL::forceScheme('https'); + } + } +} diff --git a/app/Services/AnalyticsService.php b/app/Services/AnalyticsService.php new file mode 100644 index 0000000000000000000000000000000000000000..836069a126f09c238f08fe17a451e00a9010561c --- /dev/null +++ b/app/Services/AnalyticsService.php @@ -0,0 +1,379 @@ +where('status', '!=', 'cancelled') + ->sum('total_amount'); + + $yesterdayRevenue = Order::whereDate('created_at', $yesterday) + ->where('status', '!=', 'cancelled') + ->sum('total_amount'); + + $percentageChange = $yesterdayRevenue > 0 + ? (($todayRevenue - $yesterdayRevenue) / $yesterdayRevenue) * 100 + : ($todayRevenue > 0 ? 100 : 0); + + return [ + 'current' => (float) $todayRevenue, + 'previous' => (float) $yesterdayRevenue, + 'percentage_change' => round($percentageChange, 2), + 'trend' => $percentageChange >= 0 ? 'up' : 'down' + ]; + } + + /** + * Calculate profit data with margins using real Order data + */ + public function getProfitData(string $period = 'today'): array + { + $query = Order::where('status', '!=', 'cancelled'); + $previousQuery = Order::where('status', '!=', 'cancelled'); + + switch ($period) { + case 'today': + $query->whereDate('created_at', Carbon::today()); + $previousQuery->whereDate('created_at', Carbon::yesterday()); + break; + case 'week': + $query->whereBetween('created_at', [Carbon::now()->startOfWeek(), Carbon::now()->endOfWeek()]); + $previousQuery->whereBetween('created_at', [ + Carbon::now()->subWeek()->startOfWeek(), + Carbon::now()->subWeek()->endOfWeek() + ]); + break; + case 'month': + $query->whereMonth('created_at', Carbon::now()->month); + $previousQuery->whereMonth('created_at', Carbon::now()->subMonth()->month); + break; + } + + $revenue = $query->sum('total_amount'); + $previousRevenue = $previousQuery->sum('total_amount'); + + // Estimate profit as 30% of revenue (you can adjust this based on your business model) + $estimatedProfitMargin = 0.30; + $profit = $revenue * $estimatedProfitMargin; + $previousProfit = $previousRevenue * $estimatedProfitMargin; + $cost = $revenue - $profit; + + $profitMarginPercentage = $revenue > 0 ? ($profit / $revenue) * 100 : 0; + $percentageChange = $previousProfit > 0 + ? (($profit - $previousProfit) / $previousProfit) * 100 + : ($profit > 0 ? 100 : 0); + + return [ + 'profit' => (float) $profit, + 'revenue' => (float) $revenue, + 'cost' => (float) $cost, + 'profit_margin' => round($profitMarginPercentage, 2), + 'percentage_change' => round($percentageChange, 2), + 'trend' => $percentageChange >= 0 ? 'up' : 'down' + ]; + } + + /** + * Get aggregated data for different time periods + */ + public function getAggregatedData(string $period, Carbon $date = null): array + { + $date = $date ?? Carbon::now(); + + switch ($period) { + case 'daily': + return $this->getDailyHourlyData($date); + case 'weekly': + return $this->getWeeklyDailyData($date); + case 'monthly': + return $this->getMonthlyDailyData($date); + default: + return []; + } + } + + /** + * Get hourly data for a specific day using real Order data + */ + private function getDailyHourlyData(Carbon $date): array + { + $hourlyData = []; + + for ($hour = 0; $hour < 24; $hour++) { + $startTime = $date->copy()->hour($hour)->minute(0)->second(0); + $endTime = $startTime->copy()->addHour(); + + $orders = Order::where('status', '!=', 'cancelled') + ->whereBetween('created_at', [$startTime, $endTime]) + ->get(); + + $revenue = $orders->sum('total_amount'); + $profit = $revenue * 0.30; // 30% profit margin estimate + $ordersCount = $orders->count(); + $customersCount = $orders->pluck('customer_name')->unique()->count(); + + $hourlyData[] = [ + 'hour' => $hour, + 'revenue' => (float) $revenue, + 'profit' => (float) $profit, + 'orders' => $ordersCount, + 'customers' => $customersCount, + ]; + } + + return $hourlyData; + } + + /** + * Get daily data for a week using real Order data + */ + private function getWeeklyDailyData(Carbon $date): array + { + $startOfWeek = $date->copy()->startOfWeek(); + $dailyData = []; + + for ($day = 0; $day < 7; $day++) { + $currentDate = $startOfWeek->copy()->addDays($day); + + $orders = Order::where('status', '!=', 'cancelled') + ->whereDate('created_at', $currentDate) + ->get(); + + $revenue = $orders->sum('total_amount'); + $profit = $revenue * 0.30; + $ordersCount = $orders->count(); + $customersCount = $orders->pluck('customer_name')->unique()->count(); + + $dailyData[] = [ + 'date' => $currentDate->format('Y-m-d'), + 'day_name' => $currentDate->format('l'), + 'revenue' => (float) $revenue, + 'profit' => (float) $profit, + 'orders' => $ordersCount, + 'customers' => $customersCount, + ]; + } + + return $dailyData; + } + + /** + * Get daily data for a month using real Order data + */ + private function getMonthlyDailyData(Carbon $date): array + { + $daysInMonth = $date->daysInMonth; + $dailyData = []; + + for ($day = 1; $day <= $daysInMonth; $day++) { + $currentDate = $date->copy()->day($day); + + $orders = Order::where('status', '!=', 'cancelled') + ->whereDate('created_at', $currentDate) + ->get(); + + $revenue = $orders->sum('total_amount'); + $profit = $revenue * 0.30; + $ordersCount = $orders->count(); + $customersCount = $orders->pluck('customer_name')->unique()->count(); + + $dailyData[] = [ + 'date' => $currentDate->format('Y-m-d'), + 'day' => $day, + 'revenue' => (float) $revenue, + 'profit' => (float) $profit, + 'orders' => $ordersCount, + 'customers' => $customersCount, + ]; + } + + return $dailyData; + } + + /** + * Update analytics snapshots (called by scheduled job) + */ + public function updateAnalyticsSnapshots(): void + { + $this->updateHourlySnapshots(); + $this->updateDailySnapshots(); + } + + /** + * Update hourly snapshots for today + */ + private function updateHourlySnapshots(): void + { + $today = Carbon::today(); + $currentHour = Carbon::now()->hour; + + for ($hour = 0; $hour <= $currentHour; $hour++) { + $startTime = $today->copy()->hour($hour); + $endTime = $startTime->copy()->addHour(); + + $revenue = RevenueTracking::whereBetween('created_at', [$startTime, $endTime])->sum('amount'); + $cost = RevenueTracking::whereBetween('created_at', [$startTime, $endTime])->sum('cost'); + $profit = $revenue - $cost; + $ordersCount = Order::whereBetween('created_at', [$startTime, $endTime])->count(); + $customersCount = Order::whereBetween('created_at', [$startTime, $endTime]) + ->distinct('user_id')->count('user_id'); + + AnalyticsSnapshot::updateOrCreate( + ['date' => $today->format('Y-m-d'), 'hour' => $hour], + [ + 'revenue' => $revenue, + 'profit' => $profit, + 'orders_count' => $ordersCount, + 'customers_count' => $customersCount, + 'avg_order_value' => $ordersCount > 0 ? $revenue / $ordersCount : 0, + 'conversion_rate' => 0 // Will be calculated separately + ] + ); + } + } + + /** + * Update daily snapshots + */ + private function updateDailySnapshots(): void + { + $yesterday = Carbon::yesterday(); + + $revenue = RevenueTracking::whereDate('created_at', $yesterday)->sum('amount'); + $cost = RevenueTracking::whereDate('created_at', $yesterday)->sum('cost'); + $profit = $revenue - $cost; + $ordersCount = Order::whereDate('created_at', $yesterday)->count(); + $customersCount = Order::whereDate('created_at', $yesterday) + ->distinct('user_id')->count('user_id'); + + AnalyticsSnapshot::updateOrCreate( + ['date' => $yesterday->format('Y-m-d'), 'hour' => null], + [ + 'revenue' => $revenue, + 'profit' => $profit, + 'orders_count' => $ordersCount, + 'customers_count' => $customersCount, + 'avg_order_value' => $ordersCount > 0 ? $revenue / $ordersCount : 0, + 'conversion_rate' => 0 // Will be calculated separately + ] + ); + } + + /** + * Get top selling products using real Product and Order data + */ + public function getTopProducts(int $limit = 10): array + { + // Get all products with their sales data + $products = Product::select('id', 'name', 'price', 'Amount') + ->get() + ->map(function ($product) { + // Calculate estimated sales based on stock reduction + // This is a simple estimation - you might want to implement proper sales tracking + $estimatedSold = max(0, 100 - $product->Amount); // Assuming initial stock was 100 + $totalRevenue = $estimatedSold * $product->price; + + return [ + 'product_name' => $product->name, + 'total_quantity' => $estimatedSold, + 'total_revenue' => (float) $totalRevenue, + 'current_stock' => $product->Amount, + 'price' => (float) $product->price + ]; + }) + ->sortByDesc('total_revenue') + ->take($limit) + ->values() + ->toArray(); + + return $products; + } + + /** + * Get customer analytics using real User and Order data + */ + public function getCustomerAnalytics(): array + { + $totalCustomers = User::count(); + $newCustomersToday = User::whereDate('created_at', Carbon::today())->count(); + + // Count unique customers who made orders today + $customersWithOrdersToday = Order::whereDate('created_at', Carbon::today()) + ->distinct('customer_name') + ->count('customer_name'); + + // Total orders today + $ordersToday = Order::whereDate('created_at', Carbon::today())->count(); + + return [ + 'total_customers' => $totalCustomers, + 'new_today' => $newCustomersToday, + 'active_today' => $customersWithOrdersToday, + 'orders_today' => $ordersToday, + 'new_percentage' => $totalCustomers > 0 ? ($newCustomersToday / $totalCustomers) * 100 : 0, + 'conversion_rate' => $customersWithOrdersToday > 0 ? ($ordersToday / $customersWithOrdersToday) * 100 : 0 + ]; + } + + /** + * Get real-time order statistics + */ + public function getOrderStats(): array + { + $today = Carbon::today(); + $yesterday = Carbon::yesterday(); + + $todayOrders = Order::whereDate('created_at', $today)->count(); + $yesterdayOrders = Order::whereDate('created_at', $yesterday)->count(); + + $percentageChange = $yesterdayOrders > 0 + ? (($todayOrders - $yesterdayOrders) / $yesterdayOrders) * 100 + : ($todayOrders > 0 ? 100 : 0); + + return [ + 'current' => $todayOrders, + 'previous' => $yesterdayOrders, + 'percentage_change' => round($percentageChange, 2), + 'trend' => $percentageChange >= 0 ? 'up' : 'down', + 'total_orders' => Order::count(), + 'pending_orders' => Order::where('status', 'pending')->count(), + 'completed_orders' => Order::where('status', 'completed')->count() + ]; + } + + /** + * Get real-time product statistics + */ + public function getProductStats(): array + { + $totalProducts = Product::count(); + $lowStockProducts = Product::where('Amount', '<', 10)->count(); + $outOfStockProducts = Product::where('Amount', 0)->count(); + + return [ + 'total_products' => $totalProducts, + 'low_stock' => $lowStockProducts, + 'out_of_stock' => $outOfStockProducts, + 'in_stock' => $totalProducts - $outOfStockProducts, + 'average_price' => (float) Product::avg('price') + ]; + } +} \ No newline at end of file diff --git a/app/View/Components/AppLayout.php b/app/View/Components/AppLayout.php new file mode 100644 index 0000000000000000000000000000000000000000..de0d46f58d545f9ac069c08320d22d0550913986 --- /dev/null +++ b/app/View/Components/AppLayout.php @@ -0,0 +1,17 @@ +handleCommand(new ArgvInput); + +exit($status); diff --git a/bootstrap/app.php b/bootstrap/app.php new file mode 100644 index 0000000000000000000000000000000000000000..d6542762ade805a9eaefca56c42d73bee33596f5 --- /dev/null +++ b/bootstrap/app.php @@ -0,0 +1,19 @@ +withRouting( + web: __DIR__.'/../routes/web.php', + api: __DIR__.'/../routes/api.php', + commands: __DIR__.'/../routes/console.php', + health: '/up', + ) + ->withMiddleware(function (Middleware $middleware) { + // + }) + ->withExceptions(function (Exceptions $exceptions) { + // + })->create(); diff --git a/bootstrap/providers.php b/bootstrap/providers.php new file mode 100644 index 0000000000000000000000000000000000000000..38b258d1855b5e767bde3df168106f468088472f --- /dev/null +++ b/bootstrap/providers.php @@ -0,0 +1,5 @@ +=5.0.0" + }, + "require-dev": { + "doctrine/dbal": "^4.0.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2024-02-09T16:56:22+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" + }, + "time": "2024-07-08T12:26:09+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.0.10", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^11.0", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.10" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2024-02-18T20:23:39+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "8c784d071debd117328803d86b2097615b457500" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", + "reference": "8c784d071debd117328803d86b2097615b457500", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2024-10-09T13:47:03+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2025-03-06T22:45:56+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "symfony/http-foundation": "^4.4|^5.4|^6|^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barryvdh", + "email": "barryvdh@gmail.com" + } + ], + "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", + "homepage": "https://github.com/fruitcake/php-cors", + "keywords": [ + "cors", + "laravel", + "symfony" + ], + "support": { + "issues": "https://github.com/fruitcake/php-cors/issues", + "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2023-10-12T05:21:21+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:45:45+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.9.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.9.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2025-03-27T13:37:11+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.2.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2025-03-27T13:27:01+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.7.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.7.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2025-03-27T12:30:47+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "30e286560c137526eccd4ce21b2de477ab0676d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/30e286560c137526eccd4ce21b2de477ab0676d2", + "reference": "30e286560c137526eccd4ce21b2de477ab0676d2", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "uri-template/tests": "1.0.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\UriTemplate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "A polyfill class for uri_template of PHP", + "keywords": [ + "guzzlehttp", + "uri-template" + ], + "support": { + "issues": "https://github.com/guzzle/uri-template/issues", + "source": "https://github.com/guzzle/uri-template/tree/v1.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", + "type": "tidelift" + } + ], + "time": "2025-02-03T10:55:03+00:00" + }, + { + "name": "laravel/framework", + "version": "v12.9.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "7c201749cc4b463eea1b58698f5fb4f4c09dc32b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/7c201749cc4b463eea1b58698f5fb4f4c09dc32b", + "reference": "7c201749cc4b463eea1b58698f5fb4f4c09dc32b", + "shasum": "" + }, + "require": { + "brick/math": "^0.11|^0.12", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.4", + "egulias/email-validator": "^3.2.1|^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.3", + "guzzlehttp/guzzle": "^7.8.2", + "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.3.0", + "laravel/serializable-closure": "^1.3|^2.0", + "league/commonmark": "^2.6", + "league/flysystem": "^3.25.1", + "league/flysystem-local": "^3.25.1", + "league/uri": "^7.5.1", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^3.8.4", + "nunomaduro/termwind": "^2.0", + "php": "^8.2", + "psr/container": "^1.1.1|^2.0.1", + "psr/log": "^1.0|^2.0|^3.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "ramsey/uuid": "^4.7", + "symfony/console": "^7.2.0", + "symfony/error-handler": "^7.2.0", + "symfony/finder": "^7.2.0", + "symfony/http-foundation": "^7.2.0", + "symfony/http-kernel": "^7.2.0", + "symfony/mailer": "^7.2.0", + "symfony/mime": "^7.2.0", + "symfony/polyfill-php83": "^1.31", + "symfony/process": "^7.2.0", + "symfony/routing": "^7.2.0", + "symfony/uid": "^7.2.0", + "symfony/var-dumper": "^7.2.0", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", + "vlucas/phpdotenv": "^5.6.1", + "voku/portable-ascii": "^2.0.2" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "psr/log-implementation": "1.0|2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0" + }, + "replace": { + "illuminate/auth": "self.version", + "illuminate/broadcasting": "self.version", + "illuminate/bus": "self.version", + "illuminate/cache": "self.version", + "illuminate/collections": "self.version", + "illuminate/concurrency": "self.version", + "illuminate/conditionable": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/contracts": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/filesystem": "self.version", + "illuminate/hashing": "self.version", + "illuminate/http": "self.version", + "illuminate/log": "self.version", + "illuminate/macroable": "self.version", + "illuminate/mail": "self.version", + "illuminate/notifications": "self.version", + "illuminate/pagination": "self.version", + "illuminate/pipeline": "self.version", + "illuminate/process": "self.version", + "illuminate/queue": "self.version", + "illuminate/redis": "self.version", + "illuminate/routing": "self.version", + "illuminate/session": "self.version", + "illuminate/support": "self.version", + "illuminate/testing": "self.version", + "illuminate/translation": "self.version", + "illuminate/validation": "self.version", + "illuminate/view": "self.version", + "spatie/once": "*" + }, + "require-dev": { + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.322.9", + "ext-gmp": "*", + "fakerphp/faker": "^1.24", + "guzzlehttp/promises": "^2.0.3", + "guzzlehttp/psr7": "^2.4", + "laravel/pint": "^1.18", + "league/flysystem-aws-s3-v3": "^3.25.1", + "league/flysystem-ftp": "^3.25.1", + "league/flysystem-path-prefixing": "^3.25.1", + "league/flysystem-read-only": "^3.25.1", + "league/flysystem-sftp-v3": "^3.25.1", + "mockery/mockery": "^1.6.10", + "orchestra/testbench-core": "^10.0.0", + "pda/pheanstalk": "^5.0.6|^7.0.0", + "php-http/discovery": "^1.15", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1", + "predis/predis": "^2.3", + "resend/resend-php": "^0.10.0", + "symfony/cache": "^7.2.0", + "symfony/http-client": "^7.2.0", + "symfony/psr-http-message-bridge": "^7.2.0", + "symfony/translation": "^7.2.0" + }, + "suggest": { + "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).", + "brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).", + "ext-apcu": "Required to use the APC cache driver.", + "ext-fileinfo": "Required to use the Filesystem class.", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "ext-memcached": "Required to use the memcache cache driver.", + "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", + "ext-pdo": "Required to use all database features.", + "ext-posix": "Required to use all features of the queue worker.", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).", + "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "filp/whoops": "Required for friendly error pages in development (^2.14.3).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).", + "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", + "mockery/mockery": "Required to use mocking (^1.6).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", + "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", + "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).", + "predis/predis": "Required to use the predis connector (^2.3).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^7.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Collections/functions.php", + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", + "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Log/functions.php", + "src/Illuminate/Support/functions.php", + "src/Illuminate/Support/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/", + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Laravel Framework.", + "homepage": "https://laravel.com", + "keywords": [ + "framework", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-04-16T12:02:03+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.3.5", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "57b8f7efe40333cdb925700891c7d7465325d3b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/57b8f7efe40333cdb925700891c7d7465325d3b1", + "reference": "57b8f7efe40333cdb925700891c7d7465325d3b1", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "ext-mbstring": "*", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "illuminate/collections": "^10.0|^11.0|^12.0", + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3|^3.4", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.3.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.3.5" + }, + "time": "2025-02-11T13:34:40+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v2.0.4", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b352cf0534aa1ae6b4d825d1e762e35d43f8a841", + "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0|^11.0|^12.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2025-03-19T13:51:03+00:00" + }, + { + "name": "laravel/tinker", + "version": "v2.10.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/22177cc71807d38f2810c6204d8f7183d88a57d3", + "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "php": "^7.2.5|^8.0", + "psy/psysh": "^0.11.1|^0.12.0", + "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.8|^9.3.3|^10.0" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0)." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v2.10.1" + }, + "time": "2025-01-27T14:24:01+00:00" + }, + { + "name": "league/commonmark", + "version": "2.6.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "d990688c91cedfb69753ffc2512727ec646df2ad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d990688c91cedfb69753ffc2512727ec646df2ad", + "reference": "d990688c91cedfb69753ffc2512727ec646df2ad", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.31.1", + "commonmark/commonmark.js": "0.31.1", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4 || ^2.0", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0 | ^7.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2024-12-29T14:10:59+00:00" + }, + { + "name": "league/config", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2022-12-11T20:36:23+00:00" + }, + { + "name": "league/flysystem", + "version": "3.29.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/edc1bb7c86fab0776c3287dbd19b5fa278347319", + "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319", + "shasum": "" + }, + "require": { + "league/flysystem-local": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "phpseclib/phpseclib": "3.0.15", + "symfony/http-client": "<5.2" + }, + "require-dev": { + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", + "aws/aws-sdk-php": "^3.295.10", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-mongodb": "^1.3", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "guzzlehttp/psr7": "^2.6", + "microsoft/azure-storage-blob": "^1.1", + "mongodb/mongodb": "^1.2", + "phpseclib/phpseclib": "^3.0.36", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", + "sabre/dav": "^4.6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "File storage abstraction for PHP", + "keywords": [ + "WebDAV", + "aws", + "cloud", + "file", + "files", + "filesystem", + "filesystems", + "ftp", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/3.29.1" + }, + "time": "2024-10-08T08:58:34+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.29.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/e0e8d52ce4b2ed154148453d321e97c8e931bd27", + "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Local\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-local/tree/3.29.0" + }, + "time": "2024-08-09T21:24:39+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.16.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2024-09-21T08:32:55+00:00" + }, + { + "name": "league/uri", + "version": "7.5.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "81fb5145d2644324614cc532b28efd0215bda430" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", + "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.5", + "php": "^8.1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.5.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:40:02+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.5.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-factory": "^1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common interfaces and classes for URI representation and interaction", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:18:47+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.9.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.9.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2025-03-24T10:02:05+00:00" + }, + { + "name": "nesbot/carbon", + "version": "3.9.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "6d16a8a015166fe54e22c042e0805c5363aef50d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/6d16a8a015166fe54e22c042e0805c5363aef50d", + "reference": "6d16a8a015166fe54e22c042e0805c5363aef50d", + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "<100.0", + "ext-json": "*", + "php": "^8.1", + "psr/clock": "^1.0", + "symfony/clock": "^6.3 || ^7.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation": "^4.4.18 || ^5.2.1|| ^6.0 || ^7.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^3.6.3 || ^4.0", + "doctrine/orm": "^2.15.2 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.57.2", + "kylekatarnls/multi-tester": "^2.5.3", + "ondrejmirtes/better-reflection": "^6.25.0.4", + "phpmd/phpmd": "^2.15.0", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan": "^1.11.2", + "phpunit/phpunit": "^10.5.20", + "squizlabs/php_codesniffer": "^3.9.0" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/CarbonPHP/carbon/issues", + "source": "https://github.com/CarbonPHP/carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2025-03-27T12:57:33+00:00" + }, + { + "name": "nette/schema", + "version": "v1.3.2", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d", + "shasum": "" + }, + "require": { + "nette/utils": "^4.0", + "php": "8.1 - 8.4" + }, + "require-dev": { + "nette/tester": "^2.5.2", + "phpstan/phpstan-nette": "^1.0", + "tracy/tracy": "^2.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.3.2" + }, + "time": "2024-10-06T23:10:23+00:00" + }, + { + "name": "nette/utils", + "version": "v4.0.6", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "ce708655043c7050eb050df361c5e313cf708309" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/ce708655043c7050eb050df361c5e313cf708309", + "reference": "ce708655043c7050eb050df361c5e313cf708309", + "shasum": "" + }, + "require": { + "php": "8.0 - 8.4" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "dev-master", + "nette/tester": "^2.5", + "phpstan/phpstan": "^1.0", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.0.6" + }, + "time": "2025-03-30T21:06:30+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" + }, + "time": "2024-12-30T11:07:19+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/52915afe6a1044e8b9cee1bcff836fb63acf9cda", + "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.1.8" + }, + "require-dev": { + "illuminate/console": "^11.33.2", + "laravel/pint": "^1.18.2", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0", + "phpstan/phpstan": "^1.12.11", + "phpstan/phpstan-strict-rules": "^1.6.1", + "symfony/var-dumper": "^7.1.8", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Its like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2024-11-21T10:39:51+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.3", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:41:07+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "psy/psysh", + "version": "v0.12.8", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "85057ceedee50c49d4f6ecaff73ee96adb3b3625" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/85057ceedee50c49d4f6ecaff73ee96adb3b3625", + "reference": "85057ceedee50c49d4f6ecaff73ee96adb3b3625", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^5.0 || ^4.0", + "php": "^8.0 || ^7.4", + "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2" + }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-main": "0.12.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "http://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.12.8" + }, + "time": "2025-03-16T03:05:19+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.1.1" + }, + "time": "2025-03-22T05:38:12+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.7.6", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", + "ext-json": "*", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.10", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.8", + "ergebnis/composer-normalize": "^2.15", + "mockery/mockery": "^1.3", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.2", + "php-mock/php-mock-mockery": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^8.5 || ^9", + "ramsey/composer-repl": "^1.4", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.9" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.7.6" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" + } + ], + "time": "2024-04-27T21:32:50+00:00" + }, + { + "name": "symfony/clock", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/clock": "^1.0", + "symfony/polyfill-php83": "^1.28" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/console", + "version": "v7.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "e51498ea18570c062e7df29d05a7003585b19b88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/e51498ea18570c062e7df29d05a7003585b19b88", + "reference": "e51498ea18570c062e7df29d05a7003585b19b88", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-03-12T08:11:12+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v7.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "102be5e6a8e4f4f3eb3149bcbfa33a80d1ee374b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/102be5e6a8e4f4f3eb3149bcbfa33a80d1ee374b", + "reference": "102be5e6a8e4f4f3eb3149bcbfa33a80d1ee374b", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^6.4|^7.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v7.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-03-03T07:12:39+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/910c5db85a5356d0fea57680defec4e99eb9c8c1", + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.2.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "87a71856f2f56e4100373e92529eed3171695cfb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/87a71856f2f56e4100373e92529eed3171695cfb", + "reference": "87a71856f2f56e4100373e92529eed3171695cfb", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.2.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-30T19:00:17+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v7.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "371272aeb6286f8135e028ca535f8e4d6f114126" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/371272aeb6286f8135e028ca535f8e4d6f114126", + "reference": "371272aeb6286f8135e028ca535f8e4d6f114126", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + }, + "require-dev": { + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4.12|^7.1.5", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v7.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-03-25T15:54:33+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v7.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "b1fe91bc1fa454a806d3f98db4ba826eb9941a54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b1fe91bc1fa454a806d3f98db4ba826eb9941a54", + "reference": "b1fe91bc1fa454a806d3f98db4ba826eb9941a54", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<6.4", + "symfony/cache": "<6.4", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<6.4", + "symfony/form": "<6.4", + "symfony/http-client": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4", + "symfony/translation": "<6.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<6.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.4", + "twig/twig": "<3.12" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^6.4|^7.0", + "symfony/clock": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/css-selector": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^7.1", + "symfony/routing": "^6.4|^7.0", + "symfony/serializer": "^7.1", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0", + "symfony/var-exporter": "^6.4|^7.0", + "twig/twig": "^3.12" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v7.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-03-28T13:32:50+00:00" + }, + { + "name": "symfony/mailer", + "version": "v7.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "f3871b182c44997cf039f3b462af4a48fb85f9d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/f3871b182c44997cf039f3b462af4a48fb85f9d3", + "reference": "f3871b182c44997cf039f3b462af4a48fb85f9d3", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.2", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/mime": "^7.2", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/twig-bridge": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v7.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-27T11:08:17+00:00" + }, + { + "name": "symfony/mime", + "version": "v7.2.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "87ca22046b78c3feaff04b337f33b38510fd686b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/87ca22046b78c3feaff04b337f33b38510fd686b", + "reference": "87ca22046b78c3feaff04b337f33b38510fd686b", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<6.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v7.2.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-02-19T08:51:20+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/process", + "version": "v7.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "87b7c93e57df9d8e39a093d32587702380ff045d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/87b7c93e57df9d8e39a093d32587702380ff045d", + "reference": "87b7c93e57df9d8e39a093d32587702380ff045d", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-03-13T12:21:46+00:00" + }, + { + "name": "symfony/routing", + "version": "v7.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "ee9a67edc6baa33e5fae662f94f91fd262930996" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/ee9a67edc6baa33e5fae662f94f91fd262930996", + "reference": "ee9a67edc6baa33e5fae662f94f91fd262930996", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v7.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-17T10:56:55+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/string", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-13T13:31:26+00:00" + }, + { + "name": "symfony/translation", + "version": "v7.2.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "283856e6981286cc0d800b53bd5703e8e363f05a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/283856e6981286cc0d800b53bd5703e8e363f05a", + "reference": "283856e6981286cc0d800b53bd5703e8e363f05a", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<6.4", + "symfony/yaml": "<6.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.18|^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v7.2.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-02-13T10:27:23+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c", + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/uid", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "2d294d0c48df244c71c105a169d0190bfb080426" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/2d294d0c48df244c71c105a169d0190bfb080426", + "reference": "2d294d0c48df244c71c105a169d0190bfb080426", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v7.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "82b478c69745d8878eb60f9a049a4d584996f73a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/82b478c69745d8878eb60f9a049a4d584996f73a", + "reference": "82b478c69745d8878eb60f9a049a4d584996f73a", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "twig/twig": "^3.12" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v7.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-17T11:39:41+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "0d72ac1c00084279c1816675284073c5a337c20d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0d72ac1c00084279c1816675284073c5a337c20d", + "reference": "0d72ac1c00084279c1816675284073c5a337c20d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^7.4 || ^8.0", + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^8.5.21 || ^9.5.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", + "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "support": { + "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.3.0" + }, + "time": "2024-12-21T16:25:41+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.6.1", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.3", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-filter": "*", + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "5.6-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:52:34+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "https://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2024-11-21T01:49:47+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "packages-dev": [ + { + "name": "fakerphp/faker", + "version": "v1.24.1", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" + }, + "time": "2024-11-21T13:46:39+00:00" + }, + { + "name": "filp/whoops", + "version": "2.18.0", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e", + "reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.18.0" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-03-15T12:00:00+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "shasum": "" + }, + "require": { + "php": "^5.3|^7.0|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, + "time": "2020-07-09T08:09:16+00:00" + }, + { + "name": "laravel/breeze", + "version": "v2.3.6", + "source": { + "type": "git", + "url": "https://github.com/laravel/breeze.git", + "reference": "390cbc433cb72fa6050965000b2d56c9ba6fd713" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/breeze/zipball/390cbc433cb72fa6050965000b2d56c9ba6fd713", + "reference": "390cbc433cb72fa6050965000b2d56c9ba6fd713", + "shasum": "" + }, + "require": { + "illuminate/console": "^11.0|^12.0", + "illuminate/filesystem": "^11.0|^12.0", + "illuminate/support": "^11.0|^12.0", + "illuminate/validation": "^11.0|^12.0", + "php": "^8.2.0", + "symfony/console": "^7.0" + }, + "require-dev": { + "laravel/framework": "^11.0|^12.0", + "orchestra/testbench-core": "^9.0|^10.0", + "phpstan/phpstan": "^2.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Breeze\\BreezeServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Breeze\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Minimal Laravel authentication scaffolding with Blade and Tailwind.", + "keywords": [ + "auth", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/breeze/issues", + "source": "https://github.com/laravel/breeze" + }, + "time": "2025-03-06T14:02:32+00:00" + }, + { + "name": "laravel/pail", + "version": "v1.2.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/pail.git", + "reference": "f31f4980f52be17c4667f3eafe034e6826787db2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pail/zipball/f31f4980f52be17c4667f3eafe034e6826787db2", + "reference": "f31f4980f52be17c4667f3eafe034e6826787db2", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/console": "^10.24|^11.0|^12.0", + "illuminate/contracts": "^10.24|^11.0|^12.0", + "illuminate/log": "^10.24|^11.0|^12.0", + "illuminate/process": "^10.24|^11.0|^12.0", + "illuminate/support": "^10.24|^11.0|^12.0", + "nunomaduro/termwind": "^1.15|^2.0", + "php": "^8.2", + "symfony/console": "^6.0|^7.0" + }, + "require-dev": { + "laravel/framework": "^10.24|^11.0|^12.0", + "laravel/pint": "^1.13", + "orchestra/testbench-core": "^8.13|^9.0|^10.0", + "pestphp/pest": "^2.20|^3.0", + "pestphp/pest-plugin-type-coverage": "^2.3|^3.0", + "phpstan/phpstan": "^1.10", + "symfony/var-dumper": "^6.3|^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Pail\\PailServiceProvider" + ] + }, + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Pail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Easily delve into your Laravel application's log files directly from the command line.", + "homepage": "https://github.com/laravel/pail", + "keywords": [ + "laravel", + "logs", + "php", + "tail" + ], + "support": { + "issues": "https://github.com/laravel/pail/issues", + "source": "https://github.com/laravel/pail" + }, + "time": "2025-01-28T15:15:15+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.22.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "7ddfaa6523a675fae5c4123ee38fc6bfb8ee4f36" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/7ddfaa6523a675fae5c4123ee38fc6bfb8ee4f36", + "reference": "7ddfaa6523a675fae5c4123ee38fc6bfb8ee4f36", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.2.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.75.0", + "illuminate/view": "^11.44.2", + "larastan/larastan": "^3.3.1", + "laravel-zero/framework": "^11.36.1", + "mockery/mockery": "^1.6.12", + "nunomaduro/termwind": "^2.3", + "pestphp/pest": "^2.36.0" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2025-04-08T22:11:45+00:00" + }, + { + "name": "laravel/sail", + "version": "v1.41.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/sail.git", + "reference": "fe1a4ada0abb5e4bd99eb4e4b0d87906c00cdeec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sail/zipball/fe1a4ada0abb5e4bd99eb4e4b0d87906c00cdeec", + "reference": "fe1a4ada0abb5e4bd99eb4e4b0d87906c00cdeec", + "shasum": "" + }, + "require": { + "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0", + "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0", + "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0", + "php": "^8.0", + "symfony/console": "^6.0|^7.0", + "symfony/yaml": "^6.0|^7.0" + }, + "require-dev": { + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", + "phpstan/phpstan": "^1.10" + }, + "bin": [ + "bin/sail" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sail\\SailServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Docker files for running a basic Laravel application.", + "keywords": [ + "docker", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/sail/issues", + "source": "https://github.com/laravel/sail" + }, + "time": "2025-01-24T15:45:36+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "024473a478be9df5fdaca2c793f2232fe788e414" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", + "reference": "024473a478be9df5fdaca2c793f2232fe788e414", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-02-12T12:17:51+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v8.8.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/4cf9f3b47afff38b139fb79ce54fc71799022ce8", + "reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.18.0", + "nunomaduro/termwind": "^2.3.0", + "php": "^8.2.0", + "symfony/console": "^7.2.5" + }, + "conflict": { + "laravel/framework": "<11.44.2 || >=13.0.0", + "phpunit/phpunit": "<11.5.15 || >=13.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.8.3", + "larastan/larastan": "^3.2", + "laravel/framework": "^11.44.2 || ^12.6", + "laravel/pint": "^1.21.2", + "laravel/sail": "^1.41.0", + "laravel/sanctum": "^4.0.8", + "laravel/tinker": "^2.10.1", + "orchestra/testbench-core": "^9.12.0 || ^10.1", + "pestphp/pest": "^3.8.0", + "sebastian/environment": "^7.2.0 || ^8.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + }, + "branch-alias": { + "dev-8.x": "8.x-dev" + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "dev", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2025-04-03T14:33:09+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "11.0.9", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "14d63fbcca18457e49c6f8bebaa91a87e8e188d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/14d63fbcca18457e49c6f8bebaa91a87e8e188d7", + "reference": "14d63fbcca18457e49c6f8bebaa91a87e8e188d7", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.4.0", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.0", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^11.5.2" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.9" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-25T13:26:39+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-27T05:02:59+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:07:44+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:08:43+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:09:35+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "11.5.17", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "fd2e863a2995cdfd864fb514b5e0b28b09895b5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fd2e863a2995cdfd864fb514b5e0b28b09895b5c", + "reference": "fd2e863a2995cdfd864fb514b5e0b28b09895b5c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.9", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.3", + "sebastian/comparator": "^6.3.1", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.0", + "sebastian/exporter": "^6.3.0", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/type": "^5.1.2", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.17" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2025-04-08T07:59:11+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:41:36+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-03-19T07:56:08+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:45:54+00:00" + }, + { + "name": "sebastian/comparator", + "version": "6.3.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "24b8fbc2c8e201bb1308e7b05148d6ab393b6959" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/24b8fbc2c8e201bb1308e7b05148d6ab393b6959", + "reference": "24b8fbc2c8e201bb1308e7b05148d6ab393b6959", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.4" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-03-07T06:57:01+00:00" + }, + { + "name": "sebastian/complexity", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:49:50+00:00" + }, + { + "name": "sebastian/diff", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:53:05+00:00" + }, + { + "name": "sebastian/environment", + "version": "7.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", + "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:54:44+00:00" + }, + { + "name": "sebastian/exporter", + "version": "6.3.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3", + "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-12-05T09:17:50+00:00" + }, + { + "name": "sebastian/global-state", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:57:36+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:58:38+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:00:13+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:01:32+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "694d156164372abbd149a4b85ccda2e4670c0e16" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16", + "reference": "694d156164372abbd149a4b85ccda2e4670c0e16", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:10:34+00:00" + }, + { + "name": "sebastian/type", + "version": "5.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "a8a7e30534b0eb0c77cd9d07e82de1a114389f5e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/a8a7e30534b0eb0c77cd9d07e82de1a114389f5e", + "reference": "a8a7e30534b0eb0c77cd9d07e82de1a114389f5e", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-03-18T13:35:50+00:00" + }, + { + "name": "sebastian/version", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-09T05:16:32+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "symfony/yaml", + "version": "v7.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912", + "reference": "4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-03-03T07:12:39+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": "^8.2" + }, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/config/app.php b/config/app.php new file mode 100644 index 0000000000000000000000000000000000000000..324b513a2733ba90cd9d0ab8990f4a6de633f58e --- /dev/null +++ b/config/app.php @@ -0,0 +1,126 @@ + env('APP_NAME', 'Laravel'), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services the application utilizes. Set this in your ".env" file. + | + */ + + 'env' => env('APP_ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Application Debug Mode + |-------------------------------------------------------------------------- + | + | When your application is in debug mode, detailed error messages with + | stack traces will be shown on every error that occurs within your + | application. If disabled, a simple generic error page is shown. + | + */ + + 'debug' => (bool) env('APP_DEBUG', false), + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | the application so that it's available within Artisan commands. + | + */ + + 'url' => env('APP_URL', 'http://localhost'), + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. The timezone + | is set to "UTC" by default as it is suitable for most use cases. + | + */ + + 'timezone' => 'UTC', + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by Laravel's translation / localization methods. This option can be + | set to any locale for which you plan to have translation strings. + | + */ + + 'locale' => env('APP_LOCALE', 'en'), + + 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), + + 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is utilized by Laravel's encryption services and should be set + | to a random, 32 character string to ensure that all encrypted values + | are secure. You should do this prior to deploying the application. + | + */ + + 'cipher' => 'AES-256-CBC', + + 'key' => env('APP_KEY'), + + 'previous_keys' => [ + ...array_filter( + explode(',', env('APP_PREVIOUS_KEYS', '')) + ), + ], + + /* + |-------------------------------------------------------------------------- + | Maintenance Mode Driver + |-------------------------------------------------------------------------- + | + | These configuration options determine the driver used to determine and + | manage Laravel's "maintenance mode" status. The "cache" driver will + | allow maintenance mode to be controlled across multiple machines. + | + | Supported drivers: "file", "cache" + | + */ + + 'maintenance' => [ + 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), + 'store' => env('APP_MAINTENANCE_STORE', 'database'), + ], + +]; diff --git a/config/auth.php b/config/auth.php new file mode 100644 index 0000000000000000000000000000000000000000..0ba5d5d8f10c959d538e1e6ae87aa271d41e1ff0 --- /dev/null +++ b/config/auth.php @@ -0,0 +1,115 @@ + [ + 'guard' => env('AUTH_GUARD', 'web'), + 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), + ], + + /* + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | which utilizes session storage plus the Eloquent user provider. + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | Supported: "session" + | + */ + + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + ], + ], + + /* + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | If you have multiple user tables or models you may configure multiple + | providers to represent the model / table. These providers may then + | be assigned to any extra authentication guards you have defined. + | + | Supported: "database", "eloquent" + | + */ + + 'providers' => [ + 'users' => [ + 'driver' => 'eloquent', + 'model' => env('AUTH_MODEL', App\Models\User::class), + ], + + // 'users' => [ + // 'driver' => 'database', + // 'table' => 'users', + // ], + ], + + /* + |-------------------------------------------------------------------------- + | Resetting Passwords + |-------------------------------------------------------------------------- + | + | These configuration options specify the behavior of Laravel's password + | reset functionality, including the table utilized for token storage + | and the user provider that is invoked to actually retrieve users. + | + | The expiry time is the number of minutes that each reset token will be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + | The throttle setting is the number of seconds a user must wait before + | generating more password reset tokens. This prevents the user from + | quickly generating a very large amount of password reset tokens. + | + */ + + 'passwords' => [ + 'users' => [ + 'provider' => 'users', + 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), + 'expire' => 60, + 'throttle' => 60, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Password Confirmation Timeout + |-------------------------------------------------------------------------- + | + | Here you may define the amount of seconds before a password confirmation + | window expires and users are asked to re-enter their password via the + | confirmation screen. By default, the timeout lasts for three hours. + | + */ + + 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), + +]; diff --git a/config/cache.php b/config/cache.php new file mode 100644 index 0000000000000000000000000000000000000000..925f7d2ee84ba4435a6f512534598eb06a20148a --- /dev/null +++ b/config/cache.php @@ -0,0 +1,108 @@ + env('CACHE_STORE', 'database'), + + /* + |-------------------------------------------------------------------------- + | Cache Stores + |-------------------------------------------------------------------------- + | + | Here you may define all of the cache "stores" for your application as + | well as their drivers. You may even define multiple stores for the + | same cache driver to group types of items stored in your caches. + | + | Supported drivers: "array", "database", "file", "memcached", + | "redis", "dynamodb", "octane", "null" + | + */ + + 'stores' => [ + + 'array' => [ + 'driver' => 'array', + 'serialize' => false, + ], + + 'database' => [ + 'driver' => 'database', + 'connection' => env('DB_CACHE_CONNECTION'), + 'table' => env('DB_CACHE_TABLE', 'cache'), + 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), + 'lock_table' => env('DB_CACHE_LOCK_TABLE'), + ], + + 'file' => [ + 'driver' => 'file', + 'path' => storage_path('framework/cache/data'), + 'lock_path' => storage_path('framework/cache/data'), + ], + + 'memcached' => [ + 'driver' => 'memcached', + 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), + 'sasl' => [ + env('MEMCACHED_USERNAME'), + env('MEMCACHED_PASSWORD'), + ], + 'options' => [ + // Memcached::OPT_CONNECT_TIMEOUT => 2000, + ], + 'servers' => [ + [ + 'host' => env('MEMCACHED_HOST', '127.0.0.1'), + 'port' => env('MEMCACHED_PORT', 11211), + 'weight' => 100, + ], + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), + 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), + ], + + 'dynamodb' => [ + 'driver' => 'dynamodb', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), + 'endpoint' => env('DYNAMODB_ENDPOINT'), + ], + + 'octane' => [ + 'driver' => 'octane', + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing the APC, database, memcached, Redis, and DynamoDB cache + | stores, there might be other applications using the same cache. For + | that reason, you may prefix every cache key to avoid collisions. + | + */ + + 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'), + +]; diff --git a/config/database.php b/config/database.php new file mode 100644 index 0000000000000000000000000000000000000000..8910562d6143cfdc233835f2e6499b6512764039 --- /dev/null +++ b/config/database.php @@ -0,0 +1,174 @@ + env('DB_CONNECTION', 'sqlite'), + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Below are all of the database connections defined for your application. + | An example configuration is provided for each database system which + | is supported by Laravel. You're free to add / remove connections. + | + */ + + 'connections' => [ + + 'sqlite' => [ + 'driver' => 'sqlite', + 'url' => env('DB_URL'), + 'database' => env('DB_DATABASE', database_path('database.sqlite')), + 'prefix' => '', + 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), + 'busy_timeout' => null, + 'journal_mode' => null, + 'synchronous' => null, + ], + + 'mysql' => [ + 'driver' => 'mysql', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'mariadb' => [ + 'driver' => 'mariadb', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'pgsql' => [ + 'driver' => 'pgsql', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '5432'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => '', + 'prefix_indexes' => true, + 'search_path' => 'public', + 'sslmode' => 'prefer', + ], + + 'sqlsrv' => [ + 'driver' => 'sqlsrv', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT', '1433'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => '', + 'prefix_indexes' => true, + // 'encrypt' => env('DB_ENCRYPT', 'yes'), + // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'), + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run on the database. + | + */ + + 'migrations' => [ + 'table' => 'migrations', + 'update_date_on_publish' => true, + ], + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer body of commands than a typical key-value system + | such as Memcached. You may define your connection settings here. + | + */ + + 'redis' => [ + + 'client' => env('REDIS_CLIENT', 'phpredis'), + + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), + 'persistent' => env('REDIS_PERSISTENT', false), + ], + + 'default' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), + ], + + 'cache' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_CACHE_DB', '1'), + ], + + ], + +]; diff --git a/config/filesystems.php b/config/filesystems.php new file mode 100644 index 0000000000000000000000000000000000000000..3d671bd9105e0cd917fdc14d857fdac26f54d151 --- /dev/null +++ b/config/filesystems.php @@ -0,0 +1,80 @@ + env('FILESYSTEM_DISK', 'local'), + + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Below you may configure as many filesystem disks as necessary, and you + | may even configure multiple disks for the same driver. Examples for + | most supported storage drivers are configured here for reference. + | + | Supported drivers: "local", "ftp", "sftp", "s3" + | + */ + + 'disks' => [ + + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app/private'), + 'serve' => true, + 'throw' => false, + 'report' => false, + ], + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'public', + 'throw' => false, + 'report' => false, + ], + + 's3' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION'), + 'bucket' => env('AWS_BUCKET'), + 'url' => env('AWS_URL'), + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), + 'throw' => false, + 'report' => false, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Symbolic Links + |-------------------------------------------------------------------------- + | + | Here you may configure the symbolic links that will be created when the + | `storage:link` Artisan command is executed. The array keys should be + | the locations of the links and the values should be their targets. + | + */ + + 'links' => [ + public_path('storage') => storage_path('app/public'), + ], + +]; diff --git a/config/logging.php b/config/logging.php new file mode 100644 index 0000000000000000000000000000000000000000..1345f6f66c51db027bbe415c8e1004a0cd310331 --- /dev/null +++ b/config/logging.php @@ -0,0 +1,132 @@ + env('LOG_CHANNEL', 'stack'), + + /* + |-------------------------------------------------------------------------- + | Deprecations Log Channel + |-------------------------------------------------------------------------- + | + | This option controls the log channel that should be used to log warnings + | regarding deprecated PHP and library features. This allows you to get + | your application ready for upcoming major versions of dependencies. + | + */ + + 'deprecations' => [ + 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), + 'trace' => env('LOG_DEPRECATIONS_TRACE', false), + ], + + /* + |-------------------------------------------------------------------------- + | Log Channels + |-------------------------------------------------------------------------- + | + | Here you may configure the log channels for your application. Laravel + | utilizes the Monolog PHP logging library, which includes a variety + | of powerful log handlers and formatters that you're free to use. + | + | Available drivers: "single", "daily", "slack", "syslog", + | "errorlog", "monolog", "custom", "stack" + | + */ + + 'channels' => [ + + 'stack' => [ + 'driver' => 'stack', + 'channels' => explode(',', env('LOG_STACK', 'single')), + 'ignore_exceptions' => false, + ], + + 'single' => [ + 'driver' => 'single', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'daily' => [ + 'driver' => 'daily', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'days' => env('LOG_DAILY_DAYS', 14), + 'replace_placeholders' => true, + ], + + 'slack' => [ + 'driver' => 'slack', + 'url' => env('LOG_SLACK_WEBHOOK_URL'), + 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), + 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), + 'level' => env('LOG_LEVEL', 'critical'), + 'replace_placeholders' => true, + ], + + 'papertrail' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), + 'handler_with' => [ + 'host' => env('PAPERTRAIL_URL'), + 'port' => env('PAPERTRAIL_PORT'), + 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), + ], + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'stderr' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => StreamHandler::class, + 'handler_with' => [ + 'stream' => 'php://stderr', + ], + 'formatter' => env('LOG_STDERR_FORMATTER'), + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'syslog' => [ + 'driver' => 'syslog', + 'level' => env('LOG_LEVEL', 'debug'), + 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), + 'replace_placeholders' => true, + ], + + 'errorlog' => [ + 'driver' => 'errorlog', + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'null' => [ + 'driver' => 'monolog', + 'handler' => NullHandler::class, + ], + + 'emergency' => [ + 'path' => storage_path('logs/laravel.log'), + ], + + ], + +]; diff --git a/config/mail.php b/config/mail.php new file mode 100644 index 0000000000000000000000000000000000000000..00345321ce0f95056a8f34535093be31bd023ada --- /dev/null +++ b/config/mail.php @@ -0,0 +1,118 @@ + env('MAIL_MAILER', 'log'), + + /* + |-------------------------------------------------------------------------- + | Mailer Configurations + |-------------------------------------------------------------------------- + | + | Here you may configure all of the mailers used by your application plus + | their respective settings. Several examples have been configured for + | you and you are free to add your own as your application requires. + | + | Laravel supports a variety of mail "transport" drivers that can be used + | when delivering an email. You may specify which one you're using for + | your mailers below. You may also add additional mailers if needed. + | + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "resend", "log", "array", + | "failover", "roundrobin" + | + */ + + 'mailers' => [ + + 'smtp' => [ + 'transport' => 'smtp', + 'scheme' => env('MAIL_SCHEME'), + 'url' => env('MAIL_URL'), + 'host' => env('MAIL_HOST', '127.0.0.1'), + 'port' => env('MAIL_PORT', 2525), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'timeout' => null, + 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url(env('APP_URL', 'http://localhost'), PHP_URL_HOST)), + ], + + 'ses' => [ + 'transport' => 'ses', + ], + + 'postmark' => [ + 'transport' => 'postmark', + // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), + // 'client' => [ + // 'timeout' => 5, + // ], + ], + + 'resend' => [ + 'transport' => 'resend', + ], + + 'sendmail' => [ + 'transport' => 'sendmail', + 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), + ], + + 'log' => [ + 'transport' => 'log', + 'channel' => env('MAIL_LOG_CHANNEL'), + ], + + 'array' => [ + 'transport' => 'array', + ], + + 'failover' => [ + 'transport' => 'failover', + 'mailers' => [ + 'smtp', + 'log', + ], + 'retry_after' => 60, + ], + + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', + ], + 'retry_after' => 60, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all emails sent by your application to be sent from + | the same address. Here you may specify a name and address that is + | used globally for all emails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_FROM_NAME', 'Example'), + ], + +]; diff --git a/config/queue.php b/config/queue.php new file mode 100644 index 0000000000000000000000000000000000000000..116bd8d002477e76510418cdbe2ac90a92ea7ce7 --- /dev/null +++ b/config/queue.php @@ -0,0 +1,112 @@ + env('QUEUE_CONNECTION', 'database'), + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection options for every queue backend + | used by your application. An example configuration is provided for + | each backend supported by Laravel. You're also free to add more. + | + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" + | + */ + + 'connections' => [ + + 'sync' => [ + 'driver' => 'sync', + ], + + 'database' => [ + 'driver' => 'database', + 'connection' => env('DB_QUEUE_CONNECTION'), + 'table' => env('DB_QUEUE_TABLE', 'jobs'), + 'queue' => env('DB_QUEUE', 'default'), + 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90), + 'after_commit' => false, + ], + + 'beanstalkd' => [ + 'driver' => 'beanstalkd', + 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), + 'queue' => env('BEANSTALKD_QUEUE', 'default'), + 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), + 'block_for' => 0, + 'after_commit' => false, + ], + + 'sqs' => [ + 'driver' => 'sqs', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), + 'queue' => env('SQS_QUEUE', 'default'), + 'suffix' => env('SQS_SUFFIX'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'after_commit' => false, + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), + 'block_for' => null, + 'after_commit' => false, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Job Batching + |-------------------------------------------------------------------------- + | + | The following options configure the database and table that store job + | batching information. These options can be updated to any database + | connection and table which has been defined by your application. + | + */ + + 'batching' => [ + 'database' => env('DB_CONNECTION', 'sqlite'), + 'table' => 'job_batches', + ], + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control how and where failed jobs are stored. Laravel ships with + | support for storing failed jobs in a simple file or in a database. + | + | Supported drivers: "database-uuids", "dynamodb", "file", "null" + | + */ + + 'failed' => [ + 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), + 'database' => env('DB_CONNECTION', 'sqlite'), + 'table' => 'failed_jobs', + ], + +]; diff --git a/config/services.php b/config/services.php new file mode 100644 index 0000000000000000000000000000000000000000..27a36175f8236be239854f401d88ae0f52e4b0cf --- /dev/null +++ b/config/services.php @@ -0,0 +1,38 @@ + [ + 'token' => env('POSTMARK_TOKEN'), + ], + + 'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + ], + + 'resend' => [ + 'key' => env('RESEND_KEY'), + ], + + 'slack' => [ + 'notifications' => [ + 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), + 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), + ], + ], + +]; diff --git a/config/session.php b/config/session.php new file mode 100644 index 0000000000000000000000000000000000000000..ba0aa60b074be18ef1bbdc2d489d3f6f65cc3f07 --- /dev/null +++ b/config/session.php @@ -0,0 +1,217 @@ + env('SESSION_DRIVER', 'database'), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle before it expires. If you want them + | to expire immediately when the browser is closed then you may + | indicate that via the expire_on_close configuration option. + | + */ + + 'lifetime' => (int) env('SESSION_LIFETIME', 120), + + 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false), + + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it's stored. All encryption is performed + | automatically by Laravel and you may use the session like normal. + | + */ + + 'encrypt' => env('SESSION_ENCRYPT', false), + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When utilizing the "file" session driver, the session files are placed + | on disk. The default storage location is defined here; however, you + | are free to provide another location where they should be stored. + | + */ + + 'files' => storage_path('framework/sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | + */ + + 'connection' => env('SESSION_CONNECTION'), + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table to + | be used to store sessions. Of course, a sensible default is defined + | for you; however, you're welcome to change this to another table. + | + */ + + 'table' => env('SESSION_TABLE', 'sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + | + | When using one of the framework's cache driven session backends, you may + | define the cache store which should be used to store the session data + | between requests. This must match one of your defined cache stores. + | + | Affects: "apc", "dynamodb", "memcached", "redis" + | + */ + + 'store' => env('SESSION_STORE'), + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | + */ + + 'lottery' => [2, 100], + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the session cookie that is created by + | the framework. Typically, you should not need to change this value + | since doing so does not grant a meaningful security improvement. + | + */ + + 'cookie' => env( + 'SESSION_COOKIE', + Str::slug(env('APP_NAME', 'laravel'), '_').'_session' + ), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application, but you're free to change this when necessary. + | + */ + + 'path' => env('SESSION_PATH', '/'), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | This value determines the domain and subdomains the session cookie is + | available to. By default, the cookie will be available to the root + | domain and all subdomains. Typically, this shouldn't be changed. + | + */ + + 'domain' => env('SESSION_DOMAIN'), + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + | + | By setting this option to true, session cookies will only be sent back + | to the server if the browser has a HTTPS connection. This will keep + | the cookie from being sent to you when it can't be done securely. + | + */ + + 'secure' => env('SESSION_SECURE_COOKIE'), + + /* + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + | + | Setting this value to true will prevent JavaScript from accessing the + | value of the cookie and the cookie will only be accessible through + | the HTTP protocol. It's unlikely you should disable this option. + | + */ + + 'http_only' => env('SESSION_HTTP_ONLY', true), + + /* + |-------------------------------------------------------------------------- + | Same-Site Cookies + |-------------------------------------------------------------------------- + | + | This option determines how your cookies behave when cross-site requests + | take place, and can be used to mitigate CSRF attacks. By default, we + | will set this value to "lax" to permit secure cross-site requests. + | + | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value + | + | Supported: "lax", "strict", "none", null + | + */ + + 'same_site' => env('SESSION_SAME_SITE', 'lax'), + + /* + |-------------------------------------------------------------------------- + | Partitioned Cookies + |-------------------------------------------------------------------------- + | + | Setting this value to true will tie the cookie to the top-level site for + | a cross-site context. Partitioned cookies are accepted by the browser + | when flagged "secure" and the Same-Site attribute is set to "none". + | + */ + + 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), + +]; diff --git a/database/.gitignore b/database/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..9b19b93c9f13d72749cc3bac760a28325116f3f1 --- /dev/null +++ b/database/.gitignore @@ -0,0 +1 @@ +*.sqlite* diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..584104c9cf7521e255fad7a01e2cec3658a4d2c4 --- /dev/null +++ b/database/factories/UserFactory.php @@ -0,0 +1,44 @@ + + */ +class UserFactory extends Factory +{ + /** + * The current password being used by the factory. + */ + protected static ?string $password; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => static::$password ??= Hash::make('password'), + 'remember_token' => Str::random(10), + ]; + } + + /** + * Indicate that the model's email address should be unverified. + */ + public function unverified(): static + { + return $this->state(fn (array $attributes) => [ + 'email_verified_at' => null, + ]); + } +} diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php new file mode 100644 index 0000000000000000000000000000000000000000..05fb5d9ea95d1b527ba0e7074936553944b8d302 --- /dev/null +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -0,0 +1,49 @@ +id(); + $table->string('name'); + $table->string('email')->unique(); + $table->timestamp('email_verified_at')->nullable(); + $table->string('password'); + $table->rememberToken(); + $table->timestamps(); + }); + + Schema::create('password_reset_tokens', function (Blueprint $table) { + $table->string('email')->primary(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + + Schema::create('sessions', function (Blueprint $table) { + $table->string('id')->primary(); + $table->foreignId('user_id')->nullable()->index(); + $table->string('ip_address', 45)->nullable(); + $table->text('user_agent')->nullable(); + $table->longText('payload'); + $table->integer('last_activity')->index(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('users'); + Schema::dropIfExists('password_reset_tokens'); + Schema::dropIfExists('sessions'); + } +}; diff --git a/database/migrations/0001_01_01_000001_create_cache_table.php b/database/migrations/0001_01_01_000001_create_cache_table.php new file mode 100644 index 0000000000000000000000000000000000000000..b9c106be81284c3684966b8ce632fd9895f9ede6 --- /dev/null +++ b/database/migrations/0001_01_01_000001_create_cache_table.php @@ -0,0 +1,35 @@ +string('key')->primary(); + $table->mediumText('value'); + $table->integer('expiration'); + }); + + Schema::create('cache_locks', function (Blueprint $table) { + $table->string('key')->primary(); + $table->string('owner'); + $table->integer('expiration'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('cache'); + Schema::dropIfExists('cache_locks'); + } +}; diff --git a/database/migrations/0001_01_01_000002_create_jobs_table.php b/database/migrations/0001_01_01_000002_create_jobs_table.php new file mode 100644 index 0000000000000000000000000000000000000000..425e7058fcc7c202cef39889ff4b27d2fb1f798e --- /dev/null +++ b/database/migrations/0001_01_01_000002_create_jobs_table.php @@ -0,0 +1,57 @@ +id(); + $table->string('queue')->index(); + $table->longText('payload'); + $table->unsignedTinyInteger('attempts'); + $table->unsignedInteger('reserved_at')->nullable(); + $table->unsignedInteger('available_at'); + $table->unsignedInteger('created_at'); + }); + + Schema::create('job_batches', function (Blueprint $table) { + $table->string('id')->primary(); + $table->string('name'); + $table->integer('total_jobs'); + $table->integer('pending_jobs'); + $table->integer('failed_jobs'); + $table->longText('failed_job_ids'); + $table->mediumText('options')->nullable(); + $table->integer('cancelled_at')->nullable(); + $table->integer('created_at'); + $table->integer('finished_at')->nullable(); + }); + + Schema::create('failed_jobs', function (Blueprint $table) { + $table->id(); + $table->string('uuid')->unique(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('jobs'); + Schema::dropIfExists('job_batches'); + Schema::dropIfExists('failed_jobs'); + } +}; diff --git a/database/migrations/2025_06_25_170902_create_products_table.php b/database/migrations/2025_06_25_170902_create_products_table.php new file mode 100644 index 0000000000000000000000000000000000000000..54237a2d059bc3f134e3240694f4355147b00922 --- /dev/null +++ b/database/migrations/2025_06_25_170902_create_products_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('name'); + $table->text('description')->nullable(); + $table->decimal('price', 10, 2); + $table->string('image')->nullable(); + $table->timestamps(); + }); + } + + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('products'); + } +}; diff --git a/database/migrations/2025_06_28_124805_create_orders_table.php b/database/migrations/2025_06_28_124805_create_orders_table.php new file mode 100644 index 0000000000000000000000000000000000000000..766249df20a9278aec47750e8e844af10deb0e5c --- /dev/null +++ b/database/migrations/2025_06_28_124805_create_orders_table.php @@ -0,0 +1,31 @@ +id(); + $table->string('customer_name'); + $table->string('phone'); + $table->string('payment_slip_path'); + $table->json('cart_data')->nullable(); // เก็บรายการสินค้า + $table->timestamps(); +}); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('orders'); + } +}; diff --git a/database/migrations/2025_07_21_184151_add_amount_and_game_to_products_table.php b/database/migrations/2025_07_21_184151_add_amount_and_game_to_products_table.php new file mode 100644 index 0000000000000000000000000000000000000000..d66297b9f398d5f0228038ef86a70a1755fb8090 --- /dev/null +++ b/database/migrations/2025_07_21_184151_add_amount_and_game_to_products_table.php @@ -0,0 +1,29 @@ +integer('Amount')->default(0)->after('image'); + $table->string('game')->nullable()->after('Amount'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('products', function (Blueprint $table) { + $table->dropColumn(['Amount', 'game']); + }); + } +}; diff --git a/database/migrations/2025_07_21_185851_add_total_amount_and_status_to_orders_table.php b/database/migrations/2025_07_21_185851_add_total_amount_and_status_to_orders_table.php new file mode 100644 index 0000000000000000000000000000000000000000..d92c4a5db29635afd0330f85abc876475916552e --- /dev/null +++ b/database/migrations/2025_07_21_185851_add_total_amount_and_status_to_orders_table.php @@ -0,0 +1,29 @@ +decimal('total_amount', 10, 2)->default(0)->after('cart_data'); + $table->string('status')->default('pending')->after('total_amount'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('orders', function (Blueprint $table) { + $table->dropColumn(['total_amount', 'status']); + }); + } +}; diff --git a/database/migrations/2025_07_24_145017_add_slug_to_products_table.php b/database/migrations/2025_07_24_145017_add_slug_to_products_table.php new file mode 100644 index 0000000000000000000000000000000000000000..ab7446ffe46765066ec484bf20b6fe4d284b3550 --- /dev/null +++ b/database/migrations/2025_07_24_145017_add_slug_to_products_table.php @@ -0,0 +1,30 @@ +string('slug')->nullable()->after('name'); + $table->index('slug'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('products', function (Blueprint $table) { + $table->dropIndex(['slug']); + $table->dropColumn('slug'); + }); + } +}; diff --git a/database/migrations/2025_07_25_043209_create_custom_games_table.php b/database/migrations/2025_07_25_043209_create_custom_games_table.php new file mode 100644 index 0000000000000000000000000000000000000000..c5a6616ec072260f41e8e207b38027830ad219cb --- /dev/null +++ b/database/migrations/2025_07_25_043209_create_custom_games_table.php @@ -0,0 +1,30 @@ +id(); + $table->string('name')->unique(); + $table->string('icon')->default('fas fa-gamepad'); + $table->string('color_gradient')->default('from-purple-500 to-blue-500'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('custom_games'); + } +}; diff --git a/database/migrations/2025_07_26_194151_create_analytics_snapshots_table.php b/database/migrations/2025_07_26_194151_create_analytics_snapshots_table.php new file mode 100644 index 0000000000000000000000000000000000000000..80be218b950c37e9ac3bfbf7107d75a2581c239e --- /dev/null +++ b/database/migrations/2025_07_26_194151_create_analytics_snapshots_table.php @@ -0,0 +1,39 @@ +id(); + $table->date('date'); + $table->integer('hour')->nullable(); // 0-23 for hourly data, null for daily aggregates + $table->decimal('revenue', 10, 2)->default(0); + $table->decimal('profit', 10, 2)->default(0); + $table->integer('orders_count')->default(0); + $table->integer('customers_count')->default(0); + $table->decimal('avg_order_value', 8, 2)->default(0); + $table->decimal('conversion_rate', 5, 4)->default(0); // 0.0000 to 1.0000 + $table->timestamps(); + + // Indexes for performance + $table->index(['date', 'hour']); + $table->index('date'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('analytics_snapshots'); + } +}; diff --git a/database/migrations/2025_07_26_194206_create_revenue_tracking_table.php b/database/migrations/2025_07_26_194206_create_revenue_tracking_table.php new file mode 100644 index 0000000000000000000000000000000000000000..9eb888674577870432379241da4f5e6353485f6e --- /dev/null +++ b/database/migrations/2025_07_26_194206_create_revenue_tracking_table.php @@ -0,0 +1,37 @@ +id(); + $table->foreignId('order_id')->constrained()->onDelete('cascade'); + $table->decimal('amount', 10, 2); + $table->decimal('cost', 10, 2)->default(0); // Product cost for profit calculation + $table->decimal('profit_margin', 5, 4)->default(0); // Calculated profit margin + $table->string('product_name')->nullable(); + $table->integer('quantity')->default(1); + $table->timestamps(); + + // Indexes for performance + $table->index('order_id'); + $table->index('created_at'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('revenue_tracking'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000000000000000000000000000000000000..d01a0ef2f7ffeb30b856a70a7daedf47d9b6b31b --- /dev/null +++ b/database/seeders/DatabaseSeeder.php @@ -0,0 +1,23 @@ +create(); + + User::factory()->create([ + 'name' => 'Test User', + 'email' => 'test@example.com', + ]); + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..330d3714c149227f7a443f61e314f874929c3741 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3912 @@ +{ + "name": "laravel_github-master", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "chart.js": "^4.5.0", + "countup.js": "^2.9.0", + "datatables.net-dt": "^2.3.2", + "jquery": "^3.7.1" + }, + "devDependencies": { + "@tailwindcss/forms": "^0.5.2", + "@tailwindcss/vite": "^4.0.0", + "alpinejs": "^3.4.2", + "autoprefixer": "^10.4.2", + "axios": "^1.8.2", + "concurrently": "^9.0.1", + "laravel-vite-plugin": "^1.2.0", + "postcss": "^8.4.31", + "tailwindcss": "^3.1.0", + "vite": "^6.2.4" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", + "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", + "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", + "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", + "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", + "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", + "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", + "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", + "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", + "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", + "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", + "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", + "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", + "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", + "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", + "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", + "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", + "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", + "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", + "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", + "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", + "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", + "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", + "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", + "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", + "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", + "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", + "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", + "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", + "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", + "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", + "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", + "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", + "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", + "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", + "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", + "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", + "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", + "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", + "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", + "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", + "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", + "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", + "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", + "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.4.tgz", + "integrity": "sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.29.2", + "tailwindcss": "4.1.4" + } + }, + "node_modules/@tailwindcss/node/node_modules/tailwindcss": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.4.tgz", + "integrity": "sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.4.tgz", + "integrity": "sha512-p5wOpXyOJx7mKh5MXh5oKk+kqcz8T+bA3z/5VWWeQwFrmuBItGwz8Y2CHk/sJ+dNb9B0nYFfn0rj/cKHZyjahQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.4", + "@tailwindcss/oxide-darwin-arm64": "4.1.4", + "@tailwindcss/oxide-darwin-x64": "4.1.4", + "@tailwindcss/oxide-freebsd-x64": "4.1.4", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.4", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.4", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.4", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.4", + "@tailwindcss/oxide-linux-x64-musl": "4.1.4", + "@tailwindcss/oxide-wasm32-wasi": "4.1.4", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.4", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.4" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.4.tgz", + "integrity": "sha512-xMMAe/SaCN/vHfQYui3fqaBDEXMu22BVwQ33veLc8ep+DNy7CWN52L+TTG9y1K397w9nkzv+Mw+mZWISiqhmlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.4.tgz", + "integrity": "sha512-JGRj0SYFuDuAGilWFBlshcexev2hOKfNkoX+0QTksKYq2zgF9VY/vVMq9m8IObYnLna0Xlg+ytCi2FN2rOL0Sg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.4.tgz", + "integrity": "sha512-sdDeLNvs3cYeWsEJ4H1DvjOzaGios4QbBTNLVLVs0XQ0V95bffT3+scptzYGPMjm7xv4+qMhCDrkHwhnUySEzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.4.tgz", + "integrity": "sha512-VHxAqxqdghM83HslPhRsNhHo91McsxRJaEnShJOMu8mHmEj9Ig7ToHJtDukkuLWLzLboh2XSjq/0zO6wgvykNA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.4.tgz", + "integrity": "sha512-OTU/m/eV4gQKxy9r5acuesqaymyeSCnsx1cFto/I1WhPmi5HDxX1nkzb8KYBiwkHIGg7CTfo/AcGzoXAJBxLfg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.4.tgz", + "integrity": "sha512-hKlLNvbmUC6z5g/J4H+Zx7f7w15whSVImokLPmP6ff1QqTVE+TxUM9PGuNsjHvkvlHUtGTdDnOvGNSEUiXI1Ww==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.4.tgz", + "integrity": "sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.4.tgz", + "integrity": "sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.4.tgz", + "integrity": "sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.4.tgz", + "integrity": "sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.0", + "@emnapi/runtime": "^1.4.0", + "@emnapi/wasi-threads": "^1.0.1", + "@napi-rs/wasm-runtime": "^0.2.8", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.4.tgz", + "integrity": "sha512-VlnhfilPlO0ltxW9/BgfLI5547PYzqBMPIzRrk4W7uupgCt8z6Trw/tAj6QUtF2om+1MH281Pg+HHUJoLesmng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.4.tgz", + "integrity": "sha512-+7S63t5zhYjslUGb8NcgLpFXD+Kq1F/zt5Xv5qTv7HaFTG/DHyHD9GA6ieNAxhgyA4IcKa/zy7Xx4Oad2/wuhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.4.tgz", + "integrity": "sha512-4UQeMrONbvrsXKXXp/uxmdEN5JIJ9RkH7YVzs6AMxC/KC1+Np7WZBaNIco7TEjlkthqxZbt8pU/ipD+hKjm80A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.4", + "@tailwindcss/oxide": "4.1.4", + "tailwindcss": "4.1.4" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6" + } + }, + "node_modules/@tailwindcss/vite/node_modules/tailwindcss": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.4.tgz", + "integrity": "sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", + "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.1.5" + } + }, + "node_modules/@vue/shared": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", + "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/alpinejs": { + "version": "3.14.9", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.14.9.tgz", + "integrity": "sha512-gqSOhTEyryU9FhviNqiHBHzgjkvtukq9tevew29fTj+ofZtfsYriw4zPirHHOAy9bw8QoL3WGhyk7QqCh5AYlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "~3.1.1" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "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/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "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/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "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/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001714", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001714.tgz", + "integrity": "sha512-mtgapdwDLSSBnCI3JokHM7oEQBLxiJKVRtg10AxM1AyeiKcM96f0Mkbqeq+1AbiCtvMcHRulAAEMu693JrSWqg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "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/chart.js": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", + "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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/cliui/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/cliui/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/cliui/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/cliui/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/cliui/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/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/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concurrently": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.2.tgz", + "integrity": "sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "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/countup.js": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.9.0.tgz", + "integrity": "sha512-llqrvyXztRFPp6+i8jx25phHWcVWhrHO4Nlt0uAOSKHB8778zzQswa4MU3qKBvkXfJKftRYFJuVHez67lyKdHg==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/datatables.net": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-2.3.2.tgz", + "integrity": "sha512-31TzwIQM0+pr2ZOEOEH6dsHd/WSAl5GDDGPezOHPI3mM2NK4lcDyOoG8xXeWmSbVfbi852LNK5C84fpp4Q+qxg==", + "license": "MIT", + "dependencies": { + "jquery": ">=1.7" + } + }, + "node_modules/datatables.net-dt": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/datatables.net-dt/-/datatables.net-dt-2.3.2.tgz", + "integrity": "sha512-2heAMe0AVqJ8nt9wE35ArLtL1zP7N7qnhM3u52bLcJfi4NStxyBsHScVggTHVQPj7r9O+qirAxox1Xcr+Bm4Dw==", + "license": "MIT", + "dependencies": { + "datatables.net": "2.3.2", + "jquery": ">=1.7" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "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/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.137", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz", + "integrity": "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "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.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.2", + "@esbuild/android-arm": "0.25.2", + "@esbuild/android-arm64": "0.25.2", + "@esbuild/android-x64": "0.25.2", + "@esbuild/darwin-arm64": "0.25.2", + "@esbuild/darwin-x64": "0.25.2", + "@esbuild/freebsd-arm64": "0.25.2", + "@esbuild/freebsd-x64": "0.25.2", + "@esbuild/linux-arm": "0.25.2", + "@esbuild/linux-arm64": "0.25.2", + "@esbuild/linux-ia32": "0.25.2", + "@esbuild/linux-loong64": "0.25.2", + "@esbuild/linux-mips64el": "0.25.2", + "@esbuild/linux-ppc64": "0.25.2", + "@esbuild/linux-riscv64": "0.25.2", + "@esbuild/linux-s390x": "0.25.2", + "@esbuild/linux-x64": "0.25.2", + "@esbuild/netbsd-arm64": "0.25.2", + "@esbuild/netbsd-x64": "0.25.2", + "@esbuild/openbsd-arm64": "0.25.2", + "@esbuild/openbsd-x64": "0.25.2", + "@esbuild/sunos-x64": "0.25.2", + "@esbuild/win32-arm64": "0.25.2", + "@esbuild/win32-ia32": "0.25.2", + "@esbuild/win32-x64": "0.25.2" + } + }, + "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/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "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/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "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-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "license": "MIT" + }, + "node_modules/laravel-vite-plugin": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.2.0.tgz", + "integrity": "sha512-R0pJ+IcTVeqEMoKz/B2Ij57QVq3sFTABiFmb06gAwFdivbOgsUtuhX6N2MGLEArajrS3U5JbberzwOe7uXHMHQ==", + "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": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0" + } + }, + "node_modules/lightningcss": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", + "integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.29.2", + "lightningcss-darwin-x64": "1.29.2", + "lightningcss-freebsd-x64": "1.29.2", + "lightningcss-linux-arm-gnueabihf": "1.29.2", + "lightningcss-linux-arm64-gnu": "1.29.2", + "lightningcss-linux-arm64-musl": "1.29.2", + "lightningcss-linux-x64-gnu": "1.29.2", + "lightningcss-linux-x64-musl": "1.29.2", + "lightningcss-win32-arm64-msvc": "1.29.2", + "lightningcss-win32-x64-msvc": "1.29.2" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz", + "integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==", + "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.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz", + "integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==", + "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.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz", + "integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==", + "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.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz", + "integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==", + "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.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz", + "integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==", + "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.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz", + "integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==", + "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.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz", + "integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==", + "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.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz", + "integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==", + "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.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz", + "integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==", + "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.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", + "integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==", + "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/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "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/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "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/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "dev": true, + "license": "MIT", + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "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/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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": "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/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "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.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "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/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "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/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.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/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "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/string-width-cjs/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/string-width-cjs/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/string-width-cjs/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/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "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/strip-ansi-cjs/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/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "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/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", + "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "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/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "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/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.0.tgz", + "integrity": "sha512-9aC0n4pr6hIbvi1YOpFjwQ+QOTGssvbJKoeYkuHHGWwlXfdxQlI8L2qNMo9awEEcCPSiS+5mJZk5jH1PAqoDeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.3", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.12" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "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/node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?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/yaml": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "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" + } + }, + "node_modules/yargs/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/yargs/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/yargs/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/yargs/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" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000000000000000000000000000000000000..ab0093a5ba8ac8d232ba8540643d0a338c281e3a --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "private": true, + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite" + }, + "devDependencies": { + "@tailwindcss/forms": "^0.5.2", + "@tailwindcss/vite": "^4.0.0", + "alpinejs": "^3.4.2", + "autoprefixer": "^10.4.2", + "axios": "^1.8.2", + "concurrently": "^9.0.1", + "laravel-vite-plugin": "^1.2.0", + "postcss": "^8.4.31", + "tailwindcss": "^3.1.0", + "vite": "^6.2.4" + }, + "dependencies": { + "chart.js": "^4.5.0", + "countup.js": "^2.9.0", + "datatables.net-dt": "^2.3.2", + "jquery": "^3.7.1" + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000000000000000000000000000000000000..61c031c478ef57ccc703432e52fce874d0c2c8de --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,33 @@ + + + + + tests/Unit + + + tests/Feature + + + + + app + + + + + + + + + + + + + + + + diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000000000000000000000000000000000000..49c0612d5c24aa905f8b947afbf3367746452476 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000000000000000000000000000000000000..b574a597daf309b383dc2dc2bd1e5362630e2531 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,25 @@ + + + Options -MultiViews -Indexes + + + RewriteEngine On + + # Handle Authorization Header + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # Handle X-XSRF-Token Header + RewriteCond %{HTTP:x-xsrf-token} . + RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}] + + # Redirect Trailing Slashes If Not A Folder... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} (.+)/$ + RewriteRule ^ %1 [L,R=301] + + # Send Requests To Front Controller... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000000000000000000000000000000000000..ee8f07e993802c29e166435e55e302ebfff1c12b --- /dev/null +++ b/public/index.php @@ -0,0 +1,20 @@ +handleRequest(Request::capture()); diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000000000000000000000000000000000000..eb0536286f3081c6c0646817037faf5446e3547d --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/render.yaml b/render.yaml new file mode 100644 index 0000000000000000000000000000000000000000..26ed846b4217e7c26b98e1b209ecc7a215f59751 --- /dev/null +++ b/render.yaml @@ -0,0 +1,23 @@ +services: + - type: web + name: laravel-github + env: docker + repo: https://github.com/Thitipan-James/laravel_github + branch: main + buildCommand: "./build.sh" + startCommand: "php artisan serve --host 0.0.0.0 --port 10000" + plan: free + envVars: + - key: APP_ENV + value: production + - key: APP_DEBUG + value: false + - key: APP_KEY + sync: false + - key: DB_CONNECTION + value: sqlite + - key: DB_DATABASE + value: /data/database.sqlite + disk: + name: data + mountPath: /data diff --git a/resources/css/app.css b/resources/css/app.css new file mode 100644 index 0000000000000000000000000000000000000000..4d0dc7c1761a5b321a1727ce20dcddbeb71e7f94 --- /dev/null +++ b/resources/css/app.css @@ -0,0 +1,1274 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap'); + +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* Smooth Animations */ +@keyframes float { + + 0%, + 100% { + transform: translateY(0px) rotate(0deg); + } + + 50% { + transform: translateY(-15px) rotate(180deg); + } +} + +@keyframes blob { + 0% { + transform: translate(0px, 0px) scale(1); + } + + 33% { + transform: translate(30px, -50px) scale(1.1); + } + + 66% { + transform: translate(-20px, 20px) scale(0.9); + } + + 100% { + transform: translate(0px, 0px) scale(1); + } +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(30px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes shimmer { + 0% { + transform: translateX(-100%); + } + + 100% { + transform: translateX(100%); + } +} + +@keyframes pulse { + + 0%, + 100% { + opacity: 0.4; + } + + 50% { + opacity: 0.8; + } +} + +@layer base { + * { + box-sizing: border-box; + } + + html { + scroll-behavior: smooth; + } + + body { + font-family: 'Inter', system-ui, sans-serif; + background: linear-gradient(135deg, #0c0c0c 0%, #1a1a2e 50%, #16213e 100%); + color: #ffffff; + line-height: 1.6; + min-height: 100vh; + } +} + +@layer components { + + /* Enhanced Glassmorphic Navigation */ + .glass-nav { + background: rgba(0, 0, 0, 0.2); + backdrop-filter: blur(30px); + border-bottom: 1px solid rgba(255, 255, 255, 0.05); + position: relative; + z-index: 40; + transition: all 0.3s ease; + } + + .glass-nav:hover { + background: rgba(0, 0, 0, 0.3); + backdrop-filter: blur(40px); + } + + /* Fast Glassmorphic Background */ + .glass-bg { + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(20px); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 20px; + position: relative; + z-index: 40; + } + + /* Removed shimmer effect to prevent weird highlighting */ + + /* Floating Background Blobs */ + .bg-blob { + position: absolute; + border-radius: 50%; + filter: blur(60px); + animation: blob 10s infinite; + z-index: -1; + opacity: 0.6; + } + + .bg-blob-1 { + width: 400px; + height: 400px; + background: linear-gradient(45deg, #667eea, #764ba2); + top: 10%; + left: 10%; + animation-delay: 0s; + } + + .bg-blob-2 { + width: 300px; + height: 300px; + background: linear-gradient(45deg, #f093fb, #f5576c); + top: 60%; + right: 10%; + animation-delay: 3s; + } + + .bg-blob-3 { + width: 250px; + height: 250px; + background: linear-gradient(45deg, #4facfe, #00f2fe); + bottom: 20%; + left: 20%; + animation-delay: 6s; + } + + /* Premium Dark Cards */ + .card { + background: rgba(0, 0, 0, 0.3); + backdrop-filter: blur(20px); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 20px; + padding: 24px; + position: relative; + overflow: hidden; + transition: all 0.4s ease; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); + } + + .card:hover { + transform: translateY(-8px); + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4); + border-color: rgba(255, 255, 255, 0.2); + background: rgba(0, 0, 0, 0.4); + } + + /* Removed card shimmer effect to prevent weird highlighting */ + + .card:hover::before { + left: 100%; + } + + /* Modern Dark Buttons */ + .btn-primary { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 14px 28px; + border-radius: 14px; + font-weight: 600; + border: none; + cursor: pointer; + transition: all 0.3s ease; + display: inline-flex; + align-items: center; + justify-content: center; + text-decoration: none; + position: relative; + overflow: hidden; + box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); + } + + .btn-primary:hover { + transform: translateY(-3px); + box-shadow: 0 8px 25px rgba(102, 126, 234, 0.6); + } + + .btn-secondary { + background: rgba(0, 0, 0, 0.3); + backdrop-filter: blur(10px); + color: white; + padding: 14px 28px; + border-radius: 14px; + font-weight: 500; + border: 1px solid rgba(255, 255, 255, 0.2); + cursor: pointer; + transition: all 0.3s ease; + display: inline-flex; + align-items: center; + justify-content: center; + text-decoration: none; + } + + .btn-secondary:hover { + background: rgba(0, 0, 0, 0.5); + transform: translateY(-2px); + border-color: rgba(255, 255, 255, 0.3); + } + + .btn-danger { + background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%); + color: white; + padding: 10px 20px; + border-radius: 10px; + font-weight: 500; + border: none; + cursor: pointer; + transition: all 0.3s ease; + display: inline-flex; + align-items: center; + justify-content: center; + text-decoration: none; + box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4); + } + + .btn-danger:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(255, 107, 107, 0.6); + } + + .btn-success { + background: linear-gradient(135deg, #4ecdc4 0%, #44a08d 100%); + color: white; + padding: 14px 28px; + border-radius: 14px; + font-weight: 600; + border: none; + cursor: pointer; + transition: all 0.3s ease; + display: inline-flex; + align-items: center; + justify-content: center; + text-decoration: none; + box-shadow: 0 4px 15px rgba(78, 205, 196, 0.4); + } + + .btn-success:hover { + transform: translateY(-3px); + box-shadow: 0 8px 25px rgba(78, 205, 196, 0.6); + } + + /* Dark Input Fields */ + .input-field { + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 12px; + padding: 14px 18px; + color: white; + width: 100%; + transition: all 0.3s ease; + } + + .input-field:focus { + outline: none; + border-color: rgba(102, 126, 234, 0.6); + background: rgba(0, 0, 0, 0.6); + box-shadow: 0 0 20px rgba(102, 126, 234, 0.3); + } + + .input-field::placeholder { + color: rgba(255, 255, 255, 0.5); + } + + /* Fix select dropdown styling */ + select.input-field { + background: rgba(0, 0, 0, 0.4) !important; + color: white !important; + border: 1px solid rgba(255, 255, 255, 0.2) !important; + } + + select.input-field:focus { + background: rgba(0, 0, 0, 0.6) !important; + border-color: rgba(102, 126, 234, 0.6) !important; + box-shadow: 0 0 20px rgba(102, 126, 234, 0.3) !important; + } + + select.input-field option { + background: rgba(0, 0, 0, 0.9) !important; + color: white !important; + } + + /* Override any grey backgrounds on selects */ + select { + background: rgba(0, 0, 0, 0.4) !important; + color: white !important; + } + + /* Dropdown styling - less aggressive */ + [x-data] div[x-show] { + z-index: 50; + } + + /* Dark Badges */ + .badge { + padding: 6px 14px; + border-radius: 20px; + font-size: 12px; + font-weight: 500; + display: inline-flex; + align-items: center; + backdrop-filter: blur(10px); + } + + .badge-success { + background: rgba(78, 205, 196, 0.2); + color: #4ecdc4; + border: none; + } + + .badge-danger { + background: rgba(255, 107, 107, 0.2); + color: #ff6b6b; + border: none; + } + + .badge-warning { + background: rgba(255, 193, 7, 0.2); + color: #ffc107; + border: none; + } + + .badge-info { + background: rgba(102, 126, 234, 0.2); + color: #667eea; + border: none; + } + + /* Enhanced Navigation Icons */ + .nav-icon { + color: rgba(255, 255, 255, 0.6); + padding: 12px; + border-radius: 12px; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + text-decoration: none; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + z-index: 10; + position: relative; + background: rgba(255, 255, 255, 0.02); + border: 1px solid rgba(255, 255, 255, 0.05); + backdrop-filter: blur(10px); + min-width: 44px; + min-height: 44px; + } + + .nav-icon:hover { + color: white; + background: rgba(255, 255, 255, 0.08); + border-color: rgba(255, 255, 255, 0.15); + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3); + } + + .nav-icon.active { + color: white; + background: rgba(102, 126, 234, 0.2); + border-color: rgba(102, 126, 234, 0.4); + box-shadow: 0 0 20px rgba(102, 126, 234, 0.3); + } + + .nav-icon.primary { + background: linear-gradient(135deg, rgba(102, 126, 234, 0.3), rgba(139, 92, 246, 0.3)); + border-color: rgba(102, 126, 234, 0.4); + } + + .nav-icon.primary:hover { + background: linear-gradient(135deg, rgba(102, 126, 234, 0.5), rgba(139, 92, 246, 0.5)); + box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4); + } + + .nav-icon.admin { + border-color: rgba(239, 68, 68, 0.3); + } + + .nav-icon.admin:hover { + background: rgba(239, 68, 68, 0.1); + border-color: rgba(239, 68, 68, 0.4); + } + + .nav-icon.user-menu { + padding: 8px; + min-width: 48px; + min-height: 48px; + } + + /* Legacy nav-link for compatibility */ + .nav-link { + color: rgba(255, 255, 255, 0.8); + padding: 10px 18px; + border-radius: 10px; + transition: all 0.1s ease; + text-decoration: none; + display: inline-flex; + align-items: center; + font-weight: 500; + cursor: pointer; + z-index: 10; + position: relative; + } + + .nav-link:hover { + color: white; + background: rgba(255, 255, 255, 0.1); + } + + .nav-link.active { + color: white; + background: rgba(102, 126, 234, 0.3); + border: 1px solid rgba(102, 126, 234, 0.4); + } + + /* Premium Typography */ + .heading-1 { + font-size: 3rem; + font-weight: 800; + color: white; + margin-bottom: 1.5rem; + line-height: 1.2; + padding: 0.5rem 0; + } + + .heading-2 { + font-size: 2.25rem; + font-weight: 700; + color: white; + margin-bottom: 1rem; + line-height: 1.2; + } + + .heading-3 { + font-size: 1.75rem; + font-weight: 600; + color: white; + margin-bottom: 0.75rem; + line-height: 1.3; + } + + .text-muted { + color: rgba(255, 255, 255, 0.6); + } + + /* Layout */ + .container-custom { + max-width: 1200px; + margin: 0 auto; + padding: 0 1.5rem; + } + + .grid-products { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 1.5rem; + max-width: 100%; + padding: 0.5rem; + } + + /* Enhanced hover effects */ + .product-card:hover { + transform: translateY(-4px); + box-shadow: 0 12px 35px rgba(0, 0, 0, 0.4); + } + + /* Simple gradient text - no animation */ + .gradient-text { + background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + } + + /* Utilities */ + .line-clamp-2 { + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } + + .line-clamp-3 { + display: -webkit-box; + -webkit-line-clamp: 3; + line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + } + + /* Animation Classes */ + .fade-in { + animation: fadeIn 0.8s ease-out; + } + + .float { + animation: float 8s ease-in-out infinite; + } + + /* Professional Loading Animations */ + @keyframes loading-pulse { + + 0%, + 100% { + transform: scale(1); + opacity: 1; + } + + 50% { + transform: scale(1.05); + opacity: 0.8; + } + } + + @keyframes loading-dot { + + 0%, + 100% { + transform: translateY(0); + opacity: 0.5; + } + + 50% { + transform: translateY(-10px); + opacity: 1; + } + } + + @keyframes loading-progress { + 0% { + width: 0%; + } + + 100% { + width: 100%; + } + } + + .loading-pulse { + animation: loading-pulse 2s ease-in-out infinite; + } + + .loading-dot { + animation: loading-dot 1.5s ease-in-out infinite; + } + + .loading-progress { + animation: loading-progress 3s ease-in-out infinite; + } + + .loading-fade-out { + opacity: 0; + pointer-events: none; + transition: opacity 0.5s ease-out; + } + + /* Special Effects */ + .glow-text { + text-shadow: 0 0 20px rgba(255, 255, 255, 0.5); + } + + .gradient-text { + background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + } + + /* Fix for gradient text clipping */ + .hero-title-fix { + padding: 0.25rem 0; + line-height: 1.1; + display: block; + overflow: visible; + } + + /* Clean dropdown styling */ + .dropdown-content { + background: rgba(0, 0, 0, 0.9) !important; + backdrop-filter: blur(20px) !important; + border: 1px solid rgba(255, 255, 255, 0.1) !important; + border-radius: 12px !important; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5) !important; + } + + /* Force dropdown to be dark */ + div[x-show] div[class*="bg-white"] { + background: rgba(0, 0, 0, 0.9) !important; + backdrop-filter: blur(20px) !important; + border: 1px solid rgba(255, 255, 255, 0.1) !important; + } + + /* Fix dropdown links */ + div[x-show] a[class*="text-gray"] { + color: white !important; + } + + div[x-show] a:hover { + background: rgba(255, 255, 255, 0.1) !important; + } + + /* Responsive nav links */ + .responsive-nav-link { + color: rgba(255, 255, 255, 0.8) !important; + padding: 12px 16px !important; + border-radius: 8px !important; + margin: 4px 8px !important; + transition: all 0.3s ease !important; + } + + .responsive-nav-link:hover { + color: white !important; + background: rgba(255, 255, 255, 0.1) !important; + } + + .responsive-nav-link.active { + color: white !important; + background: rgba(102, 126, 234, 0.3) !important; + } +} + +/* + Fix white rectangle issue */ +.bg-white { + background: transparent !important; +} + +main { + background: transparent !important; +} + +/* Ensure all content areas are transparent */ +.min-h-screen { + background: transparent !important; +} + +/* Fix navigation responsiveness */ +nav a, +nav button { + pointer-events: auto !important; + z-index: 50 !important; + position: relative !important; +} + +/* Remove any blocking overlays */ +nav::after, +nav::before { + pointer-events: none !important; +} + +/* Ensure immediate clickability */ +.nav-link { + pointer-events: auto !important; + user-select: none; +} + +/* Fix any transition delays */ +* { + transition-delay: 0s !important; +} +/* Cate +gory Cards */ +.category-card { + transition: all 0.3s ease; +} + +.category-card:hover { + transform: translateY(-8px); +} + +.category-card .card { + transition: all 0.3s ease; + cursor: pointer; +} + +.category-card:hover .card { + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5); + border-color: rgba(255, 255, 255, 0.3); +} + +/* Modal Animations */ +.modal-enter { + animation: modalEnter 0.3s ease-out; +} + +@keyframes modalEnter { + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } +}/* + Enhanced Category Page Styles */ +.category-card { + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); +} + +.category-card:hover { + transform: translateY(-12px) scale(1.02); +} + +.category-card:active { + transform: translateY(-8px) scale(0.98); +} + +/* Enhanced Modal Styles */ +#categoryModal { + backdrop-filter: blur(20px); +} + +#modalContainer { + transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1); +} + +/* Custom Scrollbar for Modal */ +.custom-scrollbar::-webkit-scrollbar { + width: 12px; +} + +.custom-scrollbar::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.05); + border-radius: 6px; +} + +.custom-scrollbar::-webkit-scrollbar-thumb { + background: linear-gradient(135deg, rgba(102, 126, 234, 0.8), rgba(139, 92, 246, 0.8)); + border-radius: 6px; + border: 2px solid rgba(255, 255, 255, 0.1); +} + +.custom-scrollbar::-webkit-scrollbar-thumb:hover { + background: linear-gradient(135deg, rgba(102, 126, 234, 1), rgba(139, 92, 246, 1)); +} + +/* Removed duplicate gradient animation */ + +/* Pulse Animation for Loading */ +@keyframes pulse-glow { + 0%, 100% { + box-shadow: 0 0 20px rgba(139, 92, 246, 0.5); + transform: scale(1); + } + 50% { + box-shadow: 0 0 40px rgba(139, 92, 246, 0.8); + transform: scale(1.05); + } +} + +.loading-pulse { + animation: pulse-glow 2s ease-in-out infinite; +} + +/* Enhanced Card Hover Effects */ +.card:hover { + box-shadow: + 0 25px 50px rgba(0, 0, 0, 0.5), + 0 0 50px rgba(139, 92, 246, 0.2); +} + +/* Floating Animation Variations */ +@keyframes float-slow { + 0%, 100% { transform: translateY(0px) rotate(0deg); } + 50% { transform: translateY(-20px) rotate(5deg); } +} + +@keyframes float-medium { + 0%, 100% { transform: translateY(0px) rotate(0deg); } + 50% { transform: translateY(-15px) rotate(-3deg); } +} + +.float-slow { + animation: float-slow 12s ease-in-out infinite; +} + +.float-medium { + animation: float-medium 8s ease-in-out infinite; +} + +/* Enhanced Fade In Animation */ +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(50px) scale(0.9); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +.fade-in { + animation: fadeInUp 0.8s ease-out forwards; +} + +/* Stagger Animation Delays */ +.fade-in:nth-child(1) { animation-delay: 0.1s; } +.fade-in:nth-child(2) { animation-delay: 0.2s; } +.fade-in:nth-child(3) { animation-delay: 0.3s; } +.fade-in:nth-child(4) { animation-delay: 0.4s; } +.fade-in:nth-child(5) { animation-delay: 0.5s; } +.fade-in:nth-child(6) { animation-delay: 0.6s; }/* Epic +Hero Section Animations */ +.hero-wrapper { + position: relative; + background: linear-gradient(135deg, #0c0c0c 0%, #1a1a2e 50%, #16213e 100%); +} + +/* Parallax Layers */ +.parallax-layer { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + will-change: transform; +} + +/* Floating Particles */ +.particles { + z-index: 1; +} + +.particle { + position: absolute; + width: 4px; + height: 4px; + background: rgba(255, 255, 255, 0.6); + border-radius: 50%; + animation: particleFloat 8s infinite linear; +} + +@keyframes particleFloat { + 0% { + transform: translateY(100vh) scale(0); + opacity: 0; + } + 10% { + opacity: 1; + } + 90% { + opacity: 1; + } + 100% { + transform: translateY(-100px) scale(1); + opacity: 0; + } +} + +/* Hero Content Animations */ +.hero-content { + z-index: 10; +} + +.hero-badge { + animation: fadeInUp 1s ease-out 0.5s forwards; +} + +.hero-title { + animation: fadeInUp 1s ease-out 0.8s forwards; + text-shadow: 0 0 50px rgba(139, 92, 246, 0.3); +} + +.hero-subtitle { + animation: fadeInUp 1s ease-out 1.1s forwards; +} + +.hero-stats { + animation: fadeInUp 1s ease-out 1.4s forwards; +} + +.hero-cta { + animation: fadeInUp 1s ease-out 1.7s forwards; +} + +/* Counter Animation */ +.counter { + transition: all 0.3s ease; +} + +/* Scroll-based Animations */ +.scroll-reveal { + opacity: 0; + transform: translateY(50px); + transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1); +} + +.scroll-reveal.revealed { + opacity: 1; + transform: translateY(0); +} + +/* Enhanced Floating Animation */ +@keyframes float { + 0%, 100% { + transform: translateY(0px) rotate(0deg) scale(1); + } + 25% { + transform: translateY(-20px) rotate(5deg) scale(1.05); + } + 50% { + transform: translateY(-10px) rotate(-3deg) scale(0.95); + } + 75% { + transform: translateY(-15px) rotate(2deg) scale(1.02); + } +} + +/* Scroll Indicator */ +.scroll-indicator { + animation: scrollBounce 2s infinite; +} + +@keyframes scrollBounce { + 0%, 20%, 50%, 80%, 100% { + transform: translateX(-50%) translateY(0); + } + 40% { + transform: translateX(-50%) translateY(-10px); + } + 60% { + transform: translateX(-50%) translateY(-5px); + } +} + +/* Button Hover Effects */ +.btn-primary:hover { + transform: translateY(-3px) scale(1.05); + box-shadow: 0 20px 40px rgba(102, 126, 234, 0.4); +} + +.btn-secondary:hover { + transform: translateY(-3px) scale(1.05); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); +} + +/* Stat Card Animations */ +.stat-card { + backdrop-filter: blur(20px); + background: rgba(0, 0, 0, 0.3); + border: 1px solid rgba(255, 255, 255, 0.1); + transition: all 0.3s ease; +} + +.stat-card:hover { + background: rgba(0, 0, 0, 0.5); + border-color: rgba(139, 92, 246, 0.3); + box-shadow: 0 10px 30px rgba(139, 92, 246, 0.2); +} + +/* Responsive Adjustments */ +@media (max-width: 768px) { + .hero-title { + font-size: 4rem; + } + + .hero-subtitle { + font-size: 1.25rem; + } + + .particle { + width: 2px; + height: 2px; + } +} + +/* Performance Optimizations */ +.parallax-layer, +.particle, +.hero-content { + will-change: transform; +} + +/* Smooth Scrolling */ +html { + scroll-behavior: smooth; +} + +/* Custom Scrollbar */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.1); +} + +::-webkit-scrollbar-thumb { + background: linear-gradient(135deg, rgba(139, 92, 246, 0.8), rgba(59, 130, 246, 0.8)); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: linear-gradient(135deg, rgba(139, 92, 246, 1), rgba(59, 130, 246, 1)); +} +/* Enh +anced Glassmorphic Search Bar */ +.search-container { + position: relative; +} + +.search-container::before { + content: ''; + position: absolute; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + background: linear-gradient(45deg, + rgba(102, 126, 234, 0.3), + rgba(139, 92, 246, 0.3), + rgba(59, 130, 246, 0.3) + ); + border-radius: 18px; + opacity: 0; + transition: opacity 0.3s ease; + z-index: -1; +} + +.search-container:focus-within::before { + opacity: 1; +} + +/* Glassmorphic Reveal Animation */ +@keyframes glassReveal { + 0% { + opacity: 0; + backdrop-filter: blur(0px); + transform: scale(0.95); + } + 100% { + opacity: 1; + backdrop-filter: blur(20px); + transform: scale(1); + } +} + +.glass-reveal { + animation: glassReveal 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards; +} + +/* Enhanced Search Results Dropdown */ +.search-results { + background: rgba(0, 0, 0, 0.85); + backdrop-filter: blur(40px); + border: 1px solid rgba(255, 255, 255, 0.1); + box-shadow: + 0 20px 60px rgba(0, 0, 0, 0.5), + 0 0 0 1px rgba(255, 255, 255, 0.05); +} + +/* Floating Search Bar Effect */ +.search-floating { + position: relative; +} + +.search-floating::after { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + padding: 1px; + background: linear-gradient(45deg, + rgba(102, 126, 234, 0.4), + rgba(139, 92, 246, 0.4) + ); + mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + mask-composite: exclude; + opacity: 0; + transition: opacity 0.3s ease; +} + +.search-floating:focus-within::after { + opacity: 1; +} + +/* Smooth Hover Transitions */ +.nav-icon { + will-change: transform, background-color, border-color; +} + +.nav-icon::before { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + background: rgba(255, 255, 255, 0.05); + opacity: 0; + transition: opacity 0.3s ease; +} + +.nav-icon:hover::before { + opacity: 1; +} + +/* Pulse Animation for Cart Badge */ +@keyframes cartPulse { + 0%, 100% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.1); + opacity: 0.8; + } +} + +.cart-badge { + animation: cartPulse 2s ease-in-out infinite; +} + +/* Enhanced Mobile Menu */ +.mobile-menu { + background: rgba(0, 0, 0, 0.95); + backdrop-filter: blur(30px); + border-top: 1px solid rgba(255, 255, 255, 0.1); +} + +/* Responsive Improvements */ +@media (max-width: 768px) { + .glass-nav { + backdrop-filter: blur(20px); + } + + .nav-icon { + min-width: 40px; + min-height: 40px; + padding: 10px; + } +} + +/* Performance Optimizations */ +.glass-nav, +.nav-icon, +.search-container { + will-change: transform, background-color, backdrop-filter; +} + +/* Accessibility Improvements */ +.nav-icon:focus { + outline: 2px solid rgba(102, 126, 234, 0.6); + outline-offset: 2px; +} + +/* Dark Mode Enhancements */ +@media (prefers-color-scheme: dark) { + .glass-nav { + background: rgba(0, 0, 0, 0.3); + } + + .nav-icon { + background: rgba(255, 255, 255, 0.03); + } +} + +/* Fix search bar positioning on home page - direct targeting */ +.search-container.search-floating { + margin-left: -1.5rem !important; + transform: translateX(-0.75rem) !important; +} + +/* Remove the weird blue box/border effect */ +.search-container::before { + display: none !important; + opacity: 0 !important; +} + +.search-container:focus-within::before { + display: none !important; + opacity: 0 !important; +} + +/* Alternative approach - target the parent container */ +.glass-nav .flex-1 { + padding-left: 1.5rem !important; + padding-right: 2.5rem !important; +} + +/* Target the search input directly */ +.search-container input[placeholder*="Search"] { + margin-left: -1rem !important; +} + +/* Clean and simple search bar */ +.search-container input:focus { + outline: none !important; + box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.3) !important; + border-color: rgba(255, 255, 255, 0.3) !important; + background: rgba(0, 0, 0, 0.6) !important; +} + +.search-container input:hover { + border-color: rgba(255, 255, 255, 0.2) !important; + background: rgba(0, 0, 0, 0.5) !important; +} + +.search-container input { + transition: all 0.2s ease !important; +} + +/* Remove all pseudo-elements */ +.search-container.search-floating::after, +.search-container.search-floating::before { + display: none !important; +} + +/* Mobile responsive fix */ +@media (max-width: 768px) { + .search-container.search-floating { + margin-left: -0.75rem !important; + transform: translateX(-0.5rem) !important; + } + + .glass-nav .flex-1 { + padding-left: 1rem !important; + padding-right: 1.5rem !important; + } +}/ +* Fix search bar alignment for different authentication states */ +.search-center-container { + transform: translateX(0); + transition: transform 0.3s ease; +} + +/* When user is signed in, move search bar RIGHT (less content on right side) */ +body:has(.nav-icon.user-menu) .search-center-container { + transform: translateX(1.5rem); +} + +/* When logged out, keep current position (more content on right side with 2 buttons) */ +body:has(a[href*="login"]) .search-center-container { + transform: translateX(0); +} + +/* Mobile responsive fix for search alignment */ +@media (max-width: 768px) { + .search-center-container { + transform: translateX(0) !important; + } + + body:has(.nav-icon.user-menu) .search-center-container { + transform: translateX(0.5rem) !important; + } +} \ No newline at end of file diff --git a/resources/js/app.js b/resources/js/app.js new file mode 100644 index 0000000000000000000000000000000000000000..5fc6332dd8e745b4663de81d556fe36416287636 --- /dev/null +++ b/resources/js/app.js @@ -0,0 +1,17 @@ +import './bootstrap'; + +import Alpine from 'alpinejs'; +import $ from 'jquery'; +import DataTable from 'datatables.net-dt'; + +// Dashboard Chart Components +import './dashboard/chartConfig.js'; +import './dashboard/chartUtils.js'; +import './dashboard/metricCards.js'; +import './dashboard/timeFilter.js'; +import './dashboard/dashboard.js'; + +window.Alpine = Alpine; +window.$ = $; + +Alpine.start(); diff --git a/resources/js/bootstrap.js b/resources/js/bootstrap.js new file mode 100644 index 0000000000000000000000000000000000000000..5f1390b015cbf1ee54f580c38fcd47a723eb81f4 --- /dev/null +++ b/resources/js/bootstrap.js @@ -0,0 +1,4 @@ +import axios from 'axios'; +window.axios = axios; + +window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; diff --git a/resources/js/dashboard/chartConfig.js b/resources/js/dashboard/chartConfig.js new file mode 100644 index 0000000000000000000000000000000000000000..8e9ddf4cd20c5a81b09f925c8ca672ac3bb74080 --- /dev/null +++ b/resources/js/dashboard/chartConfig.js @@ -0,0 +1,318 @@ +// Chart.js Glassmorphic Configuration +import { Chart, registerables } from 'chart.js'; + +Chart.register(...registerables); + +// Color scheme constants +export const COLORS = { + revenue: { + primary: 'rgba(52, 211, 153, 0.8)', + secondary: 'rgba(52, 211, 153, 0.2)', + gradient: ['rgba(52, 211, 153, 0.8)', 'rgba(52, 211, 153, 0.2)'] + }, + profit: { + primary: 'rgba(168, 85, 247, 0.8)', + secondary: 'rgba(168, 85, 247, 0.2)', + gradient: ['rgba(168, 85, 247, 0.8)', 'rgba(168, 85, 247, 0.2)'] + }, + orders: { + primary: 'rgba(59, 130, 246, 0.8)', + secondary: 'rgba(59, 130, 246, 0.2)', + gradient: ['rgba(59, 130, 246, 0.8)', 'rgba(59, 130, 246, 0.2)'] + }, + customers: { + primary: 'rgba(251, 146, 60, 0.8)', + secondary: 'rgba(251, 146, 60, 0.2)', + gradient: ['rgba(251, 146, 60, 0.8)', 'rgba(251, 146, 60, 0.2)'] + }, + text: { + primary: 'rgba(255, 255, 255, 0.9)', + secondary: 'rgba(255, 255, 255, 0.6)', + muted: 'rgba(255, 255, 255, 0.4)' + }, + grid: 'rgba(255, 255, 255, 0.1)', + border: 'rgba(255, 255, 255, 0.2)' +}; + +// Base glassmorphic chart configuration +export const baseChartConfig = { + responsive: true, + maintainAspectRatio: false, + interaction: { + intersect: false, + mode: 'index' + }, + plugins: { + legend: { + display: true, + position: 'top', + labels: { + color: COLORS.text.primary, + font: { + size: 12, + weight: '500' + }, + padding: 20, + usePointStyle: true, + pointStyle: 'circle' + } + }, + tooltip: { + enabled: true, + backgroundColor: 'rgba(0, 0, 0, 0.8)', + backdropFilter: 'blur(10px)', + titleColor: COLORS.text.primary, + bodyColor: COLORS.text.secondary, + borderColor: COLORS.border, + borderWidth: 1, + cornerRadius: 12, + padding: 12, + displayColors: true, + callbacks: { + title: function(context) { + return context[0].label; + }, + label: function(context) { + const label = context.dataset.label || ''; + const value = context.parsed.y; + + if (label.toLowerCase().includes('revenue') || label.toLowerCase().includes('profit')) { + return `${label}: ฿${value.toLocaleString()}`; + } else if (label.toLowerCase().includes('percentage') || label.toLowerCase().includes('margin')) { + return `${label}: ${value.toFixed(2)}%`; + } + return `${label}: ${value.toLocaleString()}`; + } + } + } + }, + scales: { + x: { + grid: { + color: COLORS.grid, + borderColor: COLORS.border, + drawBorder: false + }, + ticks: { + color: COLORS.text.secondary, + font: { + size: 11 + }, + maxTicksLimit: 12 + } + }, + y: { + grid: { + color: COLORS.grid, + borderColor: COLORS.border, + drawBorder: false + }, + ticks: { + color: COLORS.text.secondary, + font: { + size: 11 + }, + callback: function(value) { + if (this.chart.config.type === 'line' && this.chart.data.datasets[0].label?.toLowerCase().includes('revenue')) { + return '฿' + value.toLocaleString(); + } + return value.toLocaleString(); + } + } + } + }, + elements: { + point: { + radius: 4, + hoverRadius: 6, + borderWidth: 2, + hoverBorderWidth: 3 + }, + line: { + borderWidth: 3, + tension: 0.4 + }, + bar: { + borderRadius: 6, + borderSkipped: false + } + }, + animation: { + duration: 1000, + easing: 'easeInOutQuart' + } +}; + +// Create gradient for chart backgrounds +export function createGradient(ctx, colorArray, direction = 'vertical') { + const gradient = direction === 'vertical' + ? ctx.createLinearGradient(0, 0, 0, ctx.canvas.height) + : ctx.createLinearGradient(0, 0, ctx.canvas.width, 0); + + gradient.addColorStop(0, colorArray[0]); + gradient.addColorStop(1, colorArray[1]); + + return gradient; +} + +// Line chart configuration +export function createLineChartConfig(labels, datasets, options = {}) { + return { + type: 'line', + data: { + labels, + datasets: datasets.map(dataset => ({ + ...dataset, + fill: true, + backgroundColor: function(context) { + const chart = context.chart; + const {ctx} = chart; + return createGradient(ctx, dataset.gradientColors || COLORS.revenue.gradient); + }, + borderColor: dataset.borderColor || COLORS.revenue.primary, + pointBackgroundColor: dataset.borderColor || COLORS.revenue.primary, + pointBorderColor: '#ffffff', + pointHoverBackgroundColor: '#ffffff', + pointHoverBorderColor: dataset.borderColor || COLORS.revenue.primary + })) + }, + options: { + ...baseChartConfig, + ...options + } + }; +} + +// Bar chart configuration +export function createBarChartConfig(labels, datasets, options = {}) { + return { + type: 'bar', + data: { + labels, + datasets: datasets.map(dataset => ({ + ...dataset, + backgroundColor: function(context) { + const chart = context.chart; + const {ctx} = chart; + return createGradient(ctx, dataset.gradientColors || COLORS.orders.gradient); + }, + borderColor: dataset.borderColor || COLORS.orders.primary, + borderWidth: 1 + })) + }, + options: { + ...baseChartConfig, + ...options + } + }; +} + +// Doughnut chart configuration +export function createDoughnutChartConfig(labels, data, options = {}) { + const backgroundColors = [ + COLORS.revenue.primary, + COLORS.profit.primary, + COLORS.orders.primary, + COLORS.customers.primary, + 'rgba(236, 72, 153, 0.8)', // Pink + 'rgba(34, 197, 94, 0.8)', // Green + 'rgba(239, 68, 68, 0.8)', // Red + 'rgba(245, 158, 11, 0.8)', // Amber + 'rgba(139, 92, 246, 0.8)', // Violet + 'rgba(6, 182, 212, 0.8)' // Cyan + ]; + + return { + type: 'doughnut', + data: { + labels, + datasets: [{ + data, + backgroundColor: backgroundColors.slice(0, data.length), + borderColor: backgroundColors.slice(0, data.length).map(color => color.replace('0.8', '1')), + borderWidth: 2, + hoverBorderWidth: 3 + }] + }, + options: { + ...baseChartConfig, + cutout: '60%', + plugins: { + ...baseChartConfig.plugins, + legend: { + ...baseChartConfig.plugins.legend, + position: 'right' + } + }, + ...options + } + }; +} + +// Mixed chart configuration (Line + Bar) +export function createMixedChartConfig(labels, datasets, options = {}) { + return { + type: 'bar', + data: { + labels, + datasets: datasets.map(dataset => ({ + ...dataset, + backgroundColor: dataset.type === 'line' + ? 'transparent' + : function(context) { + const chart = context.chart; + const {ctx} = chart; + return createGradient(ctx, dataset.gradientColors || COLORS.customers.gradient); + }, + borderColor: dataset.borderColor || COLORS.customers.primary, + borderWidth: dataset.type === 'line' ? 3 : 1, + yAxisID: dataset.yAxisID || 'y' + })) + }, + options: { + ...baseChartConfig, + scales: { + ...baseChartConfig.scales, + y1: { + type: 'linear', + display: true, + position: 'right', + grid: { + drawOnChartArea: false, + color: COLORS.grid + }, + ticks: { + color: COLORS.text.secondary, + font: { + size: 11 + } + } + } + }, + ...options + } + }; +} + +// Animation helpers +export const chartAnimations = { + fadeIn: { + duration: 800, + easing: 'easeOutQuart' + }, + slideUp: { + duration: 1000, + easing: 'easeOutBack' + }, + bounce: { + duration: 1200, + easing: 'easeOutBounce' + } +}; + +// Make functions globally available for inline scripts +window.COLORS = COLORS; +window.createLineChartConfig = createLineChartConfig; +window.createDoughnutChartConfig = createDoughnutChartConfig; +window.createMixedChartConfig = createMixedChartConfig; +window.chartAnimations = chartAnimations; \ No newline at end of file diff --git a/resources/js/dashboard/chartUtils.js b/resources/js/dashboard/chartUtils.js new file mode 100644 index 0000000000000000000000000000000000000000..4244c010019a2cfc66b0bd42c4ba16e3ab34c769 --- /dev/null +++ b/resources/js/dashboard/chartUtils.js @@ -0,0 +1,246 @@ +// Chart Utilities for Dashboard +import { CountUp } from 'countup.js'; + +/** + * Initialize animated counter for metric cards + */ +export function initCounter(element, endValue, options = {}) { + const defaultOptions = { + duration: 2, + useEasing: true, + useGrouping: true, + separator: ',', + decimal: '.', + prefix: '', + suffix: '' + }; + + const countUp = new CountUp(element, endValue, { + ...defaultOptions, + ...options + }); + + if (!countUp.error) { + countUp.start(); + } else { + console.error('CountUp error:', countUp.error); + element.textContent = endValue; + } + + return countUp; +} + +/** + * Format currency values + */ +export function formatCurrency(value, currency = '฿') { + return `${currency}${value.toLocaleString()}`; +} + +/** + * Format percentage values + */ +export function formatPercentage(value, decimals = 2) { + return `${value.toFixed(decimals)}%`; +} + +/** + * Get trend indicator HTML + */ +export function getTrendIndicator(percentage, showValue = true) { + const isPositive = percentage >= 0; + const icon = isPositive ? '↗' : '↘'; + const colorClass = isPositive ? 'text-emerald-400' : 'text-red-400'; + const bgClass = isPositive ? 'bg-emerald-400/10' : 'bg-red-400/10'; + + const valueText = showValue ? `${Math.abs(percentage).toFixed(1)}%` : ''; + + return ` +
+ ${icon} + ${valueText ? `${valueText}` : ''} +
+ `; +} + +/** + * Update chart data with smooth animation + */ +export function updateChartData(chart, newData, newLabels = null) { + if (newLabels) { + chart.data.labels = newLabels; + } + + // Update datasets + newData.forEach((dataset, index) => { + if (chart.data.datasets[index]) { + chart.data.datasets[index].data = dataset.data; + if (dataset.label) { + chart.data.datasets[index].label = dataset.label; + } + } + }); + + chart.update('active'); +} + +/** + * Resize chart to fit container + */ +export function resizeChart(chart) { + if (chart && chart.canvas) { + chart.resize(); + } +} + +/** + * Destroy chart safely + */ +export function destroyChart(chart) { + if (chart && typeof chart.destroy === 'function') { + chart.destroy(); + } +} + +/** + * Create loading skeleton for charts + */ +export function createChartSkeleton(container) { + container.innerHTML = ` +
+
+
+
+
+
+
+
+
+
+ `; +} + +/** + * Show error state for charts + */ +export function showChartError(container, message = 'Failed to load chart data') { + container.innerHTML = ` +
+
+ + + +
+

${message}

+ +
+ `; +} + +/** + * Debounce function for performance + */ +export function debounce(func, wait) { + let timeout; + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout); + func(...args); + }; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; +} + +/** + * Throttle function for performance + */ +export function throttle(func, limit) { + let inThrottle; + return function() { + const args = arguments; + const context = this; + if (!inThrottle) { + func.apply(context, args); + inThrottle = true; + setTimeout(() => inThrottle = false, limit); + } + }; +} + +/** + * Format time labels based on period + */ +export function formatTimeLabel(value, period) { + switch (period) { + case 'daily': + return `${value}:00`; + case 'weekly': + return value; // Day name + case 'monthly': + return `Day ${value}`; + default: + return value; + } +} + +/** + * Generate random data for demo purposes + */ +export function generateDemoData(length, min = 0, max = 1000) { + return Array.from({ length }, () => Math.floor(Math.random() * (max - min + 1)) + min); +} + +/** + * Calculate percentage change + */ +export function calculatePercentageChange(current, previous) { + if (previous === 0) return current > 0 ? 100 : 0; + return ((current - previous) / previous) * 100; +} + +/** + * Get responsive chart height based on screen size + */ +export function getResponsiveHeight() { + const width = window.innerWidth; + if (width < 640) return 250; // Mobile + if (width < 1024) return 300; // Tablet + return 350; // Desktop +} + +/** + * Initialize chart container with glassmorphic styling + */ +export function initChartContainer(container) { + container.classList.add( + 'bg-white/5', + 'backdrop-blur-2xl', + 'border', + 'border-white/10', + 'rounded-2xl', + 'p-6', + 'shadow-2xl' + ); + + // Add glow effect + container.style.boxShadow = ` + 0 25px 50px -12px rgba(0, 0, 0, 0.6), + 0 0 0 1px rgba(255, 255, 255, 0.05), + inset 0 1px 0 rgba(255, 255, 255, 0.1) + `; +} + +// Make functions globally available for inline scripts +window.createChartSkeleton = createChartSkeleton; +window.showChartError = showChartError; +window.initChartContainer = initChartContainer; +window.formatCurrency = formatCurrency; +window.formatPercentage = formatPercentage; +window.getTrendIndicator = getTrendIndicator; +window.calculatePercentageChange = calculatePercentageChange; +window.updateChartData = updateChartData; +window.resizeChart = resizeChart; +window.destroyChart = destroyChart; \ No newline at end of file diff --git a/resources/js/dashboard/dashboard.js b/resources/js/dashboard/dashboard.js new file mode 100644 index 0000000000000000000000000000000000000000..b1128f55480efb84f548a78e6893e2763d0e8e4c --- /dev/null +++ b/resources/js/dashboard/dashboard.js @@ -0,0 +1,333 @@ +// Dashboard Manager - Main dashboard functionality +import { Chart, registerables } from 'chart.js'; +import { createChartSkeleton, showChartError, initCounter } from './chartUtils.js'; +import { createLineChartConfig, createDoughnutChartConfig, createMixedChartConfig } from './chartConfig.js'; + +// Register Chart.js components +Chart.register(...registerables); + +// Color scheme for charts +const COLORS = { + revenue: { + primary: '#10b981', + gradient: ['rgba(16, 185, 129, 0.8)', 'rgba(16, 185, 129, 0.1)'] + }, + profit: { + primary: '#8b5cf6', + gradient: ['rgba(139, 92, 246, 0.8)', 'rgba(139, 92, 246, 0.1)'] + }, + customers: { + primary: '#f59e0b', + gradient: ['rgba(245, 158, 11, 0.8)', 'rgba(245, 158, 11, 0.1)'] + }, + orders: { + primary: '#3b82f6', + gradient: ['rgba(59, 130, 246, 0.8)', 'rgba(59, 130, 246, 0.1)'] + } +}; + +// Global dashboard manager function for Alpine.js +window.dashboardManager = function() { + return { + loading: false, + charts: {}, + currentPeriod: 'daily', + currentDate: new Date().toISOString().split('T')[0], + + init() { + this.initializeCharts(); + this.loadDashboardData(); + this.setupEventListeners(); + this.startRealTimeUpdates(); + }, + + initializeCharts() { + // Initialize all charts with loading states + this.initRevenueChart(); + this.initProfitChart(); + this.initProductsChart(); + this.initCustomersChart(); + }, + + async initRevenueChart() { + const ctx = document.getElementById('revenueChart'); + if (!ctx) return; + + createChartSkeleton(ctx.parentElement); + + try { + const response = await fetch(`/api/dashboard/revenue?period=${this.currentPeriod}&date=${this.currentDate}`); + const result = await response.json(); + + if (result.success && result.data.chart_data) { + const chartData = result.data.chart_data; + const labels = chartData.map(item => item.hour !== undefined ? `${item.hour}:00` : item.day_name || item.date); + const data = chartData.map(item => item.revenue); + + this.charts.revenue = new Chart(ctx, createLineChartConfig( + labels, + [{ + label: 'Revenue (฿)', + data: data, + gradientColors: COLORS.revenue.gradient, + borderColor: COLORS.revenue.primary + }] + )); + } else { + throw new Error('No data available'); + } + } catch (error) { + console.error('Error loading revenue chart:', error); + showChartError(ctx.parentElement, 'Failed to load revenue data'); + } + }, + + async initProfitChart() { + const ctx = document.getElementById('profitChart'); + if (!ctx) return; + + createChartSkeleton(ctx.parentElement); + + try { + const response = await fetch(`/api/dashboard/profit?period=${this.currentPeriod}&date=${this.currentDate}`); + const result = await response.json(); + + if (result.success && result.data.chart_data) { + const chartData = result.data.chart_data; + const labels = chartData.map(item => item.label); + const revenueData = chartData.map(item => item.revenue); + const profitData = chartData.map(item => item.profit); + + this.charts.profit = new Chart(ctx, createLineChartConfig( + labels, + [ + { + label: 'Revenue (฿)', + data: revenueData, + gradientColors: COLORS.revenue.gradient, + borderColor: COLORS.revenue.primary + }, + { + label: 'Profit (฿)', + data: profitData, + gradientColors: COLORS.profit.gradient, + borderColor: COLORS.profit.primary + } + ] + )); + } else { + throw new Error('No data available'); + } + } catch (error) { + console.error('Error loading profit chart:', error); + showChartError(ctx.parentElement, 'Failed to load profit data'); + } + }, + + async initProductsChart() { + const ctx = document.getElementById('productsChart'); + if (!ctx) return; + + createChartSkeleton(ctx.parentElement); + + try { + const response = await fetch('/api/dashboard/sales'); + const result = await response.json(); + + if (result.success && result.data.top_products) { + const products = result.data.top_products; + const labels = products.map(p => p.product_name); + const data = products.map(p => parseFloat(p.total_revenue)); + + this.charts.products = new Chart(ctx, createDoughnutChartConfig(labels, data)); + } else { + throw new Error('No data available'); + } + } catch (error) { + console.error('Error loading products chart:', error); + showChartError(ctx.parentElement, 'Failed to load products data'); + } + }, + + async initCustomersChart() { + const ctx = document.getElementById('customersChart'); + if (!ctx) return; + + createChartSkeleton(ctx.parentElement); + + try { + const response = await fetch(`/api/dashboard/customers?period=${this.currentPeriod}&date=${this.currentDate}`); + const result = await response.json(); + + if (result.success && result.data.chart_data) { + const chartData = result.data.chart_data; + const labels = chartData.map(item => item.label); + const customersData = chartData.map(item => item.customers); + const ordersData = chartData.map(item => item.orders); + + this.charts.customers = new Chart(ctx, createMixedChartConfig( + labels, + [ + { + type: 'bar', + label: 'Customers', + data: customersData, + gradientColors: COLORS.customers.gradient, + borderColor: COLORS.customers.primary, + yAxisID: 'y' + }, + { + type: 'line', + label: 'Orders', + data: ordersData, + borderColor: COLORS.orders.primary, + yAxisID: 'y1' + } + ] + )); + } else { + throw new Error('No data available'); + } + } catch (error) { + console.error('Error loading customers chart:', error); + showChartError(ctx.parentElement, 'Failed to load customer data'); + } + }, + + async loadDashboardData() { + this.loading = true; + + try { + const response = await fetch('/api/dashboard/summary'); + const result = await response.json(); + + if (result.success) { + this.updateMetricCards(result.data); + } + } catch (error) { + console.error('Error loading dashboard data:', error); + } finally { + this.loading = false; + } + }, + + updateMetricCards(data) { + // Update revenue card + if (data.revenue) { + this.updateMetricCard('revenue', data.revenue.current, data.revenue.previous); + } + + // Update profit card + if (data.profit) { + this.updateMetricCard('profit', data.profit.profit_margin, 0); + } + + // Update orders card + if (data.orders) { + this.updateMetricCard('orders', data.orders.current, data.orders.previous); + } + + // Update customers card + if (data.customers) { + this.updateMetricCard('customers', data.customers.total_customers, 0); + } + }, + + updateMetricCard(type, value, previousValue) { + const card = document.querySelector(`[data-metric-card="${type}"]`); + if (!card) return; + + const valueElement = card.querySelector('[data-countup]'); + if (valueElement) { + const prefix = valueElement.dataset.prefix || ''; + const suffix = valueElement.dataset.suffix || ''; + + // Update the counter + initCounter(valueElement, value, { + prefix: prefix, + suffix: suffix, + duration: 1.5 + }); + } + }, + + setupEventListeners() { + // Listen for period changes from time filter + document.addEventListener('period-changed', (event) => { + this.onPeriodChange(event.detail.period, event.detail.date); + }); + + // Handle window resize + window.addEventListener('resize', () => { + Object.values(this.charts).forEach(chart => { + if (chart && chart.resize) { + chart.resize(); + } + }); + }); + }, + + onPeriodChange(period, date) { + this.currentPeriod = period; + this.currentDate = date; + + // Destroy existing charts + Object.values(this.charts).forEach(chart => { + if (chart && chart.destroy) { + chart.destroy(); + } + }); + + // Reinitialize charts with new period + this.charts = {}; + this.initializeCharts(); + }, + + startRealTimeUpdates() { + // Update dashboard every 30 seconds + setInterval(() => { + if (!this.loading) { + this.loadDashboardData(); + } + }, 30000); + + // Show last updated time + this.updateLastRefreshTime(); + setInterval(() => { + this.updateLastRefreshTime(); + }, 1000); + }, + + updateLastRefreshTime() { + const now = new Date(); + const timeString = now.toLocaleTimeString(); + + // Update any "last updated" indicators + const indicators = document.querySelectorAll('.last-updated'); + indicators.forEach(indicator => { + indicator.textContent = `Last updated: ${timeString}`; + }); + }, + + // Public methods for external access + refreshData() { + this.loadDashboardData(); + }, + + setPeriod(period) { + this.onPeriodChange(period, this.currentDate); + }, + + setDate(date) { + this.onPeriodChange(this.currentPeriod, date); + } + }; +}; + +// Initialize dashboard when DOM is loaded +document.addEventListener('DOMContentLoaded', function() { + // Make sure Alpine.js can access the dashboard manager + if (typeof Alpine !== 'undefined') { + Alpine.start(); + } +}); \ No newline at end of file diff --git a/resources/js/dashboard/metricCards.js b/resources/js/dashboard/metricCards.js new file mode 100644 index 0000000000000000000000000000000000000000..ac9d069120957774a706be15781d6d37bff981ab --- /dev/null +++ b/resources/js/dashboard/metricCards.js @@ -0,0 +1,216 @@ +// Metric Cards Animation and Management +import { initCounter, formatCurrency, formatPercentage, getTrendIndicator } from './chartUtils.js'; + +class MetricCardManager { + constructor() { + this.cards = new Map(); + this.counters = new Map(); + this.init(); + } + + init() { + this.initializeCards(); + this.setupEventListeners(); + } + + initializeCards() { + const cardElements = document.querySelectorAll('[data-metric-card]'); + + cardElements.forEach(element => { + const cardId = element.dataset.metricCard; + this.cards.set(cardId, element); + + // Initialize counter if present + const counterElement = element.querySelector('[data-countup]'); + if (counterElement) { + this.initializeCounter(cardId, counterElement); + } + }); + } + + initializeCounter(cardId, element) { + const value = parseFloat(element.dataset.countup) || 0; + const prefix = element.dataset.prefix || ''; + const suffix = element.dataset.suffix || ''; + const decimals = parseInt(element.dataset.decimals) || 0; + + const counter = initCounter(element, value, { + prefix: prefix, + suffix: suffix, + duration: 2.5, + useEasing: true, + useGrouping: true, + decimalPlaces: decimals + }); + + this.counters.set(cardId, counter); + } + + updateCard(cardId, data) { + const cardElement = this.cards.get(cardId); + if (!cardElement) return; + + // Update counter value + const counterElement = cardElement.querySelector('[data-countup]'); + if (counterElement && this.counters.has(cardId)) { + const counter = this.counters.get(cardId); + counter.update(data.value); + } + + // Update trend indicator + if (data.trend !== undefined) { + this.updateTrendIndicator(cardElement, data.trend); + } + + // Update previous value + if (data.previousValue !== undefined) { + const prevElement = cardElement.querySelector('.previous-value'); + if (prevElement) { + prevElement.textContent = `Previous: ${data.prefix || ''}${data.previousValue.toLocaleString()}${data.suffix || ''}`; + } + } + + // Add update animation + this.animateUpdate(cardElement); + } + + updateTrendIndicator(cardElement, trendData) { + const trendElement = cardElement.querySelector('.trend-indicator'); + if (trendElement && trendData.percentage !== undefined) { + trendElement.innerHTML = getTrendIndicator(trendData.percentage, true); + } + } + + animateUpdate(cardElement) { + cardElement.classList.add('animate-pulse'); + setTimeout(() => { + cardElement.classList.remove('animate-pulse'); + }, 1000); + } + + setLoading(cardId, loading = true) { + const cardElement = this.cards.get(cardId); + if (!cardElement) return; + + const contentElement = cardElement.querySelector('.metric-content'); + const loadingElement = cardElement.querySelector('.metric-loading'); + + if (loading) { + contentElement?.classList.add('hidden'); + loadingElement?.classList.remove('hidden'); + } else { + contentElement?.classList.remove('hidden'); + loadingElement?.classList.add('hidden'); + } + } + + showError(cardId, message = 'Failed to load data') { + const cardElement = this.cards.get(cardId); + if (!cardElement) return; + + const errorHtml = ` +
+
+ + + +
+

${message}

+
+ `; + + cardElement.innerHTML = errorHtml; + } + + setupEventListeners() { + // Handle card hover effects + this.cards.forEach((cardElement, cardId) => { + cardElement.addEventListener('mouseenter', () => { + this.onCardHover(cardElement, true); + }); + + cardElement.addEventListener('mouseleave', () => { + this.onCardHover(cardElement, false); + }); + }); + + // Handle window resize for responsive adjustments + window.addEventListener('resize', this.debounce(() => { + this.handleResize(); + }, 250)); + } + + onCardHover(cardElement, isHovering) { + const glowOverlay = cardElement.querySelector('.glow-overlay'); + if (glowOverlay) { + if (isHovering) { + glowOverlay.style.opacity = '1'; + } else { + glowOverlay.style.opacity = '0'; + } + } + } + + handleResize() { + // Adjust card layouts for different screen sizes + const isMobile = window.innerWidth < 640; + const isTablet = window.innerWidth < 1024; + + this.cards.forEach((cardElement) => { + if (isMobile) { + cardElement.classList.add('mobile-card'); + } else { + cardElement.classList.remove('mobile-card'); + } + }); + } + + debounce(func, wait) { + let timeout; + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout); + func(...args); + }; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; + } + + // Public API methods + updateAllCards(data) { + Object.keys(data).forEach(cardId => { + if (data[cardId]) { + this.updateCard(cardId, data[cardId]); + } + }); + } + + refreshCard(cardId) { + // Trigger a refresh animation + const cardElement = this.cards.get(cardId); + if (cardElement) { + cardElement.style.transform = 'scale(0.98)'; + setTimeout(() => { + cardElement.style.transform = 'scale(1)'; + }, 150); + } + } + + getCardValue(cardId) { + const counter = this.counters.get(cardId); + return counter ? counter.endVal : 0; + } +} + +// Initialize metric card manager +let metricCardManager; + +document.addEventListener('DOMContentLoaded', function() { + metricCardManager = new MetricCardManager(); + + // Make it globally available + window.metricCardManager = metricCardManager; +}); + +export { MetricCardManager }; \ No newline at end of file diff --git a/resources/js/dashboard/timeFilter.js b/resources/js/dashboard/timeFilter.js new file mode 100644 index 0000000000000000000000000000000000000000..e83efff6d75e972dac52a338b397e8779380ba83 --- /dev/null +++ b/resources/js/dashboard/timeFilter.js @@ -0,0 +1,247 @@ +// Time Filter Management for Dashboard +class TimeFilterManager { + constructor() { + this.currentPeriod = 'daily'; + this.currentDate = new Date().toISOString().split('T')[0]; + this.callbacks = new Set(); + this.init(); + } + + init() { + this.setupEventListeners(); + this.initializeFilter(); + } + + setupEventListeners() { + // Listen for period change events from the component + document.addEventListener('period-changed', (event) => { + const { period, date } = event.detail; + this.handlePeriodChange(period, date); + }); + + // Listen for keyboard shortcuts + document.addEventListener('keydown', (event) => { + if (event.ctrlKey || event.metaKey) { + switch (event.key) { + case '1': + event.preventDefault(); + this.setPeriod('daily'); + break; + case '2': + event.preventDefault(); + this.setPeriod('weekly'); + break; + case '3': + event.preventDefault(); + this.setPeriod('monthly'); + break; + } + } + }); + } + + initializeFilter() { + // Set initial state + this.triggerCallbacks(); + } + + handlePeriodChange(period, date) { + const previousPeriod = this.currentPeriod; + const previousDate = this.currentDate; + + this.currentPeriod = period; + this.currentDate = date; + + // Only trigger callbacks if something actually changed + if (previousPeriod !== period || previousDate !== date) { + this.triggerCallbacks(); + this.updateURL(); + } + } + + triggerCallbacks() { + const data = { + period: this.currentPeriod, + date: this.currentDate, + timestamp: Date.now() + }; + + this.callbacks.forEach(callback => { + try { + callback(data); + } catch (error) { + console.error('Error in time filter callback:', error); + } + }); + } + + updateURL() { + // Update URL parameters without page reload + const url = new URL(window.location); + url.searchParams.set('period', this.currentPeriod); + url.searchParams.set('date', this.currentDate); + + window.history.replaceState({}, '', url); + } + + // Public API methods + setPeriod(period) { + const filterComponent = document.querySelector('[x-data*="timeFilter"]'); + if (filterComponent && filterComponent._x_dataStack) { + const alpineData = filterComponent._x_dataStack[0]; + if (alpineData && typeof alpineData.setPeriod === 'function') { + alpineData.setPeriod(period); + } + } + } + + setDate(date) { + const filterComponent = document.querySelector('[x-data*="timeFilter"]'); + if (filterComponent && filterComponent._x_dataStack) { + const alpineData = filterComponent._x_dataStack[0]; + if (alpineData && typeof alpineData.setDate === 'function') { + alpineData.setDate(date); + } + } + } + + getCurrentPeriod() { + return this.currentPeriod; + } + + getCurrentDate() { + return this.currentDate; + } + + getCurrentSelection() { + return { + period: this.currentPeriod, + date: this.currentDate + }; + } + + // Callback management + onPeriodChange(callback) { + if (typeof callback === 'function') { + this.callbacks.add(callback); + + // Immediately call with current state + callback({ + period: this.currentPeriod, + date: this.currentDate, + timestamp: Date.now() + }); + } + + // Return unsubscribe function + return () => { + this.callbacks.delete(callback); + }; + } + + // Utility methods + getDateRange() { + const date = new Date(this.currentDate); + + switch (this.currentPeriod) { + case 'daily': + return { + start: new Date(date), + end: new Date(date) + }; + + case 'weekly': + const startOfWeek = new Date(date); + startOfWeek.setDate(date.getDate() - date.getDay()); + const endOfWeek = new Date(startOfWeek); + endOfWeek.setDate(startOfWeek.getDate() + 6); + + return { + start: startOfWeek, + end: endOfWeek + }; + + case 'monthly': + const startOfMonth = new Date(date.getFullYear(), date.getMonth(), 1); + const endOfMonth = new Date(date.getFullYear(), date.getMonth() + 1, 0); + + return { + start: startOfMonth, + end: endOfMonth + }; + + default: + return { + start: new Date(date), + end: new Date(date) + }; + } + } + + formatDateForAPI() { + return this.currentDate; + } + + getPeriodLabel() { + const labels = { + daily: 'Daily', + weekly: 'Weekly', + monthly: 'Monthly' + }; + + return labels[this.currentPeriod] || 'Daily'; + } + + // Navigation methods + goToPrevious() { + const date = new Date(this.currentDate); + + switch (this.currentPeriod) { + case 'daily': + date.setDate(date.getDate() - 1); + break; + case 'weekly': + date.setDate(date.getDate() - 7); + break; + case 'monthly': + date.setMonth(date.getMonth() - 1); + break; + } + + this.setDate(date.toISOString().split('T')[0]); + } + + goToNext() { + const date = new Date(this.currentDate); + + switch (this.currentPeriod) { + case 'daily': + date.setDate(date.getDate() + 1); + break; + case 'weekly': + date.setDate(date.getDate() + 7); + break; + case 'monthly': + date.setMonth(date.getMonth() + 1); + break; + } + + this.setDate(date.toISOString().split('T')[0]); + } + + goToToday() { + this.setDate(new Date().toISOString().split('T')[0]); + } +} + +// Initialize time filter manager +let timeFilterManager; + +document.addEventListener('DOMContentLoaded', function() { + timeFilterManager = new TimeFilterManager(); + + // Make it globally available + window.timeFilterManager = timeFilterManager; +}); + +export { TimeFilterManager }; \ No newline at end of file diff --git a/resources/views/auth/confirm-password.blade.php b/resources/views/auth/confirm-password.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..3cbbe083e36e146245feca9919da47f9d6c09664 --- /dev/null +++ b/resources/views/auth/confirm-password.blade.php @@ -0,0 +1,27 @@ + +
+ {{ __('This is a secure area of the application. Please confirm your password before continuing.') }} +
+ +
+ @csrf + + +
+ + + + + +
+ +
+ + {{ __('Confirm') }} + +
+
+
diff --git a/resources/views/auth/forgot-password.blade.php b/resources/views/auth/forgot-password.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..3c707887bca56d0904cd7d956aba0144b929bc2f --- /dev/null +++ b/resources/views/auth/forgot-password.blade.php @@ -0,0 +1,25 @@ + +
+ {{ __('Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.') }} +
+ + + + +
+ @csrf + + +
+ + + +
+ +
+ + {{ __('Email Password Reset Link') }} + +
+
+
diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..80e1b392d212b9d2fd16de77e332d83491dea908 --- /dev/null +++ b/resources/views/auth/login.blade.php @@ -0,0 +1,47 @@ + + + + +
+ @csrf + + +
+ + + +
+ + +
+ + + + + +
+ + +
+ +
+ +
+ @if (Route::has('password.request')) + + {{ __('Forgot your password?') }} + + @endif + + + {{ __('Log in') }} + +
+
+
diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..d4b3d5894e1871fe3e225eb9ae10635e352315c7 --- /dev/null +++ b/resources/views/auth/register.blade.php @@ -0,0 +1,52 @@ + +
+ @csrf + + +
+ + + +
+ + +
+ + + +
+ + +
+ + + + + +
+ + +
+ + + + + +
+ +
+ + {{ __('Already registered?') }} + + + + {{ __('Register') }} + +
+
+
diff --git a/resources/views/auth/reset-password.blade.php b/resources/views/auth/reset-password.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..a6494ccaaed54768a9d2b7cb5ea78503bbe59450 --- /dev/null +++ b/resources/views/auth/reset-password.blade.php @@ -0,0 +1,39 @@ + +
+ @csrf + + + + + +
+ + + +
+ + +
+ + + +
+ + +
+ + + + + +
+ +
+ + {{ __('Reset Password') }} + +
+
+
diff --git a/resources/views/auth/verify-email.blade.php b/resources/views/auth/verify-email.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..4e4222f4a4c83bcf2c9aa6fc508c3c8f2433b425 --- /dev/null +++ b/resources/views/auth/verify-email.blade.php @@ -0,0 +1,31 @@ + +
+ {{ __('Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }} +
+ + @if (session('status') == 'verification-link-sent') +
+ {{ __('A new verification link has been sent to the email address you provided during registration.') }} +
+ @endif + +
+
+ @csrf + +
+ + {{ __('Resend Verification Email') }} + +
+
+ +
+ @csrf + + +
+
+
diff --git a/resources/views/cart.blade.php b/resources/views/cart.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..16a80ea484cddb74ab5b37add0d032be9e74e69a --- /dev/null +++ b/resources/views/cart.blade.php @@ -0,0 +1,336 @@ + + +
+
+

Shopping Cart

+

Review your items and proceed to checkout

+
+
+ @if (!empty($cart)) +
+
{{ count($cart) }} {{ count($cart) === 1 ? 'Item' : 'Items' }}
+
in your cart
+
+ @endif +
+
+
+ +
+
+ @if (session('success')) +
+
+ + {{ session('success') }} +
+
+ @endif + + @if (empty($cart)) +
+
+
+ +
+

Your Cart is Empty

+

Looks like you haven't added any items to your cart yet. Start shopping to fill it up!

+ + Continue Shopping + +
+
+ @else +
+ +
+
+

+ Cart Items +

+ + @php $total = 0; @endphp + @foreach ($cart as $item) + @php $subtotal = $item['price'] * $item['quantity']; $total += $subtotal; @endphp + +
+
+ {{ $item['name'] }} +
+ +
+

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

+ + @if(isset($item['game']) && $item['game']) + @php + // Get custom game styling if it exists + $customGame = \App\Models\CustomGame::where('name', $item['game'])->first(); + + if ($customGame) { + // Extract color from gradient for custom games + $gradientParts = explode(' ', $customGame->color_gradient); + $primaryColor = str_replace(['from-', '-500'], '', $gradientParts[0] ?? 'purple'); + + // Map to badge classes + $badgeClasses = [ + 'red' => 'bg-red-500/20 text-red-300 border-red-500/30', + 'pink' => 'bg-pink-500/20 text-pink-300 border-pink-500/30', + 'rose' => 'bg-rose-500/20 text-rose-300 border-rose-500/30', + 'purple' => 'bg-purple-500/20 text-purple-300 border-purple-500/30', + 'blue' => 'bg-blue-500/20 text-blue-300 border-blue-500/30', + 'green' => 'bg-green-500/20 text-green-300 border-green-500/30', + 'orange' => 'bg-orange-500/20 text-orange-300 border-orange-500/30', + 'yellow' => 'bg-yellow-500/20 text-yellow-300 border-yellow-500/30', + 'teal' => 'bg-teal-500/20 text-teal-300 border-teal-500/30', + 'cyan' => 'bg-cyan-500/20 text-cyan-300 border-cyan-500/30', + 'indigo' => 'bg-indigo-500/20 text-indigo-300 border-indigo-500/30', + 'violet' => 'bg-violet-500/20 text-violet-300 border-violet-500/30', + 'emerald' => 'bg-emerald-500/20 text-emerald-300 border-emerald-500/30' + ]; + + $badgeClass = $badgeClasses[$primaryColor] ?? 'bg-purple-500/20 text-purple-300 border-purple-500/30'; + $gameIcon = $customGame->icon; + } else { + // Default styling for main games + $gameIcons = [ + 'Genshin' => 'fas fa-star', + 'Starrail' => 'fas fa-rocket', + 'WutheringWave' => 'fas fa-wave-square', + 'ZenlessZoneZero' => 'fas fa-city', + 'Arknights' => 'fas fa-chess-knight', + 'AzurLane' => 'fas fa-ship' + ]; + + $gameColors = [ + 'Genshin' => 'bg-yellow-500/20 text-yellow-300 border-yellow-500/30', + 'Starrail' => 'bg-purple-500/20 text-purple-300 border-purple-500/30', + 'WutheringWave' => 'bg-cyan-500/20 text-cyan-300 border-cyan-500/30', + 'ZenlessZoneZero' => 'bg-red-500/20 text-red-300 border-red-500/30', + 'Arknights' => 'bg-indigo-500/20 text-indigo-300 border-indigo-500/30', + 'AzurLane' => 'bg-blue-500/20 text-blue-300 border-blue-500/30' + ]; + + $badgeClass = $gameColors[$item['game']] ?? 'bg-gray-500/20 text-gray-300 border-gray-500/30'; + $gameIcon = $gameIcons[$item['game']] ?? 'fas fa-gamepad'; + } + @endphp + +
+ + {{ $item['game'] }} + +
+ @endif + +
+ + Quantity: {{ $item['quantity'] }} + + + Unit Price: ฿{{ number_format($item['price'], 2) }} + +
+
+ +
+
+ ฿{{ number_format($subtotal, 2) }} +
+
+ @csrf + +
+
+
+ @endforeach +
+
+ + +
+ +
+

+ Order Summary +

+ +
+
+ Subtotal ({{ count($cart) }} items) + ฿{{ number_format($total, 2) }} +
+
+ Shipping + Free +
+
+ Tax + ฿0.00 +
+
+
+ Total + ฿{{ number_format($total, 2) }} +
+
+
+
+ + +
+

+ Checkout Information +

+ +
+ @csrf + +
+ + +
+ +
+ + +
+ +
+ +
+
+ +
+ + or drag and drop +
+

PNG, JPG, GIF up to 10MB

+
+ + +
+
+ + +
+
+ + + +
+
+ @endif +
+
+ + +
\ No newline at end of file diff --git a/resources/views/categories.blade.php b/resources/views/categories.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..301711dcd98fb79627e1af72f3b9068a1ed9831e --- /dev/null +++ b/resources/views/categories.blade.php @@ -0,0 +1,1268 @@ + + +
+ +
+ +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+ +
+
+ + Premium Gaming Store +
+
+
+ + +

+ Gaming +
+ Store +

+ + +

+ Discover premium digital content for your favorite games. From exclusive accounts to rare items, elevate your gaming experience. +

+ + + @php + $totalCategories = \App\Models\Product::select('game')->distinct()->whereNotNull('game')->count(); + $totalItemsSold = \App\Models\Order::count(); + $totalUsers = \App\Models\User::count(); + @endphp +
+
+
0
+
Games
+
+
+
0
+
Items Sold
+
+
+
0
+
Users
+
+
+ + +
+ + +
+
+
+
+ + +
+
+ Scroll to explore + +
+
+
+ + +
+
+
+
+ + Browse Categories +
+

Choose Your Game

+

Select a category to browse available items

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

Genshin Impact

+ +
+ + + {{ $categoryCounts['Genshin'] ?? 0 }} Items + + + + Popular + +
+
+
+
+ + +
+
+
+ + +
+
+
+
+ +
+
+ +
+

Honkai: Star Rail

+ +
+ + + {{ $categoryCounts['Starrail'] ?? 0 }} Items + + + + Featured + +
+
+
+
+ + +
+
+
+ + +
+
+
+
+ +
+
+ +
+

Wuthering Waves

+ +
+ + + {{ $categoryCounts['WutheringWave'] ?? 0 }} Items + + + + New + +
+
+
+
+
+ + +
+
+
+
+ + +
+
+ +
+
+ +
+

Show More Categories

+ +
+ + + 5+ More Games + + + + Discover + +
+
+
+
+
+ + + + + +
+
+ +
+ +

+ + Secure transactions • Instant delivery • 24/7 support +

+
+
+
+ + +
+
+
+
+ + Why Choose Us +
+

Why Choose Us

+

Quality products with reliable service

+
+ +
+
+
+ +
+

Instant Delivery

+

Get your products instantly after purchase

+
+ +
+
+ +
+

Secure Payment

+

Protected transactions with secure encryption

+
+ +
+
+ +
+

24/7 Support

+

Customer support available anytime

+
+ +
+
+ +
+

Premium Quality

+

High quality accounts and items, verified by experts

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

Category Products

+

+ + Loading products... +

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

Loading Products

+

Fetching the latest items for you...

+
+ + + +
+
+
+
+ + + + + + + + +
\ No newline at end of file diff --git a/resources/views/category_products.blade.php b/resources/views/category_products.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..c6c4b628f016429a929849e64db59c9d15eaf522 --- /dev/null +++ b/resources/views/category_products.blade.php @@ -0,0 +1,487 @@ + +
+ +
+ +
+
+
+
+ +
+ + + + +
+
+ + {{ ucfirst($category) }} Collection +
+

+ {{ ucfirst($category) }} + Store +

+

+ Professional gaming products and premium digital content for {{ $category }} enthusiasts +

+
+ + +
+
+ + {{ $products->count() }} products available in {{ ucfirst($category) }} +
+
+
+
+ + +
+
+ + @if($products->count() > 0) + +
+

Premium {{ ucfirst($category) }} Products

+

Carefully curated selection of high-quality digital products

+
+ + +
+ @foreach($products as $product) +
+
+ @if($product->image) + {{ $product->name }} + @else +
+ +
+ @endif + + +
+ + +
+ @if($product->Amount === 0) + + Sold Out + + @elseif($product->Amount <= 5) + + {{ $product->Amount }} Left + + @else + + Available + + @endif +
+ + +
+
+ + +
+
+
+ +
+
+

{{ $product->name }}

+

{{ $product->description }}

+
+ +
+
฿{{ number_format($product->price, 2) }}
+
+ Stock: {{ $product->Amount }} +
+
+ + + @php + // Sales count - only count completed orders that contain this product + $salesCount = \App\Models\Order::where('cart_data', 'LIKE', '%"product_id":' . $product->id . '%') + ->where('status', 'completed') + ->count(); + @endphp +
+
+ {{ $salesCount }} Sold +
+
+ +
+ + Details + + + @if($product->Amount > 0) +
+ @csrf + +
+ @else + + @endif +
+
+
+ @endforeach +
+ @else + +
+
+ +
+

No Products Available

+

This category is currently empty. New products will be added soon.

+ + Browse Categories + +
+ @endif + + + +
+
+
+ + + + + + + + +
\ No newline at end of file diff --git a/resources/views/components/application-logo.blade.php b/resources/views/components/application-logo.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..46579cf07dfe96e2adee727bf7a2e04c02f0ff13 --- /dev/null +++ b/resources/views/components/application-logo.blade.php @@ -0,0 +1,3 @@ + + + diff --git a/resources/views/components/auth-session-status.blade.php b/resources/views/components/auth-session-status.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..a39bc7d2e8d36ee9174ce7932d14b911e3f6d07e --- /dev/null +++ b/resources/views/components/auth-session-status.blade.php @@ -0,0 +1,7 @@ +@props(['status']) + +@if ($status) +
merge(['class' => 'font-medium text-sm text-green-600 dark:text-green-400']) }}> + {{ $status }} +
+@endif diff --git a/resources/views/components/danger-button.blade.php b/resources/views/components/danger-button.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..d7417b21091ef4d60095196fabab4481f0eb7dae --- /dev/null +++ b/resources/views/components/danger-button.blade.php @@ -0,0 +1,3 @@ + diff --git a/resources/views/components/dropdown-link.blade.php b/resources/views/components/dropdown-link.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..57536156f8a23dc3d5c5f46afe36cd2e70b11373 --- /dev/null +++ b/resources/views/components/dropdown-link.blade.php @@ -0,0 +1 @@ +merge(['class' => 'block w-full px-4 py-3 text-start text-sm leading-5 text-white hover:bg-white/10 focus:outline-none focus:bg-white/10 transition duration-200 ease-in-out rounded-lg']) }}>{{ $slot }} diff --git a/resources/views/components/dropdown.blade.php b/resources/views/components/dropdown.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..cc458ef16f4d07d2dbed194b2d74688d1244b1c4 --- /dev/null +++ b/resources/views/components/dropdown.blade.php @@ -0,0 +1,35 @@ +@props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white dark:bg-gray-700']) + +@php +$alignmentClasses = match ($align) { + 'left' => 'ltr:origin-top-left rtl:origin-top-right start-0', + 'top' => 'origin-top', + default => 'ltr:origin-top-right rtl:origin-top-left end-0', +}; + +$width = match ($width) { + '48' => 'w-48', + default => $width, +}; +@endphp + +
+
+ {{ $trigger }} +
+ + +
\ No newline at end of file diff --git a/resources/views/components/input-error.blade.php b/resources/views/components/input-error.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..ad95f6b57be622d7cffbfea531cded0107f64514 --- /dev/null +++ b/resources/views/components/input-error.blade.php @@ -0,0 +1,9 @@ +@props(['messages']) + +@if ($messages) +
    merge(['class' => 'text-sm text-red-600 dark:text-red-400 space-y-1']) }}> + @foreach ((array) $messages as $message) +
  • {{ $message }}
  • + @endforeach +
+@endif diff --git a/resources/views/components/input-label.blade.php b/resources/views/components/input-label.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..e93b059acb00d1a046cc314888ca0b5ed596abe6 --- /dev/null +++ b/resources/views/components/input-label.blade.php @@ -0,0 +1,5 @@ +@props(['value']) + + diff --git a/resources/views/components/loading-screen.blade.php b/resources/views/components/loading-screen.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..a847d3709ce7b9f7b21c2ec72f49932a181fd2a5 --- /dev/null +++ b/resources/views/components/loading-screen.blade.php @@ -0,0 +1,156 @@ + +
+ +
+
+
+
+
+ + +
+ +
+
+ +
+

GameStore

+

Premium Gaming Experience

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

Loading your gaming experience...

+
+
+
+
+ + +
+
+
+ +
+ Secure connection established +
+
+
+ +
+ Loading game catalog +
+
+
+ +
+ Preparing your profile +
+
+
+
+ + + + \ No newline at end of file diff --git a/resources/views/components/metric-card.blade.php b/resources/views/components/metric-card.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..ef57aa876186d1252a2e82012b5b77a8da936dd2 --- /dev/null +++ b/resources/views/components/metric-card.blade.php @@ -0,0 +1,131 @@ +@props([ + 'title', + 'value', + 'previousValue' => 0, + 'icon', + 'color' => 'emerald', + 'prefix' => '', + 'suffix' => '', + 'showTrend' => true, + 'loading' => false +]) + +@php + $percentageChange = $previousValue > 0 ? (($value - $previousValue) / $previousValue) * 100 : 0; + $isPositive = $percentageChange >= 0; + + $colorClasses = [ + 'emerald' => [ + 'text' => 'text-emerald-400', + 'bg' => 'from-emerald-400/20 to-emerald-600/10', + 'glow' => 'rgba(52, 211, 153, 0.3)' + ], + 'purple' => [ + 'text' => 'text-purple-400', + 'bg' => 'from-purple-400/20 to-purple-600/10', + 'glow' => 'rgba(168, 85, 247, 0.3)' + ], + 'blue' => [ + 'text' => 'text-blue-400', + 'bg' => 'from-blue-400/20 to-blue-600/10', + 'glow' => 'rgba(59, 130, 246, 0.3)' + ], + 'orange' => [ + 'text' => 'text-orange-400', + 'bg' => 'from-orange-400/20 to-orange-600/10', + 'glow' => 'rgba(251, 146, 60, 0.3)' + ] + ]; + + $currentColor = $colorClasses[$color] ?? $colorClasses['emerald']; +@endphp + +
+ + +
+ + +
+ +
+
+ @if($icon) +
+ +
+ @endif +

{{ $title }}

+
+ + @if($showTrend && !$loading) +
+ + {{ $isPositive ? '↗' : '↘' }} + + + {{ number_format(abs($percentageChange), 1) }}% + +
+ @endif +
+ + +
+ @if($loading) +
+
+
+
+ @else +
+ {{ $prefix }}{{ number_format($value) }}{{ $suffix }} +
+ + @if($previousValue > 0) +

+ Previous: {{ $prefix }}{{ number_format($previousValue) }}{{ $suffix }} +

+ @endif + @endif +
+ + + @if(isset($slot) && !empty(trim($slot))) +
+ {{ $slot }} +
+ @endif +
+
+ +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/components/modal.blade.php b/resources/views/components/modal.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..384662a1f18422428b0411265511c9bfc359e61b --- /dev/null +++ b/resources/views/components/modal.blade.php @@ -0,0 +1,78 @@ +@props([ + 'name', + 'show' => false, + 'maxWidth' => '2xl' +]) + +@php +$maxWidth = [ + 'sm' => 'sm:max-w-sm', + 'md' => 'sm:max-w-md', + 'lg' => 'sm:max-w-lg', + 'xl' => 'sm:max-w-xl', + '2xl' => 'sm:max-w-2xl', +][$maxWidth]; +@endphp + +
+
+
+
+ +
+ {{ $slot }} +
+
diff --git a/resources/views/components/nav-link.blade.php b/resources/views/components/nav-link.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..37bad5542dbc74675476288a8dd6530b92a7b722 --- /dev/null +++ b/resources/views/components/nav-link.blade.php @@ -0,0 +1,11 @@ +@props(['active']) + +@php +$classes = ($active ?? false) + ? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 dark:border-indigo-600 text-sm font-medium leading-5 text-gray-900 dark:text-gray-100 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out' + : 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hover:border-gray-300 dark:hover:border-gray-700 focus:outline-none focus:text-gray-700 dark:focus:text-gray-300 focus:border-gray-300 dark:focus:border-gray-700 transition duration-150 ease-in-out'; +@endphp + +merge(['class' => $classes]) }}> + {{ $slot }} + diff --git a/resources/views/components/primary-button.blade.php b/resources/views/components/primary-button.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..99bf38907e9c3049bcf2e3abace696eb275aa0cc --- /dev/null +++ b/resources/views/components/primary-button.blade.php @@ -0,0 +1,3 @@ + diff --git a/resources/views/components/responsive-nav-link.blade.php b/resources/views/components/responsive-nav-link.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..7d29fe0346148dbcba8cb7ddc9b87b34f6688f28 --- /dev/null +++ b/resources/views/components/responsive-nav-link.blade.php @@ -0,0 +1,11 @@ +@props(['active']) + +@php +$classes = ($active ?? false) + ? 'block w-full px-4 py-3 mx-2 my-1 rounded-lg text-start text-base font-medium text-white bg-purple-500/30 border border-purple-400/30 focus:outline-none transition duration-200 ease-in-out' + : 'block w-full px-4 py-3 mx-2 my-1 rounded-lg text-start text-base font-medium text-white/80 hover:text-white hover:bg-white/10 focus:outline-none focus:text-white focus:bg-white/10 transition duration-200 ease-in-out'; +@endphp + +merge(['class' => $classes]) }}> + {{ $slot }} + \ No newline at end of file diff --git a/resources/views/components/secondary-button.blade.php b/resources/views/components/secondary-button.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..fa1c54918c14bd4f55f17be494d8009d42e27b6c --- /dev/null +++ b/resources/views/components/secondary-button.blade.php @@ -0,0 +1,3 @@ + diff --git a/resources/views/components/text-input.blade.php b/resources/views/components/text-input.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..3f44b2f74bc993c7f0f04e86063085362f4a8c68 --- /dev/null +++ b/resources/views/components/text-input.blade.php @@ -0,0 +1,3 @@ +@props(['disabled' => false]) + +merge(['class' => 'border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm']) }}> diff --git a/resources/views/components/time-filter.blade.php b/resources/views/components/time-filter.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..44fc87be694ff20753720d81d8d13f3c97aa9a63 --- /dev/null +++ b/resources/views/components/time-filter.blade.php @@ -0,0 +1,141 @@ +@props([ + 'periods' => ['daily', 'weekly', 'monthly'], + 'default' => 'daily', + 'onPeriodChange' => null +]) + +
+ +
+ @foreach($periods as $period) + + @endforeach +
+ + +
+ +
+ + +
+
+
+ + + + \ No newline at end of file diff --git a/resources/views/customer-orders.blade.php b/resources/views/customer-orders.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..8f6e10694ef2dc590cf7174509e1cb69032b6b04 --- /dev/null +++ b/resources/views/customer-orders.blade.php @@ -0,0 +1,1951 @@ + +
+ +
+
+
+
+
+
+ + +
+ @for ($i = 0; $i < 12; $i++) +
+ @endfor +
+ +
+
+ +
+
+ +
+

+ Order History +

+

+ Your gaming journey through time +

+
+ + @if($orders->isEmpty()) + +
+
+
+ +
+

No Orders Yet

+

Your gaming adventure awaits!

+ + Start Your Journey + +
+
+ @else + +
+
+
+ + Filter Orders +
+

Find Your Orders

+

Click on categories to filter your order history

+
+ + +
+ @php + // Initialize game stats first + $gameStats = []; + foreach ($orders as $order) { + $items = json_decode($order->cart_data, true); + if (is_array($items)) { + foreach ($items as $item) { + $product = \App\Models\Product::find($item['id'] ?? null); + if ($product && $product->game) { + $gameStats[$product->game] = ($gameStats[$product->game] ?? 0) + 1; + } + } + } + } + + $totalCategories = count($gameStats) + 1; // +1 for "All Orders" + @endphp + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+
+ +

All Categories

+ +
+ + + {{ $totalCategories }} + Categories + + + + Browse + +
+
+
+
+ + + + +
+
+ + Recently + (All Orders) +
+
+ + Pending + ({{ $orders->where('status', 'pending')->count() }}) +
+
+ + Completed + ({{ $orders->where('status', 'completed')->count() }}) +
+
+ + Cancelled + ({{ $orders->where('status', 'cancelled')->count() }}) +
+
+
+ + +
+ @foreach ($orders as $index => $order) + @php + $items = json_decode($order->cart_data, true); + $orderDate = $order->created_at; + + // Get primary game + $primaryGame = ''; + $gameColor = '#8b5cf6'; + if (is_array($items) && count($items) > 0) { + $gameProducts = []; + foreach ($items as $item) { + $product = \App\Models\Product::find($item['id'] ?? null); + if ($product && $product->game) { + $gameProducts[] = $product->game; + } + } + if (!empty($gameProducts)) { + $primaryGame = array_count_values($gameProducts); + $primaryGame = array_key_first($primaryGame); + + // Set game colors + $gameColors = [ + 'Genshin' => '#f59e0b', + 'Starrail' => '#ec4899', + 'WutheringWave' => '#06b6d4' + ]; + $gameColor = $gameColors[$primaryGame] ?? '#8b5cf6'; + } + } + + $animationDelay = ($index % 9) * 0.1; + @endphp + +
+ + +
+ +
+
+
+ +
+
+

+ #{{ str_pad($order->id, 4, '0', STR_PAD_LEFT) }} +

+

{{ $order->created_at->format('M j, Y') }}

+
+
+ + +
+ @if($order->status === 'completed') + Completed + @elseif($order->status === 'cancelled') + Cancelled + @else + Pending + @endif +
+
+ + + @if($primaryGame) +
+ {{ $primaryGame }} +
+ @endif + + +
+
+
+
+ +
+ {{ $order->customer_name }} +
+
+
+ +
+ {{ $order->phone }} +
+
+
+ + + @if (is_array($items) && count($items) > 0) + @php + $totalQuantity = 0; + foreach ($items as $item) { + $totalQuantity += $item['quantity'] ?? 1; + } + @endphp +
+
+ +
+ Quantity: {{ $totalQuantity }} +
+ @endif + + +
+
+ ฿{{ number_format($order->total_amount ?? 0, 2) }} +
+
{{ $order->created_at->diffForHumans() }}
+ + +
+ @if ($order->payment_slip_path) + + @endif + + + Details + +
+
+
+
+ @endforeach +
+ + + + + + + + + + + + + + + @endif +
+
+
+ + + + + + + + + + +
\ No newline at end of file diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..18dbf22f3258da557f158e6a97065b65a3a58ec3 --- /dev/null +++ b/resources/views/dashboard.blade.php @@ -0,0 +1,1118 @@ + + +
+
+

Analytics Dashboard

+

Real-time insights and performance metrics

+
+
+
{{ date('l, F j, Y') }}
+
{{ date('g:i A') }}
+
+
+
+ +
+
+ +
+
+

Performance Overview

+

Track your store's key metrics and trends

+
+
+
+
+ Real-time +
+ +
+
+ + + +
+
+
+
+ + +
+ +
+
+
+
+ +
+

Today's Revenue

+
+
+
฿0
+

Previous: ฿0

+
+ + +
+
+
+
+ +
+

Orders Today

+
+
+
0
+

Previous: 0

+
+ + +
+
+
+
+ +
+

Customers Today

+
+
+
0
+

Previous: 0

+
+ + +
+
+
+
+ +
+

Total Products

+
+
+
0
+

Active products

+
+
+ + +
+ +
+
+

Revenue Trend

+
+
+ Revenue + +
+
+
+ +
+
+ + +
+
+

Top Products

+
+ By Revenue + +
+
+
+ +
+
+
+ + + + + + +
+
+ + + + + + + + +
\ No newline at end of file diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..2ff2630249bbe786ffd4ba747e92b0e5cb8150ff --- /dev/null +++ b/resources/views/layouts/app.blade.php @@ -0,0 +1,131 @@ + + + + + + + + {{ config('app.name', 'Laravel') }} + + + + + + + + + + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + + + + + + + + + +
+
+
+
+ @include('layouts.navigation') + + + + + + @isset($header) +
+
+ {{ $header }} +
+
+ @endisset + + +
+ @yield('content') + {{ $slot ?? '' }} +
+ + +
+
+
+
+

{{ config('app.name', 'Laravel') }}

+

Your premium gaming store for the best digital products and services.

+ +
+
+

Quick Links

+ +
+
+

Contact

+
    +
  • support@example.com
  • +
  • +66 123 456 789
  • +
  • Bangkok, Thailand
  • +
+
+
+
+

© {{ date('Y') }} {{ config('app.name', 'Laravel') }}. All rights reserved.

+
+
+
+
+ + @stack('scripts') + + diff --git a/resources/views/layouts/guest.blade.php b/resources/views/layouts/guest.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..7d3226c6f915cc13088c9da989e1e97da352f1f8 --- /dev/null +++ b/resources/views/layouts/guest.blade.php @@ -0,0 +1,41 @@ + + + + + + + + {{ config('app.name', 'Laravel') }} + + + + + + + + + + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + + +
+
+
+ +
+ + +
+ {{ $slot }} +
+
+ + diff --git a/resources/views/layouts/navigation.blade.php b/resources/views/layouts/navigation.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..3d907931178f3527b46efa6313e8a6acecc6fbad --- /dev/null +++ b/resources/views/layouts/navigation.blade.php @@ -0,0 +1,1190 @@ + + + +
+ + +
+ + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+
+ +
+
+ +
+ +
+
+
+

Luna Console

+

Administrative Terminal

+
+
+ +
+
+ + +
+ +
+ + +
+
+ $ + +
+
+ + +
+
+ + Processing... +
+
+
+
+
+
\ No newline at end of file diff --git a/resources/views/order-detail.blade.php b/resources/views/order-detail.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..e38f283d3e54381c0923cd1063060e95ad16697e --- /dev/null +++ b/resources/views/order-detail.blade.php @@ -0,0 +1,394 @@ + + +
+
+ + Back to Orders + +
+

Order Details

+

Order #{{ str_pad($order->id, 4, '0', STR_PAD_LEFT) }}

+
+
+
+
+ ฿{{ number_format($order->total_amount ?? 0, 2) }} +
+
Total Amount
+
+
+
+ +
+
+ @if (session('success')) +
+
+ + {{ session('success') }} +
+
+ @endif + + @if (session('error')) +
+
+ + {{ session('error') }} +
+
+ @endif + +
+ +
+ +
+
+
+
+ +
+
+

Order #{{ str_pad($order->id, 4, '0', STR_PAD_LEFT) }}

+

{{ $order->created_at->format('M j, Y g:i A') }}

+
+
+ + + + @if($order->status === 'completed') + Completed + @elseif($order->status === 'cancelled') + Cancelled + @else + Pending + @endif + +
+ + +
+
+
+
+ +
+
+
Customer Name
+
{{ $order->customer_name }}
+
+
+
+ +
+
+
+ +
+
+
Phone Number
+
{{ $order->phone }}
+
+
+
+
+
+ + + @php + $items = json_decode($order->cart_data, true); + @endphp + + @if (is_array($items)) +
+

+ Order Items + + {{ count($items) }} {{ count($items) === 1 ? 'item' : 'items' }} + +

+ +
+ @foreach ($items as $item) + @php + $product = \App\Models\Product::find($item['id'] ?? null); + @endphp +
+
+ +
+ @if($product && $product->image) + {{ $item['name'] }} + @else +
+ +
+ @endif +
+ + +
+
+
+

{{ $item['name'] }}

+ + + @if($product && $product->game) +
+ + @switch($product->game) + @case('Genshin') + Genshin Impact + @break + @case('Starrail') + Honkai: Star Rail + @break + @case('WutheringWave') + Wuthering Waves + @break + @default + {{ $product->game }} + @endswitch + +
+ @endif + + +
+ + + Quantity: {{ $item['quantity'] }} + + + + Unit Price: ฿{{ number_format($item['price'] ?? 0, 2) }} + +
+
+ + +
+
+ ฿{{ number_format(($item['price'] ?? 0) * ($item['quantity'] ?? 1), 2) }} +
+
Subtotal
+
+
+
+
+
+ @endforeach +
+
+ @endif +
+ + +
+ +
+

+ Payment Information +

+ + @if ($order->payment_slip_path) +
+
+ Payment slip +
+ +
+ @else +
+
+ +
+

No payment slip uploaded

+
+ @endif +
+ + +
+

+ Order Summary +

+ +
+ @if (is_array($items)) + @foreach ($items as $item) +
+ {{ $item['name'] }} × {{ $item['quantity'] }} + ฿{{ number_format(($item['price'] ?? 0) * ($item['quantity'] ?? 1), 2) }} +
+ @endforeach +
+
+ Total + + ฿{{ number_format($order->total_amount ?? 0, 2) }} + +
+
+ @endif +
+
+ + +
+

+ Actions +

+ +
+
+ @csrf + @method('DELETE') + +
+
+
+ + +
+

+ Order Timeline +

+ +
+
+
+ +
+
+
Order Created
+
{{ $order->created_at->format('M j, Y g:i A') }}
+
+
+ + @if($order->updated_at != $order->created_at) +
+
+ +
+
+
Last Updated
+
{{ $order->updated_at->format('M j, Y g:i A') }}
+
+
+ @endif +
+
+
+
+
+
+ + + + + + + +
\ No newline at end of file diff --git a/resources/views/order.blade.php b/resources/views/order.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..bd569c7f0eced6ce6c02b5b64d4a2d0ffd0b392d --- /dev/null +++ b/resources/views/order.blade.php @@ -0,0 +1,724 @@ + + +
+
+

Order Management

+

Manage all customer orders and transactions

+
+
+
{{ $orders->count() }}
+
Total Orders
+
+
+
+ +
+
+ @if (session('success')) +
+
+ + {{ session('success') }} +
+
+ @endif + + @if (session('error')) +
+
+ + {{ session('error') }} +
+
+ @endif + + +
+
+
+

+ All Orders +

+
+ {{ $orders->count() }} {{ $orders->count() === 1 ? 'order' : 'orders' }} total +
+
+ +
+ +
+ +
+ +
+
+ + + + + + +
+
+
+ + @if($orders->isEmpty()) +
+
+
+ +
+

No Orders Yet

+

No customer orders have been placed yet. Orders will appear here once customers start purchasing.

+ +
+
+ @else + +
+ @foreach ($orders as $order) + @php + $items = json_decode($order->cart_data, true); + $orderDate = $order->created_at; + @endphp + +
+ +
+ +
+
+ +
+ +
+ +
+

+ Order #{{ str_pad($order->id, 4, '0', STR_PAD_LEFT) }} +

+ + +
+ + @if($order->status === 'completed') + ACCEPTED + @elseif($order->status === 'cancelled') + REJECTED + @else + PENDING + @endif + + + + {{ $order->created_at->diffForHumans() }} + +
+
+
+ + +
+
Total Amount
+
+ ฿{{ number_format($order->total_amount ?? 0, 2) }} +
+
+
+ + +
+
+
+
+ +
+
+
Customer
+
{{ $order->customer_name }}
+
+
+
+ +
+
+
+ +
+
+
Phone
+
{{ $order->phone }}
+
+
+
+ +
+
+
+ +
+
+
Order Date
+
+ {{ $order->created_at->format('M j, Y') }} +
+
+ {{ $order->created_at->format('g:i A') }} +
+
+
+
+ +
+
+
+ +
+
+
Payment Slip
+ @if ($order->payment_slip_path) + + @else + No slip uploaded + @endif +
+
+
+
+ + + @if (is_array($items)) +
+

+ Order Items + + {{ count($items) }} {{ count($items) === 1 ? 'item' : 'items' }} + +

+
+ @foreach ($items as $item) + @php + // Get product details from database + $product = \App\Models\Product::find($item['id'] ?? null); + @endphp +
+
+ +
+ @if($product && $product->image) + {{ $item['name'] }} + @else +
+ +
+ @endif +
+ + +
+
+
+
+ {{ $item['name'] }} +
+ + + @if($product && $product->game) +
+ + @switch($product->game) + @case('Genshin') + Genshin Impact + @break + @case('Starrail') + Honkai: Star Rail + @break + @case('WutheringWave') + Wuthering Waves + @break + @default + {{ $product->game }} + @endswitch + +
+ @endif + + +
+ + + Qty: {{ $item['quantity'] }} + + + + ฿{{ number_format($item['price'] ?? 0, 2) }} each + +
+
+ + +
+
+ ฿{{ number_format(($item['price'] ?? 0) * ($item['quantity'] ?? 1), 2) }} +
+
Total
+
+
+
+
+
+ @endforeach +
+ + +
+
+
+ + Order Total +
+
+ ฿{{ number_format($order->total_amount ?? 0, 2) }} +
+
+
+
+ @endif +
+ + +
+
+
+ + Order created {{ $order->created_at->diffForHumans() }} +
+
+ +
+ + View Details + + + @if($order->status === 'pending') +
+ @csrf + @method('PATCH') + +
+ +
+ @csrf + @method('PATCH') + +
+ @elseif($order->status === 'completed') + + ACCEPTED + + @elseif($order->status === 'cancelled') + + REJECTED + + @endif +
+
+
+
+ @endforeach +
+ + + + @endif +
+ + + + +
+ + + + \ No newline at end of file diff --git a/resources/views/profile/edit.blade.php b/resources/views/profile/edit.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..ef699107f7236bf602c57209a43d35ca4338305c --- /dev/null +++ b/resources/views/profile/edit.blade.php @@ -0,0 +1,29 @@ + + +

+ {{ __('Profile') }} +

+
+ +
+
+
+
+ @include('profile.partials.update-profile-information-form') +
+
+ +
+
+ @include('profile.partials.update-password-form') +
+
+ +
+
+ @include('profile.partials.delete-user-form') +
+
+
+
+
diff --git a/resources/views/profile/partials/delete-user-form.blade.php b/resources/views/profile/partials/delete-user-form.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..b3a638205cc58a6b94ca489090bcecb60e057ac8 --- /dev/null +++ b/resources/views/profile/partials/delete-user-form.blade.php @@ -0,0 +1,55 @@ +
+
+

+ {{ __('Delete Account') }} +

+ +

+ {{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.') }} +

+
+ + {{ __('Delete Account') }} + + +
+ @csrf + @method('delete') + +

+ {{ __('Are you sure you want to delete your account?') }} +

+ +

+ {{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.') }} +

+ +
+ + + + + +
+ +
+ + {{ __('Cancel') }} + + + + {{ __('Delete Account') }} + +
+
+
+
diff --git a/resources/views/profile/partials/update-password-form.blade.php b/resources/views/profile/partials/update-password-form.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..acd200d737ad00a3317ac097ac8d6a381cba0454 --- /dev/null +++ b/resources/views/profile/partials/update-password-form.blade.php @@ -0,0 +1,48 @@ +
+
+

+ {{ __('Update Password') }} +

+ +

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

+
+ +
+ @csrf + @method('put') + +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ {{ __('Save') }} + + @if (session('status') === 'password-updated') +

{{ __('Saved.') }}

+ @endif +
+
+
diff --git a/resources/views/profile/partials/update-profile-information-form.blade.php b/resources/views/profile/partials/update-profile-information-form.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..7273fff3993402a824f8c9bf733404e7c57d2d69 --- /dev/null +++ b/resources/views/profile/partials/update-profile-information-form.blade.php @@ -0,0 +1,64 @@ +
+
+

+ {{ __('Profile Information') }} +

+ +

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

+
+ +
+ @csrf +
+ +
+ @csrf + @method('patch') + +
+ + + +
+ +
+ + + + + @if ($user instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && ! $user->hasVerifiedEmail()) +
+

+ {{ __('Your email address is unverified.') }} + + +

+ + @if (session('status') === 'verification-link-sent') +

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

+ @endif +
+ @endif +
+ +
+ {{ __('Save') }} + + @if (session('status') === 'profile-updated') +

{{ __('Saved.') }}

+ @endif +
+
+
diff --git a/resources/views/scroll_reveal_landing.blade.php b/resources/views/scroll_reveal_landing.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..0084c2e4c7697e36e06a67f115ac92fea8a2bda4 --- /dev/null +++ b/resources/views/scroll_reveal_landing.blade.php @@ -0,0 +1,273 @@ +{{-- + scroll_reveal_landing.blade.php + Epic animated hero + horizontal game-card gallery + – $totalProducts & $gameCount injected + – every card glows with unique vibrant color + – last card alignment fixed + – Shop Now → /store?game=slug +--}} + + +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+ @for ($i = 0; $i < 10; $i++) +
+ @endfor +
+ + +
+
+
+ +
+
+ + Premium Gaming Store +
+
+
+ + +

+ Gaming
+ Store +

+ +

+ Discover premium digital content for your favourite games +

+ + +
+
+
0
+
Items Sold
+
+
+
0
+
Games
+
+
+
24/7
+
Support
+
+
+ + +
+ +
+
+
+
+ + +
+ +
+
+ + + @php + /* 1. Core games */ + $coreGames = [ + ['slug'=>'genshin','name'=>'Genshin Impact','desc'=>'Premium accounts & rare items','icon'=>'fas fa-star','color'=>'#fbbf24','glow'=>'rgba(251,191,36,.45)','count'=>$categoryCounts['Genshin']??0], + ['slug'=>'starrail','name'=>'Honkai: Star Rail','desc'=>'Stellar jade & limited characters','icon'=>'fas fa-rocket','color'=>'#a855f7','glow'=>'rgba(168,85,247,.45)','count'=>$categoryCounts['Starrail']??0], + ['slug'=>'wutheringwave','name'=>'Wuthering Waves','desc'=>'Resonators & weapons','icon'=>'fas fa-wave-square','color'=>'#06b6d4','glow'=>'rgba(6,182,212,.45)','count'=>$categoryCounts['WutheringWave']??0], + ]; + + /* 2. Custom games from database */ + $extraGames = []; + if(isset($customGames)) { + foreach ($customGames as $customGame) { + // Convert gradient to hex color for consistency + $gradientParts = explode(' ', $customGame->color_gradient); + $primaryColor = str_replace(['from-', '-500'], '', $gradientParts[0] ?? 'purple'); + + // Map color names to hex values and glow colors + $colorMap = [ + 'red' => ['hex' => '#ef4444', 'glow' => 'rgba(239,68,68,.45)'], + 'pink' => ['hex' => '#ec4899', 'glow' => 'rgba(236,72,153,.45)'], + 'purple' => ['hex' => '#8b5cf6', 'glow' => 'rgba(139,92,246,.45)'], + 'blue' => ['hex' => '#3b82f6', 'glow' => 'rgba(59,130,246,.45)'], + 'green' => ['hex' => '#10b981', 'glow' => 'rgba(16,185,129,.45)'], + 'orange' => ['hex' => '#f97316', 'glow' => 'rgba(249,115,22,.45)'], + 'yellow' => ['hex' => '#eab308', 'glow' => 'rgba(234,179,8,.45)'], + 'teal' => ['hex' => '#14b8a6', 'glow' => 'rgba(20,184,166,.45)'], + 'cyan' => ['hex' => '#06b6d4', 'glow' => 'rgba(6,182,212,.45)'], + 'indigo' => ['hex' => '#6366f1', 'glow' => 'rgba(99,102,241,.45)'], + 'violet' => ['hex' => '#8b5cf6', 'glow' => 'rgba(139,92,246,.45)'], + 'emerald' => ['hex' => '#10b981', 'glow' => 'rgba(16,185,129,.45)'], + 'rose' => ['hex' => '#f43f5e', 'glow' => 'rgba(244,63,94,.45)'] + ]; + + $colorData = $colorMap[$primaryColor] ?? $colorMap['purple']; + $hexColor = $colorData['hex']; + $glowColor = $colorData['glow']; + + $extraGames[] = [ + 'slug' => Illuminate\Support\Str::slug($customGame->name), + 'name' => $customGame->name, + 'desc' => "Exclusive items for {$customGame->name}", + 'icon' => $customGame->icon, + 'color' => $hexColor, + 'glow' => $glowColor, + 'count' => \App\Models\Product::where('game', $customGame->name)->count() + ]; + } + } + $games = array_merge($coreGames, $extraGames); + @endphp + + + + + + +
\ No newline at end of file diff --git a/resources/views/show.blade.php b/resources/views/show.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..49a24b60c9e6fa548bb98b47f99ddc30b3acb239 --- /dev/null +++ b/resources/views/show.blade.php @@ -0,0 +1,918 @@ + +
+ +
+
+
+
+
+ +
+ + + + + + + +
+ +
+
+
+ @if($product->image) + {{ $product->name }} + @else +
+ +
+ @endif + + + + +
+ @if($product->Amount === 0) + + Sold Out + + @elseif($product->Amount <= 5) + + {{ $product->Amount }} Left + + @else + + Available + + @endif +
+
+
+ + +
+
+
+ +
+
+ @php + // Sales count - only count completed orders that contain this product + $salesCount = \App\Models\Order::where('cart_data', 'LIKE', '%"product_id":' . $product->id . '%') + ->where('status', 'completed') + ->count(); + @endphp + 0 +
+

Total Sales

+

People have purchased this item

+
+
+
+ + +
+ +
+
+

+ {{ $product->name }} +

+
+
+
+ Premium Product +
+
+
+
+ + +
+
+
+
+ ฿{{ number_format($product->price, 2) }} +
+

Premium Quality Product

+
+
+ @if($product->Amount === 0) +
+ + Out of Stock +
+ @elseif($product->Amount <= 5) +
+ + Only {{ $product->Amount }} left +
+

Hurry! Limited stock

+ @else +
+ + In Stock +
+

{{ $product->Amount }} available

+ @endif +
+
+
+
+ + +
+
+
+ +
+

Product Description

+
+

{{ $product->description }}

+
+ + + @if($product->Amount > 0) +
+
+
+ +
+

Add to Cart

+
+ +
+ @csrf + + +
+ Quantity: +
+ + 1 + +
+
+ + +
+
+ Total Price: +
+ ฿{{ number_format($product->price, 2) }} +
+
+
+ + + +
+
+ @else +
+
+ +
+

Out of Stock

+

This product is currently unavailable. Please check back later.

+
+ @endif + + +
+
+ + +
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/resources/views/table_product.blade.php b/resources/views/table_product.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..033e7431fa2771219285d6fb21550c4c013ba389 --- /dev/null +++ b/resources/views/table_product.blade.php @@ -0,0 +1,1793 @@ + + +
+
+

Product Management

+

Manage your store inventory with precision and ease

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

Product Inventory

+

+ @if(request()->hasAny(['game', 'stock', 'search'])) + {{ $products->count() }} filtered results + @if($products->count() > 0) + + + View all products + + @endif + @else + {{ $products->count() }} products in your catalog + @endif +

+
+
+
+ + +
+
+ +
+ + + @if(request('search')) + + @endif +
+ + +
+ + + + +
+ + + Clear + +
+
+
+
+ + @if($products->isEmpty()) +
+
+ +
+

No Products Yet

+

Start building your inventory by adding your first product.

+ +
+ @else +
+ @foreach($products as $product) +
+ +
+ {{ $product->name }} + + +
+ + +
+ @if($product->Amount === 0) + + Out of Stock + + @elseif($product->Amount <= 5) + + {{ $product->Amount }} Left + + @else + + In Stock + + @endif +
+
+ + +
+ +
+
+
Price
+
+ ฿{{ number_format($product->price, 2) }} +
+
+
+ + + @if($product->game) +
+ + @if($product->game == 'Genshin') + Genshin Impact + @elseif($product->game == 'Starrail') + Honkai: Star Rail + @elseif($product->game == 'WutheringWave') + Wuthering Waves + @else + {{ $product->game }} + @endif + +
+ @endif + + +

+ {{ $product->name }} +

+ + +

+ {{ $product->description }} +

+ + +
+ +
+ @csrf + @method('DELETE') + +
+
+
+
+ @endforeach +
+ @endif +
+
+
+ + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 0000000000000000000000000000000000000000..b8c8be8b60062a7c2f50e9bdb13109e6116f95be --- /dev/null +++ b/routes/api.php @@ -0,0 +1,29 @@ +get('/user', function (Request $request) { + return $request->user(); +}); + +// Dashboard Analytics API Routes (temporarily without auth for testing) +Route::prefix('dashboard')->group(function () { + Route::get('/revenue', [DashboardController::class, 'revenue']); + Route::get('/profit', [DashboardController::class, 'profit']); + Route::get('/sales', [DashboardController::class, 'sales']); + Route::get('/customers', [DashboardController::class, 'customers']); + Route::get('/summary', [DashboardController::class, 'summary']); +}); \ No newline at end of file diff --git a/routes/auth.php b/routes/auth.php new file mode 100644 index 0000000000000000000000000000000000000000..3926ecf72a8b0ceb1d796215be602d6b6accf846 --- /dev/null +++ b/routes/auth.php @@ -0,0 +1,59 @@ +group(function () { + Route::get('register', [RegisteredUserController::class, 'create']) + ->name('register'); + + Route::post('register', [RegisteredUserController::class, 'store']); + + Route::get('login', [AuthenticatedSessionController::class, 'create']) + ->name('login'); + + Route::post('login', [AuthenticatedSessionController::class, 'store']); + + Route::get('forgot-password', [PasswordResetLinkController::class, 'create']) + ->name('password.request'); + + Route::post('forgot-password', [PasswordResetLinkController::class, 'store']) + ->name('password.email'); + + Route::get('reset-password/{token}', [NewPasswordController::class, 'create']) + ->name('password.reset'); + + Route::post('reset-password', [NewPasswordController::class, 'store']) + ->name('password.store'); +}); + +Route::middleware('auth')->group(function () { + Route::get('verify-email', EmailVerificationPromptController::class) + ->name('verification.notice'); + + Route::get('verify-email/{id}/{hash}', VerifyEmailController::class) + ->middleware(['signed', 'throttle:6,1']) + ->name('verification.verify'); + + Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store']) + ->middleware('throttle:6,1') + ->name('verification.send'); + + Route::get('confirm-password', [ConfirmablePasswordController::class, 'show']) + ->name('password.confirm'); + + Route::post('confirm-password', [ConfirmablePasswordController::class, 'store']); + + Route::put('password', [PasswordController::class, 'update'])->name('password.update'); + + Route::post('logout', [AuthenticatedSessionController::class, 'destroy']) + ->name('logout'); +}); diff --git a/routes/console.php b/routes/console.php new file mode 100644 index 0000000000000000000000000000000000000000..3c9adf1af8430da566d123514630bcc30c9c1345 --- /dev/null +++ b/routes/console.php @@ -0,0 +1,8 @@ +comment(Inspiring::quote()); +})->purpose('Display an inspiring quote'); diff --git a/routes/web.php b/routes/web.php new file mode 100644 index 0000000000000000000000000000000000000000..09e064f35ccd79b802943bff6152121cf72da293 --- /dev/null +++ b/routes/web.php @@ -0,0 +1,84 @@ +middleware(['auth', 'verified'])->name('dashboard'); + +Route::middleware('auth')->group(function () { + Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); + Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); + Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); +}); + +Route::get('/cart', [CartController::class, 'index'])->name('cart.index'); +Route::post('/cart/add/{id}', [CartController::class, 'add'])->name('cart.add'); +Route::post('/cart/remove/{id}', [CartController::class, 'remove'])->name('cart.remove'); +Route::get('/my-orders', [CartController::class, 'customerOrders'])->name('customer.orders'); + +Route::get('/', function () { + $totalProducts = \App\Models\Product::count(); // total items + $gameCount = \App\Models\Product::select('game') + ->distinct() + ->count('game'); // unique games + + $categoryCounts = [ + 'Genshin' => \App\Models\Product::where('game', 'Genshin')->count(), + 'Starrail' => \App\Models\Product::where('game', 'Starrail')->count(), + 'WutheringWave' => \App\Models\Product::where('game', 'WutheringWave')->count(), + ]; + + // Get all custom games for the homepage + $customGames = \App\Models\CustomGame::orderBy('name')->get(); + + return view('scroll_reveal_landing', compact( + 'totalProducts', + 'gameCount', + 'categoryCounts', + 'customGames' + )); +})->name('home'); + +Route::get('/store', [ProductController::class, 'categories'])->name('categories'); +Route::get('/store/{category}', [ProductController::class, 'categoryProducts'])->name('store.category'); +Route::get('/store/{category}/{product_name}', [ProductController::class, 'show'])->name('store.product'); +// Products browsing route removed +Route::get('/api/products/category/{game}', [ProductController::class, 'getByCategory'])->name('api.products.category'); +Route::get('/api/search', [ProductController::class, 'search'])->name('api.search'); + +// Dashboard Analytics API Routes +Route::prefix('api/dashboard')->group(function () { + Route::get('/revenue', [App\Http\Controllers\Api\DashboardController::class, 'revenue'])->name('api.dashboard.revenue'); + Route::get('/profit', [App\Http\Controllers\Api\DashboardController::class, 'profit'])->name('api.dashboard.profit'); + Route::get('/sales', [App\Http\Controllers\Api\DashboardController::class, 'sales'])->name('api.dashboard.sales'); + Route::get('/customers', [App\Http\Controllers\Api\DashboardController::class, 'customers'])->name('api.dashboard.customers'); + Route::get('/summary', [App\Http\Controllers\Api\DashboardController::class, 'summary'])->name('api.dashboard.summary'); +}); +Route::post('/store', [ProductController::class, 'store'])->name('products.store'); + +// Old product URLs removed + +Route::put('/products/{id}', [ProductController::class, 'update'])->name('products.update'); + +Route::post('/checkout', [CartController::class, 'checkout'])->name('checkout.submit'); + +Route::middleware('auth')->group(function () { + Route::get('/orders', [CartController::class, 'order'])->name('orders.index'); + Route::delete('/orders/{id}', [CartController::class, 'deleteOrder'])->name('orders.delete'); + Route::patch('/orders/{id}/complete', [CartController::class, 'completeOrder'])->name('orders.complete'); + Route::patch('/orders/{id}/reject', [CartController::class, 'rejectOrder'])->name('orders.reject'); + Route::get('/manage-products', [ProductController::class, 'listOFproduct'])->name('products.listOFproduct'); + Route::delete('/products/{id}', [ProductController::class, 'destroy'])->name('products.destroy'); + Route::post('/custom-games', [ProductController::class, 'storeCustomGame'])->name('custom-games.store'); + Route::delete('/custom-games/{gameName}', [ProductController::class, 'deleteCustomGame'])->name('custom-games.destroy'); + Route::get('/orders/{order}', [CartController::class, 'show'])->name('orders.show'); +}); + +require __DIR__.'/auth.php'; \ No newline at end of file diff --git a/storage/app/.gitignore b/storage/app/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..fedb287fece8049b6bd243804b133e3af7a43f4c --- /dev/null +++ b/storage/app/.gitignore @@ -0,0 +1,4 @@ +* +!private/ +!public/ +!.gitignore diff --git a/storage/app/private/.gitignore b/storage/app/private/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d6b7ef32c8478a48c3994dcadc86837f4371184d --- /dev/null +++ b/storage/app/private/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/app/public/.gitignore b/storage/app/public/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d6b7ef32c8478a48c3994dcadc86837f4371184d --- /dev/null +++ b/storage/app/public/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/app/public/payment_slips/DYkSTmRL61EsCcoW8zvLLnDPqGIvRO3pJaV6h66S.jpg b/storage/app/public/payment_slips/DYkSTmRL61EsCcoW8zvLLnDPqGIvRO3pJaV6h66S.jpg new file mode 100644 index 0000000000000000000000000000000000000000..591fd38b5f9f6dedc5f1d2d1fd3178968ed47570 Binary files /dev/null and b/storage/app/public/payment_slips/DYkSTmRL61EsCcoW8zvLLnDPqGIvRO3pJaV6h66S.jpg differ diff --git a/storage/app/public/payment_slips/Gdx6m0rw4cUSUAufq3NTNAUWyQSKjRRg7eLmIZEA.gif b/storage/app/public/payment_slips/Gdx6m0rw4cUSUAufq3NTNAUWyQSKjRRg7eLmIZEA.gif new file mode 100644 index 0000000000000000000000000000000000000000..5eb70754ab665878de81b9ca35ed1fa1b073dd81 --- /dev/null +++ b/storage/app/public/payment_slips/Gdx6m0rw4cUSUAufq3NTNAUWyQSKjRRg7eLmIZEA.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04d87a98a07502877f2ae518868e3e0d58b45e5f0db016ff29c3065d987a8916 +size 268757 diff --git a/storage/app/public/payment_slips/HhdqTRvFBZbgxQ1W32v4ZHPBTfLMGddaV1CceZRv.jpg b/storage/app/public/payment_slips/HhdqTRvFBZbgxQ1W32v4ZHPBTfLMGddaV1CceZRv.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b9830c1efc53a7f302a6da864b0e0c0f8df7f49f Binary files /dev/null and b/storage/app/public/payment_slips/HhdqTRvFBZbgxQ1W32v4ZHPBTfLMGddaV1CceZRv.jpg differ diff --git a/storage/app/public/payment_slips/Q7E2oKEKKkm27IgJ6CyRl5HFZdvRMUs1xsMzOj53.jpg b/storage/app/public/payment_slips/Q7E2oKEKKkm27IgJ6CyRl5HFZdvRMUs1xsMzOj53.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1580526ee080f739cbc0c97319fc10009a109537 Binary files /dev/null and b/storage/app/public/payment_slips/Q7E2oKEKKkm27IgJ6CyRl5HFZdvRMUs1xsMzOj53.jpg differ diff --git a/storage/app/public/payment_slips/YaS0Sd9UUJBNmekUXcgndzeVeGfXGMiTOnxnvEYj.jpg b/storage/app/public/payment_slips/YaS0Sd9UUJBNmekUXcgndzeVeGfXGMiTOnxnvEYj.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e1aad53f7e34119643e1650646aed514c8159a9d --- /dev/null +++ b/storage/app/public/payment_slips/YaS0Sd9UUJBNmekUXcgndzeVeGfXGMiTOnxnvEYj.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8e5967a04cdb2ad3caa3b6a156bc4a5fe810ce03fea293b5dd0636d2c84924d +size 344320 diff --git a/storage/app/public/payment_slips/seALBjryd95AWErPzTvLGVfMOdvwDQVYK62vIJ5D.png b/storage/app/public/payment_slips/seALBjryd95AWErPzTvLGVfMOdvwDQVYK62vIJ5D.png new file mode 100644 index 0000000000000000000000000000000000000000..c8780b8ae357fce8ac05d4fb63b54aa51546ecde --- /dev/null +++ b/storage/app/public/payment_slips/seALBjryd95AWErPzTvLGVfMOdvwDQVYK62vIJ5D.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13dd85f023a65d2665117187c44804589803c3ff5ed40498c91a4357d8af4600 +size 2110057 diff --git a/storage/app/public/products/3Qk5m64c3TAqttQLi39MPeukQNG2rSEhZ8g1l9qI.jpg b/storage/app/public/products/3Qk5m64c3TAqttQLi39MPeukQNG2rSEhZ8g1l9qI.jpg new file mode 100644 index 0000000000000000000000000000000000000000..229a0ac6ef703af3871b7b0f60f37f79ddfe1063 --- /dev/null +++ b/storage/app/public/products/3Qk5m64c3TAqttQLi39MPeukQNG2rSEhZ8g1l9qI.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba5ea57fe7271809b7f41bb10ea0e12c1837e307dc0f87fdeeb0e36fb00a8f5a +size 197125 diff --git a/storage/app/public/products/5HYqx6FkpQOzJkQ8biHSuUCm6BiqPKcmBVbXCe13.png b/storage/app/public/products/5HYqx6FkpQOzJkQ8biHSuUCm6BiqPKcmBVbXCe13.png new file mode 100644 index 0000000000000000000000000000000000000000..ee079fe7e1a955ab26082b581ce156c6dedc6ab8 Binary files /dev/null and b/storage/app/public/products/5HYqx6FkpQOzJkQ8biHSuUCm6BiqPKcmBVbXCe13.png differ diff --git a/storage/app/public/products/6CSKo9nXd4Xj61MEEmXeH3LO0jcVh1HCqowzQoBk.png b/storage/app/public/products/6CSKo9nXd4Xj61MEEmXeH3LO0jcVh1HCqowzQoBk.png new file mode 100644 index 0000000000000000000000000000000000000000..04871c429dfa798447c420c140a15cca7f6159b9 --- /dev/null +++ b/storage/app/public/products/6CSKo9nXd4Xj61MEEmXeH3LO0jcVh1HCqowzQoBk.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e30a09d5e2685ac286de0c84d88cdb47a8cd12af03f1d9d1391bc85eeaef9ebb +size 1305982 diff --git a/storage/app/public/products/D3419EtxxLBwPIZQtYa3FfDXpVrQNQOHtdEfOK8U.png b/storage/app/public/products/D3419EtxxLBwPIZQtYa3FfDXpVrQNQOHtdEfOK8U.png new file mode 100644 index 0000000000000000000000000000000000000000..e6d068abd15e7f71f5388e721b4a8037e33c412b --- /dev/null +++ b/storage/app/public/products/D3419EtxxLBwPIZQtYa3FfDXpVrQNQOHtdEfOK8U.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:57bf5bdd8223d99782245b36e2ecc952fc5a8d990174ff2c7ca65ed01b16e974 +size 308675 diff --git a/storage/app/public/products/HdmkT7kc8AjFLIbZMseuNMLuo4MwnbWXzERl2hQQ.png b/storage/app/public/products/HdmkT7kc8AjFLIbZMseuNMLuo4MwnbWXzERl2hQQ.png new file mode 100644 index 0000000000000000000000000000000000000000..81a8cefab39990ab91cabee1d95e5b1e2960ba7b --- /dev/null +++ b/storage/app/public/products/HdmkT7kc8AjFLIbZMseuNMLuo4MwnbWXzERl2hQQ.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9549f531b0ba0d1d9a8cf68b31ce964ff254e31a54eecb939543eb0969da4b4 +size 391027 diff --git a/storage/app/public/products/HxgaBcFWUAVlXmPH4wsasrVE3NDzHqApCC4EJe76.png b/storage/app/public/products/HxgaBcFWUAVlXmPH4wsasrVE3NDzHqApCC4EJe76.png new file mode 100644 index 0000000000000000000000000000000000000000..81a8cefab39990ab91cabee1d95e5b1e2960ba7b --- /dev/null +++ b/storage/app/public/products/HxgaBcFWUAVlXmPH4wsasrVE3NDzHqApCC4EJe76.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9549f531b0ba0d1d9a8cf68b31ce964ff254e31a54eecb939543eb0969da4b4 +size 391027 diff --git a/storage/app/public/products/I1X5iyRSD2GD1Vi6dwbE37rhzgPFFQf80JDmpkFQ.jpg b/storage/app/public/products/I1X5iyRSD2GD1Vi6dwbE37rhzgPFFQf80JDmpkFQ.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a814185ba572daefe5e7a3a01c0cfb748ab75a76 --- /dev/null +++ b/storage/app/public/products/I1X5iyRSD2GD1Vi6dwbE37rhzgPFFQf80JDmpkFQ.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf602cbea173b32bac12efa8d9bd0cac3b11fef524485077aa5bd094c584b73f +size 705033 diff --git a/storage/app/public/products/KOzqgZj7XUkMt2uPDYRXXKFVT1gmfRSh9RwB4Qnp.png b/storage/app/public/products/KOzqgZj7XUkMt2uPDYRXXKFVT1gmfRSh9RwB4Qnp.png new file mode 100644 index 0000000000000000000000000000000000000000..81a8cefab39990ab91cabee1d95e5b1e2960ba7b --- /dev/null +++ b/storage/app/public/products/KOzqgZj7XUkMt2uPDYRXXKFVT1gmfRSh9RwB4Qnp.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9549f531b0ba0d1d9a8cf68b31ce964ff254e31a54eecb939543eb0969da4b4 +size 391027 diff --git a/storage/app/public/products/Keuu1FkTKIBfFghhZqn7Ly5uw33Yetqk0DlhjDjN.jpg b/storage/app/public/products/Keuu1FkTKIBfFghhZqn7Ly5uw33Yetqk0DlhjDjN.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7f08f25704ef8e3fd963af5fd02fdf6e477c23dc --- /dev/null +++ b/storage/app/public/products/Keuu1FkTKIBfFghhZqn7Ly5uw33Yetqk0DlhjDjN.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:335a9cd9ce1a6f2f0191bb7fa6119753f4af94b5d5d4bf69dd12018edde38f99 +size 256308 diff --git a/storage/app/public/products/NVqj7sJZUZl7nIg4iSmAIJ7WG0kkrSwA9uwXsIBG.png b/storage/app/public/products/NVqj7sJZUZl7nIg4iSmAIJ7WG0kkrSwA9uwXsIBG.png new file mode 100644 index 0000000000000000000000000000000000000000..f8bdfa9a628da1f4bdb77090249a5d9331aaaf6a Binary files /dev/null and b/storage/app/public/products/NVqj7sJZUZl7nIg4iSmAIJ7WG0kkrSwA9uwXsIBG.png differ diff --git a/storage/app/public/products/XYixdNVbWsh0WDYV9YiNN99CwXp6nMcoIdp07Gt7.jpg b/storage/app/public/products/XYixdNVbWsh0WDYV9YiNN99CwXp6nMcoIdp07Gt7.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e1aad53f7e34119643e1650646aed514c8159a9d --- /dev/null +++ b/storage/app/public/products/XYixdNVbWsh0WDYV9YiNN99CwXp6nMcoIdp07Gt7.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8e5967a04cdb2ad3caa3b6a156bc4a5fe810ce03fea293b5dd0636d2c84924d +size 344320 diff --git a/storage/app/public/products/YBE7KBU74W2XxWwQFDLC8qqH3abpRM9WEFco8tt2.jpg b/storage/app/public/products/YBE7KBU74W2XxWwQFDLC8qqH3abpRM9WEFco8tt2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7f08f25704ef8e3fd963af5fd02fdf6e477c23dc --- /dev/null +++ b/storage/app/public/products/YBE7KBU74W2XxWwQFDLC8qqH3abpRM9WEFco8tt2.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:335a9cd9ce1a6f2f0191bb7fa6119753f4af94b5d5d4bf69dd12018edde38f99 +size 256308 diff --git a/storage/app/public/products/Zl5tjkxHxKwEE69XgnJSUJgqNH0iQ32TWYv9fb8o.png b/storage/app/public/products/Zl5tjkxHxKwEE69XgnJSUJgqNH0iQ32TWYv9fb8o.png new file mode 100644 index 0000000000000000000000000000000000000000..3a21d4dfc687b13ee6ac75ffbb9646fbf615654f --- /dev/null +++ b/storage/app/public/products/Zl5tjkxHxKwEE69XgnJSUJgqNH0iQ32TWYv9fb8o.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2bcdda915d3074f3f98999139f8b4be1dc7deddcebf775c0650d15be101e5a3 +size 177223 diff --git a/storage/app/public/products/cxSKE9LV3Zg1KzpaIFm43T9fCL3UngNSeJevsthU.jpg b/storage/app/public/products/cxSKE9LV3Zg1KzpaIFm43T9fCL3UngNSeJevsthU.jpg new file mode 100644 index 0000000000000000000000000000000000000000..455bd3026c43af8920d08ac70db481e25bf9f80b --- /dev/null +++ b/storage/app/public/products/cxSKE9LV3Zg1KzpaIFm43T9fCL3UngNSeJevsthU.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a16be95d6ada66007cf994fbbd38e40bc9a0163aca1b09ccb3e7212deec0a4fc +size 563041 diff --git a/storage/app/public/products/h1M1kvV1Q2B1hmiTICKbSt3Wakwnka26mqAtacXY.jpg b/storage/app/public/products/h1M1kvV1Q2B1hmiTICKbSt3Wakwnka26mqAtacXY.jpg new file mode 100644 index 0000000000000000000000000000000000000000..591fd38b5f9f6dedc5f1d2d1fd3178968ed47570 Binary files /dev/null and b/storage/app/public/products/h1M1kvV1Q2B1hmiTICKbSt3Wakwnka26mqAtacXY.jpg differ diff --git a/storage/app/public/products/iG3yc6NCOj5bWJhVk4JLIUJeg42lmfWyLcPuHQdX.png b/storage/app/public/products/iG3yc6NCOj5bWJhVk4JLIUJeg42lmfWyLcPuHQdX.png new file mode 100644 index 0000000000000000000000000000000000000000..81a8cefab39990ab91cabee1d95e5b1e2960ba7b --- /dev/null +++ b/storage/app/public/products/iG3yc6NCOj5bWJhVk4JLIUJeg42lmfWyLcPuHQdX.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9549f531b0ba0d1d9a8cf68b31ce964ff254e31a54eecb939543eb0969da4b4 +size 391027 diff --git a/storage/app/public/products/p1IZEaJITbWLSg0A2mS4dg0zfmNzQbsoFk6vEDvy.png b/storage/app/public/products/p1IZEaJITbWLSg0A2mS4dg0zfmNzQbsoFk6vEDvy.png new file mode 100644 index 0000000000000000000000000000000000000000..81a8cefab39990ab91cabee1d95e5b1e2960ba7b --- /dev/null +++ b/storage/app/public/products/p1IZEaJITbWLSg0A2mS4dg0zfmNzQbsoFk6vEDvy.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9549f531b0ba0d1d9a8cf68b31ce964ff254e31a54eecb939543eb0969da4b4 +size 391027 diff --git a/storage/app/public/products/x17zdxxvZfiApA82X8YTdFeF6kl3G3hpLDGcSk1M.jpg b/storage/app/public/products/x17zdxxvZfiApA82X8YTdFeF6kl3G3hpLDGcSk1M.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4284b6620794d1c3862d2678a92801dd25fd1fa5 Binary files /dev/null and b/storage/app/public/products/x17zdxxvZfiApA82X8YTdFeF6kl3G3hpLDGcSk1M.jpg differ diff --git a/storage/framework/.gitignore b/storage/framework/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..05c4471f2b53fc17d3cac9d3d252755a35479f7c --- /dev/null +++ b/storage/framework/.gitignore @@ -0,0 +1,9 @@ +compiled.php +config.php +down +events.scanned.php +maintenance.php +routes.php +routes.scanned.php +schedule-* +services.json diff --git a/storage/framework/testing/.gitignore b/storage/framework/testing/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d6b7ef32c8478a48c3994dcadc86837f4371184d --- /dev/null +++ b/storage/framework/testing/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000000000000000000000000000000000000..c29eb1a15ba81609f29337f8db51d91712409341 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,21 @@ +import defaultTheme from 'tailwindcss/defaultTheme'; +import forms from '@tailwindcss/forms'; + +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php', + './storage/framework/views/*.php', + './resources/views/**/*.blade.php', + ], + + theme: { + extend: { + fontFamily: { + sans: ['Figtree', ...defaultTheme.fontFamily.sans], + }, + }, + }, + + plugins: [forms], +}; diff --git a/tests/Feature/Auth/AuthenticationTest.php b/tests/Feature/Auth/AuthenticationTest.php new file mode 100644 index 0000000000000000000000000000000000000000..13dcb7ce7eb6c9d3a75bc95ddfdcd024337f1418 --- /dev/null +++ b/tests/Feature/Auth/AuthenticationTest.php @@ -0,0 +1,54 @@ +get('/login'); + + $response->assertStatus(200); + } + + public function test_users_can_authenticate_using_the_login_screen(): void + { + $user = User::factory()->create(); + + $response = $this->post('/login', [ + 'email' => $user->email, + 'password' => 'password', + ]); + + $this->assertAuthenticated(); + $response->assertRedirect(route('dashboard', absolute: false)); + } + + public function test_users_can_not_authenticate_with_invalid_password(): void + { + $user = User::factory()->create(); + + $this->post('/login', [ + 'email' => $user->email, + 'password' => 'wrong-password', + ]); + + $this->assertGuest(); + } + + public function test_users_can_logout(): void + { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->post('/logout'); + + $this->assertGuest(); + $response->assertRedirect('/'); + } +} diff --git a/tests/Feature/Auth/EmailVerificationTest.php b/tests/Feature/Auth/EmailVerificationTest.php new file mode 100644 index 0000000000000000000000000000000000000000..705570b43f3a048623759a159020d7ee0739cc57 --- /dev/null +++ b/tests/Feature/Auth/EmailVerificationTest.php @@ -0,0 +1,58 @@ +unverified()->create(); + + $response = $this->actingAs($user)->get('/verify-email'); + + $response->assertStatus(200); + } + + public function test_email_can_be_verified(): void + { + $user = User::factory()->unverified()->create(); + + Event::fake(); + + $verificationUrl = URL::temporarySignedRoute( + 'verification.verify', + now()->addMinutes(60), + ['id' => $user->id, 'hash' => sha1($user->email)] + ); + + $response = $this->actingAs($user)->get($verificationUrl); + + Event::assertDispatched(Verified::class); + $this->assertTrue($user->fresh()->hasVerifiedEmail()); + $response->assertRedirect(route('dashboard', absolute: false).'?verified=1'); + } + + public function test_email_is_not_verified_with_invalid_hash(): void + { + $user = User::factory()->unverified()->create(); + + $verificationUrl = URL::temporarySignedRoute( + 'verification.verify', + now()->addMinutes(60), + ['id' => $user->id, 'hash' => sha1('wrong-email')] + ); + + $this->actingAs($user)->get($verificationUrl); + + $this->assertFalse($user->fresh()->hasVerifiedEmail()); + } +} diff --git a/tests/Feature/Auth/PasswordConfirmationTest.php b/tests/Feature/Auth/PasswordConfirmationTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ff85721e25d7f6c662352ffb861d52798bbf27f2 --- /dev/null +++ b/tests/Feature/Auth/PasswordConfirmationTest.php @@ -0,0 +1,44 @@ +create(); + + $response = $this->actingAs($user)->get('/confirm-password'); + + $response->assertStatus(200); + } + + public function test_password_can_be_confirmed(): void + { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->post('/confirm-password', [ + 'password' => 'password', + ]); + + $response->assertRedirect(); + $response->assertSessionHasNoErrors(); + } + + public function test_password_is_not_confirmed_with_invalid_password(): void + { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->post('/confirm-password', [ + 'password' => 'wrong-password', + ]); + + $response->assertSessionHasErrors(); + } +} diff --git a/tests/Feature/Auth/PasswordResetTest.php b/tests/Feature/Auth/PasswordResetTest.php new file mode 100644 index 0000000000000000000000000000000000000000..aa50350588345237d55315d5e59f8fe4fd93ad6f --- /dev/null +++ b/tests/Feature/Auth/PasswordResetTest.php @@ -0,0 +1,73 @@ +get('/forgot-password'); + + $response->assertStatus(200); + } + + public function test_reset_password_link_can_be_requested(): void + { + Notification::fake(); + + $user = User::factory()->create(); + + $this->post('/forgot-password', ['email' => $user->email]); + + Notification::assertSentTo($user, ResetPassword::class); + } + + public function test_reset_password_screen_can_be_rendered(): void + { + Notification::fake(); + + $user = User::factory()->create(); + + $this->post('/forgot-password', ['email' => $user->email]); + + Notification::assertSentTo($user, ResetPassword::class, function ($notification) { + $response = $this->get('/reset-password/'.$notification->token); + + $response->assertStatus(200); + + return true; + }); + } + + public function test_password_can_be_reset_with_valid_token(): void + { + Notification::fake(); + + $user = User::factory()->create(); + + $this->post('/forgot-password', ['email' => $user->email]); + + Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) { + $response = $this->post('/reset-password', [ + 'token' => $notification->token, + 'email' => $user->email, + 'password' => 'password', + 'password_confirmation' => 'password', + ]); + + $response + ->assertSessionHasNoErrors() + ->assertRedirect(route('login')); + + return true; + }); + } +} diff --git a/tests/Feature/Auth/PasswordUpdateTest.php b/tests/Feature/Auth/PasswordUpdateTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ca28c6c6eb4687e4207b83c9d2f871fe7dae1dd2 --- /dev/null +++ b/tests/Feature/Auth/PasswordUpdateTest.php @@ -0,0 +1,51 @@ +create(); + + $response = $this + ->actingAs($user) + ->from('/profile') + ->put('/password', [ + 'current_password' => 'password', + 'password' => 'new-password', + 'password_confirmation' => 'new-password', + ]); + + $response + ->assertSessionHasNoErrors() + ->assertRedirect('/profile'); + + $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); + } + + public function test_correct_password_must_be_provided_to_update_password(): void + { + $user = User::factory()->create(); + + $response = $this + ->actingAs($user) + ->from('/profile') + ->put('/password', [ + 'current_password' => 'wrong-password', + 'password' => 'new-password', + 'password_confirmation' => 'new-password', + ]); + + $response + ->assertSessionHasErrorsIn('updatePassword', 'current_password') + ->assertRedirect('/profile'); + } +} diff --git a/tests/Feature/Auth/RegistrationTest.php b/tests/Feature/Auth/RegistrationTest.php new file mode 100644 index 0000000000000000000000000000000000000000..1489d0e0f1d7b358509b0226a279d62081c11410 --- /dev/null +++ b/tests/Feature/Auth/RegistrationTest.php @@ -0,0 +1,31 @@ +get('/register'); + + $response->assertStatus(200); + } + + public function test_new_users_can_register(): void + { + $response = $this->post('/register', [ + 'name' => 'Test User', + 'email' => 'test@example.com', + 'password' => 'password', + 'password_confirmation' => 'password', + ]); + + $this->assertAuthenticated(); + $response->assertRedirect(route('dashboard', absolute: false)); + } +} diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8364a84e2b7eea9f007e99a5d3333273fe30bf8a --- /dev/null +++ b/tests/Feature/ExampleTest.php @@ -0,0 +1,19 @@ +get('/'); + + $response->assertStatus(200); + } +} diff --git a/tests/Feature/ProfileTest.php b/tests/Feature/ProfileTest.php new file mode 100644 index 0000000000000000000000000000000000000000..252fdcc525a1d0a0c52fe12ffca7d89fc87292ea --- /dev/null +++ b/tests/Feature/ProfileTest.php @@ -0,0 +1,99 @@ +create(); + + $response = $this + ->actingAs($user) + ->get('/profile'); + + $response->assertOk(); + } + + public function test_profile_information_can_be_updated(): void + { + $user = User::factory()->create(); + + $response = $this + ->actingAs($user) + ->patch('/profile', [ + 'name' => 'Test User', + 'email' => 'test@example.com', + ]); + + $response + ->assertSessionHasNoErrors() + ->assertRedirect('/profile'); + + $user->refresh(); + + $this->assertSame('Test User', $user->name); + $this->assertSame('test@example.com', $user->email); + $this->assertNull($user->email_verified_at); + } + + public function test_email_verification_status_is_unchanged_when_the_email_address_is_unchanged(): void + { + $user = User::factory()->create(); + + $response = $this + ->actingAs($user) + ->patch('/profile', [ + 'name' => 'Test User', + 'email' => $user->email, + ]); + + $response + ->assertSessionHasNoErrors() + ->assertRedirect('/profile'); + + $this->assertNotNull($user->refresh()->email_verified_at); + } + + public function test_user_can_delete_their_account(): void + { + $user = User::factory()->create(); + + $response = $this + ->actingAs($user) + ->delete('/profile', [ + 'password' => 'password', + ]); + + $response + ->assertSessionHasNoErrors() + ->assertRedirect('/'); + + $this->assertGuest(); + $this->assertNull($user->fresh()); + } + + public function test_correct_password_must_be_provided_to_delete_account(): void + { + $user = User::factory()->create(); + + $response = $this + ->actingAs($user) + ->from('/profile') + ->delete('/profile', [ + 'password' => 'wrong-password', + ]); + + $response + ->assertSessionHasErrorsIn('userDeletion', 'password') + ->assertRedirect('/profile'); + + $this->assertNotNull($user->fresh()); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000000000000000000000000000000000000..fe1ffc2ff1abc1c23522418994879623c5647859 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,10 @@ +assertTrue(true); + } +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000000000000000000000000000000000000..421b5695627db43c022947cfc7c0ecce6b9689be --- /dev/null +++ b/vite.config.js @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + input: ['resources/css/app.css', 'resources/js/app.js'], + refresh: true, + }), + ], +});