Skip to content

Comments

Staging#929

Merged
nizzyabi merged 6 commits intomainfrom
staging
May 10, 2025
Merged

Staging#929
nizzyabi merged 6 commits intomainfrom
staging

Conversation

@nizzyabi
Copy link
Collaborator

@nizzyabi nizzyabi commented May 10, 2025

small pushed to prod

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive pricing page with Free and Pro plans, including plan comparison and upgrade functionality.
    • Added a responsive navigation bar with dropdown menus and mobile support.
    • Added a new "CircleX" icon and updated the "LinkedIn" icon appearance.
    • Dynamic onboarding steps now adapt based on user subscription status, with upgrade handling integrated.
    • English localization updated with a "Close Compose" shortcut label.
  • Improvements

    • Sidebar now only shows usage and upgrade prompts to non-Pro users.
    • Enhanced visual styling for navigation and color picker components.
    • Onboarding and sidebar logic improved for better user experience.
    • Removed "beta" label from the desktop navigation header.
    • Added a "Pricing" navigation menu item to the desktop navigation.
  • Style

    • Minor visual adjustments and formatting improvements across navigation and user menu components.

@vercel
Copy link

vercel bot commented May 10, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
0 ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 10, 2025 3:58pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented May 10, 2025

Walkthrough

This update introduces a new pricing page with interactive plan selection and upgrade logic, adds a responsive Navbar component for navigation, and dynamically adjusts onboarding steps and sidebar UI based on user subscription status. It also includes new and updated icon components, refines UI styling, updates localization strings, removes a UI label, and modifies server utility logic for connection handling.

Changes

File(s) Change Summary
apps/mail/app/(full-width)/pricing/page.tsx Added a new client-side React component for the pricing page, featuring Free and Pro plans, navigation, upgrade logic, and responsive design.
apps/mail/components/navbar.tsx Introduced a new responsive Navbar component with desktop and mobile navigation, dropdown menus, and action links.
apps/mail/components/icons/icons.tsx Added a new CircleX SVG icon component and updated the LinkedIn icon's SVG fill attribute to "white" and reformatted its JSX.
apps/mail/components/onboarding.tsx Refactored onboarding to generate steps dynamically based on Pro status, added upgrade logic, improved conditional rendering, and renamed the onboarding localStorage key.
apps/mail/components/ui/ai-sidebar.tsx Added logic to check for Pro subscription and conditionally render usage gauge and upgrade prompt in the sidebar.
apps/mail/components/ui/nav-main.tsx Reordered and enhanced CSS class attributes for improved styling in color picker and dropdown menu content; no logic changes.
apps/mail/components/ui/nav-user.tsx Removed the "pro" badge icon from the collapsed sidebar user dropdown and made minor formatting adjustments.
apps/mail/locales/en.json Added a new localization string: "closeCompose": "Close Compose" under keyboard shortcut actions.
apps/server/src/lib/server-utils.ts Updated active connection checks and simplified driver creation logic in server utilities; no API changes.
apps/mail/components/home/HomeContent.tsx Removed the "beta" label next to the "Zero Email" icon in the desktop navigation header and added a "Pricing" menu item in the desktop navigation menu.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant PricingPage
    participant useBilling
    participant useCustomer

    User->>PricingPage: Loads /pricing
    PricingPage->>useCustomer: Fetch customer data
    PricingPage->>useBilling: Prepare upgrade handler
    User->>PricingPage: Clicks "Upgrade" on Pro plan
    PricingPage->>useBilling: attach({ productId, redirectUrl })
    useBilling-->>PricingPage: Returns result or error
    PricingPage-->>User: Navigates or shows error
Loading
sequenceDiagram
    participant User
    participant OnboardingDialog
    participant useBilling
    participant useCustomer

    User->>OnboardingDialog: Opens onboarding
    OnboardingDialog->>useCustomer: Fetch customer data
    OnboardingDialog->>getSteps: Generate steps (isPro)
    User->>OnboardingDialog: Interacts with steps
    alt Step requires upgrade
        User->>OnboardingDialog: Clicks upgrade
        OnboardingDialog->>useBilling: attach({ productId, redirectUrl })
        useBilling-->>OnboardingDialog: Returns result or error
    end
    OnboardingDialog-->>User: Shows next step or completion
Loading

Suggested reviewers

  • ahmetskilinc
  • needleXO

Poem

In the meadow of code, where new features bloom,
A pricing page rises, dispelling the gloom.
The Navbar now guides with a hop and a cheer,
Onboarding adapts as Pro bunnies appear.
With icons refreshed and the sidebar anew,
This rabbit says, "Great work—hippity-hoo!"
🐇✨

Tip

⚡️ Faster reviews with caching
  • CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.

Enjoy the performance boost—your workflow just got faster.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26729e5 and ca3039a.

📒 Files selected for processing (2)
  • apps/mail/app/(full-width)/pricing/page.tsx (1 hunks)
  • apps/mail/components/home/HomeContent.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/mail/components/home/HomeContent.tsx
  • apps/mail/app/(full-width)/pricing/page.tsx
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (8)
apps/mail/components/ui/nav-user.tsx (1)

179-180: Minor formatting improvement

Added spacing for better code readability.

apps/mail/components/ui/ai-sidebar.tsx (1)

82-93: Pro subscription detection logic implemented.

The code now checks if the user has a Pro subscription by examining the customer's products array, looking for products with IDs or names containing "pro-example".

Consider using exact matching instead of includes() for more precise product identification:

- product.id.includes('pro-example') || product.name.includes('pro-example')
+ product.id === 'pro-example' || product.name === 'pro-example'

Also, it would be better to type product properly rather than using any:

- (product: any) =>
+ (product: { id: string; name: string }) =>
apps/mail/app/(full-width)/pricing/page.tsx (2)

25-69: Navigation link definitions for resources and company information.

The code defines arrays of navigation links for resources (GitHub, Twitter, etc.) and company information (About, Privacy, Terms).

These link arrays are duplicated in both pricing/page.tsx and navbar.tsx. Consider extracting them to a shared configuration file:

// New file: apps/mail/config/navigation-links.ts
export const resources = [
  {
    title: 'GitHub',
    href: 'https://github.com/Mail-0/Zero',
    description: 'Check out our open-source projects and contributions.',
    platform: 'github' as const,
  },
  // ...other resources
];

export const aboutLinks = [
  {
    title: 'About',
    href: '/about',
    description: 'Learn more about Zero and our mission.',
  },
  // ...other about links
];

Then import these in both files:

- const resources = [...]
- const aboutLinks = [...]
+ import { resources, aboutLinks } from '@/config/navigation-links';

102-105: Commented out background component.

There's a commented out PixelatedBackground component.

Either remove the commented code or add a comment explaining why it's being kept for future reference:

- {/* <PixelatedBackground
-   className="z-1 absolute -top-32 left-1/2 h-auto w-screen min-w-[1920px] -translate-x-1/2 object-cover opacity-5"
-   style={{ mixBlendMode: 'screen' }}
- /> */}
+ {/* TODO: Pixelated background temporarily disabled - will be re-enabled after design review */}
+ {/* <PixelatedBackground
+   className="z-1 absolute -top-32 left-1/2 h-auto w-screen min-w-[1920px] -translate-x-1/2 object-cover opacity-5"
+   style={{ mixBlendMode: 'screen' }}
+ /> */}
apps/mail/components/navbar.tsx (2)

19-62: Navigation link definitions for resources and company information.

The code defines arrays of navigation links for resources (GitHub, Twitter, etc.) and company information (About, Privacy, Terms).

As mentioned in my review of the pricing page, these link arrays are duplicated and should be extracted to a shared configuration file.


124-168: Mobile navigation with slide-in sheet.

For mobile devices, the component renders a hamburger menu button that triggers a slide-in sheet with navigation links.

The resource icons in the mobile view lack alternative text for screen readers. Consider adding more descriptive alt text:

- {resource.platform && <Image src={`/${resource.platform}.svg`} alt={resource.platform} width={20} height={20} />}
+ {resource.platform && <Image src={`/${resource.platform}.svg`} alt={`${resource.title} logo`} width={20} height={20} />}
apps/mail/components/onboarding.tsx (2)

126-127: Confetti may never fire after a step-count change

The effect’s dependency on steps.length is great, but if isPro toggles while the user is on the last visible step, currentStep could now exceed the new steps.length – 1, preventing both navigation and the confetti trigger.

Consider resetting currentStep to Math.min(currentStep, steps.length - 1) inside a useEffect([steps.length]).


136-147: Missing early return & feedback in handleUpgrade

  1. attach can be undefined; the current guard is good, but an early return saves nesting:
- if (attach) {
-   try {
+ if (!attach) return;
+ try {
  1. The user gets no visual feedback on failure. Surfacing a toast/snackbar would greatly improve UX.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 239680f and cf2cbec.

⛔ Files ignored due to path filters (1)
  • apps/mail/public/onboarding/coming-soon.png is excluded by !**/*.png
📒 Files selected for processing (9)
  • apps/mail/app/(full-width)/pricing/page.tsx (1 hunks)
  • apps/mail/components/icons/icons.tsx (2 hunks)
  • apps/mail/components/navbar.tsx (1 hunks)
  • apps/mail/components/onboarding.tsx (7 hunks)
  • apps/mail/components/ui/ai-sidebar.tsx (4 hunks)
  • apps/mail/components/ui/nav-main.tsx (2 hunks)
  • apps/mail/components/ui/nav-user.tsx (1 hunks)
  • apps/mail/locales/en.json (1 hunks)
  • apps/server/src/lib/server-utils.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
apps/mail/components/ui/ai-sidebar.tsx (1)
apps/mail/components/ui/gauge.tsx (1)
  • Gauge (1-84)
apps/mail/components/onboarding.tsx (1)
apps/mail/hooks/use-billing.ts (1)
  • useBilling (36-84)
🪛 Biome (1.9.4)
apps/mail/components/onboarding.tsx

[error] 178-178: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (15)
apps/server/src/lib/server-utils.ts (3)

9-9: Improved session validation with structured object check

The change from checking session.connectionId to session.activeConnection?.id provides better structure and optional chaining for safer access to nested properties.


12-15: Enhanced database query with explicit conditions

Using and() with explicit equality checks on both user ID and connection ID improves the query clarity and ensures proper record filtering based on the updated session structure.


31-38: Simplified driver creation implementation

The refactored code simplifies the driver creation by passing only the essential parameters (providerId and auth object). This makes the code more readable and maintains a cleaner API interface.

apps/mail/components/icons/icons.tsx (2)

162-177: Good addition of the CircleX icon for UI actions

The new CircleX icon follows the established pattern and will be useful for close/cancel actions in the UI. The SVG implementation with fill-rule is clean and efficient.


1059-1066: LinkedIn icon visibility improvement

Changing the fill from "none" to "white" improves the visibility of the LinkedIn icon, especially on darker backgrounds. This enhances the overall UI consistency.

apps/mail/locales/en.json (1)

440-441: Localization added for compose close action

Addition of the "closeCompose" localization string properly supports the new UI action for closing the composition interface. Follows existing naming conventions.

apps/mail/components/ui/nav-user.tsx (1)

150-159: Consistent subscription status check maintained

The code maintains the isPro logic for subscription status detection, which is still used in other parts of the component (line 562). This ensures consistent subscription status determination throughout the application.

apps/mail/components/ui/ai-sidebar.tsx (2)

13-13: New import added for subscription status management.

The useState, useEffect, useContext, createContext, useCallback, useMemo imports have been reorganized, and a new import for useCustomer from autumn-js/next has been added to support subscription status checking.

Also applies to: 29-29


164-187: Conditionally render usage gauge and upgrade tooltip only for non-Pro users.

The UI now intelligently hides the message usage gauge and upgrade tooltip for Pro subscribers, providing a cleaner experience for paid users while maintaining the upgrade path for free users.

This is a good UX improvement that reduces visual clutter for Pro users who don't need to see usage limits.

apps/mail/components/ui/nav-main.tsx (2)

345-345: Reordered CSS classes for styling consistency.

Background styling classes were moved before layout classes.

Though functionally equivalent, this change likely aligns with a team convention of ordering background styles before layout styles.


498-498: Enhanced dropdown menu styling with modern visual effects.

The dropdown menu now includes translucent backgrounds, proper spacing with flex layout, and a backdrop blur effect.

These styling improvements create a more modern, visually appealing dropdown that's consistent with contemporary UI trends.

apps/mail/app/(full-width)/pricing/page.tsx (2)

1-24: New pricing page implemented with necessary imports.

A client-side pricing page has been created with imports for UI components, hooks for billing and customer data, and navigation elements.

The imports are well-organized and include all necessary components for the pricing functionality.


214-300: Pricing plans UI implementation.

The pricing section displays two plans (Free and Pro) with their respective features and prices, highlighting the differences and providing appropriate upgrade/downgrade buttons based on the user's current subscription status.

The pricing UI is well-structured and clearly communicates the value proposition of each plan. The conditional button states based on subscription status are appropriately implemented.

apps/mail/components/navbar.tsx (2)

1-18: New responsive navbar component with required imports.

A new Navbar component is added with imports for navigation elements, sheet components for mobile views, and basic UI utilities.

The component is well-structured with all necessary imports for both desktop and mobile navigation patterns.


64-123: Desktop navigation implementation in Navbar component.

The component renders a fixed header for desktop with the company logo, dropdown menus, and action buttons.

The desktop navigation is well-implemented with appropriate responsive classes and interactive elements. The dropdown menus provide a clean way to access additional links without cluttering the main navigation.

Comment on lines 169 to 212
<div className="md:hidden">
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" className="fixed left-4 top-6 z-50">
<Menu className="h-6 w-6" />
</Button>
</SheetTrigger>
<SheetContent side="left" className="w-[300px] bg-[#111111] sm:w-[400px]">
<SheetHeader className="flex flex-row items-center justify-between">
<SheetTitle>
<Image src="white-icon.svg" alt="Zero Email" width={22} height={22} />
</SheetTitle>
<Link href="/login">
<Button className="w-full">Sign in</Button>
</Link>
</SheetHeader>
<div className="mt-8 flex flex-col space-y-3">
<div className="space-y-3">
<h4 className="text-muted-foreground text-sm font-medium">Company</h4>
{aboutLinks.map((link) => (
<Link key={link.title} href={link.href} className="block font-medium">
{link.title}
</Link>
))}
</div>
<Link target="_blank" href="https://cal.com/team/0" className="font-medium">
Contact Us
</Link>
</div>
<Separator className="mt-8" />
<div className="mt-8 flex flex-row items-center justify-center gap-4">
{resources.map((resource) => (
<Link
key={resource.title}
href={resource.href}
className="flex items-center gap-2 font-medium"
>
{resource.platform && <Image src={`/${resource.platform}.svg`} alt={resource.platform} width={20} height={20} />}
</Link>
))}
</div>
</SheetContent>
</Sheet>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Mobile navigation with slide-in sheet.

A mobile-friendly navigation is implemented using a slide-in sheet triggered by a hamburger menu button.

Similar to the desktop navigation, this is duplicated with the Navbar component and should be refactored to use it.

Comment on lines 107 to 166
{/* Desktop Navigation - Hidden on mobile */}
<header className="fixed z-50 hidden w-full items-center justify-center px-4 pt-6 md:flex">
<nav className="border-input/50 bg-popover flex w-full max-w-3xl items-center justify-between gap-2 rounded-xl border-t p-2 px-4">
<div className="flex items-center gap-6">
<Link href="/" className="relative cursor-pointer">
<Image src="white-icon.svg" alt="Zero Email" width={22} height={22} />
</Link>
<NavigationMenu>
<NavigationMenuList className="gap-1">
<NavigationMenuItem>
<NavigationMenuTrigger>Company</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-1 lg:w-[600px]">
{aboutLinks.map((link) => (
<ListItem key={link.title} title={link.title} href={link.href}>
{link.description}
</ListItem>
))}
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Resources</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px]">
{resources.map((resource) => (
<ListItem
key={resource.title}
title={resource.title}
href={resource.href}
platform={resource.platform}
>
{resource.description}
</ListItem>
))}
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<Link href="/pricing">
<Button variant="ghost" className="h-9">
Pricing
</Button>
</Link>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
</div>
<div className="flex gap-2">
<Link href="/login">
<Button variant="ghost" className="h-8">
Sign in
</Button>
</Link>
<Link target="_blank" href="https://cal.com/team/0">
<Button className="h-8 font-medium">Contact Us</Button>
</Link>
</div>
</nav>
</header>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Desktop navigation implementation.

The desktop navigation includes the company logo, dropdown menus for company info and resources, and action buttons for sign-in and contact.

The navigation code is duplicated between this file and the Navbar component. Consider reusing the Navbar component instead:

- {/* Desktop Navigation - Hidden on mobile */}
- <header className="fixed z-50 hidden w-full items-center justify-center px-4 pt-6 md:flex">
-   {/* ... all the navigation code ... */}
- </header>
+ <Navbar />

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +70 to +98
export default function PricingPage() {
const [open, setOpen] = useState(false);
const { attach } = useBilling();
const { customer } = useCustomer();

const isPro = useMemo(() => {
return (
customer &&
Array.isArray(customer.products) &&
customer.products.some(
(product: any) =>
product.id.includes('pro-example') || product.name.includes('pro-example'),
)
);
}, [customer]);

const handleUpgrade = async () => {
if (attach) {
try {
await attach({
productId: 'pro-example',
successUrl: `${window.location.origin}/mail/inbox?success=true`,
authUrl: `${window.location.origin}/login?redirect=/pricing`,
});
} catch (error) {
console.error('Failed to upgrade:', error);
}
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Pricing page component with subscription status checking and upgrade handling.

The component uses hooks to access billing functionality and customer data, checks if the user has a Pro subscription, and provides an upgrade function.

The error handling in handleUpgrade only logs to the console without providing user feedback. Consider adding toast notifications:

const handleUpgrade = async () => {
  if (attach) {
    try {
      await attach({
        productId: 'pro-example',
        successUrl: `${window.location.origin}/mail/inbox?success=true`,
        authUrl: `${window.location.origin}/login?redirect=/pricing`,
      });
+     // Success is handled by the redirect
    } catch (error) {
      console.error('Failed to upgrade:', error);
+     toast.error('Failed to process your upgrade. Please try again later.');
    }
  }
};

Also, consider adding loading state feedback during the upgrade process:

const [isUpgrading, setIsUpgrading] = useState(false);

const handleUpgrade = async () => {
  if (attach) {
+   setIsUpgrading(true);
    try {
      await attach({
        // ...
      });
    } catch (error) {
      // ...
+   } finally {
+     setIsUpgrading(false);
    }
  }
};

// Then in the button:
<Button 
  // ...
+ disabled={isPro || isUpgrading}
+ className="h-10 w-full"
>
+ {isUpgrading ? "Processing..." : isPro ? "Current Plan" : "Upgrade"}
</Button>

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +155 to +165
<div className="mt-8 flex flex-row items-center justify-center gap-4">
{resources.map((resource) => (
<Link
key={resource.title}
href={resource.href}
className="flex items-center gap-2 font-medium"
>
{resource.platform && <Image src={`/${resource.platform}.svg`} alt={resource.platform} width={20} height={20} />}
</Link>
))}
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Social media links in mobile navigation.

The code renders social media platform icons as links in the mobile sheet menu.

Verify that all the platform SVG files referenced in the Image src attributes exist in the public directory:


🏁 Script executed:

#!/bin/bash
# Check if all required social media icon files exist
missing_files=0

for platform in github twitter linkedin discord
do
  if [ ! -f "public/${platform}.svg" ]; then
    echo "Missing file: public/${platform}.svg"
    missing_files=$((missing_files+1))
  fi
done

if [ $missing_files -eq 0 ]; then
  echo "All platform icon files exist."
else
  echo "Missing ${missing_files} platform icon files."
fi

Length of output: 822


Missing social media icon files in public directory

The mobile navigation renders social media icons using <Image src="/{platform}.svg" />, but none of the expected SVGs are present:

• public/github.svg
• public/twitter.svg
• public/linkedin.svg
• public/discord.svg

Please add the corresponding SVG files (or update the platform values) so these links render correctly.

Comment on lines +172 to 175
{typeof steps[currentStep]?.description === 'function'
? steps[currentStep]?.description(handleUpgrade)
: steps[currentStep]?.description}
</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

description typing is too permissive for the <p> wrapper

description currently accepts string | ReactNode | ((handleUpgrade)=>ReactNode), but only strings are guaranteed to be valid inside a <p>. When it’s a JSX element that renders a <div> the HTML becomes invalid.

Two possible fixes:

- <p className="text-muted-foreground mx-auto max-w-md text-sm">
-   {typeof steps[currentStep]?.description === 'function'
-     ? steps[currentStep]?.description(handleUpgrade)
-     : steps[currentStep]?.description}
- </p>
+ const Description = steps[currentStep]?.description;
+ <div className="text-muted-foreground mx-auto max-w-md text-sm">
+   {typeof Description === 'function' ? Description(handleUpgrade) : Description}
+ </div>

This removes the semantic trap and lets description be any valid ReactNode.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +105 to +114
const isPro = useMemo(() => {
return (
customer &&
Array.isArray(customer.products) &&
customer.products.some(
(product: any) =>
product.id.includes('pro-example') || product.name.includes('pro-example'),
)
);
}, [customer]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Guard against unexpected product shapes in isPro detection

isPro assumes every entry in customer.products is an object with both id and name properties.
If the API ever sends a string identifier (or any non-object) the optional chain breaks and the component crashes while rendering.

- customer.products.some(
-   (product: any) =>
-     product.id.includes('pro-example') || product.name.includes('pro-example'),
- )
+ customer.products.some((product: any) => {
+   if (typeof product !== 'object' || product === null) return false;
+   return (
+     (typeof product.id === 'string' && product.id.includes('pro-example')) ||
+     (typeof product.name === 'string' && product.name.includes('pro-example'))
+   );
+ })

Adding the type-checks costs virtually nothing but prevents a fatal runtime error if the backend shape changes.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const isPro = useMemo(() => {
return (
customer &&
Array.isArray(customer.products) &&
customer.products.some(
(product: any) =>
product.id.includes('pro-example') || product.name.includes('pro-example'),
)
);
}, [customer]);
const isPro = useMemo(() => {
return (
customer &&
Array.isArray(customer.products) &&
customer.products.some((product: any) => {
if (typeof product !== 'object' || product === null) return false;
return (
(typeof product.id === 'string' && product.id.includes('pro-example')) ||
(typeof product.name === 'string' && product.name.includes('pro-example'))
);
})
);
}, [customer]);

Comment on lines +178 to +201
{steps[currentStep] && steps[currentStep].video && (
<div className="relative flex items-center justify-center">
<div className="bg-muted aspect-video w-full max-w-4xl overflow-hidden rounded-lg">
{steps.map(
(step, index) =>
step.video && (
<div
key={index}
className={`absolute inset-0 transition-opacity duration-300 ${
index === currentStep ? 'opacity-100' : 'opacity-0'
}`}
>
<Image
priority
width={500}
height={500}
src={step.video}
alt={step.title}
className="h-full w-full object-cover rounded-lg border"
/>
</div>
),
)}
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Rendering every video at once harms performance

Each step’s image is mounted (and pre-loaded, due to priority) even when hidden. This can easily add several megabytes to the initial payload.

Render only the active frame:

- {steps.map(
-   (step, index) =>
-     step.video && (
-       <div
-         key={index}
-         className={`absolute inset-0 transition-opacity duration-300 ${
-           index === currentStep ? 'opacity-100' : 'opacity-0'
-         }`}
-       >
-         <Image ... src={step.video} />
-       </div>
-     ),
- )}
+ {steps[currentStep]?.video && (
+   <Image
+     priority
+     width={500}
+     height={500}
+     src={steps[currentStep].video}
+     alt={steps[currentStep].title}
+     className="h-full w-full object-cover rounded-lg border"
+   />
+ )}

This keeps memory and network usage minimal while preserving the fade effect (you can wrap the Image in a single animated container if the transition is required).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{steps[currentStep] && steps[currentStep].video && (
<div className="relative flex items-center justify-center">
<div className="bg-muted aspect-video w-full max-w-4xl overflow-hidden rounded-lg">
{steps.map(
(step, index) =>
step.video && (
<div
key={index}
className={`absolute inset-0 transition-opacity duration-300 ${
index === currentStep ? 'opacity-100' : 'opacity-0'
}`}
>
<Image
priority
width={500}
height={500}
src={step.video}
alt={step.title}
className="h-full w-full object-cover rounded-lg border"
/>
</div>
),
)}
</div>
{steps[currentStep] && steps[currentStep].video && (
<div className="relative flex items-center justify-center">
<div className="bg-muted aspect-video w-full max-w-4xl overflow-hidden rounded-lg">
{steps[currentStep]?.video && (
<Image
priority
width={500}
height={500}
src={steps[currentStep].video}
alt={steps[currentStep].title}
className="h-full w-full object-cover rounded-lg border"
/>
)}
</div>
</div>
)}
🧰 Tools
🪛 Biome (1.9.4)

[error] 178-178: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

@nizzyabi nizzyabi merged commit 770ab6d into main May 10, 2025
5 checks passed
This was referenced May 11, 2025
@coderabbitai coderabbitai bot mentioned this pull request May 30, 2025
8 tasks
@coderabbitai coderabbitai bot mentioned this pull request Aug 22, 2025
34 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants