Common Topics - #1004
Closed
jshwrnr wants to merge 1 commit into
Closed
MacroscopeApp / Review for correctness
succeeded
Nov 24, 2025 in 5m 37s
4 issues identified (78 code objects reviewed).
• Merge Base:
7c6c783
• Head:480449f
Details
| ✅ | File Path | Comments Posted |
|---|---|---|
| ✅ | apps/web/app/(app)/[emailAccountId]/assistant/RulesSelect.tsx |
0 |
| ✅ | apps/web/app/(app)/[emailAccountId]/bulk-unsubscribe/BulkUnsubscribeDesktop.tsx |
0 |
| ✅ | apps/web/app/(app)/[emailAccountId]/bulk-unsubscribe/BulkUnsubscribeSection.tsx |
0 |
| ✅ | apps/web/app/(app)/[emailAccountId]/stats/ActionBar.tsx |
0 |
| ❌ | apps/web/app/(app)/[emailAccountId]/stats/BarListCard.tsx |
1 |
| ✅ | apps/web/app/(app)/[emailAccountId]/stats/DetailedStatsFilter.tsx |
0 |
| ✅ | apps/web/app/(app)/[emailAccountId]/stats/EmailActionsAnalytics.tsx |
0 |
| ✅ | apps/web/app/(app)/[emailAccountId]/stats/EmailAnalytics.tsx |
0 |
| ✅ | apps/web/app/(app)/[emailAccountId]/stats/LoadStatsButton.tsx |
0 |
| ✅ | apps/web/app/(app)/[emailAccountId]/stats/MainStatChart.tsx |
0 |
| ✅ | apps/web/app/(app)/[emailAccountId]/stats/NewBarChart.tsx |
0 |
| ✅ | apps/web/app/(app)/[emailAccountId]/stats/NewsletterModal.tsx |
0 |
| ✅ | apps/web/app/(app)/[emailAccountId]/stats/RuleStatsChart.tsx |
0 |
| ❌ | apps/web/app/(app)/[emailAccountId]/stats/Stats.tsx |
1 |
| ✅ | apps/web/app/(app)/[emailAccountId]/stats/StatsSummary.tsx |
0 |
| ✅ | apps/web/app/(app)/[emailAccountId]/stats/TopicDistribution.tsx |
0 |
| ✅ | apps/web/app/api/user/stats/by-period/route.ts |
0 |
| ✅ | apps/web/components/Banner.tsx |
0 |
| ❌ | apps/web/components/DatePickerWithRange.tsx |
1 |
| ✅ | apps/web/components/List.tsx |
0 |
| ✅ | apps/web/components/PageHeader.tsx |
0 |
| ✅ | apps/web/components/ProgressPanel.tsx |
0 |
| ✅ | apps/web/components/StatsCard.tsx |
0 |
| ✅ | apps/web/components/TabSelect.tsx |
0 |
| ✅ | apps/web/components/charts/DomainIcon.tsx |
0 |
| ✅ | apps/web/components/charts/HorizontalBarChart.tsx |
0 |
| ✅ | apps/web/components/new-landing/BrandScroller.tsx |
0 |
| ✅ | apps/web/components/new-landing/CallToAction.tsx |
0 |
| ✅ | apps/web/components/new-landing/FooterLineLogo.tsx |
0 |
| ✅ | apps/web/components/new-landing/HeaderLinks.tsx |
0 |
| ✅ | apps/web/components/new-landing/UnicornScene.tsx |
0 |
| ❌ | apps/web/components/new-landing/common/Anchor.tsx |
1 |
| ✅ | apps/web/components/new-landing/common/Badge.tsx |
0 |
| ✅ | apps/web/components/new-landing/common/BlurFade.tsx |
0 |
| ✅ | apps/web/components/new-landing/common/Button.tsx |
0 |
| ✅ | apps/web/components/new-landing/common/Card.tsx |
0 |
| ✅ | apps/web/components/new-landing/common/DisplayCard.tsx |
0 |
| ✅ | apps/web/components/new-landing/common/Section.tsx |
0 |
| ✅ | apps/web/components/new-landing/common/Typography.tsx |
0 |
| ✅ | apps/web/components/new-landing/common/WordReveal.tsx |
0 |
| ✅ | apps/web/components/new-landing/sections/Pricing.tsx |
0 |
| ✅ | apps/web/components/ui/button.tsx |
0 |
| ✅ | apps/web/components/ui/calendar.tsx |
0 |
| ✅ | apps/web/components/ui/progress.tsx |
0 |
| ✅ | apps/web/next.config.ts |
0 |
| ✅ | apps/web/utils/colors.ts |
0 |
Filtered Issues Details
apps/web/app/(app)/[emailAccountId]/bulk-unsubscribe/BulkUnsubscribeSection.tsx
- line 171:
readPercentageandarchivedPercentageare computed by dividing byitem.valuewithout guarding against0. Ifitem.valueis0, both(item.readEmails / item.value) * 100and((item.value - item.inboxEmails) / item.value) * 100will yieldInfinityorNaN, which then propagates to UI components (e.g., progress bars and percentage text), causing broken rendering and potentially invalidvalueprops. Add a zero-check and fall back to0or skip rendering whenitem.value === 0. [ Out of scope ] - line 201: When
sortColumn === "emails", the sorting iteratee returnsundefined, sosortBydoesn’t sort by the intended emails count. This yields an unsorted or implementation-defined order rather than sorting byitem.value. Useitem.value(the emails count) as the iteratee for the"emails"case. [ Out of scope ]
apps/web/app/(app)/[emailAccountId]/stats/DetailedStatsFilter.tsx
- line 36:
DetailedStatsFiltersetsDropdownMenuto a controlled open state whenkeepOpenOnSelectis true, but itsonOpenChangehandler ignores theopenargument and only ever setsisOpentotrueif it was previouslyfalse. This prevents normal close actions (e.g., pressing Escape or clicking the trigger again) from closing the menu; the menu can remain stuck open unlessonInteractOutsidefires. Use theopenparameter to set state (setIsOpen(open)) or otherwise allow close events when appropriate. [ Low confidence ]
apps/web/app/(app)/[emailAccountId]/stats/MainStatChart.tsx
- line 38:
format(date, "yyyy-MM-dd")is called ondateparsed fromitem.startOfPeriodwithout validating the parse result. Ifitem.startOfPerioddoes not match"MMM dd, yyyy",parse(...)yields an Invalid Date anddate-fnsformat(...)will throw aRangeError: Invalid time value. Add validation (e.g.,isValid(date)) or a fallback before formatting. [ Low confidence ]
apps/web/app/(app)/[emailAccountId]/stats/NewBarChart.tsx
- line 50: Date formatting in
NewBarChartassumesxAxisKeyvalues are valid dates.new Date(value)followed bytoLocaleDateString(...)will throwRangeError: Invalid time valueifvalueis missing or unparsable. This occurs in the default tick formatter and in the tooltip (new Date(data.payload[xAxisKey])). Add validation and fallback formatting, or guard against missing/invalidxAxisKeyvalues. [ Low confidence ] - line 74:
config[key].coloris accessed without existence checks while iteratingkeysderived fromdataKeys || Object.keys(config). IfdataKeysincludes a key not present inconfig, accessingconfig[key].colorwill throw at runtime. Guard withconfig[key] ? config[key].color : fallback, validatedataKeysagainstconfig, or derivekeyssolely fromconfig. Occurs in gradientstopColorand in<Bar color={...}>. [ Low confidence ]
apps/web/app/(app)/[emailAccountId]/stats/NewsletterModal.tsx
- line 59: Interactive element nesting: a
Button(renders as a native<button>whenasChildis false) contains an<a>anchor inside it (lines59-67). Nesting interactive controls is invalid HTML and can cause broken keyboard/focus behavior and inconsistent click handling. UseButtonwithasChildand render the anchor as the root (e.g.,<Button asChild><Link ... /></Button>) or make the anchor the primary element without wrapping it in a button. [ Out of scope ] - line 82: Provider-specific link mismatch: When
newsletter.autoArchivedis truthy, the UI always shows aView Skip Inbox Filterlink built withgetGmailFilterSettingsUrl(userEmail)(lines82-91). For non-Gmail providers (e.g., Outlook), this link is incorrect and misleading. Gate this by provider (show Gmail link only for Google accounts) or render the appropriate settings destination per provider. [ Out of scope ]
apps/web/app/api/user/stats/by-period/route.ts
- line 25:
getEmailStatsByPerioduses truthy checks forfromDateandtoDate(if (fromDate),if (toDate)), which will skip valid timestamp values of0(Unix epoch). SincestatsByWeekParamsallows any number viaz.coerce.number().nullish(),0is an explicitly valid input and should be included. Use explicit null/undefined checks (e.g.,if (fromDate != null)) to avoid dropping epoch filters. [ Out of scope ]
apps/web/components/DatePickerWithRange.tsx
- line 99: Selecting the "All" option (commonly represented with a
valueof"0") now setsdateRangeto{ from: subDays(now, 0), to: now }(i.e., a single day) instead of clearing the range. Previously, "All" cleared the date range (undefined). This breaks contract parity and produces inconsistent semantics: the label shows"All"while the selection is actually a one-day range. [ Low confidence ] - line 100: Selecting a non-numeric or malformed
valueinselectOptionswill passNaNtoNumber.parseInt(value), resulting insubDays(now, NaN)which yields an invalidDate. That invalidDateis set intodateRange({ from: Invalid Date, to: now }), and then passed to<Calendar selected={dateRange}>, risking runtime errors or inconsistent rendering in the date picker. [ Low confidence ]
apps/web/components/PageHeader.tsx
- line 28: The conditional
video && (video.youtubeVideoId || video.muxPlaybackId)relies on JavaScript truthiness to decide whether to render<WatchVideo />. This will render the button when eitheryoutubeVideoIdormuxPlaybackIdis a non-empty string, including whitespace-only values (e.g.," "), which are truthy but invalid IDs. That can cause downstream runtime errors or a broken player when<OnboardingDialogContent>receives an invalid ID. Use explicit validation (e.g., trim and non-empty checks) before rendering:const hasValidId = !!(video.youtubeVideoId?.trim() || video.muxPlaybackId?.trim());and guard onhasValidId. [ Low confidence ]
apps/web/components/ProgressPanel.tsx
- line 22:
progressis not clamped to the [0, 100] range. IfremainingItemsis greater thantotalItems,totalProcessedbecomes negative andprogressbecomes negative; ifremainingItemsis negative ortotalItemsis smaller thantotalProcessed,progresscan exceed 100. This yields incorrect UI and out-of-bounds indicator motion when passed to<Progress value={progress} />. Clampprogressto[0, 100]before rendering. [ Low confidence ] - line 50: Invalid HTML structure: a block-level
<div>is rendered inside an inline<span>(<span> ... <div className="flex items-center gap-1"> ... </div> ... </span>). This produces malformed markup and may lead to inconsistent layout/semantics across browsers and screen readers. Replace the outer<span>with a<div>or change the inner container to a<span>. [ Out of scope ]
apps/web/components/TabSelect.tsx
- line 65: When
hrefis provided,AsisLinkwhich renders an<a>element, and a<button>is rendered inside it. Nesting interactive elements (<button>inside<a>) violates accessibility and can cause unexpected click/keyboard behavior. Consider making the clickable element a single<a>styled as a button, or handling selection/navigation on one element only. [ Out of scope ] - line 68: When
hrefis falsy,Asbecomes adiv, buthrefandtargetprops are still applied (href={href ?? "#"}andtarget={target ?? undefined}), resulting in invalid DOM attributes on a<div>. This can produce console warnings and malformed semantics (a non-interactive element with link attributes). Guard these props so they are only passed whenAsisLink, or conditionally render attributes based onhrefpresence. [ Out of scope ] - line 69: Opening links in a new tab (
target="_blank") withoutrel="noopener noreferrer"is a security/performance issue (tabnabbing). Addrel="noopener noreferrer"whentarget="_blank". [ Out of scope ] - line 82: UI contract inconsistency: the external link icon (
ArrowUpRight) renders whentarget === "_blank"even ifhrefis absent (soAsis adiv). This suggests an external link but results in a non-link element withtargetattr incorrectly applied, confusing users and a11y. Gate the icon andtargetby the presence ofhref. [ Out of scope ]
apps/web/components/charts/DomainIcon.tsx
- line 6:
getFaviconattempts to derive the apex domain by removing only the first label when it detects a subdomain (lines6-10in code object0). For multi-level subdomains (e.g.,a.b.example.com), it returnsb.example.comrather thanexample.com. For special public suffixes (e.g.,example.co.ukwithwww), this heuristic can produce incorrect hosts. This yields incorrect favicon URLs and avoidableonErrorfallbacks. Consider using a proper public suffix list or leave the domain unchanged. [ Low confidence ]
apps/web/components/charts/HorizontalBarChart.tsx
- line 33: When
item.nameis an email missing the domain part (e.g.,"user@") or contains multiple@,domaincomputed inHorizontalBarChart(lines33-35in code object8) can be empty or invalid, leadingDomainIconto build a favicon URL withurl=http://(no host). This reliably triggers an error andonErrorfallback on each render. Add a guard to skip favicon fetch when the extracted domain is empty or invalid. [ Low confidence ] - line 56: Anchor tags opened in a new tab (
target="_blank") are rendered withoutrel="noopener noreferrer"(lines53-59in code object8). This exposes users to reverse tabnabbing and leaves the opener reference accessible to the new page. Addrel="noopener noreferrer"whentarget==="_blank". [ Low confidence ] - line 83:
HorizontalBarChartrenders a<button>container whenonItemClickis provided (lines83-91in code object8), butcontentmay contain an interactive<a>element whenitem.hrefis set (lines53-59in code object8). This nests an interactive control inside another interactive control, which is invalid HTML, can cause conflicting click/keyboard behaviors, and may lead to double-handling (both navigation andonItemClickfiring). Use either a single interactive element per item or conditionally avoid wrapping link content inside a button. [ Low confidence ] - line 84: React keys use
item.name(lines84and96in code object8). Ifdatacontains duplicate names, keys collide, causing incorrect reconciliation, stale UI state, or event handler mix-ups. Use a stable unique key (e.g., an ID or index combined with name) or validate uniqueness. [ Low confidence ]
apps/web/components/new-landing/BrandScroller.tsx
- line 23: The
classNameon line 23 contains an invalid Tailwind arbitrary style token:[gap:var(--gap))]has an extra)which prevents Tailwind from generating the rule. As a result, the intendedgap: var(--gap)will not apply, breaking spacing between items in the scroller. It should be[gap:var(--gap)]. [ Out of scope ] - line 33: Using
altas the Reactkeyfor brand items (line 33) is unsafe becausealtvalues are not guaranteed unique across a customizablebrandList. Duplicate keys cause React reconciliation issues (items may be merged or updated incorrectly), leading to visual glitches during animation. Use a guaranteed-unique identifier (e.g., an index scoped to the list or a stable uniqueid) or enforce uniqueness ofaltupstream. [ Low confidence ]
apps/web/components/new-landing/UnicornScene.tsx
- line 38: Potential TypeError on
UnicornStudio.init()if the CDN script respects an existing global and does not overwritewindow.UnicornStudiobecause it was set to the flag object earlier. In that case,UnicornStudiowould still refer to the flag object without aninitmethod, andscript.onloadwill attemptUnicornStudio.init()leading to a runtime error. [ Low confidence ]
apps/web/components/new-landing/common/Badge.tsx
- line 94: Using
icon || nullcan silently drop validReact.ReactNodevalues like the number0. React supports rendering numbers, but because0is falsy,icon || nullwill rendernullinstead of0. This causes unexpected loss of output when a numeric icon or count of0is passed. Prefer{icon}(React ignoresundefined/nullautomatically) or an explicit check foricon !== undefined && icon !== nullto avoid dropping0. [ Out of scope ]
apps/web/components/new-landing/sections/Pricing.tsx
- line 106: In
Pricing, theRadioGroup’sclassNamecontains an invalid RGBA alpha valuergba(0,0,0,0.0.07)within the arbitrary shadow class:shadow-[0_0_7px_0_rgba(0,0,0,0.0.07)]. CSS numbers cannot have multiple decimal points, so this likely results in the entirebox-shadowbeing ignored by the browser. Use a valid value likergba(0,0,0,0.07). [ Out of scope ]
apps/web/components/ui/progress.tsx
- line 23:
Progressdoes not defensively clampvalueto[0, 100]. When given out-of-range values (e.g., negative or >100), the computed transformtranslateX(-${100 - (value || 0)}%)can produce unexpected motion beyond the track. Clampvalueto[0, 100]before computing the transform to ensure consistent visuals. [ Low confidence ]
apps/web/next.config.ts
- line 237: Setting the
Access-Control-Allow-Originheader globally to a fixed valueenv.NEXT_PUBLIC_BASE_URLon all routes (source: '/(.*)') can cause CORS failures for requests originating from any other valid deployment domain (e.g., preview/staging URLs). When the browser origin doesn’t exactly matchenv.NEXT_PUBLIC_BASE_URL, cross-origin requests to Next.js API routes or assets will be blocked by CORS, breaking functionality in those environments. Consider reflecting the request’sOriginwhen appropriate or scoping CORS headers only to API endpoints that require them. [ Low confidence ]
Loading