Skip to content

Common Topics - #1004

Closed
jshwrnr wants to merge 1 commit into
elie222:mainfrom
jshwrnr:feat/common-topics
Closed

Common Topics#1004
jshwrnr wants to merge 1 commit into
elie222:mainfrom
jshwrnr:feat/common-topics

Add support for 'Common Topics' chart

480449f
Select commit
Loading
Failed to load commit list.
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: readPercentage and archivedPercentage are computed by dividing by item.value without guarding against 0. If item.value is 0, both (item.readEmails / item.value) * 100 and ((item.value - item.inboxEmails) / item.value) * 100 will yield Infinity or NaN, which then propagates to UI components (e.g., progress bars and percentage text), causing broken rendering and potentially invalid value props. Add a zero-check and fall back to 0 or skip rendering when item.value === 0. [ Out of scope ]
  • line 201: When sortColumn === "emails", the sorting iteratee returns undefined, so sortBy doesn’t sort by the intended emails count. This yields an unsorted or implementation-defined order rather than sorting by item.value. Use item.value (the emails count) as the iteratee for the "emails" case. [ Out of scope ]
apps/web/app/(app)/[emailAccountId]/stats/DetailedStatsFilter.tsx
  • line 36: DetailedStatsFilter sets DropdownMenu to a controlled open state when keepOpenOnSelect is true, but its onOpenChange handler ignores the open argument and only ever sets isOpen to true if it was previously false. This prevents normal close actions (e.g., pressing Escape or clicking the trigger again) from closing the menu; the menu can remain stuck open unless onInteractOutside fires. Use the open parameter 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 on date parsed from item.startOfPeriod without validating the parse result. If item.startOfPeriod does not match "MMM dd, yyyy", parse(...) yields an Invalid Date and date-fns format(...) will throw a RangeError: 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 NewBarChart assumes xAxisKey values are valid dates. new Date(value) followed by toLocaleDateString(...) will throw RangeError: Invalid time value if value is 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/invalid xAxisKey values. [ Low confidence ]
  • line 74: config[key].color is accessed without existence checks while iterating keys derived from dataKeys || Object.keys(config). If dataKeys includes a key not present in config, accessing config[key].color will throw at runtime. Guard with config[key] ? config[key].color : fallback, validate dataKeys against config, or derive keys solely from config. Occurs in gradient stopColor and 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> when asChild is false) contains an <a> anchor inside it (lines 59-67). Nesting interactive controls is invalid HTML and can cause broken keyboard/focus behavior and inconsistent click handling. Use Button with asChild and 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.autoArchived is truthy, the UI always shows a View Skip Inbox Filter link built with getGmailFilterSettingsUrl(userEmail) (lines 82-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: getEmailStatsByPeriod uses truthy checks for fromDate and toDate (if (fromDate), if (toDate)), which will skip valid timestamp values of 0 (Unix epoch). Since statsByWeekParams allows any number via z.coerce.number().nullish(), 0 is 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 value of "0") now sets dateRange to { 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 value in selectOptions will pass NaN to Number.parseInt(value), resulting in subDays(now, NaN) which yields an invalid Date. That invalid Date is set into dateRange ({ 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 either youtubeVideoId or muxPlaybackId is 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 on hasValidId. [ Low confidence ]
apps/web/components/ProgressPanel.tsx
  • line 22: progress is not clamped to the [0, 100] range. If remainingItems is greater than totalItems, totalProcessed becomes negative and progress becomes negative; if remainingItems is negative or totalItems is smaller than totalProcessed, progress can exceed 100. This yields incorrect UI and out-of-bounds indicator motion when passed to <Progress value={progress} />. Clamp progress to [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 href is provided, As is Link which 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 href is falsy, As becomes a div, but href and target props are still applied (href={href ?? "#"} and target={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 when As is Link, or conditionally render attributes based on href presence. [ Out of scope ]
  • line 69: Opening links in a new tab (target="_blank") without rel="noopener noreferrer" is a security/performance issue (tabnabbing). Add rel="noopener noreferrer" when target="_blank". [ Out of scope ]
  • line 82: UI contract inconsistency: the external link icon (ArrowUpRight) renders when target === "_blank" even if href is absent (so As is a div). This suggests an external link but results in a non-link element with target attr incorrectly applied, confusing users and a11y. Gate the icon and target by the presence of href. [ Out of scope ]
apps/web/components/charts/DomainIcon.tsx
  • line 6: getFavicon attempts to derive the apex domain by removing only the first label when it detects a subdomain (lines 6-10 in code object 0). For multi-level subdomains (e.g., a.b.example.com), it returns b.example.com rather than example.com. For special public suffixes (e.g., example.co.uk with www), this heuristic can produce incorrect hosts. This yields incorrect favicon URLs and avoidable onError fallbacks. Consider using a proper public suffix list or leave the domain unchanged. [ Low confidence ]
apps/web/components/charts/HorizontalBarChart.tsx
  • line 33: When item.name is an email missing the domain part (e.g., "user@") or contains multiple @, domain computed in HorizontalBarChart (lines 33-35 in code object 8) can be empty or invalid, leading DomainIcon to build a favicon URL with url=http:// (no host). This reliably triggers an error and onError fallback 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 without rel="noopener noreferrer" (lines 53-59 in code object 8). This exposes users to reverse tabnabbing and leaves the opener reference accessible to the new page. Add rel="noopener noreferrer" when target==="_blank". [ Low confidence ]
  • line 83: HorizontalBarChart renders a <button> container when onItemClick is provided (lines 83-91 in code object 8), but content may contain an interactive <a> element when item.href is set (lines 53-59 in code object 8). 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 and onItemClick firing). 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 (lines 84 and 96 in code object 8). If data contains 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 className on 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 intended gap: var(--gap) will not apply, breaking spacing between items in the scroller. It should be [gap:var(--gap)]. [ Out of scope ]
  • line 33: Using alt as the React key for brand items (line 33) is unsafe because alt values are not guaranteed unique across a customizable brandList. 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 unique id) or enforce uniqueness of alt upstream. [ 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 overwrite window.UnicornStudio because it was set to the flag object earlier. In that case, UnicornStudio would still refer to the flag object without an init method, and script.onload will attempt UnicornStudio.init() leading to a runtime error. [ Low confidence ]
apps/web/components/new-landing/common/Badge.tsx
  • line 94: Using icon || null can silently drop valid React.ReactNode values like the number 0. React supports rendering numbers, but because 0 is falsy, icon || null will render null instead of 0. This causes unexpected loss of output when a numeric icon or count of 0 is passed. Prefer {icon} (React ignores undefined/null automatically) or an explicit check for icon !== undefined && icon !== null to avoid dropping 0. [ Out of scope ]
apps/web/components/new-landing/sections/Pricing.tsx
  • line 106: In Pricing, the RadioGroup’s className contains an invalid RGBA alpha value rgba(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 entire box-shadow being ignored by the browser. Use a valid value like rgba(0,0,0,0.07). [ Out of scope ]
apps/web/components/ui/progress.tsx
  • line 23: Progress does not defensively clamp value to [0, 100]. When given out-of-range values (e.g., negative or >100), the computed transform translateX(-${100 - (value || 0)}%) can produce unexpected motion beyond the track. Clamp value to [0, 100] before computing the transform to ensure consistent visuals. [ Low confidence ]
apps/web/next.config.ts
  • line 237: Setting the Access-Control-Allow-Origin header globally to a fixed value env.NEXT_PUBLIC_BASE_URL on 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 match env.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’s Origin when appropriate or scoping CORS headers only to API endpoints that require them. [ Low confidence ]