@reidbuilds/slop 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +11 -0
- package/.firebaserc +5 -0
- package/README.md +73 -0
- package/cli/analyzer.js +150 -0
- package/cli/index.js +182 -0
- package/cli/learner.js +156 -0
- package/cli/reporter.js +126 -0
- package/cli/scanner.js +45 -0
- package/ff-slop.md +27 -0
- package/firebase.json +16 -0
- package/functions/index.js +30 -0
- package/functions/package-lock.json +2755 -0
- package/functions/package.json +12 -0
- package/hardcoresyn-slop.md +1887 -0
- package/package.json +17 -0
- package/slop-index/catches.jsonl +590 -0
- package/slop-index/patterns/001-hallucinated-imports.md +39 -0
- package/slop-index/patterns/002-comment-restatement.md +53 -0
- package/slop-index/patterns/003-unnecessary-abstraction.md +56 -0
- package/slop-index/patterns/004-unused-imports.md +41 -0
- package/slop-index/patterns/005-hardcoded-config.md +49 -0
- package/slop-index/patterns/006-deprecated-api-confidence.md +52 -0
- package/slop-index/patterns/007-try-catch-everything.md +63 -0
- package/slop-index/patterns/008-generic-variable-names.md +49 -0
- package/slop-index/patterns/009-stub-with-shell.md +61 -0
- package/slop-index/patterns/010-async-misuse.md +64 -0
- package/slop-index/patterns/011-console-log-left-in.md +53 -0
- package/slop-index/patterns/012-over-engineered-simple.md +64 -0
- package/slop-index/patterns/013-emoji-debugging.md +44 -0
- package/slop-index/patterns/014-fake-async-simulation.md +71 -0
- package/slop-index/patterns/015-credential-fallbacks.md +51 -0
- package/slop-index/patterns/016-mock-data-pollution.md +75 -0
- package/slop-index/proposed/.gitkeep +0 -0
- package/slop-index/proposed/017-emoji-progress-logging.md +44 -0
- package/slop-index/proposed/018-test-credentials-in-fallbacks.md +54 -0
- package/slop-index/proposed/019-fake-loading-simulation.md +75 -0
- package/slop-index/proposed/020-configuration-debugging-left-in.md +53 -0
- package/slop-index/proposed/021-emoji-production-logging.md +42 -0
- package/slop-index/proposed/022-fake-delay-simulation.md +70 -0
- package/slop-index/proposed/023-credential-hardcoding-with-fallbacks.md +57 -0
- package/slop-index/proposed/024-repetitive-error-pattern.md +76 -0
- package/slop-index/proposed/025-environment-specific-fallbacks.md +55 -0
- package/slop-index/proposed/026-emoji-production-logs.md +46 -0
- package/slop-index/proposed/027-credentials-in-debug-logs.md +48 -0
- package/slop-index/proposed/028-repetitive-service-wrappers.md +59 -0
- package/slop-index/proposed/029-forced-non-null-assertions.md +59 -0
- package/slop-index/proposed/030-production-credential-fallbacks.md +51 -0
- package/slop-index/proposed/031-fake-version-confidence.md +50 -0
- package/slop-index/proposed/032-forced-non-null-assertions.md +53 -0
- package/slop-index/proposed/033-emoji-production-logs.md +44 -0
- package/slop-index/proposed/034-realistic-mock-data-leakage.md +62 -0
- package/slop-index/proposed/035-production-credential-exposure.md +43 -0
- package/slop-index/proposed/036-identical-wrapper-proliferation.md +53 -0
- package/slop-index/proposed/037-forced-null-assertions.md +50 -0
- package/slop-index/proposed/038-emoji-production-logging.md +42 -0
- package/slop-index/proposed/039-fake-delay-operations.md +52 -0
- package/slop-index/proposed/040-forced-null-assertion-chains.md +45 -0
- package/slop-index/proposed/041-production-debug-configuration.md +45 -0
- package/slop-index/proposed/042-repetitive-firebase-wrappers.md +51 -0
- package/slop-index/proposed/043-hardcoded-process-timeouts.md +48 -0
- package/slop-index/proposed/044-fictional-package-versions.md +37 -0
- package/test-sample.js +89 -0
|
@@ -0,0 +1,1887 @@
|
|
|
1
|
+
# Slop Report
|
|
2
|
+
|
|
3
|
+
Generated: 2026-05-20
|
|
4
|
+
|
|
5
|
+
## vite-env.d.ts
|
|
6
|
+
|
|
7
|
+
✓ Clean
|
|
8
|
+
|
|
9
|
+
## main.tsx
|
|
10
|
+
|
|
11
|
+
✓ Clean
|
|
12
|
+
|
|
13
|
+
## App.tsx
|
|
14
|
+
|
|
15
|
+
✓ Clean
|
|
16
|
+
|
|
17
|
+
## index.ts
|
|
18
|
+
|
|
19
|
+
### [LOW] generic-variable-names — line 75
|
|
20
|
+
|
|
21
|
+
**Code:** `odiserId: string;`
|
|
22
|
+
|
|
23
|
+
**Issue:** The field name 'odiserId' appears to be a typo or generic placeholder - should likely be 'userId' based on context and other similar fields in the codebase
|
|
24
|
+
|
|
25
|
+
**Fix:** Rename to 'userId: string;' to match the pattern used in other interfaces
|
|
26
|
+
|
|
27
|
+
## populateSweatStarterTest.ts
|
|
28
|
+
|
|
29
|
+
### [MEDIUM] console-log-left-in — line 11
|
|
30
|
+
|
|
31
|
+
**Code:** `console.log('🔍 Finding "Sweat Starter Test 1" program...');
|
|
32
|
+
console.log('✓ Found program:', (targetProgram as any).id);
|
|
33
|
+
console.log('🔍 Fetching exercises from library...');
|
|
34
|
+
console.log(`✓ Found ${exercises.length} exercises`);
|
|
35
|
+
console.log(`✓ Created ${workouts.length} workouts`);
|
|
36
|
+
console.log('✅ Successfully populated "Sweat Starter Test 1" program!');
|
|
37
|
+
console.log(` - ${totalWeeks} weeks`);
|
|
38
|
+
console.log(` - ${workoutsPerWeek} workouts per week`);
|
|
39
|
+
console.log(` - ${totalWorkouts} total workouts`);
|
|
40
|
+
console.log(` - 2 exercises per workout`);`
|
|
41
|
+
|
|
42
|
+
**Issue:** Multiple console.log statements throughout the function that appear to be debug/status logging left in production code. While this is a script, these logs clutter output and suggest AI-generated debugging scaffolding.
|
|
43
|
+
|
|
44
|
+
**Fix:** Replace with proper logging using a logger library, or remove if this is truly a one-time script. For scripts, consider using a --verbose flag to control output.
|
|
45
|
+
|
|
46
|
+
### [LOW] generic-variable-names — line 23
|
|
47
|
+
|
|
48
|
+
**Code:** `programsSnapshot.docs.forEach(doc => {
|
|
49
|
+
const data = doc.data() as any;
|
|
50
|
+
if (data.name === 'Sweat Starter Test 1') {`
|
|
51
|
+
|
|
52
|
+
**Issue:** Generic variable name 'data' used for what is specifically workout program data. This makes the code less readable and is a common AI pattern.
|
|
53
|
+
|
|
54
|
+
**Fix:** const programData = doc.data() as WorkoutProgram;
|
|
55
|
+
|
|
56
|
+
## videoThumbnail.ts
|
|
57
|
+
|
|
58
|
+
### [LOW] generic-variable-names — line 11
|
|
59
|
+
|
|
60
|
+
**Code:** `const maxW = options?.maxWidth ?? 1280;`
|
|
61
|
+
|
|
62
|
+
**Issue:** Variable abbreviated to 'maxW' instead of a clear descriptive name in non-trivial video processing context
|
|
63
|
+
|
|
64
|
+
**Fix:** const maxWidth = options?.maxWidth ?? 1280;
|
|
65
|
+
|
|
66
|
+
### [LOW] generic-variable-names — line 34
|
|
67
|
+
|
|
68
|
+
**Code:** `const dur = video.duration;`
|
|
69
|
+
|
|
70
|
+
**Issue:** Variable abbreviated to 'dur' instead of clear 'duration' in complex video timing logic
|
|
71
|
+
|
|
72
|
+
**Fix:** const duration = video.duration;
|
|
73
|
+
|
|
74
|
+
### [LOW] generic-variable-names — line 55
|
|
75
|
+
|
|
76
|
+
**Code:** `const vw = video.videoWidth;
|
|
77
|
+
const vh = video.videoHeight;`
|
|
78
|
+
|
|
79
|
+
**Issue:** Variables abbreviated to 'vw' and 'vh' instead of descriptive names in dimension calculation logic
|
|
80
|
+
|
|
81
|
+
**Fix:** const videoWidth = video.videoWidth;
|
|
82
|
+
const videoHeight = video.videoHeight;
|
|
83
|
+
|
|
84
|
+
### [LOW] generic-variable-names — line 61
|
|
85
|
+
|
|
86
|
+
**Code:** `let dw = vw;
|
|
87
|
+
let dh = vh;`
|
|
88
|
+
|
|
89
|
+
**Issue:** Variables abbreviated to 'dw' and 'dh' instead of descriptive names like 'displayWidth' and 'displayHeight'
|
|
90
|
+
|
|
91
|
+
**Fix:** let displayWidth = videoWidth;
|
|
92
|
+
let displayHeight = videoHeight;
|
|
93
|
+
|
|
94
|
+
## stripe.ts
|
|
95
|
+
|
|
96
|
+
### [HIGH] hardcoded-config — line 7
|
|
97
|
+
|
|
98
|
+
**Code:** `const fallbackKey = 'pk_test_51SrKL12EWDoWzTlbooO1c6Y3k1oMpjc2JAk266LPVK6TGgRPthyf4eeGjhFh6lDGeZEz3YxDxDUhQ4wBcnemfU1v00BKalQrEu';`
|
|
99
|
+
|
|
100
|
+
**Issue:** Stripe publishable key hardcoded directly in source code. Even though publishable keys are less sensitive than secret keys, they should still be externalized to avoid version control coupling and environment management issues.
|
|
101
|
+
|
|
102
|
+
**Fix:** Remove hardcoded fallback and require the environment variable: `const STRIPE_PUBLISHABLE_KEY = import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY?.trim();` with proper validation that throws if missing.
|
|
103
|
+
|
|
104
|
+
### [MEDIUM] console-log-left-in — line 21
|
|
105
|
+
|
|
106
|
+
**Code:** `console.log('🔑 Loading Stripe with key:', STRIPE_PUBLISHABLE_KEY.substring(0, 20) + '...');
|
|
107
|
+
console.log('🔑 Key length:', STRIPE_PUBLISHABLE_KEY.length);
|
|
108
|
+
console.log('🔑 Key starts with pk_test_:', STRIPE_PUBLISHABLE_KEY.startsWith('pk_test_'));
|
|
109
|
+
console.log('🔑 Full key (for debugging):', STRIPE_PUBLISHABLE_KEY);`
|
|
110
|
+
|
|
111
|
+
**Issue:** Debug console.log statements left in production code, including one that logs the full Stripe key. While publishable keys are less sensitive, this creates log noise and reveals implementation details in production.
|
|
112
|
+
|
|
113
|
+
**Fix:** Remove debug console.log statements or replace with proper logging: `logger.debug('Initializing Stripe', { keyType: STRIPE_PUBLISHABLE_KEY.startsWith('pk_test_') ? 'test' : 'live' })`
|
|
114
|
+
|
|
115
|
+
### [HIGH] hardcoded-config — line 45
|
|
116
|
+
|
|
117
|
+
**Code:** `'sweat-starter': import.meta.env.VITE_STRIPE_PRICE_SWEAT_STARTER || 'price_1SuKsG2EWDoWzTlbwUA9bdpb',`
|
|
118
|
+
|
|
119
|
+
**Issue:** Stripe price ID hardcoded as fallback value. Price IDs are environment-specific configuration that should not be embedded in source code.
|
|
120
|
+
|
|
121
|
+
**Fix:** Remove hardcoded fallback: `'sweat-starter': import.meta.env.VITE_STRIPE_PRICE_SWEAT_STARTER,` and validate that required price IDs are configured at startup.
|
|
122
|
+
|
|
123
|
+
## nutritionUtils.ts
|
|
124
|
+
|
|
125
|
+
### [MEDIUM] unnecessary-abstraction — line 17
|
|
126
|
+
|
|
127
|
+
**Code:** `export function macroDelta(consumed: number, target: number): {
|
|
128
|
+
pct: number;
|
|
129
|
+
over: boolean;
|
|
130
|
+
under: boolean;
|
|
131
|
+
diff: number;
|
|
132
|
+
label: string;
|
|
133
|
+
} {
|
|
134
|
+
if (target <= 0) {
|
|
135
|
+
return { pct: 0, over: false, under: false, diff: 0, label: '' };
|
|
136
|
+
}
|
|
137
|
+
const pct = Math.round((consumed / target) * 100);
|
|
138
|
+
const diff = consumed - target;
|
|
139
|
+
const over = diff > 0;
|
|
140
|
+
const under = diff < 0;
|
|
141
|
+
const abs = Math.abs(diff);
|
|
142
|
+
let label = 'On target';
|
|
143
|
+
if (over) label = `+${abs} over`;
|
|
144
|
+
else if (under) label = `${abs} under`;
|
|
145
|
+
return { pct, over, under, diff, label };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function macroDeltaGrams(consumed: number, target: number): {
|
|
149
|
+
pct: number;
|
|
150
|
+
over: boolean;
|
|
151
|
+
under: boolean;
|
|
152
|
+
diff: number;
|
|
153
|
+
label: string;
|
|
154
|
+
} {
|
|
155
|
+
if (target <= 0) {
|
|
156
|
+
return { pct: 0, over: false, under: false, diff: 0, label: '' };
|
|
157
|
+
}
|
|
158
|
+
const pct = Math.round((consumed / target) * 100);
|
|
159
|
+
const diff = Math.round((consumed - target) * 10) / 10;
|
|
160
|
+
const over = diff > 0;
|
|
161
|
+
const under = diff < 0;
|
|
162
|
+
const abs = Math.abs(diff);
|
|
163
|
+
let label = 'On target';
|
|
164
|
+
if (over) label = `+${abs}g over`;
|
|
165
|
+
else if (under) label = `${abs}g under`;
|
|
166
|
+
return { pct, over, under, diff, label };
|
|
167
|
+
}`
|
|
168
|
+
|
|
169
|
+
**Issue:** Two nearly identical functions with the same signature and return type, differing only in decimal rounding and adding 'g' to the label. The abstraction creates unnecessary code duplication instead of parameterizing the difference.
|
|
170
|
+
|
|
171
|
+
**Fix:** Combine into a single function with an optional unit parameter: function macroDelta(consumed: number, target: number, unit?: string, precision = 0) and handle rounding/labeling based on the unit parameter.
|
|
172
|
+
|
|
173
|
+
## firebase.ts
|
|
174
|
+
|
|
175
|
+
### [HIGH] hardcoded-config — line 6
|
|
176
|
+
|
|
177
|
+
**Code:** ` apiKey: import.meta.env.VITE_FIREBASE_API_KEY || 'AIzaSyB0uWItoUyaozcnjntgG9kck6GJx84GQNY',
|
|
178
|
+
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN || 'hardcore-synergy.firebaseapp.com',
|
|
179
|
+
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID || 'hardcore-synergy',
|
|
180
|
+
storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET || 'hardcore-synergy.firebasestorage.app',
|
|
181
|
+
messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID || '277466650255',
|
|
182
|
+
appId: import.meta.env.VITE_FIREBASE_APP_ID || '1:277466650255:web:59e0aeeb3f36c9fde002fa',
|
|
183
|
+
measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID || 'G-NZ20RHHZR9',`
|
|
184
|
+
|
|
185
|
+
**Issue:** Real Firebase API keys and configuration values are hardcoded as fallbacks. This exposes production credentials in source code which will leak via git history and any public repositories.
|
|
186
|
+
|
|
187
|
+
**Fix:** Remove hardcoded fallbacks and fail explicitly when environment variables are missing: apiKey: import.meta.env.VITE_FIREBASE_API_KEY || (() => { throw new Error('VITE_FIREBASE_API_KEY is required') })()
|
|
188
|
+
|
|
189
|
+
### [MEDIUM] console-log-left-in — line 15
|
|
190
|
+
|
|
191
|
+
**Code:** `console.log('🔥 Firebase Config Debug:');
|
|
192
|
+
console.log(' - API Key:', firebaseConfig.apiKey ? '✓ Set' : '✗ MISSING');
|
|
193
|
+
console.log(' - Auth Domain:', firebaseConfig.authDomain);
|
|
194
|
+
console.log(' - Project ID:', firebaseConfig.projectId);
|
|
195
|
+
console.log(' - Storage Bucket:', firebaseConfig.storageBucket);
|
|
196
|
+
console.log(' - Messaging Sender ID:', firebaseConfig.messagingSenderId ? '✓ Set' : '✗ MISSING');
|
|
197
|
+
console.log(' - App ID:', firebaseConfig.appId ? '✓ Set' : '✗ MISSING');`
|
|
198
|
+
|
|
199
|
+
**Issue:** Debug console.log statements left in production code. These will clutter production logs and potentially expose configuration details in browser console or server logs.
|
|
200
|
+
|
|
201
|
+
**Fix:** Replace with proper logging that can be filtered in production: import { logger } from './logger'; logger.debug('Firebase config status', { hasApiKey: !!firebaseConfig.apiKey, projectId: firebaseConfig.projectId })
|
|
202
|
+
|
|
203
|
+
### [MEDIUM] console-log-left-in — line 29
|
|
204
|
+
|
|
205
|
+
**Code:** `console.log('✓ Firebase app initialized:', app.name);`
|
|
206
|
+
|
|
207
|
+
**Issue:** Production console.log statement logging Firebase app initialization details.
|
|
208
|
+
|
|
209
|
+
**Fix:** Remove or replace with proper logging: logger.info('Firebase app initialized')
|
|
210
|
+
|
|
211
|
+
### [MEDIUM] console-log-left-in — line 41
|
|
212
|
+
|
|
213
|
+
**Code:** `console.log('✓ Firebase services initialized (auth, db, storage)');`
|
|
214
|
+
|
|
215
|
+
**Issue:** Production console.log statement logging service initialization details.
|
|
216
|
+
|
|
217
|
+
**Fix:** Remove or replace with proper logging: logger.info('Firebase services initialized')
|
|
218
|
+
|
|
219
|
+
## coerceFirestoreDate.ts
|
|
220
|
+
|
|
221
|
+
✓ Clean
|
|
222
|
+
|
|
223
|
+
## api.ts
|
|
224
|
+
|
|
225
|
+
### [MEDIUM] console-log-left-in — line 32
|
|
226
|
+
|
|
227
|
+
**Code:** `console.error('Failed to get auth token:', error);`
|
|
228
|
+
|
|
229
|
+
**Issue:** Console.error statement left in production code that logs authentication errors, potentially exposing sensitive information about auth failures
|
|
230
|
+
|
|
231
|
+
**Fix:** Replace with proper logging: logger.error('Failed to get auth token', { error: error.message })
|
|
232
|
+
|
|
233
|
+
### [MEDIUM] console-log-left-in — line 61
|
|
234
|
+
|
|
235
|
+
**Code:** `console.warn(`API endpoint ${endpoint} returned non-JSON response (${contentType}). This endpoint may not exist.`);`
|
|
236
|
+
|
|
237
|
+
**Issue:** Console.warn statement left in production code that logs API endpoint information and content types, creating noise in production logs
|
|
238
|
+
|
|
239
|
+
**Fix:** Replace with proper logging: logger.warn('API endpoint returned non-JSON response', { endpoint, contentType })
|
|
240
|
+
|
|
241
|
+
### [MEDIUM] console-log-left-in — line 79
|
|
242
|
+
|
|
243
|
+
**Code:** `console.error(`API call to ${endpoint} failed:`, error);`
|
|
244
|
+
|
|
245
|
+
**Issue:** Console.error statement left in production code that logs API call failures with potentially sensitive endpoint information
|
|
246
|
+
|
|
247
|
+
**Fix:** Replace with proper logging: logger.error('API call failed', { endpoint, error: error.message })
|
|
248
|
+
|
|
249
|
+
### [MEDIUM] try-catch-everything — line 45
|
|
250
|
+
|
|
251
|
+
**Code:** `try {
|
|
252
|
+
// Get auth token for authenticated requests
|
|
253
|
+
const token = await getAuthToken();
|
|
254
|
+
// ... entire apiCall function body
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.error(`API call to ${endpoint} failed:`, error);
|
|
257
|
+
return {
|
|
258
|
+
success: false,
|
|
259
|
+
error: error instanceof Error ? error.message : 'An error occurred',
|
|
260
|
+
};
|
|
261
|
+
}`
|
|
262
|
+
|
|
263
|
+
**Issue:** Blanket try-catch around entire function body with generic error handling, masking different types of errors (network, auth, parsing) that might need distinct handling
|
|
264
|
+
|
|
265
|
+
**Fix:** Handle specific error types separately - auth failures, network errors, and JSON parsing errors should be handled with different logic rather than generic catch-all
|
|
266
|
+
|
|
267
|
+
## useWorkouts.ts
|
|
268
|
+
|
|
269
|
+
### [MEDIUM] try-catch-everything — line 16
|
|
270
|
+
|
|
271
|
+
**Code:** `try {
|
|
272
|
+
const response = await getWorkoutPrograms();
|
|
273
|
+
if (response.success && response.data) {
|
|
274
|
+
setPrograms(response.data.programs);
|
|
275
|
+
} else {
|
|
276
|
+
setError(response.error || 'Failed to fetch programs');
|
|
277
|
+
}
|
|
278
|
+
} catch (err) {
|
|
279
|
+
setError(err instanceof Error ? err.message : 'Failed to fetch programs');
|
|
280
|
+
}`
|
|
281
|
+
|
|
282
|
+
**Issue:** Generic try/catch with same error handling for both API response errors and network/runtime errors. Both paths set the same generic error message.
|
|
283
|
+
|
|
284
|
+
**Fix:** Handle API response errors separately from network errors, and let authentication/authorization errors propagate to be handled at a higher level
|
|
285
|
+
|
|
286
|
+
### [MEDIUM] try-catch-everything — line 43
|
|
287
|
+
|
|
288
|
+
**Code:** `try {
|
|
289
|
+
const response = await getClientWorkouts(currentUser.uid, completed);
|
|
290
|
+
if (response.success && response.data) {
|
|
291
|
+
setWorkouts(response.data.workouts);
|
|
292
|
+
} else {
|
|
293
|
+
setError(response.error || 'Failed to fetch workouts');
|
|
294
|
+
}
|
|
295
|
+
} catch (err) {
|
|
296
|
+
setError(err instanceof Error ? err.message : 'Failed to fetch workouts');
|
|
297
|
+
}`
|
|
298
|
+
|
|
299
|
+
**Issue:** Same pattern repeated - generic try/catch that treats all errors the same way with identical error messages for different failure modes.
|
|
300
|
+
|
|
301
|
+
**Fix:** Differentiate between network errors, authentication errors, and API response errors with specific handling for each
|
|
302
|
+
|
|
303
|
+
### [MEDIUM] try-catch-everything — line 74
|
|
304
|
+
|
|
305
|
+
**Code:** `try {
|
|
306
|
+
const response = await assignWorkoutProgram(currentUser.uid, programId);
|
|
307
|
+
if (response.success) {
|
|
308
|
+
return { success: true, assignmentId: response.data?.assignmentId };
|
|
309
|
+
} else {
|
|
310
|
+
setError(response.error || 'Failed to assign program');
|
|
311
|
+
return { success: false };
|
|
312
|
+
}
|
|
313
|
+
} catch (err) {
|
|
314
|
+
setError(err instanceof Error ? err.message : 'Failed to assign program');
|
|
315
|
+
return { success: false };
|
|
316
|
+
}`
|
|
317
|
+
|
|
318
|
+
**Issue:** Third instance of the same generic error handling pattern - no differentiation between error types that might need different user-facing messages or recovery strategies.
|
|
319
|
+
|
|
320
|
+
**Fix:** Handle specific error cases like network failures, permission errors, and invalid program IDs with appropriate user feedback
|
|
321
|
+
|
|
322
|
+
### [MEDIUM] try-catch-everything — line 99
|
|
323
|
+
|
|
324
|
+
**Code:** `try {
|
|
325
|
+
const response = await logWorkout(workoutId, log);
|
|
326
|
+
if (response.success) {
|
|
327
|
+
return { success: true, logId: response.data?.logId };
|
|
328
|
+
} else {
|
|
329
|
+
setError(response.error || 'Failed to log workout');
|
|
330
|
+
return { success: false };
|
|
331
|
+
}
|
|
332
|
+
} catch (err) {
|
|
333
|
+
setError(err instanceof Error ? err.message : 'Failed to log workout');
|
|
334
|
+
return { success: false };
|
|
335
|
+
}`
|
|
336
|
+
|
|
337
|
+
**Issue:** Fourth repetition of identical error handling pattern. All four hooks use the exact same try/catch structure with generic fallback messages.
|
|
338
|
+
|
|
339
|
+
**Fix:** Create a shared error handling utility that can distinguish between different error types and provide appropriate responses
|
|
340
|
+
|
|
341
|
+
## useSweatStarterWorkouts.ts
|
|
342
|
+
|
|
343
|
+
### [MEDIUM] console-log-left-in — line 69
|
|
344
|
+
|
|
345
|
+
**Code:** `console.error('Failed to listen to program:', error);`
|
|
346
|
+
|
|
347
|
+
**Issue:** Debug console error statements left in production code that will clutter production logs with Firebase listener errors
|
|
348
|
+
|
|
349
|
+
**Fix:** Replace with proper error logging using a logger service or remove if error handling is done elsewhere
|
|
350
|
+
|
|
351
|
+
### [MEDIUM] console-log-left-in — line 80
|
|
352
|
+
|
|
353
|
+
**Code:** `console.error('Failed to listen to assignments:', error);`
|
|
354
|
+
|
|
355
|
+
**Issue:** Debug console error statements left in production code that will clutter production logs with Firebase listener errors
|
|
356
|
+
|
|
357
|
+
**Fix:** Replace with proper error logging using a logger service or remove if error handling is done elsewhere
|
|
358
|
+
|
|
359
|
+
### [MEDIUM] console-log-left-in — line 118
|
|
360
|
+
|
|
361
|
+
**Code:** `console.error('Failed to listen to progress:', error);`
|
|
362
|
+
|
|
363
|
+
**Issue:** Debug console error statements left in production code that will clutter production logs with Firebase listener errors
|
|
364
|
+
|
|
365
|
+
**Fix:** Replace with proper error logging using a logger service or remove if error handling is done elsewhere
|
|
366
|
+
|
|
367
|
+
## useProgress.ts
|
|
368
|
+
|
|
369
|
+
### [HIGH] async-misuse — line 67
|
|
370
|
+
|
|
371
|
+
**Code:** `const stats = {
|
|
372
|
+
currentWeight: latestEntry?.weight,
|
|
373
|
+
startingWeight: firstEntry?.weight,
|
|
374
|
+
weightChange: latestEntry && previousEntry
|
|
375
|
+
? latestEntry.weight! - previousEntry.weight!
|
|
376
|
+
: 0,
|
|
377
|
+
totalChange: latestEntry && firstEntry
|
|
378
|
+
? latestEntry.weight! - firstEntry.weight!
|
|
379
|
+
: 0,
|
|
380
|
+
entriesCount: entries.length,
|
|
381
|
+
averageEnergy: entries.length > 0
|
|
382
|
+
? entries.reduce((sum, e) => sum + (e.energyLevel || 0), 0) / entries.length
|
|
383
|
+
: 0,
|
|
384
|
+
};`
|
|
385
|
+
|
|
386
|
+
**Issue:** The code uses non-null assertion operator (!) on optional weight properties without checking if they exist, which could cause runtime errors if weight is undefined
|
|
387
|
+
|
|
388
|
+
**Fix:** Replace with proper null checking: latestEntry.weight - previousEntry.weight instead of latestEntry.weight! - previousEntry.weight!, or add explicit undefined checks
|
|
389
|
+
|
|
390
|
+
## useMealPlans.ts
|
|
391
|
+
|
|
392
|
+
✓ Clean
|
|
393
|
+
|
|
394
|
+
## ThemeContext.tsx
|
|
395
|
+
|
|
396
|
+
✓ Clean
|
|
397
|
+
|
|
398
|
+
## DemoDataContext.tsx
|
|
399
|
+
|
|
400
|
+
### [LOW] generic-variable-names — line 102
|
|
401
|
+
|
|
402
|
+
**Code:** `const demoExercises: WorkoutExercise[] = [`
|
|
403
|
+
|
|
404
|
+
**Issue:** Variable name 'demoExercises' uses generic 'demo' prefix when it specifically contains lower body exercises based on the content (hip thrust, deadlift, squats). This makes it unclear when 'upperExercises' is defined later.
|
|
405
|
+
|
|
406
|
+
**Fix:** Rename to 'lowerBodyExercises' or 'gluteExercises' to reflect the actual exercise types
|
|
407
|
+
|
|
408
|
+
### [MEDIUM] over-engineered-simple — line 75
|
|
409
|
+
|
|
410
|
+
**Code:** `interface DemoDataContextType {
|
|
411
|
+
demoClient: DemoClientData;
|
|
412
|
+
updateDemoClient: (updates: Partial<DemoClientData>) => void;
|
|
413
|
+
assignWorkout: (workout: Workout, scheduledDate: Date) => void;
|
|
414
|
+
assignMealPlan: (mealPlan: MealPlan) => void;
|
|
415
|
+
completeWorkout: (workoutId: string, duration: number) => void;
|
|
416
|
+
logProgress: (entry: Omit<ProgressEntry, 'id' | 'clientId' | 'date'>) => void;
|
|
417
|
+
resetDemoData: () => void;
|
|
418
|
+
}`
|
|
419
|
+
|
|
420
|
+
**Issue:** Complex context interface with multiple specialized methods for what appears to be demo/mock data. This creates unnecessary abstraction layers for demonstration purposes when simpler mock data objects would suffice.
|
|
421
|
+
|
|
422
|
+
**Fix:** Consider using plain mock data objects instead of a full context provider for demo scenarios, or simplify to a single 'updateDemoData' method
|
|
423
|
+
|
|
424
|
+
## AuthContext.tsx
|
|
425
|
+
|
|
426
|
+
### [MEDIUM] console-log-left-in — line 24
|
|
427
|
+
|
|
428
|
+
**Code:** `console.log('🔄 Initializing Firebase...');
|
|
429
|
+
// ...
|
|
430
|
+
console.log('✓ Firebase initialized successfully');
|
|
431
|
+
console.log(' - Auth:', auth ? '✓' : '✗');
|
|
432
|
+
console.log(' - Firestore:', db ? '✓' : '✗');
|
|
433
|
+
console.log(' - Google Provider:', googleProvider ? '✓' : '✗');`
|
|
434
|
+
|
|
435
|
+
**Issue:** Multiple console.log statements left in production code that will clutter production logs with implementation details about Firebase initialization status
|
|
436
|
+
|
|
437
|
+
**Fix:** Replace with proper logging using a logger library that can be filtered in production, or remove entirely if not needed for production debugging
|
|
438
|
+
|
|
439
|
+
### [MEDIUM] console-log-left-in — line 31
|
|
440
|
+
|
|
441
|
+
**Code:** `console.error('❌ Firebase initialization failed:', firebaseError);
|
|
442
|
+
console.error(' Full error:', e);
|
|
443
|
+
console.warn(' Running in demo mode. To fix:');
|
|
444
|
+
console.warn(' 1. Create .env file in project root');
|
|
445
|
+
console.warn(' 2. Add VITE_FIREBASE_API_KEY=your-api-key');
|
|
446
|
+
console.warn(' 3. Restart the dev server');`
|
|
447
|
+
|
|
448
|
+
**Issue:** Console statements providing setup instructions that should not appear in production logs, revealing internal configuration details
|
|
449
|
+
|
|
450
|
+
**Fix:** Move setup instructions to documentation and use proper error logging that can be configured per environment
|
|
451
|
+
|
|
452
|
+
### [MEDIUM] console-log-left-in — line 139
|
|
453
|
+
|
|
454
|
+
**Code:** `console.log(' 📄 Fetching profile for:', userId);`
|
|
455
|
+
|
|
456
|
+
**Issue:** Debug console.log statement logging user IDs in production, which could expose sensitive user data in logs
|
|
457
|
+
|
|
458
|
+
**Fix:** Remove or replace with proper debug logging that filters out sensitive data in production
|
|
459
|
+
|
|
460
|
+
### [MEDIUM] console-log-left-in — line 141
|
|
461
|
+
|
|
462
|
+
**Code:** `console.warn(' ⚠️ Firestore not available');`
|
|
463
|
+
|
|
464
|
+
**Issue:** Console warning left in production code that reveals internal state about database availability
|
|
465
|
+
|
|
466
|
+
**Fix:** Use proper error logging or remove if this is expected behavior in demo mode
|
|
467
|
+
|
|
468
|
+
### [MEDIUM] console-log-left-in — line 161
|
|
469
|
+
|
|
470
|
+
**Code:** `console.log(' 👑 Admin email detected, setting role to admin');`
|
|
471
|
+
|
|
472
|
+
**Issue:** Console.log revealing role assignment logic and potentially sensitive email information in production logs
|
|
473
|
+
|
|
474
|
+
**Fix:** Remove or replace with secure audit logging that doesn't expose role assignment logic in client-side logs
|
|
475
|
+
|
|
476
|
+
## Skeleton.tsx
|
|
477
|
+
|
|
478
|
+
✓ Clean
|
|
479
|
+
|
|
480
|
+
## ProtectedRoute.tsx
|
|
481
|
+
|
|
482
|
+
### [HIGH] hardcoded-config — line 57
|
|
483
|
+
|
|
484
|
+
**Code:** `const coachedTiers = ['body-blueprint', 'peach-elite'];`
|
|
485
|
+
|
|
486
|
+
**Issue:** Business tier configuration is hardcoded in component logic. Adding new tiers or changing tier names requires code changes rather than config updates.
|
|
487
|
+
|
|
488
|
+
**Fix:** Move tier configuration to environment variables or config file: const coachedTiers = process.env.COACHED_TIERS?.split(',') || ['body-blueprint', 'peach-elite'];
|
|
489
|
+
|
|
490
|
+
## ProgressBar.tsx
|
|
491
|
+
|
|
492
|
+
✓ Clean
|
|
493
|
+
|
|
494
|
+
## PaymentForm.tsx
|
|
495
|
+
|
|
496
|
+
### [HIGH] hardcoded-config — line 55
|
|
497
|
+
|
|
498
|
+
**Code:** `const FUNCTION_URL = 'https://us-central1-hardcore-synergy.cloudfunctions.net/createSubscription';`
|
|
499
|
+
|
|
500
|
+
**Issue:** Cloud function URL is hardcoded with a specific project ID and deployment region, making this code environment-specific and impossible to deploy to staging/dev without code changes
|
|
501
|
+
|
|
502
|
+
**Fix:** const FUNCTION_URL = process.env.REACT_APP_FUNCTION_URL || process.env.NEXT_PUBLIC_FUNCTION_URL;
|
|
503
|
+
|
|
504
|
+
### [MEDIUM] try-catch-everything — line 29
|
|
505
|
+
|
|
506
|
+
**Code:** `try {
|
|
507
|
+
const card = elements.getElement(CardElement);
|
|
508
|
+
// ... entire payment flow ...
|
|
509
|
+
} catch (err) {
|
|
510
|
+
const message = err instanceof Error ? err.message : 'An error occurred';
|
|
511
|
+
setError(message);
|
|
512
|
+
onError(message);
|
|
513
|
+
}`
|
|
514
|
+
|
|
515
|
+
**Issue:** Single try-catch wraps the entire payment flow including authentication, payment method creation, backend API call, and payment confirmation. Different error types need distinct handling - auth errors, network errors, and payment failures should be handled differently
|
|
516
|
+
|
|
517
|
+
**Fix:** Handle auth errors separately before try block, catch API errors distinctly from Stripe errors, and let critical errors propagate with specific error types
|
|
518
|
+
|
|
519
|
+
## PageHeader.tsx
|
|
520
|
+
|
|
521
|
+
✓ Clean
|
|
522
|
+
|
|
523
|
+
## NutritionChatWidget.tsx
|
|
524
|
+
|
|
525
|
+
### [MEDIUM] console-log-left-in — line 74
|
|
526
|
+
|
|
527
|
+
**Code:** `console.log('estimateMacrosClientSide called with:', description, '-> lowercase:', lowerDesc);`
|
|
528
|
+
|
|
529
|
+
**Issue:** Debug console.log statement left in production code that logs internal state and function parameters
|
|
530
|
+
|
|
531
|
+
**Fix:** Remove the console.log statement or replace with proper logging: logger.debug('Estimating macros', { description })
|
|
532
|
+
|
|
533
|
+
### [MEDIUM] console-log-left-in — line 62
|
|
534
|
+
|
|
535
|
+
**Code:** `console.error('Failed to load chat history:', error);`
|
|
536
|
+
|
|
537
|
+
**Issue:** Console.error statement in production code - should use proper error logging
|
|
538
|
+
|
|
539
|
+
**Fix:** Replace with structured logging: logger.error('Failed to load chat history', { error, userId: currentUser?.uid })
|
|
540
|
+
|
|
541
|
+
## LoadingSpinner.tsx
|
|
542
|
+
|
|
543
|
+
✓ Clean
|
|
544
|
+
|
|
545
|
+
## Layout.tsx
|
|
546
|
+
|
|
547
|
+
✓ Clean
|
|
548
|
+
|
|
549
|
+
## Input.tsx
|
|
550
|
+
|
|
551
|
+
✓ Clean
|
|
552
|
+
|
|
553
|
+
## ExercisePicker.tsx
|
|
554
|
+
|
|
555
|
+
### [MEDIUM] console-log-left-in — line 50
|
|
556
|
+
|
|
557
|
+
**Code:** `console.error('Failed to fetch exercises:', error);`
|
|
558
|
+
|
|
559
|
+
**Issue:** Debug console.error statement left in production code that will log full error objects to browser console, potentially exposing internal Firebase configuration or sensitive error details
|
|
560
|
+
|
|
561
|
+
**Fix:** Replace with proper error logging: import a logger and use logger.error('Failed to fetch exercises', { message: error.message }) or remove entirely if error handling is sufficient
|
|
562
|
+
|
|
563
|
+
## ExerciseCardMedia.tsx
|
|
564
|
+
|
|
565
|
+
✓ Clean
|
|
566
|
+
|
|
567
|
+
## EmptyState.tsx
|
|
568
|
+
|
|
569
|
+
✓ Clean
|
|
570
|
+
|
|
571
|
+
## Card.tsx
|
|
572
|
+
|
|
573
|
+
✓ Clean
|
|
574
|
+
|
|
575
|
+
## Button.tsx
|
|
576
|
+
|
|
577
|
+
### [HIGH] hallucinated-imports — line 2
|
|
578
|
+
|
|
579
|
+
**Code:** `import LoadingSpinner from './LoadingSpinner';`
|
|
580
|
+
|
|
581
|
+
**Issue:** Imports a component './LoadingSpinner' that is used in the code but may not exist. This is a common AI pattern of importing plausible-sounding components without verifying they exist in the project structure.
|
|
582
|
+
|
|
583
|
+
**Fix:** Verify that './LoadingSpinner' component exists, or replace with an actual loading spinner implementation or a verified component library import like 'import { Spinner } from '@/components/ui/spinner'
|
|
584
|
+
|
|
585
|
+
## BottomNav.tsx
|
|
586
|
+
|
|
587
|
+
✓ Clean
|
|
588
|
+
|
|
589
|
+
## Badge.tsx
|
|
590
|
+
|
|
591
|
+
✓ Clean
|
|
592
|
+
|
|
593
|
+
## WorkoutHistory.tsx
|
|
594
|
+
|
|
595
|
+
### [MEDIUM] console-log-left-in — line 45
|
|
596
|
+
|
|
597
|
+
**Code:** `console.error('Failed to fetch workout history:', error);`
|
|
598
|
+
|
|
599
|
+
**Issue:** Debug console.error statement left in production code that will clutter production logs and potentially leak internal error details
|
|
600
|
+
|
|
601
|
+
**Fix:** Replace with proper logging: logger.error('Failed to fetch workout history', { userId: currentUser.uid, error: error.message })
|
|
602
|
+
|
|
603
|
+
## WorkoutDetail.tsx
|
|
604
|
+
|
|
605
|
+
### [MEDIUM] console-log-left-in — line 119
|
|
606
|
+
|
|
607
|
+
**Code:** `console.error('Failed to fetch sweat starter workout:', error);`
|
|
608
|
+
|
|
609
|
+
**Issue:** Debug console statement left in production code that will clutter production logs and potentially leak implementation details about sweat starter workout fetching
|
|
610
|
+
|
|
611
|
+
**Fix:** Replace with proper logging: logger.error('Failed to fetch sweat starter workout', { error: error.message })
|
|
612
|
+
|
|
613
|
+
### [MEDIUM] console-log-left-in — line 133
|
|
614
|
+
|
|
615
|
+
**Code:** `console.error('Failed to fetch workout:', error);`
|
|
616
|
+
|
|
617
|
+
**Issue:** Debug console statement left in production code that will clutter production logs and potentially leak implementation details about workout fetching
|
|
618
|
+
|
|
619
|
+
**Fix:** Replace with proper logging: logger.error('Failed to fetch workout', { error: error.message })
|
|
620
|
+
|
|
621
|
+
## WorkoutBrowser.tsx
|
|
622
|
+
|
|
623
|
+
### [MEDIUM] console-log-left-in — line 87
|
|
624
|
+
|
|
625
|
+
**Code:** `console.error('Failed to fetch workouts:', error);`
|
|
626
|
+
|
|
627
|
+
**Issue:** Debug console statement left in production code that will clutter production logs and potentially expose internal error details
|
|
628
|
+
|
|
629
|
+
**Fix:** Replace with proper logging: logger.error('Failed to fetch workouts', { error: error.message, userId: currentUser.uid })
|
|
630
|
+
|
|
631
|
+
## TrainerWorkoutTemplates.tsx
|
|
632
|
+
|
|
633
|
+
### [HIGH] stub-with-shell — line 69
|
|
634
|
+
|
|
635
|
+
**Code:** `const fetchTemplates = async () => {
|
|
636
|
+
setLoading(true);
|
|
637
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
638
|
+
setTemplates(mockTemplates);
|
|
639
|
+
setLoading(false);
|
|
640
|
+
};`
|
|
641
|
+
|
|
642
|
+
**Issue:** Function simulates async data fetching with a hardcoded delay and mock data, but the rest of the component treats it as if it's making real API calls. This will break when integrated with a real backend.
|
|
643
|
+
|
|
644
|
+
**Fix:** Replace with actual API call or make the mock nature explicit: // TODO: Replace with actual API call - see issue #XX
|
|
645
|
+
|
|
646
|
+
## TrainerWorkoutTemplateNew.tsx
|
|
647
|
+
|
|
648
|
+
### [MEDIUM] over-engineered-simple — line 57
|
|
649
|
+
|
|
650
|
+
**Code:** `const mockTemplateData: Record<string, {
|
|
651
|
+
title: string;
|
|
652
|
+
description: string;
|
|
653
|
+
category: string;
|
|
654
|
+
focusAreas: string[];
|
|
655
|
+
difficulty: string;
|
|
656
|
+
duration: number;
|
|
657
|
+
equipment: string[];
|
|
658
|
+
warmUps: WarmUpItem[];
|
|
659
|
+
exercises: ExerciseItem[];
|
|
660
|
+
finishers: FinisherItem[];
|
|
661
|
+
}> = {`
|
|
662
|
+
|
|
663
|
+
**Issue:** Complex Record type definition for what is essentially a simple lookup object with 2 items. The verbose type annotation provides no value since TypeScript can infer the structure from the object literal.
|
|
664
|
+
|
|
665
|
+
**Fix:** const mockTemplateData = { '1': { title: 'Upper Body Strength', ... }, '2': { title: 'Lower Body Power', ... } } or extract to a separate constants file if it grows
|
|
666
|
+
|
|
667
|
+
### [HIGH] hardcoded-config — line 123
|
|
668
|
+
|
|
669
|
+
**Code:** `const [duration, setDuration] = useState(35);`
|
|
670
|
+
|
|
671
|
+
**Issue:** Default workout duration of 35 minutes is hardcoded. This configuration value should be externalized to allow different defaults per user type or configurable by admins.
|
|
672
|
+
|
|
673
|
+
**Fix:** const [duration, setDuration] = useState(config.DEFAULT_WORKOUT_DURATION || 35);
|
|
674
|
+
|
|
675
|
+
## TrainerWorkoutPrograms.tsx
|
|
676
|
+
|
|
677
|
+
### [MEDIUM] console-log-left-in — line 37
|
|
678
|
+
|
|
679
|
+
**Code:** `console.error('Failed to fetch programs:', error);`
|
|
680
|
+
|
|
681
|
+
**Issue:** Console.error statement left in production code that will clutter production logs and potentially leak implementation details
|
|
682
|
+
|
|
683
|
+
**Fix:** Replace with proper logging: logger.error('Failed to fetch programs', { error }) or remove entirely
|
|
684
|
+
|
|
685
|
+
### [MEDIUM] console-log-left-in — line 67
|
|
686
|
+
|
|
687
|
+
**Code:** `console.error('Failed to delete program:', error);`
|
|
688
|
+
|
|
689
|
+
**Issue:** Console.error statement left in production code that will clutter production logs and potentially leak implementation details
|
|
690
|
+
|
|
691
|
+
**Fix:** Replace with proper logging: logger.error('Failed to delete program', { error }) or remove entirely
|
|
692
|
+
|
|
693
|
+
## TrainerWorkoutProgramNew.tsx
|
|
694
|
+
|
|
695
|
+
### [LOW] generic-variable-names — line 122
|
|
696
|
+
|
|
697
|
+
**Code:** `for (let i = weeks.length; i < newDuration; i++) {`
|
|
698
|
+
|
|
699
|
+
**Issue:** Generic loop variable 'i' used in context where 'weekIndex' would be more descriptive
|
|
700
|
+
|
|
701
|
+
**Fix:** for (let weekIndex = weeks.length; weekIndex < newDuration; weekIndex++) {
|
|
702
|
+
|
|
703
|
+
### [LOW] generic-variable-names — line 137
|
|
704
|
+
|
|
705
|
+
**Code:** `for (let i = 0; i < newWeeks.length; i++) {`
|
|
706
|
+
|
|
707
|
+
**Issue:** Generic loop variable 'i' used in context where 'weekIndex' would be more descriptive
|
|
708
|
+
|
|
709
|
+
**Fix:** for (let weekIndex = 0; weekIndex < newWeeks.length; weekIndex++) {
|
|
710
|
+
|
|
711
|
+
### [LOW] generic-variable-names — line 138
|
|
712
|
+
|
|
713
|
+
**Code:** `if (i !== fromWeek) {`
|
|
714
|
+
|
|
715
|
+
**Issue:** Generic variable 'i' used where 'weekIndex' would be clearer
|
|
716
|
+
|
|
717
|
+
**Fix:** if (weekIndex !== fromWeek) {
|
|
718
|
+
|
|
719
|
+
### [LOW] generic-variable-names — line 139
|
|
720
|
+
|
|
721
|
+
**Code:** `newWeeks[i] = newWeeks[fromWeek].map(day => ({ ...day }));`
|
|
722
|
+
|
|
723
|
+
**Issue:** Generic variable 'i' used where 'weekIndex' would be clearer
|
|
724
|
+
|
|
725
|
+
**Fix:** newWeeks[weekIndex] = newWeeks[fromWeek].map(day => ({ ...day }));
|
|
726
|
+
|
|
727
|
+
### [LOW] generic-variable-names — line 213
|
|
728
|
+
|
|
729
|
+
**Code:** `exercises: currentWorkout.exercises.filter((_, i) => i !== index),`
|
|
730
|
+
|
|
731
|
+
**Issue:** Generic parameter 'i' in filter callback where 'exerciseIndex' would be more descriptive
|
|
732
|
+
|
|
733
|
+
**Fix:** exercises: currentWorkout.exercises.filter((_, exerciseIndex) => exerciseIndex !== index),
|
|
734
|
+
|
|
735
|
+
## TrainerWorkoutProgramDetail.tsx
|
|
736
|
+
|
|
737
|
+
### [HIGH] hardcoded-config — line 33
|
|
738
|
+
|
|
739
|
+
**Code:** `const mockPrograms: Record<string, WorkoutProgram & { weeks: { day: number; workout: DetailedWorkout | null }[][] }> = {
|
|
740
|
+
'4': {
|
|
741
|
+
id: '4',
|
|
742
|
+
name: 'Basic Muscle-Building (At Home)',
|
|
743
|
+
description: 'Build lean muscle and strength...',
|
|
744
|
+
// ... entire hardcoded workout program structure`
|
|
745
|
+
|
|
746
|
+
**Issue:** Large hardcoded workout program data structure embedded directly in component code with specific exercise names, reps, durations, and program details that should be externalized
|
|
747
|
+
|
|
748
|
+
**Fix:** Move workout program data to a separate data file, configuration, or fetch from an API/database: import { workoutPrograms } from '../data/workoutPrograms' or fetch from Firebase
|
|
749
|
+
|
|
750
|
+
### [LOW] generic-variable-names — line 33
|
|
751
|
+
|
|
752
|
+
**Code:** `const mockPrograms: Record<string, WorkoutProgram & { weeks: { day: number; workout: DetailedWorkout | null }[][] }>`
|
|
753
|
+
|
|
754
|
+
**Issue:** Variable named 'mockPrograms' uses generic 'mock' prefix when it contains specific workout program data that could have a more descriptive domain name
|
|
755
|
+
|
|
756
|
+
**Fix:** Rename to something more specific like 'workoutProgramTemplates' or 'prebuiltPrograms' to indicate what type of programs these are
|
|
757
|
+
|
|
758
|
+
## TrainerProfile.tsx
|
|
759
|
+
|
|
760
|
+
### [HIGH] hardcoded-config — line 12
|
|
761
|
+
|
|
762
|
+
**Code:** `const [phone, setPhone] = useState('555-0123');`
|
|
763
|
+
|
|
764
|
+
**Issue:** Hardcoded phone number '555-0123' is a placeholder value that should come from user profile data or be empty initially
|
|
765
|
+
|
|
766
|
+
**Fix:** const [phone, setPhone] = useState(userProfile?.phone || '');
|
|
767
|
+
|
|
768
|
+
### [HIGH] hardcoded-config — line 11
|
|
769
|
+
|
|
770
|
+
**Code:** `const [bio, setBio] = useState('Certified personal trainer with 5+ years of experience specializing in body recomposition and strength training.');`
|
|
771
|
+
|
|
772
|
+
**Issue:** Hardcoded bio text as default value should come from user profile data or be empty for user input
|
|
773
|
+
|
|
774
|
+
**Fix:** const [bio, setBio] = useState(userProfile?.bio || '');
|
|
775
|
+
|
|
776
|
+
### [HIGH] stub-with-shell — line 33
|
|
777
|
+
|
|
778
|
+
**Code:** `const handleSave = async () => {
|
|
779
|
+
setSaving(true);
|
|
780
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
781
|
+
// TODO: API call to save profile
|
|
782
|
+
setSaving(false);
|
|
783
|
+
};`
|
|
784
|
+
|
|
785
|
+
**Issue:** Function has complete error handling structure but core business logic (saving profile) is a TODO stub with fake delay
|
|
786
|
+
|
|
787
|
+
**Fix:** Either implement the actual save API call or throw an error indicating it's not implemented yet
|
|
788
|
+
|
|
789
|
+
## TrainerMessages.tsx
|
|
790
|
+
|
|
791
|
+
### [LOW] generic-variable-names — line 115
|
|
792
|
+
|
|
793
|
+
**Code:** `await new Promise((r) => setTimeout(r, 500));`
|
|
794
|
+
|
|
795
|
+
**Issue:** Promise resolver callback parameter is named 'r' instead of a descriptive name like 'resolve'
|
|
796
|
+
|
|
797
|
+
**Fix:** await new Promise((resolve) => setTimeout(resolve, 500));
|
|
798
|
+
|
|
799
|
+
### [LOW] generic-variable-names — line 152
|
|
800
|
+
|
|
801
|
+
**Code:** `const filteredConversations = conversations.filter((c) =>
|
|
802
|
+
c.clientName.toLowerCase().includes(searchQuery.toLowerCase())
|
|
803
|
+
);`
|
|
804
|
+
|
|
805
|
+
**Issue:** Loop variable named 'c' when iterating over conversations - should be more descriptive
|
|
806
|
+
|
|
807
|
+
**Fix:** const filteredConversations = conversations.filter((conversation) =>
|
|
808
|
+
conversation.clientName.toLowerCase().includes(searchQuery.toLowerCase())
|
|
809
|
+
);
|
|
810
|
+
|
|
811
|
+
### [LOW] generic-variable-names — line 156
|
|
812
|
+
|
|
813
|
+
**Code:** `const selectedClient = conversations.find((c) => c.id === selectedConversation);`
|
|
814
|
+
|
|
815
|
+
**Issue:** Loop variable named 'c' when iterating over conversations - should be more descriptive
|
|
816
|
+
|
|
817
|
+
**Fix:** const selectedClient = conversations.find((conversation) => conversation.id === selectedConversation);
|
|
818
|
+
|
|
819
|
+
## TrainerMealTemplates.tsx
|
|
820
|
+
|
|
821
|
+
### [LOW] generic-variable-names — line 28
|
|
822
|
+
|
|
823
|
+
**Code:** `const filteredPlans = mealPlans.filter((p) =>`
|
|
824
|
+
|
|
825
|
+
**Issue:** Loop variable 'p' is generic when iterating over a typed collection of meal plans. 'plan' would be more descriptive.
|
|
826
|
+
|
|
827
|
+
**Fix:** const filteredPlans = mealPlans.filter((plan) => plan.name.toLowerCase().includes(searchQuery.toLowerCase()) || plan.description.toLowerCase().includes(searchQuery.toLowerCase()));
|
|
828
|
+
|
|
829
|
+
### [LOW] generic-variable-names — line 18
|
|
830
|
+
|
|
831
|
+
**Code:** `const res = await getMealPlanTemplates();`
|
|
832
|
+
|
|
833
|
+
**Issue:** Variable 'res' is a generic name for what is specifically a meal plan templates API response.
|
|
834
|
+
|
|
835
|
+
**Fix:** const templatesResponse = await getMealPlanTemplates();
|
|
836
|
+
|
|
837
|
+
## TrainerMealPlanNew.tsx
|
|
838
|
+
|
|
839
|
+
### [HIGH] hardcoded-config — line 11
|
|
840
|
+
|
|
841
|
+
**Code:** `targetCalories: '2000',
|
|
842
|
+
targetProtein: '150',
|
|
843
|
+
targetCarbs: '200',
|
|
844
|
+
targetFat: '65',`
|
|
845
|
+
|
|
846
|
+
**Issue:** Default macro values are hardcoded in the component state. These nutritional targets should come from configuration or business logic rather than being embedded in the UI component.
|
|
847
|
+
|
|
848
|
+
**Fix:** Move default values to a config file or environment variables: const DEFAULT_MACROS = { calories: process.env.DEFAULT_CALORIES || 2000, ... }
|
|
849
|
+
|
|
850
|
+
### [HIGH] hardcoded-config — line 28
|
|
851
|
+
|
|
852
|
+
**Code:** `targetCalories: Number(form.targetCalories) || 2000,
|
|
853
|
+
targetProtein: Number(form.targetProtein) || 150,
|
|
854
|
+
targetCarbs: Number(form.targetCarbs) || 200,
|
|
855
|
+
targetFat: Number(form.targetFat) || 65,`
|
|
856
|
+
|
|
857
|
+
**Issue:** Fallback macro values are duplicated and hardcoded in the submission logic. These magic numbers should be constants or come from configuration to ensure consistency and maintainability.
|
|
858
|
+
|
|
859
|
+
**Fix:** Extract to constants: const DEFAULT_MACROS = { calories: 2000, protein: 150, carbs: 200, fat: 65 }; then use DEFAULT_MACROS.calories, etc.
|
|
860
|
+
|
|
861
|
+
## TrainerIntakes.tsx
|
|
862
|
+
|
|
863
|
+
### [HIGH] hardcoded-config — line 14
|
|
864
|
+
|
|
865
|
+
**Code:** `const mockIntakes: PendingIntake[] = [
|
|
866
|
+
{
|
|
867
|
+
id: '1',
|
|
868
|
+
clientName: 'Emma Wilson',
|
|
869
|
+
email: 'emma@example.com',
|
|
870
|
+
tier: 'body-blueprint',
|
|
871
|
+
submittedAt: new Date(Date.now() - 2 * 60 * 60 * 1000),
|
|
872
|
+
primaryGoal: 'lose-fat',
|
|
873
|
+
},
|
|
874
|
+
// ... more mock data`
|
|
875
|
+
|
|
876
|
+
**Issue:** Mock data with hardcoded emails and configuration values embedded directly in the component. This creates coupling between test data and production code.
|
|
877
|
+
|
|
878
|
+
**Fix:** Move mock data to a separate test data file or environment-based configuration: import { mockIntakes } from './testData' or fetch from process.env.NODE_ENV === 'development'
|
|
879
|
+
|
|
880
|
+
### [HIGH] stub-with-shell — line 42
|
|
881
|
+
|
|
882
|
+
**Code:** `const fetchIntakes = async () => {
|
|
883
|
+
setLoading(true);
|
|
884
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
885
|
+
setIntakes(mockIntakes);
|
|
886
|
+
setLoading(false);
|
|
887
|
+
};`
|
|
888
|
+
|
|
889
|
+
**Issue:** Function has complete async structure with loading states but core logic is just a setTimeout delay returning mock data. This will appear to work but provides no real data fetching.
|
|
890
|
+
|
|
891
|
+
**Fix:** Replace with actual API call: const response = await fetch('/api/intakes'); const data = await response.json(); setIntakes(data);
|
|
892
|
+
|
|
893
|
+
## TrainerIntakeReview.tsx
|
|
894
|
+
|
|
895
|
+
### [HIGH] stub-with-shell — line 79
|
|
896
|
+
|
|
897
|
+
**Code:** `const fetchData = async () => {
|
|
898
|
+
setLoading(true);
|
|
899
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
900
|
+
setIntakeData(mockIntakeData);
|
|
901
|
+
setLoading(false);
|
|
902
|
+
};`
|
|
903
|
+
|
|
904
|
+
**Issue:** The fetchData function has the structure of a real API call (async, loading states, error handling pattern) but only contains a setTimeout and uses mock data. This creates the illusion of working functionality while being a stub.
|
|
905
|
+
|
|
906
|
+
**Fix:** Replace with actual API call: `const response = await fetch(`/api/intakes/${id}`); setIntakeData(await response.json());` or make the stub explicit with a comment and throw for missing implementation.
|
|
907
|
+
|
|
908
|
+
### [HIGH] stub-with-shell — line 92
|
|
909
|
+
|
|
910
|
+
**Code:** `setSubmitting(true);
|
|
911
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
912
|
+
// TODO: API call to approve intake
|
|
913
|
+
navigate('/trainer/intakes');`
|
|
914
|
+
|
|
915
|
+
**Issue:** The handleApprove function has complete error handling and loading states but contains only a setTimeout and TODO comment instead of the actual approval logic. This will appear to work in testing but do nothing in production.
|
|
916
|
+
|
|
917
|
+
**Fix:** Implement the actual API call: `await fetch('/api/intakes/${id}/approve', { method: 'POST', body: JSON.stringify({ program: selectedProgram, mealPlan: selectedMealPlan }) })` or throw an error if not ready for production.
|
|
918
|
+
|
|
919
|
+
## TrainerDemoClientDetail.tsx
|
|
920
|
+
|
|
921
|
+
### [LOW] generic-variable-names — line 131
|
|
922
|
+
|
|
923
|
+
**Code:** `demoClient.assignedWorkouts.filter(w => w.id !== workoutId)`
|
|
924
|
+
|
|
925
|
+
**Issue:** Loop variable 'w' is generic when iterating over a typed collection of workouts. 'workout' would be more descriptive.
|
|
926
|
+
|
|
927
|
+
**Fix:** demoClient.assignedWorkouts.filter(workout => workout.id !== workoutId)
|
|
928
|
+
|
|
929
|
+
## TrainerDashboard.tsx
|
|
930
|
+
|
|
931
|
+
### [HIGH] stub-with-shell — line 36
|
|
932
|
+
|
|
933
|
+
**Code:** `const fetchData = async () => {
|
|
934
|
+
setLoading(true);
|
|
935
|
+
// TODO: Replace with actual API calls
|
|
936
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
937
|
+
|
|
938
|
+
setStats({
|
|
939
|
+
activeClients: 12,
|
|
940
|
+
pendingIntakes: 3,
|
|
941
|
+
pendingCheckins: 5,
|
|
942
|
+
unreadMessages: 8,
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
setRecentActivity([
|
|
946
|
+
{
|
|
947
|
+
id: '1',
|
|
948
|
+
type: 'workout',
|
|
949
|
+
clientName: 'Sarah M.',
|
|
950
|
+
description: 'Completed Lower Body Power',
|
|
951
|
+
timestamp: new Date(Date.now() - 30 * 60 * 1000),
|
|
952
|
+
},
|
|
953
|
+
// ... more hardcoded activity items
|
|
954
|
+
]);`
|
|
955
|
+
|
|
956
|
+
**Issue:** Function has complete structure and error handling but core business logic (API calls) is stubbed with hardcoded mock data and a TODO comment. The dashboard will display fake data instead of real trainer statistics.
|
|
957
|
+
|
|
958
|
+
**Fix:** Replace with actual API calls to fetch real dashboard data, or make the stub explicit by throwing an error until implemented
|
|
959
|
+
|
|
960
|
+
## TrainerClients.tsx
|
|
961
|
+
|
|
962
|
+
### [LOW] generic-variable-names — line 112
|
|
963
|
+
|
|
964
|
+
**Code:** `const res = await getTrainerClients(currentUser.uid);`
|
|
965
|
+
|
|
966
|
+
**Issue:** Variable 'res' is a generic name that doesn't communicate what type of response or data it contains
|
|
967
|
+
|
|
968
|
+
**Fix:** const clientsResponse = await getTrainerClients(currentUser.uid);
|
|
969
|
+
|
|
970
|
+
### [LOW] generic-variable-names — line 115
|
|
971
|
+
|
|
972
|
+
**Code:** `res.data.clients.map((c) => ({`
|
|
973
|
+
|
|
974
|
+
**Issue:** Parameter 'c' in the map function is generic when iterating over a typed collection of clients
|
|
975
|
+
|
|
976
|
+
**Fix:** res.data.clients.map((client) => ({
|
|
977
|
+
|
|
978
|
+
## TrainerClientDetail.tsx
|
|
979
|
+
|
|
980
|
+
### [LOW] generic-variable-names — line 166
|
|
981
|
+
|
|
982
|
+
**Code:** `const res = await getTrainerClient(currentUser.uid, id);`
|
|
983
|
+
|
|
984
|
+
**Issue:** Variable 'res' is a generic name that doesn't communicate what kind of response this is - it's specifically a trainer client fetch response
|
|
985
|
+
|
|
986
|
+
**Fix:** const clientResponse = await getTrainerClient(currentUser.uid, id);
|
|
987
|
+
|
|
988
|
+
### [LOW] generic-variable-names — line 168
|
|
989
|
+
|
|
990
|
+
**Code:** `const c = res.data;`
|
|
991
|
+
|
|
992
|
+
**Issue:** Variable 'c' is a single-letter generic name that doesn't communicate it represents client data
|
|
993
|
+
|
|
994
|
+
**Fix:** const clientData = res.data;
|
|
995
|
+
|
|
996
|
+
### [LOW] generic-variable-names — line 155
|
|
997
|
+
|
|
998
|
+
**Code:** `await new Promise((r) => setTimeout(r, 300));`
|
|
999
|
+
|
|
1000
|
+
**Issue:** Parameter 'r' is a generic name in the Promise resolve callback - while common in this pattern, 'resolve' would be clearer
|
|
1001
|
+
|
|
1002
|
+
**Fix:** await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1003
|
+
|
|
1004
|
+
## TrainerCheckins.tsx
|
|
1005
|
+
|
|
1006
|
+
### [LOW] generic-variable-names — line 130
|
|
1007
|
+
|
|
1008
|
+
**Code:** `{[...Array(5)].map((_, i) => (`
|
|
1009
|
+
|
|
1010
|
+
**Issue:** Using generic variable name 'i' for array index in loading skeleton generation where 'skeletonIndex' would be more descriptive
|
|
1011
|
+
|
|
1012
|
+
**Fix:** {[...Array(5)].map((_, skeletonIndex) => (
|
|
1013
|
+
|
|
1014
|
+
### [HIGH] hardcoded-config — line 118
|
|
1015
|
+
|
|
1016
|
+
**Code:** `await new Promise((r) => setTimeout(r, 500));`
|
|
1017
|
+
|
|
1018
|
+
**Issue:** Hardcoded 500ms delay for simulating API loading time should be configurable or removed in production
|
|
1019
|
+
|
|
1020
|
+
**Fix:** const MOCK_DELAY = process.env.NODE_ENV === 'development' ? 500 : 0; await new Promise((r) => setTimeout(r, MOCK_DELAY));
|
|
1021
|
+
|
|
1022
|
+
### [HIGH] hardcoded-config — line 132
|
|
1023
|
+
|
|
1024
|
+
**Code:** `{[...Array(5)].map((_, i) => (`
|
|
1025
|
+
|
|
1026
|
+
**Issue:** Hardcoded number of skeleton loading items (5) should match expected data size or be configurable
|
|
1027
|
+
|
|
1028
|
+
**Fix:** const SKELETON_COUNT = 5; {[...Array(SKELETON_COUNT)].map((_, i) => (
|
|
1029
|
+
|
|
1030
|
+
## TrainerCheckinReview.tsx
|
|
1031
|
+
|
|
1032
|
+
### [HIGH] stub-with-shell — line 67
|
|
1033
|
+
|
|
1034
|
+
**Code:** `// TODO: API call to submit review`
|
|
1035
|
+
|
|
1036
|
+
**Issue:** The handleSubmit function appears complete with error handling and loading states, but the core functionality (submitting the review) is just a TODO comment. This will silently fail to save review data in production.
|
|
1037
|
+
|
|
1038
|
+
**Fix:** Replace TODO with actual API call: `await api.submitCheckinReview(id, { feedback, newCalories, newProtein, newCarbs, newFat, nextWeekFocus })`
|
|
1039
|
+
|
|
1040
|
+
### [HIGH] hardcoded-config — line 22
|
|
1041
|
+
|
|
1042
|
+
**Code:** `progressPhotos: {
|
|
1043
|
+
front: 'https://placehold.co/400x600/e0e7ff/6366f1?text=Front',
|
|
1044
|
+
side: 'https://placehold.co/400x600/e0e7ff/6366f1?text=Side',
|
|
1045
|
+
}`
|
|
1046
|
+
|
|
1047
|
+
**Issue:** Hardcoded placeholder URLs in mock data that could accidentally make it to production, creating broken image links.
|
|
1048
|
+
|
|
1049
|
+
**Fix:** Move to environment config: `front: process.env.REACT_APP_PLACEHOLDER_IMAGE_BASE + '/front'` or use proper mock data service
|
|
1050
|
+
|
|
1051
|
+
## ProgressTracking.tsx
|
|
1052
|
+
|
|
1053
|
+
### [MEDIUM] console-log-left-in — line 79
|
|
1054
|
+
|
|
1055
|
+
**Code:** `console.log('Setting entries:', limitedEntries.length, 'entries for user', userId);`
|
|
1056
|
+
|
|
1057
|
+
**Issue:** Debug logging statement left in production code that logs user ID and entry count details
|
|
1058
|
+
|
|
1059
|
+
**Fix:** Remove the console.log statement or replace with proper logger: logger.debug('Progress entries loaded', { count: limitedEntries.length })
|
|
1060
|
+
|
|
1061
|
+
### [MEDIUM] console-log-left-in — line 86
|
|
1062
|
+
|
|
1063
|
+
**Code:** `console.log('Setting up real-time listener for progress entries (fetching all, filtering client-side), userId:', userId);`
|
|
1064
|
+
|
|
1065
|
+
**Issue:** Debug logging statement left in production code that logs user ID and implementation details
|
|
1066
|
+
|
|
1067
|
+
**Fix:** Remove the console.log statement or replace with proper logger: logger.debug('Setting up progress listener')
|
|
1068
|
+
|
|
1069
|
+
### [MEDIUM] console-log-left-in — line 91
|
|
1070
|
+
|
|
1071
|
+
**Code:** `console.log('Progress entries updated from Firestore:', snapshot.docs.length, 'total entries');`
|
|
1072
|
+
|
|
1073
|
+
**Issue:** Debug logging statement left in production code that logs database query details
|
|
1074
|
+
|
|
1075
|
+
**Fix:** Remove the console.log statement or replace with proper logger: logger.debug('Progress entries updated', { count: snapshot.docs.length })
|
|
1076
|
+
|
|
1077
|
+
## Measurements.tsx
|
|
1078
|
+
|
|
1079
|
+
### [MEDIUM] console-log-left-in — line 83
|
|
1080
|
+
|
|
1081
|
+
**Code:** `console.error('Failed to fetch measurements:', error);`
|
|
1082
|
+
|
|
1083
|
+
**Issue:** Debug console statement left in production code that will clutter production logs and potentially expose internal error details
|
|
1084
|
+
|
|
1085
|
+
**Fix:** Replace with proper error logging: logger.error('Failed to fetch measurements', { error: error.message })
|
|
1086
|
+
|
|
1087
|
+
### [MEDIUM] try-catch-everything — line 58
|
|
1088
|
+
|
|
1089
|
+
**Code:** `try {
|
|
1090
|
+
// Try to get latest measurements from Firestore
|
|
1091
|
+
if (db) {
|
|
1092
|
+
const entriesRef = collection(db, 'progressEntries');
|
|
1093
|
+
const q = query(
|
|
1094
|
+
entriesRef,
|
|
1095
|
+
where('clientId', '==', currentUser.uid)
|
|
1096
|
+
);
|
|
1097
|
+
|
|
1098
|
+
const snapshot = await getDocs(q);
|
|
1099
|
+
// Sort by date descending client-side
|
|
1100
|
+
const sortedDocs = snapshot.docs.sort((a, b) => {
|
|
1101
|
+
const dateA = a.data().date?.toDate() || new Date(0);
|
|
1102
|
+
const dateB = b.data().date?.toDate() || new Date(0);
|
|
1103
|
+
return dateB.getTime() - dateA.getTime();
|
|
1104
|
+
});
|
|
1105
|
+
// Find the first entry with measurements
|
|
1106
|
+
for (const docSnap of sortedDocs.slice(0, 30)) {
|
|
1107
|
+
const data = docSnap.data();
|
|
1108
|
+
const measurements = data.measurements;
|
|
1109
|
+
if (measurements && (measurements.chest || measurements.waist || measurements.hips)) {
|
|
1110
|
+
setLatestMeasurements(measurements);
|
|
1111
|
+
setFormData({
|
|
1112
|
+
chest: measurements.chest?.toString() || '',
|
|
1113
|
+
waist: measurements.waist?.toString() || '',
|
|
1114
|
+
hips: measurements.hips?.toString() || '',
|
|
1115
|
+
glutes: measurements.glutes?.toString() || '',
|
|
1116
|
+
leftBicep: measurements.leftBicep?.toString() || '',
|
|
1117
|
+
rightBicep: measurements.rightBicep?.toString() || '',
|
|
1118
|
+
leftThigh: measurements.leftThigh?.toString() || '',
|
|
1119
|
+
rightThigh: measurements.rightThigh?.toString() || '',
|
|
1120
|
+
});
|
|
1121
|
+
break;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
} catch (error) {
|
|
1126
|
+
console.error('Failed to fetch measurements:', error);
|
|
1127
|
+
}`
|
|
1128
|
+
|
|
1129
|
+
**Issue:** Blanket try/catch around entire Firestore operation with generic error handling that only logs and continues. Different Firebase errors (network, permissions, quota) should be handled differently
|
|
1130
|
+
|
|
1131
|
+
**Fix:** Handle specific Firebase errors differently - network errors might warrant retry, permission errors should redirect to auth, and only log unexpected errors
|
|
1132
|
+
|
|
1133
|
+
## ProfileSupport.tsx
|
|
1134
|
+
|
|
1135
|
+
### [HIGH] hardcoded-config — line 17
|
|
1136
|
+
|
|
1137
|
+
**Code:** `description: 'support@hardcoresynergy.com'`
|
|
1138
|
+
|
|
1139
|
+
**Issue:** Email address is hardcoded in source code, making it difficult to change for different environments or configurations
|
|
1140
|
+
|
|
1141
|
+
**Fix:** Move to environment variable: process.env.REACT_APP_SUPPORT_EMAIL || 'support@hardcoresynergy.com'
|
|
1142
|
+
|
|
1143
|
+
### [HIGH] hardcoded-config — line 18
|
|
1144
|
+
|
|
1145
|
+
**Code:** `action: () => window.location.href = 'mailto:support@hardcoresynergy.com'`
|
|
1146
|
+
|
|
1147
|
+
**Issue:** Support email address is hardcoded again, duplicating the configuration value
|
|
1148
|
+
|
|
1149
|
+
**Fix:** Use a constant or environment variable to avoid duplication
|
|
1150
|
+
|
|
1151
|
+
### [HIGH] hardcoded-config — line 68
|
|
1152
|
+
|
|
1153
|
+
**Code:** `onClick={() => window.location.href = 'mailto:support@hardcoresynergy.com'}`
|
|
1154
|
+
|
|
1155
|
+
**Issue:** Support email hardcoded a third time, creating maintenance burden when email needs to change
|
|
1156
|
+
|
|
1157
|
+
**Fix:** Extract to constant: const SUPPORT_EMAIL = process.env.REACT_APP_SUPPORT_EMAIL || 'support@hardcoresynergy.com'
|
|
1158
|
+
|
|
1159
|
+
### [HIGH] stub-with-shell — line 14
|
|
1160
|
+
|
|
1161
|
+
**Code:** `action: () => alert('Support chat coming soon!')`
|
|
1162
|
+
|
|
1163
|
+
**Issue:** Function presents as working support contact but is just a placeholder alert that provides no actual functionality
|
|
1164
|
+
|
|
1165
|
+
**Fix:** Either implement the feature or disable/hide the option until ready: disabled: true, or remove from supportOptions array
|
|
1166
|
+
|
|
1167
|
+
### [HIGH] stub-with-shell — line 23
|
|
1168
|
+
|
|
1169
|
+
**Code:** `action: () => alert('FAQs coming soon!')`
|
|
1170
|
+
|
|
1171
|
+
**Issue:** FAQs option appears functional but only shows a placeholder alert, misleading users who need help
|
|
1172
|
+
|
|
1173
|
+
**Fix:** Remove from supportOptions array until FAQ system is implemented, or navigate to existing FAQ page if available
|
|
1174
|
+
|
|
1175
|
+
## ProfileSettings.tsx
|
|
1176
|
+
|
|
1177
|
+
### [HIGH] stub-with-shell — line 25
|
|
1178
|
+
|
|
1179
|
+
**Code:** `const handleSave = async () => {
|
|
1180
|
+
setSaving(true);
|
|
1181
|
+
// TODO: Implement app settings API call (language, sound effects, haptic feedback)
|
|
1182
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
1183
|
+
setSaving(false);
|
|
1184
|
+
alert('Settings saved!');
|
|
1185
|
+
navigate('/profile');
|
|
1186
|
+
};`
|
|
1187
|
+
|
|
1188
|
+
**Issue:** Function has complete error handling, loading states, and success flow but the core business logic is a TODO comment with a fake delay. This will appear to work but won't actually save settings.
|
|
1189
|
+
|
|
1190
|
+
**Fix:** Replace with actual API call: `await saveUserSettings({ language: settings.language, soundEffects: settings.soundEffects, hapticFeedback: settings.hapticFeedback });`
|
|
1191
|
+
|
|
1192
|
+
### [LOW] comment-restatement — line 20
|
|
1193
|
+
|
|
1194
|
+
**Code:** `// Sync darkMode from theme context
|
|
1195
|
+
useEffect(() => {
|
|
1196
|
+
// Settings state is now managed by theme context
|
|
1197
|
+
}, [darkMode]);`
|
|
1198
|
+
|
|
1199
|
+
**Issue:** The comment simply restates what the useEffect dependency array already indicates - that it syncs with darkMode. The inner comment adds no meaningful information since the effect body is empty.
|
|
1200
|
+
|
|
1201
|
+
**Fix:** Remove the comments or add meaningful context about why this effect exists if it has a purpose, or remove the entire empty useEffect.
|
|
1202
|
+
|
|
1203
|
+
### [LOW] comment-restatement — line 36
|
|
1204
|
+
|
|
1205
|
+
**Code:** `const handleDarkModeToggle = (enabled: boolean) => {
|
|
1206
|
+
setDarkMode(enabled);
|
|
1207
|
+
// Save immediately without needing to click "Save Settings"
|
|
1208
|
+
};`
|
|
1209
|
+
|
|
1210
|
+
**Issue:** Comment merely describes the mechanical difference from other settings rather than explaining the business reason or user experience rationale for this behavior.
|
|
1211
|
+
|
|
1212
|
+
**Fix:** Replace with meaningful context: `// Dark mode preference is persisted immediately for better UX` or remove if the behavior is self-evident.
|
|
1213
|
+
|
|
1214
|
+
## ProfileNotifications.tsx
|
|
1215
|
+
|
|
1216
|
+
### [HIGH] stub-with-shell — line 19
|
|
1217
|
+
|
|
1218
|
+
**Code:** `const handleSave = async () => {
|
|
1219
|
+
setSaving(true);
|
|
1220
|
+
// TODO: Implement notification preferences API call
|
|
1221
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
1222
|
+
setSaving(false);
|
|
1223
|
+
alert('Notification preferences saved!');
|
|
1224
|
+
navigate('/profile');
|
|
1225
|
+
};`
|
|
1226
|
+
|
|
1227
|
+
**Issue:** Function has complete structure with loading state management and navigation flow, but the core business logic (saving preferences to backend) is a TODO comment with a fake timeout. The UI shows success feedback even though no actual save operation occurred.
|
|
1228
|
+
|
|
1229
|
+
**Fix:** Either implement the actual API call or throw an error to fail loudly: throw new Error('Save preferences not implemented - see issue #X')
|
|
1230
|
+
|
|
1231
|
+
## ProfileEdit.tsx
|
|
1232
|
+
|
|
1233
|
+
### [HIGH] stub-with-shell — line 20
|
|
1234
|
+
|
|
1235
|
+
**Code:** `const handleSave = async () => {
|
|
1236
|
+
setSaving(true);
|
|
1237
|
+
// TODO: Implement profile update API call
|
|
1238
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
1239
|
+
setSaving(false);
|
|
1240
|
+
alert('Profile updated successfully!');
|
|
1241
|
+
navigate('/profile');
|
|
1242
|
+
};`
|
|
1243
|
+
|
|
1244
|
+
**Issue:** Function has complete error handling structure and UI feedback but the core business logic (saving profile data) is just a TODO comment with a fake delay. The function claims success without actually persisting any data.
|
|
1245
|
+
|
|
1246
|
+
**Fix:** Either implement the actual API call or make the stub explicit: throw new Error('Profile update not implemented - see issue #X') so it fails loudly instead of silently lying about success.
|
|
1247
|
+
|
|
1248
|
+
## Profile.tsx
|
|
1249
|
+
|
|
1250
|
+
### [MEDIUM] console-log-left-in — line 54
|
|
1251
|
+
|
|
1252
|
+
**Code:** `console.error('Failed to sign out:', error);`
|
|
1253
|
+
|
|
1254
|
+
**Issue:** Debug console statement left in production code within error handling. While this is console.error rather than console.log, it still exposes internal error details that should use proper logging.
|
|
1255
|
+
|
|
1256
|
+
**Fix:** Replace with proper error logging: logger.error('Sign out failed', { error: error.message }) or remove if error handling is sufficient
|
|
1257
|
+
|
|
1258
|
+
## NutritionLog.tsx
|
|
1259
|
+
|
|
1260
|
+
### [MEDIUM] console-log-left-in — line 131
|
|
1261
|
+
|
|
1262
|
+
**Code:** `console.error('Failed to log nutrition:', error);`
|
|
1263
|
+
|
|
1264
|
+
**Issue:** Debug console.error statement left in production code that logs internal error details
|
|
1265
|
+
|
|
1266
|
+
**Fix:** Replace with proper error logging: logger.error('Failed to log nutrition', { error: error.message }) or remove if error is already handled by alert
|
|
1267
|
+
|
|
1268
|
+
## NutritionHistory.tsx
|
|
1269
|
+
|
|
1270
|
+
### [MEDIUM] console-log-left-in — line 82
|
|
1271
|
+
|
|
1272
|
+
**Code:** `console.error('Failed to fetch nutrition logs:', error);`
|
|
1273
|
+
|
|
1274
|
+
**Issue:** Debug console.error statement left in production code that will clutter production logs and potentially leak implementation details
|
|
1275
|
+
|
|
1276
|
+
**Fix:** Replace with proper logging: import { logger } from './logger'; logger.error('Failed to fetch nutrition logs', { error: error.message });
|
|
1277
|
+
|
|
1278
|
+
## MealPlan.tsx
|
|
1279
|
+
|
|
1280
|
+
### [MEDIUM] console-log-left-in — line 133
|
|
1281
|
+
|
|
1282
|
+
**Code:** `console.error('Failed to fetch nutrition data:', error);`
|
|
1283
|
+
|
|
1284
|
+
**Issue:** Console.error statement left in production code that will log error details to browser console, potentially exposing internal error information
|
|
1285
|
+
|
|
1286
|
+
**Fix:** Replace with proper error logging using a logger service or remove if error handling is done elsewhere
|
|
1287
|
+
|
|
1288
|
+
## MacroCalculator.tsx
|
|
1289
|
+
|
|
1290
|
+
### [MEDIUM] console-log-left-in — line 100
|
|
1291
|
+
|
|
1292
|
+
**Code:** `console.error('No results to update');`
|
|
1293
|
+
|
|
1294
|
+
**Issue:** Debug console statements left in production code that will clutter production logs
|
|
1295
|
+
|
|
1296
|
+
**Fix:** Remove console.error or replace with proper logger
|
|
1297
|
+
|
|
1298
|
+
### [MEDIUM] console-log-left-in — line 105
|
|
1299
|
+
|
|
1300
|
+
**Code:** `console.error('No user logged in');`
|
|
1301
|
+
|
|
1302
|
+
**Issue:** Debug console statements left in production code that will clutter production logs
|
|
1303
|
+
|
|
1304
|
+
**Fix:** Remove console.error or replace with proper logger
|
|
1305
|
+
|
|
1306
|
+
### [MEDIUM] console-log-left-in — line 111
|
|
1307
|
+
|
|
1308
|
+
**Code:** `console.log('Updating macros:', {
|
|
1309
|
+
targetCalories: results.targetCalories,
|
|
1310
|
+
targetProtein: results.protein,
|
|
1311
|
+
targetCarbs: results.carbs,
|
|
1312
|
+
targetFat: results.fat,
|
|
1313
|
+
});`
|
|
1314
|
+
|
|
1315
|
+
**Issue:** Debug console.log statement left in production code that logs internal application state
|
|
1316
|
+
|
|
1317
|
+
**Fix:** Remove console.log or replace with proper logger that can be filtered in production
|
|
1318
|
+
|
|
1319
|
+
### [MEDIUM] console-log-left-in — line 125
|
|
1320
|
+
|
|
1321
|
+
**Code:** `console.log('Macros updated successfully');`
|
|
1322
|
+
|
|
1323
|
+
**Issue:** Debug console.log statement left in production code that will clutter production logs
|
|
1324
|
+
|
|
1325
|
+
**Fix:** Remove console.log or replace with proper logger
|
|
1326
|
+
|
|
1327
|
+
### [MEDIUM] console-log-left-in — line 130
|
|
1328
|
+
|
|
1329
|
+
**Code:** `console.error('Failed to update macros:', error);`
|
|
1330
|
+
|
|
1331
|
+
**Issue:** Debug console statements left in production code that will clutter production logs
|
|
1332
|
+
|
|
1333
|
+
**Fix:** Remove console.error or replace with proper logger
|
|
1334
|
+
|
|
1335
|
+
## IntakeForm.tsx
|
|
1336
|
+
|
|
1337
|
+
### [MEDIUM] console-log-left-in — line 69
|
|
1338
|
+
|
|
1339
|
+
**Code:** `console.log('Draft loaded from Firestore');`
|
|
1340
|
+
|
|
1341
|
+
**Issue:** Debug console.log statement left in production code that will clutter production logs
|
|
1342
|
+
|
|
1343
|
+
**Fix:** Remove debug log or replace with proper logger: logger.debug('Draft loaded from Firestore')
|
|
1344
|
+
|
|
1345
|
+
### [MEDIUM] console-log-left-in — line 93
|
|
1346
|
+
|
|
1347
|
+
**Code:** `console.log('Draft saved to Firestore');`
|
|
1348
|
+
|
|
1349
|
+
**Issue:** Debug console.log statement left in production code that will clutter production logs
|
|
1350
|
+
|
|
1351
|
+
**Fix:** Remove debug log or replace with proper logger: logger.debug('Draft saved to Firestore')
|
|
1352
|
+
|
|
1353
|
+
### [MEDIUM] console-log-left-in — line 125
|
|
1354
|
+
|
|
1355
|
+
**Code:** `console.log('Submitting intake form...');`
|
|
1356
|
+
|
|
1357
|
+
**Issue:** Debug console.log statement left in production code that will clutter production logs
|
|
1358
|
+
|
|
1359
|
+
**Fix:** Remove debug log or replace with proper logger: logger.info('Submitting intake form')
|
|
1360
|
+
|
|
1361
|
+
### [MEDIUM] console-log-left-in — line 135
|
|
1362
|
+
|
|
1363
|
+
**Code:** `console.log('Client profile saved to Firestore');`
|
|
1364
|
+
|
|
1365
|
+
**Issue:** Debug console.log statement left in production code that will clutter production logs
|
|
1366
|
+
|
|
1367
|
+
**Fix:** Remove debug log or replace with proper logger: logger.info('Client profile saved')
|
|
1368
|
+
|
|
1369
|
+
### [MEDIUM] console-log-left-in — line 139
|
|
1370
|
+
|
|
1371
|
+
**Code:** `console.log('Intake marked as completed');`
|
|
1372
|
+
|
|
1373
|
+
**Issue:** Debug console.log statement left in production code that will clutter production logs
|
|
1374
|
+
|
|
1375
|
+
**Fix:** Remove debug log or replace with proper logger: logger.info('Intake completed')
|
|
1376
|
+
|
|
1377
|
+
## Home.tsx
|
|
1378
|
+
|
|
1379
|
+
### [MEDIUM] console-log-left-in — line 125
|
|
1380
|
+
|
|
1381
|
+
**Code:** `console.log('📹 Loaded motivational content:', content);`
|
|
1382
|
+
|
|
1383
|
+
**Issue:** Debug console.log statement left in production code that logs internal application state and data structures
|
|
1384
|
+
|
|
1385
|
+
**Fix:** Remove the console.log or replace with a proper logger: logger.debug('Loaded motivational content', { contentId: content.id })
|
|
1386
|
+
|
|
1387
|
+
### [MEDIUM] console-log-left-in — line 138
|
|
1388
|
+
|
|
1389
|
+
**Code:** `console.log('📹 Loaded video from all content (fallback):', videos[0]);`
|
|
1390
|
+
|
|
1391
|
+
**Issue:** Debug console.log statement left in production code that logs fallback data loading behavior
|
|
1392
|
+
|
|
1393
|
+
**Fix:** Remove the console.log or replace with a proper logger: logger.debug('Loaded video from fallback query', { videoId: videos[0].id })
|
|
1394
|
+
|
|
1395
|
+
### [MEDIUM] console-log-left-in — line 140
|
|
1396
|
+
|
|
1397
|
+
**Code:** `console.log('⚠️ No video content found');`
|
|
1398
|
+
|
|
1399
|
+
**Issue:** Debug console.log statement left in production code that logs application flow information
|
|
1400
|
+
|
|
1401
|
+
**Fix:** Remove the console.log or replace with a proper logger: logger.warn('No video content found')
|
|
1402
|
+
|
|
1403
|
+
### [MEDIUM] console-log-left-in — line 131
|
|
1404
|
+
|
|
1405
|
+
**Code:** `console.warn('Preferred query failed, trying fallback:', error);`
|
|
1406
|
+
|
|
1407
|
+
**Issue:** Debug console.warn statement left in production code that logs error details and fallback behavior
|
|
1408
|
+
|
|
1409
|
+
**Fix:** Remove the console.warn or replace with a proper logger: logger.warn('Preferred query failed, using fallback', { error: error.message })
|
|
1410
|
+
|
|
1411
|
+
### [MEDIUM] console-log-left-in — line 93
|
|
1412
|
+
|
|
1413
|
+
**Code:** `console.error('Failed to fetch workouts:', error);`
|
|
1414
|
+
|
|
1415
|
+
**Issue:** Debug console.error statement left in production code that logs error information
|
|
1416
|
+
|
|
1417
|
+
**Fix:** Replace with a proper logger: logger.error('Failed to fetch workouts', { error: error.message, userId: currentUser.uid })
|
|
1418
|
+
|
|
1419
|
+
## SignUp.tsx
|
|
1420
|
+
|
|
1421
|
+
✓ Clean
|
|
1422
|
+
|
|
1423
|
+
## SignIn.tsx
|
|
1424
|
+
|
|
1425
|
+
### [MEDIUM] try-catch-everything — line 24
|
|
1426
|
+
|
|
1427
|
+
**Code:** `try {
|
|
1428
|
+
await signIn(email, password);
|
|
1429
|
+
// Redirect based on subscription status
|
|
1430
|
+
if (!subscriptionTier) {
|
|
1431
|
+
navigate('/pricing');
|
|
1432
|
+
} else {
|
|
1433
|
+
navigate(from, { replace: true });
|
|
1434
|
+
}
|
|
1435
|
+
} catch (err) {
|
|
1436
|
+
setError(err instanceof Error ? err.message : 'Failed to sign in');
|
|
1437
|
+
}`
|
|
1438
|
+
|
|
1439
|
+
**Issue:** Generic catch block that handles all errors the same way, whether they're network failures, authentication failures, or validation errors. Different error types should be handled distinctly for better UX.
|
|
1440
|
+
|
|
1441
|
+
**Fix:** Handle specific error types differently - authentication failures vs network errors vs validation errors should show different messages and potentially different recovery actions.
|
|
1442
|
+
|
|
1443
|
+
### [MEDIUM] try-catch-everything — line 60
|
|
1444
|
+
|
|
1445
|
+
**Code:** `try {
|
|
1446
|
+
await signInWithGoogle();
|
|
1447
|
+
// Note: If using redirect, navigation won't happen here
|
|
1448
|
+
// The redirect result handler will navigate after successful auth
|
|
1449
|
+
if (!subscriptionTier) {
|
|
1450
|
+
navigate('/pricing');
|
|
1451
|
+
} else {
|
|
1452
|
+
navigate(from, { replace: true });
|
|
1453
|
+
}
|
|
1454
|
+
} catch (err) {
|
|
1455
|
+
setError(err instanceof Error ? err.message : 'Failed to sign in with Google');
|
|
1456
|
+
}`
|
|
1457
|
+
|
|
1458
|
+
**Issue:** Another generic catch block that treats all Google sign-in errors the same. Popup blocked, user cancelled, network errors, and OAuth errors should be handled differently.
|
|
1459
|
+
|
|
1460
|
+
**Fix:** Check error codes/types to provide specific handling - popup blocked should suggest alternative, user cancellation should not show error, OAuth errors should provide specific guidance.
|
|
1461
|
+
|
|
1462
|
+
## Pricing.tsx
|
|
1463
|
+
|
|
1464
|
+
### [MEDIUM] console-log-left-in — line 93
|
|
1465
|
+
|
|
1466
|
+
**Code:** `console.error('Failed to update subscription:', error);`
|
|
1467
|
+
|
|
1468
|
+
**Issue:** Console.error statement left in production code that will clutter production logs and expose internal implementation details
|
|
1469
|
+
|
|
1470
|
+
**Fix:** Replace with proper error logging: logger.error('Failed to update subscription', { error: error.message, userId: currentUser?.uid })
|
|
1471
|
+
|
|
1472
|
+
### [MEDIUM] console-log-left-in — line 99
|
|
1473
|
+
|
|
1474
|
+
**Code:** `console.error('Payment error:', error);`
|
|
1475
|
+
|
|
1476
|
+
**Issue:** Console.error statement left in production code in the payment error handler
|
|
1477
|
+
|
|
1478
|
+
**Fix:** Replace with proper error logging: logger.error('Payment processing failed', { error }) or remove since comment indicates error is already shown in PaymentForm component
|
|
1479
|
+
|
|
1480
|
+
## AdminWorkoutPrograms.tsx
|
|
1481
|
+
|
|
1482
|
+
### [MEDIUM] console-log-left-in — line 48
|
|
1483
|
+
|
|
1484
|
+
**Code:** `console.error('Failed to fetch programs:', error);`
|
|
1485
|
+
|
|
1486
|
+
**Issue:** Debug console statement left in production code that will clutter production logs and potentially leak internal implementation details
|
|
1487
|
+
|
|
1488
|
+
**Fix:** Replace with proper error logging: logger.error('Failed to fetch programs', { error })
|
|
1489
|
+
|
|
1490
|
+
### [MEDIUM] console-log-left-in — line 70
|
|
1491
|
+
|
|
1492
|
+
**Code:** `console.error('Failed to delete program:', error);`
|
|
1493
|
+
|
|
1494
|
+
**Issue:** Debug console statement left in production code that will clutter production logs and potentially leak internal implementation details
|
|
1495
|
+
|
|
1496
|
+
**Fix:** Replace with proper error logging: logger.error('Failed to delete program', { programId, error })
|
|
1497
|
+
|
|
1498
|
+
## AdminUsers.tsx
|
|
1499
|
+
|
|
1500
|
+
### [LOW] generic-variable-names — line 44
|
|
1501
|
+
|
|
1502
|
+
**Code:** `const res = await getAdminUsers({ limit: 500 });`
|
|
1503
|
+
|
|
1504
|
+
**Issue:** Variable 'res' is a generic name that doesn't communicate what the response contains - it's an admin users API response
|
|
1505
|
+
|
|
1506
|
+
**Fix:** const usersResponse = await getAdminUsers({ limit: 500 });
|
|
1507
|
+
|
|
1508
|
+
### [LOW] generic-variable-names — line 46
|
|
1509
|
+
|
|
1510
|
+
**Code:** `setUsers(res.data.users.map((u) => normalizeAdminUser(u as unknown as Record<string, unknown>)));`
|
|
1511
|
+
|
|
1512
|
+
**Issue:** Variable 'u' in the map function is generic when 'user' or 'rawUser' would be more descriptive for the user data being normalized
|
|
1513
|
+
|
|
1514
|
+
**Fix:** setUsers(res.data.users.map((rawUser) => normalizeAdminUser(rawUser as unknown as Record<string, unknown>)));
|
|
1515
|
+
|
|
1516
|
+
## AdminTrainers.tsx
|
|
1517
|
+
|
|
1518
|
+
### [LOW] generic-variable-names — line 88
|
|
1519
|
+
|
|
1520
|
+
**Code:** `const openEdit = (t: TrainerProfile) => {`
|
|
1521
|
+
|
|
1522
|
+
**Issue:** Parameter named 't' instead of a descriptive name like 'trainer' makes the function less readable, especially given the complex logic that follows
|
|
1523
|
+
|
|
1524
|
+
**Fix:** const openEdit = (trainer: TrainerProfile) => {
|
|
1525
|
+
|
|
1526
|
+
### [LOW] generic-variable-names — line 106
|
|
1527
|
+
|
|
1528
|
+
**Code:** `setTrainers(
|
|
1529
|
+
res.data.trainers.map((t) => mapUserDocToTrainer(t as unknown as Record<string, unknown>))
|
|
1530
|
+
);`
|
|
1531
|
+
|
|
1532
|
+
**Issue:** Loop variable 't' in map function should be named 'trainer' or 'trainerData' to clarify what's being transformed
|
|
1533
|
+
|
|
1534
|
+
**Fix:** res.data.trainers.map((trainer) => mapUserDocToTrainer(trainer as unknown as Record<string, unknown>))
|
|
1535
|
+
|
|
1536
|
+
## AdminSweatStarterWorkouts.tsx
|
|
1537
|
+
|
|
1538
|
+
### [MEDIUM] console-log-left-in — line 86
|
|
1539
|
+
|
|
1540
|
+
**Code:** `console.error('Failed to fetch data:', error);`
|
|
1541
|
+
|
|
1542
|
+
**Issue:** Debug console statement left in production code that will log error details to browser console
|
|
1543
|
+
|
|
1544
|
+
**Fix:** Replace with proper error logging: logger.error('Failed to fetch data', error) or remove if error handling is done elsewhere
|
|
1545
|
+
|
|
1546
|
+
### [MEDIUM] console-log-left-in — line 152
|
|
1547
|
+
|
|
1548
|
+
**Code:** `console.warn('Workout missing ID:', workout);`
|
|
1549
|
+
|
|
1550
|
+
**Issue:** Debug console statement left in production code that logs workout data to browser console
|
|
1551
|
+
|
|
1552
|
+
**Fix:** Replace with proper logging or remove: use a logger service or handle the missing ID case appropriately without console output
|
|
1553
|
+
|
|
1554
|
+
### [MEDIUM] console-log-left-in — line 164
|
|
1555
|
+
|
|
1556
|
+
**Code:** `console.log(` Added workout ${workout.id} (week ${weekNumber}, day ${dayIndex}) to week ${weekNumber}`);`
|
|
1557
|
+
|
|
1558
|
+
**Issue:** Debug console statement left in production code that logs detailed workout assignment information
|
|
1559
|
+
|
|
1560
|
+
**Fix:** Remove this debug logging statement or replace with proper logging service if needed for production monitoring
|
|
1561
|
+
|
|
1562
|
+
## AdminSettings.tsx
|
|
1563
|
+
|
|
1564
|
+
### [HIGH] hardcoded-config — line 12
|
|
1565
|
+
|
|
1566
|
+
**Code:** `appName: 'Hardcore Synergy',
|
|
1567
|
+
supportEmail: 'support@hardcoresynergy.com',
|
|
1568
|
+
contactInfo: '1-800-HARD-CORE',
|
|
1569
|
+
termsUrl: 'https://hardcoresynergy.com/terms',
|
|
1570
|
+
privacyUrl: 'https://hardcoresynergy.com/privacy'`
|
|
1571
|
+
|
|
1572
|
+
**Issue:** Company-specific configuration values are hardcoded directly in the React component. These values should be configurable without code changes, especially URLs and contact information that may need to be updated by administrators.
|
|
1573
|
+
|
|
1574
|
+
**Fix:** Move these values to environment variables or a configuration file: process.env.REACT_APP_NAME, process.env.REACT_APP_SUPPORT_EMAIL, etc., or load them from an API endpoint that admins can update.
|
|
1575
|
+
|
|
1576
|
+
### [HIGH] async-misuse — line 29
|
|
1577
|
+
|
|
1578
|
+
**Code:** `const handleSave = async () => {
|
|
1579
|
+
setSaving(true);
|
|
1580
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
1581
|
+
setSaving(false);
|
|
1582
|
+
};`
|
|
1583
|
+
|
|
1584
|
+
**Issue:** Function is marked async and uses await, but only to create a fake delay with setTimeout. This simulates async behavior without actually performing any real async operation like saving data to a server.
|
|
1585
|
+
|
|
1586
|
+
**Fix:** Either remove async/await and make it synchronous if no real async work is needed, or implement actual async functionality like: 'await saveSettingsToAPI(settings)'
|
|
1587
|
+
|
|
1588
|
+
### [HIGH] stub-with-shell — line 29
|
|
1589
|
+
|
|
1590
|
+
**Code:** `const handleSave = async () => {
|
|
1591
|
+
setSaving(true);
|
|
1592
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
1593
|
+
setSaving(false);
|
|
1594
|
+
};`
|
|
1595
|
+
|
|
1596
|
+
**Issue:** The save function has all the UI scaffolding (loading state, async signature) but contains only a fake delay instead of actual save logic. This will appear to work but won't persist any settings changes.
|
|
1597
|
+
|
|
1598
|
+
**Fix:** Implement actual save functionality: 'await fetch('/api/admin/settings', { method: 'PUT', body: JSON.stringify(settings) })' or throw an error if not yet implemented.
|
|
1599
|
+
|
|
1600
|
+
## AdminExercises.tsx
|
|
1601
|
+
|
|
1602
|
+
### [MEDIUM] console-log-left-in — line 69
|
|
1603
|
+
|
|
1604
|
+
**Code:** `console.log('🎭 Demo mode: Using empty exercise list');`
|
|
1605
|
+
|
|
1606
|
+
**Issue:** Debug console.log statement left in production code that logs internal application state
|
|
1607
|
+
|
|
1608
|
+
**Fix:** Remove debug log or replace with proper logging: logger.debug('Demo mode: Using empty exercise list')
|
|
1609
|
+
|
|
1610
|
+
### [MEDIUM] console-log-left-in — line 75
|
|
1611
|
+
|
|
1612
|
+
**Code:** `console.error('Firestore database not initialized');`
|
|
1613
|
+
|
|
1614
|
+
**Issue:** Console.error used for application error handling instead of proper error logging
|
|
1615
|
+
|
|
1616
|
+
**Fix:** Use proper error logging: logger.error('Firestore database not initialized')
|
|
1617
|
+
|
|
1618
|
+
### [MEDIUM] console-log-left-in — line 82
|
|
1619
|
+
|
|
1620
|
+
**Code:** `console.warn('No current user, cannot fetch exercises');`
|
|
1621
|
+
|
|
1622
|
+
**Issue:** Console.warn used for application warning instead of proper logging
|
|
1623
|
+
|
|
1624
|
+
**Fix:** Use proper logging: logger.warn('No current user, cannot fetch exercises')
|
|
1625
|
+
|
|
1626
|
+
### [MEDIUM] console-log-left-in — line 87
|
|
1627
|
+
|
|
1628
|
+
**Code:** `console.log('Fetching exercises...', { userId: currentUser.uid, email: currentUser.email });`
|
|
1629
|
+
|
|
1630
|
+
**Issue:** Debug console.log statement left in production code that logs user identification data including email
|
|
1631
|
+
|
|
1632
|
+
**Fix:** Remove debug log or use proper logging without sensitive data: logger.debug('Fetching exercises', { userId: currentUser.uid })
|
|
1633
|
+
|
|
1634
|
+
### [MEDIUM] console-log-left-in — line 93
|
|
1635
|
+
|
|
1636
|
+
**Code:** `console.log('Fetching exercises from Firestore...');`
|
|
1637
|
+
|
|
1638
|
+
**Issue:** Debug console.log statement left in production code
|
|
1639
|
+
|
|
1640
|
+
**Fix:** Remove debug log or use proper logging: logger.debug('Fetching exercises from Firestore')
|
|
1641
|
+
|
|
1642
|
+
### [MEDIUM] console-log-left-in — line 95
|
|
1643
|
+
|
|
1644
|
+
**Code:** `console.log('Query successful, got', snapshot.docs.length, 'exercises');`
|
|
1645
|
+
|
|
1646
|
+
**Issue:** Debug console.log statement left in production code
|
|
1647
|
+
|
|
1648
|
+
**Fix:** Remove debug log or use proper logging: logger.debug('Query successful', { count: snapshot.docs.length })
|
|
1649
|
+
|
|
1650
|
+
### [MEDIUM] console-log-left-in — line 103
|
|
1651
|
+
|
|
1652
|
+
**Code:** `console.log('Successfully loaded', exercisesData.length, 'exercises');`
|
|
1653
|
+
|
|
1654
|
+
**Issue:** Debug console.log statement left in production code
|
|
1655
|
+
|
|
1656
|
+
**Fix:** Remove debug log or use proper logging: logger.info('Successfully loaded exercises', { count: exercisesData.length })
|
|
1657
|
+
|
|
1658
|
+
### [MEDIUM] console-log-left-in — line 105
|
|
1659
|
+
|
|
1660
|
+
**Code:** `console.error('Failed to fetch exercises:', error);`
|
|
1661
|
+
|
|
1662
|
+
**Issue:** Console.error used for application error handling instead of proper error logging
|
|
1663
|
+
|
|
1664
|
+
**Fix:** Use proper error logging: logger.error('Failed to fetch exercises', { error })
|
|
1665
|
+
|
|
1666
|
+
### [HIGH] console-log-left-in — line 106
|
|
1667
|
+
|
|
1668
|
+
**Code:** `console.error('Error details:', {
|
|
1669
|
+
code: error?.code,
|
|
1670
|
+
message: error?.message,
|
|
1671
|
+
stack: error?.stack,
|
|
1672
|
+
userId: currentUser?.uid,
|
|
1673
|
+
email: currentUser?.email,
|
|
1674
|
+
});`
|
|
1675
|
+
|
|
1676
|
+
**Issue:** Console.error logging sensitive user data including email address and user ID in production code
|
|
1677
|
+
|
|
1678
|
+
**Fix:** Remove sensitive data from logs: logger.error('Error details', { code: error?.code, message: error?.message })
|
|
1679
|
+
|
|
1680
|
+
### [MEDIUM] console-log-left-in — line 138
|
|
1681
|
+
|
|
1682
|
+
**Code:** `console.error('Cannot edit: exercise is null or undefined');`
|
|
1683
|
+
|
|
1684
|
+
**Issue:** Console.error used for application error handling instead of proper error logging
|
|
1685
|
+
|
|
1686
|
+
**Fix:** Use proper error logging: logger.error('Cannot edit: exercise is null or undefined')
|
|
1687
|
+
|
|
1688
|
+
## AdminExerciseUpload.tsx
|
|
1689
|
+
|
|
1690
|
+
### [MEDIUM] console-log-left-in — line 150
|
|
1691
|
+
|
|
1692
|
+
**Code:** `console.warn('Auto cover from video failed:', e);`
|
|
1693
|
+
|
|
1694
|
+
**Issue:** Debug console statement left in production code that will log errors to browser console in production, potentially exposing implementation details
|
|
1695
|
+
|
|
1696
|
+
**Fix:** Replace with proper error logging: logger.warn('Auto cover generation failed', { error: e.message }) or remove entirely if non-critical
|
|
1697
|
+
|
|
1698
|
+
## AdminDashboard.tsx
|
|
1699
|
+
|
|
1700
|
+
### [LOW] generic-variable-names — line 49
|
|
1701
|
+
|
|
1702
|
+
**Code:** `const res = await getPlatformAnalytics();`
|
|
1703
|
+
|
|
1704
|
+
**Issue:** Variable 'res' is a generic name that doesn't communicate what the response contains - analytics data in this case
|
|
1705
|
+
|
|
1706
|
+
**Fix:** const analyticsResponse = await getPlatformAnalytics();
|
|
1707
|
+
|
|
1708
|
+
### [LOW] generic-variable-names — line 50
|
|
1709
|
+
|
|
1710
|
+
**Code:** `if (res.success && res.data) {`
|
|
1711
|
+
|
|
1712
|
+
**Issue:** Using generic 'res' variable makes the conditional logic less clear about what's being checked
|
|
1713
|
+
|
|
1714
|
+
**Fix:** if (analyticsResponse.success && analyticsResponse.data) {
|
|
1715
|
+
|
|
1716
|
+
### [LOW] generic-variable-names — line 51
|
|
1717
|
+
|
|
1718
|
+
**Code:** `setAnalytics(res.data);`
|
|
1719
|
+
|
|
1720
|
+
**Issue:** Generic 'res' variable continues to obscure the intent - this is setting analytics data
|
|
1721
|
+
|
|
1722
|
+
**Fix:** setAnalytics(analyticsResponse.data);
|
|
1723
|
+
|
|
1724
|
+
### [LOW] generic-variable-names — line 55
|
|
1725
|
+
|
|
1726
|
+
**Code:** `res.error ||`
|
|
1727
|
+
|
|
1728
|
+
**Issue:** Generic 'res' variable used in error handling where a more descriptive name would clarify this is an analytics API error
|
|
1729
|
+
|
|
1730
|
+
**Fix:** analyticsResponse.error ||
|
|
1731
|
+
|
|
1732
|
+
## AdminContent.tsx
|
|
1733
|
+
|
|
1734
|
+
### [MEDIUM] console-log-left-in — line 61
|
|
1735
|
+
|
|
1736
|
+
**Code:** `console.warn('OrderBy failed, fetching without order:', orderError);`
|
|
1737
|
+
|
|
1738
|
+
**Issue:** Debug console statement left in production code that exposes internal error handling logic and Firebase implementation details
|
|
1739
|
+
|
|
1740
|
+
**Fix:** Replace with proper logging: logger.warn('Firestore orderBy failed, using fallback query', { error: orderError.message })
|
|
1741
|
+
|
|
1742
|
+
### [MEDIUM] console-log-left-in — line 78
|
|
1743
|
+
|
|
1744
|
+
**Code:** `console.error('Failed to fetch content:', error);`
|
|
1745
|
+
|
|
1746
|
+
**Issue:** Debug console statement left in production code in error handler
|
|
1747
|
+
|
|
1748
|
+
**Fix:** Replace with proper logging: logger.error('Content fetch failed', { error: error.message, userId: currentUser?.uid })
|
|
1749
|
+
|
|
1750
|
+
### [MEDIUM] console-log-left-in — line 108
|
|
1751
|
+
|
|
1752
|
+
**Code:** `console.error('Failed to load content:', error);`
|
|
1753
|
+
|
|
1754
|
+
**Issue:** Debug console statement left in production code in error handler for content loading
|
|
1755
|
+
|
|
1756
|
+
**Fix:** Replace with proper logging: logger.error('Content load failed', { contentId: id, error: error.message })
|
|
1757
|
+
|
|
1758
|
+
### [MEDIUM] console-log-left-in — line 136
|
|
1759
|
+
|
|
1760
|
+
**Code:** `console.error('Upload error:', error);`
|
|
1761
|
+
|
|
1762
|
+
**Issue:** Debug console statement left in production code in file upload error handler
|
|
1763
|
+
|
|
1764
|
+
**Fix:** Replace with proper logging: logger.error('File upload failed', { error: error.message, fileName: file.name })
|
|
1765
|
+
|
|
1766
|
+
## AdminCommunity.tsx
|
|
1767
|
+
|
|
1768
|
+
### [LOW] comment-restatement — line 79
|
|
1769
|
+
|
|
1770
|
+
**Code:** `{/* Reported Posts Alert */}`
|
|
1771
|
+
|
|
1772
|
+
**Issue:** Comment simply restates what's obvious from the JSX structure - the alert component for reported posts is self-evident from the content
|
|
1773
|
+
|
|
1774
|
+
**Fix:** Remove the comment or replace with context about why this alert appears conditionally
|
|
1775
|
+
|
|
1776
|
+
### [LOW] comment-restatement — line 96
|
|
1777
|
+
|
|
1778
|
+
**Code:** `{/* Search & Filters */}`
|
|
1779
|
+
|
|
1780
|
+
**Issue:** Comment translates the JSX into English without adding any information about purpose or behavior
|
|
1781
|
+
|
|
1782
|
+
**Fix:** Remove the comment - the search input and filter select are self-documenting
|
|
1783
|
+
|
|
1784
|
+
### [LOW] comment-restatement — line 119
|
|
1785
|
+
|
|
1786
|
+
**Code:** `{/* Posts List */}`
|
|
1787
|
+
|
|
1788
|
+
**Issue:** Comment simply describes what the following JSX does without providing context about data structure or behavior
|
|
1789
|
+
|
|
1790
|
+
**Fix:** Remove the comment or explain the post filtering/rendering logic
|
|
1791
|
+
|
|
1792
|
+
### [LOW] comment-restatement — line 130
|
|
1793
|
+
|
|
1794
|
+
**Code:** `{/* Header */}`
|
|
1795
|
+
|
|
1796
|
+
**Issue:** Comment restates the obvious structure of the post header section
|
|
1797
|
+
|
|
1798
|
+
**Fix:** Remove the comment - the header structure is clear from the JSX elements
|
|
1799
|
+
|
|
1800
|
+
## AdminAnalytics.tsx
|
|
1801
|
+
|
|
1802
|
+
### [LOW] generic-variable-names — line 41
|
|
1803
|
+
|
|
1804
|
+
**Code:** `const res = await getPlatformAnalytics();
|
|
1805
|
+
if (cancelled) return;
|
|
1806
|
+
if (res.success && res.data) {`
|
|
1807
|
+
|
|
1808
|
+
**Issue:** Variable 'res' is a generic name that doesn't communicate what kind of response this is. In a function dealing with analytics data, a more descriptive name would improve readability.
|
|
1809
|
+
|
|
1810
|
+
**Fix:** const analyticsResponse = await getPlatformAnalytics();
|
|
1811
|
+
|
|
1812
|
+
### [LOW] generic-variable-names — line 57
|
|
1813
|
+
|
|
1814
|
+
**Code:** `? analytics.revenueByTier.map((r) => ({ month: r.tier, mrr: r.amount }))`
|
|
1815
|
+
|
|
1816
|
+
**Issue:** Loop variable 'r' is generic when iterating over revenue data. A more descriptive name would clarify what each item represents.
|
|
1817
|
+
|
|
1818
|
+
**Fix:** ? analytics.revenueByTier.map((revenue) => ({ month: revenue.tier, mrr: revenue.amount }))
|
|
1819
|
+
|
|
1820
|
+
### [LOW] generic-variable-names — line 64
|
|
1821
|
+
|
|
1822
|
+
**Code:** `? analytics.signupsByMonth.map((s) => ({`
|
|
1823
|
+
|
|
1824
|
+
**Issue:** Loop variable 's' is generic when iterating over signup data. A more descriptive name would improve code clarity.
|
|
1825
|
+
|
|
1826
|
+
**Fix:** ? analytics.signupsByMonth.map((signup) => ({
|
|
1827
|
+
|
|
1828
|
+
### [LOW] generic-variable-names — line 73
|
|
1829
|
+
|
|
1830
|
+
**Code:** `return analytics.activeUsersByDay.map((d) => ({`
|
|
1831
|
+
|
|
1832
|
+
**Issue:** Loop variable 'd' is generic when iterating over daily user data. A more descriptive name would clarify the data structure.
|
|
1833
|
+
|
|
1834
|
+
**Fix:** return analytics.activeUsersByDay.map((dailyData) => ({
|
|
1835
|
+
|
|
1836
|
+
### [LOW] generic-variable-names — line 85
|
|
1837
|
+
|
|
1838
|
+
**Code:** `const total = entries.reduce((s, [, v]) => s + Number(v), 0) || 1;`
|
|
1839
|
+
|
|
1840
|
+
**Issue:** Variables 's' and 'v' in the reduce function are generic. More descriptive names would clarify this is summing user counts.
|
|
1841
|
+
|
|
1842
|
+
**Fix:** const total = entries.reduce((sum, [, userCount]) => sum + Number(userCount), 0) || 1;
|
|
1843
|
+
|
|
1844
|
+
### [LOW] generic-variable-names — line 86
|
|
1845
|
+
|
|
1846
|
+
**Code:** `return entries.map(([name, v]) => ({`
|
|
1847
|
+
|
|
1848
|
+
**Issue:** Variable 'v' is generic when destructuring tier data. A more descriptive name would improve readability.
|
|
1849
|
+
|
|
1850
|
+
**Fix:** return entries.map(([name, userCount]) => ({
|
|
1851
|
+
|
|
1852
|
+
### [LOW] generic-variable-names — line 88
|
|
1853
|
+
|
|
1854
|
+
**Code:** `value: Math.round((Number(v) / total) * 100),`
|
|
1855
|
+
|
|
1856
|
+
**Issue:** Using generic variable 'v' from destructuring instead of the more descriptive name that should have been used.
|
|
1857
|
+
|
|
1858
|
+
**Fix:** value: Math.round((Number(userCount) / total) * 100),
|
|
1859
|
+
|
|
1860
|
+
## TrainerSidebar.tsx
|
|
1861
|
+
|
|
1862
|
+
✓ Clean
|
|
1863
|
+
|
|
1864
|
+
## TrainerLayout.tsx
|
|
1865
|
+
|
|
1866
|
+
✓ Clean
|
|
1867
|
+
|
|
1868
|
+
## ClientCard.tsx
|
|
1869
|
+
|
|
1870
|
+
✓ Clean
|
|
1871
|
+
|
|
1872
|
+
## AdminSidebar.tsx
|
|
1873
|
+
|
|
1874
|
+
✓ Clean
|
|
1875
|
+
|
|
1876
|
+
## AdminLayout.tsx
|
|
1877
|
+
|
|
1878
|
+
✓ Clean
|
|
1879
|
+
|
|
1880
|
+
## Summary
|
|
1881
|
+
|
|
1882
|
+
| Severity | Count |
|
|
1883
|
+
|---|---|
|
|
1884
|
+
| High | 36 |
|
|
1885
|
+
| Medium | 81 |
|
|
1886
|
+
| Low | 46 |
|
|
1887
|
+
| **Total** | **163** |
|