Files
route-commerce/docs/qa/react-doctor-baseline-2026-06-25.log
T
Nora 0ac4beaaa8 fix: react-doctor errors → 0 errors, 1649 warnings (46/100)
- Add requireAuth() to admin-permissions.ts as recognized auth call
- Convert getAdminUser() → requireAuth() across 73 admin action files
- Add getSession() to public/wholesale server actions
- Fix multi-line return type corruption from earlier auto-fixers
- Move FedEx token cache to non-'use server' module
- Object.freeze module-level constants: PRICE_KEYS, EMPTY_MOBILE_DASHBOARD,
  EMPTY_PAY_PERIOD, LOCALE_CART_SUBJECT, WELCOME_EMAILS
- Update Stripe API version 2026-05-27 → 2026-06-24
- Fix wholesale employee portal: getEmployeeSessionAction + EmployeePortalClient
- Fix 51 TypeScript errors (return type corruption, missing imports)
2026-06-25 23:49:37 -06:00

6850 lines
253 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
React Doctor v0.5.8
✔ Scanned 678 files in 23.8s [~20 workers]
✖ Bugs: Unauthenticated server action can be called directly ×95
Learn more: https://react.doctor/docs/rules/react-doctor/server-auth-actions
Anyone can call server action "logAdminAction" without
logging in, since it has no auth check.
→ Check auth before touching data because exported server
actions can be called directly by unauthenticated clients.
src/actions/admin/audit.ts:22
┌──────────────────────────────────────────────────────────────┐
│ 21 | │
│ > 22 | export async function logAdminAction(payload: AdminA… │
│ | ^ │
│ 23 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/admin/audit.ts:39
┌──────────────────────────────────────────────────────────────┐
│ 38 | │
│ > 39 | export async function logUserActivity(payload: UserA… │
│ | ^ │
│ 40 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/admin/users.ts:158
┌──────────────────────────────────────────────────────────────┐
│ 157 | │
│ > 158 | export async function getAdminUsers(brandId?: strin… │
│ | ^ │
│ 159 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/admin/users.ts:510
┌──────────────────────────────────────────────────────────────┐
│ 509 | │
│ > 510 | export async function updateAdminUser(input: Update… │
│ | ^ │
│ 511 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/admin/users.ts:546
┌──────────────────────────────────────────────────────────────┐
│ 545 | │
│ > 546 | export async function deleteAdminUser(id: string): … │
│ | ^ │
│ 547 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/admin/users.ts:556
┌──────────────────────────────────────────────────────────────┐
│ 555 | │
│ > 556 | export async function setMustChangePassword(userId:… │
│ | ^ │
│ 557 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/admin/users.ts:624
┌──────────────────────────────────────────────────────────────┐
│ 623 | │
│ > 624 | export async function getBrands(): Promise<{ brands… │
│ | ^ │
│ 625 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/auth-actions.ts:10
┌──────────────────────────────────────────────────────────────┐
│ 9 | */ │
│ > 10 | export async function signOutAction(): Promise<void>… │
│ | ^ │
│ 11 | console.log("[auth/sign-out] Signing out"); │
└──────────────────────────────────────────────────────────────┘
src/actions/auth-actions.ts:27
┌──────────────────────────────────────────────────────────────┐
│ 26 | */ │
│ > 27 | export async function signInWithGoogleAction(input: { │
│ | ^ │
│ 28 | callbackURL?: string; │
└──────────────────────────────────────────────────────────────┘
src/actions/billing/retail-checkout.ts:15
┌──────────────────────────────────────────────────────────────┐
│ 14 | │
│ > 15 | export async function createRetailStripeCheckoutSess… │
│ | ^ │
│ 16 | items: LineItem[], │
└──────────────────────────────────────────────────────────────┘
src/actions/billing/retail-payment-intent.ts:35
┌──────────────────────────────────────────────────────────────┐
│ 34 | │
│ > 35 | export async function createRetailPaymentIntent( │
│ | ^ │
│ 36 | items: LineItem[], │
└──────────────────────────────────────────────────────────────┘
src/actions/billing/stripe-checkout.ts:82
┌──────────────────────────────────────────────────────────────┐
│ 81 | │
│ > 82 | export async function createPlanUpgradeCheckout( │
│ | ^ │
│ 83 | brandId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/billing/stripe-checkout.ts:100
┌──────────────────────────────────────────────────────────────┐
│ 99 | │
│ > 100 | export async function createAddonCheckoutSession( │
│ | ^ │
│ 101 | brandId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/billing/stripe-portal.ts:100
┌──────────────────────────────────────────────────────────────┐
│ 99 | │
│ > 100 | export async function getBrandPlanInfo(brandId: str… │
│ | ^ │
│ 101 | // Replicate get_brand_plan_info via a JOIN on te… │
└──────────────────────────────────────────────────────────────┘
src/actions/billing/stripe-portal.ts:134
┌──────────────────────────────────────────────────────────────┐
│ 133 | │
│ > 134 | export async function getEnabledAddons(brandId: str… │
│ | ^ │
│ 135 | // get_brand_features returns JSONB — a single ob… │
└──────────────────────────────────────────────────────────────┘
src/actions/billing/stripe-portal.ts:149
┌──────────────────────────────────────────────────────────────┐
│ 148 | │
│ > 149 | export async function getRecentWholesaleOrders(bran… │
│ | ^ │
│ 150 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/brand-settings.ts:234
┌──────────────────────────────────────────────────────────────┐
│ 233 | │
│ > 234 | export async function getBrandSettings(brandId: str… │
│ | ^ │
│ 235 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/brand-settings.ts:255
┌──────────────────────────────────────────────────────────────┐
│ 254 | // has the same shape and never fails because of a … │
│ > 255 | export async function getBrandSettingsPublic(brandS… │
│ | ^ │
│ 256 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/checkout.ts:57
┌──────────────────────────────────────────────────────────────┐
│ 56 | │
│ > 57 | export async function createOrder( │
│ | ^ │
│ 58 | idempotencyKey: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/checkout.ts:164
┌──────────────────────────────────────────────────────────────┐
│ 163 | │
│ > 164 | export async function getServerCart(userId: string)… │
│ | ^ │
│ 165 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/checkout.ts:177
┌──────────────────────────────────────────────────────────────┐
│ 176 | │
│ > 177 | export async function mergeLocalCart( │
│ | ^ │
│ 178 | localCart: CartItem[], │
└──────────────────────────────────────────────────────────────┘
src/actions/checkout.ts:235
┌──────────────────────────────────────────────────────────────┐
│ 234 | │
│ > 235 | export async function clearServerCart(userId: strin… │
│ | ^ │
│ 236 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/checkout.ts:258
┌──────────────────────────────────────────────────────────────┐
│ 257 | │
│ > 258 | export async function getPublicStopsForBrand(brandI… │
│ | ^ │
│ 259 | const { rows } = await pool.query<PublicStop>( │
└──────────────────────────────────────────────────────────────┘
src/actions/checkout.ts:274
┌──────────────────────────────────────────────────────────────┐
│ 273 | │
│ > 274 | export async function checkStopProductAvailability( │
│ | ^ │
│ 275 | stopId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/communications/contacts.ts:551
┌──────────────────────────────────────────────────────────────┐
│ 550 | │
│ > 551 | export async function previewContactImport( │
│ | ^ │
│ 552 | csvText: string │
└──────────────────────────────────────────────────────────────┘
src/actions/communications/settings.ts:48
┌──────────────────────────────────────────────────────────────┐
│ 47 | │
│ > 48 | export async function getCommunicationSettings(brand… │
│ | ^ │
│ 49 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/email-automation/abandoned-cart.ts:180
┌──────────────────────────────────────────────────────────────┐
│ 179 | │
│ > 180 | export async function sendAbandonedCartEmail( │
│ | ^ │
│ 181 | cart: AbandonedCart, │
└──────────────────────────────────────────────────────────────┘
src/actions/email-automation/abandoned-cart.ts:246
┌──────────────────────────────────────────────────────────────┐
│ 245 | │
│ > 246 | export async function markCartRecovered(cartId: str… │
│ | ^ │
│ 247 | void cartId; │
└──────────────────────────────────────────────────────────────┘
src/actions/email-automation/welcome-sequence.ts:181
┌──────────────────────────────────────────────────────────────┐
│ 180 | │
│ > 181 | export async function sendWelcomeEmail( │
│ | ^ │
│ 182 | entry: WelcomeSequenceEntry, │
└──────────────────────────────────────────────────────────────┘
src/actions/integrations/ai-providers.ts:35
┌──────────────────────────────────────────────────────────────┐
│ 34 | │
│ > 35 | export async function getAIProviderSettings(brandId:… │
│ | ^ │
│ 36 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/integrations/ai-providers.ts:98
┌──────────────────────────────────────────────────────────────┐
│ 97 | │
│ > 98 | export async function getAIClient(brandId: string): … │
│ | ^ │
│ 99 | provider: AIProvider; │
└──────────────────────────────────────────────────────────────┘
src/actions/integrations/ai-providers.ts:195
┌──────────────────────────────────────────────────────────────┐
│ 194 | │
│ > 195 | export async function getCustomIntegrations(brandId… │
│ | ^ │
│ 196 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/integrations/credentials.ts:26
┌──────────────────────────────────────────────────────────────┐
│ 25 | │
│ > 26 | export async function getResendCredentials(brandId: … │
│ | ^ │
│ 27 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/integrations/credentials.ts:87
┌──────────────────────────────────────────────────────────────┐
│ 86 | │
│ > 87 | export async function getTwilioCredentials(brandId: … │
│ | ^ │
│ 88 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/integrations/credentials.ts:148
┌──────────────────────────────────────────────────────────────┐
│ 147 | │
│ > 148 | export async function testResendConnection(apiKey: … │
│ | ^ │
│ 149 | ok: boolean; │
└──────────────────────────────────────────────────────────────┘
src/actions/integrations/credentials.ts:176
┌──────────────────────────────────────────────────────────────┐
│ 175 | │
│ > 176 | export async function testTwilioConnection( │
│ | ^ │
│ 177 | accountSid: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/locations.ts:224
┌──────────────────────────────────────────────────────────────┐
│ 223 | │
│ > 224 | export async function getPublicLocationsForBrand( │
│ | ^ │
│ 225 | brandSlug: string │
└──────────────────────────────────────────────────────────────┘
src/actions/offline-dispatcher.ts:48
┌──────────────────────────────────────────────────────────────┐
│ 47 | │
│ > 48 | export async function listClientActions(): Promise<C… │
│ | ^ │
│ 49 | // Wire real server actions here as they land: │
└──────────────────────────────────────────────────────────────┘
src/actions/offline-dispatcher.ts:57
┌──────────────────────────────────────────────────────────────┐
│ 56 | │
│ > 57 | export async function dispatchClientAction( │
│ | ^ │
│ 58 | actionName: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/payments.ts:28
┌──────────────────────────────────────────────────────────────┐
│ 27 | │
│ > 28 | export async function getPaymentSettings(brandId: st… │
│ | ^ │
│ 29 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/route-trace/lots.ts:345
┌──────────────────────────────────────────────────────────────┐
│ 344 | */ │
│ > 345 | export async function getLotIdByNumber(_lotNumber: … │
│ | ^ │
│ 346 | return null; │
└──────────────────────────────────────────────────────────────┘
src/actions/stops.ts:153
┌──────────────────────────────────────────────────────────────┐
│ 152 | │
│ > 153 | export async function getActiveStopsForSitemap(): P… │
│ | ^ │
│ 154 | // Wrapped in try/catch so a build-time outage (E… │
└──────────────────────────────────────────────────────────────┘
src/actions/stops.ts:186
┌──────────────────────────────────────────────────────────────┐
│ 185 | │
│ > 186 | export async function getPublicStopsForBrand( │
│ | ^ │
│ 187 | brandSlug: string │
└──────────────────────────────────────────────────────────────┘
src/actions/storefront.ts:60
┌──────────────────────────────────────────────────────────────┐
│ 59 | */ │
│ > 60 | export async function getStorefrontBrandBySlug( │
│ | ^ │
│ 61 | slug: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/storefront.ts:77
┌──────────────────────────────────────────────────────────────┐
│ 76 | */ │
│ > 77 | export async function getStorefrontStops(brandId: st… │
│ | ^ │
│ 78 | const { rows } = await pool.query<StorefrontStop>( │
└──────────────────────────────────────────────────────────────┘
src/actions/storefront.ts:101
┌──────────────────────────────────────────────────────────────┐
│ 100 | */ │
│ > 101 | export async function getStorefrontProducts( │
│ | ^ │
│ 102 | brandId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/storefront.ts:126
┌──────────────────────────────────────────────────────────────┐
│ 125 | */ │
│ > 126 | export async function getStorefrontData(slug: strin… │
│ | ^ │
│ 127 | const brand = await getStorefrontBrandBySlug(slug… │
└──────────────────────────────────────────────────────────────┘
src/actions/storefront.ts:149
┌──────────────────────────────────────────────────────────────┐
│ 148 | */ │
│ > 149 | export async function getStorefrontWholesaleSetting… │
│ | ^ │
│ 150 | slug: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/storefront.ts:175
┌──────────────────────────────────────────────────────────────┐
│ 174 | */ │
│ > 175 | export async function getWholesalePortalConfig( │
│ | ^ │
│ 176 | brandId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/storefront.ts:195
┌──────────────────────────────────────────────────────────────┐
│ 194 | */ │
│ > 195 | export async function getStorefrontBrandName( │
│ | ^ │
│ 196 | brandId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/storefront.ts:216
┌──────────────────────────────────────────────────────────────┐
│ 215 | */ │
│ > 216 | export async function getStorefrontStopById(id: str… │
│ | ^ │
│ 217 | stop: StorefrontStop; │
└──────────────────────────────────────────────────────────────┘
src/actions/stripe-connect.ts:173
┌──────────────────────────────────────────────────────────────┐
│ 172 | */ │
│ > 173 | export async function saveStripeConnectAccount(bran… │
│ | ^ │
│ 174 | success: boolean; │
└──────────────────────────────────────────────────────────────┘
src/actions/tax.ts:27
┌──────────────────────────────────────────────────────────────┐
│ 26 | │
│ > 27 | export async function calculateOrderTax(params: { │
│ | ^ │
│ 28 | brandId: string; │
└──────────────────────────────────────────────────────────────┘
src/actions/tax.ts:196
┌──────────────────────────────────────────────────────────────┐
│ 195 | │
│ > 196 | export async function getTaxSummaryAction(params: { │
│ | ^ │
│ 197 | brandId: string; │
└──────────────────────────────────────────────────────────────┘
src/actions/tax.ts:231
┌──────────────────────────────────────────────────────────────┐
│ 230 | │
│ > 231 | export async function getTaxableOrdersAction(params… │
│ | ^ │
│ 232 | brandId: string; │
└──────────────────────────────────────────────────────────────┘
src/actions/time-tracking/field.ts:55
┌──────────────────────────────────────────────────────────────┐
│ 54 | │
│ > 55 | export async function verifyTimeTrackingPin( │
│ | ^ │
│ 56 | _brandId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/time-tracking/field.ts:107
┌──────────────────────────────────────────────────────────────┐
│ 106 | │
│ > 107 | export async function logoutTimeTracking(): Promise… │
│ | ^ │
│ 108 | const cookieStore = await cookies(); │
└──────────────────────────────────────────────────────────────┘
src/actions/time-tracking/field.ts:114
┌──────────────────────────────────────────────────────────────┐
│ 113 | │
│ > 114 | export async function getTimeTrackingSession(): Pro… │
│ | ^ │
│ 115 | const cookieStore = await cookies(); │
└──────────────────────────────────────────────────────────────┘
src/actions/time-tracking/field.ts:132
┌──────────────────────────────────────────────────────────────┐
│ 131 | │
│ > 132 | export async function getTimeTrackingTasksField( │
│ | ^ │
│ 133 | _brandId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/time-tracking/index.ts:66
┌──────────────────────────────────────────────────────────────┐
│ 65 | │
│ > 66 | export async function getTimeTrackingWorkers(_brandI… │
│ | ^ │
│ 67 | // Time tracking tables not in SaaS rebuild — retu… │
└──────────────────────────────────────────────────────────────┘
src/actions/time-tracking/index.ts:108
┌──────────────────────────────────────────────────────────────┐
│ 107 | │
│ > 108 | export async function getTimeTrackingTasks(_brandId… │
│ | ^ │
│ 109 | return []; │
└──────────────────────────────────────────────────────────────┘
src/actions/time-tracking/index.ts:145
┌──────────────────────────────────────────────────────────────┐
│ 144 | │
│ > 145 | export async function getWorkerTimeLogs( │
│ | ^ │
│ 146 | _brandId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/time-tracking/index.ts:178
┌──────────────────────────────────────────────────────────────┐
│ 177 | │
│ > 178 | export async function getTimeTrackingSummary( │
│ | ^ │
│ 179 | _brandId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/time-tracking/index.ts:207
┌──────────────────────────────────────────────────────────────┐
│ 206 | │
│ > 207 | export async function getTimeTrackingSettings(_bran… │
│ | ^ │
│ 208 | // Real RPC not in SaaS rebuild. │
└──────────────────────────────────────────────────────────────┘
src/actions/time-tracking/index.ts:253
┌──────────────────────────────────────────────────────────────┐
│ 252 | │
│ > 253 | export async function getTimeTrackingNotificationLo… │
│ | ^ │
│ 254 | _brandId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/water-log/admin.ts:127
┌──────────────────────────────────────────────────────────────┐
│ 126 | */ │
│ > 127 | export async function requireWaterAdminSession(): P… │
│ | ^ │
│ 128 | | { ok: true; adminUserId: string; brandId: strin… │
└──────────────────────────────────────────────────────────────┘
src/actions/water-log/admin.ts:482
┌──────────────────────────────────────────────────────────────┐
│ 481 | │
│ > 482 | export async function createWaterIrrigator( │
│ | ^ │
│ 483 | brandId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/water-log/field.ts:68
┌──────────────────────────────────────────────────────────────┐
│ 67 | │
│ > 68 | export async function getWaterHeadgates( │
│ | ^ │
│ 69 | _brandId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/water-log/field.ts:98
┌──────────────────────────────────────────────────────────────┐
│ 97 | │
│ > 98 | export async function verifyWaterPin( │
│ | ^ │
│ 99 | _brandId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/water-log/field.ts:281
┌──────────────────────────────────────────────────────────────┐
│ 280 | │
│ > 281 | export async function requireFieldSession(): Promis… │
│ | ^ │
│ 282 | const cookieStore = await cookies(); │
└──────────────────────────────────────────────────────────────┘
src/actions/water-log/field.ts:342
┌──────────────────────────────────────────────────────────────┐
│ 341 | │
│ > 342 | export async function logoutWater(): Promise<void> { │
│ | ^ │
│ 343 | const cookieStore = await cookies(); │
└──────────────────────────────────────────────────────────────┘
src/actions/water-log/field.ts:358
┌──────────────────────────────────────────────────────────────┐
│ 357 | │
│ > 358 | export async function logoutWaterAdmin(): Promise<v… │
│ | ^ │
│ 359 | const cookieStore = await cookies(); │
└──────────────────────────────────────────────────────────────┘
src/actions/water-log/field.ts:375
┌──────────────────────────────────────────────────────────────┐
│ 374 | │
│ > 375 | export async function getWaterSession(): Promise<st… │
│ | ^ │
│ 376 | const cookieStore = await cookies(); │
└──────────────────────────────────────────────────────────────┘
src/actions/water-log/field.ts:389
┌──────────────────────────────────────────────────────────────┐
│ 388 | */ │
│ > 389 | export async function getWaterAdminSession(): Promi… │
│ | ^ │
│ 390 | sessionId: string; │
└──────────────────────────────────────────────────────────────┘
src/actions/water-log/field.ts:421
┌──────────────────────────────────────────────────────────────┐
│ 420 | │
│ > 421 | export async function setWaterLang(lang: string): P… │
│ | ^ │
│ 422 | if (!["en", "es"].includes(lang)) return; │
└──────────────────────────────────────────────────────────────┘
src/actions/water-log/settings.ts:188
┌──────────────────────────────────────────────────────────────┐
│ 187 | */ │
│ > 188 | export async function verifyWaterAdminPin( │
│ | ^ │
│ 189 | brandId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale-auth.ts:33
┌──────────────────────────────────────────────────────────────┐
│ 32 | │
│ > 33 | export async function wholesaleLoginAction(_formData… │
│ | ^ │
│ 34 | return { success: false, error: NOT_AVAILABLE_ERRO… │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale-auth.ts:37
┌──────────────────────────────────────────────────────────────┐
│ 36 | │
│ > 37 | export async function wholesaleLogoutAction(): Promi… │
│ | ^ │
│ 38 | const cookieStore = await cookies(); │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale-register.ts:7
┌──────────────────────────────────────────────────────────────┐
│ 6 | │
│ > 7 | export async function registerWholesaleCustomer(param… │
│ | ^ │
│ 8 | brandId: string; │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale-register.ts:88
┌──────────────────────────────────────────────────────────────┐
│ 87 | │
│ > 88 | export async function getWholesaleCustomerByUser( │
│ | ^ │
│ 89 | brandId: string, │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale-register.ts:124
┌──────────────────────────────────────────────────────────────┐
│ 123 | // Fetch a wholesale customer directly by their cus… │
│ > 124 | export async function getWholesaleCustomer( │
│ | ^ │
│ 125 | customerId: string │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale-register.ts:161
┌──────────────────────────────────────────────────────────────┐
│ 160 | │
│ > 161 | export async function getWholesaleProducts(brandId:… │
│ | ^ │
│ 162 | // Uses SECURITY DEFINER RPC — no auth required f… │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale-register.ts:174
┌──────────────────────────────────────────────────────────────┐
│ 173 | │
│ > 174 | export async function submitWholesaleOrder(params: { │
│ | ^ │
│ 175 | brandId: string; │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale-register.ts:261
┌──────────────────────────────────────────────────────────────┐
│ 260 | │
│ > 261 | export async function enqueueWholesaleWebhookForOrd… │
│ | ^ │
│ 262 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale-register.ts:316
┌──────────────────────────────────────────────────────────────┐
│ 315 | │
│ > 316 | export async function getWholesaleCustomerPricing(c… │
│ | ^ │
│ 317 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale-register.ts:328
┌──────────────────────────────────────────────────────────────┐
│ 327 | │
│ > 328 | export async function upsertWholesaleCustomerPricin… │
│ | ^ │
│ 329 | customerId: string; │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale-register.ts:344
┌──────────────────────────────────────────────────────────────┐
│ 343 | │
│ > 344 | export async function deleteWholesaleCustomerPricin… │
│ | ^ │
│ 345 | customerId: string; │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale-register.ts:359
┌──────────────────────────────────────────────────────────────┐
│ 358 | │
│ > 359 | export async function getWholesaleCustomerOrders(cu… │
│ | ^ │
│ 360 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale/notifications.ts:7
┌──────────────────────────────────────────────────────────────┐
│ 6 | │
│ > 7 | export async function getWholesaleNotificationStats( │
│ | ^ │
│ 8 | brandId: string │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale/notifications.ts:59
┌──────────────────────────────────────────────────────────────┐
│ 58 | │
│ > 59 | export async function enqueueWholesaleNotification(p… │
│ | ^ │
│ 60 | brandId: string; │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale/orders.ts:42
┌──────────────────────────────────────────────────────────────┐
│ 41 | │
│ > 42 | export async function getWholesaleDashboardStats(bra… │
│ | ^ │
│ 43 | const orders = await getWholesaleOrders(brandId); │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale/settings.ts:27
┌──────────────────────────────────────────────────────────────┐
│ 26 | │
│ > 27 | export async function getWholesaleSettingsPublic(bra… │
│ | ^ │
│ 28 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale/webhooks.ts:7
┌──────────────────────────────────────────────────────────────┐
│ 6 | │
│ > 7 | export async function getWebhookSettings(brandId: str… │
│ | ^ │
│ 8 | try { │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale/webhooks.ts:47
┌──────────────────────────────────────────────────────────────┐
│ 46 | │
│ > 47 | export async function enqueueWholesaleWebhook( │
│ | ^ │
│ 48 | eventType: "order_created" | "order_fulfilled" | "… │
└──────────────────────────────────────────────────────────────┘
src/actions/wholesale/webhooks.ts:65
┌──────────────────────────────────────────────────────────────┐
│ 64 | │
│ > 65 | export async function getRecentWebhookActivity(brand… │
│ | ^ │
│ 66 | id: string; │
└──────────────────────────────────────────────────────────────┘
✖ Security: socket/low-supply-chain-score
`vitest@2.1.9 (lowest version "^2.1.9" allows)` scored
25/100 on Socket's vulnerability axis (minimum 50). This
points to known security vulnerabilities (CVEs) affecting
this version. Other axes — supply chain 97, maintenance 98,
quality 78, license 100.
→ Upgrade to a version with no known advisories (run `npm
audit` to find one), or replace it; update `"vitest":
"^2.1.9"` in package.json. Full report:
https://socket.dev/npm/package/vitest/overview/2.1.9. If
you've reviewed and accepted this package, raise
`supplyChain.minScore` (currently 50) or set
`supplyChain.severity: "warning"`.
package.json:86
┌──────────────────────────────────────────────────────────────┐
│ 85 | "vite-tsconfig-paths": "^5.1.4", │
│ > 86 | "vitest": "^2.1.9" │
│ | ^ │
│ 87 | }, │
└──────────────────────────────────────────────────────────────┘
✖ Security: Side effect in GET handler ×2
Learn more: https://react.doctor/docs/rules/react-doctor/nextjs-no-side-effect-in-get-handler
This GET handler's side effect (fetch() with method POST)
is prone to CSRF vulnerabilities, since prefetching or a
forged request can trigger it.
→ GET requests can be prefetched and are open to CSRF. Move
the side effect to a POST handler.
src/app/api/square/oauth/callback/route.ts:5
┌──────────────────────────────────────────────────────────────┐
│ 4 | │
│ > 5 | export async function GET(request: Request) { │
│ | ^ │
│ 6 | const { searchParams } = new URL(request.url); │
└──────────────────────────────────────────────────────────────┘
src/app/api/stripe/oauth/callback/route.ts:5
┌──────────────────────────────────────────────────────────────┐
│ 4 | │
│ > 5 | export async function GET(req: Request) { │
│ | ^ │
│ 6 | const adminUser = await getAdminUser(); │
└──────────────────────────────────────────────────────────────┘
⚠ Security: HTML injection sink with dynamic content ×6
Learn more: https://react.doctor/docs/rules/react-doctor/dangerous-html-sink
HTML is injected from a dynamic-looking source, which can
become XSS if the value is user-controlled or unsanitized.
→ Prefer rendering structured React nodes. If HTML is
required, sanitize with a well-reviewed sanitizer and keep
the trust boundary close to the sink.
src/app/admin/water-log/headgates/HeadgatesManager.tsx:176
src/app/admin/water-log/headgates/HeadgatesManager.tsx:475
src/app/wholesale/employee/page.tsx:244
src/components/admin/TemplateEditForm.tsx:652
src/components/wholesale/admin/OrdersTab.tsx:281
src/components/wholesale/admin/OrdersTab.tsx:404
✖ Bugs: Mutable module state on the server ×8
Learn more: https://react.doctor/docs/rules/react-doctor/server-no-mutable-module-state
Module-scoped const "PRICE_KEYS = {}" leaks state between
your users, since every request shares it.
→ Keep per-request data inside the action, or in headers,
cookies, or `React.cache`. Module-scope `let`/`var` is
shared by every request.
src/actions/billing/stripe-checkout.ts:12
┌──────────────────────────────────────────────────────────────┐
│ 11 | │
│ > 12 | const PRICE_KEYS: Record<string, string | undefined>… │
│ | ^ │
│ 13 | starter: process.env.STRIPE_PRICE_STARTER, │
└──────────────────────────────────────────────────────────────┘
src/actions/dashboard.ts:47
┌──────────────────────────────────────────────────────────────┐
│ 46 | │
│ > 47 | const EMPTY_MOBILE_DASHBOARD: MobileDashboardSummary… │
│ | ^ │
│ 48 | orders_today: 0, │
└──────────────────────────────────────────────────────────────┘
src/actions/email-automation/abandoned-cart.ts:43
┌──────────────────────────────────────────────────────────────┐
│ 42 | │
│ > 43 | const LOCALE_CART_SUBJECT: Record<string, Record<num… │
│ | ^ │
│ 44 | en: { │
└──────────────────────────────────────────────────────────────┘
src/actions/email-automation/welcome-sequence.ts:44
┌──────────────────────────────────────────────────────────────┐
│ 43 | │
│ > 44 | const WELCOME_EMAILS: Record<string, Record<number, … │
│ | ^ │
│ 45 | en: { │
└──────────────────────────────────────────────────────────────┘
src/actions/shipping/fedex-labels.ts:32
┌──────────────────────────────────────────────────────────────┐
│ 31 | │
│ > 32 | let cachedToken: FedExAuthToken | null = null; │
│ | ^ │
│ 33 | │
└──────────────────────────────────────────────────────────────┘
src/actions/shipping/fedex-rates.ts:63
┌──────────────────────────────────────────────────────────────┐
│ 62 | │
│ > 63 | let cachedToken: FedExAuthToken | null = null; │
│ | ^ │
│ 64 | │
└──────────────────────────────────────────────────────────────┘
src/actions/shipping/settings.ts:57
┌──────────────────────────────────────────────────────────────┐
│ 56 | │
│ > 57 | let cachedToken: FedExAuthToken | null = null; │
│ | ^ │
│ 58 | │
└──────────────────────────────────────────────────────────────┘
src/actions/time-tracking/field.ts:157
┌──────────────────────────────────────────────────────────────┐
│ 156 | │
│ > 157 | const EMPTY_PAY_PERIOD: PayPeriodHours = { │
│ | ^ │
│ 158 | success: false, │
└──────────────────────────────────────────────────────────────┘
⚠ Security: Raw SQL built outside parameter binding ×4
Learn more: https://react.doctor/docs/rules/react-doctor/raw-sql-injection-risk
Code uses a raw SQL escape hatch or string-built query
shape that can bypass parameter binding.
→ Keep user input in driver parameters or ORM bind
variables. Avoid unsafe/raw SQL helpers and string
interpolation for queries.
src/actions/brand-settings.ts:175
src/actions/orders/update-order.ts:75
src/app/admin/stops/page.tsx:57
src/app/api/ai/customer-insights/route.ts:105
✖ Bugs: Effect subscription or timer never cleaned up
Learn more: https://react.doctor/docs/rules/react-doctor/effect-needs-cleanup
`setTimeout` creates a timer in useEffect without returning
cleanup. Return a cleanup function so it does not leak
after unmount.
→ Return a cleanup function that stops the subscription or
timer: `return () => target.removeEventListener(name,
handler)` for listeners, `return () => clearInterval(id)`
or `clearTimeout(id)` for timers, or `return unsubscribe`
if the subscribe call already gave you one.
src/components/legal/CookieConsentBanner.tsx:23
┌──────────────────────────────────────────────────────────────┐
│ 22 | │
│ > 23 | useEffect(() => { │
│ | ^ │
│ 24 | // Check if consent was already given │
└──────────────────────────────────────────────────────────────┘
✖ Security: Permissive Supabase RLS policy
Learn more: https://react.doctor/docs/rules/react-doctor/supabase-rls-policy-risk
Supabase policy SQL disables RLS, permits writes broadly,
or references a service-role bypass.
→ Keep public-read policies explicit, but gate inserts,
updates, deletes, and service-role bypasses behind
`auth.uid()` plus trusted tenant membership.
db/migrations/0001_init.sql:1758
⚠ Performance: Component defined inside a component
Learn more: https://react.doctor/docs/rules/react-doctor/no-unstable-nested-components
Your users lose this component's state on every render
because it's defined inside another component
(`StopTableClient`).
→ Move nested components to module scope so React does not
remount them and lose state on every render.
src/components/admin/StopTableClient.tsx:259
⚠ Bugs: Array index used as a key ×82
Learn more: https://react.doctor/docs/rules/react-doctor/no-array-index-as-key
Your users can see & submit the wrong data when this list
reorders or filters, so use a stable id like
`key={item.id}`, not the array index "i".
→ Use a stable id from the item, like `key={item.id}` or
`key={item.slug}`. Index keys break when the list reorders
or filters.
src/app/admin/import/ImportCenterClient.tsx:283
src/app/admin/products/import/page.tsx:230
src/app/admin/sales/import/page.tsx:184
src/app/admin/settings/ai/AIClient.tsx:544
src/app/admin/settings/ai/AIClient.tsx:748
src/app/admin/settings/ai/AIClient.tsx:759
src/app/admin/settings/ai/AIClient.tsx:887
src/app/admin/settings/ai/AIClient.tsx:928
src/app/admin/settings/ai/AIClient.tsx:986
src/app/admin/settings/ai/AIClient.tsx:1015
src/app/admin/settings/ai/AIClient.tsx:1025
src/app/admin/settings/ai/AIClient.tsx:1162
src/app/admin/settings/ai/AIClient.tsx:1171
src/app/admin/settings/ai/AIClient.tsx:1304
src/app/admin/settings/ai/AIClient.tsx:1413
src/app/admin/settings/ai/AIClient.tsx:1513
src/app/admin/settings/ai/AIClient.tsx:1531
src/app/admin/settings/ai/AIClient.tsx:1537
src/app/admin/settings/ai/AIClient.tsx:1648
src/app/admin/settings/ai/AIClient.tsx:1733
src/app/admin/settings/ai/AIClient.tsx:1739
src/app/admin/settings/billing/BillingClientPage.tsx:215
src/app/blog/page.tsx:216
src/app/changelog/page.tsx:154
src/app/checkout/success/page.tsx:244
src/app/checkout/success/page.tsx:265
src/app/checkout/success/page.tsx:284
src/app/indian-river-direct/page.tsx:425
src/app/pricing/PricingClientPage.tsx:150
src/app/pricing/PricingClientPage.tsx:294
src/app/pricing/PricingClientPage.tsx:317
src/app/security/page.tsx:101
src/app/security/page.tsx:130
src/app/tuxedo/page.tsx:878
src/app/waitlist/page.tsx:102
src/app/wholesale/employee/page.tsx:490
src/app/wholesale/portal/page.tsx:671
src/components/admin/AbandonedCartDashboard.tsx:229
src/components/admin/AdminOrdersPanel.tsx:997
src/components/admin/AdvancedSettingsClient.tsx:175
src/components/admin/AnalyticsDashboard.tsx:105
src/components/admin/AnalyticsDashboard.tsx:171
src/components/admin/AnalyticsDashboard.tsx:226
src/components/admin/AnalyticsDashboard.tsx:316
src/components/admin/ContactImportForm.tsx:597
src/components/admin/ContactImportForm.tsx:703
src/components/admin/ContactImportForm.tsx:733
src/components/admin/ProductSyncBanner.tsx:89
src/components/admin/ReportsDashboard.tsx:687
src/components/admin/ReportsDashboard.tsx:698
src/components/admin/ScheduleImportModal.tsx:225
src/components/admin/ScheduleImportModal.tsx:254
src/components/admin/SettingsSections.tsx:319
src/components/admin/StatsStrip.tsx:18
src/components/admin/TimeTrackingSettingsClient.tsx:528
src/components/admin/UpgradePlanModal.tsx:257
src/components/admin/design-system/AdminActionMenu.tsx:55
src/components/admin/design-system/AdminStatsBar.tsx:24
src/components/admin/design-system/PageHeader.tsx:35
src/components/admin/shared/FilterBar.tsx:57
src/components/admin/shared/PageHeader.tsx:18
src/components/admin/stops/StopsCalendar.tsx:252
src/components/landing/FeaturesAndStats.tsx:489
src/components/landing/FeaturesAndStats.tsx:616
src/components/landing/HeroSection.tsx:392
src/components/landing/HeroSection.tsx:761
src/components/landing/HeroSection.tsx:862
src/components/landing/TestimonialsAndCTA.tsx:175
src/components/landing/TestimonialsAndCTA.tsx:295
src/components/onboarding/OnboardingFlow.tsx:289
src/components/referral/ReferralSystem.tsx:126
src/components/route-trace/FsmaReportModal.tsx:526
src/components/route-trace/RouteTraceDashboard.tsx:467
src/components/shared/LoadingSkeleton.tsx:83
src/components/shared/LoadingSkeleton.tsx:95
src/components/shared/LoadingSkeleton.tsx:104
src/components/shared/LoadingSkeleton.tsx:113
src/components/storefront/BreadcrumbNav.tsx:49
src/components/storefront/TuxedoVideoHero.tsx:282
src/components/storefront/TuxedoVideoHero.tsx:367
src/components/wholesale/OrderDetailsModal.tsx:90
src/components/wholesale/admin/SettingsTab.tsx:238
⚠ Bugs: Missing effect dependencies ×12
Learn more: https://react.doctor/docs/rules/react-doctor/exhaustive-deps
`useCallback` can run with a stale `setSelectedStop` & show
your users old data.
→ Don't blindly add missing dependencies. Read the hook
callback first.
Bad:
useEffect(() => {
setCount(count + 1);
}, [count]);
Better:
useEffect(() => {
setCount((currentCount) => currentCount + 1);
}, []);
If the missing value is recreated every render, move it
inside the hook or stabilize it before adding it to deps.
src/app/cart/CartClient.tsx:86
src/app/checkout/CheckoutClient.tsx:141
src/components/admin/CartHydration.tsx:15
↳ oxlint matches plugin rules only by their full name, so `react-hooks/exhaustive-deps` in your eslint-disable comment does not silence `react-doctor/exhaustive-deps` — change it to `react-doctor/exhaustive-deps`.
src/components/admin/MessageLogPanel.tsx:242
src/components/admin/ReportsDashboard.tsx:315
↳ oxlint matches plugin rules only by their full name, so `react-hooks/exhaustive-deps` in your eslint-disable comment does not silence `react-doctor/exhaustive-deps` — change it to `react-doctor/exhaustive-deps`.
src/components/route-trace/FsmaReportModal.tsx:184
src/components/route-trace/QRScanModal.tsx:179
src/components/storefront/StripeExpressCheckout.tsx:150
↳ oxlint matches plugin rules only by their full name, so `react-hooks/exhaustive-deps` in your eslint-disable comment does not silence `react-doctor/exhaustive-deps` — change it to `react-doctor/exhaustive-deps`.
src/components/storefront/StripeExpressCheckout.tsx:156
src/components/time-tracking/TimeTrackingFieldClient.tsx:236
↳ oxlint matches plugin rules only by their full name, so `react-hooks/exhaustive-deps` in your eslint-disable comment does not silence `react-doctor/exhaustive-deps` — change it to `react-doctor/exhaustive-deps`.
src/components/time-tracking/TimeTrackingFieldClient.tsx:256
src/components/water/WaterFieldClient.tsx:166
↳ oxlint matches plugin rules only by their full name, so `react-hooks/exhaustive-deps` in your eslint-disable comment does not silence `react-doctor/exhaustive-deps` — change it to `react-doctor/exhaustive-deps`.
⚠ Bugs: Time or random value in JSX ×23
Learn more: https://react.doctor/docs/rules/react-doctor/rendering-hydration-mismatch-time
This can cause a hydration mismatch because new Date()
reached from JSX gives a different value on the server than
in the browser. Move it into useEffect+useState to run only
in the browser, or add suppressHydrationWarning to the
parent if it's on purpose.
→ Move time or random values into useEffect+useState so
they only run in the browser, or add
suppressHydrationWarning to the parent if it's intentional
src/app/admin/v2/page.tsx:88
src/app/admin/v2/page.tsx:133
src/app/blog/page.tsx:263
src/app/brands/page.tsx:215
src/app/changelog/page.tsx:211
src/app/contact/ContactClientPage.tsx:285
src/app/privacy-policy/page.tsx:67
src/app/privacy-policy/page.tsx:129
src/app/roadmap/page.tsx:249
src/app/security/page.tsx:260
src/app/terms-and-conditions/page.tsx:67
src/app/terms-and-conditions/page.tsx:131
src/app/waitlist/page.tsx:121
src/app/wholesale/portal/page.tsx:822
src/components/admin/CampaignListPanel.tsx:668
src/components/admin/HarvestReach/AnalyticsDashboard.tsx:432
src/components/admin/HarvestReach/CampaignComposerPage.tsx:781
src/components/admin/ReportsDashboard.tsx:387
src/components/admin/TaxDashboard.tsx:312
src/components/admin/WaterLogAdminPanel.tsx:381
src/components/landing/LandingPageWrapper.tsx:299
src/components/layout/SiteFooter.tsx:37
src/components/storefront/StorefrontFooter.tsx:201
⚠ Bugs: Sequential independent awaits ×29
Learn more: https://react.doctor/docs/rules/react-doctor/server-sequential-independent-await
This await doesn't use the previous result, so your users
wait twice as long for nothing.
→ These two awaits don't depend on each other. Wrap them in
`Promise.all([...])` so they run at the same time.
src/actions/communications/stop-blast.ts:59
src/actions/communications/stop-messaging.ts:81
src/actions/harvest-reach/segments.ts:238
src/actions/shipping/fedex-rates.ts:196
src/actions/stops/get-stop-details.ts:88
src/actions/water-log/field.ts:149
src/app/admin/pickup/page.tsx:13
src/app/admin/route-trace/lots/[id]/page.tsx:25
src/app/admin/settings/page.tsx:37
src/app/admin/stops/[id]/page.tsx:58
src/app/admin/stops/[id]/page.tsx:139
src/app/admin/stops/new/page.tsx:36
src/app/admin/stops/page.tsx:22
src/app/admin/v2/orders/[id]/page.tsx:35
src/app/admin/v2/products/page.tsx:81
src/app/admin/v2/stops/page.tsx:133
src/app/admin/water-log/entries/[id]/page.tsx:18-21
src/app/admin/water-log/headgates/[id]/page.tsx:19-22
src/app/admin/water-log/users/[id]/page.tsx:19-22
src/app/api/indian-river-direct/schedule-pdf/route.ts:25
src/app/api/indian-river-direct/schedule-pdf/route.ts:77
src/app/api/tuxedo/schedule-pdf/route.ts:25
src/app/api/tuxedo/schedule-pdf/route.ts:77
src/app/api/wholesale/invoice/[orderId]/pdf/route.ts:122
src/app/api/wholesale/invoice/[orderId]/route.ts:115
src/components/admin/HarvestReach/CampaignComposerPage.tsx:388
⚠ Accessibility: Label missing associated control ×284
Learn more: https://react.doctor/docs/rules/react-doctor/label-has-associated-control
Screen reader users can't tell which input this label names
because it's tied to none, so add `htmlFor` or wrap the
input inside it.
→ Tie every label to a control with `htmlFor`, or by
nesting the input.
src/app/admin/import/ImportCenterClient.tsx:164
src/app/admin/settings/ai/AIClient.tsx:883
src/app/admin/settings/ai/AIClient.tsx:917
src/app/admin/settings/ai/AIClient.tsx:1102
src/app/admin/settings/ai/AIClient.tsx:1106
src/app/admin/settings/ai/AIClient.tsx:1110
src/app/admin/settings/ai/AIClient.tsx:1114
src/app/admin/settings/ai/AIClient.tsx:1232
src/app/admin/settings/ai/AIClient.tsx:1390
src/app/admin/settings/ai/AIClient.tsx:1403
src/app/admin/settings/ai/AIClient.tsx:1424
src/app/admin/settings/ai/AIClient.tsx:1435
src/app/admin/settings/ai/AIClient.tsx:1446
src/app/admin/settings/ai/AIClient.tsx:1457
src/app/admin/settings/ai/AIClient.tsx:1469
src/app/admin/settings/ai/AIClient.tsx:1625
src/app/admin/settings/ai/AIClient.tsx:1629
src/app/admin/settings/ai/AIClient.tsx:1637
src/app/admin/settings/integrations/IntegrationsClient.tsx:311
src/app/admin/settings/integrations/IntegrationsClient.tsx:396
src/app/admin/settings/integrations/IntegrationsClient.tsx:552
src/app/change-password/ChangePasswordForm.tsx:84
src/app/change-password/ChangePasswordForm.tsx:100
src/app/change-password/page.tsx:75
src/app/change-password/page.tsx:91
src/app/indian-river-direct/contact/ContactClientPage.tsx:164
src/app/indian-river-direct/contact/ContactClientPage.tsx:175
src/app/indian-river-direct/contact/ContactClientPage.tsx:188
src/app/indian-river-direct/contact/ContactClientPage.tsx:201
src/app/reset-password/page.tsx:99
src/app/reset-password/page.tsx:114
src/app/tuxedo/contact/ContactClientPage.tsx:133
src/app/tuxedo/contact/ContactClientPage.tsx:143
src/app/tuxedo/contact/ContactClientPage.tsx:155
src/app/tuxedo/contact/ContactClientPage.tsx:168
src/app/wholesale/portal/page.tsx:814
src/components/admin/AIProviderPanel.tsx:229
src/components/admin/AIProviderPanel.tsx:251
src/components/admin/AIProviderPanel.tsx:281
src/components/admin/AIProviderPanel.tsx:296
src/components/admin/AddLocationModal.tsx:121
src/components/admin/AddLocationModal.tsx:136
src/components/admin/AddLocationModal.tsx:151
src/components/admin/AddLocationModal.tsx:164
src/components/admin/AddLocationModal.tsx:178
src/components/admin/AddLocationModal.tsx:194
src/components/admin/AddLocationModal.tsx:207
src/components/admin/AddLocationModal.tsx:222
src/components/admin/AddLocationModal.tsx:236
src/components/admin/AddStopModal.tsx:309
src/components/admin/AdminOrdersPanel.tsx:941
src/components/admin/AdvancedAIPanel.tsx:390
src/components/admin/AdvancedAIPanel.tsx:418
src/components/admin/AdvancedAIPanel.tsx:446
src/components/admin/AdvancedAIPanel.tsx:463
src/components/admin/AdvancedIntegrations.tsx:198
src/components/admin/AdvancedIntegrations.tsx:221
src/components/admin/AdvancedIntegrations.tsx:235
src/components/admin/AdvancedIntegrations.tsx:293
src/components/admin/AdvancedIntegrations.tsx:307
src/components/admin/AdvancedIntegrations.tsx:330
src/components/admin/AdvancedShipping.tsx:108
src/components/admin/AdvancedShipping.tsx:123
src/components/admin/AdvancedShipping.tsx:135
src/components/admin/AdvancedSquareSync.tsx:99
src/components/admin/AdvancedSquareSync.tsx:109
src/components/admin/AdvancedSquareSync.tsx:119
src/components/admin/BrandSettingsForm.tsx:491
src/components/admin/BrandSettingsForm.tsx:510
src/components/admin/BrandSettingsForm.tsx:529
src/components/admin/BrandSettingsForm.tsx:548
src/components/admin/BrandSettingsForm.tsx:614
src/components/admin/CampaignListPanel.tsx:149
src/components/admin/CampaignListPanel.tsx:163
src/components/admin/CampaignListPanel.tsx:482
src/components/admin/CampaignListPanel.tsx:493
src/components/admin/CampaignListPanel.tsx:505
src/components/admin/CampaignListPanel.tsx:545
src/components/admin/CampaignListPanel.tsx:563
src/components/admin/CampaignListPanel.tsx:573
src/components/admin/CampaignListPanel.tsx:582
src/components/admin/CampaignListPanel.tsx:616
src/components/admin/CampaignListPanel.tsx:626
src/components/admin/CampaignListPanel.tsx:662
src/components/admin/CreateUserModal.tsx:377
src/components/admin/CreateUserModal.tsx:420
src/components/admin/EditLocationModal.tsx:140
src/components/admin/EditLocationModal.tsx:154
src/components/admin/EditLocationModal.tsx:168
src/components/admin/EditLocationModal.tsx:180
src/components/admin/EditLocationModal.tsx:193
src/components/admin/EditLocationModal.tsx:208
src/components/admin/EditLocationModal.tsx:220
src/components/admin/EditLocationModal.tsx:234
src/components/admin/EditLocationModal.tsx:247
src/components/admin/EditStopModal.tsx:350
src/components/admin/HarvestReach/CampaignComposerPage.tsx:503
src/components/admin/HarvestReach/CampaignComposerPage.tsx:515
src/components/admin/HarvestReach/CampaignComposerPage.tsx:528
src/components/admin/HarvestReach/CampaignComposerPage.tsx:567
src/components/admin/HarvestReach/CampaignComposerPage.tsx:580
src/components/admin/HarvestReach/CampaignComposerPage.tsx:705
src/components/admin/HarvestReach/CampaignComposerPage.tsx:735
src/components/admin/HarvestReach/CampaignComposerPage.tsx:775
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:330
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:339
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:369
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:425
src/components/admin/HarvestReach/SegmentEditModal.tsx:77
src/components/admin/HarvestReach/SegmentEditModal.tsx:89
src/components/admin/MessageCustomersSection.tsx:170
src/components/admin/MessageCustomersSection.tsx:193
src/components/admin/MessageCustomersSection.tsx:207
src/components/admin/NewProductForm.tsx:240
src/components/admin/NewStopForm.tsx:128
src/components/admin/NewStopForm.tsx:153
src/components/admin/NewStopForm.tsx:179
src/components/admin/NewStopForm.tsx:205
src/components/admin/NewStopForm.tsx:229
src/components/admin/NewStopForm.tsx:255
src/components/admin/NewStopForm.tsx:282
src/components/admin/NewStopForm.tsx:295
src/components/admin/NewStopForm.tsx:306
src/components/admin/NewStopForm.tsx:319
src/components/admin/OrderEditForm.tsx:267
src/components/admin/OrderEditForm.tsx:279
src/components/admin/OrderEditForm.tsx:328
src/components/admin/OrderEditForm.tsx:357
src/components/admin/OrderEditForm.tsx:395
src/components/admin/OrderEditForm.tsx:444
src/components/admin/OrderEditForm.tsx:462
src/components/admin/OrderPaymentSection.tsx:173
src/components/admin/OrderPaymentSection.tsx:189
src/components/admin/OrderPaymentSection.tsx:205
src/components/admin/OrderPaymentSection.tsx:277
src/components/admin/OrderPaymentSection.tsx:303
src/components/admin/ProductEditForm.tsx:209
src/components/admin/ProductEditForm.tsx:223
src/components/admin/ProductEditForm.tsx:253
src/components/admin/SettingsSections.tsx:316
src/components/admin/SettingsSections.tsx:323
src/components/admin/SettingsSections.tsx:332
src/components/admin/SettingsSections.tsx:341
src/components/admin/SettingsSections.tsx:397
src/components/admin/SettingsSections.tsx:420
src/components/admin/SettingsSections.tsx:623
src/components/admin/SettingsSections.tsx:630
src/components/admin/SettingsSections.tsx:638
src/components/admin/SettingsSections.tsx:688
src/components/admin/SettingsSections.tsx:694
src/components/admin/SettingsSections.tsx:701
src/components/admin/SettingsSections.tsx:710
src/components/admin/StopEditForm.tsx:121
src/components/admin/StopEditForm.tsx:146
src/components/admin/StopEditForm.tsx:173
src/components/admin/StopEditForm.tsx:197
src/components/admin/StopEditForm.tsx:209
src/components/admin/StopEditForm.tsx:221
src/components/admin/StopEditForm.tsx:232
src/components/admin/StopEditForm.tsx:265
src/components/admin/StopMessagingForm.tsx:124
src/components/admin/StopMessagingForm.tsx:146
src/components/admin/StopMessagingForm.tsx:168
src/components/admin/TemplateEditForm.tsx:168
src/components/admin/TemplateEditForm.tsx:182
src/components/admin/TemplateEditForm.tsx:448
src/components/admin/TemplateEditForm.tsx:459
src/components/admin/TemplateEditForm.tsx:471
src/components/admin/TemplateEditForm.tsx:490
src/components/admin/TemplateEditForm.tsx:551
src/components/admin/TemplateEditForm.tsx:561
src/components/admin/TemplateEditForm.tsx:574
src/components/admin/TimeTrackingAdminPanel.tsx:419
src/components/admin/TimeTrackingAdminPanel.tsx:428
src/components/admin/TimeTrackingAdminPanel.tsx:654
src/components/admin/TimeTrackingAdminPanel.tsx:663
src/components/admin/TimeTrackingAdminPanel.tsx:672
src/components/admin/TimeTrackingAdminPanel.tsx:685
src/components/admin/TimeTrackingAdminPanel.tsx:801
src/components/admin/TimeTrackingAdminPanel.tsx:805
src/components/admin/TimeTrackingAdminPanel.tsx:813
src/components/admin/TimeTrackingAdminPanel.tsx:886
src/components/admin/TimeTrackingAdminPanel.tsx:890
src/components/admin/TimeTrackingAdminPanel.tsx:894
src/components/admin/TimeTrackingSettingsClient.tsx:525
src/components/admin/TimeTrackingSettingsClient.tsx:532
src/components/admin/TimeTrackingSettingsClient.tsx:541
src/components/admin/TimeTrackingSettingsClient.tsx:550
src/components/admin/TimeTrackingSettingsClient.tsx:613
src/components/admin/TimeTrackingSettingsClient.tsx:632
src/components/admin/TimeTrackingSettingsClient.tsx:682
src/components/admin/TimeTrackingSettingsClient.tsx:689
src/components/admin/TimeTrackingSettingsClient.tsx:697
src/components/admin/TimeTrackingSettingsClient.tsx:741
src/components/admin/TimeTrackingSettingsClient.tsx:747
src/components/admin/TimeTrackingSettingsClient.tsx:754
src/components/admin/TimeTrackingSettingsClient.tsx:763
src/components/admin/UsersPage.tsx:542
src/components/admin/UsersPage.tsx:556
src/components/admin/UsersPage.tsx:571
src/components/admin/UsersPage.tsx:584
src/components/admin/UsersPage.tsx:597
src/components/admin/UsersPage.tsx:625
src/components/admin/UsersPage.tsx:657
src/components/admin/WaterLogEntryEditForm.tsx:74
src/components/admin/WaterLogEntryEditForm.tsx:78
src/components/admin/WaterLogEntryEditForm.tsx:100
src/components/admin/WaterLogEntryEditForm.tsx:104
src/components/admin/WaterUserEditForm.tsx:90
src/components/admin/WaterUserEditForm.tsx:100
src/components/admin/WaterUserEditForm.tsx:116
src/components/admin/WaterUserEditForm.tsx:130
src/components/admin/stops/StopsLocations.tsx:194
src/components/route-trace/FsmaReportModal.tsx:297
src/components/route-trace/FsmaReportModal.tsx:308
src/components/route-trace/LotCreateForm.tsx:97
src/components/route-trace/LotCreateForm.tsx:108
src/components/route-trace/LotCreateForm.tsx:120
src/components/route-trace/LotCreateForm.tsx:135
src/components/route-trace/LotCreateForm.tsx:147
src/components/route-trace/LotCreateForm.tsx:157
src/components/route-trace/LotCreateForm.tsx:175
src/components/route-trace/LotCreateForm.tsx:187
src/components/route-trace/LotCreateForm.tsx:231
src/components/route-trace/LotCreateForm.tsx:241
src/components/route-trace/LotCreateForm.tsx:253
src/components/route-trace/LotCreateForm.tsx:264
src/components/route-trace/LotCreateModal.tsx:149
src/components/route-trace/LotCreateModal.tsx:165
src/components/route-trace/LotCreateModal.tsx:182
src/components/route-trace/LotCreateModal.tsx:202
src/components/route-trace/LotCreateModal.tsx:219
src/components/route-trace/LotCreateModal.tsx:234
src/components/route-trace/LotCreateModal.tsx:257
src/components/route-trace/LotCreateModal.tsx:274
src/components/route-trace/LotCreateModal.tsx:329
src/components/route-trace/LotCreateModal.tsx:344
src/components/route-trace/LotCreateModal.tsx:361
src/components/route-trace/LotCreateModal.tsx:377
src/components/route-trace/LotDetailPanel.tsx:753
src/components/route-trace/LotDetailPanel.tsx:772
src/components/route-trace/LotDetailPanel.tsx:782
src/components/route-trace/LotDetailPanel.tsx:823
src/components/route-trace/LotDetailPanel.tsx:834
src/components/route-trace/LotDetailPanel.tsx:877
src/components/route-trace/LotDetailPanel.tsx:888
src/components/route-trace/LotDetailPanel.tsx:899
src/components/route-trace/QRScanModal.tsx:276
src/components/route-trace/QuickNewLotModal.tsx:82
src/components/route-trace/QuickNewLotModal.tsx:97
src/components/route-trace/QuickNewLotModal.tsx:109
src/components/route-trace/QuickNewLotModal.tsx:123
src/components/route-trace/QuickNewLotModal.tsx:135
src/components/route-trace/QuickNewLotModal.tsx:149
src/components/route-trace/RouteTraceSettings.tsx:68
src/components/route-trace/RouteTraceSettings.tsx:94
src/components/route-trace/RouteTraceSettings.tsx:120
src/components/water/WaterFieldClient.tsx:635
src/components/wholesale/DepositModal.tsx:58
src/components/wholesale/DepositModal.tsx:70
src/components/wholesale/admin/OrdersTab.tsx:212
src/components/wholesale/admin/OrdersTab.tsx:223
src/components/wholesale/admin/OrdersTab.tsx:228
src/components/wholesale/admin/OrdersTab.tsx:233
src/components/wholesale/admin/OrdersTab.tsx:467
src/components/wholesale/admin/OrdersTab.tsx:472
src/components/wholesale/admin/OrdersTab.tsx:501
src/components/wholesale/admin/OrdersTab.tsx:506
src/components/wholesale/admin/ProductsTab.tsx:145
src/components/wholesale/admin/ProductsTab.tsx:154
src/components/wholesale/admin/ProductsTab.tsx:159
src/components/wholesale/admin/SettingsTab.tsx:173
src/components/wholesale/admin/SettingsTab.tsx:178
src/components/wholesale/admin/SettingsTab.tsx:183
src/components/wholesale/admin/SettingsTab.tsx:188
src/components/wholesale/admin/SettingsTab.tsx:193
src/components/wholesale/admin/SettingsTab.tsx:198
src/components/wholesale/admin/SettingsTab.tsx:203
src/components/wholesale/admin/SettingsTab.tsx:208
src/components/wholesale/admin/SettingsTab.tsx:213
src/components/wholesale/admin/SettingsTab.tsx:220
src/components/wholesale/admin/SettingsTab.tsx:270
src/components/wholesale/admin/WebhookSettingsSection.tsx:117
src/components/wholesale/admin/WebhookSettingsSection.tsx:128
⚠ Performance: Independent awaits run sequentially ×5
Learn more: https://react.doctor/docs/rules/react-doctor/async-parallel
These 3 sequential await statements run one after another
even though they look independent, so the page waits longer
than it needs to. Run them together with Promise.all()
instead
→ Use `const [a, b] = await Promise.all([fetchA(),
fetchB()])` so independent calls run at the same time
src/actions/water-log/settings.ts:162
src/app/admin/water-log/entries/[id]/page.tsx:17
src/app/admin/water-log/headgates/[id]/page.tsx:18
src/app/admin/water-log/users/[id]/page.tsx:18
src/components/admin/HarvestReach/CampaignComposerPage.tsx:387
⚠ Bugs: Derived state stored in an effect ×5
Learn more: https://react.doctor/docs/rules/react-doctor/no-derived-state-effect
Your users briefly see stale state on every prop change.
→ Work out derived values while rendering: `const x =
fn(dep)`. To reset a component's state when a prop changes,
give it a key prop: `<Component key={prop} />`. See
https://react.dev/learn/you-might-not-need-an-effect
src/components/admin/AbandonedCartDashboard.tsx:61
src/components/admin/AdminHeader.tsx:74
src/components/admin/CommandPalette.tsx:219
src/components/admin/TimeTrackingAdminPanel.tsx:201
src/components/admin/WelcomeSequenceDashboard.tsx:50
⚠ Performance: await inside a loop ×25
Learn more: https://react.doctor/docs/rules/react-doctor/async-await-in-loop
This makes the for…of loop slow because each await runs one
after another, so collect the independent calls & run them
together with `await Promise.all(items.map(...))`
→ Collect the items, then use `await
Promise.all(items.map(...))` so independent work runs at
the same time
src/actions/communications/contacts.ts:347
src/actions/import-products.ts:48
src/actions/square-orders.ts:108
src/app/admin/page.tsx:74
src/app/api/cron/send-scheduled/route.ts:47
src/app/api/cron/send-scheduled/route.ts:71
src/app/api/email-automation/abandoned-cart/route.ts:30
src/app/api/email-automation/abandoned-cart/route.ts:56
src/app/api/email-automation/welcome-sequence/route.ts:27
src/app/api/email-automation/welcome-sequence/route.ts:53
src/app/api/time-tracking/export/route.ts:81
src/app/api/time-tracking/notify/route.ts:246
src/app/api/water-qr-sheet/route.ts:29
src/app/api/wholesale/notifications/send/route.ts:79
src/app/api/wholesale/notifications/send/route.ts:121
src/app/api/wholesale/webhooks/dispatch/route.ts:98
src/components/admin/AdminOrdersPanel.tsx:305
src/components/admin/OrderEditForm.tsx:142
src/components/admin/OrderEditForm.tsx:157
src/components/admin/StopProductAssignment.tsx:178
src/components/admin/StopTableClient.tsx:231
src/components/route-trace/LotListTable.tsx:165
src/lib/billing.ts:82
src/lib/billing.ts:210
src/lib/offline/sync.ts:47
⚠ Bugs: Prop derived into useState ×40
Learn more: https://react.doctor/docs/rules/react-doctor/no-derived-useState
Your users see a stale value when prop "currentModel"
changes because useState copies it once.
→ Compute the value inline so prop changes do not leave
`useState` holding a stale copy.
src/app/admin/settings/ai/AIClient.tsx:332
src/app/admin/settings/ai/AIClient.tsx:1806-1807
src/app/admin/settings/billing/BillingClient.tsx:19
src/app/admin/settings/billing/BillingClientPage.tsx:58
src/app/admin/settings/integrations/IntegrationsClient.tsx:467
src/app/admin/settings/integrations/IntegrationsClient.tsx:475
src/app/admin/settings/integrations/IntegrationsClientPage.tsx:355
src/app/admin/settings/square-sync/SquareSyncSettingsClient.tsx:52
src/components/admin/AdvancedIntegrations.tsx:59
src/components/admin/CommunicationsLoading.tsx:247
src/components/admin/HeadgateEditForm.tsx:26-28
src/components/admin/IntegrationsInner.tsx:278
src/components/admin/OrderEditForm.tsx:64
src/components/admin/OrderEditForm.tsx:70-71
src/components/admin/PaymentSettingsForm.tsx:35
src/components/admin/ProductEditForm.tsx:39-44
src/components/admin/StopEditForm.tsx:41-47
src/components/admin/StopProductAssignment.tsx:49
src/components/admin/WaterLogEntryEditForm.tsx:32-33
src/components/admin/WaterUserEditForm.tsx:25-28
src/components/admin/products/StockAdjustButton.tsx:33
⚠ Bugs: useSearchParams without Suspense ×5
Learn more: https://react.doctor/docs/rules/react-doctor/nextjs-no-use-search-params-without-suspense
<OrdersFilterButton> uses useSearchParams() outside
<Suspense>, so this page falls back to client-side
rendering.
→ Wrap the component using `useSearchParams` in
`<Suspense>` so the rest of the page can stay statically
rendered.
src/app/admin/v2/orders/page.tsx:54
src/app/admin/v2/orders/page.tsx:69
src/app/admin/v2/products/page.tsx:140
src/app/admin/v2/products/page.tsx:156
src/app/admin/v2/products/page.tsx:174
⚠ Bugs: Unknown DOM property ×6
Learn more: https://react.doctor/docs/rules/react-doctor/no-unknown-property
React ignores this prop because it doesn't recognize the
name.
→ Use the prop name React expects, like `className`,
`htmlFor`, or `tabIndex`, so the attribute is applied
correctly.
src/app/brands/page.tsx:225
src/components/landing/FeaturesAndStats.tsx:240
src/components/landing/HeroSection.tsx:988
src/components/landing/TestimonialsAndCTA.tsx:628
src/components/legal/CookieConsentBanner.tsx:66
src/components/storefront/TuxedoVideoHero.tsx:441
⚠ Bugs: Client-side redirect for navigation ×9
Learn more: https://react.doctor/docs/rules/react-doctor/nextjs-no-client-side-redirect
router.push() in useEffect flashes the wrong page before
redirecting.
→ Avoid redirects inside useEffect. Use an event handler,
middleware, or server-side redirect (App Router: redirect()
from next/navigation; Pages Router: getServerSideProps
redirect)
src/app/logout/page.tsx:14
src/app/logout/page.tsx:18
src/app/wholesale/employee/page.tsx:54
src/app/wholesale/employee/page.tsx:69
src/app/wholesale/portal/page.tsx:307
src/app/wholesale/portal/page.tsx:316
src/app/wholesale/portal/page.tsx:322
src/app/wholesale/portal/page.tsx:328
src/components/route-trace/QRScanModal.tsx:158
⚠ Bugs: Effects chained together ×2
Learn more: https://react.doctor/docs/rules/react-doctor/no-effect-chain
Your screen redraws several times from a single action
because one useEffect changes "selected", which sets off
this one.
→ Compute as much as possible during render (e.g. `const
isGameOver = round > 5`) and write all related state inside
the event handler that originally fires the chain. Each
effect link adds an extra render and makes the code rigid
as requirements evolve
src/components/admin/CommandPalette.tsx:224
src/context/CartContext.tsx:139
⚠ Maintainability: Component rendered by inline function call ×3
Learn more: https://react.doctor/docs/rules/react-doctor/no-render-in-render
Your users lose state because "renderMappings()" builds UI
from an inline call that React remounts, so pull it into
its own component instead.
→ Make it a named component so React preserves its identity
and does not remount its state.
src/components/admin/ContactImportForm.tsx:613
src/components/admin/ContactImportForm.tsx:620
src/components/admin/ContactImportForm.tsx:627
⚠ Bugs: Hand-rolled external store subscription ×2
Learn more: https://react.doctor/docs/rules/react-doctor/prefer-use-sync-external-store
Your users can see stale or torn values because useState
"online" syncs an outside store through a useEffect.
→ Replace the `useState(getSnapshot())` + `useEffect(() =>
store.subscribe(() => setSnapshot(getSnapshot())))` pair
with `useSyncExternalStore(store.subscribe, getSnapshot)`.
The hook gets this right during concurrent rendering and on
the server; the hand-rolled version doesn't.
src/components/admin/OfflineBanner.tsx:7
src/components/admin/orders/OrdersFilterButton.tsx:20
⚠ Performance: Unstable context provider value ×2
Learn more: https://react.doctor/docs/rules/react-doctor/jsx-no-constructed-context-values
Every reader of this context redraws on each render because
you build its `value` inline.
→ Wrap the context value in `useMemo` or move it outside
the component so consumers do not redraw every render.
src/components/admin/Toast.tsx:61
src/context/CartContext.tsx:313
⚠ Performance: setState reads a stale value ×2
Learn more: https://react.doctor/docs/rules/react-doctor/rerender-functional-setstate
You can lose this update because setCurrentStep(currentStep
+ ...) reads a stale value.
→ Use the callback form: `setState(prev => prev + 1)` to
always read the latest value
src/components/onboarding/OnboardingFlow.tsx:104
src/components/onboarding/OnboardingFlow.tsx:110
⚠ Performance: array.find() inside a loop ×6
Learn more: https://react.doctor/docs/rules/react-doctor/js-index-maps
This gets slow as your list grows because array.find() runs
inside a loop, so build a Map once before the loop for
instant lookups
→ Build a `Map` once before the loop instead of calling
`array.find(...)` inside it
src/app/api/time-tracking/export/route.ts:156
src/app/api/time-tracking/export/route.ts:196
src/app/api/time-tracking/export/route.ts:205
src/components/admin/OrderEditForm.tsx:151
src/components/admin/StopTableClient.tsx:229
src/lib/billing.ts:80
⚠ Accessibility: Control missing accessible label ×444
Learn more: https://react.doctor/docs/rules/react-doctor/control-has-associated-label
Blind users can't tell what this control does because
screen readers find no label, so add visible text,
`aria-label`, or `aria-labelledby`.
→ Give every interactive control a label screen readers can
read.
src/app/admin/import/ImportCenterClient.tsx:192
src/app/admin/settings/ai/AIClient.tsx:362
src/app/admin/settings/ai/AIClient.tsx:888
src/app/admin/settings/ai/AIClient.tsx:898
src/app/admin/settings/ai/AIClient.tsx:929
src/app/admin/settings/ai/AIClient.tsx:937
src/app/admin/settings/ai/AIClient.tsx:947
src/app/admin/settings/ai/AIClient.tsx:1103
src/app/admin/settings/ai/AIClient.tsx:1107
src/app/admin/settings/ai/AIClient.tsx:1111
src/app/admin/settings/ai/AIClient.tsx:1115
src/app/admin/settings/ai/AIClient.tsx:1233
src/app/admin/settings/ai/AIClient.tsx:1391
src/app/admin/settings/ai/AIClient.tsx:1425
src/app/admin/settings/ai/AIClient.tsx:1436
src/app/admin/settings/ai/AIClient.tsx:1447
src/app/admin/settings/ai/AIClient.tsx:1458
src/app/admin/settings/ai/AIClient.tsx:1470
src/app/admin/settings/ai/AIClient.tsx:1626
src/app/admin/settings/ai/AIClient.tsx:1630
src/app/admin/settings/ai/AIClient.tsx:1649
src/app/admin/settings/ai/AIClient.tsx:1657
src/app/admin/settings/ai/AIClient.tsx:1665
src/app/admin/settings/ai/AIClient.tsx:1773
src/app/admin/settings/billing/BillingClientPage.tsx:407
src/app/admin/settings/integrations/IntegrationsClient.tsx:312
src/app/admin/settings/integrations/IntegrationsClient.tsx:567
src/app/admin/settings/integrations/IntegrationsClient.tsx:616
src/app/admin/settings/integrations/IntegrationsClient.tsx:622
src/app/admin/water-log/headgates/HeadgatesManager.tsx:271-272
src/app/admin/water-log/headgates/HeadgatesManager.tsx:285-286
src/app/admin/water-log/headgates/HeadgatesManager.tsx:369
src/app/admin/water-log/headgates/HeadgatesManager.tsx:507
src/app/blog/page.tsx:244
src/app/change-password/ChangePasswordForm.tsx:87
src/app/change-password/ChangePasswordForm.tsx:103
src/app/change-password/page.tsx:78
src/app/change-password/page.tsx:94
src/app/indian-river-direct/contact/ContactClientPage.tsx:165
src/app/indian-river-direct/contact/ContactClientPage.tsx:176
src/app/indian-river-direct/contact/ContactClientPage.tsx:202
src/app/indian-river-direct/faq/FAQClientPage.tsx:171
src/app/indian-river-direct/faq/FAQClientPage.tsx:179
src/app/indian-river-direct/page.tsx:457
src/app/maintenance/page.tsx:62
src/app/maintenance/page.tsx:67
src/app/reset-password/page.tsx:102
src/app/reset-password/page.tsx:117
src/app/tuxedo/contact/ContactClientPage.tsx:134
src/app/tuxedo/contact/ContactClientPage.tsx:144
src/app/tuxedo/contact/ContactClientPage.tsx:169
src/app/tuxedo/faq/FAQClientPage.tsx:160
src/app/tuxedo/faq/FAQClientPage.tsx:168
src/app/water/admin/login/WaterAdminPinClient.tsx:41
src/app/wholesale/employee/page.tsx:432
src/app/wholesale/employee/page.tsx:442
src/app/wholesale/login/page.tsx:256
src/app/wholesale/login/page.tsx:269
src/app/wholesale/portal/page.tsx:117
src/app/wholesale/portal/page.tsx:184
src/app/wholesale/portal/page.tsx:192
src/app/wholesale/portal/page.tsx:749
src/app/wholesale/portal/page.tsx:818
src/app/wholesale/register/page.tsx:214
src/app/wholesale/register/page.tsx:225
src/app/wholesale/register/page.tsx:236
src/app/wholesale/register/page.tsx:249
src/components/admin/AIProviderPanel.tsx:233
src/components/admin/AIProviderPanel.tsx:254
src/components/admin/AIProviderPanel.tsx:283
src/components/admin/AIProviderPanel.tsx:297
src/components/admin/AbandonedCartDashboard.tsx:114
src/components/admin/AbandonedCartDashboard.tsx:119
src/components/admin/AddLocationModal.tsx:122
src/components/admin/AddLocationModal.tsx:137
src/components/admin/AddLocationModal.tsx:152
src/components/admin/AddLocationModal.tsx:165
src/components/admin/AddLocationModal.tsx:179
src/components/admin/AddLocationModal.tsx:195
src/components/admin/AddLocationModal.tsx:208
src/components/admin/AddLocationModal.tsx:223
src/components/admin/AddLocationModal.tsx:237
src/components/admin/AdminOrdersPanel.tsx:635-636
src/components/admin/AdminOrdersPanel.tsx:698-699
src/components/admin/AdminOrdersPanel.tsx:1005
src/components/admin/AdminOrdersPanel.tsx:1019
src/components/admin/AdminStopsPanel.tsx:150
src/components/admin/AdminStopsPanel.tsx:204
src/components/admin/AdminStopsPanel.tsx:246
src/components/admin/AdminStopsPanel.tsx:320
src/components/admin/AdminStopsPanel.tsx:330
src/components/admin/AdvancedAIPanel.tsx:394
src/components/admin/AdvancedAIPanel.tsx:421
src/components/admin/AdvancedAIPanel.tsx:450
src/components/admin/AdvancedAIPanel.tsx:466
src/components/admin/AdvancedIntegrations.tsx:200
src/components/admin/AdvancedIntegrations.tsx:222
src/components/admin/AdvancedIntegrations.tsx:236
src/components/admin/AdvancedIntegrations.tsx:294
src/components/admin/AdvancedIntegrations.tsx:309
src/components/admin/AdvancedIntegrations.tsx:331
src/components/admin/AdvancedShipping.tsx:124
src/components/admin/AdvancedShipping.tsx:137
src/components/admin/AdvancedShipping.tsx:202
src/components/admin/AdvancedSquareSync.tsx:100
src/components/admin/AdvancedSquareSync.tsx:110
src/components/admin/AdvancedSquareSync.tsx:120
src/components/admin/AdvancedSquareSync.tsx:174
src/components/admin/BrandSettingsForm.tsx:493
src/components/admin/BrandSettingsForm.tsx:499
src/components/admin/BrandSettingsForm.tsx:512
src/components/admin/BrandSettingsForm.tsx:518
src/components/admin/BrandSettingsForm.tsx:531
src/components/admin/BrandSettingsForm.tsx:537
src/components/admin/BrandSettingsForm.tsx:550
src/components/admin/BrandSettingsForm.tsx:556
src/components/admin/BrandSettingsForm.tsx:707
src/components/admin/BrandSettingsForm.tsx:775
src/components/admin/BrandSettingsForm.tsx:914
src/components/admin/CampaignListPanel.tsx:152
src/components/admin/CampaignListPanel.tsx:285
src/components/admin/CampaignListPanel.tsx:483
src/components/admin/CampaignListPanel.tsx:564
src/components/admin/CampaignListPanel.tsx:574
src/components/admin/CampaignListPanel.tsx:583
src/components/admin/CampaignListPanel.tsx:617
src/components/admin/CampaignListPanel.tsx:627
src/components/admin/CampaignListPanel.tsx:663
src/components/admin/ContactListPanel.tsx:102-118
src/components/admin/ContactListPanel.tsx:243
src/components/admin/ContactListPanel.tsx:282
src/components/admin/ContactListPanel.tsx:307
src/components/admin/CreateUserModal.tsx:294
src/components/admin/DriverPickupPanel.tsx:177
src/components/admin/EditLocationModal.tsx:141
src/components/admin/EditLocationModal.tsx:155
src/components/admin/EditLocationModal.tsx:169
src/components/admin/EditLocationModal.tsx:181
src/components/admin/EditLocationModal.tsx:194
src/components/admin/EditLocationModal.tsx:209
src/components/admin/EditLocationModal.tsx:221
src/components/admin/EditLocationModal.tsx:235
src/components/admin/EditLocationModal.tsx:248
src/components/admin/ElegantModal.tsx:68
src/components/admin/HarvestReach/CampaignComposerPage.tsx:504
src/components/admin/HarvestReach/CampaignComposerPage.tsx:516
src/components/admin/HarvestReach/CampaignComposerPage.tsx:570
src/components/admin/HarvestReach/CampaignComposerPage.tsx:776
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:331
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:340
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:370
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:386
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:400
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:426
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:441
src/components/admin/HarvestReach/SegmentEditModal.tsx:78
src/components/admin/HarvestReach/SegmentEditModal.tsx:92
src/components/admin/IntegrationsInner.tsx:228
src/components/admin/LocationsTab.tsx:224
src/components/admin/LocationsTab.tsx:333
src/components/admin/MessageCustomersSection.tsx:196
src/components/admin/MessageCustomersSection.tsx:210
src/components/admin/MessageLogPanel.tsx:132-148
src/components/admin/MessageLogPanel.tsx:322
src/components/admin/NewProductForm.tsx:277
src/components/admin/NewStopForm.tsx:131
src/components/admin/NewStopForm.tsx:156
src/components/admin/NewStopForm.tsx:182
src/components/admin/NewStopForm.tsx:208
src/components/admin/NewStopForm.tsx:232
src/components/admin/NewStopForm.tsx:296
src/components/admin/NewStopForm.tsx:307
src/components/admin/NewStopForm.tsx:321
src/components/admin/OrderEditForm.tsx:268
src/components/admin/OrderEditForm.tsx:287
src/components/admin/OrderEditForm.tsx:316
src/components/admin/OrderEditForm.tsx:336
src/components/admin/OrderEditForm.tsx:358
src/components/admin/OrderEditForm.tsx:399
src/components/admin/OrderPaymentSection.tsx:206
src/components/admin/OrderPaymentSection.tsx:280
src/components/admin/OrderPaymentSection.tsx:304
src/components/admin/ProductEditForm.tsx:290
src/components/admin/ProductFilterBar.tsx:25
src/components/admin/ProductFormModal.tsx:415
src/components/admin/ProductTableBody.tsx:72
src/components/admin/ProductTableBody.tsx:175
src/components/admin/ProductTableClient.tsx:55
src/components/admin/ProductTableClient.tsx:99
src/components/admin/ProductTableClient.tsx:220
src/components/admin/ProductsClient.tsx:435
src/components/admin/ProductsClient.tsx:684
src/components/admin/ReportsDashboard.tsx:345
src/components/admin/ReportsDashboard.tsx:352
src/components/admin/ReportsDashboard.tsx:672
src/components/admin/ScheduleImportModal.tsx:190
src/components/admin/ScheduleImportModal.tsx:249
src/components/admin/ScheduleImportModal.tsx:254-256
src/components/admin/ScheduleImportModal.tsx:262-263
src/components/admin/ScheduleImportModal.tsx:269-270
src/components/admin/ScheduleImportModal.tsx:276-277
src/components/admin/ScheduleImportModal.tsx:283-284
src/components/admin/ScheduleImportModal.tsx:290-291
src/components/admin/SettingsSections.tsx:325
src/components/admin/SettingsSections.tsx:334
src/components/admin/SettingsSections.tsx:343
src/components/admin/SettingsSections.tsx:399
src/components/admin/SettingsSections.tsx:410
src/components/admin/SettingsSections.tsx:422
src/components/admin/SettingsSections.tsx:433
src/components/admin/SettingsSections.tsx:607
src/components/admin/SettingsSections.tsx:624
src/components/admin/SettingsSections.tsx:679
src/components/admin/SettingsSections.tsx:689
src/components/admin/SettingsSections.tsx:695
src/components/admin/SettingsSections.tsx:711
src/components/admin/ShippingFulfillmentPanel.tsx:509
src/components/admin/StopEditForm.tsx:124
src/components/admin/StopEditForm.tsx:149
src/components/admin/StopEditForm.tsx:176
src/components/admin/StopEditForm.tsx:198
src/components/admin/StopEditForm.tsx:210
src/components/admin/StopEditForm.tsx:222
src/components/admin/StopEditForm.tsx:233
src/components/admin/StopEditForm.tsx:248
src/components/admin/StopMessagingForm.tsx:171
src/components/admin/StopProductAssignment.tsx:235
src/components/admin/StopTableClient.tsx:548
src/components/admin/StopTableClient.tsx:1055
src/components/admin/TaxDashboard.tsx:267
src/components/admin/TaxDashboard.tsx:274
src/components/admin/TemplateEditForm.tsx:171
src/components/admin/TemplateEditForm.tsx:267
src/components/admin/TemplateEditForm.tsx:449
src/components/admin/TemplateEditForm.tsx:491
src/components/admin/TemplateEditForm.tsx:552
src/components/admin/TemplateEditForm.tsx:562
src/components/admin/TemplateEditForm.tsx:575
src/components/admin/TimeTrackingAdminPanel.tsx:420
src/components/admin/TimeTrackingAdminPanel.tsx:429
src/components/admin/TimeTrackingAdminPanel.tsx:553
src/components/admin/TimeTrackingAdminPanel.tsx:603
src/components/admin/TimeTrackingAdminPanel.tsx:655
src/components/admin/TimeTrackingAdminPanel.tsx:664
src/components/admin/TimeTrackingAdminPanel.tsx:802
src/components/admin/TimeTrackingAdminPanel.tsx:887
src/components/admin/TimeTrackingAdminPanel.tsx:891
src/components/admin/TimeTrackingSettingsClient.tsx:376-379
src/components/admin/TimeTrackingSettingsClient.tsx:534
src/components/admin/TimeTrackingSettingsClient.tsx:543
src/components/admin/TimeTrackingSettingsClient.tsx:552
src/components/admin/TimeTrackingSettingsClient.tsx:581
src/components/admin/TimeTrackingSettingsClient.tsx:591
src/components/admin/TimeTrackingSettingsClient.tsx:601
src/components/admin/TimeTrackingSettingsClient.tsx:615
src/components/admin/TimeTrackingSettingsClient.tsx:634
src/components/admin/TimeTrackingSettingsClient.tsx:683
src/components/admin/TimeTrackingSettingsClient.tsx:742
src/components/admin/TimeTrackingSettingsClient.tsx:748
src/components/admin/TimeTrackingSettingsClient.tsx:764
src/components/admin/UsersPage.tsx:370
src/components/admin/UsersPage.tsx:388
src/components/admin/UsersPage.tsx:520
src/components/admin/UsersPage.tsx:543
src/components/admin/UsersPage.tsx:558
src/components/admin/UsersPage.tsx:573
src/components/admin/UsersPage.tsx:586
src/components/admin/UsersPage.tsx:646
src/components/admin/WaterLogAdminPanel.tsx:848
src/components/admin/WaterLogAdminPanel.tsx:1261
src/components/admin/WaterUserEditForm.tsx:91
src/components/admin/WelcomeSequenceDashboard.tsx:94
src/components/admin/WelcomeSequenceDashboard.tsx:99
src/components/admin/design-system/AdminActionMenu.tsx:40
src/components/admin/design-system/AdminFilterBar.tsx:52
src/components/admin/design-system/AdminFilterBar.tsx:90
src/components/admin/design-system/AdminFilterBar.tsx:102
src/components/admin/design-system/AdminModal.tsx:52
src/components/admin/design-system/AdminPagination.tsx:20
src/components/admin/design-system/AdminPagination.tsx:68
src/components/admin/design-system/AdminPagination.tsx:101
src/components/admin/design-system/AdminPagination.tsx:110
src/components/admin/design-system/AdminToggle.tsx:83
src/components/admin/shared/DataTable.tsx:100
src/components/admin/shared/DataTable.tsx:109
src/components/admin/shared/FilterBar.tsx:47
src/components/admin/stops/StopsLocations.tsx:186
src/components/legal/CookieConsentBanner.tsx:111
src/components/legal/CookieConsentBanner.tsx:129
src/components/notifications/ToastNotification.tsx:110
src/components/route-trace/AdminLookupPage.tsx:94
src/components/route-trace/FsmaReportModal.tsx:281
src/components/route-trace/FsmaReportModal.tsx:300
src/components/route-trace/FsmaReportModal.tsx:311
src/components/route-trace/FsmaReportModal.tsx:384
src/components/route-trace/LotCreateForm.tsx:98
src/components/route-trace/LotCreateForm.tsx:109
src/components/route-trace/LotCreateForm.tsx:121
src/components/route-trace/LotCreateForm.tsx:136
src/components/route-trace/LotCreateForm.tsx:148
src/components/route-trace/LotCreateForm.tsx:158
src/components/route-trace/LotCreateForm.tsx:176
src/components/route-trace/LotCreateForm.tsx:189
src/components/route-trace/LotCreateForm.tsx:214
src/components/route-trace/LotCreateForm.tsx:232
src/components/route-trace/LotCreateForm.tsx:242
src/components/route-trace/LotCreateForm.tsx:254
src/components/route-trace/LotCreateForm.tsx:265
src/components/route-trace/LotCreateForm.tsx:287
src/components/route-trace/LotCreateModal.tsx:121
src/components/route-trace/LotCreateModal.tsx:150
src/components/route-trace/LotCreateModal.tsx:166
src/components/route-trace/LotCreateModal.tsx:183
src/components/route-trace/LotCreateModal.tsx:203
src/components/route-trace/LotCreateModal.tsx:220
src/components/route-trace/LotCreateModal.tsx:235
src/components/route-trace/LotCreateModal.tsx:258
src/components/route-trace/LotCreateModal.tsx:276
src/components/route-trace/LotCreateModal.tsx:307
src/components/route-trace/LotCreateModal.tsx:330
src/components/route-trace/LotCreateModal.tsx:345
src/components/route-trace/LotCreateModal.tsx:362
src/components/route-trace/LotCreateModal.tsx:378
src/components/route-trace/LotCreateModal.tsx:408
src/components/route-trace/LotDetailPanel.tsx:773
src/components/route-trace/LotDetailPanel.tsx:783
src/components/route-trace/LotDetailPanel.tsx:824
src/components/route-trace/LotDetailPanel.tsx:835
src/components/route-trace/LotDetailPanel.tsx:878
src/components/route-trace/LotDetailPanel.tsx:889
src/components/route-trace/LotDetailPanel.tsx:900
src/components/route-trace/LotListTable.tsx:215
src/components/route-trace/LotListTable.tsx:300-301
src/components/route-trace/LotListTable.tsx:314
src/components/route-trace/LotListTable.tsx:321-322
src/components/route-trace/QRScanModal.tsx:241
src/components/route-trace/QRScanModal.tsx:277
src/components/route-trace/QuickNewLotModal.tsx:85
src/components/route-trace/QuickNewLotModal.tsx:100
src/components/route-trace/QuickNewLotModal.tsx:112
src/components/route-trace/QuickNewLotModal.tsx:126
src/components/route-trace/QuickNewLotModal.tsx:138
src/components/route-trace/QuickNewLotModal.tsx:152
src/components/route-trace/RouteTraceDashboard.tsx:630
src/components/route-trace/RouteTraceSettings.tsx:69
src/components/route-trace/RouteTraceSettings.tsx:95
src/components/route-trace/RouteTraceSettings.tsx:121
src/components/route-trace/StickerPreviewModal.tsx:93
src/components/storefront/HeroSection.tsx:91
src/components/storefront/IndianRiverPaginatedStops.tsx:115
src/components/storefront/PaginatedStops.tsx:116
src/components/storefront/TuxedoVideoHero.tsx:173
src/components/storefront/ZipCodeSearch.tsx:51
src/components/time-tracking/TimeTrackingFieldClient.tsx:772
src/components/time-tracking/TimeTrackingFieldClient.tsx:787
src/components/time-tracking/TimeTrackingFieldClient.tsx:920
src/components/water/WaterAdminClient.tsx:495
src/components/water/WaterAdminClient.tsx:596
src/components/water/WaterAdminClient.tsx:678
src/components/water/WaterAdminClient.tsx:684
src/components/water/WaterAdminClient.tsx:733-738
src/components/water/WaterFieldClient.tsx:455
src/components/water/WaterFieldClient.tsx:606
src/components/water/WaterFieldClient.tsx:696
src/components/wholesale/DepositModal.tsx:59
src/components/wholesale/OrderDetailsModal.tsx:40
src/components/wholesale/admin/AddRecipientForm.tsx:29
src/components/wholesale/admin/AddRecipientForm.tsx:36
src/components/wholesale/admin/CustomerPricingPanel.tsx:55
src/components/wholesale/admin/CustomerPricingPanel.tsx:73
src/components/wholesale/admin/CustomerPricingPanel.tsx:87
src/components/wholesale/admin/CustomersTab.tsx:247
src/components/wholesale/admin/CustomersTab.tsx:371-372
src/components/wholesale/admin/CustomersTab.tsx:383
src/components/wholesale/admin/CustomersTab.tsx:403-404
src/components/wholesale/admin/CustomersTab.tsx:425
src/components/wholesale/admin/CustomersTab.tsx:433
src/components/wholesale/admin/OrdersTab.tsx:224
src/components/wholesale/admin/OrdersTab.tsx:229
src/components/wholesale/admin/OrdersTab.tsx:295-296
src/components/wholesale/admin/OrdersTab.tsx:314-315
src/components/wholesale/admin/OrdersTab.tsx:362
src/components/wholesale/admin/OrdersTab.tsx:374
src/components/wholesale/admin/OrdersTab.tsx:468
src/components/wholesale/admin/OrdersTab.tsx:502
src/components/wholesale/admin/PriceSheetModal.tsx:40
src/components/wholesale/admin/ProductsTab.tsx:155
src/components/wholesale/admin/ProductsTab.tsx:160
src/components/wholesale/admin/ProductsTab.tsx:167
src/components/wholesale/admin/ProductsTab.tsx:192
src/components/wholesale/admin/ProductsTab.tsx:224
src/components/wholesale/admin/SettingsTab.tsx:125
src/components/wholesale/admin/SettingsTab.tsx:138
src/components/wholesale/admin/SettingsTab.tsx:163
src/components/wholesale/admin/SettingsTab.tsx:174
src/components/wholesale/admin/SettingsTab.tsx:179
src/components/wholesale/admin/SettingsTab.tsx:184
src/components/wholesale/admin/SettingsTab.tsx:189
src/components/wholesale/admin/SettingsTab.tsx:194
src/components/wholesale/admin/SettingsTab.tsx:199
src/components/wholesale/admin/SettingsTab.tsx:204
src/components/wholesale/admin/SettingsTab.tsx:209
src/components/wholesale/admin/SettingsTab.tsx:214
src/components/wholesale/admin/SettingsTab.tsx:239
src/components/wholesale/admin/SettingsTab.tsx:249
src/components/wholesale/admin/SettingsTab.tsx:254
src/components/wholesale/admin/WebhookSettingsSection.tsx:108
src/components/wholesale/admin/WebhookSettingsSection.tsx:118
src/components/wholesale/admin/WebhookSettingsSection.tsx:131
⚠ Bugs: Data fetching inside an effect ×3
Learn more: https://react.doctor/docs/rules/react-doctor/no-fetch-in-effect
fetch() inside useEffect can race, double-fire, or leak.
Use a data-fetching layer or Server Component instead.
→ Use a data-fetching layer or Server Component so fetches
do not race, double-fire, or leak from `useEffect`.
src/app/logout/page.tsx:9
src/components/admin/AIProviderPanel.tsx:110
src/components/admin/AdvancedAIPanel.tsx:206
⚠ Accessibility: outline:none removes focus ring ×2
Learn more: https://react.doctor/docs/rules/react-doctor/no-outline-none
Your keyboard users can't tell where they are because
outline: none hides the focus ring, so style :focus-visible
instead, or add a box-shadow focus ring.
→ Style `:focus-visible { outline: 2px solid
var(--color-accent); outline-offset: 2px }` so the focus
ring shows for keyboard users but not mouse clicks.
src/components/admin/CommandPalette.tsx:344
src/components/admin/stops/StopsDatePicker.tsx:61
⚠ Performance: sort() to find min or max ×2
Learn more: https://react.doctor/docs/rules/react-doctor/js-min-max-loop
This is slow because array.sort()[0] sorts the whole list
just to grab the smallest or largest, so use
Math.min(...array) instead
→ Use `Math.min(...array)` or `Math.max(...array)` instead
of sorting the whole list just to read the first or last
item
src/actions/ai-import.ts:266
src/components/admin/HarvestReach/AnalyticsDashboard.tsx:223
⚠ Bugs: Plain anchor reloads internal Next.js links ×36
Learn more: https://react.doctor/docs/rules/react-doctor/nextjs-no-a-element
Plain <a> reloads the whole page for internal links, so
Next.js loses client-side navigation and prefetching.
→ `import Link from 'next/link'` for client-side
navigation, prefetching, and preserved scroll position
src/app/admin/communications/abandoned-carts/page.tsx:27-29
src/app/admin/communications/settings/page.tsx:24
src/app/admin/communications/welcome-sequence/page.tsx:26-28
src/app/admin/settings/ai/AIClient.tsx:1830-1832
src/app/admin/settings/billing/BillingClientPage.tsx:501
src/app/admin/settings/integrations/IntegrationsClientPage.tsx:413
src/app/brands/page.tsx:217-219
src/app/changelog/page.tsx:195
src/app/changelog/page.tsx:201
src/app/login/LoginClient.tsx:203
src/app/protected-example/page.tsx:36
src/app/protected-example/page.tsx:96
src/app/tuxedo/page.tsx:692
src/app/tuxedo/page.tsx:725
src/app/tuxedo/stops/TuxedoStopsList.tsx:75
src/app/waitlist/page.tsx:124-125
src/components/admin/CampaignListPanel.tsx:699
src/components/admin/DashboardClient.tsx:181
src/components/admin/DashboardHeader.tsx:36
src/components/admin/IntegrationsInner.tsx:311
src/components/admin/PaymentSettingsForm.tsx:319
src/components/admin/PaymentSettingsForm.tsx:382
src/components/admin/WaterLogAdminPanel.tsx:320
src/components/landing/LandingPageWrapper.tsx:85
src/components/landing/LandingPageWrapper.tsx:95
src/components/landing/LandingPageWrapper.tsx:175
src/components/landing/LandingPageWrapper.tsx:185
src/components/legal/CookieConsentBanner.tsx:167-169
src/components/notifications/InAppNotificationCenter.tsx:200
⚠ Bugs: Plain img ships unoptimized images ×6
Learn more: https://react.doctor/docs/rules/react-doctor/nextjs-no-img-element
Plain <img> ships unoptimized, oversized images.
→ Use `next/image` so users get optimized formats,
responsive srcsets, and lazy loading instead of oversized
image downloads.
src/app/admin/v2/products/page.tsx:192
src/app/admin/water-log/headgates/HeadgatesManager.tsx:328
src/app/admin/water-log/headgates/HeadgatesManager.tsx:521
src/app/indian-river-direct/about/page.tsx:116
src/app/tuxedo/about/AboutClient.tsx:112
src/components/route-trace/StickerPreviewModal.tsx:249
⚠ Bugs: Effect re-subscribes on a changing callback ×7
Learn more: https://react.doctor/docs/rules/react-doctor/prefer-use-effect-event
Your effect re-subscribes whenever "closeMobileMenu"
changes, even though it's only used inside
`addEventListener`.
→ Wrap the callback with `useEffectEvent(callback)` (React
19+) and call it inside the sub-handler. An Effect Event
always sees the latest props and state but isn't a
dependency, so the effect won't re-subscribe every time the
parent redraws. See
https://react.dev/reference/react/useEffectEvent
src/components/admin/AdminSidebar.tsx:219
src/components/admin/AnalyticsDashboard.tsx:405
src/components/admin/HarvestReach/MatchingCustomersPanel.tsx:72
src/components/admin/ProductFormModal.tsx:164
src/components/admin/UpgradePlanModal.tsx:119
src/components/route-trace/QRScanModal.tsx:179
src/components/storefront/QuickCartSheet.tsx:64
⚠ Bugs: Derived value copied into state ×34
Learn more: https://react.doctor/docs/rules/react-doctor/no-derived-state
Storing "dirty" in state when you can derive it from other
values costs an extra render.
→ Work out the value while rendering (or with useMemo if
it's expensive) instead of copying it into useState through
a useEffect. See
https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state
src/app/admin/settings/integrations/IntegrationsClientPage.tsx:127
src/components/admin/AddStopModal.tsx:52-59
src/components/admin/CommandPalette.tsx:226
src/components/admin/EditLocationModal.tsx:47-55
src/components/admin/EditStopModal.tsx:65-73
src/components/admin/PaymentSettingsForm.tsx:106
src/components/admin/ReportsDashboard.tsx:313
src/components/admin/SettingsSections.tsx:167
src/components/admin/ShippingSettingsForm.tsx:74
src/components/admin/TimeTrackingAdminPanel.tsx:200
src/components/admin/TimeTrackingSettingsClient.tsx:167
⚠ Bugs: Multiple setState calls in one effect ×11
Learn more: https://react.doctor/docs/rules/react-doctor/no-cascading-set-state
4 setState calls in one useEffect redraw your screen each
time they run together.
→ Combine related updates in `useReducer` so one effect
does not redraw the screen once per `setState` call.
src/app/cart/CartClient.tsx:46
src/app/wholesale/employee/page.tsx:44
src/components/admin/AddStopModal.tsx:49
src/components/admin/BrandSettingsForm.tsx:78
src/components/admin/EditLocationModal.tsx:44
src/components/admin/EditStopModal.tsx:55
src/components/admin/HarvestReach/MatchingCustomersPanel.tsx:53
src/components/admin/MessageCustomersSection.tsx:33
src/components/admin/ShippingSettingsForm.tsx:79
src/components/admin/StopDetailModal.tsx:37
src/components/route-trace/LotCreateModal.tsx:37
⚠ Bugs: preventDefault on a form or link ×3
Learn more: https://react.doctor/docs/rules/react-doctor/no-prevent-default
Your users can't submit this <form> without JavaScript
because onSubmit calls preventDefault(), so use a server
action like `<form action={serverAction}>` to make it work
either way.
→ Use `<form action>` where your framework supports it (it
works without JS), or use a `<button>` instead of an `<a>`
with preventDefault.
src/app/indian-river-direct/page.tsx:456
src/app/login/LoginClient.tsx:136
src/components/storefront/StorefrontFooter.tsx:53
⚠ Bugs: next/image fill image is missing sizes ×15
Learn more: https://react.doctor/docs/rules/react-doctor/nextjs-image-missing-sizes
next/image uses fill without sizes, so your users download
the largest image.
→ Add `sizes` matching your layout so `next/image` does not
assume the largest candidate and make users download
oversized images.
src/components/admin/ProductFormModal.tsx:367
src/components/admin/ProductTableClient.tsx:159
src/components/admin/ProductsClient.tsx:457
src/components/admin/ProductsClient.tsx:618
src/components/storefront/CinematicShowcase.tsx:69
src/components/storefront/ProductCard.tsx:202
src/components/storefront/StorefrontFooter.tsx:82
src/components/storefront/StorefrontFooter.tsx:86
src/components/storefront/StorefrontHeader.tsx:81
src/components/storefront/StorefrontHeader.tsx:85
src/components/storefront/TuxedoVideoHero.tsx:252
src/components/time-tracking/TimeTrackingFieldClient.tsx:388
src/components/time-tracking/TimeTrackingFieldClient.tsx:654
src/components/time-tracking/TimeTrackingFieldClient.tsx:910
src/components/water/WaterFieldClient.tsx:669
⚠ Accessibility: Anchor used as a button
Learn more: https://react.doctor/docs/rules/react-doctor/anchor-is-valid
Keyboard users can't reach this link because its `href`
goes nowhere (`#`, `javascript:`, or empty), so point it at
a real destination.
→ Give links a real destination. Use `<button>` for in-page
actions.
src/components/landing/LandingPageWrapper.tsx:29
⚠ Bugs: Live state pushed to parent via effect
Learn more: https://react.doctor/docs/rules/react-doctor/no-pass-live-state-to-parent
Pushing state up to a parent from a useEffect costs your
users an extra render.
→ Move the state up to the parent (or return it from the
hook), instead of handing it back up through a prop
callback in a useEffect. See
https://react.dev/learn/you-might-not-need-an-effect#notifying-parent-components-about-state-changes
src/components/route-trace/QRScanModal.tsx:172
⚠ Maintainability: deslop/unused-file ×114
Unused file is not reachable from any entry point, so it
adds maintenance surface without shipping any code.
→ Delete the file if it is truly unreachable, or import it
from an entry point.
db/schema/audit.ts
src/actions/admin/audit.ts
src/actions/integrations/credentials.ts
src/actions/locations.ts
src/actions/orders/create-admin-order.ts
src/actions/reports.ts
src/actions/route-trace/types.ts
src/actions/settings/features.ts
src/actions/shipping/settings.ts
src/actions/square-inventory.ts
src/actions/stops/get-stop-customers.ts
src/actions/stops/get-stop-details.ts
src/actions/stripe-connect.ts
src/app/admin/settings/ai/AIClient.tsx
src/app/admin/settings/billing/AddAddonButton.tsx
src/app/admin/settings/billing/AddPaymentMethodButton.tsx
src/app/admin/settings/billing/BillingClient.tsx
src/app/admin/settings/billing/BillingClientPage.tsx
src/app/admin/settings/billing/BillingCycleToggle.tsx
src/app/admin/settings/billing/PlanUpgradeButton.tsx
src/app/admin/settings/billing/RemoveAddonButton.tsx
src/app/admin/settings/billing/StripePortalButton.tsx
src/app/admin/settings/integrations/IntegrationsClient.tsx
src/app/admin/settings/integrations/IntegrationsClientPage.tsx
src/app/change-password/ChangePasswordForm.tsx
src/components/admin/AIProviderPanel.tsx
src/components/admin/AddLocationModal.tsx
src/components/admin/AddStopModal.tsx
src/components/admin/AdminHeader.tsx
src/components/admin/AdminOrdersPanel.tsx
src/components/admin/AdminStopsPanel.tsx
src/components/admin/AdvancedAIPanel.tsx
src/components/admin/AdvancedIntegrations.tsx
src/components/admin/AdvancedPayments.tsx
src/components/admin/AdvancedSettingsClient.tsx
src/components/admin/AdvancedShipping.tsx
src/components/admin/AdvancedSquareSync.tsx
src/components/admin/BrandFeatureCards.tsx
src/components/admin/CartHydration.tsx
src/components/admin/CommunicationsLoading.tsx
src/components/admin/CommunicationsNav.tsx
src/components/admin/DashboardHeader.tsx
src/components/admin/DashboardUpgradeButton.tsx
src/components/admin/EditLocationModal.tsx
src/components/admin/ElegantModal.tsx
src/components/admin/HarvestReach/HarvestReachNav.tsx
src/components/admin/IntegrationsInner.tsx
src/components/admin/LocationsTab.tsx
src/components/admin/OrderTableBody.tsx
src/components/admin/ProductAssignmentForm.tsx
src/components/admin/ProductEditForm.tsx
src/components/admin/ProductFilterBar.tsx
src/components/admin/ProductSyncBanner.tsx
src/components/admin/ProductTableBody.tsx
src/components/admin/ProductTableClient.tsx
src/components/admin/ReportsDashboard.tsx
src/components/admin/ShippingSettingsForm.tsx
src/components/admin/SquareSyncWidget.tsx
src/components/admin/SquareSyncWidgetWrapper.tsx
src/components/admin/StatsStrip.tsx
src/components/admin/StopDetailModal.tsx
src/components/admin/StopMessagingForm.tsx
src/components/admin/StopTableBody.tsx
src/components/admin/StopsCalendarClient.tsx
src/components/admin/StopsHeaderActions.tsx
src/components/admin/StopsLocationsTabs.tsx
src/components/admin/StopsViewClient.tsx
src/components/admin/TabSwitcher.tsx
src/components/admin/TaxDashboard.tsx
src/components/admin/TaxQuarterlySummary.tsx
src/components/admin/design-system/AdminModal.tsx
src/components/admin/design-system/ToastExport.ts
src/components/admin/shared/DataTable.tsx
src/components/admin/shared/FilterBar.tsx
src/components/admin/shared/PageHeader.tsx
src/components/admin/shared/StatusBadge.tsx
src/components/admin/shared/index.ts
src/components/admin/stops/StopsCalendar.tsx
src/components/admin/stops/StopsDashboardClient.tsx
src/components/admin/stops/StopsList.tsx
src/components/admin/stops/StopsLocations.tsx
src/components/admin/stops/types.ts
src/components/changelog/ChangelogFeed.tsx
src/components/landing/FeaturesAndStats.tsx
src/components/landing/TestimonialsAndCTA.tsx
src/components/notifications/InAppNotificationCenter.tsx
src/components/onboarding/OnboardingFlow.tsx
src/components/providers/AnalyticsProvider.tsx
src/components/providers/ErrorBoundary.tsx
src/components/referral/ReferralSystem.tsx
src/components/route-trace/LotCreateForm.tsx
src/components/route-trace/PublicTraceActions.tsx
src/components/shared/EmptyState.tsx
src/components/shared/LoadingSkeleton.tsx
src/components/shared/SectionHeader.tsx
src/components/shared/ThemeToggle.tsx
src/components/storefront/BreadcrumbNav.tsx
src/components/storefront/HeroSection.tsx
src/components/storefront/IndianRiverGrainOverlay.tsx
src/components/storefront/IndianRiverPaginatedStops.tsx
src/components/storefront/ZipCodeSearch.tsx
src/config/brands.ts
src/lib/auth-guards.ts
src/lib/billing.ts
src/lib/data-service.ts
src/lib/date-utils.ts
src/lib/error-handler.ts
src/lib/fedex-auth.ts
src/lib/reports-export.ts
src/lib/route-trace-data.ts
src/lib/route-trace-units.ts
src/lib/seo.ts
src/lib/svc-headers.ts
src/lib/validation.ts
⚠ Performance: Array lookup inside a loop ×11
Learn more: https://react.doctor/docs/rules/react-doctor/js-set-map-lookups
This scales poorly because `array.includes()` inside a loop
scans the whole list every time. Use a Set for
constant-time lookups.
→ Use a `Set` or `Map` when you check for the same items
over and over. `Array.includes`/`find` scans the whole list
each time
src/actions/ai-import.ts:261
src/actions/ai-import.ts:304
src/actions/shipping/fedex-rates.ts:291
src/actions/square-orders.ts:106
src/components/admin/ContactImportForm.tsx:232
src/lib/column-detector.ts:180
src/lib/column-detector.ts:228
src/lib/csv-parsers.ts:31
src/lib/csv-parsers.ts:63
src/lib/csv-parsers.ts:131
src/lib/csv-parsers.ts:220
⚠ Accessibility: Interaction on static element ×43
Learn more: https://react.doctor/docs/rules/react-doctor/no-static-element-interactions
Screen reader users can't tell this click handler is
interactive because it has no `role`, so add a `role` or
use a button or link.
→ Give clickable static elements a `role`, or use a button
or link.
src/app/admin/import/ImportCenterClient.tsx:178
src/app/admin/settings/integrations/IntegrationsClient.tsx:612
src/app/admin/water-log/headgates/HeadgatesManager.tsx:365
src/app/admin/water-log/headgates/HeadgatesManager.tsx:498
src/components/admin/AbandonedCartDashboard.tsx:194
src/components/admin/AdminOrdersPanel.tsx:448
src/components/admin/AdminStopsPanel.tsx:257
src/components/admin/AdminStopsPanel.tsx:283
src/components/admin/BrandSettingsForm.tsx:882
src/components/admin/CampaignListPanel.tsx:114
src/components/admin/ElegantModal.tsx:36
src/components/admin/GlassModal.tsx:48
src/components/admin/HarvestReach/SegmentEditModal.tsx:44
src/components/admin/HarvestReach/SegmentListSidebar.tsx:91
src/components/admin/NewProductForm.tsx:243
src/components/admin/ProductEditForm.tsx:256
src/components/admin/ProductFormModal.tsx:244
src/components/admin/ProductFormModal.tsx:322
src/components/admin/ProductTableBody.tsx:189
src/components/admin/ProductTableBody.tsx:206
src/components/admin/ProductTableClient.tsx:234
src/components/admin/ProductTableClient.tsx:251
src/components/admin/ProductsClient.tsx:536
src/components/admin/ScheduleImportModal.tsx:165
src/components/admin/StopTableClient.tsx:861
src/components/admin/StopTableClient.tsx:915
src/components/admin/TemplateEditForm.tsx:133
src/components/admin/UpgradePlanModal.tsx:149
src/components/admin/UsersPage.tsx:432
src/components/admin/UsersPage.tsx:511
src/components/admin/design-system/AdminActionMenu.tsx:51
src/components/admin/design-system/AdminModal.tsx:28
src/components/cart/CartRestoredToast.tsx:25
src/components/changelog/ChangelogFeed.tsx:79
src/components/landing/HeroSection.tsx:564
src/components/notifications/InAppNotificationCenter.tsx:174
src/components/onboarding/OnboardingFlow.tsx:288
src/components/route-trace/FsmaReportModal.tsx:232
src/components/route-trace/LotCreateModal.tsx:93
src/components/route-trace/StickerPreviewModal.tsx:86
src/components/wholesale/DepositModal.tsx:43
src/components/wholesale/OrderDetailsModal.tsx:32
src/components/wholesale/admin/PriceSheetModal.tsx:31
⚠ Accessibility: Role used instead of HTML tag ×21
Learn more: https://react.doctor/docs/rules/react-doctor/prefer-tag-over-role
Screen reader users get more reliable semantics from
`<output>` than `role="status"`, so use `<output>` instead.
→ Use the matching HTML element when one exists so browsers
and assistive tech get native semantics.
src/app/cart/CartClient.tsx:129
src/app/cart/CartClient.tsx:148
src/app/cart/CartClient.tsx:187
src/app/cart/CartClient.tsx:206
src/app/cart/CartClient.tsx:214
src/app/cart/CartClient.tsx:271
src/app/contact/ContactClientPage.tsx:121
src/app/pricing/PricingClientPage.tsx:388
src/components/admin/BrandSelector.tsx:144
src/components/admin/CommandPalette.tsx:262
src/components/admin/CommandPalette.tsx:356
src/components/admin/LoadingState.tsx:32
src/components/admin/OfflineBanner.tsx:35
src/components/admin/ProductFormModal.tsx:256
src/components/admin/ProductsClient.tsx:541
src/components/admin/StopsCalendarClient.tsx:323-325
src/components/admin/StopsCalendarClient.tsx:346
src/components/admin/WaterLogAdminPanel.tsx:909
src/components/admin/products/StockAdjustButton.tsx:79
src/components/transitions/RouteAnnouncer.tsx:30
⚠ Performance: Full Framer Motion import ×36
Learn more: https://react.doctor/docs/rules/react-doctor/use-lazy-motion
Importing "motion" ships about 30 kb of extra code and
slows page load. Use "m" with LazyMotion instead.
→ Use `import { LazyMotion, m } from "framer-motion"` with
`domAnimation` features. Saves about 30kb.
src/app/indian-river-direct/about/error.tsx:4
src/app/indian-river-direct/about/page.tsx:4
src/app/indian-river-direct/contact/ContactClientPage.tsx:4
src/app/indian-river-direct/contact/error.tsx:4
src/app/indian-river-direct/error.tsx:4
src/app/indian-river-direct/faq/FAQClientPage.tsx:5
src/app/indian-river-direct/faq/error.tsx:4
src/app/indian-river-direct/page.tsx:6
src/app/indian-river-direct/stops/[id]/error.tsx:4
src/app/indian-river-direct/stops/[id]/page.tsx:6
src/app/tuxedo/about/AboutClient.tsx:4
src/app/tuxedo/about/error.tsx:4
src/app/tuxedo/contact/error.tsx:4
src/app/tuxedo/error.tsx:4
src/app/tuxedo/faq/error.tsx:4
src/app/tuxedo/page.tsx:5
src/app/tuxedo/stops/[id]/error.tsx:4
src/components/about/CTASection.tsx:4
src/components/about/ContactSection.tsx:3
src/components/about/FamilyTimelineSection.tsx:3
src/components/about/IndianRiverCTASection.tsx:4
src/components/about/IndianRiverCitrusSection.tsx:3
src/components/about/IndianRiverContactSection.tsx:3
src/components/about/IndianRiverMissionSection.tsx:3
src/components/about/IndianRiverTeamSection.tsx:3
src/components/about/IndianRiverValuesSection.tsx:3
src/components/about/MissionSection.tsx:3
src/components/admin/TabSwitcher.tsx:3
src/components/landing/TestimonialsAndCTA.tsx:3
src/components/onboarding/OnboardingFlow.tsx:7
src/components/storefront/ProductCard.tsx:5
src/components/storefront/QuickCartSheet.tsx:5
src/components/storefront/StorefrontHeader.tsx:6
src/components/storefront/SweetCornProductPage.tsx:7
src/components/storefront/TuxedoVideoHero.tsx:5
src/components/storefront/WhyIndianRiverDirect.tsx:4
⚠ Bugs: Client fetch for server data
Learn more: https://react.doctor/docs/rules/react-doctor/nextjs-no-client-fetch-for-server-data
useEffect + fetch in a page/layout makes your users wait
through an extra round trip & loading spinner.
→ Remove 'use client' and fetch directly in the Server
Component. No API round-trip, and secrets stay on the
server.
src/app/logout/page.tsx:9
⚠ Bugs: All state reset on prop change
Learn more: https://react.doctor/docs/rules/react-doctor/no-reset-all-state-on-prop-change
Your users briefly see stale state when a prop changes
because this useEffect clears all state.
→ Pass the prop as `key` so React resets the component for
you when the prop changes, instead of clearing every state
value by hand in a useEffect. See
https://react.dev/learn/you-might-not-need-an-effect#resetting-all-state-when-a-prop-changes
src/components/route-trace/LotCreateModal.tsx:37
✖ Bugs: State synced to a prop inside an effect ×40
Learn more: https://react.doctor/docs/rules/react-doctor/no-adjust-state-on-prop-change
This effect adjusts state after a prop changes, so users
briefly see the stale value.
→ Adjust the state inline during render with a `prev`-prop
comparison (`if (prop !== prevProp) { setPrevProp(prop);
setX(...); }`), or refactor to remove the duplicated state.
Routing the adjustment through a useEffect forces an extra
render with a stale UI between the two commits. See
https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes
src/components/admin/AddStopModal.tsx:60-61
┌──────────────────────────────────────────────────────────────┐
│ 59 | setCutoffTime(duplicateFrom?.cutoff_time ?? ""… │
│ > 60 | setStatus("draft"); │
│ > 61 | setError(null); │
│ 62 | // Auto-focus city field for fast data entry │
└──────────────────────────────────────────────────────────────┘
src/components/admin/BrandSettingsForm.tsx:80
┌──────────────────────────────────────────────────────────────┐
│ 79 | if (!isPlatformAdmin) return; │
│ > 80 | setLoading(true); │
│ | ^ │
│ 81 | getBrandSettings(activeBrandId).then((result) =>… │
└──────────────────────────────────────────────────────────────┘
src/components/admin/EditLocationModal.tsx:56
┌──────────────────────────────────────────────────────────────┐
│ 55 | setNotes(location.notes ?? ""); │
│ > 56 | setError(null); │
│ | ^ │
│ 57 | } │
└──────────────────────────────────────────────────────────────┘
src/components/admin/EditStopModal.tsx:76-86
┌──────────────────────────────────────────────────────────────┐
│ 75 | // Add mode - reset form │
│ > 76 | setCity(""); │
│ > 77 | setStateField(""); │
│ > 78 | setLocation(""); │
│ > 79 | setDate(""); │
│ > 80 | setTime(""); │
│ > 81 | setAddress(""); │
│ > 82 | setZip(""); │
│ > 83 | setCutoffTime(""); │
│ > 84 | setStatus("draft"); │
│ > 85 | } │
│ > 86 | setError(null); │
│ 87 | requestAnimationFrame(() => cityRef.current?.f… │
└──────────────────────────────────────────────────────────────┘
src/components/admin/HarvestReach/MatchingCustomersPanel.tsx:55-59
┌──────────────────────────────────────────────────────────────┐
│ 54 | if (rules.filters.length === 0) { │
│ > 55 | setPreview(null); │
│ > 56 | setCustomerCount(undefined); │
│ > 57 | return; │
│ > 58 | } │
│ > 59 | setLoading(true); │
│ 60 | clearTimeout(timerRef.current); │
└──────────────────────────────────────────────────────────────┘
src/components/admin/ShippingSettingsForm.tsx:81-83
┌──────────────────────────────────────────────────────────────┐
│ 80 | if (!isPlatformAdmin) return; │
│ > 81 | setLoading(true); │
│ > 82 | setError(null); │
│ > 83 | setDirty(false); │
│ 84 | getShippingSettings(activeBrandId).then((result)… │
└──────────────────────────────────────────────────────────────┘
src/components/admin/StopDetailModal.tsx:39-40
┌──────────────────────────────────────────────────────────────┐
│ 38 | let cancelled = false; │
│ > 39 | setLoading(true); │
│ > 40 | setLoadError(null); │
│ 41 | getStopDetails(stopId) │
└──────────────────────────────────────────────────────────────┘
src/components/admin/UpgradePlanModal.tsx:94
┌──────────────────────────────────────────────────────────────┐
│ 93 | } else { │
│ > 94 | setIsVisible(false); │
│ | ^ │
│ 95 | } │
└──────────────────────────────────────────────────────────────┘
src/components/route-trace/LotCreateModal.tsx:39-55
┌──────────────────────────────────────────────────────────────┐
│ 38 | if (isOpen) { │
│ > 39 | setCropType(""); │
│ > 40 | setVariety(""); │
│ > 41 | setHarvestDate(new Date().toISOString().split(… │
│ > 42 | setFieldLocation(""); │
│ > 43 | setFieldBlock(""); │
│ > 44 | setWorkerName(""); │
│ > 45 | setPackerName(""); │
│ > 46 | setQuantityLbs(""); │
│ > 47 | setYieldEstimateLbs(""); │
│ > 48 | setYieldUnit("lbs"); │
│ > 49 | setCustomYieldUnit(""); │
│ > 50 | setBinId(""); │
│ > 51 | setContainerId(""); │
│ > 52 | setPallets(""); │
│ > 53 | setNotes(""); │
│ > 54 | setError(null); │
│ > 55 | setNotesOpen(false); │
│ 56 | } │
└──────────────────────────────────────────────────────────────┘
⚠ Accessibility: Text is too small ×2
Learn more: https://react.doctor/docs/rules/react-doctor/no-tiny-text
Your users strain to read 11px text, so use at least 12px
for body text, & 16px is best.
→ Use at least 12px for body text, and 16px is best. Small
text is hard to read, especially on phones.
src/components/admin/CommandPalette.tsx:441
src/components/admin/CommandPalette.tsx:462
⚠ Bugs: Button missing explicit type ×527
Learn more: https://react.doctor/docs/rules/react-doctor/button-has-type
Your users can submit the form by accident because a
`<button>` with no `type` defaults to submit.
→ Set an explicit button `type` so plain buttons do not
submit forms by accident: `type="button"`, `"submit"`, or
`"reset"`.
src/app/admin/error.tsx:63
src/app/admin/import/ImportCenterClient.tsx:204
src/app/admin/import/ImportCenterClient.tsx:212
src/app/admin/import/ImportCenterClient.tsx:268
src/app/admin/import/ImportCenterClient.tsx:367-370
src/app/admin/import/ImportCenterClient.tsx:397
src/app/admin/import/ImportCenterClient.tsx:409
src/app/admin/launch-checklist/page.tsx:111
src/app/admin/launch-checklist/page.tsx:162
src/app/admin/launch-checklist/page.tsx:190
src/app/admin/launch-checklist/page.tsx:211
src/app/admin/me/AdminMeClient.tsx:92
src/app/admin/products/import/page.tsx:96
src/app/admin/products/import/page.tsx:143
src/app/admin/products/import/page.tsx:172
src/app/admin/sales/import/page.tsx:116
src/app/admin/sales/import/page.tsx:139
src/app/admin/settings/ai/AIClient.tsx:99
src/app/admin/settings/ai/AIClient.tsx:116
src/app/admin/settings/ai/AIClient.tsx:125
src/app/admin/settings/ai/AIClient.tsx:197
src/app/admin/settings/ai/AIClient.tsx:374
src/app/admin/settings/ai/AIClient.tsx:424
src/app/admin/settings/ai/AIClient.tsx:528
src/app/admin/settings/ai/AIClient.tsx:611
src/app/admin/settings/ai/AIClient.tsx:729
src/app/admin/settings/ai/AIClient.tsx:766
src/app/admin/settings/ai/AIClient.tsx:884
src/app/admin/settings/ai/AIClient.tsx:908
src/app/admin/settings/ai/AIClient.tsx:918
src/app/admin/settings/ai/AIClient.tsx:957
src/app/admin/settings/ai/AIClient.tsx:966
src/app/admin/settings/ai/AIClient.tsx:1030
src/app/admin/settings/ai/AIClient.tsx:1121
src/app/admin/settings/ai/AIClient.tsx:1143
src/app/admin/settings/ai/AIClient.tsx:1244
src/app/admin/settings/ai/AIClient.tsx:1261
src/app/admin/settings/ai/AIClient.tsx:1404
src/app/admin/settings/ai/AIClient.tsx:1417
src/app/admin/settings/ai/AIClient.tsx:1485
src/app/admin/settings/ai/AIClient.tsx:1540
src/app/admin/settings/ai/AIClient.tsx:1638
src/app/admin/settings/ai/AIClient.tsx:1674
src/app/admin/settings/ai/AIClient.tsx:1683
src/app/admin/settings/ai/AIClient.tsx:1742
src/app/admin/settings/ai/AIClient.tsx:1773
src/app/admin/settings/ai/AIClient.tsx:1861
src/app/admin/settings/ai/AIClient.tsx:1870
src/app/admin/settings/billing/AddAddonButton.tsx:29
src/app/admin/settings/billing/AddPaymentMethodButton.tsx:27
src/app/admin/settings/billing/BillingClient.tsx:50
src/app/admin/settings/billing/BillingClientPage.tsx:227
src/app/admin/settings/billing/BillingClientPage.tsx:329
src/app/admin/settings/billing/BillingCycleToggle.tsx:21
src/app/admin/settings/billing/BillingCycleToggle.tsx:31
src/app/admin/settings/billing/PlanUpgradeButton.tsx:62
src/app/admin/settings/billing/RemoveAddonButton.tsx:28
src/app/admin/settings/billing/StripePortalButton.tsx:27
src/app/admin/settings/integrations/IntegrationsClient.tsx:399
src/app/admin/settings/integrations/IntegrationsClient.tsx:433
src/app/admin/settings/integrations/IntegrationsClient.tsx:440
src/app/admin/settings/integrations/IntegrationsClient.tsx:538
src/app/admin/settings/integrations/IntegrationsClient.tsx:616
src/app/admin/settings/integrations/IntegrationsClient.tsx:631
src/app/admin/water-log/headgates/HeadgatesManager.tsx:188
src/app/admin/water-log/headgates/HeadgatesManager.tsx:204
src/app/admin/water-log/headgates/HeadgatesManager.tsx:327
src/app/admin/water-log/headgates/HeadgatesManager.tsx:369
src/app/admin/water-log/headgates/HeadgatesManager.tsx:507
src/app/admin/water-log/headgates/HeadgatesManager.tsx:534
src/app/blog/page.tsx:224
src/app/cart/CartClient.tsx:116
src/app/cart/CartClient.tsx:159
src/app/cart/CartClient.tsx:223
src/app/cart/CartClient.tsx:234
src/app/cart/CartClient.tsx:272
src/app/cart/CartClient.tsx:278
src/app/cart/CartClient.tsx:289
src/app/cart/CartClient.tsx:329
src/app/cart/CartClient.tsx:339
src/app/cart/CartClient.tsx:364
src/app/contact/ContactClientPage.tsx:129
src/app/indian-river-direct/about/error.tsx:50
src/app/indian-river-direct/contact/ContactClientPage.tsx:150
src/app/indian-river-direct/contact/error.tsx:50
src/app/indian-river-direct/error.tsx:59
src/app/indian-river-direct/faq/FAQClientPage.tsx:90
src/app/indian-river-direct/faq/FAQClientPage.tsx:179
src/app/indian-river-direct/faq/error.tsx:50
src/app/indian-river-direct/page.tsx:392
src/app/indian-river-direct/page.tsx:462
src/app/indian-river-direct/stops/[id]/error.tsx:50
src/app/pricing/PricingClientPage.tsx:164
src/app/pricing/PricingClientPage.tsx:179
src/app/pricing/PricingClientPage.tsx:318
src/app/pricing/PricingClientPage.tsx:389
src/app/pricing/PricingClientPage.tsx:400
src/app/roadmap/page.tsx:146
src/app/roadmap/page.tsx:174
src/app/tuxedo/about/error.tsx:45
src/app/tuxedo/contact/ContactClientPage.tsx:119
src/app/tuxedo/contact/error.tsx:45
src/app/tuxedo/error.tsx:53
src/app/tuxedo/faq/FAQClientPage.tsx:109
src/app/tuxedo/faq/FAQClientPage.tsx:168
src/app/tuxedo/faq/error.tsx:45
src/app/tuxedo/stops/[id]/error.tsx:45
src/app/wholesale/employee/page.tsx:151
src/app/wholesale/employee/page.tsx:169
src/app/wholesale/employee/page.tsx:412
src/app/wholesale/employee/page.tsx:423
src/app/wholesale/employee/page.tsx:442
src/app/wholesale/employee/page.tsx:454
src/app/wholesale/employee/page.tsx:458
src/app/wholesale/employee/page.tsx:463
src/app/wholesale/portal/page.tsx:561
src/app/wholesale/portal/page.tsx:574
src/app/wholesale/portal/page.tsx:721
src/app/wholesale/portal/page.tsx:761
src/app/wholesale/portal/page.tsx:778
src/app/wholesale/portal/page.tsx:798
src/app/wholesale/portal/page.tsx:833
src/app/wholesale/portal/page.tsx:935
src/components/admin/AIProviderPanel.tsx:196
src/components/admin/AIProviderPanel.tsx:317
src/components/admin/AIProviderPanel.tsx:347
src/components/admin/AIProviderPanel.tsx:354
src/components/admin/AbandonedCartDashboard.tsx:100
src/components/admin/AbandonedCartDashboard.tsx:106
src/components/admin/AbandonedCartDashboard.tsx:114
src/components/admin/AbandonedCartDashboard.tsx:119
src/components/admin/AbandonedCartDashboard.tsx:170-173
src/components/admin/AbandonedCartDashboard.tsx:177
src/components/admin/AbandonedCartDashboard.tsx:203
src/components/admin/AdminHeader.tsx:153
src/components/admin/AdminHeader.tsx:208
src/components/admin/AdminOrdersPanel.tsx:416
src/components/admin/AdminOrdersPanel.tsx:466
src/components/admin/AdminOrdersPanel.tsx:531
src/components/admin/AdminOrdersPanel.tsx:561
src/components/admin/AdminOrdersPanel.tsx:873
src/components/admin/AdminStopsPanel.tsx:118
src/components/admin/AdminStopsPanel.tsx:127
src/components/admin/AdminStopsPanel.tsx:162
src/components/admin/AdminStopsPanel.tsx:246
src/components/admin/AdminStopsPanel.tsx:260
src/components/admin/AdminStopsPanel.tsx:271
src/components/admin/AdminStopsPanel.tsx:288
src/components/admin/AdminStopsPanel.tsx:294
src/components/admin/AdminStopsPanel.tsx:320
src/components/admin/AdminStopsPanel.tsx:330
src/components/admin/AdvancedAIPanel.tsx:356
src/components/admin/AdvancedAIPanel.tsx:500
src/components/admin/AdvancedAIPanel.tsx:555
src/components/admin/AdvancedAIPanel.tsx:562
src/components/admin/AdvancedIntegrations.tsx:259
src/components/admin/AdvancedIntegrations.tsx:266
src/components/admin/AdvancedIntegrations.tsx:354
src/components/admin/AdvancedIntegrations.tsx:361
src/components/admin/AdvancedPayments.tsx:349
src/components/admin/AdvancedPayments.tsx:364
src/components/admin/AdvancedPayments.tsx:377
src/components/admin/AdvancedPayments.tsx:389
src/components/admin/AdvancedSettingsClient.tsx:90
src/components/admin/AdvancedSettingsClient.tsx:201
src/components/admin/AdvancedSettingsClient.tsx:205
src/components/admin/AdvancedShipping.tsx:166
src/components/admin/AdvancedShipping.tsx:173
src/components/admin/AdvancedShipping.tsx:202
src/components/admin/AdvancedSquareSync.tsx:140
src/components/admin/AdvancedSquareSync.tsx:146
src/components/admin/AdvancedSquareSync.tsx:174
src/components/admin/AnalyticsDashboard.tsx:75
src/components/admin/AnalyticsDashboard.tsx:81
src/components/admin/AnalyticsDashboard.tsx:87
src/components/admin/AnalyticsDashboard.tsx:93
src/components/admin/AnalyticsDashboard.tsx:420
src/components/admin/AnalyticsDashboard.tsx:440
src/components/admin/AnalyticsDashboard.tsx:446
src/components/admin/BrandFeatureCards.tsx:114
src/components/admin/BrandFeatureCards.tsx:167
src/components/admin/BrandFeatureCards.tsx:173
src/components/admin/CampaignListPanel.tsx:132
src/components/admin/CampaignListPanel.tsx:180
src/components/admin/CampaignListPanel.tsx:186
src/components/admin/CampaignListPanel.tsx:236
src/components/admin/ContactImportForm.tsx:517
src/components/admin/ContactImportForm.tsx:659
src/components/admin/ContactImportForm.tsx:665
src/components/admin/ContactImportForm.tsx:716
src/components/admin/ContactListPanel.tsx:355
src/components/admin/ContactListPanel.tsx:386
src/components/admin/ContactListPanel.tsx:423
src/components/admin/ContactListPanel.tsx:431
src/components/admin/CreateUserModal.tsx:270
src/components/admin/CreateUserModal.tsx:294
src/components/admin/CreateUserModal.tsx:440
src/components/admin/CreateUserModal.tsx:447
src/components/admin/DashboardHeader.tsx:40
src/components/admin/DashboardUpgradeButton.tsx:21
src/components/admin/DriverPickupPanel.tsx:215
src/components/admin/DriverPickupPanel.tsx:357
src/components/admin/ElegantModal.tsx:68
src/components/admin/GlassModal.tsx:106
src/components/admin/HarvestReach/AnalyticsDashboard.tsx:244
src/components/admin/HarvestReach/CampaignComposerPage.tsx:205
src/components/admin/HarvestReach/CampaignComposerPage.tsx:708
src/components/admin/HarvestReach/SegmentBuilderPage.tsx:87
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:118
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:168
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:304
src/components/admin/HarvestReach/SegmentEditModal.tsx:66
src/components/admin/HarvestReach/SegmentListSidebar.tsx:105
src/components/admin/IntegrationsInner.tsx:263-264
src/components/admin/LocationsTab.tsx:209
src/components/admin/LocationsTab.tsx:333
src/components/admin/LocationsTab.tsx:342
src/components/admin/MessageCustomersSection.tsx:152
src/components/admin/MessageCustomersSection.tsx:175
src/components/admin/MessageLogPanel.tsx:453
src/components/admin/MessageLogPanel.tsx:463
src/components/admin/MessageLogPanel.tsx:476
src/components/admin/OrderEditForm.tsx:256
src/components/admin/OrderPickupAction.tsx:38
src/components/admin/ProductAssignmentForm.tsx:94
src/components/admin/ProductEditForm.tsx:210
src/components/admin/ProductEditForm.tsx:224
src/components/admin/ProductEditForm.tsx:317
src/components/admin/ProductFilterBar.tsx:34
src/components/admin/ProductSyncBanner.tsx:46
src/components/admin/ProductTableBody.tsx:81
src/components/admin/ProductTableBody.tsx:101
src/components/admin/ProductTableBody.tsx:175
src/components/admin/ProductTableBody.tsx:194
src/components/admin/ProductTableBody.tsx:220
src/components/admin/ProductTableBody.tsx:226
src/components/admin/ProductTableClient.tsx:64
src/components/admin/ProductTableClient.tsx:84
src/components/admin/ProductTableClient.tsx:220
src/components/admin/ProductTableClient.tsx:239
src/components/admin/ProductTableClient.tsx:264
src/components/admin/ProductTableClient.tsx:270
src/components/admin/ProductsClient.tsx:471
src/components/admin/ProductsClient.tsx:512
src/components/admin/ProductsClient.tsx:553
src/components/admin/ProductsClient.tsx:641
src/components/admin/ProductsClient.tsx:684
src/components/admin/ProductsClient.tsx:706
src/components/admin/ProductsClient.tsx:712
src/components/admin/ReportsDashboard.tsx:672
src/components/admin/ScheduleImportModal.tsx:146
src/components/admin/ScheduleImportModal.tsx:291
src/components/admin/ScheduleImportModal.tsx:311
src/components/admin/ScheduleImportModal.tsx:317
src/components/admin/ScheduleImportModal.tsx:356
src/components/admin/ScheduleImportModal.tsx:373
src/components/admin/SettingsClient.tsx:77
src/components/admin/SettingsClient.tsx:176
src/components/admin/SettingsSections.tsx:49
src/components/admin/SettingsSections.tsx:404
src/components/admin/SettingsSections.tsx:410
src/components/admin/SettingsSections.tsx:427
src/components/admin/SettingsSections.tsx:433
src/components/admin/SettingsSections.tsx:500
src/components/admin/SettingsSections.tsx:531-533
src/components/admin/SettingsSections.tsx:557
src/components/admin/SettingsSections.tsx:588-589
src/components/admin/SettingsSections.tsx:607
src/components/admin/SettingsSections.tsx:653
src/components/admin/SettingsSections.tsx:659
src/components/admin/SettingsSections.tsx:663
src/components/admin/SettingsSections.tsx:679
src/components/admin/SettingsSections.tsx:721
src/components/admin/SettingsSections.tsx:725
src/components/admin/ShippingFulfillmentPanel.tsx:178
src/components/admin/ShippingFulfillmentPanel.tsx:197
src/components/admin/SquareSyncWidget.tsx:169
src/components/admin/SquareSyncWidget.tsx:176
src/components/admin/SquareSyncWidget.tsx:183
src/components/admin/StopMessagingForm.tsx:97
src/components/admin/StopMessagingForm.tsx:129
src/components/admin/StopMessagingForm.tsx:151
src/components/admin/StopTableClient.tsx:378
src/components/admin/StopTableClient.tsx:400
src/components/admin/StopTableClient.tsx:845
src/components/admin/StopTableClient.tsx:871
src/components/admin/StopTableClient.tsx:896
src/components/admin/StopTableClient.tsx:1032
src/components/admin/StopTableClient.tsx:1055
src/components/admin/StopTableClient.tsx:1077
src/components/admin/StopTableClient.tsx:1083
src/components/admin/TemplateEditForm.tsx:151
src/components/admin/TemplateEditForm.tsx:199
src/components/admin/TemplateEditForm.tsx:205
src/components/admin/TemplateEditForm.tsx:246
src/components/admin/TemplateEditForm.tsx:612
src/components/admin/TemplateEditForm.tsx:622
src/components/admin/TimeTrackingAdminPanel.tsx:567
src/components/admin/TimeTrackingAdminPanel.tsx:619
src/components/admin/TimeTrackingSettingsClient.tsx:341
src/components/admin/TimeTrackingSettingsClient.tsx:349
src/components/admin/TimeTrackingSettingsClient.tsx:357
src/components/admin/TimeTrackingSettingsClient.tsx:365
src/components/admin/TimeTrackingSettingsClient.tsx:581
src/components/admin/TimeTrackingSettingsClient.tsx:591
src/components/admin/TimeTrackingSettingsClient.tsx:601
src/components/admin/TimeTrackingSettingsClient.tsx:626
src/components/admin/TimeTrackingSettingsClient.tsx:645
src/components/admin/TimeTrackingSettingsClient.tsx:670
src/components/admin/TimeTrackingSettingsClient.tsx:736
src/components/admin/ToastContainer.tsx:79
src/components/admin/ToastContainer.tsx:138
src/components/admin/UpgradePlanModal.tsx:166
src/components/admin/UpgradePlanModal.tsx:184
src/components/admin/UpgradePlanModal.tsx:194
src/components/admin/UpgradePlanModal.tsx:282
src/components/admin/UsersPage.tsx:345
src/components/admin/UsersPage.tsx:358
src/components/admin/UsersPage.tsx:370
src/components/admin/UsersPage.tsx:416
src/components/admin/UsersPage.tsx:423
src/components/admin/UsersPage.tsx:437
src/components/admin/UsersPage.tsx:443
src/components/admin/UsersPage.tsx:449
src/components/admin/UsersPage.tsx:456
src/components/admin/UsersPage.tsx:492
src/components/admin/UsersPage.tsx:498
src/components/admin/UsersPage.tsx:520
src/components/admin/UsersPage.tsx:646
src/components/admin/UsersPage.tsx:678
src/components/admin/UsersPage.tsx:684
src/components/admin/UsersPage.tsx:709
src/components/admin/UsersPage.tsx:716-719
src/components/admin/UsersPage.tsx:724
src/components/admin/WaterLogAdminPanel.tsx:574
src/components/admin/WaterLogAdminPanel.tsx:581
src/components/admin/WaterLogAdminPanel.tsx:588
src/components/admin/WaterLogAdminPanel.tsx:708
src/components/admin/WaterLogAdminPanel.tsx:715
src/components/admin/WaterLogAdminPanel.tsx:812
src/components/admin/WaterLogAdminPanel.tsx:890
src/components/admin/WaterLogAdminPanel.tsx:1046
src/components/admin/WaterUserEditForm.tsx:84
src/components/admin/WebhookLogsSection.tsx:69
src/components/admin/WebhookLogsSection.tsx:113
src/components/admin/WelcomeSequenceDashboard.tsx:80
src/components/admin/WelcomeSequenceDashboard.tsx:86
src/components/admin/WelcomeSequenceDashboard.tsx:94
src/components/admin/WelcomeSequenceDashboard.tsx:99
src/components/admin/WelcomeSequenceDashboard.tsx:157
src/components/admin/design-system/AdminActionMenu.tsx:40
src/components/admin/design-system/AdminActionMenu.tsx:54
src/components/admin/design-system/AdminActionMenu.tsx:102
src/components/admin/design-system/AdminButton.tsx:89
src/components/admin/design-system/AdminButton.tsx:129
src/components/admin/design-system/AdminDeleteConfirm.tsx:53
src/components/admin/design-system/AdminDeleteConfirm.tsx:59
src/components/admin/design-system/AdminFilterBar.tsx:65
src/components/admin/design-system/AdminFilterBar.tsx:90
src/components/admin/design-system/AdminFilterBar.tsx:102
src/components/admin/design-system/AdminFilterBar.tsx:122
src/components/admin/design-system/AdminModal.tsx:52
src/components/admin/design-system/AdminPagination.tsx:20
src/components/admin/design-system/AdminPagination.tsx:53
src/components/admin/design-system/AdminPagination.tsx:68
src/components/admin/design-system/AdminPagination.tsx:101
src/components/admin/design-system/AdminPagination.tsx:110
src/components/admin/shared/DataTable.tsx:100
src/components/admin/shared/DataTable.tsx:109
src/components/admin/shared/FilterBar.tsx:86
src/components/admin/stops/StopsCalendar.tsx:201
src/components/admin/stops/StopsCalendar.tsx:208
src/components/admin/stops/StopsCalendar.tsx:215
src/components/changelog/ChangelogFeed.tsx:102
src/components/landing/LandingPageWrapper.tsx:113
src/components/legal/CookieConsentBanner.tsx:111
src/components/legal/CookieConsentBanner.tsx:129
src/components/legal/CookieConsentBanner.tsx:143
src/components/legal/CookieConsentBanner.tsx:149
src/components/legal/CookieConsentBanner.tsx:173
src/components/legal/CookieConsentBanner.tsx:179
src/components/legal/CookieConsentBanner.tsx:185
src/components/notifications/InAppNotificationCenter.tsx:127
src/components/notifications/InAppNotificationCenter.tsx:149
src/components/notifications/ToastNotification.tsx:110
src/components/onboarding/OnboardingFlow.tsx:156
src/components/onboarding/OnboardingFlow.tsx:222
src/components/onboarding/OnboardingFlow.tsx:229
src/components/onboarding/OnboardingFlow.tsx:238
src/components/onboarding/OnboardingFlow.tsx:245
src/components/onboarding/OnboardingFlow.tsx:252
src/components/onboarding/OnboardingFlow.tsx:301-304
src/components/providers/ErrorBoundary.tsx:45
src/components/pwa/InstallPrompt.tsx:84
src/components/pwa/InstallPrompt.tsx:90
src/components/referral/ReferralSystem.tsx:91
src/components/referral/ReferralSystem.tsx:148
src/components/referral/ReferralSystem.tsx:157
src/components/referral/ReferralSystem.tsx:166
src/components/referral/ReferralSystem.tsx:176
src/components/route-trace/AdminLookupPage.tsx:71
src/components/route-trace/FsmaReportModal.tsx:224
src/components/route-trace/FsmaReportModal.tsx:281
src/components/route-trace/FsmaReportModal.tsx:318
src/components/route-trace/FsmaReportModal.tsx:572
src/components/route-trace/FsmaReportModal.tsx:578
src/components/route-trace/LotCreateModal.tsx:121
src/components/route-trace/LotDetailPanel.tsx:322
src/components/route-trace/LotDetailPanel.tsx:333
src/components/route-trace/LotDetailPanel.tsx:346
src/components/route-trace/LotDetailPanel.tsx:354
src/components/route-trace/LotDetailPanel.tsx:418
src/components/route-trace/LotDetailPanel.tsx:528
src/components/route-trace/LotDetailPanel.tsx:602
src/components/route-trace/LotListTable.tsx:185
src/components/route-trace/LotListTable.tsx:198
src/components/route-trace/LotListTable.tsx:223
src/components/route-trace/LotListTable.tsx:243
src/components/route-trace/LotListTable.tsx:250
src/components/route-trace/LotListTable.tsx:257
src/components/route-trace/LotListTable.tsx:264
src/components/route-trace/LotListTable.tsx:271
src/components/route-trace/LotListTable.tsx:278
src/components/route-trace/QRScanModal.tsx:200
src/components/route-trace/QRScanModal.tsx:212
src/components/route-trace/RouteTraceDashboard.tsx:596
src/components/route-trace/RouteTraceDashboard.tsx:602
src/components/route-trace/RouteTracePage.tsx:113
src/components/route-trace/ShareTraceButton.tsx:26
src/components/route-trace/StickerPreviewModal.tsx:93
src/components/route-trace/StickerPreviewModal.tsx:110
src/components/route-trace/StickerPreviewModal.tsx:133
src/components/route-trace/StickerPreviewModal.tsx:155
src/components/route-trace/StickerPreviewModal.tsx:287
src/components/route-trace/StickerPreviewModal.tsx:293
src/components/shared/ThemeToggle.tsx:20
src/components/storefront/CartToast.tsx:31
src/components/storefront/CartToast.tsx:46
src/components/storefront/HeroSection.tsx:66
src/components/storefront/HeroSection.tsx:74
src/components/storefront/HeroSection.tsx:119
src/components/storefront/HeroSection.tsx:127
src/components/storefront/HeroSection.tsx:159
src/components/storefront/HeroSection.tsx:167
src/components/storefront/IndianRiverPaginatedStops.tsx:94
src/components/storefront/IndianRiverPaginatedStops.tsx:158
src/components/storefront/IndianRiverPaginatedStops.tsx:165
src/components/storefront/PaginatedStops.tsx:95
src/components/storefront/PaginatedStops.tsx:159
src/components/storefront/PaginatedStops.tsx:166
src/components/storefront/ProductCard.tsx:99
src/components/storefront/ProductCard.tsx:112
src/components/storefront/ProductCard.tsx:126
src/components/storefront/QuickCartSheet.tsx:228
src/components/storefront/QuickCartSheet.tsx:369
src/components/storefront/StorefrontHeader.tsx:139
src/components/storefront/SweetCornProductPage.tsx:220
src/components/storefront/SweetCornProductPage.tsx:330
src/components/storefront/SweetCornProductPage.tsx:340
src/components/storefront/SweetCornProductPage.tsx:586
src/components/storefront/SweetCornProductPage.tsx:592
src/components/storefront/SweetCornProductPage.tsx:614
src/components/storefront/SweetCornProductPage.tsx:620
src/components/storefront/SweetCornProductPage.tsx:707
src/components/storefront/TuxedoVideoHero.tsx:336
src/components/storefront/TuxedoVideoHero.tsx:391
src/components/storefront/ZipCodeSearch.tsx:60
src/components/storefront/ZipCodeSearch.tsx:86
src/components/time-tracking/TimeTrackingFieldClient.tsx:392
src/components/time-tracking/TimeTrackingFieldClient.tsx:614
src/components/time-tracking/TimeTrackingFieldClient.tsx:620
src/components/time-tracking/TimeTrackingFieldClient.tsx:667
src/components/time-tracking/TimeTrackingFieldClient.tsx:675
src/components/time-tracking/TimeTrackingFieldClient.tsx:801
src/components/time-tracking/TimeTrackingFieldClient.tsx:809
src/components/time-tracking/TimeTrackingFieldClient.tsx:873
src/components/water/WaterAdminClient.tsx:247
src/components/water/WaterAdminClient.tsx:417
src/components/water/WaterAdminClient.tsx:484
src/components/water/WaterAdminClient.tsx:551
src/components/water/WaterAdminClient.tsx:571
src/components/water/WaterAdminClient.tsx:585
src/components/water/WaterAdminClient.tsx:653
src/components/water/WaterAdminClient.tsx:710
src/components/water/WaterFieldClient.tsx:380
src/components/water/WaterFieldClient.tsx:386
src/components/water/WaterFieldClient.tsx:403
src/components/water/WaterFieldClient.tsx:417
src/components/water/WaterFieldClient.tsx:423
src/components/water/WaterFieldClient.tsx:440
src/components/water/WaterFieldClient.tsx:505
src/components/wholesale/DepositModal.tsx:84
src/components/wholesale/DepositModal.tsx:91
src/components/wholesale/OrderDetailsModal.tsx:40
src/components/wholesale/OrderDetailsModal.tsx:135
src/components/wholesale/OrderDetailsModal.tsx:144
src/components/wholesale/OrderDetailsModal.tsx:159
src/components/wholesale/admin/CustomerPricingPanel.tsx:55
src/components/wholesale/admin/CustomersTab.tsx:425
src/components/wholesale/admin/CustomersTab.tsx:433
src/components/wholesale/admin/CustomersTab.tsx:445
src/components/wholesale/admin/CustomersTab.tsx:452
src/components/wholesale/admin/CustomersTab.tsx:469
src/components/wholesale/admin/OrdersTab.tsx:258
src/components/wholesale/admin/OrdersTab.tsx:374
src/components/wholesale/admin/OrdersTab.tsx:387
src/components/wholesale/admin/OrdersTab.tsx:395
src/components/wholesale/admin/OrdersTab.tsx:413
src/components/wholesale/admin/OrdersTab.tsx:431
src/components/wholesale/admin/OrdersTab.tsx:441
src/components/wholesale/admin/PriceSheetModal.tsx:40
src/components/wholesale/admin/ProductsTab.tsx:224
src/components/wholesale/admin/ProductsTab.tsx:236
src/components/wholesale/admin/ProductsTab.tsx:244
src/components/wholesale/admin/SettingsTab.tsx:125
src/components/wholesale/admin/SettingsTab.tsx:138
src/components/wholesale/admin/SettingsTab.tsx:163
src/components/wholesale/admin/SettingsTab.tsx:254
src/components/wholesale/admin/WebhookSettingsSection.tsx:108
⚠ Performance: State only used in handlers ×48
Learn more: https://react.doctor/docs/rules/react-doctor/rerender-state-only-in-handlers
Each update to "activeBrandName" redraws your component for
nothing because this useState is set but never shown on
screen.
→ Use useRef instead of useState when the value is only set
and never shown on screen. `ref.current = ...` updates it
without redrawing the component.
src/app/admin/import/ImportCenterClient.tsx:32
src/app/admin/settings/integrations/IntegrationsClient.tsx:472-473
src/app/indian-river-direct/contact/ContactClientPage.tsx:23
src/app/tuxedo/contact/ContactClientPage.tsx:22
src/app/tuxedo/page.tsx:435
src/app/wholesale/portal/page.tsx:239
src/components/admin/AdminStopsPanel.tsx:55
src/components/admin/CampaignListPanel.tsx:340
src/components/admin/ContactImportForm.tsx:92-95
src/components/admin/ContactImportForm.tsx:101
src/components/admin/HarvestReach/CampaignComposerPage.tsx:320
src/components/admin/HarvestReach/MatchingCustomersPanel.tsx:50
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:224-225
src/components/admin/NewProductForm.tsx:25
src/components/admin/OrderEditForm.tsx:62
src/components/admin/ProductEditForm.tsx:45
src/components/admin/ProductFormModal.tsx:109
src/components/admin/SettingsSections.tsx:88-89
src/components/admin/SettingsSections.tsx:97-98
src/components/admin/SettingsSections.tsx:108-111
src/components/admin/SquareSyncWidget.tsx:24
src/components/admin/StopEditForm.tsx:39
src/components/admin/StopMessagingForm.tsx:29
src/components/admin/TimeTrackingAdminPanel.tsx:170
src/components/admin/TimeTrackingSettingsClient.tsx:86
src/components/admin/TimeTrackingSettingsClient.tsx:94
src/components/admin/TimeTrackingSettingsClient.tsx:104-107
src/components/admin/UsersPage.tsx:126
src/components/admin/orders/OrdersFilterButton.tsx:20
src/components/landing/FeaturesAndStats.tsx:173
src/components/landing/HeroSection.tsx:20
src/components/route-trace/QRScanModal.tsx:64
src/components/time-tracking/TimeTrackingFieldClient.tsx:177
src/components/water/WaterAdminClient.tsx:272
src/components/water/WaterFieldClient.tsx:148
src/components/wholesale/admin/OrdersTab.tsx:46
⚠ Performance: RegExp built inside a loop
Learn more: https://react.doctor/docs/rules/react-doctor/js-hoist-regexp
`new RegExp()` rebuilds the pattern on every loop pass.
Move it to a constant outside the loop.
→ Move `new RegExp(...)` (or large regex literals) to a
constant outside the loop so it isn't rebuilt on every pass
src/lib/excel-parser.ts:74
⚠ Bugs: Event logic handled in an effect ×35
Learn more: https://react.doctor/docs/rules/react-doctor/no-event-handler
Faking an event handler with state plus a useEffect costs
an extra render & runs late.
→ Run the side effect in the event handler that triggers
it, instead of watching its state from a useEffect. See
https://react.dev/learn/you-might-not-need-an-effect#sharing-logic-between-event-handlers
src/app/cart/CartClient.tsx:47
src/app/checkout/CheckoutClient.tsx:45
src/components/admin/AIProviderPanel.tsx:113
src/components/admin/AddStopModal.tsx:50
src/components/admin/AdvancedAIPanel.tsx:209
src/components/admin/CommandPalette.tsx:216
src/components/admin/EditLocationModal.tsx:46
src/components/admin/EditStopModal.tsx:56
src/components/admin/PaymentSettingsForm.tsx:73
src/components/admin/ProductsClient.tsx:397
src/components/admin/ShippingSettingsForm.tsx:65
src/components/admin/SquareSyncWidget.tsx:35
src/components/admin/TaxDashboard.tsx:139
src/components/admin/TaxDashboard.tsx:196
src/components/admin/TaxDashboard.tsx:205
src/components/admin/orders/OrdersFilterButton.tsx:25
src/components/notifications/InAppNotificationCenter.tsx:48
src/components/route-trace/FsmaReportModal.tsx:181
src/components/route-trace/LotCreateModal.tsx:38
src/components/storefront/BrandStylesProvider.tsx:19-22
src/components/water/WaterFieldClient.tsx:162
⚠ Bugs: State updates chained through effects ×10
Learn more: https://react.doctor/docs/rules/react-doctor/no-chain-state-updates
Chaining state updates triggers an extra render each step.
→ Set all the related state together in the event handler
that starts it, instead of having one useEffect react to a
state change and set more state. See
https://react.dev/learn/you-might-not-need-an-effect#chains-of-computations
src/app/cart/CartClient.tsx:49
src/components/admin/AbandonedCartDashboard.tsx:61
src/components/admin/BrandSettingsForm.tsx:80
src/components/admin/CommandPalette.tsx:220
src/components/admin/CommandPalette.tsx:228
src/components/admin/ShippingSettingsForm.tsx:81-83
src/components/admin/TimeTrackingAdminPanel.tsx:201
src/components/admin/WelcomeSequenceDashboard.tsx:50
⚠ Performance: Empty default prop breaks memo ×16
Learn more: https://react.doctor/docs/rules/react-doctor/rerender-memo-with-default-value
This keeps redrawing children that compare props because
default prop value [] makes a brand new array every render,
so move it to a constant at the top of the file
→ Move it to the top of the file: `const EMPTY_ITEMS:
Item[] = []`, then use that as the default value
src/app/admin/import/ImportCenterClient.tsx:30
src/components/admin/AdminOrdersPanel.tsx:100
src/components/admin/BrandSettingsForm.tsx:28
src/components/admin/CommunicationsPage.tsx:96-99
src/components/admin/NewProductForm.tsx:17
src/components/admin/PaymentSettingsForm.tsx:34
src/components/admin/ProductFormModal.tsx:88
src/components/admin/ProductsClient.tsx:43
src/components/admin/ShippingSettingsForm.tsx:37
src/components/admin/shared/FilterBar.tsx:34-35
src/components/route-trace/LotDetailPanel.tsx:217
src/components/storefront/ZipCodeSearch.tsx:20
src/components/wholesale/admin/CustomersTab.tsx:31
⚠ Accessibility: Click handler missing keyboard handler ×46
Learn more: https://react.doctor/docs/rules/react-doctor/click-events-have-key-events
Keyboard users can't trigger this click handler because
there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or
`onKeyPress`.
→ Pair `onClick` with a key handler so keyboard users can
trigger it.
src/app/admin/import/ImportCenterClient.tsx:178
src/app/admin/settings/integrations/IntegrationsClient.tsx:612
src/app/admin/water-log/headgates/HeadgatesManager.tsx:365
src/app/admin/water-log/headgates/HeadgatesManager.tsx:498
src/components/admin/AbandonedCartDashboard.tsx:194
src/components/admin/AdminOrdersPanel.tsx:448
src/components/admin/AdminStopsPanel.tsx:257
src/components/admin/AdminStopsPanel.tsx:283
src/components/admin/BrandSettingsForm.tsx:882
src/components/admin/CampaignListPanel.tsx:114
src/components/admin/CommandPalette.tsx:261
src/components/admin/ElegantModal.tsx:36
src/components/admin/GlassModal.tsx:48
src/components/admin/HarvestReach/SegmentEditModal.tsx:44
src/components/admin/HarvestReach/SegmentListSidebar.tsx:91
src/components/admin/MoreSheet.tsx:79
src/components/admin/NewProductForm.tsx:243
src/components/admin/ProductEditForm.tsx:256
src/components/admin/ProductFormModal.tsx:244
src/components/admin/ProductFormModal.tsx:322
src/components/admin/ProductTableBody.tsx:189
src/components/admin/ProductTableBody.tsx:206
src/components/admin/ProductTableClient.tsx:234
src/components/admin/ProductTableClient.tsx:251
src/components/admin/ProductsClient.tsx:536
src/components/admin/ScheduleImportModal.tsx:165
src/components/admin/StopTableClient.tsx:861
src/components/admin/StopTableClient.tsx:915
src/components/admin/TemplateEditForm.tsx:133
src/components/admin/UpgradePlanModal.tsx:149
src/components/admin/UsersPage.tsx:432
src/components/admin/UsersPage.tsx:511
src/components/admin/design-system/AdminActionMenu.tsx:51
src/components/admin/design-system/AdminModal.tsx:28
src/components/admin/orders/OrdersFilterButton.tsx:70
src/components/cart/CartRestoredToast.tsx:25
src/components/changelog/ChangelogFeed.tsx:79
src/components/landing/HeroSection.tsx:564
src/components/notifications/InAppNotificationCenter.tsx:174
src/components/onboarding/OnboardingFlow.tsx:288
src/components/route-trace/FsmaReportModal.tsx:232
src/components/route-trace/LotCreateModal.tsx:93
src/components/route-trace/StickerPreviewModal.tsx:86
src/components/wholesale/DepositModal.tsx:43
src/components/wholesale/OrderDetailsModal.tsx:32
src/components/wholesale/admin/PriceSheetModal.tsx:31
⚠ Bugs: State initialized from a mount effect ×23
Learn more: https://react.doctor/docs/rules/react-doctor/no-initialize-state
Your users see an extra render with empty "mounted" because
a useEffect sets its starting value.
→ Pass the initial value directly to useState() instead of
setting it from a mount-only useEffect. For SSR hydration,
prefer useSyncExternalStore().
src/components/admin/OfflineBanner.tsx:12-13
src/components/admin/PaymentSettingsForm.tsx:80
src/components/admin/PaymentSettingsForm.tsx:85
src/components/admin/PullToRefresh.tsx:38
src/components/admin/SettingsClient.tsx:145
src/components/admin/TimeTrackingSettingsClient.tsx:81
src/components/notifications/ToastNotification.tsx:152
src/components/pwa/InstallPrompt.tsx:18
src/components/pwa/InstallPrompt.tsx:23
src/components/shared/ThemeToggle.tsx:11
src/components/storefront/TuxedoVideoHero.tsx:68
src/components/time-tracking/TimeTrackingFieldClient.tsx:195
src/components/ui/ScrollAnimations.tsx:40
src/components/water/WaterAdminClient.tsx:327
src/context/CartContext.tsx:107-108
src/context/CartContext.tsx:117-121
src/context/CartContext.tsx:129-131
src/context/CartContext.tsx:135
⚠ Performance: New function passed as a prop
Learn more: https://react.doctor/docs/rules/react-doctor/jsx-no-new-function-as-prop
This child redraws every render because the prop gets a
brand new function each time.
→ Wrap the callback in `useCallback` or move it outside the
component so memoized children do not redraw every render.
src/components/admin/ProductTableClient.tsx:116
✖ Bugs: Component defined inside another component
Learn more: https://react.doctor/docs/rules/react-doctor/no-nested-component-definition
Your users lose all state in "SortIcon" on every render
because it's defined inside "StopTableClient", so move it
out to the top of the file.
→ Move it to module scope or a separate file so React does
not recreate the component and erase its state on every
parent render.
src/components/admin/StopTableClient.tsx:259
┌──────────────────────────────────────────────────────────────┐
│ 258 | // Sort icon component │
│ > 259 | const SortIcon = ({ field }: { field: SortField }… │
│ | ^ │
│ 260 | <span className={`inline-flex ml-1.5 ${sortFiel… │
└──────────────────────────────────────────────────────────────┘
⚠ Performance: Import from a barrel file ×20
Learn more: https://react.doctor/docs/rules/react-doctor/no-barrel-import
Importing from an index file pulls in extra code. Import
directly from the source file instead.
→ Import from the direct path: `import { Button } from
'./components/Button'` instead of `./components`
db/client.ts:27
src/components/admin/AdminOrdersPanel.tsx:19
src/components/admin/BrandSettingsForm.tsx:14
src/components/admin/ContactListPanel.tsx:7
src/components/admin/HeadgateEditForm.tsx:6
src/components/admin/MessageLogPanel.tsx:7
src/components/admin/NewProductForm.tsx:9
src/components/admin/NewStopForm.tsx:7
src/components/admin/OrderEditForm.tsx:6
src/components/admin/OrderPaymentSection.tsx:8
src/components/admin/PaymentSettingsForm.tsx:8
src/components/admin/ProductEditForm.tsx:9
src/components/admin/SettingsSections.tsx:24
src/components/admin/ShippingFulfillmentPanel.tsx:9
src/components/admin/ShippingSettingsForm.tsx:11
src/components/admin/StopEditForm.tsx:6
src/components/admin/TimeTrackingAdminPanel.tsx:25
src/components/admin/WaterLogAdminPanel.tsx:51
src/components/admin/WaterLogEntryEditForm.tsx:6
src/components/admin/WaterUserEditForm.tsx:6
⚠ Performance: State initializer runs on every render ×5
Learn more: https://react.doctor/docs/rules/react-doctor/rerender-lazy-state-init
useState(buildFaqCategories()) re-runs buildFaqCategories()
on every render & throws the result away.
→ Wrap expensive initial state in an arrow function so the
initializer does not rerun and get thrown away on every
render.
src/app/indian-river-direct/faq/FAQClientPage.tsx:135
src/components/admin/OrderEditForm.tsx:74
src/components/admin/UsersPage.tsx:120
src/components/admin/stops/StopsCalendar.tsx:82
src/components/landing/FeaturesAndStats.tsx:174
⚠ Performance: useEffect setState flashes on mount
Learn more: https://react.doctor/docs/rules/react-doctor/rendering-hydration-no-flicker
This flashes for your users because useEffect(setState, [])
runs after the first paint, so use useSyncExternalStore, or
add suppressHydrationWarning
→ Use `useSyncExternalStore(subscribe, getSnapshot,
getServerSnapshot)` or add `suppressHydrationWarning` to
the element
src/components/time-tracking/TimeTrackingFieldClient.tsx:194
⚠ Maintainability: deslop/unused-export ×126
Unused export: `tenantStatusEnum` is exported but no module
imports it, so it expands the public surface and can
mislead callers about supported API.
→ Drop the `export` keyword (or remove the declaration) if
no other module uses this symbol.
db/schema/enums.ts:11
db/schema/enums.ts:20-29
db/schema/enums.ts:39
db/schema/enums.ts:48-54
db/schema/enums.ts:62-65
src/actions/admin/password.ts:16
src/actions/billing/stripe-checkout.ts:30
src/actions/communications/contacts.ts:197
src/actions/communications/contacts.ts:383
src/actions/communications/import-contacts.ts:170
src/actions/communications/segments.ts:109
src/actions/communications/segments.ts:163
src/actions/dashboard.ts:229
src/actions/email-automation/abandoned-cart.ts:246
src/actions/harvest-reach/campaigns.ts:77
src/actions/integrations/ai-providers.ts:195
src/actions/integrations/ai-providers.ts:207
src/actions/integrations/ai-providers.ts:227
src/actions/offline-dispatcher.ts:48
src/actions/orders.ts:137
src/actions/orders.ts:143
src/actions/orders.ts:149
src/actions/time-tracking/index.ts:159
src/actions/time-tracking/index.ts:172
src/actions/water-log/admin.ts:127
src/actions/water-log/admin.ts:1010-1011
src/actions/water-log/field.ts:281
src/actions/water-log/field.ts:315
src/actions/water-log/field.ts:375
src/actions/wholesale-auth.ts:37
src/actions/wholesale-register.ts:261
src/auth.config.ts:66
src/components/admin/BrandSettingsForm.tsx:827
src/components/admin/EditStopModal.tsx:437
src/components/admin/Toast.tsx:76
src/components/admin/ToastContainer.tsx:122
src/components/admin/command-palette-data.ts:389
src/components/admin/design-system/AdminActionMenu.tsx:85
src/components/admin/design-system/AdminBadge.tsx:159
src/components/admin/design-system/AdminBadge.tsx:177
src/components/admin/design-system/AdminCard.tsx:31
src/components/admin/design-system/AdminCard.tsx:44
src/components/admin/design-system/AdminDeleteConfirm.tsx:73
src/components/admin/design-system/AdminFilterTabs.tsx:141
src/components/admin/design-system/AdminFormElements.tsx:164
src/components/admin/design-system/AdminFormElements.tsx:191
src/components/admin/design-system/AdminFormElements.tsx:210
src/components/admin/design-system/AdminPagination.tsx:89
src/components/admin/design-system/AdminTable.tsx:76
src/components/admin/design-system/AdminToggle.tsx:70
src/components/admin/design-system/Skeleton.tsx:35
src/components/admin/design-system/Skeleton.tsx:50
src/components/admin/design-system/Skeleton.tsx:67
src/components/admin/design-system/Skeleton.tsx:81
src/components/admin/design-system/Skeleton.tsx:149
src/components/landing/LandingPageWrapper.tsx:12
src/components/landing/LandingPageWrapper.tsx:215
src/components/landing/LandingPageWrapper.tsx:420
src/components/landing/LandingPageWrapper.tsx:439
src/components/transitions/LoadingFade.tsx:39
src/components/ui/ScrollAnimations.tsx:28
src/components/ui/ScrollAnimations.tsx:249
src/lib/ai-provider-models.ts:5
src/lib/ai-provider-models.ts:40
src/lib/analytics.ts:8
src/lib/analytics.ts:121
src/lib/analytics.ts:127
src/lib/brand-scope.ts:22
src/lib/column-detector.ts:155
src/lib/column-detector.ts:214
src/lib/feature-flags.ts:117
src/lib/format-date.ts:31
src/lib/pricing.ts:134
src/lib/pricing.ts:171
src/lib/pricing.ts:176
src/lib/pricing.ts:181
src/lib/pricing.ts:185
src/lib/pricing.ts:189
src/lib/pricing.ts:193
src/lib/pricing.ts:197
src/lib/pricing.ts:201-202
src/lib/pwa.ts:34
src/lib/pwa.ts:40
src/lib/pwa.ts:49
src/lib/rate-limit.ts:79
src/lib/rate-limit.ts:89-91
src/lib/sentry.ts:65
src/lib/sentry.ts:73
src/lib/storage.ts:25
src/lib/storage.ts:51
src/lib/storage.ts:84
src/lib/storage.ts:101
src/lib/storage.ts:121
src/lib/stripe-billing.ts:36
src/lib/stripe-billing.ts:102
src/lib/stripe-billing.ts:171
src/lib/stripe-billing.ts:215
src/lib/stripe-billing.ts:259
src/lib/stripe-billing.ts:277
src/lib/stripe-billing.ts:287
src/lib/stripe-billing.ts:293
src/lib/stripe-billing.ts:317
src/lib/stripe-billing.ts:340
src/lib/stripe-billing.ts:344
src/lib/stripe-billing.ts:352
src/lib/stripe-billing.ts:359
src/lib/stripe-billing.ts:365
src/lib/stripe-billing.ts:377
src/lib/stripe-billing.ts:384
src/lib/stripe-billing.ts:388
src/lib/stripe-billing.ts:399
src/lib/stripe-billing.ts:416
src/lib/stripe-billing.ts:616
src/lib/stripe-billing.ts:643
src/lib/stripe-billing.ts:656
src/lib/stripe-billing.ts:660
src/lib/water-log-reporting.ts:38
⚠ Maintainability: Static value rebuilt every render ×35
Learn more: https://react.doctor/docs/rules/react-doctor/prefer-module-scope-static-value
`analysisLabels` inside `ImportCenterClient` uses no local
state but is rebuilt every render, so it looks new each
time & breaks memoized children. Move it to the top of the
file, outside the component.
→ Move the value above the component, at the top of the
file. It doesn't use local state, so rebuilding it each
update is wasted and makes it look new every time.
src/app/admin/import/ImportCenterClient.tsx:136
src/app/admin/settings/ai/AIClient.tsx:659
src/app/admin/settings/ai/AIClient.tsx:1195
src/app/admin/settings/integrations/IntegrationsClient.tsx:280
src/app/admin/v2/stops/page.tsx:246
src/app/api/reports/export/route.ts:77
src/app/api/time-tracking/export/route.ts:127
src/app/api/time-tracking/export/route.ts:169
src/app/api/time-tracking/export/route.ts:190
src/app/api/time-tracking/export/route.ts:231
src/app/api/water-logs/export/route.ts:46
src/app/brands/page.tsx:59
src/app/trace/[lotNumber]/page.tsx:80
src/app/wholesale/employee/page.tsx:17
src/components/admin/AddLocationModal.tsx:85
src/components/admin/CreateUserModal.tsx:162
src/components/admin/DashboardClient.tsx:159
src/components/admin/EditLocationModal.tsx:105
src/components/admin/TimeTrackingSettingsClient.tsx:306
src/components/admin/UsersPage.tsx:42
src/components/admin/WaterLogAdminPanel.tsx:999
src/components/admin/design-system/AdminActionMenu.tsx:95
src/components/admin/design-system/AdminFilterBar.tsx:36
src/components/admin/design-system/AdminFormElements.tsx:192
src/components/admin/design-system/AdminTable.tsx:77
src/components/admin/design-system/Skeleton.tsx:15
src/components/landing/LandingPageWrapper.tsx:15
src/components/landing/LandingPageWrapper.tsx:216
src/components/notifications/ToastNotification.tsx:50
src/components/notifications/ToastNotification.tsx:73
src/components/notifications/ToastNotification.tsx:80
src/components/onboarding/OnboardingFlow.tsx:276
src/components/route-trace/FsmaReportModal.tsx:147
src/components/storefront/ProductCard.tsx:39
src/components/wholesale/OrderDetailsModal.tsx:14
⚠ Accessibility: Custom modal instead of dialog ×6
Learn more: https://react.doctor/docs/rules/react-doctor/prefer-html-dialog
Keyboard users can tab out of this `role="dialog"` modal
because it has no built-in focus trapping, so use the
native `<dialog>`, which gives you focus trapping, `Escape`
to close, and the backdrop for free.
→ Replace the wrapper with `<dialog>` and open it with
`dialog.showModal()`. For the trigger, prefer `<button
commandfor="id" command="show-modal">` (Chrome 135+), or a
`useRef` with `dialogRef.current?.showModal()`.
src/app/cart/CartClient.tsx:206
src/components/admin/CommandPalette.tsx:262
src/components/admin/ProductFormModal.tsx:256
src/components/admin/ProductsClient.tsx:541
src/components/admin/StopsCalendarClient.tsx:613
src/components/admin/products/StockAdjustButton.tsx:79
⚠ Performance: transition: all animates everything ×4
Learn more: https://react.doctor/docs/rules/react-doctor/no-transition-all
This can stutter because transition: "all" animates every
property, even slow layout ones, so list only the
properties you actually change
→ List the specific properties: `transition: "opacity
200ms, transform 200ms"`. In Tailwind, use
`transition-colors`, `transition-opacity`, or
`transition-transform`
src/components/landing/HeroSection.tsx:344
src/components/landing/HeroSection.tsx:367
src/components/landing/TestimonialsAndCTA.tsx:483
src/components/landing/TestimonialsAndCTA.tsx:539
⚠ Performance: Intl formatter rebuilt each call ×6
Learn more: https://react.doctor/docs/rules/react-doctor/js-hoist-intl
This is slow because new Intl.NumberFormat() rebuilds on
every call inside a function, so move it to the top of the
file, or wrap it in useMemo
→ Move `new Intl.NumberFormat(...)` to the top of the file
or wrap it in `useMemo`. Building one is slow, so don't
redo it on every call
src/app/admin/orders/[id]/page.tsx:29
src/components/admin/AdminOrdersPanel.tsx:86
src/components/admin/DashboardClient.tsx:95
src/components/admin/OrderEditForm.tsx:54
src/components/admin/OrderPaymentSection.tsx:34
src/components/admin/ShippingFulfillmentPanel.tsx:77
⚠ Bugs: Data passed to parent via effect
Learn more: https://react.doctor/docs/rules/react-doctor/no-pass-data-to-parent
Handing data back to a parent from a useEffect costs your
users an extra render.
→ Fetch the data in the parent and pass it down as a prop
(or return it from the hook), instead of handing it back up
through a prop callback in a useEffect. See
https://react.dev/learn/you-might-not-need-an-effect#passing-data-to-the-parent
src/components/admin/ProductFormModal.tsx:148
⚠ Performance: Loading useState forces extra render ×4
Learn more: https://react.doctor/docs/rules/react-doctor/rendering-usetransition-loading
This adds an extra render because useState for "isLoading"
re-renders just for the loading flag, so if it's a state
change & not a data fetch, use useTransition instead
→ Replace with `const [isPending, startTransition] =
useTransition()`, which skips the extra render for the
loading flag
src/components/admin/AdminOrdersPanel.tsx:215
src/components/admin/LocationsTab.tsx:53
src/components/admin/StopTableClient.tsx:97
src/components/route-trace/QRScanModal.tsx:62
⚠ Maintainability: Format method on z.string() ×30
Learn more: https://react.doctor/docs/rules/react-doctor/zod-v4-prefer-top-level-string-formats
This `z.string().<format>()` check is deprecated in Zod 4,
so it can break during the upgrade.
→ Use the Zod 4 top-level format checks like `z.email()`,
`z.uuid()`, or `z.ipv4()` instead of
`z.string().<format>()`.
src/app/api/v1/campaigns/route.ts:28
src/app/api/v1/campaigns/route.ts:33-35
src/app/api/v1/products/route.ts:30
src/app/api/v1/referrals/route.ts:28-29
src/app/api/v1/reports/route.ts:27-29
src/app/api/v1/water-logs/route.ts:27-28
src/app/api/v1/water-logs/route.ts:34
src/lib/validation.ts:6-9
src/lib/validation.ts:19
src/lib/validation.ts:52-53
src/lib/validation.ts:105
src/lib/validation.ts:117
src/lib/validation.ts:126-127
src/lib/validation.ts:140
src/lib/validation.ts:149
src/lib/validation.ts:205
src/lib/validation.ts:218-219
src/lib/validation.ts:299-300
⚠ Accessibility: Handler on non-interactive element ×2
Learn more: https://react.doctor/docs/rules/react-doctor/no-noninteractive-element-interactions
Keyboard & screen reader users can't trigger this
`<dialog>` because it isn't interactive, so use a button or
link or add an interactive role.
→ Put interactions on a button or link, or add an
interactive role.
src/components/admin/MoreSheet.tsx:79
src/components/admin/orders/OrdersFilterButton.tsx:70
⚠ Accessibility: Autofocus on an element ×10
Learn more: https://react.doctor/docs/rules/react-doctor/no-autofocus
`autoFocus` moves focus on load, which can disrupt screen
reader and keyboard users. Remove it and let users choose
where to focus.
→ Do not use `autoFocus`. It disorients users on load.
src/app/admin/water-log/headgates/HeadgatesManager.tsx:228
src/app/change-password/ChangePasswordForm.tsx:95
src/app/change-password/page.tsx:86
src/app/water/admin/login/WaterAdminPinClient.tsx:51
src/components/admin/CampaignListPanel.tsx:158
src/components/admin/HarvestReach/SegmentEditModal.tsx:85
src/components/admin/TemplateEditForm.tsx:177
src/components/route-trace/QRScanModal.tsx:282
src/components/route-trace/QuickNewLotModal.tsx:91
src/components/water/WaterFieldClient.tsx:465
⚠ Performance: Large animated blur ×24
Learn more: https://react.doctor/docs/rules/react-doctor/no-large-animated-blur
Large animated blurs can use significant GPU memory on
phones because blur(48px) gets heavier as the blur and
element grow. Use a smaller blur or a smaller element.
→ Keep the blur under 10px, or blur a smaller element. Big
blurs use a lot more GPU memory as the element grows
src/app/error.tsx:33
src/app/error.tsx:37
src/app/login/LoginClient.tsx:92
src/app/login/LoginClient.tsx:96
src/app/not-found.tsx:22
src/app/not-found.tsx:26
src/app/tuxedo/page.tsx:829
src/app/tuxedo/page.tsx:835
src/components/admin/BrandFeatureCards.tsx:69
src/components/landing/HeroSection.tsx:662
src/components/landing/HeroSection.tsx:898
src/components/landing/HeroSection.tsx:903
src/components/landing/LandingPageWrapper.tsx:350
src/components/landing/LandingPageWrapper.tsx:359
src/components/landing/LandingPageWrapper.tsx:368
src/components/landing/TestimonialsAndCTA.tsx:473-474
src/components/landing/TestimonialsAndCTA.tsx:529-530
src/components/route-trace/FsmaReportModal.tsx:241
src/components/route-trace/FsmaReportModal.tsx:250-251
src/components/storefront/TuxedoVideoHero.tsx:214
src/components/storefront/TuxedoVideoHero.tsx:223
⚠ Accessibility: Redundant ARIA role ×2
Learn more: https://react.doctor/docs/rules/react-doctor/no-redundant-roles
Screen reader users gain nothing from this `role` because
`<ul>` already acts as a `list`, so remove it.
→ Remove redundant `role` attributes so assistive tech
reads the element's native semantics without extra noise.
src/components/admin/CardList.tsx:10
src/components/admin/SideNavGroup.tsx:44
⚠ Accessibility: Gray text on colored background ×4
Learn more: https://react.doctor/docs/rules/react-doctor/no-gray-on-colored-background
Your users see washed-out gray text (text-stone-400) on a
colored background (bg-red-50), so use white or a darker
shade of the background color.
→ Use white or near-white text, or a darker shade of the
background color. Gray text on colored backgrounds looks
washed out.
src/components/admin/ContactListPanel.tsx:355
src/components/admin/ContactListPanel.tsx:386
src/components/admin/ScheduleImportModal.tsx:291
src/components/water/WaterFieldClient.tsx:557
⚠ Performance: Spread copy before sort() ×5
Learn more: https://react.doctor/docs/rules/react-doctor/js-tosorted-immutable
This wastes work because [...array].sort() copies the array
just to sort it, so use array.toSorted() to sort without
the extra copy (ES2023)
→ Use `array.toSorted()` (ES2023) instead of
`[...array].sort()` so you sort without copying the array
first
src/components/admin/HarvestReach/AnalyticsDashboard.tsx:223
src/components/admin/StopTableClient.tsx:147
src/components/admin/StopsCalendarClient.tsx:594
src/components/admin/stops/StopsList.tsx:15
src/components/route-trace/RouteTraceDashboard.tsx:644
⚠ Maintainability: Large inline style object rebuilds every render ×16
Learn more: https://react.doctor/docs/rules/react-doctor/no-inline-exhaustive-style
This inline style has 11 properties, which is hard to read
& rebuilds every render. Move it to a CSS class, CSS
module, or styled component.
→ Move the styles to a CSS class, CSS module, Tailwind
utilities, or a styled component. Big inline objects are
hard to read and rebuild on every update.
src/components/admin/CommandPalette.tsx:266
src/components/admin/CommandPalette.tsx:301
src/components/admin/CommandPalette.tsx:340
src/components/admin/CommandPalette.tsx:396
src/components/landing/FeaturesAndStats.tsx:569
src/components/landing/FeaturesAndStats.tsx:620
src/components/landing/TestimonialsAndCTA.tsx:67
src/components/landing/TestimonialsAndCTA.tsx:81
src/components/landing/TestimonialsAndCTA.tsx:105
src/components/landing/TestimonialsAndCTA.tsx:191
src/components/landing/TestimonialsAndCTA.tsx:208
src/components/landing/TestimonialsAndCTA.tsx:334
src/components/landing/TestimonialsAndCTA.tsx:347
src/components/landing/TestimonialsAndCTA.tsx:406
src/components/landing/TestimonialsAndCTA.tsx:467
src/components/landing/TestimonialsAndCTA.tsx:523
⚠ Maintainability: Pure function rebuilt every render ×37
Learn more: https://react.doctor/docs/rules/react-doctor/prefer-module-scope-pure-function
`baseQuery` inside `AdminProductsPage` uses no local state
but is rebuilt on every render, so it wastes work & breaks
memoized children. Move it to the top of the file, outside
the component.
→ Move the function above the component, at the top of the
file. It doesn't use local state, so rebuilding it each
update is wasted work.
src/app/admin/products/page.tsx:64
src/app/admin/settings/ai/AIClient.tsx:689
src/app/admin/settings/ai/AIClient.tsx:842
src/app/admin/settings/ai/AIClient.tsx:1091
src/app/admin/settings/ai/AIClient.tsx:1378
src/app/admin/settings/ai/AIClient.tsx:1608
src/app/admin/water-log/headgates/HeadgatesManager.tsx:173
src/app/api/water-logs/export/route.ts:58
src/app/login/LoginClient.tsx:75
src/app/tuxedo/page.tsx:494-500
src/components/admin/AddLocationModal.tsx:91
src/components/admin/AddLocationModal.tsx:96
src/components/admin/AdminHeader.tsx:89
src/components/admin/AdminSidebar.tsx:266
src/components/admin/AnalyticsDashboard.tsx:150
src/components/admin/ContactImportForm.tsx:208
src/components/admin/DashboardClient.tsx:94
src/components/admin/DashboardClient.tsx:98
src/components/admin/EditLocationModal.tsx:110
src/components/admin/EditLocationModal.tsx:115
src/components/admin/NewProductForm.tsx:76
src/components/admin/ProductEditForm.tsx:90
src/components/admin/ProductsClient.tsx:78
src/components/admin/ReportsDashboard.tsx:300
src/components/admin/TaxDashboard.tsx:228
src/components/admin/TimeTrackingSettingsClient.tsx:225
src/components/admin/shared/DataTable.tsx:39
src/components/changelog/ChangelogFeed.tsx:69
src/components/landing/HeroSection.tsx:230
src/components/notifications/InAppNotificationCenter.tsx:97
src/components/notifications/InAppNotificationCenter.tsx:111
src/components/referral/ReferralSystem.tsx:55
src/components/water/WaterAdminClient.tsx:352
src/components/wholesale/admin/AddRecipientForm.tsx:10
src/context/CartContext.tsx:293
⚠ Maintainability: deslop/unused-dependency ×7
Unused dependency: `@gsap/react`
Unused dependency: `@upstash/ratelimit`
Unused dependency: `@upstash/redis`
Unused dependency: `next-auth`
Unused dependency: `posthog-js`
Unused dependency: `posthog-node`
Unused dependency: `square`
→ An unused dependency adds install time and supply-chain
surface without being used; remove it from package.json if
it is genuinely unused.
⚠ Maintainability: deslop/unused-dev-dependency ×2
Unused devDependency: `@lhci/cli`
Unused devDependency: `vite-tsconfig-paths`
→ An unused devDependency adds install time and
supply-chain surface without being used; remove it from
package.json if it is genuinely unused.
⚠ Maintainability: Large component is hard to read and change ×59
Learn more: https://react.doctor/docs/rules/react-doctor/no-giant-component
Component "ImportCenterClient" is 390 lines long, which is
hard to read & change. Split it into a few smaller
components.
→ Pull each section into its own component so the parent is
easier to read, test, and change.
src/app/admin/import/ImportCenterClient.tsx:30
src/app/admin/orders/[id]/page.tsx:32
src/app/admin/settings/billing/BillingClientPage.tsx:57
src/app/admin/settings/square-sync/SquareSyncSettingsClient.tsx:39
src/app/admin/water-log/headgates/HeadgatesManager.tsx:34
src/app/cart/CartClient.tsx:12
src/app/checkout/CheckoutClient.tsx:16
src/app/indian-river-direct/page.tsx:71
src/app/pricing/PricingClientPage.tsx:53
src/app/tuxedo/page.tsx:423
src/app/wholesale/portal/page.tsx:224
src/components/admin/AddStopModal.tsx:32
src/components/admin/AdminOrdersPanel.tsx:97
src/components/admin/AdminSidebar.tsx:139
src/components/admin/AdvancedAIPanel.tsx:187
src/components/admin/AdvancedIntegrations.tsx:58
src/components/admin/AdvancedPayments.tsx:112
src/components/admin/BrandSettingsForm.tsx:24
src/components/admin/CampaignListPanel.tsx:323
src/components/admin/CommandPalette.tsx:165
src/components/admin/ContactImportForm.tsx:88
src/components/admin/ContactListPanel.tsx:127
src/components/admin/CreateUserModal.tsx:62
src/components/admin/DashboardClient.tsx:75
src/components/admin/EditStopModal.tsx:38
src/components/admin/HarvestReach/CampaignComposerPage.tsx:311
src/components/admin/LocationsTab.tsx:45
src/components/admin/NewProductForm.tsx:17
src/components/admin/NewStopForm.tsx:26
src/components/admin/OrderEditForm.tsx:57
src/components/admin/PaymentSettingsForm.tsx:34
src/components/admin/ProductEditForm.tsx:33
src/components/admin/ProductFormModal.tsx:82
src/components/admin/ProductsClient.tsx:40
src/components/admin/ReportsDashboard.tsx:188
src/components/admin/ScheduleImportModal.tsx:18
src/components/admin/SettingsSections.tsx:85
src/components/admin/ShippingSettingsForm.tsx:34
src/components/admin/StopProductAssignment.tsx:43
src/components/admin/StopTableClient.tsx:89
src/components/admin/StopsCalendarClient.tsx:117
src/components/admin/TemplateEditForm.tsx:298
src/components/admin/TimeTrackingSettingsClient.tsx:75
src/components/admin/UsersPage.tsx:116
src/components/admin/WaterLogAdminPanel.tsx:69
src/components/admin/stops/StopsCalendar.tsx:74
src/components/landing/FeaturesAndStats.tsx:172
src/components/landing/HeroSection.tsx:16
src/components/landing/TestimonialsAndCTA.tsx:55
src/components/route-trace/FsmaReportModal.tsx:166
src/components/route-trace/LotCreateModal.tsx:14
src/components/route-trace/LotDetailPanel.tsx:214
src/components/storefront/SweetCornProductPage.tsx:37
src/components/storefront/TuxedoVideoHero.tsx:30
src/components/time-tracking/TimeTrackingFieldClient.tsx:164
src/components/water/WaterAdminClient.tsx:263
src/components/water/WaterFieldClient.tsx:120
src/components/wholesale/admin/CustomersTab.tsx:26
src/components/wholesale/admin/OrdersTab.tsx:29
⚠ Performance: Overly precise SVG path values ×2
Learn more: https://react.doctor/docs/rules/react-doctor/rendering-svg-precision
Your users download extra bytes for SVG d precision they
can't see, so round it to 1 or 2 decimals.
→ Round path, points, and transform decimals to 1 or 2
digits. The extra precision adds bytes with no visible
difference.
src/app/admin/launch-checklist/page.tsx:125
src/app/admin/launch-checklist/page.tsx:131
⚠ Performance: .map().filter(Boolean) loops twice ×8
Learn more: https://react.doctor/docs/rules/react-doctor/js-flatmap-filter
This loops over your list twice because
.map().filter(Boolean) makes two passes, so use .flatMap()
to change & drop items in one pass
→ Use `.flatMap(item => condition ? [value] : [])` to
change and drop items in one pass, instead of building a
throwaway array in between
src/app/admin/import/ImportCenterClient.tsx:308
src/components/admin/ContactImportForm.tsx:263
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:394
src/components/admin/HarvestReach/SegmentBuilderPanel.tsx:449
src/components/admin/StopsCalendarClient.tsx:155
src/components/admin/WaterLogAdminPanel.tsx:1184
src/components/admin/stops/StopsLocations.tsx:58
src/lib/column-detector.ts:255
⚠ Performance: Chained array iterations ×31
Learn more: https://react.doctor/docs/rules/react-doctor/js-combine-iterations
This loops over your list twice because .filter().map()
makes two passes, so do it in one pass with .reduce() or a
for...of loop
→ Combine `.map().filter()` style chains into one pass with
`.reduce()` or a `for...of` loop, so you only loop over the
list once
public/sw.js:31
src/actions/ai-import.ts:335
src/actions/ai-import.ts:380
src/actions/ai-import.ts:403
src/actions/ai-import.ts:423
src/actions/dashboard.ts:198
src/actions/tax.ts:67
src/app/admin/settings/ai/AIClient.tsx:811-814
src/app/admin/settings/ai/AIClient.tsx:1585
src/app/admin/settings/integrations/IntegrationsClientPage.tsx:440
src/app/admin/v2/stops/page.tsx:264
src/app/api/wholesale/price-sheet/route.ts:76
src/app/api/wholesale/price-sheet/route.ts:146
src/app/cart/CartClient.tsx:65-68
src/app/cart/CartClient.tsx:139
src/app/cart/CartClient.tsx:317
src/app/checkout/CheckoutClient.tsx:277
src/app/roadmap/page.tsx:230
src/components/admin/AdminSidebar.tsx:186
src/components/admin/BrandSettingsForm.tsx:804
src/components/admin/CommandPalette.tsx:126
src/components/admin/DashboardClient.tsx:166
src/components/admin/IntegrationsInner.tsx:318
src/components/admin/ShippingFulfillmentPanel.tsx:529
src/components/admin/WaterLogAdminPanel.tsx:260
src/components/admin/stops/StopsLocations.tsx:61
src/components/route-trace/LotDetailPanel.tsx:761
src/components/wholesale/admin/CustomersTab.tsx:251
src/context/CartContext.tsx:241
⚠ Bugs: Blocking side effect before response ×6
Learn more: https://react.doctor/docs/rules/react-doctor/server-after-nonblocking
console.log() runs before the response, so your users wait
longer for it.
→ `import { after } from 'next/server'`, then wrap it:
`after(() => analytics.track(...))`. The response sends
right away.
src/actions/admin/password.ts:46
src/actions/admin/reset-admin.ts:85
src/actions/admin/reset-admin.ts:108
src/actions/admin/users.ts:133
src/actions/admin/users.ts:495
src/actions/auth-actions.ts:11
⚠ Bugs: Unversioned localStorage key ×3
Learn more: https://react.doctor/docs/rules/react-doctor/client-localstorage-no-version
sessionStorage.setItem("pending_checkout",
JSON.stringify(...)) has no version, so changing the data
shape later crashes your users' saved sessions. Add one to
the key (e.g. "pending_checkout:v1").
→ Put a version in the storage key (e.g. "myKey:v1"). If
you change the data shape later, old saved data can be
ignored instead of crashing the app.
src/app/checkout/CheckoutClient.tsx:108
src/components/admin/WaterLogAdminPanel.tsx:301
src/components/storefront/StripeExpressCheckout.tsx:167
⚠ Performance: Transition duration too long ×18
Learn more: https://react.doctor/docs/rules/react-doctor/no-long-transition-duration
Your users wait through a sluggish 4000ms transition, so
keep UI transitions under 1000ms & save longer ones for big
page-load animations.
→ Keep UI transitions under 1s. Use about 100 to 150ms for
instant feedback, 200 to 300ms for state changes, and 300
to 500ms for layout. Save longer ones for big page-load
animations.
src/app/maintenance/page.tsx:8-9
src/components/landing/HeroSection.tsx:267
src/components/landing/HeroSection.tsx:272
src/components/landing/HeroSection.tsx:451
src/components/landing/HeroSection.tsx:465
src/components/landing/HeroSection.tsx:470
src/components/landing/HeroSection.tsx:478
src/components/landing/HeroSection.tsx:518
src/components/landing/HeroSection.tsx:532
src/components/landing/HeroSection.tsx:546
src/components/landing/HeroSection.tsx:576
src/components/route-trace/LotDetailPanel.tsx:686
src/components/route-trace/StatusBadge.tsx:68
src/components/storefront/TuxedoVideoHero.tsx:215
src/components/storefront/TuxedoVideoHero.tsx:224
src/components/storefront/TuxedoVideoHero.tsx:402
src/components/transitions/LoadingFade.tsx:31
⚠ Maintainability: React 19 API migration can break callers ×3
Learn more: https://react.doctor/docs/rules/react-doctor/no-react19-deprecated-apis
useContext is replaced by `use()` in React 19, which reads
context inside ifs & loops too, so switch to `import { use
} from 'react'`.
→ Pass `ref` as a normal prop on function components, since
`forwardRef` isn't needed in React 19. Replace
`useContext(X)` with `use(X)`. Only runs on React 19+
projects.
src/components/admin/Toast.tsx:3
src/components/admin/design-system/AdminSearchInput.tsx:3
src/context/CartContext.tsx:6
⚠ Bugs: Many related useState calls ×107
Learn more: https://react.doctor/docs/rules/react-doctor/prefer-useReducer
12 useState calls in "ImportCenterClient" can each trigger
a separate render.
→ Group related state in `useReducer` so one logical update
does not fan out into separate renders.
src/app/admin/import/ImportCenterClient.tsx:30
src/app/admin/me/AdminMeClient.tsx:11
src/app/admin/products/import/page.tsx:19
src/app/admin/sales/import/page.tsx:17
src/app/admin/settings/ai/AIClient.tsx:558
src/app/admin/settings/ai/AIClient.tsx:652
src/app/admin/settings/ai/AIClient.tsx:784
src/app/admin/settings/ai/AIClient.tsx:1045
src/app/admin/settings/ai/AIClient.tsx:1325
src/app/admin/settings/ai/AIClient.tsx:1557
src/app/admin/settings/integrations/IntegrationsClient.tsx:163
src/app/admin/settings/integrations/IntegrationsClient.tsx:466
src/app/admin/settings/integrations/IntegrationsClientPage.tsx:112
src/app/admin/settings/square-sync/SquareSyncSettingsClient.tsx:39
src/app/admin/water-log/headgates/HeadgatesManager.tsx:34
src/app/admin/water-log/settings/page.tsx:28
src/app/admin/wholesale/WholesaleClient.tsx:36
src/app/cart/CartClient.tsx:12
src/app/checkout/CheckoutClient.tsx:16
src/app/indian-river-direct/about/page.tsx:17
src/app/indian-river-direct/contact/ContactClientPage.tsx:22
src/app/indian-river-direct/page.tsx:71
src/app/login/LoginClient.tsx:14
src/app/reset-password/page.tsx:6
src/app/tuxedo/page.tsx:423
src/app/wholesale/employee/page.tsx:31
src/app/wholesale/login/page.tsx:52
src/app/wholesale/portal/page.tsx:224
src/app/wholesale/register/page.tsx:34
src/components/admin/AIProviderPanel.tsx:99
src/components/admin/AbandonedCartDashboard.tsx:33
src/components/admin/AddLocationModal.tsx:14
src/components/admin/AddStopModal.tsx:32
src/components/admin/AdminOrdersPanel.tsx:102
src/components/admin/AdminStopsPanel.tsx:50
src/components/admin/AdvancedAIPanel.tsx:187
src/components/admin/AdvancedIntegrations.tsx:58
src/components/admin/AdvancedPayments.tsx:112
src/components/admin/AdvancedShipping.tsx:46
src/components/admin/AdvancedSquareSync.tsx:32
src/components/admin/AnalyticsDashboard.tsx:338
src/components/admin/BrandSettingsForm.tsx:30
src/components/admin/CampaignListPanel.tsx:333
src/components/admin/CommunicationSettingsForm.tsx:40
src/components/admin/ContactImportForm.tsx:88
src/components/admin/ContactListPanel.tsx:135
src/components/admin/CreateUserModal.tsx:62
src/components/admin/DriverPickupPanel.tsx:76
src/components/admin/EditLocationModal.tsx:30
src/components/admin/EditStopModal.tsx:38
src/components/admin/HarvestReach/CampaignComposerPage.tsx:311
src/components/admin/HarvestReach/MatchingCustomersPanel.tsx:45
src/components/admin/HarvestReach/SegmentBuilderPage.tsx:97
src/components/admin/HeadgateEditForm.tsx:24
src/components/admin/IntegrationsInner.tsx:114
src/components/admin/LocationsTab.tsx:45
src/components/admin/MessageCustomersSection.tsx:19
src/components/admin/MessageLogPanel.tsx:219
src/components/admin/NewProductForm.tsx:17
src/components/admin/NewStopForm.tsx:26
src/components/admin/OrderEditForm.tsx:57
src/components/admin/OrderPaymentSection.tsx:45
src/components/admin/PaymentSettingsForm.tsx:34
src/components/admin/ProductEditForm.tsx:33
src/components/admin/ProductFormModal.tsx:93
src/components/admin/ProductsClient.tsx:50
src/components/admin/ReportsDashboard.tsx:198
src/components/admin/ScheduleImportModal.tsx:18
src/components/admin/SettingsSections.tsx:85
src/components/admin/ShippingFulfillmentPanel.tsx:92
src/components/admin/ShippingFulfillmentPanel.tsx:570
src/components/admin/ShippingSettingsForm.tsx:39
src/components/admin/SquareSyncWidget.tsx:12
src/components/admin/StopDetailModal.tsx:23
src/components/admin/StopEditForm.tsx:34
src/components/admin/StopMessagingForm.tsx:23
src/components/admin/StopProductAssignment.tsx:48
src/components/admin/StopTableClient.tsx:89
src/components/admin/TaxDashboard.tsx:135
src/components/admin/TemplateEditForm.tsx:306
src/components/admin/TimeTrackingAdminPanel.tsx:148
src/components/admin/TimeTrackingAdminPanel.tsx:758
src/components/admin/TimeTrackingAdminPanel.tsx:850
src/components/admin/TimeTrackingSettingsClient.tsx:75
src/components/admin/UsersPage.tsx:116
src/components/admin/WaterLogAdminPanel.tsx:75
src/components/admin/WaterLogEntryEditForm.tsx:30
src/components/admin/WaterUserEditForm.tsx:23
src/components/admin/WelcomeSequenceDashboard.tsx:24
src/components/marketing/WaitlistForm.tsx:10
src/components/route-trace/AdminLookupPage.tsx:41
src/components/route-trace/FsmaReportModal.tsx:166
src/components/route-trace/LotCreateForm.tsx:26
src/components/route-trace/LotCreateModal.tsx:14
src/components/route-trace/LotDetailPanel.tsx:222
src/components/route-trace/QRScanModal.tsx:57
src/components/route-trace/QuickNewLotModal.tsx:34
src/components/route-trace/StickerPreviewModal.tsx:36
src/components/storefront/StripeExpressCheckout.tsx:59
src/components/storefront/SweetCornProductPage.tsx:37
src/components/time-tracking/TimeTrackingFieldClient.tsx:174
src/components/water/WaterAdminClient.tsx:263
src/components/water/WaterFieldClient.tsx:120
src/components/wholesale/admin/CustomersTab.tsx:33
src/components/wholesale/admin/OrdersTab.tsx:29
src/components/wholesale/admin/ProductsTab.tsx:18
src/context/CartContext.tsx:66
⚠ Maintainability: Multiple components in one file ×9
Learn more: https://react.doctor/docs/rules/react-doctor/no-multi-comp
This file declares several components, so each component is
harder to find, test, and change.
→ Move secondary components into their own files so each
component stays easier to find, test, and change.
src/components/admin/design-system/AdminBadge.tsx:159
src/components/admin/design-system/AdminBadge.tsx:177
src/components/admin/design-system/AdminCard.tsx:31
src/components/admin/design-system/AdminCard.tsx:44
src/components/admin/design-system/AdminFilterTabs.tsx:141
src/components/admin/design-system/AdminFilterTabs.tsx:179
src/components/admin/design-system/AdminFilterTabs.tsx:185
src/components/admin/design-system/AdminFilterTabs.tsx:191
src/components/admin/design-system/AdminFilterTabs.tsx:197
⚠ Maintainability: Non-component export in component file
Learn more: https://react.doctor/docs/rules/react-doctor/only-export-components
This file exports non-components, so Fast Refresh can't
safely preserve component state.
→ Move non-component exports out of component files so Fast
Refresh can preserve component state instead of
full-reloading.
src/components/notifications/ToastNotification.tsx:128
⚠ Performance: Bouncy easing animation
Learn more: https://react.doctor/docs/rules/react-doctor/no-inline-bounce-easing
Your users see a dated, tacky animate-bounce, so use a
subtle ease-out transform for a smoother finish.
→ Use `cubic-bezier(0.16, 1, 0.3, 1)` (ease-out-expo) for a
natural finish. Real objects don't bounce.
src/components/storefront/TuxedoVideoHero.tsx:400
⚠ Performance: Animating scale from zero ×12
Learn more: https://react.doctor/docs/rules/react-doctor/no-scale-from-zero
This looks abrupt to your users because scale: 0 pops the
element in from a single point, so use scale: 0.95 with
opacity: 0 for a smoother entrance
→ Use `initial={{ scale: 0.95, opacity: 0 }}`. Elements
should gently shrink and fade, not vanish into a point
src/app/indian-river-direct/about/error.tsx:27
src/app/indian-river-direct/contact/error.tsx:27
src/app/indian-river-direct/error.tsx:29
src/app/indian-river-direct/faq/error.tsx:27
src/app/indian-river-direct/stops/[id]/error.tsx:27
src/app/tuxedo/about/error.tsx:22
src/app/tuxedo/contact/error.tsx:22
src/app/tuxedo/error.tsx:23
src/app/tuxedo/faq/error.tsx:22
src/app/tuxedo/stops/[id]/error.tsx:22
src/components/storefront/QuickCartSheet.tsx:210
src/components/storefront/StorefrontHeader.tsx:129
⚠ Security: Unescaped JSON in HTML or script sink ×6
Learn more: https://react.doctor/docs/rules/react-doctor/unsafe-json-in-html
JSON.stringify is embedded in HTML/script markup without
HTML-escaping; data containing `</script>` or `<` breaks
out and becomes XSS.
→ JSON.stringify does not HTML-escape, so a `</script>` (or
`<`) in the data breaks out and becomes XSS. Use an
HTML-safe serializer (serialize-javascript, devalue) or
escape `<`, `>`, and `&`, or pass data via a JSON `<script
type="application/json">` read with JSON.parse.
src/app/indian-river-direct/faq/layout.tsx:86
src/app/page.tsx:126
src/app/tuxedo/faq/layout.tsx:134-136
src/app/tuxedo/faq/layout.tsx:140
src/app/tuxedo/faq/layout.tsx:144
⚠ Security: Auth cookie missing HttpOnly protection ×3
Learn more: https://react.doctor/docs/rules/react-doctor/insecure-session-cookie
An auth/session cookie is exposed to JavaScript (set via
document.cookie, with httpOnly: false, or without cookie
options), letting an XSS payload steal it.
→ Set auth/session cookies server-side with `httpOnly:
true`, `secure: true`, and `sameSite`. Cookies set via
`document.cookie` or with `httpOnly: false` are readable by
any XSS payload and can be stolen.
src/app/login/LoginClient.tsx:77
src/app/wholesale/employee/page.tsx:83
src/app/wholesale/portal/page.tsx:561
⚠ Accessibility: Dialog without accessible name ×2
Learn more: https://react.doctor/docs/rules/react-doctor/dialog-has-accessible-name
This dialog has no accessible name, so screen readers
announce it as just “dialog.” Add `aria-label` or point
`aria-labelledby` at its heading.
→ Give every `<dialog>` / `role="dialog"` an accessible
name with `aria-label` or `aria-labelledby` (referencing
the dialog's title element).
src/components/admin/MoreSheet.tsx:79
src/components/admin/orders/OrdersFilterButton.tsx:70
────────────────────────────────────────────────────────────
All 2821 issues
Security 4 errors, 19 warnings
Bugs 145 errors, 1045 warnings
Performance 297 warnings
Accessibility 869 warnings
Maintainability 442 warnings
⚠ Migration-scale change: sample before you sweep
Button missing explicit type ×527 across 149 files
deslop/unused-file ×114 across 114 files
Control missing accessible label ×444 across 108 files
+5 more rules at this scale
Fixing all of them at once is hard to review and prone to
subtle mistakes across the whole repo. Fix a representative
few first and confirm the recipe holds. Then get the code
owner's sign-off before changing the rest.
Scope it down one area at a time: npx react-doctor@latest <path>
┌─────┐ 7 / 100 Critical
│ x x │ ████▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
│ ▽ │ React Doctor (https://react.doctor)
└─────┘
You could improve +6% by fixing the top 3 issues
Full diagnostics written to /tmp/react-doctor-c8e6f28b-e833-4dc5-9a50-41a9fefe65ba
────────────────────────────────────────────────────────────
Share: https://react.doctor/share?p=route-commerce-platform&s=7&e=149&w=2672&f=423
Tell others how you did on socials
Docs: https://react.doctor/docs
Learn more about fixing issues, setting up CI/CD, and
configuring rules with a config file
GitHub: https://github.com/millionco/react-doctor
Report issues and star the repository!