veela4 commited on
Commit
70ba896
·
verified ·
1 Parent(s): a16fd10

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .editorconfig +18 -0
  2. .env.example +65 -0
  3. .gitattributes +28 -35
  4. .gitignore +57 -0
  5. .kiro/specs/category-search-fix/design.md +172 -0
  6. .kiro/specs/category-search-fix/requirements.md +43 -0
  7. .kiro/specs/category-search-fix/tasks.md +43 -0
  8. .kiro/specs/console-interface-fix/design.md +161 -0
  9. .kiro/specs/console-interface-fix/requirements.md +60 -0
  10. .kiro/specs/console-interface-fix/tasks.md +62 -0
  11. .kiro/specs/dashboard-analytics-enhancement/design.md +148 -0
  12. .kiro/specs/dashboard-analytics-enhancement/requirements.md +73 -0
  13. .kiro/specs/dashboard-analytics-enhancement/tasks.md +124 -0
  14. .kiro/specs/dashboard-time-filter-fix/design.md +220 -0
  15. .kiro/specs/dashboard-time-filter-fix/requirements.md +55 -0
  16. .kiro/specs/dashboard-time-filter-fix/tasks.md +43 -0
  17. .kiro/specs/search-glassmorphism-fix/design.md +161 -0
  18. .kiro/specs/search-glassmorphism-fix/requirements.md +50 -0
  19. .kiro/specs/search-glassmorphism-fix/tasks.md +36 -0
  20. README.md +61 -3
  21. README_HF.md +85 -0
  22. app/Console/Commands/MigrateExistingCustomGames.php +108 -0
  23. app/Console/Commands/PopulateAnalyticsData.php +59 -0
  24. app/Console/Commands/TestDashboard.php +57 -0
  25. app/Http/Controllers/Api/DashboardController.php +263 -0
  26. app/Http/Controllers/Auth/AuthenticatedSessionController.php +47 -0
  27. app/Http/Controllers/Auth/ConfirmablePasswordController.php +40 -0
  28. app/Http/Controllers/Auth/EmailVerificationNotificationController.php +24 -0
  29. app/Http/Controllers/Auth/EmailVerificationPromptController.php +21 -0
  30. app/Http/Controllers/Auth/NewPasswordController.php +62 -0
  31. app/Http/Controllers/Auth/PasswordController.php +29 -0
  32. app/Http/Controllers/Auth/PasswordResetLinkController.php +44 -0
  33. app/Http/Controllers/Auth/RegisteredUserController.php +50 -0
  34. app/Http/Controllers/Auth/VerifyEmailController.php +27 -0
  35. app/Http/Controllers/CartController.php +255 -0
  36. app/Http/Controllers/Controller.php +8 -0
  37. app/Http/Controllers/DashboardController.php +39 -0
  38. app/Http/Controllers/ProductController.php +411 -0
  39. app/Http/Controllers/ProfileController.php +60 -0
  40. app/Http/Requests/Auth/LoginRequest.php +85 -0
  41. app/Http/Requests/ProfileUpdateRequest.php +30 -0
  42. app/Models/AnalyticsSnapshot.php +63 -0
  43. app/Models/CustomGame.php +14 -0
  44. app/Models/Order.php +17 -0
  45. app/Models/Product.php +48 -0
  46. app/Models/RevenueTracking.php +59 -0
  47. app/Models/User.php +48 -0
  48. app/Providers/AppServiceProvider.php +28 -0
  49. app/Services/AnalyticsService.php +379 -0
  50. app/View/Components/AppLayout.php +17 -0
.editorconfig ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ indent_size = 4
7
+ indent_style = space
8
+ insert_final_newline = true
9
+ trim_trailing_whitespace = true
10
+
11
+ [*.md]
12
+ trim_trailing_whitespace = false
13
+
14
+ [*.{yml,yaml}]
15
+ indent_size = 2
16
+
17
+ [docker-compose.yml]
18
+ indent_size = 4
.env.example ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ APP_NAME=Laravel
2
+ APP_ENV=local
3
+ APP_KEY=
4
+ APP_DEBUG=true
5
+ APP_URL=http://localhost
6
+
7
+ APP_LOCALE=en
8
+ APP_FALLBACK_LOCALE=en
9
+ APP_FAKER_LOCALE=en_US
10
+
11
+ APP_MAINTENANCE_DRIVER=file
12
+ # APP_MAINTENANCE_STORE=database
13
+
14
+ PHP_CLI_SERVER_WORKERS=4
15
+
16
+ BCRYPT_ROUNDS=12
17
+
18
+ LOG_CHANNEL=stack
19
+ LOG_STACK=single
20
+ LOG_DEPRECATIONS_CHANNEL=null
21
+ LOG_LEVEL=debug
22
+
23
+ DB_CONNECTION=sqlite
24
+ # DB_HOST=127.0.0.1
25
+ # DB_PORT=3306
26
+ # DB_DATABASE=laravel
27
+ # DB_USERNAME=root
28
+ # DB_PASSWORD=
29
+
30
+ SESSION_DRIVER=database
31
+ SESSION_LIFETIME=120
32
+ SESSION_ENCRYPT=false
33
+ SESSION_PATH=/
34
+ SESSION_DOMAIN=null
35
+
36
+ BROADCAST_CONNECTION=log
37
+ FILESYSTEM_DISK=local
38
+ QUEUE_CONNECTION=database
39
+
40
+ CACHE_STORE=database
41
+ # CACHE_PREFIX=
42
+
43
+ MEMCACHED_HOST=127.0.0.1
44
+
45
+ REDIS_CLIENT=phpredis
46
+ REDIS_HOST=127.0.0.1
47
+ REDIS_PASSWORD=null
48
+ REDIS_PORT=6379
49
+
50
+ MAIL_MAILER=log
51
+ MAIL_SCHEME=null
52
+ MAIL_HOST=127.0.0.1
53
+ MAIL_PORT=2525
54
+ MAIL_USERNAME=null
55
+ MAIL_PASSWORD=null
56
+ MAIL_FROM_ADDRESS="hello@example.com"
57
+ MAIL_FROM_NAME="${APP_NAME}"
58
+
59
+ AWS_ACCESS_KEY_ID=
60
+ AWS_SECRET_ACCESS_KEY=
61
+ AWS_DEFAULT_REGION=us-east-1
62
+ AWS_BUCKET=
63
+ AWS_USE_PATH_STYLE_ENDPOINT=false
64
+
65
+ VITE_APP_NAME="${APP_NAME}"
.gitattributes CHANGED
@@ -1,35 +1,28 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ * text=auto eol=lf
2
+
3
+ *.blade.php diff=html
4
+ *.css diff=css
5
+ *.html diff=html
6
+ *.md diff=markdown
7
+ *.php diff=php
8
+
9
+ /.github export-ignore
10
+ CHANGELOG.md export-ignore
11
+ .styleci.yml export-ignore
12
+ storage/app/public/payment_slips/Gdx6m0rw4cUSUAufq3NTNAUWyQSKjRRg7eLmIZEA.gif filter=lfs diff=lfs merge=lfs -text
13
+ storage/app/public/payment_slips/seALBjryd95AWErPzTvLGVfMOdvwDQVYK62vIJ5D.png filter=lfs diff=lfs merge=lfs -text
14
+ storage/app/public/payment_slips/YaS0Sd9UUJBNmekUXcgndzeVeGfXGMiTOnxnvEYj.jpg filter=lfs diff=lfs merge=lfs -text
15
+ storage/app/public/products/3Qk5m64c3TAqttQLi39MPeukQNG2rSEhZ8g1l9qI.jpg filter=lfs diff=lfs merge=lfs -text
16
+ storage/app/public/products/6CSKo9nXd4Xj61MEEmXeH3LO0jcVh1HCqowzQoBk.png filter=lfs diff=lfs merge=lfs -text
17
+ storage/app/public/products/cxSKE9LV3Zg1KzpaIFm43T9fCL3UngNSeJevsthU.jpg filter=lfs diff=lfs merge=lfs -text
18
+ storage/app/public/products/D3419EtxxLBwPIZQtYa3FfDXpVrQNQOHtdEfOK8U.png filter=lfs diff=lfs merge=lfs -text
19
+ storage/app/public/products/HdmkT7kc8AjFLIbZMseuNMLuo4MwnbWXzERl2hQQ.png filter=lfs diff=lfs merge=lfs -text
20
+ storage/app/public/products/HxgaBcFWUAVlXmPH4wsasrVE3NDzHqApCC4EJe76.png filter=lfs diff=lfs merge=lfs -text
21
+ storage/app/public/products/I1X5iyRSD2GD1Vi6dwbE37rhzgPFFQf80JDmpkFQ.jpg filter=lfs diff=lfs merge=lfs -text
22
+ storage/app/public/products/iG3yc6NCOj5bWJhVk4JLIUJeg42lmfWyLcPuHQdX.png filter=lfs diff=lfs merge=lfs -text
23
+ storage/app/public/products/Keuu1FkTKIBfFghhZqn7Ly5uw33Yetqk0DlhjDjN.jpg filter=lfs diff=lfs merge=lfs -text
24
+ storage/app/public/products/KOzqgZj7XUkMt2uPDYRXXKFVT1gmfRSh9RwB4Qnp.png filter=lfs diff=lfs merge=lfs -text
25
+ storage/app/public/products/p1IZEaJITbWLSg0A2mS4dg0zfmNzQbsoFk6vEDvy.png filter=lfs diff=lfs merge=lfs -text
26
+ storage/app/public/products/XYixdNVbWsh0WDYV9YiNN99CwXp6nMcoIdp07Gt7.jpg filter=lfs diff=lfs merge=lfs -text
27
+ storage/app/public/products/YBE7KBU74W2XxWwQFDLC8qqH3abpRM9WEFco8tt2.jpg filter=lfs diff=lfs merge=lfs -text
28
+ storage/app/public/products/Zl5tjkxHxKwEE69XgnJSUJgqNH0iQ32TWYv9fb8o.png filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
.gitignore ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /.phpunit.cache
2
+ /node_modules
3
+ /public/build
4
+ /public/hot
5
+ /public/storage
6
+ /storage/*.key
7
+ /storage/pail
8
+ /storage/logs
9
+ /vendor
10
+ .env
11
+ .env.backup
12
+ .env.production
13
+ .env.local
14
+ .env.*.local
15
+ .phpactor.json
16
+ .phpunit.result.cache
17
+ Homestead.json
18
+ Homestead.yaml
19
+ npm-debug.log
20
+ yarn-error.log
21
+ /auth.json
22
+ /.fleet
23
+ /.idea
24
+ /.nova
25
+ /.vscode
26
+ /.zed
27
+
28
+ # Database files
29
+ /database/database.sqlite
30
+ *.sqlite
31
+ *.db
32
+
33
+ # Log files
34
+ *.log
35
+
36
+ # Cache and session files
37
+ /bootstrap/cache/*
38
+ /storage/framework/cache/*
39
+ /storage/framework/sessions/*
40
+ /storage/framework/views/*
41
+
42
+ # Sensitive configuration
43
+ /config/database.php.backup
44
+ /config/app.php.backup
45
+
46
+ # IDE and editor files
47
+ .DS_Store
48
+ Thumbs.db
49
+
50
+ # Temporary files
51
+ *.tmp
52
+ *.temp
53
+ *.swp
54
+ *.swo
55
+
56
+ # Kiro IDE files (optional - you might want to keep these)
57
+ # .kiro/
.kiro/specs/category-search-fix/design.md ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Design Document
2
+
3
+ ## Overview
4
+
5
+ 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.
6
+
7
+ ## Architecture
8
+
9
+ The solution involves modifying the existing category selection system to:
10
+
11
+ 1. **Button State Management**: Track the currently selected category and update the button appearance
12
+ 2. **Visual Transformation**: Replace the "All Categories" button content with the selected category's icon and name
13
+ 3. **Interaction Preservation**: Maintain the existing modal/dropdown functionality for category selection
14
+ 4. **Filter Integration**: Ensure the visual changes work seamlessly with the existing filter system
15
+
16
+ ## Components and Interfaces
17
+
18
+ ### 1. Category Button Component
19
+
20
+ **Current Structure:**
21
+ ```html
22
+ <div class="filter-card" data-filter="categories" onclick="showAllCategories()">
23
+ <div class="filter-icon bg-gradient-to-r from-purple-600 to-blue-600">
24
+ <i class="fas fa-th-large text-3xl"></i>
25
+ </div>
26
+ <h3 class="filter-title">All Categories</h3>
27
+ <div class="filter-count" id="categories-count">{{ $totalCategories }}</div>
28
+ <div class="filter-badge">Browse</div>
29
+ </div>
30
+ ```
31
+
32
+ **Enhanced Structure:**
33
+ - Add dynamic content areas that can be updated via JavaScript
34
+ - Maintain the same onclick functionality
35
+ - Add data attributes to track selected category state
36
+
37
+ ### 2. Category Selection System
38
+
39
+ **Current Flow:**
40
+ 1. User clicks "All Categories" → `showAllCategories()` → Opens modal
41
+ 2. User selects category → `selectCategory(category)` → Closes modal + `setFilter(category)`
42
+
43
+ **Enhanced Flow:**
44
+ 1. User clicks category button → `showAllCategories()` → Opens modal
45
+ 2. User selects category → `selectCategory(category)` → Closes modal + `setFilter(category)` + `updateCategoryButton(category)`
46
+
47
+ ### 3. Category Configuration
48
+
49
+ **Game Category Mapping:**
50
+ ```javascript
51
+ const categoryConfigs = {
52
+ 'all': {
53
+ icon: 'fas fa-th-large',
54
+ color: 'from-purple-600 to-blue-600',
55
+ name: 'All Categories',
56
+ badge: 'Browse'
57
+ },
58
+ 'Genshin': {
59
+ icon: 'fas fa-star',
60
+ color: 'from-yellow-400 to-orange-500',
61
+ name: 'Genshin Impact',
62
+ badge: 'Orders'
63
+ },
64
+ 'Starrail': {
65
+ icon: 'fas fa-rocket',
66
+ color: 'from-purple-500 to-pink-500',
67
+ name: 'Honkai: Star Rail',
68
+ badge: 'Orders'
69
+ },
70
+ 'WutheringWave': {
71
+ icon: 'fas fa-water',
72
+ color: 'from-cyan-400 to-blue-500',
73
+ name: 'Wuthering Waves',
74
+ badge: 'Orders'
75
+ }
76
+ };
77
+ ```
78
+
79
+ ## Data Models
80
+
81
+ ### Category State
82
+ ```javascript
83
+ {
84
+ selectedCategory: string, // Current selected category key
85
+ categoryConfig: object, // Configuration for the selected category
86
+ orderCount: number // Number of orders for this category
87
+ }
88
+ ```
89
+
90
+ ### Button Update Data
91
+ ```javascript
92
+ {
93
+ icon: string, // Font Awesome icon class
94
+ color: string, // Tailwind gradient classes
95
+ name: string, // Display name
96
+ count: number, // Order count
97
+ badge: string // Badge text
98
+ }
99
+ ```
100
+
101
+ ## Error Handling
102
+
103
+ ### Invalid Category Selection
104
+ - **Issue**: User selects a category that doesn't exist or has no orders
105
+ - **Solution**: Fall back to "All Categories" state and show appropriate message
106
+
107
+ ### Missing Category Configuration
108
+ - **Issue**: A game category exists in orders but not in configuration
109
+ - **Solution**: Use default configuration with generic game icon and original game name
110
+
111
+ ### Button Update Failures
112
+ - **Issue**: JavaScript fails to update button elements
113
+ - **Solution**: Graceful degradation - filter still works, button shows original state
114
+
115
+ ## Testing Strategy
116
+
117
+ ### Unit Tests
118
+ 1. **Category Configuration Tests**
119
+ - Verify all game categories have proper configuration
120
+ - Test fallback configuration for unknown games
121
+ - Validate icon and color mappings
122
+
123
+ 2. **Button Update Tests**
124
+ - Test button transformation for each category
125
+ - Verify icon, name, and color updates
126
+ - Test reset to "All Categories" state
127
+
128
+ 3. **Integration Tests**
129
+ - Test category selection flow end-to-end
130
+ - Verify filter functionality works with button updates
131
+ - Test modal interaction with button state
132
+
133
+ ### Visual Tests
134
+ 1. **Button Appearance**
135
+ - Verify correct icon displays for each category
136
+ - Check color gradients apply correctly
137
+ - Ensure text and count update properly
138
+
139
+ 2. **Interaction Tests**
140
+ - Confirm button remains clickable after transformation
141
+ - Test modal opens correctly from transformed button
142
+ - Verify hover states work on transformed button
143
+
144
+ ### Edge Cases
145
+ 1. **No Orders Scenario**
146
+ - Test behavior when no orders exist for a category
147
+ - Verify button handles zero count gracefully
148
+
149
+ 2. **Multiple Rapid Selections**
150
+ - Test rapid category switching
151
+ - Ensure button updates don't conflict
152
+
153
+ 3. **Browser Compatibility**
154
+ - Test in different browsers
155
+ - Verify CSS transitions work consistently
156
+
157
+ ## Implementation Notes
158
+
159
+ ### CSS Considerations
160
+ - Maintain existing hover and active states
161
+ - Ensure smooth transitions between category states
162
+ - Preserve responsive design for different screen sizes
163
+
164
+ ### JavaScript Architecture
165
+ - Extend existing `selectCategory()` function
166
+ - Add new `updateCategoryButton()` function
167
+ - Maintain compatibility with existing filter system
168
+
169
+ ### Performance
170
+ - Minimize DOM manipulations during button updates
171
+ - Cache category configurations for quick access
172
+ - Ensure smooth animations don't impact performance
.kiro/specs/category-search-fix/requirements.md ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Requirements Document
2
+
3
+ ## Introduction
4
+
5
+ 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.
6
+
7
+ ## Requirements
8
+
9
+ ### Requirement 1
10
+
11
+ **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.
12
+
13
+ #### Acceptance Criteria
14
+
15
+ 1. WHEN a user clicks on "All Categories" dropdown THEN the system SHALL display a list of available categories
16
+ 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
17
+ 3. WHEN a category is selected THEN the system SHALL maintain all other search interface elements unchanged
18
+ 4. WHEN a category is selected THEN the system SHALL filter the displayed orders to match the selected category
19
+
20
+ ### Requirement 2
21
+
22
+ **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.
23
+
24
+ #### Acceptance Criteria
25
+
26
+ 1. WHEN a category is selected THEN the system SHALL replace the "All Categories" button with the selected category's button
27
+ 2. WHEN a category is selected THEN the system SHALL display the selected category's specific icon and name on the button
28
+ 3. WHEN no specific category is selected THEN the system SHALL display "All Categories" as the default button with a generic categories icon
29
+ 4. WHEN the dropdown is open THEN the system SHALL highlight the currently selected category option
30
+ 5. WHEN a category is selected THEN the system SHALL close the dropdown automatically
31
+ 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
32
+ 7. WHEN the transformed category button is clicked THEN the system SHALL open the dropdown menu to allow selecting a different category
33
+
34
+ ### Requirement 3
35
+
36
+ **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.
37
+
38
+ #### Acceptance Criteria
39
+
40
+ 1. WHEN a category is selected AND other search terms are present THEN the system SHALL apply both filters simultaneously
41
+ 2. WHEN a category is selected THEN the system SHALL preserve any existing search query text
42
+ 3. WHEN the search is cleared THEN the system SHALL reset the category selection to "All Categories"
43
+ 4. WHEN a new category is selected THEN the system SHALL immediately update the search results without requiring additional user action
.kiro/specs/category-search-fix/tasks.md ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Implementation Plan
2
+
3
+ - [x] 1. Create category configuration system
4
+
5
+
6
+ - Add JavaScript object with category configurations (icon, color, name, badge)
7
+ - Include configurations for existing games (Genshin, Starrail, WutheringWave)
8
+ - Add fallback configuration for unknown categories
9
+ - _Requirements: 2.1, 2.3, 2.6_
10
+
11
+ - [x] 2. Implement button update functionality
12
+
13
+
14
+ - Create `updateCategoryButton()` function to transform the category button
15
+ - Update button icon, name, color, and count based on selected category
16
+ - Ensure smooth CSS transitions during button transformation
17
+ - _Requirements: 2.1, 2.2, 2.6_
18
+
19
+ - [x] 3. Enhance category selection flow
20
+
21
+
22
+ - Modify `selectCategory()` function to call button update after filter change
23
+ - Ensure button updates when "All Categories" is selected (reset to default)
24
+ - Maintain existing modal closing and filter setting functionality
25
+ - _Requirements: 1.1, 1.2, 2.7_
26
+
27
+ - [x] 4. Add category count calculation
28
+
29
+
30
+ - Create function to calculate order count for each category
31
+ - Update button count display when category is selected
32
+ - Handle zero count scenarios gracefully
33
+ - _Requirements: 1.4, 2.1_
34
+
35
+ - [x] 5. Test and validate functionality
36
+
37
+
38
+
39
+ - Test category selection and button transformation for all game categories
40
+ - Verify "All Categories" reset functionality works correctly
41
+ - Ensure existing filter and modal functionality remains intact
42
+ - Test responsive behavior and CSS transitions
43
+ - _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_
.kiro/specs/console-interface-fix/design.md ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Console Interface Fix Design
2
+
3
+ ## Overview
4
+
5
+ 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.
6
+
7
+ ## Architecture
8
+
9
+ ### Component Structure
10
+ ```
11
+ Console Interface
12
+ ├── Overlay Container (backdrop)
13
+ ├── Console Window
14
+ │ ├── Header Section
15
+ │ │ ├── Icon + Title
16
+ │ │ └── Close Button
17
+ │ ├── Output Area (scrollable)
18
+ │ └── Input Section
19
+ │ ├── Prompt Indicator
20
+ │ └── Input Field
21
+ └── Animation System
22
+ ```
23
+
24
+ ### State Management
25
+ - Console visibility state (open/closed)
26
+ - Input state (active/processing/disabled)
27
+ - Output buffer for command results
28
+ - Animation states for smooth transitions
29
+
30
+ ## Components and Interfaces
31
+
32
+ ### Console Container
33
+ - **Purpose**: Main wrapper with backdrop and positioning
34
+ - **Styling**: Full-screen overlay with blur backdrop
35
+ - **Behavior**: Centers console window, handles click-outside-to-close
36
+
37
+ ### Console Window
38
+ - **Dimensions**: Fixed size (900px × 600px) with responsive adjustments
39
+ - **Styling**: Glassmorphic design with consistent border radius and shadows
40
+ - **Layout**: Flexbox with header, content, and input sections
41
+
42
+ ### Header Section
43
+ - **Height**: 70px fixed
44
+ - **Content**: Icon, title, subtitle, and close button
45
+ - **Styling**: Gradient background matching site theme
46
+ - **Interactive**: Close button with hover effects
47
+
48
+ ### Output Area
49
+ - **Height**: Flexible (450px default)
50
+ - **Scrolling**: Auto-scroll to bottom on new content
51
+ - **Styling**: Dark background with proper text contrast
52
+ - **Content**: Monospace font for terminal-like appearance
53
+
54
+ ### Input Section
55
+ - **Height**: 80px fixed
56
+ - **Layout**: Prompt symbol + input field
57
+ - **Styling**: Consistent with site's input styling
58
+ - **Behavior**: Focus management and command processing
59
+
60
+ ## Data Models
61
+
62
+ ### Console State
63
+ ```typescript
64
+ interface ConsoleState {
65
+ isVisible: boolean;
66
+ isProcessing: boolean;
67
+ outputLines: string[];
68
+ currentInput: string;
69
+ animationState: 'entering' | 'visible' | 'exiting' | 'hidden';
70
+ }
71
+ ```
72
+
73
+ ### Console Configuration
74
+ ```typescript
75
+ interface ConsoleConfig {
76
+ dimensions: {
77
+ width: number;
78
+ height: number;
79
+ headerHeight: number;
80
+ inputHeight: number;
81
+ };
82
+ styling: {
83
+ borderRadius: string;
84
+ backdropBlur: string;
85
+ backgroundColor: string;
86
+ };
87
+ animations: {
88
+ enterDuration: number;
89
+ exitDuration: number;
90
+ typingSpeed: number;
91
+ };
92
+ }
93
+ ```
94
+
95
+ ## Error Handling
96
+
97
+ ### Visual Error States
98
+ - **Invalid Commands**: Display error messages in red text
99
+ - **Connection Issues**: Show appropriate status indicators
100
+ - **Processing Errors**: Clear error messaging with recovery options
101
+
102
+ ### Fallback Behaviors
103
+ - **Animation Failures**: Graceful degradation to instant show/hide
104
+ - **Styling Issues**: Fallback to basic styling if glassmorphic effects fail
105
+ - **Input Problems**: Basic text input as fallback
106
+
107
+ ## Testing Strategy
108
+
109
+ ### Visual Testing
110
+ - Cross-browser compatibility testing
111
+ - Responsive design testing on various screen sizes
112
+ - Animation performance testing
113
+ - Color contrast and accessibility testing
114
+
115
+ ### Functional Testing
116
+ - Console open/close functionality
117
+ - Input field behavior and command processing
118
+ - Scroll behavior in output area
119
+ - Keyboard navigation and shortcuts
120
+
121
+ ### Integration Testing
122
+ - Integration with existing authentication system
123
+ - Compatibility with site's existing CSS framework
124
+ - Performance impact on page load and interactions
125
+
126
+ ## Implementation Notes
127
+
128
+ ### CSS Improvements
129
+ - Fix any conflicting styles causing visual artifacts
130
+ - Ensure proper z-index layering
131
+ - Optimize animations for smooth performance
132
+ - Implement proper responsive breakpoints
133
+
134
+ ### JavaScript Enhancements
135
+ - Improve event handling for better reliability
136
+ - Add proper error handling for edge cases
137
+ - Optimize animation timing and easing
138
+ - Implement proper cleanup on component unmount
139
+
140
+ ### Accessibility Considerations
141
+ - Proper ARIA labels for screen readers
142
+ - Keyboard navigation support
143
+ - High contrast mode compatibility
144
+ - Focus management for modal behavior
145
+
146
+ ## Design Decisions
147
+
148
+ ### Styling Approach
149
+ - **Glassmorphic Theme**: Maintain consistency with site's existing design
150
+ - **Monospace Typography**: Use system monospace fonts for terminal feel
151
+ - **Color Scheme**: Dark theme with white/gray text for readability
152
+
153
+ ### Animation Strategy
154
+ - **Smooth Transitions**: Use CSS transitions with appropriate easing
155
+ - **Performance**: Prefer CSS animations over JavaScript for better performance
156
+ - **Accessibility**: Respect user's motion preferences
157
+
158
+ ### Responsive Design
159
+ - **Desktop First**: Optimize for desktop admin usage
160
+ - **Mobile Adaptation**: Scale appropriately for smaller screens
161
+ - **Touch Friendly**: Ensure interactive elements are appropriately sized
.kiro/specs/console-interface-fix/requirements.md ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Console Interface Fix Requirements
2
+
3
+ ## Introduction
4
+
5
+ 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.
6
+
7
+ ## Requirements
8
+
9
+ ### Requirement 1
10
+
11
+ **User Story:** As an administrator, I want the Luna Console to display properly without visual glitches, so that I can access administrative functions reliably.
12
+
13
+ #### Acceptance Criteria
14
+
15
+ 1. WHEN the console is opened THEN the interface SHALL display without visual artifacts or layout issues
16
+ 2. WHEN the console header is rendered THEN it SHALL show the correct title "Luna Console" and subtitle "Administrative Terminal" with proper styling
17
+ 3. WHEN the console window appears THEN it SHALL have consistent glassmorphic styling matching the site's design theme
18
+ 4. WHEN the console is displayed THEN all text SHALL be clearly readable with proper contrast
19
+
20
+ ### Requirement 2
21
+
22
+ **User Story:** As an administrator, I want the console close button to work properly, so that I can dismiss the console when needed.
23
+
24
+ #### Acceptance Criteria
25
+
26
+ 1. WHEN I click the close button (X) THEN the console SHALL close immediately
27
+ 2. WHEN the close button is hovered THEN it SHALL show appropriate hover effects
28
+ 3. WHEN the console is open THEN the close button SHALL be clearly visible and accessible
29
+
30
+ ### Requirement 3
31
+
32
+ **User Story:** As an administrator, I want the console input area to function correctly, so that I can enter commands without issues.
33
+
34
+ #### Acceptance Criteria
35
+
36
+ 1. WHEN the console is open THEN the input field SHALL be properly positioned and styled
37
+ 2. WHEN I type in the console input THEN the text SHALL appear clearly without visual artifacts
38
+ 3. WHEN the console is in processing state THEN it SHALL show appropriate loading indicators
39
+ 4. WHEN commands are entered THEN the console output SHALL display properly formatted text
40
+
41
+ ### Requirement 4
42
+
43
+ **User Story:** As an administrator, I want the console animations to work smoothly, so that the interface feels polished and professional.
44
+
45
+ #### Acceptance Criteria
46
+
47
+ 1. WHEN the console opens THEN it SHALL animate smoothly into view
48
+ 2. WHEN the console closes THEN it SHALL animate smoothly out of view
49
+ 3. WHEN console text is being typed THEN animations SHALL be smooth and not cause visual glitches
50
+ 4. WHEN the console state changes THEN transitions SHALL be fluid and professional
51
+
52
+ ### Requirement 5
53
+
54
+ **User Story:** As an administrator, I want the console to be responsive, so that it works properly on different screen sizes.
55
+
56
+ #### Acceptance Criteria
57
+
58
+ 1. WHEN viewed on desktop THEN the console SHALL display at appropriate size with proper proportions
59
+ 2. WHEN viewed on mobile devices THEN the console SHALL adapt to smaller screens appropriately
60
+ 3. WHEN the browser window is resized THEN the console SHALL maintain proper layout and functionality
.kiro/specs/console-interface-fix/tasks.md ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Console Interface Fix Implementation Plan
2
+
3
+ - [x] 1. Analyze and fix console CSS styling issues
4
+
5
+
6
+
7
+ - Identify conflicting CSS rules causing visual artifacts
8
+ - Fix glassmorphic styling inconsistencies
9
+ - Ensure proper z-index layering for modal behavior
10
+
11
+
12
+
13
+ - _Requirements: 1.1, 1.3_
14
+
15
+ - [ ] 2. Fix console header layout and styling
16
+ - Correct header section positioning and dimensions
17
+ - Fix icon and title alignment issues
18
+ - Ensure proper gradient background rendering
19
+ - Style close button with appropriate hover effects
20
+ - _Requirements: 1.2, 2.2_
21
+
22
+ - [ ] 3. Repair console output area functionality
23
+ - Fix output area scrolling behavior
24
+ - Ensure proper text rendering without artifacts
25
+ - Implement auto-scroll to bottom for new content
26
+ - Fix monospace font rendering and line spacing
27
+ - _Requirements: 3.4, 1.4_
28
+
29
+ - [ ] 4. Fix console input section styling and behavior
30
+ - Repair input field positioning and styling
31
+ - Fix prompt indicator alignment
32
+ - Ensure proper focus states and cursor visibility
33
+ - Fix input text rendering and character spacing
34
+ - _Requirements: 3.1, 3.2_
35
+
36
+ - [ ] 5. Optimize console animations and transitions
37
+ - Fix jerky or broken opening/closing animations
38
+ - Smooth out typing animations and state transitions
39
+ - Ensure consistent animation timing and easing
40
+ - Fix any animation-related visual glitches
41
+ - _Requirements: 4.1, 4.2, 4.3, 4.4_
42
+
43
+ - [ ] 6. Implement responsive design fixes
44
+ - Fix console sizing issues on different screen sizes
45
+ - Ensure proper mobile responsiveness
46
+ - Fix layout issues when browser window is resized
47
+ - Implement appropriate breakpoints for different devices
48
+ - _Requirements: 5.1, 5.2, 5.3_
49
+
50
+ - [ ] 7. Fix console close button functionality
51
+ - Ensure close button click events work properly
52
+ - Fix any event propagation issues
53
+ - Implement proper cleanup when console closes
54
+ - Test close functionality across different browsers
55
+ - _Requirements: 2.1, 2.3_
56
+
57
+ - [ ] 8. Test and validate console interface fixes
58
+ - Test console functionality across different browsers
59
+ - Validate visual consistency with site theme
60
+ - Test responsive behavior on various screen sizes
61
+ - Verify all animations work smoothly
62
+ - _Requirements: 1.1, 2.1, 3.1, 4.1, 5.1_
.kiro/specs/dashboard-analytics-enhancement/design.md ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dashboard Analytics Enhancement - Design Document
2
+
3
+ ## Overview
4
+
5
+ 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.
6
+
7
+ ## Architecture
8
+
9
+ ### Frontend Components
10
+ - **Dashboard Controller**: Main dashboard view with real-time data binding
11
+ - **Chart Components**: Reusable chart widgets (Line, Bar, Doughnut, Area)
12
+ - **Metric Cards**: Glassmorphic cards displaying KPIs with animations
13
+ - **Time Filter**: Interactive period selector (Daily/Weekly/Monthly)
14
+ - **Real-time Updates**: WebSocket integration for live data
15
+
16
+ ### Backend Services
17
+ - **Analytics Service**: Calculates revenue, profit, and sales metrics
18
+ - **Chart Data API**: Provides formatted data for different chart types
19
+ - **Real-time Service**: WebSocket server for live updates
20
+ - **Caching Layer**: Redis for performance optimization
21
+
22
+ ### Database Schema
23
+ - **analytics_snapshots**: Hourly/daily aggregated data
24
+ - **revenue_tracking**: Real-time revenue calculations
25
+ - **profit_margins**: Product-specific profit calculations
26
+
27
+ ## Components and Interfaces
28
+
29
+ ### 1. Dashboard Layout
30
+ ```
31
+ ┌─────────────────────────────────────────────────────────┐
32
+ │ Header: Time Filter (Daily/Weekly/Monthly) │
33
+ ├─────────────────────────────────────────────────────────┤
34
+ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
35
+ │ │Revenue │ │ Profit │ │ Orders │ │ Customers│ │
36
+ │ │ Card │ │ Card │ │ Card │ │ Card │ │
37
+ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
38
+ ├─────────────────────────────────────────────────────────┤
39
+ │ ┌─────────────────────┐ ┌─────────────────────────────┐ │
40
+ │ │ Revenue Chart │ │ Profit Trend Chart │ │
41
+ │ │ (Line/Area) │ │ (Line/Bar) │ │
42
+ │ └─────────────────────┘ └─────────────────────────────┘ │
43
+ ├─────────────────────────────────────────────────────────┤
44
+ │ ┌─────────────────────┐ ┌─────────────────────────────┐ │
45
+ │ │ Top Products Chart │ │ Customer Analytics │ │
46
+ │ │ (Doughnut/Bar) │ │ (Mixed Chart) │ │
47
+ │ └─────────────────────┘ └─────────────────────────────┘ │
48
+ └─────────────────────────────────────────────────────────┘
49
+ ```
50
+
51
+ ### 2. Metric Cards Design
52
+ - **Glassmorphic Background**: `bg-white/5 backdrop-blur-2xl`
53
+ - **Animated Numbers**: CountUp.js for smooth number transitions
54
+ - **Trend Indicators**: Up/down arrows with color coding
55
+ - **Glow Effects**: Subtle shadows and text glows
56
+
57
+ ### 3. Chart Configuration
58
+ - **Chart.js Integration**: Modern, interactive charts
59
+ - **Glassmorphic Styling**: Transparent backgrounds, subtle borders
60
+ - **Color Scheme**:
61
+ - Revenue: Emerald gradient (`from-emerald-400 to-emerald-600`)
62
+ - Profit: Purple gradient (`from-purple-400 to-purple-600`)
63
+ - Orders: Blue gradient (`from-blue-400 to-blue-600`)
64
+ - Customers: Orange gradient (`from-orange-400 to-orange-600`)
65
+
66
+ ### 4. Real-time Updates
67
+ - **WebSocket Connection**: Live data streaming
68
+ - **Smooth Animations**: Chart data updates with transitions
69
+ - **Performance Optimization**: Throttled updates (max 1/second)
70
+
71
+ ## Data Models
72
+
73
+ ### Analytics Snapshot
74
+ ```php
75
+ class AnalyticsSnapshot extends Model
76
+ {
77
+ protected $fillable = [
78
+ 'date',
79
+ 'hour',
80
+ 'revenue',
81
+ 'profit',
82
+ 'orders_count',
83
+ 'customers_count',
84
+ 'avg_order_value',
85
+ 'conversion_rate'
86
+ ];
87
+
88
+ protected $casts = [
89
+ 'date' => 'date',
90
+ 'revenue' => 'decimal:2',
91
+ 'profit' => 'decimal:2',
92
+ 'avg_order_value' => 'decimal:2',
93
+ 'conversion_rate' => 'decimal:4'
94
+ ];
95
+ }
96
+ ```
97
+
98
+ ### Revenue Tracking
99
+ ```php
100
+ class RevenueTracking extends Model
101
+ {
102
+ protected $fillable = [
103
+ 'order_id',
104
+ 'amount',
105
+ 'profit_margin',
106
+ 'created_at'
107
+ ];
108
+
109
+ protected $casts = [
110
+ 'amount' => 'decimal:2',
111
+ 'profit_margin' => 'decimal:4'
112
+ ];
113
+ }
114
+ ```
115
+
116
+ ## Error Handling
117
+
118
+ ### Chart Loading States
119
+ - **Loading Skeletons**: Animated placeholders while data loads
120
+ - **Error States**: Graceful fallbacks for failed API calls
121
+ - **Retry Mechanisms**: Automatic retry for failed WebSocket connections
122
+
123
+ ### Data Validation
124
+ - **Input Sanitization**: All user inputs validated and sanitized
125
+ - **Date Range Validation**: Ensure valid date ranges for filtering
126
+ - **Performance Limits**: Prevent excessive data requests
127
+
128
+ ## Testing Strategy
129
+
130
+ ### Unit Tests
131
+ - **Analytics Service**: Test revenue/profit calculations
132
+ - **Chart Data API**: Verify data formatting and aggregation
133
+ - **Real-time Service**: Test WebSocket connections and updates
134
+
135
+ ### Integration Tests
136
+ - **Dashboard Loading**: Test complete dashboard initialization
137
+ - **Time Filter**: Verify period switching functionality
138
+ - **Real-time Updates**: Test live data streaming
139
+
140
+ ### Performance Tests
141
+ - **Chart Rendering**: Measure chart load times with large datasets
142
+ - **WebSocket Performance**: Test concurrent connection limits
143
+ - **Database Queries**: Optimize analytics query performance
144
+
145
+ ### Browser Tests
146
+ - **Cross-browser Compatibility**: Test on Chrome, Firefox, Safari, Edge
147
+ - **Responsive Design**: Verify layout on different screen sizes
148
+ - **Chart Interactions**: Test hover, click, and zoom functionality
.kiro/specs/dashboard-analytics-enhancement/requirements.md ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dashboard Analytics Enhancement - Requirements Document
2
+
3
+ ## Introduction
4
+
5
+ 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.
6
+
7
+ ## Requirements
8
+
9
+ ### Requirement 1
10
+
11
+ **User Story:** As an admin, I want to see real-time revenue analytics, so that I can monitor business performance instantly.
12
+
13
+ #### Acceptance Criteria
14
+
15
+ 1. WHEN I access the dashboard THEN the system SHALL display current day revenue in real-time
16
+ 2. WHEN revenue data changes THEN the dashboard SHALL update automatically without page refresh
17
+ 3. WHEN viewing revenue metrics THEN the system SHALL show percentage change from previous period
18
+ 4. WHEN displaying revenue THEN the system SHALL format currency properly with ฿ symbol
19
+
20
+ ### Requirement 2
21
+
22
+ **User Story:** As an admin, I want interactive time-based filtering, so that I can analyze performance across different periods.
23
+
24
+ #### Acceptance Criteria
25
+
26
+ 1. WHEN I select daily view THEN the system SHALL show hourly revenue breakdown for today
27
+ 2. WHEN I select weekly view THEN the system SHALL show daily revenue for the current week
28
+ 3. WHEN I select monthly view THEN the system SHALL show daily revenue for the current month
29
+ 4. WHEN switching time periods THEN charts SHALL animate smoothly to new data
30
+
31
+ ### Requirement 3
32
+
33
+ **User Story:** As an admin, I want profit analysis with visual charts, so that I can understand business profitability trends.
34
+
35
+ #### Acceptance Criteria
36
+
37
+ 1. WHEN viewing profit metrics THEN the system SHALL calculate profit as revenue minus costs
38
+ 2. WHEN displaying profit charts THEN the system SHALL use different colors for profit vs revenue
39
+ 3. WHEN showing profit trends THEN the system SHALL indicate positive/negative changes clearly
40
+ 4. WHEN viewing profit data THEN the system SHALL show profit margins as percentages
41
+
42
+ ### Requirement 4
43
+
44
+ **User Story:** As an admin, I want comprehensive sales analytics, so that I can track product performance and customer behavior.
45
+
46
+ #### Acceptance Criteria
47
+
48
+ 1. WHEN viewing sales data THEN the system SHALL show top-selling products with quantities
49
+ 2. WHEN displaying customer metrics THEN the system SHALL show new vs returning customers
50
+ 3. WHEN viewing order analytics THEN the system SHALL show average order value trends
51
+ 4. WHEN showing product performance THEN the system SHALL display conversion rates
52
+
53
+ ### Requirement 5
54
+
55
+ **User Story:** As an admin, I want a modern glassmorphic dashboard design, so that the interface matches the existing UI aesthetic.
56
+
57
+ #### Acceptance Criteria
58
+
59
+ 1. WHEN viewing dashboard cards THEN they SHALL use glassmorphic design with backdrop blur
60
+ 2. WHEN displaying charts THEN they SHALL have transparent backgrounds with subtle borders
61
+ 3. WHEN showing metrics THEN they SHALL use consistent color scheme with glowing effects
62
+ 4. WHEN interacting with elements THEN they SHALL have smooth hover animations
63
+
64
+ ### Requirement 6
65
+
66
+ **User Story:** As an admin, I want responsive dashboard layout, so that I can access analytics on different devices.
67
+
68
+ #### Acceptance Criteria
69
+
70
+ 1. WHEN viewing on desktop THEN the dashboard SHALL display in multi-column grid layout
71
+ 2. WHEN viewing on tablet THEN the dashboard SHALL adapt to smaller screen with proper spacing
72
+ 3. WHEN viewing on mobile THEN the dashboard SHALL stack cards vertically for optimal viewing
73
+ 4. WHEN resizing browser THEN charts SHALL resize smoothly to fit available space
.kiro/specs/dashboard-analytics-enhancement/tasks.md ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Implementation Plan
2
+
3
+ - [x] 1. Set up database structure and analytics models
4
+
5
+
6
+ - Create analytics_snapshots migration with date, hour, revenue, profit, orders_count, customers_count columns
7
+ - Create revenue_tracking migration with order_id, amount, profit_margin, timestamps
8
+ - Implement AnalyticsSnapshot model with proper casts and relationships
9
+ - Implement RevenueTracking model with decimal casting for financial data
10
+ - _Requirements: 1.1, 3.1, 4.1_
11
+
12
+
13
+
14
+ - [ ] 2. Create analytics service for data calculations
15
+ - Implement AnalyticsService class with revenue calculation methods
16
+ - Create profit calculation logic (revenue - costs) with margin percentages
17
+ - Build aggregation methods for daily, weekly, monthly data grouping
18
+
19
+
20
+
21
+ - Add real-time data update methods for live dashboard updates
22
+ - _Requirements: 1.1, 1.2, 3.1, 3.2, 3.3_
23
+
24
+ - [ ] 3. Build chart data API endpoints
25
+ - Create DashboardController with analytics data endpoints
26
+
27
+
28
+ - Implement /api/dashboard/revenue endpoint with time period filtering
29
+ - Create /api/dashboard/profit endpoint with trend calculations
30
+ - Build /api/dashboard/sales endpoint with product performance data
31
+ - Add /api/dashboard/customers endpoint with new vs returning metrics
32
+ - _Requirements: 2.1, 2.2, 2.3, 4.1, 4.2, 4.3_
33
+
34
+
35
+
36
+ - [ ] 4. Install and configure Chart.js with glassmorphic styling
37
+ - Install Chart.js via npm and configure webpack compilation
38
+ - Create base chart configuration with glassmorphic theme (transparent backgrounds, subtle borders)
39
+ - Implement color scheme constants (emerald for revenue, purple for profit, blue for orders, orange for customers)
40
+
41
+
42
+
43
+ - Configure chart animations and smooth transitions for data updates
44
+ - _Requirements: 2.4, 5.1, 5.2, 5.3_
45
+
46
+ - [x] 5. Create glassmorphic metric cards component
47
+
48
+
49
+
50
+ - Build MetricCard blade component with glassmorphic styling (bg-white/5 backdrop-blur-2xl)
51
+ - Implement animated number display using CountUp.js for smooth transitions
52
+ - Add trend indicators with up/down arrows and color-coded percentage changes
53
+ - Create glow effects with text shadows and subtle box shadows
54
+ - _Requirements: 1.3, 3.3, 5.1, 5.3, 5.4_
55
+
56
+ - [ ] 6. Implement time period filter component
57
+ - Create TimeFilter blade component with Daily/Weekly/Monthly buttons
58
+ - Add active state styling with glassmorphic selected appearance
59
+ - Implement JavaScript for period switching with smooth chart transitions
60
+ - Connect filter to chart data updates via AJAX calls
61
+ - _Requirements: 2.1, 2.2, 2.3, 2.4_
62
+
63
+ - [ ] 7. Build main dashboard layout with responsive grid
64
+ - Create enhanced dashboard.blade.php with responsive CSS Grid layout
65
+ - Implement 4-column metric cards row for desktop, 2-column for tablet, 1-column for mobile
66
+ - Add 2-column chart grid for desktop, single column for mobile
67
+ - Ensure proper spacing and glassmorphic card styling throughout
68
+ - _Requirements: 5.1, 5.2, 6.1, 6.2, 6.3_
69
+
70
+ - [ ] 8. Create revenue chart component
71
+ - Build RevenueChart JavaScript class using Chart.js Line/Area chart
72
+ - Implement data fetching from /api/dashboard/revenue endpoint
73
+ - Add smooth animations and hover effects with glassmorphic tooltips
74
+ - Configure responsive chart sizing that adapts to container width
75
+ - _Requirements: 1.1, 1.2, 2.1, 2.4, 6.4_
76
+
77
+ - [ ] 9. Implement profit trend chart
78
+ - Create ProfitChart JavaScript class with Line/Bar chart combination
79
+ - Add profit vs revenue comparison with different colored datasets
80
+ - Implement profit margin percentage display in chart tooltips
81
+ - Configure positive/negative profit indication with green/red colors
82
+ - _Requirements: 3.1, 3.2, 3.3, 2.4_
83
+
84
+ - [ ] 10. Build top products performance chart
85
+ - Create ProductsChart JavaScript class using Doughnut/Bar chart
86
+ - Implement data fetching from sales endpoint with product quantities
87
+ - Add interactive hover effects showing product details
88
+ - Configure chart to show top 10 products with "Others" category for remaining
89
+ - _Requirements: 4.1, 4.4, 5.3, 5.4_
90
+
91
+ - [ ] 11. Create customer analytics chart
92
+ - Build CustomerChart JavaScript class with mixed chart type (Line + Bar)
93
+ - Implement new vs returning customers visualization
94
+ - Add average order value trend line overlay
95
+ - Configure conversion rate display with percentage formatting
96
+ - _Requirements: 4.2, 4.3, 4.4_
97
+
98
+ - [ ] 12. Implement real-time WebSocket updates
99
+ - Install and configure Laravel WebSockets or Pusher for real-time updates
100
+ - Create WebSocket events for revenue, profit, and order updates
101
+ - Implement JavaScript WebSocket client with automatic reconnection
102
+ - Add throttled update mechanism (max 1 update per second) for performance
103
+ - _Requirements: 1.2, 1.3, 5.4_
104
+
105
+ - [ ] 13. Add loading states and error handling
106
+ - Create animated skeleton loaders for charts while data is loading
107
+ - Implement error states with retry buttons for failed API calls
108
+ - Add graceful fallbacks when WebSocket connection fails
109
+ - Create user-friendly error messages with glassmorphic styling
110
+ - _Requirements: 5.4, 6.4_
111
+
112
+ - [ ] 14. Optimize performance and add caching
113
+ - Implement Redis caching for analytics data with 5-minute TTL
114
+ - Add database query optimization with proper indexes
115
+ - Configure chart data pagination for large datasets
116
+ - Implement lazy loading for charts that are not immediately visible
117
+ - _Requirements: 1.2, 6.4_
118
+
119
+ - [ ] 15. Create comprehensive test suite
120
+ - Write unit tests for AnalyticsService revenue and profit calculations
121
+ - Create integration tests for dashboard API endpoints
122
+ - Add browser tests for chart interactions and responsive design
123
+ - Implement performance tests for WebSocket connections and chart rendering
124
+ - _Requirements: 1.1, 2.4, 3.1, 4.1, 6.1, 6.2, 6.3_
.kiro/specs/dashboard-time-filter-fix/design.md ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dashboard Time Filter Fix - Design Document
2
+
3
+ ## Overview
4
+
5
+ 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.
6
+
7
+ ## Architecture
8
+
9
+ ### Component Structure
10
+ ```
11
+ Dashboard View
12
+ ├── Time Filter Component (x-time-filter)
13
+ ├── Metric Cards (with period-aware comparisons)
14
+ ├── Chart Containers (with proper data binding)
15
+ └── Dashboard Manager (Alpine.js component)
16
+ ```
17
+
18
+ ### Data Flow
19
+ ```
20
+ User Clicks Filter → Dashboard Manager → API Request → Data Processing → Chart Update → Metric Card Update
21
+ ```
22
+
23
+ ## Components and Interfaces
24
+
25
+ ### 1. Time Filter Component Enhancement
26
+
27
+ **File:** `resources/views/components/time-filter.blade.php`
28
+
29
+ **Current Issues:**
30
+ - Filter buttons don't properly communicate with dashboard
31
+ - No visual feedback for active state
32
+ - Missing loading states
33
+
34
+ **Design Solution:**
35
+ - Add Alpine.js integration with dashboard manager
36
+ - Implement proper active state styling
37
+ - Add loading indicators during data fetch
38
+ - Use event dispatching for filter changes
39
+
40
+ **Interface:**
41
+ ```php
42
+ <div x-data="{ activeFilter: 'daily' }" class="time-filter">
43
+ <button @click="changeFilter('daily')" :class="activeFilter === 'daily' ? 'active' : ''">Daily</button>
44
+ <button @click="changeFilter('weekly')" :class="activeFilter === 'weekly' ? 'active' : ''">Weekly</button>
45
+ <button @click="changeFilter('monthly')" :class="activeFilter === 'monthly' ? 'active' : ''">Monthly</button>
46
+ </div>
47
+ ```
48
+
49
+ ### 2. Dashboard Manager JavaScript Enhancement
50
+
51
+ **File:** `resources/views/dashboard.blade.php` (JavaScript section)
52
+
53
+ **Current Issues:**
54
+ - Charts not initializing properly
55
+ - No period-aware data fetching
56
+ - Missing chart update functionality
57
+
58
+ **Design Solution:**
59
+ - Fix chart initialization sequence
60
+ - Implement period-aware API calls
61
+ - Add chart update methods for filter changes
62
+ - Proper error handling and loading states
63
+
64
+ **Key Methods:**
65
+ ```javascript
66
+ dashboardManager() {
67
+ return {
68
+ currentPeriod: 'daily',
69
+ loading: false,
70
+ charts: {},
71
+
72
+ changeFilter(period) {
73
+ this.currentPeriod = period;
74
+ this.refreshDashboard();
75
+ },
76
+
77
+ async refreshDashboard() {
78
+ this.loading = true;
79
+ await this.updateCharts();
80
+ await this.updateMetrics();
81
+ this.loading = false;
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ ### 3. API Controller Enhancement
88
+
89
+ **File:** `app/Http/Controllers/Api/DashboardController.php`
90
+
91
+ **Current Issues:**
92
+ - Period parameter not properly handled
93
+ - Chart data format inconsistent
94
+ - Missing proper date range calculations
95
+
96
+ **Design Solution:**
97
+ - Enhance period handling logic
98
+ - Standardize chart data format
99
+ - Implement proper date range calculations for each period
100
+ - Add data aggregation for weekly/monthly views
101
+
102
+ **Data Format:**
103
+ ```php
104
+ // Daily: Hourly data points (0-23)
105
+ // Weekly: Daily data points (7 days)
106
+ // Monthly: Daily data points (30/31 days)
107
+ [
108
+ 'chart_data' => [
109
+ ['label' => '00:00', 'revenue' => 1000, 'profit' => 300, 'orders' => 5],
110
+ // ... more data points
111
+ ],
112
+ 'summary' => [
113
+ 'current' => 5000,
114
+ 'previous' => 4500,
115
+ 'percentage_change' => 11.11,
116
+ 'trend' => 'up'
117
+ ]
118
+ ]
119
+ ```
120
+
121
+ ### 4. Analytics Service Enhancement
122
+
123
+ **File:** `app/Services/AnalyticsService.php`
124
+
125
+ **Current Issues:**
126
+ - getAggregatedData method needs period-specific logic
127
+ - Comparison calculations need previous period data
128
+ - Chart data formatting inconsistent
129
+
130
+ **Design Solution:**
131
+ - Implement period-specific data aggregation
132
+ - Add previous period comparison methods
133
+ - Standardize data formatting across all methods
134
+ - Optimize database queries for different time ranges
135
+
136
+ ## Data Models
137
+
138
+ ### Chart Data Structure
139
+ ```php
140
+ interface ChartDataPoint {
141
+ label: string; // "00:00", "Monday", "Jan 15"
142
+ revenue: number;
143
+ profit: number;
144
+ orders: number;
145
+ customers: number;
146
+ }
147
+ ```
148
+
149
+ ### Metric Summary Structure
150
+ ```php
151
+ interface MetricSummary {
152
+ current: number;
153
+ previous: number;
154
+ percentage_change: number;
155
+ trend: 'up' | 'down' | 'neutral';
156
+ period: 'daily' | 'weekly' | 'monthly';
157
+ }
158
+ ```
159
+
160
+ ## Error Handling
161
+
162
+ ### Frontend Error Handling
163
+ - Chart initialization failures → Show error message in chart container
164
+ - API request failures → Display "Unable to load data" message
165
+ - Network timeouts → Retry mechanism with exponential backoff
166
+
167
+ ### Backend Error Handling
168
+ - Database query failures → Return empty data with success: false
169
+ - Invalid period parameters → Default to 'daily' with warning
170
+ - Missing data → Return zeros with appropriate messaging
171
+
172
+ ## Testing Strategy
173
+
174
+ ### Unit Tests
175
+ - AnalyticsService period calculations
176
+ - API controller response formats
177
+ - Chart data transformation methods
178
+
179
+ ### Integration Tests
180
+ - Full dashboard load with different periods
181
+ - Filter switching functionality
182
+ - API endpoint responses for all periods
183
+
184
+ ### Manual Testing Scenarios
185
+ 1. Load dashboard → Verify daily view works
186
+ 2. Click Weekly → Verify charts update with weekly data
187
+ 3. Click Monthly → Verify charts update with monthly data
188
+ 4. Refresh page → Verify default daily view loads
189
+ 5. Network offline → Verify error handling
190
+
191
+ ## Implementation Priority
192
+
193
+ ### Phase 1: Core Functionality
194
+ 1. Fix chart initialization
195
+ 2. Implement basic period switching
196
+ 3. Update API endpoints for proper period handling
197
+
198
+ ### Phase 2: Enhanced Features
199
+ 1. Add loading states and animations
200
+ 2. Implement proper error handling
201
+ 3. Add URL state management
202
+
203
+ ### Phase 3: Polish
204
+ 1. Smooth transitions between periods
205
+ 2. Advanced error recovery
206
+ 3. Performance optimizations
207
+
208
+ ## Performance Considerations
209
+
210
+ - Cache API responses for 30 seconds to reduce server load
211
+ - Lazy load charts only when visible
212
+ - Debounce filter changes to prevent rapid API calls
213
+ - Use efficient database queries with proper indexing
214
+
215
+ ## Security Considerations
216
+
217
+ - Validate period parameters on backend
218
+ - Sanitize date inputs to prevent SQL injection
219
+ - Rate limit API endpoints to prevent abuse
220
+ - Ensure proper authentication for dashboard access
.kiro/specs/dashboard-time-filter-fix/requirements.md ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dashboard Time Filter Fix - Requirements Document
2
+
3
+ ## Introduction
4
+
5
+ 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.
6
+
7
+ ## Requirements
8
+
9
+ ### Requirement 1
10
+
11
+ **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.
12
+
13
+ #### Acceptance Criteria
14
+
15
+ 1. WHEN user clicks "Daily" filter THEN dashboard SHALL display hourly data for the current day
16
+ 2. WHEN user clicks "Weekly" filter THEN dashboard SHALL display daily data for the current week
17
+ 3. WHEN user clicks "Monthly" filter THEN dashboard SHALL display daily data for the current month
18
+ 4. WHEN time period changes THEN all charts SHALL update to reflect the selected timeframe
19
+ 5. WHEN time period changes THEN metric cards SHALL show comparison data from previous period (previous day/week/month)
20
+
21
+ ### Requirement 2
22
+
23
+ **User Story:** As a dashboard user, I want to see working charts with real data, so that I can visualize my store's performance.
24
+
25
+ #### Acceptance Criteria
26
+
27
+ 1. WHEN dashboard loads THEN revenue chart SHALL display with real data points
28
+ 2. WHEN dashboard loads THEN profit chart SHALL show revenue vs profit comparison
29
+ 3. WHEN dashboard loads THEN products chart SHALL display top products by revenue
30
+ 4. WHEN dashboard loads THEN customers chart SHALL show customer activity trends
31
+ 5. WHEN time period changes THEN all charts SHALL re-render with appropriate data
32
+
33
+ ### Requirement 3
34
+
35
+ **User Story:** As a dashboard user, I want metric cards to show meaningful comparisons, so that I can understand performance trends.
36
+
37
+ #### Acceptance Criteria
38
+
39
+ 1. WHEN viewing daily data THEN metric cards SHALL compare with previous day
40
+ 2. WHEN viewing weekly data THEN metric cards SHALL compare with previous week
41
+ 3. WHEN viewing monthly data THEN metric cards SHALL compare with previous month
42
+ 4. WHEN comparison data is available THEN cards SHALL show percentage change and trend direction
43
+ 5. WHEN no comparison data exists THEN cards SHALL show "No previous data" message
44
+
45
+ ### Requirement 4
46
+
47
+ **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.
48
+
49
+ #### Acceptance Criteria
50
+
51
+ 1. WHEN dashboard loads THEN "Daily" filter SHALL be selected by default
52
+ 2. WHEN user clicks a time filter THEN it SHALL become visually active
53
+ 3. WHEN time filter is clicked THEN loading state SHALL be shown during data fetch
54
+ 4. WHEN data loading completes THEN loading state SHALL be removed
55
+ 5. WHEN filter changes THEN URL SHALL update to reflect current selection (optional)
.kiro/specs/dashboard-time-filter-fix/tasks.md ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Implementation Plan
2
+
3
+ - [ ] 1. Fix time filter component integration
4
+ - Update time-filter component to properly communicate with dashboard manager
5
+ - Add Alpine.js event dispatching for filter changes
6
+ - Implement active state styling and loading indicators
7
+ - _Requirements: 4.1, 4.2, 4.3_
8
+
9
+ - [ ] 2. Enhance dashboard JavaScript manager
10
+ - Fix chart initialization sequence and error handling
11
+ - Implement changeFilter method to handle period switching
12
+ - Add refreshDashboard method to update all components
13
+ - Create proper loading states during data fetching
14
+ - _Requirements: 1.4, 2.5, 4.4_
15
+
16
+ - [ ] 3. Update API controller period handling
17
+ - Enhance period parameter processing in all dashboard API methods
18
+ - Implement proper date range calculations for daily/weekly/monthly
19
+ - Standardize chart data format across all endpoints
20
+ - Add previous period comparison data to all responses
21
+ - _Requirements: 1.1, 1.2, 1.3, 3.1, 3.2, 3.3_
22
+
23
+ - [ ] 4. Fix analytics service data aggregation
24
+ - Update getAggregatedData method with period-specific logic
25
+ - Implement previous period calculation methods
26
+ - Add proper data formatting for different time ranges
27
+ - Optimize database queries for weekly and monthly aggregation
28
+ - _Requirements: 1.1, 1.2, 1.3, 3.1, 3.2, 3.3_
29
+
30
+ - [ ] 5. Update metric cards for period-aware comparisons
31
+ - Modify dashboard view to pass period-specific comparison data
32
+ - Update metric card component to handle different comparison types
33
+ - Add proper messaging for missing comparison data
34
+ - Implement percentage change calculations for all periods
35
+ - _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5_
36
+
37
+ - [ ] 6. Test and validate all functionality
38
+ - Test daily, weekly, and monthly filter switching
39
+ - Verify all charts render correctly with real data
40
+ - Validate metric card comparisons for all periods
41
+ - Test error handling and loading states
42
+ - Ensure proper default state (daily) on page load
43
+ - _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_
.kiro/specs/search-glassmorphism-fix/design.md ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Search Results Glassmorphism Fix - Design Document
2
+
3
+ ## Overview
4
+
5
+ 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.
6
+
7
+ ## Architecture
8
+
9
+ The search results dropdown is implemented using Alpine.js with Tailwind CSS classes. The main components that need improvement are:
10
+
11
+ 1. **Main Container**: The dropdown wrapper with glassmorphic background
12
+ 2. **Content Sections**: Game categories and products sections
13
+ 3. **Individual Items**: Game and product result items
14
+ 4. **Hover Preview**: Product detail popup on hover
15
+
16
+ ## Components and Interfaces
17
+
18
+ ### 1. Search Results Container
19
+ **Current Issues:**
20
+ - `bg-black/30` is too transparent (30% opacity)
21
+ - `border-white/10` is barely visible (10% opacity)
22
+ - Positioning offset `translateX(-1.5rem)` causes misalignment
23
+
24
+ **Design Solution:**
25
+ - Increase background opacity to `bg-black/70` for better frosted glass effect
26
+ - Enhance border visibility to `border-white/20`
27
+ - Adjust positioning to `translateX(-0.75rem)` for better alignment
28
+ - Add CSS backdrop-filter for enhanced blur effect
29
+
30
+ ### 2. Text Color Hierarchy
31
+ **Current Issues:**
32
+ - Loading text `text-white/60` has poor contrast
33
+ - Section headers `text-purple-300` and `text-green-300` are too dim
34
+ - Product descriptions `text-white/60` are hard to read
35
+ - Secondary text `text-white/50` lacks visibility
36
+
37
+ **Design Solution:**
38
+ ```css
39
+ Loading State: text-white/80 (improved from 60%)
40
+ Section Headers: text-purple-200, text-green-200 (brighter variants)
41
+ Primary Text: text-white (full opacity for names)
42
+ Secondary Text: text-white/70 (improved from 50%)
43
+ Description Text: text-white/80 (improved from 60%)
44
+ ```
45
+
46
+ ### 3. Visual Hierarchy Improvements
47
+ **Current Issues:**
48
+ - Small icons and images reduce visual impact
49
+ - Weak hover states provide poor feedback
50
+ - Insufficient padding creates cramped appearance
51
+
52
+ **Design Solution:**
53
+ - Increase section header icons from `w-3 h-3` to `w-4 h-4`
54
+ - Enhance product images from `w-8 h-8` to `w-10 h-10`
55
+ - Improve hover backgrounds from `hover:bg-white/5` to `hover:bg-white/10`
56
+ - Increase padding on product items from `p-2` to `p-3`
57
+
58
+ ### 4. Border and Divider Enhancement
59
+ **Current Issues:**
60
+ - `divide-white/10` dividers are barely visible
61
+ - Item borders `border-white/5` provide no visual separation
62
+
63
+ **Design Solution:**
64
+ - Strengthen dividers to `divide-white/20`
65
+ - Improve item borders to `border-white/10` with hover state `hover:border-white/20`
66
+
67
+ ## Data Models
68
+
69
+ No data model changes required. The component works with existing search API response structure:
70
+
71
+ ```javascript
72
+ {
73
+ games: [
74
+ {
75
+ name: string,
76
+ slug: string,
77
+ description: string,
78
+ icon: string,
79
+ gradient: string,
80
+ category: string,
81
+ popularity: string,
82
+ count: number
83
+ }
84
+ ],
85
+ products: [
86
+ {
87
+ id: number,
88
+ name: string,
89
+ slug: string,
90
+ game_slug: string,
91
+ price: number,
92
+ image: string,
93
+ game: string,
94
+ amount: number,
95
+ stock_color: string
96
+ }
97
+ ]
98
+ }
99
+ ```
100
+
101
+ ## Error Handling
102
+
103
+ The existing error handling remains unchanged:
104
+ - Network errors fall back to empty results
105
+ - Image loading errors use placeholder images
106
+ - Missing data properties are handled gracefully
107
+
108
+ ## Testing Strategy
109
+
110
+ ### Visual Testing
111
+ 1. **Contrast Testing**: Verify all text meets WCAG AA contrast requirements
112
+ 2. **Responsive Testing**: Ensure dropdown works across different screen sizes
113
+ 3. **Browser Testing**: Test glassmorphism effects across modern browsers
114
+
115
+ ### Functional Testing
116
+ 1. **Positioning**: Verify dropdown aligns properly with search input
117
+ 2. **Hover States**: Test all interactive elements provide clear feedback
118
+ 3. **Keyboard Navigation**: Ensure accessibility is maintained
119
+
120
+ ### Performance Testing
121
+ 1. **Backdrop Blur**: Verify enhanced blur effects don't impact performance
122
+ 2. **Animation Smoothness**: Test transition animations remain smooth
123
+
124
+ ## Implementation Approach
125
+
126
+ ### Phase 1: Core Glassmorphism
127
+ - Update main container background and border opacity
128
+ - Adjust positioning for proper alignment
129
+ - Add enhanced backdrop-filter CSS
130
+
131
+ ### Phase 2: Text Contrast
132
+ - Improve all text color values for better readability
133
+ - Enhance section headers and primary text
134
+ - Strengthen secondary text visibility
135
+
136
+ ### Phase 3: Visual Polish
137
+ - Increase icon and image sizes
138
+ - Improve hover states and transitions
139
+ - Enhance borders and dividers
140
+
141
+ ### Phase 4: Testing & Refinement
142
+ - Cross-browser testing
143
+ - Accessibility validation
144
+ - Performance optimization if needed
145
+
146
+ ## CSS Implementation Details
147
+
148
+ ### Enhanced Backdrop Filter
149
+ ```css
150
+ backdrop-filter: blur(20px) saturate(180%);
151
+ -webkit-backdrop-filter: blur(20px) saturate(180%);
152
+ ```
153
+
154
+ ### Improved Container Styling
155
+ ```css
156
+ background: rgba(0, 0, 0, 0.7);
157
+ border: 1px solid rgba(255, 255, 255, 0.2);
158
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
159
+ ```
160
+
161
+ This design ensures the search results dropdown maintains its modern glassmorphic aesthetic while providing excellent readability and user experience.
.kiro/specs/search-glassmorphism-fix/requirements.md ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Search Results Glassmorphism Fix - Requirements Document
2
+
3
+ ## Introduction
4
+
5
+ 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.
6
+
7
+ ## Requirements
8
+
9
+ ### Requirement 1
10
+
11
+ **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.
12
+
13
+ #### Acceptance Criteria
14
+
15
+ 1. WHEN the search dropdown appears THEN the background SHALL have appropriate opacity (not too transparent)
16
+ 2. WHEN viewing the search results THEN the backdrop blur SHALL create a proper frosted glass effect
17
+ 3. WHEN text is displayed in the dropdown THEN it SHALL have sufficient contrast for readability
18
+ 4. WHEN the dropdown is visible THEN borders SHALL be clearly defined with appropriate opacity
19
+
20
+ ### Requirement 2
21
+
22
+ **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.
23
+
24
+ #### Acceptance Criteria
25
+
26
+ 1. WHEN the search dropdown opens THEN it SHALL align properly with the search input field
27
+ 2. WHEN viewing the dropdown THEN the positioning SHALL not cause layout shifts or misalignment
28
+ 3. WHEN the dropdown is displayed THEN it SHALL maintain consistent spacing from the search bar
29
+
30
+ ### Requirement 3
31
+
32
+ **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.
33
+
34
+ #### Acceptance Criteria
35
+
36
+ 1. WHEN viewing game categories THEN section headers SHALL have high contrast colors
37
+ 2. WHEN viewing product information THEN product names SHALL be clearly readable
38
+ 3. WHEN viewing metadata THEN secondary text SHALL have appropriate contrast while maintaining hierarchy
39
+ 4. WHEN hovering over items THEN color changes SHALL provide clear visual feedback
40
+
41
+ ### Requirement 4
42
+
43
+ **User Story:** As a user, I want consistent visual hierarchy in search results, so that I can quickly scan and find relevant information.
44
+
45
+ #### Acceptance Criteria
46
+
47
+ 1. WHEN viewing search results THEN different content types SHALL be visually distinct
48
+ 2. WHEN scanning results THEN important information SHALL stand out through proper typography
49
+ 3. WHEN viewing product images THEN they SHALL be appropriately sized and styled
50
+ 4. WHEN interacting with results THEN hover states SHALL provide clear feedback
.kiro/specs/search-glassmorphism-fix/tasks.md ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Implementation Plan
2
+
3
+ - [ ] 1. Enhance main search results container glassmorphism
4
+ - Update background opacity from `bg-black/30` to `bg-black/70` for better frosted glass effect
5
+ - Improve border visibility from `border-white/10` to `border-white/20`
6
+ - Adjust positioning from `translateX(-1.5rem)` to `translateX(-0.75rem)` for proper alignment
7
+ - Add enhanced backdrop-filter CSS for better blur effect
8
+ - _Requirements: 1.1, 1.2, 1.3, 1.4, 2.1, 2.2_
9
+
10
+ - [ ] 2. Improve text colors and contrast throughout search results
11
+ - Enhance loading state text from `text-white/60` to `text-white/80`
12
+ - Brighten section headers from `text-purple-300` to `text-purple-200` and `text-green-300` to `text-green-200`
13
+ - Improve secondary text visibility from `text-white/50` to `text-white/70`
14
+ - Enhance description text from `text-white/60` to `text-white/80`
15
+ - _Requirements: 3.1, 3.2, 3.3, 3.4_
16
+
17
+ - [ ] 3. Enhance visual hierarchy and interactive elements
18
+ - Increase section header icons from `w-3 h-3` to `w-4 h-4` with proper margin adjustments
19
+ - Improve product images from `w-8 h-8` to `w-10 h-10` with better border styling
20
+ - Strengthen hover backgrounds from `hover:bg-white/5` to `hover:bg-white/10`
21
+ - Increase product item padding from `p-2` to `p-3` for better spacing
22
+ - _Requirements: 4.1, 4.2, 4.3, 4.4_
23
+
24
+ - [ ] 4. Strengthen borders and dividers for better visual separation
25
+ - Improve main dividers from `divide-white/10` to `divide-white/20`
26
+ - Enhance item borders from `border-white/5` to `border-white/10`
27
+ - Add better hover border states `hover:border-white/20`
28
+ - Update product image borders from `border-white/10` to `border-white/20`
29
+ - _Requirements: 4.1, 4.2_
30
+
31
+ - [ ] 5. Enhance hover preview popup styling
32
+ - Improve popup background from `bg-black/95` to `bg-black/90` with enhanced backdrop-filter
33
+ - Strengthen popup borders from `border-white/20` to `border-white/30`
34
+ - Improve internal text colors and contrast for better readability
35
+ - Add proper backdrop-filter CSS for consistent glassmorphism
36
+ - _Requirements: 1.1, 1.3, 3.1, 3.2_
README.md CHANGED
@@ -1,3 +1,61 @@
1
- ---
2
- license: mit
3
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
2
+
3
+ <p align="center">
4
+ <a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
5
+ <a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
6
+ <a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
7
+ <a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
8
+ </p>
9
+
10
+ ## About Laravel
11
+
12
+ 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:
13
+
14
+ - [Simple, fast routing engine](https://laravel.com/docs/routing).
15
+ - [Powerful dependency injection container](https://laravel.com/docs/container).
16
+ - Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
17
+ - Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
18
+ - Database agnostic [schema migrations](https://laravel.com/docs/migrations).
19
+ - [Robust background job processing](https://laravel.com/docs/queues).
20
+ - [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
21
+
22
+ Laravel is accessible, powerful, and provides tools required for large, robust applications.
23
+
24
+ ## Learning Laravel
25
+
26
+ 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.
27
+
28
+ You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
29
+
30
+ 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.
31
+
32
+ ## Laravel Sponsors
33
+
34
+ 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).
35
+
36
+ ### Premium Partners
37
+
38
+ - **[Vehikl](https://vehikl.com/)**
39
+ - **[Tighten Co.](https://tighten.co)**
40
+ - **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
41
+ - **[64 Robots](https://64robots.com)**
42
+ - **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
43
+ - **[DevSquad](https://devsquad.com/hire-laravel-developers)**
44
+ - **[Redberry](https://redberry.international/laravel-development/)**
45
+ - **[Active Logic](https://activelogic.com)**
46
+
47
+ ## Contributing
48
+
49
+ Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
50
+
51
+ ## Code of Conduct
52
+
53
+ 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).
54
+
55
+ ## Security Vulnerabilities
56
+
57
+ 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.
58
+
59
+ ## License
60
+
61
+ The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
README_HF.md ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Laravel E-commerce Dashboard
2
+
3
+ A modern Laravel-based e-commerce platform with an advanced analytics dashboard featuring real-time metrics, interactive charts, and comprehensive product management.
4
+
5
+ ## Features
6
+
7
+ ### 🎯 Analytics Dashboard
8
+ - **Real-time Metrics**: Revenue, orders, customers, and product statistics
9
+ - **Interactive Charts**: Revenue trends, top products, and purchase patterns
10
+ - **Time Period Filtering**: Daily, weekly, and monthly views
11
+ - **Product Trends**: Shows what products people buy at different time periods
12
+ - **Glassmorphism UI**: Modern, beautiful interface with smooth animations
13
+
14
+ ### 🛍️ E-commerce Features
15
+ - Product catalog with categories
16
+ - Order management system
17
+ - Customer tracking
18
+ - Search functionality with glassmorphism effects
19
+ - Responsive design
20
+
21
+ ### 📊 Advanced Analytics
22
+ - Revenue tracking with period comparisons
23
+ - Product performance metrics
24
+ - Customer behavior analysis
25
+ - Purchase trend visualization
26
+ - Detailed modal views with comprehensive data
27
+
28
+ ## Technology Stack
29
+
30
+ - **Backend**: Laravel 10+ with PHP 8.1+
31
+ - **Frontend**: Blade templates with Alpine.js
32
+ - **Styling**: Tailwind CSS with custom glassmorphism effects
33
+ - **Charts**: Chart.js for interactive visualizations
34
+ - **Database**: MySQL/SQLite support
35
+ - **Build Tools**: Vite for asset compilation
36
+
37
+ ## Key Components
38
+
39
+ ### Dashboard Features
40
+ - Time-sensitive metric cards
41
+ - Interactive revenue and product charts
42
+ - Product purchase trends by time period
43
+ - Modal details with comprehensive analytics
44
+ - Auto-refreshing data every 30 seconds
45
+
46
+ ### UI/UX Highlights
47
+ - Glassmorphism design language
48
+ - Smooth transitions and animations
49
+ - Responsive grid layouts
50
+ - Custom time filter buttons with natural glow effects
51
+ - Enhanced contrast for better readability
52
+
53
+ ## Installation
54
+
55
+ 1. Clone the repository
56
+ 2. Install dependencies: `composer install && npm install`
57
+ 3. Set up environment: `cp .env.example .env`
58
+ 4. Generate key: `php artisan key:generate`
59
+ 5. Run migrations: `php artisan migrate`
60
+ 6. Build assets: `npm run build`
61
+ 7. Start server: `php artisan serve`
62
+
63
+ ## API Endpoints
64
+
65
+ - `/api/dashboard/summary` - Overall dashboard metrics
66
+ - `/api/dashboard/revenue` - Revenue data with time filtering
67
+ - `/api/dashboard/sales` - Sales and product performance
68
+ - `/api/dashboard/customers` - Customer analytics
69
+ - `/api/dashboard/product-trends` - Product purchase trends by period
70
+
71
+ ## Recent Enhancements
72
+
73
+ - Fixed color contrast issues in metric cards
74
+ - Enhanced time filter button styling with natural glow effects
75
+ - Added product purchase trends chart showing buying patterns
76
+ - Improved glassmorphism effects throughout the interface
77
+ - Optimized database queries for better performance
78
+
79
+ ## Screenshots
80
+
81
+ The dashboard features a modern dark theme with glassmorphism effects, interactive charts, and comprehensive analytics views.
82
+
83
+ ## License
84
+
85
+ This project is open source and available under the MIT License.
app/Console/Commands/MigrateExistingCustomGames.php ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Console\Commands;
4
+
5
+ use Illuminate\Console\Command;
6
+ use App\Models\Product;
7
+ use App\Models\CustomGame;
8
+
9
+ class MigrateExistingCustomGames extends Command
10
+ {
11
+ /**
12
+ * The name and signature of the console command.
13
+ *
14
+ * @var string
15
+ */
16
+ protected $signature = 'migrate:custom-games';
17
+
18
+ /**
19
+ * The console command description.
20
+ *
21
+ * @var string
22
+ */
23
+ protected $description = 'Migrate existing custom games from products to custom_games table';
24
+
25
+ /**
26
+ * Execute the console command.
27
+ */
28
+ public function handle()
29
+ {
30
+ $this->info('Starting migration of existing custom games...');
31
+
32
+ // Get all unique custom game categories from existing products
33
+ $existingCustomGames = Product::select('game')
34
+ ->distinct()
35
+ ->whereNotNull('game')
36
+ ->whereNotIn('game', ['Genshin', 'Starrail', 'WutheringWave', 'ZenlessZoneZero', 'Arknights', 'AzurLane'])
37
+ ->pluck('game');
38
+
39
+ if ($existingCustomGames->isEmpty()) {
40
+ $this->info('No existing custom games found to migrate.');
41
+ return;
42
+ }
43
+
44
+ $this->info("Found {$existingCustomGames->count()} custom games to migrate:");
45
+
46
+ $customGameColors = [
47
+ 'from-red-500 to-pink-500',
48
+ 'from-orange-500 to-red-500',
49
+ 'from-yellow-500 to-orange-500',
50
+ 'from-green-500 to-teal-500',
51
+ 'from-teal-500 to-cyan-500',
52
+ 'from-blue-500 to-indigo-500',
53
+ 'from-indigo-500 to-purple-500',
54
+ 'from-purple-500 to-pink-500',
55
+ 'from-pink-500 to-rose-500',
56
+ 'from-emerald-500 to-green-500',
57
+ 'from-cyan-500 to-blue-500',
58
+ 'from-violet-500 to-purple-500'
59
+ ];
60
+
61
+ $customGameIcons = [
62
+ 'fas fa-gamepad',
63
+ 'fas fa-dice',
64
+ 'fas fa-chess',
65
+ 'fas fa-puzzle-piece',
66
+ 'fas fa-trophy',
67
+ 'fas fa-crown',
68
+ 'fas fa-gem',
69
+ 'fas fa-fire',
70
+ 'fas fa-bolt',
71
+ 'fas fa-magic',
72
+ 'fas fa-dragon',
73
+ 'fas fa-shield',
74
+ 'fas fa-sword',
75
+ 'fas fa-heart',
76
+ 'fas fa-star',
77
+ 'fas fa-moon',
78
+ 'fas fa-sun',
79
+ 'fas fa-leaf',
80
+ 'fas fa-snowflake',
81
+ 'fas fa-mountain'
82
+ ];
83
+
84
+ foreach ($existingCustomGames as $gameName) {
85
+ // Check if it already exists in custom_games table
86
+ $existingCustomGame = CustomGame::where('name', $gameName)->first();
87
+
88
+ if (!$existingCustomGame) {
89
+ // Generate consistent styling based on game name
90
+ $hash = crc32($gameName);
91
+ $colorIndex = abs($hash) % count($customGameColors);
92
+ $iconIndex = abs($hash >> 8) % count($customGameIcons);
93
+
94
+ CustomGame::create([
95
+ 'name' => $gameName,
96
+ 'icon' => $customGameIcons[$iconIndex],
97
+ 'color_gradient' => $customGameColors[$colorIndex]
98
+ ]);
99
+
100
+ $this->info("✓ Migrated: {$gameName}");
101
+ } else {
102
+ $this->info("- Already exists: {$gameName}");
103
+ }
104
+ }
105
+
106
+ $this->info('Migration completed successfully!');
107
+ }
108
+ }
app/Console/Commands/PopulateAnalyticsData.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Console\Commands;
4
+
5
+ use Illuminate\Console\Command;
6
+ use App\Models\Order;
7
+ use App\Models\RevenueTracking;
8
+ use Carbon\Carbon;
9
+
10
+ class PopulateAnalyticsData extends Command
11
+ {
12
+ protected $signature = 'analytics:populate';
13
+ protected $description = 'Populate analytics data from existing orders';
14
+
15
+ public function handle()
16
+ {
17
+ $this->info('Populating analytics data from existing orders...');
18
+
19
+ // Get all orders and create revenue tracking entries
20
+ $orders = Order::where('status', '!=', 'cancelled')->get();
21
+
22
+ foreach ($orders as $order) {
23
+ // Parse cart data if it exists
24
+ $cartData = json_decode($order->cart_data, true) ?? [];
25
+
26
+ if (!empty($cartData)) {
27
+ foreach ($cartData as $item) {
28
+ RevenueTracking::updateOrCreate([
29
+ 'order_id' => $order->id,
30
+ 'product_name' => $item['name'] ?? 'Unknown Product'
31
+ ], [
32
+ 'amount' => $item['price'] ?? 0,
33
+ 'cost' => ($item['price'] ?? 0) * 0.7, // 70% cost, 30% profit
34
+ 'profit_margin' => 0.30,
35
+ 'quantity' => $item['quantity'] ?? 1,
36
+ 'created_at' => $order->created_at,
37
+ 'updated_at' => $order->updated_at
38
+ ]);
39
+ }
40
+ } else {
41
+ // If no cart data, create a single entry for the order
42
+ RevenueTracking::updateOrCreate([
43
+ 'order_id' => $order->id,
44
+ 'product_name' => 'Order #' . $order->id
45
+ ], [
46
+ 'amount' => $order->total_amount,
47
+ 'cost' => $order->total_amount * 0.7,
48
+ 'profit_margin' => 0.30,
49
+ 'quantity' => 1,
50
+ 'created_at' => $order->created_at,
51
+ 'updated_at' => $order->updated_at
52
+ ]);
53
+ }
54
+ }
55
+
56
+ $this->info('Analytics data populated successfully!');
57
+ $this->info('Total revenue tracking entries: ' . RevenueTracking::count());
58
+ }
59
+ }
app/Console/Commands/TestDashboard.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Console\Commands;
4
+
5
+ use App\Services\AnalyticsService;
6
+ use Illuminate\Console\Command;
7
+
8
+ class TestDashboard extends Command
9
+ {
10
+ /**
11
+ * The name and signature of the console command.
12
+ *
13
+ * @var string
14
+ */
15
+ protected $signature = 'test:dashboard';
16
+
17
+ /**
18
+ * The console command description.
19
+ *
20
+ * @var string
21
+ */
22
+ protected $description = 'Test dashboard analytics service';
23
+
24
+ /**
25
+ * Execute the console command.
26
+ */
27
+ public function handle()
28
+ {
29
+ $this->info('Testing Analytics Service...');
30
+
31
+ try {
32
+ $analyticsService = app(AnalyticsService::class);
33
+
34
+ $this->info('Testing getTodayRevenue...');
35
+ $todayRevenue = $analyticsService->getTodayRevenue();
36
+ $this->info('Today Revenue: ' . json_encode($todayRevenue));
37
+
38
+ $this->info('Testing getProfitData...');
39
+ $profitData = $analyticsService->getProfitData('today');
40
+ $this->info('Profit Data: ' . json_encode($profitData));
41
+
42
+ $this->info('Testing getCustomerAnalytics...');
43
+ $customerAnalytics = $analyticsService->getCustomerAnalytics();
44
+ $this->info('Customer Analytics: ' . json_encode($customerAnalytics));
45
+
46
+ $this->info('Testing getOrderStats...');
47
+ $orderStats = $analyticsService->getOrderStats();
48
+ $this->info('Order Stats: ' . json_encode($orderStats));
49
+
50
+ $this->info('✅ All tests passed!');
51
+
52
+ } catch (\Exception $e) {
53
+ $this->error('❌ Error: ' . $e->getMessage());
54
+ $this->error('File: ' . $e->getFile() . ':' . $e->getLine());
55
+ }
56
+ }
57
+ }
app/Http/Controllers/Api/DashboardController.php ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Controllers\Api;
4
+
5
+ use App\Http\Controllers\Controller;
6
+ use App\Services\AnalyticsService;
7
+ use Illuminate\Http\Request;
8
+ use Illuminate\Http\JsonResponse;
9
+ use Carbon\Carbon;
10
+
11
+ class DashboardController extends Controller
12
+ {
13
+ protected AnalyticsService $analyticsService;
14
+
15
+ public function __construct(AnalyticsService $analyticsService)
16
+ {
17
+ $this->analyticsService = $analyticsService;
18
+ }
19
+
20
+ /**
21
+ * Get revenue data with time period filtering
22
+ */
23
+ public function revenue(Request $request): JsonResponse
24
+ {
25
+ $period = $request->get('period', 'daily');
26
+ $date = $request->get('date') ? Carbon::parse($request->get('date')) : Carbon::now();
27
+
28
+ $data = $this->analyticsService->getAggregatedData($period, $date);
29
+ $todayRevenue = $this->analyticsService->getTodayRevenue();
30
+
31
+ return response()->json([
32
+ 'success' => true,
33
+ 'data' => [
34
+ 'chart_data' => $data,
35
+ 'summary' => $todayRevenue,
36
+ 'period' => $period,
37
+ 'date' => $date->format('Y-m-d')
38
+ ]
39
+ ]);
40
+ }
41
+
42
+ /**
43
+ * Get profit data with trend calculations
44
+ */
45
+ public function profit(Request $request): JsonResponse
46
+ {
47
+ $period = $request->get('period', 'today');
48
+ $date = $request->get('date') ? Carbon::parse($request->get('date')) : Carbon::now();
49
+
50
+ $profitData = $this->analyticsService->getProfitData($period);
51
+ $chartData = $this->analyticsService->getAggregatedData($period === 'today' ? 'daily' : $period, $date);
52
+
53
+ // Format chart data for profit visualization
54
+ $formattedChartData = array_map(function ($item) {
55
+ return [
56
+ 'label' => $item['hour'] ?? $item['day_name'] ?? $item['day'] ?? $item['date'],
57
+ 'revenue' => $item['revenue'],
58
+ 'profit' => $item['profit'],
59
+ 'profit_margin' => $item['revenue'] > 0 ? (($item['profit'] / $item['revenue']) * 100) : 0
60
+ ];
61
+ }, $chartData);
62
+
63
+ return response()->json([
64
+ 'success' => true,
65
+ 'data' => [
66
+ 'chart_data' => $formattedChartData,
67
+ 'summary' => $profitData,
68
+ 'period' => $period,
69
+ 'date' => $date->format('Y-m-d')
70
+ ]
71
+ ]);
72
+ }
73
+
74
+ /**
75
+ * Get sales analytics with product performance
76
+ */
77
+ public function sales(Request $request): JsonResponse
78
+ {
79
+ $limit = $request->get('limit', 10);
80
+
81
+ $topProducts = $this->analyticsService->getTopProducts($limit);
82
+ $period = $request->get('period', 'daily');
83
+ $date = $request->get('date') ? Carbon::parse($request->get('date')) : Carbon::now();
84
+
85
+ $salesData = $this->analyticsService->getAggregatedData($period, $date);
86
+
87
+ // Format sales chart data
88
+ $formattedSalesData = array_map(function ($item) {
89
+ return [
90
+ 'label' => $item['hour'] ?? $item['day_name'] ?? $item['day'] ?? $item['date'],
91
+ 'orders' => $item['orders'],
92
+ 'revenue' => $item['revenue'],
93
+ 'avg_order_value' => $item['orders'] > 0 ? ($item['revenue'] / $item['orders']) : 0
94
+ ];
95
+ }, $salesData);
96
+
97
+ return response()->json([
98
+ 'success' => true,
99
+ 'data' => [
100
+ 'top_products' => $topProducts,
101
+ 'sales_chart' => $formattedSalesData,
102
+ 'period' => $period,
103
+ 'date' => $date->format('Y-m-d')
104
+ ]
105
+ ]);
106
+ }
107
+
108
+ /**
109
+ * Get customer analytics with new vs returning metrics
110
+ */
111
+ public function customers(Request $request): JsonResponse
112
+ {
113
+ $customerAnalytics = $this->analyticsService->getCustomerAnalytics();
114
+ $period = $request->get('period', 'daily');
115
+ $date = $request->get('date') ? Carbon::parse($request->get('date')) : Carbon::now();
116
+
117
+ $customerData = $this->analyticsService->getAggregatedData($period, $date);
118
+
119
+ // Format customer chart data
120
+ $formattedCustomerData = array_map(function ($item) {
121
+ return [
122
+ 'label' => $item['hour'] ?? $item['day_name'] ?? $item['day'] ?? $item['date'],
123
+ 'customers' => $item['customers'],
124
+ 'orders' => $item['orders'],
125
+ 'conversion_rate' => $item['customers'] > 0 ? (($item['orders'] / $item['customers']) * 100) : 0
126
+ ];
127
+ }, $customerData);
128
+
129
+ return response()->json([
130
+ 'success' => true,
131
+ 'data' => [
132
+ 'analytics' => $customerAnalytics,
133
+ 'chart_data' => $formattedCustomerData,
134
+ 'period' => $period,
135
+ 'date' => $date->format('Y-m-d')
136
+ ]
137
+ ]);
138
+ }
139
+
140
+ /**
141
+ * Get all dashboard metrics summary with period support
142
+ */
143
+ public function summary(Request $request): JsonResponse
144
+ {
145
+ $period = $request->get('period', 'daily');
146
+ $date = $request->get('date') ? Carbon::parse($request->get('date')) : Carbon::now();
147
+
148
+ // Get period-specific data
149
+ $revenueData = $this->getRevenueForPeriod($period, $date);
150
+ $profitData = $this->analyticsService->getProfitData($period);
151
+ $customerAnalytics = $this->analyticsService->getCustomerAnalytics();
152
+ $orderStats = $this->getOrdersForPeriod($period, $date);
153
+ $productStats = $this->analyticsService->getProductStats();
154
+ $topProducts = $this->analyticsService->getTopProducts(5);
155
+
156
+ return response()->json([
157
+ 'success' => true,
158
+ 'data' => [
159
+ 'revenue' => $revenueData,
160
+ 'profit' => $profitData,
161
+ 'orders' => $orderStats,
162
+ 'customers' => $customerAnalytics,
163
+ 'products' => $productStats,
164
+ 'top_products' => $topProducts,
165
+ 'period' => $period,
166
+ 'date' => $date->format('Y-m-d'),
167
+ 'last_updated' => Carbon::now()->toISOString()
168
+ ]
169
+ ]);
170
+ }
171
+
172
+ /**
173
+ * Get revenue data for specific period
174
+ */
175
+ private function getRevenueForPeriod(string $period, Carbon $date): array
176
+ {
177
+ switch ($period) {
178
+ case 'weekly':
179
+ $current = \App\Models\Order::whereBetween('created_at', [
180
+ $date->copy()->startOfWeek(),
181
+ $date->copy()->endOfWeek()
182
+ ])->sum('total_amount');
183
+
184
+ $previous = \App\Models\Order::whereBetween('created_at', [
185
+ $date->copy()->subWeek()->startOfWeek(),
186
+ $date->copy()->subWeek()->endOfWeek()
187
+ ])->sum('total_amount');
188
+ break;
189
+
190
+ case 'monthly':
191
+ $current = \App\Models\Order::whereBetween('created_at', [
192
+ $date->copy()->startOfMonth(),
193
+ $date->copy()->endOfMonth()
194
+ ])->sum('total_amount');
195
+
196
+ $previous = \App\Models\Order::whereBetween('created_at', [
197
+ $date->copy()->subMonth()->startOfMonth(),
198
+ $date->copy()->subMonth()->endOfMonth()
199
+ ])->sum('total_amount');
200
+ break;
201
+
202
+ default: // daily
203
+ $current = \App\Models\Order::whereDate('created_at', $date)->sum('total_amount');
204
+ $previous = \App\Models\Order::whereDate('created_at', $date->copy()->subDay())->sum('total_amount');
205
+ break;
206
+ }
207
+
208
+ $percentageChange = $previous > 0 ? (($current - $previous) / $previous) * 100 : 0;
209
+
210
+ return [
211
+ 'current' => $current,
212
+ 'previous' => $previous,
213
+ 'percentage_change' => round($percentageChange, 2),
214
+ 'trend' => $percentageChange >= 0 ? 'up' : 'down'
215
+ ];
216
+ }
217
+
218
+ /**
219
+ * Get orders data for specific period
220
+ */
221
+ private function getOrdersForPeriod(string $period, Carbon $date): array
222
+ {
223
+ switch ($period) {
224
+ case 'weekly':
225
+ $current = \App\Models\Order::whereBetween('created_at', [
226
+ $date->copy()->startOfWeek(),
227
+ $date->copy()->endOfWeek()
228
+ ])->count();
229
+
230
+ $previous = \App\Models\Order::whereBetween('created_at', [
231
+ $date->copy()->subWeek()->startOfWeek(),
232
+ $date->copy()->subWeek()->endOfWeek()
233
+ ])->count();
234
+ break;
235
+
236
+ case 'monthly':
237
+ $current = \App\Models\Order::whereBetween('created_at', [
238
+ $date->copy()->startOfMonth(),
239
+ $date->copy()->endOfMonth()
240
+ ])->count();
241
+
242
+ $previous = \App\Models\Order::whereBetween('created_at', [
243
+ $date->copy()->subMonth()->startOfMonth(),
244
+ $date->copy()->subMonth()->endOfMonth()
245
+ ])->count();
246
+ break;
247
+
248
+ default: // daily
249
+ $current = \App\Models\Order::whereDate('created_at', $date)->count();
250
+ $previous = \App\Models\Order::whereDate('created_at', $date->copy()->subDay())->count();
251
+ break;
252
+ }
253
+
254
+ $percentageChange = $previous > 0 ? (($current - $previous) / $previous) * 100 : 0;
255
+
256
+ return [
257
+ 'current' => $current,
258
+ 'previous' => $previous,
259
+ 'percentage_change' => round($percentageChange, 2),
260
+ 'trend' => $percentageChange >= 0 ? 'up' : 'down'
261
+ ];
262
+ }
263
+ }
app/Http/Controllers/Auth/AuthenticatedSessionController.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Controllers\Auth;
4
+
5
+ use App\Http\Controllers\Controller;
6
+ use App\Http\Requests\Auth\LoginRequest;
7
+ use Illuminate\Http\RedirectResponse;
8
+ use Illuminate\Http\Request;
9
+ use Illuminate\Support\Facades\Auth;
10
+ use Illuminate\View\View;
11
+
12
+ class AuthenticatedSessionController extends Controller
13
+ {
14
+ /**
15
+ * Display the login view.
16
+ */
17
+ public function create(): View
18
+ {
19
+ return view('auth.login');
20
+ }
21
+
22
+ /**
23
+ * Handle an incoming authentication request.
24
+ */
25
+ public function store(LoginRequest $request): RedirectResponse
26
+ {
27
+ $request->authenticate();
28
+
29
+ $request->session()->regenerate();
30
+
31
+ return redirect()->intended(route('dashboard', absolute: false));
32
+ }
33
+
34
+ /**
35
+ * Destroy an authenticated session.
36
+ */
37
+ public function destroy(Request $request): RedirectResponse
38
+ {
39
+ Auth::guard('web')->logout();
40
+
41
+ $request->session()->invalidate();
42
+
43
+ $request->session()->regenerateToken();
44
+
45
+ return redirect('/');
46
+ }
47
+ }
app/Http/Controllers/Auth/ConfirmablePasswordController.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Controllers\Auth;
4
+
5
+ use App\Http\Controllers\Controller;
6
+ use Illuminate\Http\RedirectResponse;
7
+ use Illuminate\Http\Request;
8
+ use Illuminate\Support\Facades\Auth;
9
+ use Illuminate\Validation\ValidationException;
10
+ use Illuminate\View\View;
11
+
12
+ class ConfirmablePasswordController extends Controller
13
+ {
14
+ /**
15
+ * Show the confirm password view.
16
+ */
17
+ public function show(): View
18
+ {
19
+ return view('auth.confirm-password');
20
+ }
21
+
22
+ /**
23
+ * Confirm the user's password.
24
+ */
25
+ public function store(Request $request): RedirectResponse
26
+ {
27
+ if (! Auth::guard('web')->validate([
28
+ 'email' => $request->user()->email,
29
+ 'password' => $request->password,
30
+ ])) {
31
+ throw ValidationException::withMessages([
32
+ 'password' => __('auth.password'),
33
+ ]);
34
+ }
35
+
36
+ $request->session()->put('auth.password_confirmed_at', time());
37
+
38
+ return redirect()->intended(route('dashboard', absolute: false));
39
+ }
40
+ }
app/Http/Controllers/Auth/EmailVerificationNotificationController.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Controllers\Auth;
4
+
5
+ use App\Http\Controllers\Controller;
6
+ use Illuminate\Http\RedirectResponse;
7
+ use Illuminate\Http\Request;
8
+
9
+ class EmailVerificationNotificationController extends Controller
10
+ {
11
+ /**
12
+ * Send a new email verification notification.
13
+ */
14
+ public function store(Request $request): RedirectResponse
15
+ {
16
+ if ($request->user()->hasVerifiedEmail()) {
17
+ return redirect()->intended(route('dashboard', absolute: false));
18
+ }
19
+
20
+ $request->user()->sendEmailVerificationNotification();
21
+
22
+ return back()->with('status', 'verification-link-sent');
23
+ }
24
+ }
app/Http/Controllers/Auth/EmailVerificationPromptController.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Controllers\Auth;
4
+
5
+ use App\Http\Controllers\Controller;
6
+ use Illuminate\Http\RedirectResponse;
7
+ use Illuminate\Http\Request;
8
+ use Illuminate\View\View;
9
+
10
+ class EmailVerificationPromptController extends Controller
11
+ {
12
+ /**
13
+ * Display the email verification prompt.
14
+ */
15
+ public function __invoke(Request $request): RedirectResponse|View
16
+ {
17
+ return $request->user()->hasVerifiedEmail()
18
+ ? redirect()->intended(route('dashboard', absolute: false))
19
+ : view('auth.verify-email');
20
+ }
21
+ }
app/Http/Controllers/Auth/NewPasswordController.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Controllers\Auth;
4
+
5
+ use App\Http\Controllers\Controller;
6
+ use App\Models\User;
7
+ use Illuminate\Auth\Events\PasswordReset;
8
+ use Illuminate\Http\RedirectResponse;
9
+ use Illuminate\Http\Request;
10
+ use Illuminate\Support\Facades\Hash;
11
+ use Illuminate\Support\Facades\Password;
12
+ use Illuminate\Support\Str;
13
+ use Illuminate\Validation\Rules;
14
+ use Illuminate\View\View;
15
+
16
+ class NewPasswordController extends Controller
17
+ {
18
+ /**
19
+ * Display the password reset view.
20
+ */
21
+ public function create(Request $request): View
22
+ {
23
+ return view('auth.reset-password', ['request' => $request]);
24
+ }
25
+
26
+ /**
27
+ * Handle an incoming new password request.
28
+ *
29
+ * @throws \Illuminate\Validation\ValidationException
30
+ */
31
+ public function store(Request $request): RedirectResponse
32
+ {
33
+ $request->validate([
34
+ 'token' => ['required'],
35
+ 'email' => ['required', 'email'],
36
+ 'password' => ['required', 'confirmed', Rules\Password::defaults()],
37
+ ]);
38
+
39
+ // Here we will attempt to reset the user's password. If it is successful we
40
+ // will update the password on an actual user model and persist it to the
41
+ // database. Otherwise we will parse the error and return the response.
42
+ $status = Password::reset(
43
+ $request->only('email', 'password', 'password_confirmation', 'token'),
44
+ function (User $user) use ($request) {
45
+ $user->forceFill([
46
+ 'password' => Hash::make($request->password),
47
+ 'remember_token' => Str::random(60),
48
+ ])->save();
49
+
50
+ event(new PasswordReset($user));
51
+ }
52
+ );
53
+
54
+ // If the password was successfully reset, we will redirect the user back to
55
+ // the application's home authenticated view. If there is an error we can
56
+ // redirect them back to where they came from with their error message.
57
+ return $status == Password::PASSWORD_RESET
58
+ ? redirect()->route('login')->with('status', __($status))
59
+ : back()->withInput($request->only('email'))
60
+ ->withErrors(['email' => __($status)]);
61
+ }
62
+ }
app/Http/Controllers/Auth/PasswordController.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Controllers\Auth;
4
+
5
+ use App\Http\Controllers\Controller;
6
+ use Illuminate\Http\RedirectResponse;
7
+ use Illuminate\Http\Request;
8
+ use Illuminate\Support\Facades\Hash;
9
+ use Illuminate\Validation\Rules\Password;
10
+
11
+ class PasswordController extends Controller
12
+ {
13
+ /**
14
+ * Update the user's password.
15
+ */
16
+ public function update(Request $request): RedirectResponse
17
+ {
18
+ $validated = $request->validateWithBag('updatePassword', [
19
+ 'current_password' => ['required', 'current_password'],
20
+ 'password' => ['required', Password::defaults(), 'confirmed'],
21
+ ]);
22
+
23
+ $request->user()->update([
24
+ 'password' => Hash::make($validated['password']),
25
+ ]);
26
+
27
+ return back()->with('status', 'password-updated');
28
+ }
29
+ }
app/Http/Controllers/Auth/PasswordResetLinkController.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Controllers\Auth;
4
+
5
+ use App\Http\Controllers\Controller;
6
+ use Illuminate\Http\RedirectResponse;
7
+ use Illuminate\Http\Request;
8
+ use Illuminate\Support\Facades\Password;
9
+ use Illuminate\View\View;
10
+
11
+ class PasswordResetLinkController extends Controller
12
+ {
13
+ /**
14
+ * Display the password reset link request view.
15
+ */
16
+ public function create(): View
17
+ {
18
+ return view('auth.forgot-password');
19
+ }
20
+
21
+ /**
22
+ * Handle an incoming password reset link request.
23
+ *
24
+ * @throws \Illuminate\Validation\ValidationException
25
+ */
26
+ public function store(Request $request): RedirectResponse
27
+ {
28
+ $request->validate([
29
+ 'email' => ['required', 'email'],
30
+ ]);
31
+
32
+ // We will send the password reset link to this user. Once we have attempted
33
+ // to send the link, we will examine the response then see the message we
34
+ // need to show to the user. Finally, we'll send out a proper response.
35
+ $status = Password::sendResetLink(
36
+ $request->only('email')
37
+ );
38
+
39
+ return $status == Password::RESET_LINK_SENT
40
+ ? back()->with('status', __($status))
41
+ : back()->withInput($request->only('email'))
42
+ ->withErrors(['email' => __($status)]);
43
+ }
44
+ }
app/Http/Controllers/Auth/RegisteredUserController.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Controllers\Auth;
4
+
5
+ use App\Http\Controllers\Controller;
6
+ use App\Models\User;
7
+ use Illuminate\Auth\Events\Registered;
8
+ use Illuminate\Http\RedirectResponse;
9
+ use Illuminate\Http\Request;
10
+ use Illuminate\Support\Facades\Auth;
11
+ use Illuminate\Support\Facades\Hash;
12
+ use Illuminate\Validation\Rules;
13
+ use Illuminate\View\View;
14
+
15
+ class RegisteredUserController extends Controller
16
+ {
17
+ /**
18
+ * Display the registration view.
19
+ */
20
+ public function create(): View
21
+ {
22
+ return view('auth.register');
23
+ }
24
+
25
+ /**
26
+ * Handle an incoming registration request.
27
+ *
28
+ * @throws \Illuminate\Validation\ValidationException
29
+ */
30
+ public function store(Request $request): RedirectResponse
31
+ {
32
+ $request->validate([
33
+ 'name' => ['required', 'string', 'max:255'],
34
+ 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],
35
+ 'password' => ['required', 'confirmed', Rules\Password::defaults()],
36
+ ]);
37
+
38
+ $user = User::create([
39
+ 'name' => $request->name,
40
+ 'email' => $request->email,
41
+ 'password' => Hash::make($request->password),
42
+ ]);
43
+
44
+ event(new Registered($user));
45
+
46
+ Auth::login($user);
47
+
48
+ return redirect(route('dashboard', absolute: false));
49
+ }
50
+ }
app/Http/Controllers/Auth/VerifyEmailController.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Controllers\Auth;
4
+
5
+ use App\Http\Controllers\Controller;
6
+ use Illuminate\Auth\Events\Verified;
7
+ use Illuminate\Foundation\Auth\EmailVerificationRequest;
8
+ use Illuminate\Http\RedirectResponse;
9
+
10
+ class VerifyEmailController extends Controller
11
+ {
12
+ /**
13
+ * Mark the authenticated user's email address as verified.
14
+ */
15
+ public function __invoke(EmailVerificationRequest $request): RedirectResponse
16
+ {
17
+ if ($request->user()->hasVerifiedEmail()) {
18
+ return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
19
+ }
20
+
21
+ if ($request->user()->markEmailAsVerified()) {
22
+ event(new Verified($request->user()));
23
+ }
24
+
25
+ return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
26
+ }
27
+ }
app/Http/Controllers/CartController.php ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Controllers;
4
+
5
+ use Illuminate\Http\Request;
6
+ use Illuminate\Support\Facades\Storage;
7
+ use App\Models\Product;
8
+ use App\Models\Order;
9
+
10
+ class CartController extends Controller
11
+ {
12
+ public function index()
13
+ {
14
+ $cart = session()->get('cart', []);
15
+ return view('cart', compact('cart'));
16
+ }
17
+
18
+ public function add(Request $request, $id)
19
+ {
20
+ $product = Product::findOrFail($id);
21
+ $quantity = (int) $request->input('quantity', 1);
22
+
23
+ // Check if product has enough stock
24
+ if ($product->Amount < $quantity) {
25
+ if ($request->ajax()) {
26
+ return response()->json([
27
+ 'success' => false,
28
+ 'message' => 'Insufficient stock. Available: ' . $product->Amount
29
+ ]);
30
+ }
31
+ return back()->with('error', 'Insufficient stock. Available: ' . $product->Amount);
32
+ }
33
+
34
+ $cart = session()->get('cart', []);
35
+
36
+ if (isset($cart[$id])) {
37
+ $newQuantity = $cart[$id]['quantity'] + $quantity;
38
+ if ($newQuantity > $product->Amount) {
39
+ if ($request->ajax()) {
40
+ return response()->json([
41
+ 'success' => false,
42
+ 'message' => 'Cannot add more items. Stock limit: ' . $product->Amount
43
+ ]);
44
+ }
45
+ return back()->with('error', 'Cannot add more items. Stock limit: ' . $product->Amount);
46
+ }
47
+ $cart[$id]['quantity'] = $newQuantity;
48
+ } else {
49
+ $cart[$id] = [
50
+ 'id' => $product->id,
51
+ 'name' => $product->name,
52
+ 'image' => $product->image,
53
+ 'price' => $product->price,
54
+ 'quantity' => $quantity,
55
+ 'game' => $product->game,
56
+ ];
57
+ }
58
+
59
+ session()->put('cart', $cart);
60
+
61
+ // Return JSON response for AJAX requests
62
+ if ($request->ajax()) {
63
+ return response()->json([
64
+ 'success' => true,
65
+ 'message' => 'Item added to cart successfully',
66
+ 'cart_count' => array_sum(array_column($cart, 'quantity'))
67
+ ]);
68
+ }
69
+
70
+ return redirect()->route('cart.index')->with('success', 'เพิ่มสินค้าลงตะกร้าเรียบร้อยแล้ว');
71
+ }
72
+
73
+ public function remove($id)
74
+ {
75
+ $cart = session()->get('cart', []);
76
+
77
+ if (isset($cart[$id])) {
78
+ unset($cart[$id]);
79
+ session()->put('cart', $cart);
80
+ }
81
+
82
+ return redirect()->route('cart.index')->with('success', 'ลบสินค้าออกจากตะกร้าเรียบร้อยแล้ว');
83
+ }
84
+ public function checkout(Request $request)
85
+ {
86
+ $request->validate([
87
+ 'customer_name' => 'required|string|max:255',
88
+ 'phone' => 'required|string|max:20',
89
+ 'payment_slip' => 'required|image|mimes:jpeg,png,jpg,gif|max:10240', // 10MB limit
90
+ ]);
91
+
92
+ try {
93
+ $path = $request->file('payment_slip')->store('payment_slips', 'public');
94
+
95
+ // Get cart data
96
+ $cart = session()->get('cart', []);
97
+
98
+ if (empty($cart)) {
99
+ return back()->with('error', 'Your cart is empty');
100
+ }
101
+
102
+ // Calculate total amount and validate stock
103
+ $totalAmount = 0;
104
+ foreach ($cart as $item) {
105
+ $product = Product::find($item['id']);
106
+ if (!$product) {
107
+ return back()->with('error', "Product {$item['name']} no longer exists");
108
+ }
109
+ if ($product->Amount < $item['quantity']) {
110
+ return back()->with('error', "Insufficient stock for {$product->name}. Available: {$product->Amount}, Requested: {$item['quantity']}");
111
+ }
112
+ $totalAmount += $item['price'] * $item['quantity'];
113
+ }
114
+
115
+ // Create order
116
+ $order = Order::create([
117
+ 'customer_name' => $request->customer_name,
118
+ 'phone' => $request->phone,
119
+ 'payment_slip_path' => $path,
120
+ 'cart_data' => json_encode($cart),
121
+ 'total_amount' => $totalAmount,
122
+ 'status' => 'pending',
123
+ ]);
124
+
125
+ // Update product stock
126
+ foreach ($cart as $item) {
127
+ $product = Product::find($item['id']);
128
+ if ($product) {
129
+ $product->Amount = max(0, $product->Amount - $item['quantity']);
130
+ $product->save();
131
+ }
132
+ }
133
+
134
+ // Clear cart
135
+ session()->forget('cart');
136
+
137
+ return redirect()->route('cart.index')->with('success', 'Thank you for your order! We have received your order successfully.');
138
+
139
+ } catch (\Exception $e) {
140
+ return back()->with('error', 'Failed to process order: ' . $e->getMessage());
141
+ }
142
+ }
143
+ public function order()
144
+ {
145
+ $orders = Order::latest()->get(); // ดึงทั้งหมด เรียงล่าสุดก่อน
146
+ return view('order', compact('orders'));
147
+ }
148
+ public function deleteOrder($id)
149
+ {
150
+ try {
151
+ $order = Order::findOrFail($id);
152
+
153
+ // Delete payment slip file if it exists
154
+ if ($order->payment_slip_path && Storage::disk('public')->exists($order->payment_slip_path)) {
155
+ Storage::disk('public')->delete($order->payment_slip_path);
156
+ }
157
+
158
+ $order->delete();
159
+
160
+ return back()->with('success', 'Order deleted successfully');
161
+ } catch (\Exception $e) {
162
+ return back()->with('error', 'Failed to delete order: ' . $e->getMessage());
163
+ }
164
+ }
165
+
166
+ public function show($id)
167
+ {
168
+ try {
169
+ $order = Order::findOrFail($id);
170
+ return view('order-detail', compact('order'));
171
+ } catch (\Exception $e) {
172
+ return redirect()->route('orders.index')->with('error', 'Order not found');
173
+ }
174
+ }
175
+
176
+ public function completeOrder($id)
177
+ {
178
+ try {
179
+ $order = Order::findOrFail($id);
180
+
181
+ // Update order status to completed
182
+ $order->status = 'completed';
183
+ $order->save();
184
+
185
+ return redirect()->route('orders.index')->with('success', 'Order #' . str_pad($order->id, 4, '0', STR_PAD_LEFT) . ' has been ACCEPTED successfully!');
186
+ } catch (\Exception $e) {
187
+ return redirect()->route('orders.index')->with('error', 'Failed to accept order: ' . $e->getMessage());
188
+ }
189
+ }
190
+
191
+ public function rejectOrder($id)
192
+ {
193
+ try {
194
+ $order = Order::findOrFail($id);
195
+
196
+ // Update order status to cancelled (rejected)
197
+ $order->status = 'cancelled';
198
+ $order->save();
199
+
200
+ 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.');
201
+ } catch (\Exception $e) {
202
+ return redirect()->route('orders.index')->with('error', 'Failed to reject order: ' . $e->getMessage());
203
+ }
204
+ }
205
+
206
+ public function customerOrders(Request $request)
207
+ {
208
+ // For now, we'll show all orders since we don't have user authentication for customers
209
+ // In a real app, you'd filter by authenticated customer
210
+ $orders = Order::latest()->get();
211
+
212
+ // If it's an AJAX request for load more functionality
213
+ if ($request->ajax()) {
214
+ $page = $request->get('page', 1);
215
+ $perPage = 3;
216
+ $offset = ($page - 1) * $perPage;
217
+ $status = $request->get('status');
218
+ $game = $request->get('game');
219
+
220
+ // Filter orders based on status
221
+ $filteredOrders = $orders;
222
+
223
+ if ($status && $status !== 'recently') {
224
+ $filteredOrders = $orders->where('status', $status);
225
+ }
226
+
227
+ // Filter by game if specified
228
+ if ($game && $game !== 'all') {
229
+ $filteredOrders = $filteredOrders->filter(function($order) use ($game) {
230
+ $items = json_decode($order->cart_data, true);
231
+ if (is_array($items)) {
232
+ foreach ($items as $item) {
233
+ $product = \App\Models\Product::find($item['id'] ?? null);
234
+ if ($product && $product->game === $game) {
235
+ return true;
236
+ }
237
+ }
238
+ }
239
+ return false;
240
+ });
241
+ }
242
+
243
+ $paginatedOrders = $filteredOrders->slice($offset, $perPage);
244
+ $hasMore = $filteredOrders->count() > ($offset + $perPage);
245
+
246
+ return response()->json([
247
+ 'orders' => $paginatedOrders->values(),
248
+ 'hasMore' => $hasMore,
249
+ 'currentPage' => $page
250
+ ]);
251
+ }
252
+
253
+ return view('customer-orders', compact('orders'));
254
+ }
255
+ }
app/Http/Controllers/Controller.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Controllers;
4
+
5
+ abstract class Controller
6
+ {
7
+ //
8
+ }
app/Http/Controllers/DashboardController.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Controllers;
4
+
5
+ use App\Services\AnalyticsService;
6
+ use Illuminate\Http\Request;
7
+
8
+ class DashboardController extends Controller
9
+ {
10
+ protected AnalyticsService $analyticsService;
11
+
12
+ public function __construct(AnalyticsService $analyticsService)
13
+ {
14
+ $this->analyticsService = $analyticsService;
15
+ }
16
+
17
+ public function index()
18
+ {
19
+ try {
20
+ $todayRevenue = $this->analyticsService->getTodayRevenue();
21
+ $profitData = $this->analyticsService->getProfitData('today');
22
+ $customerAnalytics = $this->analyticsService->getCustomerAnalytics();
23
+ $orderStats = $this->analyticsService->getOrderStats();
24
+ } catch (\Exception $e) {
25
+ // Fallback values if service fails
26
+ $todayRevenue = ['current' => 0, 'previous' => 0, 'percentage_change' => 0, 'trend' => 'up'];
27
+ $profitData = ['profit_margin' => 0, 'percentage_change' => 0, 'trend' => 'up'];
28
+ $customerAnalytics = ['total_customers' => 0, 'active_today' => 0];
29
+ $orderStats = ['current' => 0, 'previous' => 0, 'percentage_change' => 0, 'trend' => 'up'];
30
+ }
31
+
32
+ return view('dashboard', compact(
33
+ 'todayRevenue',
34
+ 'profitData',
35
+ 'customerAnalytics',
36
+ 'orderStats'
37
+ ));
38
+ }
39
+ }
app/Http/Controllers/ProductController.php ADDED
@@ -0,0 +1,411 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Controllers;
4
+ use App\Models\Product;
5
+ use App\Models\CustomGame;
6
+ use Illuminate\Http\Request;
7
+ use Illuminate\Support\Facades\Storage;
8
+
9
+ class ProductController extends Controller
10
+ {
11
+ // Products index method removed
12
+ public function store(Request $request)
13
+ {
14
+ $request->validate([
15
+ 'name' => 'required',
16
+ 'price' => 'required|numeric',
17
+ 'description' => 'required',
18
+ 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
19
+ 'Amount' => 'required|numeric',
20
+ 'game' => 'nullable|string'
21
+ ]);
22
+
23
+ $product = new Product($request->only(['name', 'price', 'description', 'Amount', 'game']));
24
+
25
+ if ($request->hasFile('image')) {
26
+ $imagePath = $request->file('image')->store('products', 'public');
27
+ $product->image = $imagePath;
28
+ }
29
+
30
+ $product->save();
31
+
32
+ return redirect()->back()->with('success', 'Product created successfully');
33
+ }
34
+ public function show($category, $product_name)
35
+ {
36
+ $product = Product::where('game', $category)
37
+ ->where('slug', $product_name)
38
+ ->firstOrFail();
39
+ return view('show', compact('product'));
40
+ }
41
+
42
+ public function categoryProducts($category)
43
+ {
44
+ $products = Product::where('game', $category)->get();
45
+
46
+ // Get all custom games for navigation
47
+ $customGames = CustomGame::orderBy('name')->get();
48
+
49
+ return view('category_products', compact('products', 'category', 'customGames'));
50
+ }
51
+
52
+ public function edit($id)
53
+ {
54
+ $product = Product::findOrFail($id);
55
+ return response()->json($product);
56
+ }
57
+ public function listOFproduct(Request $request)
58
+ {
59
+ $query = Product::query();
60
+
61
+ // Game filter
62
+ if ($request->filled('game')) {
63
+ $query->where('game', $request->input('game'));
64
+ }
65
+
66
+ // Stock filter
67
+ if ($request->filled('stock')) {
68
+ $stock = $request->input('stock');
69
+ switch ($stock) {
70
+ case 'in_stock':
71
+ $query->where('Amount', '>', 5);
72
+ break;
73
+ case 'low_stock':
74
+ $query->whereBetween('Amount', [1, 5]);
75
+ break;
76
+ case 'out_of_stock':
77
+ $query->where('Amount', 0);
78
+ break;
79
+ }
80
+ }
81
+
82
+ // Search filter
83
+ if ($request->filled('search')) {
84
+ $search = $request->input('search');
85
+ $query->where(function($q) use ($search) {
86
+ $q->where('name', 'like', '%' . $search . '%')
87
+ ->orWhere('description', 'like', '%' . $search . '%');
88
+ });
89
+ }
90
+
91
+ $products = $query->orderBy('created_at', 'desc')->get();
92
+
93
+ // Get all custom games for the view
94
+ $customGames = CustomGame::orderBy('name')->get();
95
+
96
+ return view('table_product', compact('products', 'customGames'));
97
+ }
98
+ public function update(Request $request, $id)
99
+ {
100
+ $request->validate([
101
+ 'name' => 'required',
102
+ 'price' => 'required|numeric',
103
+ 'description' => 'required',
104
+ 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
105
+ 'Amount' => 'required|numeric',
106
+ 'game' => 'nullable|string'
107
+ ]);
108
+
109
+ $product = Product::findOrFail($id);
110
+ $product->update($request->only(['name', 'price', 'description', 'Amount', 'game']));
111
+
112
+ if ($request->hasFile('image')) {
113
+ // Delete old image if exists
114
+ if ($product->image && Storage::disk('public')->exists($product->image)) {
115
+ Storage::disk('public')->delete($product->image);
116
+ }
117
+
118
+ $imagePath = $request->file('image')->store('products', 'public');
119
+ $product->image = $imagePath;
120
+ $product->save();
121
+ }
122
+
123
+ return redirect()->back()->with('success', 'Product updated successfully');
124
+ }
125
+
126
+ public function destroy($id)
127
+ {
128
+ try {
129
+ $product = Product::findOrFail($id);
130
+
131
+ // Delete image file if exists
132
+ if ($product->image && Storage::disk('public')->exists($product->image)) {
133
+ Storage::disk('public')->delete($product->image);
134
+ }
135
+
136
+ $product->delete();
137
+
138
+ return back()->with('success', 'Product deleted successfully');
139
+ } catch (\Exception $e) {
140
+ return back()->with('error', 'Failed to delete product: ' . $e->getMessage());
141
+ }
142
+ }
143
+
144
+ public function categories()
145
+ {
146
+ // Get count of products for each category
147
+ $categoryCounts = [
148
+ 'Genshin' => Product::where('game', 'Genshin')->count(),
149
+ 'Starrail' => Product::where('game', 'Starrail')->count(),
150
+ 'WutheringWave' => Product::where('game', 'WutheringWave')->count(),
151
+ ];
152
+
153
+ // Get all custom games
154
+ $customGames = CustomGame::orderBy('name')->get();
155
+
156
+ return view('categories', compact('categoryCounts', 'customGames'));
157
+ }
158
+
159
+ public function getByCategory($game)
160
+ {
161
+ $products = Product::where('game', $game)->get();
162
+
163
+ // Add sales count to each product - only count completed orders
164
+ $productsWithSales = $products->map(function ($product) {
165
+ $salesCount = \App\Models\Order::where('cart_data', 'LIKE', '%"product_id":' . $product->id . '%')
166
+ ->where('status', 'completed')
167
+ ->count();
168
+ $product->sales_count = $salesCount;
169
+ return $product;
170
+ });
171
+
172
+ return response()->json($productsWithSales);
173
+ }
174
+
175
+ public function storeCustomGame(Request $request)
176
+ {
177
+ try {
178
+ $request->validate([
179
+ 'name' => 'required|string|max:50|unique:custom_games,name',
180
+ 'icon' => 'nullable|string',
181
+ 'color_gradient' => 'nullable|string'
182
+ ]);
183
+
184
+ $customGame = CustomGame::create([
185
+ 'name' => $request->name,
186
+ 'icon' => $request->icon ?? 'fas fa-gamepad',
187
+ 'color_gradient' => $request->color_gradient ?? 'from-purple-500 to-blue-500'
188
+ ]);
189
+
190
+ return response()->json([
191
+ 'success' => true,
192
+ 'message' => "Custom game '{$request->name}' has been created successfully!",
193
+ 'data' => $customGame
194
+ ]);
195
+
196
+ } catch (\Exception $e) {
197
+ return response()->json([
198
+ 'success' => false,
199
+ 'message' => 'Failed to create custom game: ' . $e->getMessage()
200
+ ], 500);
201
+ }
202
+ }
203
+
204
+ public function deleteCustomGame($gameName)
205
+ {
206
+ try {
207
+ // Check if there are any products using this custom game
208
+ $productsCount = Product::where('game', $gameName)->count();
209
+
210
+ if ($productsCount > 0) {
211
+ return response()->json([
212
+ 'success' => false,
213
+ 'message' => "Cannot delete '{$gameName}' because {$productsCount} product(s) are still using this category. Please reassign or delete those products first."
214
+ ], 400);
215
+ }
216
+
217
+ // Delete from database
218
+ $customGame = CustomGame::where('name', $gameName)->first();
219
+ if ($customGame) {
220
+ $customGame->delete();
221
+ }
222
+
223
+ return response()->json([
224
+ 'success' => true,
225
+ 'message' => "Custom game '{$gameName}' has been deleted successfully!"
226
+ ]);
227
+
228
+ } catch (\Exception $e) {
229
+ return response()->json([
230
+ 'success' => false,
231
+ 'message' => 'Failed to delete custom game: ' . $e->getMessage()
232
+ ], 500);
233
+ }
234
+ }
235
+
236
+ public function search(Request $request)
237
+ {
238
+ $query = $request->get('q', '');
239
+
240
+ if (strlen($query) < 2) {
241
+ return response()->json(['games' => [], 'products' => []]);
242
+ }
243
+
244
+ // Search games with rich data including descriptions and gradients
245
+ $mainGames = [
246
+ [
247
+ 'name' => 'Genshin Impact',
248
+ 'slug' => 'Genshin',
249
+ 'icon' => 'fas fa-star',
250
+ 'count' => Product::where('game', 'Genshin')->count(),
251
+ 'description' => 'Discover premium digital content for your favorite games',
252
+ 'gradient' => 'from-yellow-500 to-orange-500',
253
+ 'logo' => asset('images/games/genshin-logo.png'), // You can add actual logos later
254
+ 'category' => 'RPG',
255
+ 'popularity' => 'Popular'
256
+ ],
257
+ [
258
+ 'name' => 'Honkai: Star Rail',
259
+ 'slug' => 'Starrail',
260
+ 'icon' => 'fas fa-rocket',
261
+ 'count' => Product::where('game', 'Starrail')->count(),
262
+ 'description' => 'Space fantasy RPG with strategic combat',
263
+ 'gradient' => 'from-purple-500 to-pink-500',
264
+ 'logo' => asset('images/games/starrail-logo.png'),
265
+ 'category' => 'RPG',
266
+ 'popularity' => 'Featured'
267
+ ],
268
+ [
269
+ 'name' => 'Wuthering Waves',
270
+ 'slug' => 'WutheringWave',
271
+ 'icon' => 'fas fa-wave-square',
272
+ 'count' => Product::where('game', 'WutheringWave')->count(),
273
+ 'description' => 'Open-world action RPG with stunning visuals',
274
+ 'gradient' => 'from-cyan-500 to-blue-500',
275
+ 'logo' => asset('images/games/wuthering-logo.png'),
276
+ 'category' => 'Action RPG',
277
+ 'popularity' => 'New'
278
+ ],
279
+ [
280
+ 'name' => 'Zenless Zone Zero',
281
+ 'slug' => 'ZenlessZoneZero',
282
+ 'icon' => 'fas fa-city',
283
+ 'count' => Product::where('game', 'ZenlessZoneZero')->count(),
284
+ 'description' => 'Urban fantasy action game',
285
+ 'gradient' => 'from-red-500 to-pink-500',
286
+ 'logo' => asset('images/games/zenless-logo.png'),
287
+ 'category' => 'Action',
288
+ 'popularity' => 'Trending'
289
+ ],
290
+ [
291
+ 'name' => 'Arknights',
292
+ 'slug' => 'Arknights',
293
+ 'icon' => 'fas fa-chess-knight',
294
+ 'count' => Product::where('game', 'Arknights')->count(),
295
+ 'description' => 'Strategic tower defense with anime characters',
296
+ 'gradient' => 'from-indigo-500 to-purple-500',
297
+ 'logo' => asset('images/games/arknights-logo.png'),
298
+ 'category' => 'Strategy',
299
+ 'popularity' => 'Classic'
300
+ ],
301
+ [
302
+ 'name' => 'Azur Lane',
303
+ 'slug' => 'AzurLane',
304
+ 'icon' => 'fas fa-ship',
305
+ 'count' => Product::where('game', 'AzurLane')->count(),
306
+ 'description' => 'Naval warfare with anthropomorphic ships',
307
+ 'gradient' => 'from-blue-500 to-teal-500',
308
+ 'logo' => asset('images/games/azurlane-logo.png'),
309
+ 'category' => 'Strategy',
310
+ 'popularity' => 'Popular'
311
+ ],
312
+ ];
313
+
314
+ // Add custom games with their stored styling
315
+ $customGames = CustomGame::all()->map(function($game) {
316
+ return [
317
+ 'name' => $game->name,
318
+ 'slug' => $game->name,
319
+ 'icon' => $game->icon,
320
+ 'count' => Product::where('game', $game->name)->count(),
321
+ 'description' => 'Custom game category with unique items',
322
+ 'gradient' => $game->color_gradient,
323
+ 'logo' => asset('images/games/custom-logo.png'),
324
+ 'category' => 'Custom',
325
+ 'popularity' => 'Special'
326
+ ];
327
+ });
328
+
329
+ $allGames = collect($mainGames)->concat($customGames);
330
+
331
+ // Filter games by query
332
+ $matchingGames = $allGames->filter(function($game) use ($query) {
333
+ return stripos($game['name'], $query) !== false;
334
+ })->take(5)->values();
335
+
336
+ // Search products with enhanced data including real sales count
337
+ $products = Product::where('name', 'like', "%{$query}%")
338
+ ->orWhere('description', 'like', "%{$query}%")
339
+ ->orWhere('game', 'like', "%{$query}%")
340
+ ->take(8)
341
+ ->get()
342
+ ->map(function($product) {
343
+ // Calculate real sales count from completed orders
344
+ $salesCount = \App\Models\Order::where('status', 'completed')
345
+ ->where('cart_data', 'LIKE', '%"id":' . $product->id . '%')
346
+ ->get()
347
+ ->sum(function($order) use ($product) {
348
+ $cartData = json_decode($order->cart_data, true);
349
+ $quantity = 0;
350
+ if (is_array($cartData)) {
351
+ foreach ($cartData as $item) {
352
+ if (isset($item['id']) && $item['id'] == $product->id) {
353
+ $quantity += $item['quantity'] ?? 1;
354
+ }
355
+ }
356
+ }
357
+ return $quantity;
358
+ });
359
+
360
+ // Calculate average rating from orders (mock for now, can be enhanced later)
361
+ $rating = $salesCount > 0 ? min(5.0, 3.5 + ($salesCount / 100)) : 4.0;
362
+
363
+ return [
364
+ 'id' => $product->id,
365
+ 'name' => $product->name,
366
+ 'slug' => $product->slug ?? strtolower(str_replace(' ', '-', $product->name)),
367
+ 'game' => $product->game,
368
+ 'game_slug' => $product->game,
369
+ 'price' => number_format($product->price, 2),
370
+ 'original_price' => $product->price,
371
+ 'image' => $product->image ? asset($product->image) : 'https://via.placeholder.com/150x150/374151/ffffff?text=No+Image',
372
+ 'description' => $product->description,
373
+ 'short_description' => strlen($product->description) > 100 ? substr($product->description, 0, 100) . '...' : $product->description,
374
+ 'amount' => $product->Amount,
375
+ 'stock_status' => $product->Amount > 10 ? 'In Stock (' . $product->Amount . ')' :
376
+ ($product->Amount > 0 ? 'Low Stock (' . $product->Amount . ')' : 'Out of Stock'),
377
+ 'stock_color' => $product->Amount > 10 ? 'text-green-400' :
378
+ ($product->Amount > 0 ? 'text-yellow-400' : 'text-red-400'),
379
+ 'stock_bg' => $product->Amount > 10 ? 'bg-green-500/20' :
380
+ ($product->Amount > 0 ? 'bg-yellow-500/20' : 'bg-red-500/20'),
381
+ 'created_at' => $product->created_at->format('M d, Y'),
382
+ 'is_new' => $product->created_at->diffInDays(now()) <= 7,
383
+ 'rating' => round($rating, 1),
384
+ 'sales_count' => $salesCount,
385
+ 'total_revenue' => $salesCount * $product->price,
386
+ 'availability' => $product->Amount > 0 ? 'Available' : 'Sold Out',
387
+ 'category_icon' => $this->getGameIcon($product->game),
388
+ 'popularity_score' => $salesCount > 50 ? 'Hot' : ($salesCount > 20 ? 'Popular' : 'New')
389
+ ];
390
+ });
391
+
392
+ return response()->json([
393
+ 'games' => $matchingGames,
394
+ 'products' => $products
395
+ ]);
396
+ }
397
+
398
+ private function getGameIcon($game)
399
+ {
400
+ $icons = [
401
+ 'Genshin' => 'fas fa-star',
402
+ 'Starrail' => 'fas fa-rocket',
403
+ 'WutheringWave' => 'fas fa-wave-square',
404
+ 'ZenlessZoneZero' => 'fas fa-city',
405
+ 'Arknights' => 'fas fa-chess-knight',
406
+ 'AzurLane' => 'fas fa-ship',
407
+ ];
408
+
409
+ return $icons[$game] ?? 'fas fa-gamepad';
410
+ }
411
+ }
app/Http/Controllers/ProfileController.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Controllers;
4
+
5
+ use App\Http\Requests\ProfileUpdateRequest;
6
+ use Illuminate\Http\RedirectResponse;
7
+ use Illuminate\Http\Request;
8
+ use Illuminate\Support\Facades\Auth;
9
+ use Illuminate\Support\Facades\Redirect;
10
+ use Illuminate\View\View;
11
+
12
+ class ProfileController extends Controller
13
+ {
14
+ /**
15
+ * Display the user's profile form.
16
+ */
17
+ public function edit(Request $request): View
18
+ {
19
+ return view('profile.edit', [
20
+ 'user' => $request->user(),
21
+ ]);
22
+ }
23
+
24
+ /**
25
+ * Update the user's profile information.
26
+ */
27
+ public function update(ProfileUpdateRequest $request): RedirectResponse
28
+ {
29
+ $request->user()->fill($request->validated());
30
+
31
+ if ($request->user()->isDirty('email')) {
32
+ $request->user()->email_verified_at = null;
33
+ }
34
+
35
+ $request->user()->save();
36
+
37
+ return Redirect::route('profile.edit')->with('status', 'profile-updated');
38
+ }
39
+
40
+ /**
41
+ * Delete the user's account.
42
+ */
43
+ public function destroy(Request $request): RedirectResponse
44
+ {
45
+ $request->validateWithBag('userDeletion', [
46
+ 'password' => ['required', 'current_password'],
47
+ ]);
48
+
49
+ $user = $request->user();
50
+
51
+ Auth::logout();
52
+
53
+ $user->delete();
54
+
55
+ $request->session()->invalidate();
56
+ $request->session()->regenerateToken();
57
+
58
+ return Redirect::to('/');
59
+ }
60
+ }
app/Http/Requests/Auth/LoginRequest.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Requests\Auth;
4
+
5
+ use Illuminate\Auth\Events\Lockout;
6
+ use Illuminate\Foundation\Http\FormRequest;
7
+ use Illuminate\Support\Facades\Auth;
8
+ use Illuminate\Support\Facades\RateLimiter;
9
+ use Illuminate\Support\Str;
10
+ use Illuminate\Validation\ValidationException;
11
+
12
+ class LoginRequest extends FormRequest
13
+ {
14
+ /**
15
+ * Determine if the user is authorized to make this request.
16
+ */
17
+ public function authorize(): bool
18
+ {
19
+ return true;
20
+ }
21
+
22
+ /**
23
+ * Get the validation rules that apply to the request.
24
+ *
25
+ * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
26
+ */
27
+ public function rules(): array
28
+ {
29
+ return [
30
+ 'email' => ['required', 'string', 'email'],
31
+ 'password' => ['required', 'string'],
32
+ ];
33
+ }
34
+
35
+ /**
36
+ * Attempt to authenticate the request's credentials.
37
+ *
38
+ * @throws \Illuminate\Validation\ValidationException
39
+ */
40
+ public function authenticate(): void
41
+ {
42
+ $this->ensureIsNotRateLimited();
43
+
44
+ if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
45
+ RateLimiter::hit($this->throttleKey());
46
+
47
+ throw ValidationException::withMessages([
48
+ 'email' => trans('auth.failed'),
49
+ ]);
50
+ }
51
+
52
+ RateLimiter::clear($this->throttleKey());
53
+ }
54
+
55
+ /**
56
+ * Ensure the login request is not rate limited.
57
+ *
58
+ * @throws \Illuminate\Validation\ValidationException
59
+ */
60
+ public function ensureIsNotRateLimited(): void
61
+ {
62
+ if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
63
+ return;
64
+ }
65
+
66
+ event(new Lockout($this));
67
+
68
+ $seconds = RateLimiter::availableIn($this->throttleKey());
69
+
70
+ throw ValidationException::withMessages([
71
+ 'email' => trans('auth.throttle', [
72
+ 'seconds' => $seconds,
73
+ 'minutes' => ceil($seconds / 60),
74
+ ]),
75
+ ]);
76
+ }
77
+
78
+ /**
79
+ * Get the rate limiting throttle key for the request.
80
+ */
81
+ public function throttleKey(): string
82
+ {
83
+ return Str::transliterate(Str::lower($this->string('email')).'|'.$this->ip());
84
+ }
85
+ }
app/Http/Requests/ProfileUpdateRequest.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Http\Requests;
4
+
5
+ use App\Models\User;
6
+ use Illuminate\Foundation\Http\FormRequest;
7
+ use Illuminate\Validation\Rule;
8
+
9
+ class ProfileUpdateRequest extends FormRequest
10
+ {
11
+ /**
12
+ * Get the validation rules that apply to the request.
13
+ *
14
+ * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
15
+ */
16
+ public function rules(): array
17
+ {
18
+ return [
19
+ 'name' => ['required', 'string', 'max:255'],
20
+ 'email' => [
21
+ 'required',
22
+ 'string',
23
+ 'lowercase',
24
+ 'email',
25
+ 'max:255',
26
+ Rule::unique(User::class)->ignore($this->user()->id),
27
+ ],
28
+ ];
29
+ }
30
+ }
app/Models/AnalyticsSnapshot.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Models;
4
+
5
+ use Illuminate\Database\Eloquent\Model;
6
+ use Carbon\Carbon;
7
+
8
+ class AnalyticsSnapshot extends Model
9
+ {
10
+ protected $fillable = [
11
+ 'date',
12
+ 'hour',
13
+ 'revenue',
14
+ 'profit',
15
+ 'orders_count',
16
+ 'customers_count',
17
+ 'avg_order_value',
18
+ 'conversion_rate'
19
+ ];
20
+
21
+ protected $casts = [
22
+ 'date' => 'date',
23
+ 'revenue' => 'decimal:2',
24
+ 'profit' => 'decimal:2',
25
+ 'avg_order_value' => 'decimal:2',
26
+ 'conversion_rate' => 'decimal:4'
27
+ ];
28
+
29
+ // Scopes for different time periods
30
+ public function scopeDaily($query, Carbon $date)
31
+ {
32
+ return $query->where('date', $date->format('Y-m-d'))
33
+ ->whereNotNull('hour')
34
+ ->orderBy('hour');
35
+ }
36
+
37
+ public function scopeWeekly($query, Carbon $startDate)
38
+ {
39
+ return $query->whereBetween('date', [
40
+ $startDate->format('Y-m-d'),
41
+ $startDate->copy()->addDays(6)->format('Y-m-d')
42
+ ])
43
+ ->whereNull('hour')
44
+ ->orderBy('date');
45
+ }
46
+
47
+ public function scopeMonthly($query, Carbon $date)
48
+ {
49
+ return $query->whereYear('date', $date->year)
50
+ ->whereMonth('date', $date->month)
51
+ ->whereNull('hour')
52
+ ->orderBy('date');
53
+ }
54
+
55
+ // Helper method to get profit margin percentage
56
+ public function getProfitMarginAttribute()
57
+ {
58
+ if ($this->revenue > 0) {
59
+ return ($this->profit / $this->revenue) * 100;
60
+ }
61
+ return 0;
62
+ }
63
+ }
app/Models/CustomGame.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Models;
4
+
5
+ use Illuminate\Database\Eloquent\Model;
6
+
7
+ class CustomGame extends Model
8
+ {
9
+ protected $fillable = [
10
+ 'name',
11
+ 'icon',
12
+ 'color_gradient'
13
+ ];
14
+ }
app/Models/Order.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Models;
4
+
5
+ use Illuminate\Database\Eloquent\Model;
6
+
7
+ class Order extends Model
8
+ {
9
+ protected $fillable = [
10
+ 'customer_name',
11
+ 'phone',
12
+ 'payment_slip_path',
13
+ 'cart_data',
14
+ 'total_amount',
15
+ 'status',
16
+ ];
17
+ }
app/Models/Product.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Models;
4
+
5
+ use Illuminate\Database\Eloquent\Model;
6
+ use Illuminate\Support\Str;
7
+
8
+ class Product extends Model
9
+ {
10
+ protected $fillable = ['name', 'price', 'description', 'image', 'Amount', 'game', 'slug'];
11
+
12
+ protected static function boot()
13
+ {
14
+ parent::boot();
15
+
16
+ static::creating(function ($product) {
17
+ if (empty($product->slug)) {
18
+ $product->slug = Str::slug($product->name);
19
+ }
20
+ });
21
+
22
+ static::updating(function ($product) {
23
+ if ($product->isDirty('name') && empty($product->slug)) {
24
+ $product->slug = Str::slug($product->name);
25
+ }
26
+ });
27
+ }
28
+
29
+ public function getImageAttribute($value)
30
+ {
31
+ if (!$value) {
32
+ return null;
33
+ }
34
+
35
+ // If it's already a full URL, return as is
36
+ if (str_starts_with($value, 'http')) {
37
+ return $value;
38
+ }
39
+
40
+ // If it starts with 'storage/', it's already formatted
41
+ if (str_starts_with($value, 'storage/')) {
42
+ return asset($value);
43
+ }
44
+
45
+ // Otherwise, prepend 'storage/'
46
+ return asset('storage/' . $value);
47
+ }
48
+ }
app/Models/RevenueTracking.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Models;
4
+
5
+ use Illuminate\Database\Eloquent\Model;
6
+ use Illuminate\Database\Eloquent\Relations\BelongsTo;
7
+
8
+ class RevenueTracking extends Model
9
+ {
10
+ protected $table = 'revenue_tracking';
11
+
12
+ protected $fillable = [
13
+ 'order_id',
14
+ 'amount',
15
+ 'cost',
16
+ 'profit_margin',
17
+ 'product_name',
18
+ 'quantity'
19
+ ];
20
+
21
+ protected $casts = [
22
+ 'amount' => 'decimal:2',
23
+ 'cost' => 'decimal:2',
24
+ 'profit_margin' => 'decimal:4'
25
+ ];
26
+
27
+ // Relationships
28
+ public function order(): BelongsTo
29
+ {
30
+ return $this->belongsTo(Order::class);
31
+ }
32
+
33
+ // Calculate profit amount
34
+ public function getProfitAttribute()
35
+ {
36
+ return $this->amount - $this->cost;
37
+ }
38
+
39
+ // Calculate profit margin percentage
40
+ public function getProfitMarginPercentageAttribute()
41
+ {
42
+ if ($this->amount > 0) {
43
+ return (($this->amount - $this->cost) / $this->amount) * 100;
44
+ }
45
+ return 0;
46
+ }
47
+
48
+ // Scope for today's revenue
49
+ public function scopeToday($query)
50
+ {
51
+ return $query->whereDate('created_at', today());
52
+ }
53
+
54
+ // Scope for date range
55
+ public function scopeDateRange($query, $startDate, $endDate)
56
+ {
57
+ return $query->whereBetween('created_at', [$startDate, $endDate]);
58
+ }
59
+ }
app/Models/User.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Models;
4
+
5
+ // use Illuminate\Contracts\Auth\MustVerifyEmail;
6
+ use Illuminate\Database\Eloquent\Factories\HasFactory;
7
+ use Illuminate\Foundation\Auth\User as Authenticatable;
8
+ use Illuminate\Notifications\Notifiable;
9
+
10
+ class User extends Authenticatable
11
+ {
12
+ /** @use HasFactory<\Database\Factories\UserFactory> */
13
+ use HasFactory, Notifiable;
14
+
15
+ /**
16
+ * The attributes that are mass assignable.
17
+ *
18
+ * @var list<string>
19
+ */
20
+ protected $fillable = [
21
+ 'name',
22
+ 'email',
23
+ 'password',
24
+ ];
25
+
26
+ /**
27
+ * The attributes that should be hidden for serialization.
28
+ *
29
+ * @var list<string>
30
+ */
31
+ protected $hidden = [
32
+ 'password',
33
+ 'remember_token',
34
+ ];
35
+
36
+ /**
37
+ * Get the attributes that should be cast.
38
+ *
39
+ * @return array<string, string>
40
+ */
41
+ protected function casts(): array
42
+ {
43
+ return [
44
+ 'email_verified_at' => 'datetime',
45
+ 'password' => 'hashed',
46
+ ];
47
+ }
48
+ }
app/Providers/AppServiceProvider.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Providers;
4
+
5
+ use Illuminate\Support\ServiceProvider;
6
+ use Illuminate\Support\Facades\URL;
7
+
8
+ class AppServiceProvider extends ServiceProvider
9
+ {
10
+ /**
11
+ * Register any application services.
12
+ */
13
+ public function register(): void
14
+ {
15
+ //
16
+ }
17
+
18
+ /**
19
+ * Bootstrap any application services.
20
+ */
21
+ public function boot(): void
22
+ {
23
+ // Force HTTPS when using ngrok or in production
24
+ if (config('app.env') !== 'local' || request()->header('x-forwarded-proto') === 'https') {
25
+ URL::forceScheme('https');
26
+ }
27
+ }
28
+ }
app/Services/AnalyticsService.php ADDED
@@ -0,0 +1,379 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\Services;
4
+
5
+ use App\Models\AnalyticsSnapshot;
6
+ use App\Models\RevenueTracking;
7
+ use App\Models\Order;
8
+ use App\Models\User;
9
+ use App\Models\Product;
10
+ use Carbon\Carbon;
11
+ use Illuminate\Support\Facades\DB;
12
+
13
+ class AnalyticsService
14
+ {
15
+ /**
16
+ * Calculate real-time revenue for today using real Order data
17
+ */
18
+ public function getTodayRevenue(): array
19
+ {
20
+ $today = Carbon::today();
21
+ $yesterday = Carbon::yesterday();
22
+
23
+ // Use real Order data
24
+ $todayRevenue = Order::whereDate('created_at', $today)
25
+ ->where('status', '!=', 'cancelled')
26
+ ->sum('total_amount');
27
+
28
+ $yesterdayRevenue = Order::whereDate('created_at', $yesterday)
29
+ ->where('status', '!=', 'cancelled')
30
+ ->sum('total_amount');
31
+
32
+ $percentageChange = $yesterdayRevenue > 0
33
+ ? (($todayRevenue - $yesterdayRevenue) / $yesterdayRevenue) * 100
34
+ : ($todayRevenue > 0 ? 100 : 0);
35
+
36
+ return [
37
+ 'current' => (float) $todayRevenue,
38
+ 'previous' => (float) $yesterdayRevenue,
39
+ 'percentage_change' => round($percentageChange, 2),
40
+ 'trend' => $percentageChange >= 0 ? 'up' : 'down'
41
+ ];
42
+ }
43
+
44
+ /**
45
+ * Calculate profit data with margins using real Order data
46
+ */
47
+ public function getProfitData(string $period = 'today'): array
48
+ {
49
+ $query = Order::where('status', '!=', 'cancelled');
50
+ $previousQuery = Order::where('status', '!=', 'cancelled');
51
+
52
+ switch ($period) {
53
+ case 'today':
54
+ $query->whereDate('created_at', Carbon::today());
55
+ $previousQuery->whereDate('created_at', Carbon::yesterday());
56
+ break;
57
+ case 'week':
58
+ $query->whereBetween('created_at', [Carbon::now()->startOfWeek(), Carbon::now()->endOfWeek()]);
59
+ $previousQuery->whereBetween('created_at', [
60
+ Carbon::now()->subWeek()->startOfWeek(),
61
+ Carbon::now()->subWeek()->endOfWeek()
62
+ ]);
63
+ break;
64
+ case 'month':
65
+ $query->whereMonth('created_at', Carbon::now()->month);
66
+ $previousQuery->whereMonth('created_at', Carbon::now()->subMonth()->month);
67
+ break;
68
+ }
69
+
70
+ $revenue = $query->sum('total_amount');
71
+ $previousRevenue = $previousQuery->sum('total_amount');
72
+
73
+ // Estimate profit as 30% of revenue (you can adjust this based on your business model)
74
+ $estimatedProfitMargin = 0.30;
75
+ $profit = $revenue * $estimatedProfitMargin;
76
+ $previousProfit = $previousRevenue * $estimatedProfitMargin;
77
+ $cost = $revenue - $profit;
78
+
79
+ $profitMarginPercentage = $revenue > 0 ? ($profit / $revenue) * 100 : 0;
80
+ $percentageChange = $previousProfit > 0
81
+ ? (($profit - $previousProfit) / $previousProfit) * 100
82
+ : ($profit > 0 ? 100 : 0);
83
+
84
+ return [
85
+ 'profit' => (float) $profit,
86
+ 'revenue' => (float) $revenue,
87
+ 'cost' => (float) $cost,
88
+ 'profit_margin' => round($profitMarginPercentage, 2),
89
+ 'percentage_change' => round($percentageChange, 2),
90
+ 'trend' => $percentageChange >= 0 ? 'up' : 'down'
91
+ ];
92
+ }
93
+
94
+ /**
95
+ * Get aggregated data for different time periods
96
+ */
97
+ public function getAggregatedData(string $period, Carbon $date = null): array
98
+ {
99
+ $date = $date ?? Carbon::now();
100
+
101
+ switch ($period) {
102
+ case 'daily':
103
+ return $this->getDailyHourlyData($date);
104
+ case 'weekly':
105
+ return $this->getWeeklyDailyData($date);
106
+ case 'monthly':
107
+ return $this->getMonthlyDailyData($date);
108
+ default:
109
+ return [];
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Get hourly data for a specific day using real Order data
115
+ */
116
+ private function getDailyHourlyData(Carbon $date): array
117
+ {
118
+ $hourlyData = [];
119
+
120
+ for ($hour = 0; $hour < 24; $hour++) {
121
+ $startTime = $date->copy()->hour($hour)->minute(0)->second(0);
122
+ $endTime = $startTime->copy()->addHour();
123
+
124
+ $orders = Order::where('status', '!=', 'cancelled')
125
+ ->whereBetween('created_at', [$startTime, $endTime])
126
+ ->get();
127
+
128
+ $revenue = $orders->sum('total_amount');
129
+ $profit = $revenue * 0.30; // 30% profit margin estimate
130
+ $ordersCount = $orders->count();
131
+ $customersCount = $orders->pluck('customer_name')->unique()->count();
132
+
133
+ $hourlyData[] = [
134
+ 'hour' => $hour,
135
+ 'revenue' => (float) $revenue,
136
+ 'profit' => (float) $profit,
137
+ 'orders' => $ordersCount,
138
+ 'customers' => $customersCount,
139
+ ];
140
+ }
141
+
142
+ return $hourlyData;
143
+ }
144
+
145
+ /**
146
+ * Get daily data for a week using real Order data
147
+ */
148
+ private function getWeeklyDailyData(Carbon $date): array
149
+ {
150
+ $startOfWeek = $date->copy()->startOfWeek();
151
+ $dailyData = [];
152
+
153
+ for ($day = 0; $day < 7; $day++) {
154
+ $currentDate = $startOfWeek->copy()->addDays($day);
155
+
156
+ $orders = Order::where('status', '!=', 'cancelled')
157
+ ->whereDate('created_at', $currentDate)
158
+ ->get();
159
+
160
+ $revenue = $orders->sum('total_amount');
161
+ $profit = $revenue * 0.30;
162
+ $ordersCount = $orders->count();
163
+ $customersCount = $orders->pluck('customer_name')->unique()->count();
164
+
165
+ $dailyData[] = [
166
+ 'date' => $currentDate->format('Y-m-d'),
167
+ 'day_name' => $currentDate->format('l'),
168
+ 'revenue' => (float) $revenue,
169
+ 'profit' => (float) $profit,
170
+ 'orders' => $ordersCount,
171
+ 'customers' => $customersCount,
172
+ ];
173
+ }
174
+
175
+ return $dailyData;
176
+ }
177
+
178
+ /**
179
+ * Get daily data for a month using real Order data
180
+ */
181
+ private function getMonthlyDailyData(Carbon $date): array
182
+ {
183
+ $daysInMonth = $date->daysInMonth;
184
+ $dailyData = [];
185
+
186
+ for ($day = 1; $day <= $daysInMonth; $day++) {
187
+ $currentDate = $date->copy()->day($day);
188
+
189
+ $orders = Order::where('status', '!=', 'cancelled')
190
+ ->whereDate('created_at', $currentDate)
191
+ ->get();
192
+
193
+ $revenue = $orders->sum('total_amount');
194
+ $profit = $revenue * 0.30;
195
+ $ordersCount = $orders->count();
196
+ $customersCount = $orders->pluck('customer_name')->unique()->count();
197
+
198
+ $dailyData[] = [
199
+ 'date' => $currentDate->format('Y-m-d'),
200
+ 'day' => $day,
201
+ 'revenue' => (float) $revenue,
202
+ 'profit' => (float) $profit,
203
+ 'orders' => $ordersCount,
204
+ 'customers' => $customersCount,
205
+ ];
206
+ }
207
+
208
+ return $dailyData;
209
+ }
210
+
211
+ /**
212
+ * Update analytics snapshots (called by scheduled job)
213
+ */
214
+ public function updateAnalyticsSnapshots(): void
215
+ {
216
+ $this->updateHourlySnapshots();
217
+ $this->updateDailySnapshots();
218
+ }
219
+
220
+ /**
221
+ * Update hourly snapshots for today
222
+ */
223
+ private function updateHourlySnapshots(): void
224
+ {
225
+ $today = Carbon::today();
226
+ $currentHour = Carbon::now()->hour;
227
+
228
+ for ($hour = 0; $hour <= $currentHour; $hour++) {
229
+ $startTime = $today->copy()->hour($hour);
230
+ $endTime = $startTime->copy()->addHour();
231
+
232
+ $revenue = RevenueTracking::whereBetween('created_at', [$startTime, $endTime])->sum('amount');
233
+ $cost = RevenueTracking::whereBetween('created_at', [$startTime, $endTime])->sum('cost');
234
+ $profit = $revenue - $cost;
235
+ $ordersCount = Order::whereBetween('created_at', [$startTime, $endTime])->count();
236
+ $customersCount = Order::whereBetween('created_at', [$startTime, $endTime])
237
+ ->distinct('user_id')->count('user_id');
238
+
239
+ AnalyticsSnapshot::updateOrCreate(
240
+ ['date' => $today->format('Y-m-d'), 'hour' => $hour],
241
+ [
242
+ 'revenue' => $revenue,
243
+ 'profit' => $profit,
244
+ 'orders_count' => $ordersCount,
245
+ 'customers_count' => $customersCount,
246
+ 'avg_order_value' => $ordersCount > 0 ? $revenue / $ordersCount : 0,
247
+ 'conversion_rate' => 0 // Will be calculated separately
248
+ ]
249
+ );
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Update daily snapshots
255
+ */
256
+ private function updateDailySnapshots(): void
257
+ {
258
+ $yesterday = Carbon::yesterday();
259
+
260
+ $revenue = RevenueTracking::whereDate('created_at', $yesterday)->sum('amount');
261
+ $cost = RevenueTracking::whereDate('created_at', $yesterday)->sum('cost');
262
+ $profit = $revenue - $cost;
263
+ $ordersCount = Order::whereDate('created_at', $yesterday)->count();
264
+ $customersCount = Order::whereDate('created_at', $yesterday)
265
+ ->distinct('user_id')->count('user_id');
266
+
267
+ AnalyticsSnapshot::updateOrCreate(
268
+ ['date' => $yesterday->format('Y-m-d'), 'hour' => null],
269
+ [
270
+ 'revenue' => $revenue,
271
+ 'profit' => $profit,
272
+ 'orders_count' => $ordersCount,
273
+ 'customers_count' => $customersCount,
274
+ 'avg_order_value' => $ordersCount > 0 ? $revenue / $ordersCount : 0,
275
+ 'conversion_rate' => 0 // Will be calculated separately
276
+ ]
277
+ );
278
+ }
279
+
280
+ /**
281
+ * Get top selling products using real Product and Order data
282
+ */
283
+ public function getTopProducts(int $limit = 10): array
284
+ {
285
+ // Get all products with their sales data
286
+ $products = Product::select('id', 'name', 'price', 'Amount')
287
+ ->get()
288
+ ->map(function ($product) {
289
+ // Calculate estimated sales based on stock reduction
290
+ // This is a simple estimation - you might want to implement proper sales tracking
291
+ $estimatedSold = max(0, 100 - $product->Amount); // Assuming initial stock was 100
292
+ $totalRevenue = $estimatedSold * $product->price;
293
+
294
+ return [
295
+ 'product_name' => $product->name,
296
+ 'total_quantity' => $estimatedSold,
297
+ 'total_revenue' => (float) $totalRevenue,
298
+ 'current_stock' => $product->Amount,
299
+ 'price' => (float) $product->price
300
+ ];
301
+ })
302
+ ->sortByDesc('total_revenue')
303
+ ->take($limit)
304
+ ->values()
305
+ ->toArray();
306
+
307
+ return $products;
308
+ }
309
+
310
+ /**
311
+ * Get customer analytics using real User and Order data
312
+ */
313
+ public function getCustomerAnalytics(): array
314
+ {
315
+ $totalCustomers = User::count();
316
+ $newCustomersToday = User::whereDate('created_at', Carbon::today())->count();
317
+
318
+ // Count unique customers who made orders today
319
+ $customersWithOrdersToday = Order::whereDate('created_at', Carbon::today())
320
+ ->distinct('customer_name')
321
+ ->count('customer_name');
322
+
323
+ // Total orders today
324
+ $ordersToday = Order::whereDate('created_at', Carbon::today())->count();
325
+
326
+ return [
327
+ 'total_customers' => $totalCustomers,
328
+ 'new_today' => $newCustomersToday,
329
+ 'active_today' => $customersWithOrdersToday,
330
+ 'orders_today' => $ordersToday,
331
+ 'new_percentage' => $totalCustomers > 0 ? ($newCustomersToday / $totalCustomers) * 100 : 0,
332
+ 'conversion_rate' => $customersWithOrdersToday > 0 ? ($ordersToday / $customersWithOrdersToday) * 100 : 0
333
+ ];
334
+ }
335
+
336
+ /**
337
+ * Get real-time order statistics
338
+ */
339
+ public function getOrderStats(): array
340
+ {
341
+ $today = Carbon::today();
342
+ $yesterday = Carbon::yesterday();
343
+
344
+ $todayOrders = Order::whereDate('created_at', $today)->count();
345
+ $yesterdayOrders = Order::whereDate('created_at', $yesterday)->count();
346
+
347
+ $percentageChange = $yesterdayOrders > 0
348
+ ? (($todayOrders - $yesterdayOrders) / $yesterdayOrders) * 100
349
+ : ($todayOrders > 0 ? 100 : 0);
350
+
351
+ return [
352
+ 'current' => $todayOrders,
353
+ 'previous' => $yesterdayOrders,
354
+ 'percentage_change' => round($percentageChange, 2),
355
+ 'trend' => $percentageChange >= 0 ? 'up' : 'down',
356
+ 'total_orders' => Order::count(),
357
+ 'pending_orders' => Order::where('status', 'pending')->count(),
358
+ 'completed_orders' => Order::where('status', 'completed')->count()
359
+ ];
360
+ }
361
+
362
+ /**
363
+ * Get real-time product statistics
364
+ */
365
+ public function getProductStats(): array
366
+ {
367
+ $totalProducts = Product::count();
368
+ $lowStockProducts = Product::where('Amount', '<', 10)->count();
369
+ $outOfStockProducts = Product::where('Amount', 0)->count();
370
+
371
+ return [
372
+ 'total_products' => $totalProducts,
373
+ 'low_stock' => $lowStockProducts,
374
+ 'out_of_stock' => $outOfStockProducts,
375
+ 'in_stock' => $totalProducts - $outOfStockProducts,
376
+ 'average_price' => (float) Product::avg('price')
377
+ ];
378
+ }
379
+ }
app/View/Components/AppLayout.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace App\View\Components;
4
+
5
+ use Illuminate\View\Component;
6
+ use Illuminate\View\View;
7
+
8
+ class AppLayout extends Component
9
+ {
10
+ /**
11
+ * Get the view / contents that represents the component.
12
+ */
13
+ public function render(): View
14
+ {
15
+ return view('layouts.app');
16
+ }
17
+ }