Skip to content

Service Pros Started - #33

Merged
Nihalpuse merged 2 commits into
mainfrom
nihal
Nov 14, 2025
Merged

Service Pros Started#33
Nihalpuse merged 2 commits into
mainfrom
nihal

Conversation

@Nihalpuse

@Nihalpuse Nihalpuse commented Nov 14, 2025

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

Release Notes

  • New Features

    • Added auto-scrolling and pagination controls for the features section on mobile and tablet devices.
  • Style

    • Improved responsive design across multiple components for better visual balance on all screen sizes.
    • Refined spacing and typography throughout the app for enhanced readability and content organization.

…tency across ExplorePropertiesBanner, ChooseButton, EverythingElseCard, ScalableCard, EverythingElseSection, FeaturesSection, and ScalableSolutionSection.
@vercel

vercel Bot commented Nov 14, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
pms-frontend Error Error Nov 14, 2025 4:46am

@coderabbitai

coderabbitai Bot commented Nov 14, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This PR enhances responsive design across six UI components and sections through spacing refinements, responsive typography, conditional icon rendering at breakpoints, and introduces mobile/tablet/desktop scroll variants with auto-scrolling behavior in FeaturesSection.

Changes

Cohort / File(s) Summary
Responsive Icon Rendering
src/components/common/buttons/ChooseButton.tsx, src/components/common/cards/EverythingElseCard.tsx
Implemented conditional icon rendering with size variants across breakpoints. Each component now renders two icon sizes—smaller on mobile, larger on tablet/desktop—using hidden and responsive classes for breakpoint-specific display.
Spacing & Typography Adjustments
src/components/ExplorePropertiesBanner.tsx
Reduced padding (py-20 → py-10 sm:py-16 lg:py-20), adjusted decorative image heights (fixed 70% → responsive 50%–70%), responsive button sizing (px-4 sm:px-6, py-2.5 sm:py-3, text-xs sm:text-sm), and responsive heading typography with line breaks.
Layout Refinements
src/components/common/cards/ScalableCard.tsx, src/pages/home/sections/ScalableSolutionSection.tsx
Container padding tightened (p-4 → p-2 sm:p-4), image height made responsive (h-32 sm:h-52), grid layout adjusted (grid-cols-2 sm:grid-cols-2 lg:grid-cols-4), and typography scaled responsively (text-xs sm:text-base). Gap values reduced across all breakpoints.
Section Container & Typography
src/pages/home/sections/EverythingElseSection.tsx
Updated section padding (px-6/py-8 → px-4 sm:px-6/py-6 sm:py-8 lg:py-10), grid layout shifted to 2-column on larger screens, reduced heading size (text-3xl md:text-4xl → text-2xl sm:text-3xl lg:text-4xl), and tightened card/button spacing.
Mobile/Tablet/Desktop Scroll Variants
src/pages/home/sections/FeaturesSection.tsx
Added viewport-specific scroll containers (mobileScrollRef, tabletScrollRef, scrollContainerRef) with dedicated pagination controls and click handlers per breakpoint. Integrated IntersectionObserver for auto-scroll on first viewport intersection (mobile/tablet only). Introduced feature duplication for infinite-like desktop scrolling. Updated React imports to include useRef and useEffect.

Sequence Diagram(s)

sequenceDiagram
    participant Observer as IntersectionObserver
    participant Component as FeaturesSection
    participant DOM as Scroll Container

    Note over Component: Component Mounts
    Component->>Component: useRef() creates refs for mobile/tablet/desktop
    Component->>Component: useEffect sets up Observer
    Component->>Observer: Register scroll container(s) for observation

    Observer->>Component: onIntersectionObserved (visible)
    alt hasAutoScrolled = false
        Component->>Component: setHasAutoScrolled(true)
        Component->>DOM: auto-scroll to position
        Note over Component: Executes once on first view
    else hasAutoScrolled = true
        Component->>Component: Skip auto-scroll (already done)
    end

    Component->>DOM: User clicks PaginationButton (prev/next)
    Component->>Component: handleMobilePrevClick/Next triggered
    Component->>DOM: Scroll to target position (immediate)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • FeaturesSection.tsx requires close attention due to new stateful logic (IntersectionObserver, multiple refs per breakpoint, auto-scroll flags) and verification that auto-scroll executes only once per viewport and doesn't interfere with user interactions.
  • Responsive icon rendering pattern in ChooseButton and EverythingElseCard should be verified for consistent class application and breakpoint alignment (hidden vs display).
  • Verify all spacing/padding conversions across components follow consistent responsive scales and don't introduce unintended layout shifts or text cutoff on edge breakpoints.

Possibly related PRs

Poem

🐰 Hop along the responsive path,
Icons shrink and stretch with math,
Mobile scrolls with keen delight,
Spacing trims look crisp and right!
Breakpoints dance—from small to wide,
Design flows smooth, with pixel pride.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The pull request title 'Service Pros Started' is vague and does not clearly describe the actual changes made. The changeset involves responsive design improvements, layout adjustments, and component updates across multiple files, but the title provides no meaningful information about these changes. Replace the title with a descriptive summary of the main changes, such as 'Improve responsive design and layout across home page components' or 'Make UI components more mobile-friendly with responsive styling adjustments'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch nihal

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
src/pages/home/sections/ScalableSolutionSection.tsx (1)

7-16: Check 2‑column grid density on very small screens

Using grid-cols-2 plus tight gaps even at the smallest breakpoint may make each ScalableCard feel cramped on narrow phones. It might be worth verifying on a 320–360px viewport and, if it feels too tight, reverting to grid-cols-1 sm:grid-cols-2 to keep readability high on the smallest screens.

src/components/common/buttons/ChooseButton.tsx (1)

11-12: DRY up icon markup and mark decorative icon as hidden from AT

The Send icon markup is duplicated for the Link and button paths, and it looks purely decorative.

You can factor it out and mark it as decorative to improve readability and accessibility:

 const baseClasses = "w-full flex items-center justify-center gap-1 sm:gap-2 py-2 sm:py-3 rounded-full bg-[var(--color-card-1)] text-black text-xs sm:text-sm font-medium shadow-md hover:shadow-lg transition-all duration-200";
 const iconClasses = "inline-flex items-center justify-center w-6 h-6 sm:w-8 sm:h-8 rounded-full bg-[var(--color-primary)] text-white";
 
 export default function ChooseButton({ text = "Choose", onClick, to, className = "" }: ChooseButtonProps) {
   const classes = `${baseClasses} ${className}`;
+
+  const icon = (
+    <span className={iconClasses} aria-hidden="true">
+      <Send size={14} className="sm:hidden" />
+      <Send size={16} className="hidden sm:inline" />
+    </span>
+  );
 
   if (to) {
     return (
       <Link
         to={to}
         className={classes}
         onClick={onClick}
       >
-        {text}
-        <span className={iconClasses}>
-          <Send size={14} className="sm:hidden" />
-          <Send size={16} className="hidden sm:inline" />
-        </span>
+        {text}
+        {icon}
       </Link>
     );
   }
 
   return (
@@ -38,9 +44,8 @@ export default function ChooseButton({ text = "Choose", onClick, to, className =
       onClick={onClick}
       className={classes}
     >
       {text}
-      <span className={iconClasses}>
-        <Send size={14} className="sm:hidden" />
-        <Send size={16} className="hidden sm:inline" />
-      </span>
+      {icon}
     </button>
   );
 }

Also applies to: 26-27, 40-41

src/components/ExplorePropertiesBanner.tsx (1)

5-8: Treat abstract side images as decorative for accessibility

The left/right abstract images appear purely decorative, but they currently have meaningful alt text, which will be announced by screen readers. Consider making them decorative instead:

-        <img
-          src="https://res.cloudinary.com/dxwspucxw/image/upload/v1761905090/abstractleft_yhtzva.png"
-          alt="Abstract Left"
+        <img
+          src="https://res.cloudinary.com/dxwspucxw/image/upload/v1761905090/abstractleft_yhtzva.png"
+          alt=""
+          aria-hidden="true"
@@
-        <img
+        <img
           src="https://res.cloudinary.com/dxwspucxw/image/upload/v1761905090/abstractright_qhdpgm.png"
-          alt="Abstract Right"
+          alt=""
+          aria-hidden="true"

This keeps the visuals while avoiding extra noise for assistive technologies.

Also applies to: 15-22

src/pages/home/sections/EverythingElseSection.tsx (1)

8-27: Verify 2‑column card grid usability on narrow viewports

The right-hand cards use grid grid-cols-2 even at the smallest breakpoint with reduced gaps. Combined with EverythingElseCard’s compact typography, this is probably fine, but on ~320px devices each card will be quite narrow.

It’s worth checking on a small handset; if it feels cramped, consider grid-cols-1 sm:grid-cols-2 while keeping the tighter gaps.

src/pages/home/sections/FeaturesSection.tsx (1)

14-18: Hoist static feature config and simplify useEffect dependencies

The scroll logic and auto-scroll behavior look good, but features/duplicatedFeatures are static config and get recreated on every render, while useEffect depends on features.length yet closes over features itself.

To keep things simple and future‑proof if features ever becomes dynamic, you could:

  • Hoist features (and duplicatedFeatures) to module scope so they’re created once.
  • Drop features.length from the dependency array (or replace it with an empty array) since the data then truly is static.

For example:

-import React, { useRef, useEffect } from "react";
+import React, { useRef, useEffect } from "react";
@@
-const FeaturesSection: React.FC = () => {
-  const { scrollContainerRef, handlePrevClick, handleNextClick } =
-    useHorizontalInfiniteScroll({
-      scrollAmount: 430,
-    });
-
-  const mobileScrollRef = useRef<HTMLDivElement>(null);
-  const tabletScrollRef = useRef<HTMLDivElement>(null);
-  const hasAutoScrolledMobile = useRef(false);
-  const hasAutoScrolledTablet = useRef(false);
-
-  const features = [
+const FEATURES = [
@@
-  ];
+];
+
+const DUPLICATED_FEATURES = [...FEATURES, ...FEATURES, ...FEATURES];
+
+const FeaturesSection: React.FC = () => {
+  const { scrollContainerRef, handlePrevClick, handleNextClick } =
+    useHorizontalInfiniteScroll({
+      scrollAmount: 430,
+    });
+
+  const mobileScrollRef = useRef<HTMLDivElement>(null);
+  const tabletScrollRef = useRef<HTMLDivElement>(null);
+  const hasAutoScrolledMobile = useRef(false);
+  const hasAutoScrolledTablet = useRef(false);
+
+  const features = FEATURES;
@@
-  useEffect(() => {
+  useEffect(() => {
@@
-  }, [features.length]);
+  }, []);
@@
-  // Duplicate features for infinite scroll
-  const duplicatedFeatures = [...features, ...features, ...features];
+  // Duplicate features for infinite scroll
+  const duplicatedFeatures = DUPLICATED_FEATURES;

Functionally this keeps behavior the same, while reducing work per render and making the effect dependencies more obviously correct.

Also applies to: 61-115, 116-180, 182-184

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e71156 and 28a99a4.

📒 Files selected for processing (7)
  • src/components/ExplorePropertiesBanner.tsx (3 hunks)
  • src/components/common/buttons/ChooseButton.tsx (3 hunks)
  • src/components/common/cards/EverythingElseCard.tsx (1 hunks)
  • src/components/common/cards/ScalableCard.tsx (2 hunks)
  • src/pages/home/sections/EverythingElseSection.tsx (1 hunks)
  • src/pages/home/sections/FeaturesSection.tsx (4 hunks)
  • src/pages/home/sections/ScalableSolutionSection.tsx (1 hunks)
🔇 Additional comments (1)
src/components/common/cards/ScalableCard.tsx (1)

13-26: Responsive spacing and typography look solid

The tighter padding, responsive image height, and scaled-down mobile typography read well and keep the card compact on small screens while preserving hierarchy at sm+. No functional issues spotted.

Comment on lines +18 to 35
<div className="flex flex-col border rounded-lg p-3 sm:p-8 w-full max-w-[320px] shadow-sm hover:shadow-md transition-all duration-200 border-[var(--color-primary)]/20 hover:bg-[#0CA474] group cursor-pointer">
<div className="flex justify-between items-start mb-2 sm:mb-3">
<span className="text-xl sm:text-3xl font-semibold text-[var(--color-primary)] group-hover:text-white transition-colors duration-200">{number}</span>
<div className="text-[var(--color-primary)] group-hover:text-white transition-colors duration-200">
{React.isValidElement(icon)
? React.cloneElement(icon as React.ReactElement<any>, {
size: 20,
className: "sm:hidden"
} as any)
: icon}
{React.isValidElement(icon)
? React.cloneElement(icon as React.ReactElement<any>, {
size: 28,
className: "hidden sm:block"
} as any)
: null}
</div>
</div>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Preserve icon props when cloning for different breakpoints

The new breakpoint-based cloning overwrites className (and potentially other props) on icon. If callers pass a custom icon with its own classes, those will be lost.

You can preserve the original className and avoid duplicating the validity check like this:

-      <div className="flex justify-between items-start mb-2 sm:mb-3">
-        <span className="text-xl sm:text-3xl font-semibold text-[var(--color-primary)] group-hover:text-white transition-colors duration-200">{number}</span>
-        <div className="text-[var(--color-primary)] group-hover:text-white transition-colors duration-200">
-          {React.isValidElement(icon)
-            ? React.cloneElement(icon as React.ReactElement<any>, { 
-                size: 20,
-                className: "sm:hidden"
-              } as any)
-            : icon}
-          {React.isValidElement(icon)
-            ? React.cloneElement(icon as React.ReactElement<any>, { 
-                size: 28,
-                className: "hidden sm:block"
-              } as any)
-            : null}
-        </div>
-      </div>
+      <div className="flex justify-between items-start mb-2 sm:mb-3">
+        <span className="text-xl sm:text-3xl font-semibold text-[var(--color-primary)] group-hover:text-white transition-colors duration-200">
+          {number}
+        </span>
+        <div className="text-[var(--color-primary)] group-hover:text-white transition-colors duration-200">
+          {React.isValidElement(icon) ? (
+            <>
+              {(() => {
+                const iconElement = icon as React.ReactElement<any>;
+                const baseClassName = iconElement.props.className ?? "";
+                return React.cloneElement(iconElement, {
+                  size: 20,
+                  className: `${baseClassName} sm:hidden`.trim(),
+                } as any);
+              })()}
+              {(() => {
+                const iconElement = icon as React.ReactElement<any>;
+                const baseClassName = iconElement.props.className ?? "";
+                return React.cloneElement(iconElement, {
+                  size: 28,
+                  className: `${baseClassName} hidden sm:block`.trim(),
+                } as any);
+              })()}
+            </>
+          ) : (
+            icon
+          )}
+        </div>
+      </div>

This keeps any styling the caller applied to the icon while still giving you responsive sizing.

🤖 Prompt for AI Agents
In src/components/common/cards/EverythingElseCard.tsx around lines 18 to 35, the
cloning of the icon for different breakpoints overwrites the icon's existing
props (notably className) and repeats the React.isValidElement check; change
this to check isValidElement once, capture the original element and its props,
and when cloning spread the original props first then override size and merge
className (e.g. concatenate original className with "sm:hidden" / "hidden
sm:block") so caller-supplied classes are preserved and other props remain
intact.

@Nihalpuse
Nihalpuse merged commit ce58c49 into main Nov 14, 2025
2 of 3 checks passed
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.

1 participant