Skip to content

Refactored card layoutin tenants and fixed pagination at the bottom in Tenants and ServicePros - #98

Merged
Nihalpuse merged 4 commits into
mainfrom
nihal
Dec 18, 2025
Merged

Refactored card layoutin tenants and fixed pagination at the bottom in Tenants and ServicePros#98
Nihalpuse merged 4 commits into
mainfrom
nihal

Conversation

@Nihalpuse

@Nihalpuse Nihalpuse commented Dec 17, 2025

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added dedicated add/edit page for service pros with profile photo upload, contact details, category selection, address information, and document uploads.
    • Enhanced tenant cards with property information display and additional action buttons.
  • UI Improvements

    • Redesigned service pro form navigation from modal-based to page-based flow.
    • Updated sort control button with visual label and indicator.
    • Optimized tenant card layout and sizing.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Dec 17, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

The PR adds comprehensive Add/Edit functionality for Service Pros with a new AddEditServicePro component accessible via dedicated routes, replacing modal-based workflows. Additionally, Tenant management UI receives styling and layout refinements including improved card design and responsive grid adjustments.

Changes

Cohort / File(s) Summary
Service Pro Add/Edit Feature
src/pages/Dashboard/features/ServicePros/AddEditServicePro.tsx
New React component providing form for adding/editing service pros with profile photo upload, general information (name, contact details, dynamic emails/phones), company toggle, category/subcategory dropdown selection, address fields, document uploads, and client-side validation. Detects edit mode via route param and preloads mock data.
Service Pro Routing Updates
src/App.tsx
Added import for AddEditServicePro and two new routes: /dashboard/contacts/service-pros/add and /dashboard/contacts/service-pros/edit/:id, both rendering the AddEditServicePro component.
Service Pro Navigation Flow
src/pages/Dashboard/features/ServicePros/ServicePros.tsx, src/pages/Dashboard/features/ServicePros/ServiceProsDetail.tsx, src/pages/Dashboard/features/ServicePros/components/ServiceProCard.tsx
Replaced modal-based add flow with route navigation. ServicePros navigates to /dashboard/contacts/service-pros/add on Add click. ServiceProsDetail and ServiceProCard Edit actions now navigate to /dashboard/contacts/service-pros/edit/${id}.
Tenant UI Styling
src/pages/Dashboard/features/Tenants/AddEditTenant.tsx
Updated profile photo styling from rounded-full to rounded-2xl. Changed ImageCropModal circularCrop from true to false in two locations.
Tenant Layout & Card Enhancements
src/pages/Dashboard/features/Tenants/Tenants.tsx, src/pages/Dashboard/features/Tenants/components/TenantCard.tsx
Tenants.tsx: Added flex column wrapper, enhanced sort button with label and rotating SVG icon, adjusted responsive grid from 3 to 2 columns, added propertyName prop injection and empty state message, repositioned pagination with mt-auto. TenantCard.tsx: Added optional propertyName prop, increased image size to w-50/h-50, redesigned card with Info Pill display, added View Profile and chat buttons, updated context menu styling with translucent border and backdrop blur.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • AddEditServicePro component: Significant new component with complex form state management, file upload handling, dynamic field arrays, category/subcategory logic, and client-side validation—requires careful review of logic flow and edge cases
  • Service Pro navigation refactoring: Multiple files updated to replace modal flow with route navigation; verify consistency across all three files and confirm proper route integration
  • Tenant card redesign: Extensive UI changes including layout restructuring, new props, and styling updates; verify visual consistency and responsive behavior across breakpoints
  • ImageCropModal configuration change: Confirm the circularCrop change from true to false aligns with new design requirements

Possibly related PRs

Poem

🐰 A noble rabbit hops with glee,
New forms for service pros to see!
Add, edit, upload with care,
Tenants styled beyond compare—
Buttons dance and layouts blend,
Navigation's now the newest trend!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title partially relates to the changeset. It mentions card layout refactoring in Tenants and pagination fixes, which are present, but omits the significant addition of a new Add/Edit Service Pro feature and routing changes that represent a substantial portion of the changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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: 2

🧹 Nitpick comments (1)
src/pages/Dashboard/features/ServicePros/AddEditServicePro.tsx (1)

493-502: Expand country dropdown options or use a library.

The country dropdown only includes USA and India, which is very limiting for a Service Pro management system that may need to support providers from various countries.

Consider using the country-state-city library (already used in AddEditTenant.tsx) for comprehensive country/state/city selection:

+import { Country, State, City } from 'country-state-city';
+import type { ICountry } from 'country-state-city';
+
+// In component:
+const [countries, setCountries] = useState<ICountry[]>([]);
+
+useEffect(() => {
+    setCountries(Country.getAllCountries());
+}, []);
+
+const countryOptions = useMemo(() => {
+    return countries.map(country => ({
+        value: country.isoCode,
+        label: country.name
+    })).sort((a, b) => a.label.localeCompare(b.label));
+}, [countries]);
+
 <CustomDropdown
     value={formData.address.country}
     onChange={(value) => handleDropdownChange('address', 'country', value)}
-    options={[
-        { value: 'USA', label: 'USA' },
-        { value: 'India', label: 'India' }
-    ]}
+    options={countryOptions}
     placeholder="Country"
+    searchable={true}
     buttonClassName="w-full bg-white border border-gray-200 text-gray-800 px-6 py-2.5 rounded-lg outline-none focus:ring-2 focus:ring-[#3A6D6C]/20 transition-all font-medium"
 />
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe5276c and d61a827.

📒 Files selected for processing (8)
  • src/App.tsx (2 hunks)
  • src/pages/Dashboard/features/ServicePros/AddEditServicePro.tsx (1 hunks)
  • src/pages/Dashboard/features/ServicePros/ServicePros.tsx (4 hunks)
  • src/pages/Dashboard/features/ServicePros/ServiceProsDetail.tsx (1 hunks)
  • src/pages/Dashboard/features/ServicePros/components/ServiceProCard.tsx (1 hunks)
  • src/pages/Dashboard/features/Tenants/AddEditTenant.tsx (9 hunks)
  • src/pages/Dashboard/features/Tenants/Tenants.tsx (4 hunks)
  • src/pages/Dashboard/features/Tenants/components/TenantCard.tsx (3 hunks)
🔇 Additional comments (16)
src/pages/Dashboard/features/ServicePros/components/ServiceProCard.tsx (1)

45-45: LGTM! Navigation-based edit flow implemented correctly.

The Edit action now navigates to the dedicated edit route, consistent with the new AddEditServicePro component and routing structure introduced in this PR.

src/pages/Dashboard/features/ServicePros/ServiceProsDetail.tsx (1)

193-193: LGTM! Consistent with the navigation-based edit pattern.

The Edit action correctly navigates to the edit route, matching the implementation in ServiceProCard and leveraging the new AddEditServicePro component.

src/pages/Dashboard/features/Tenants/AddEditTenant.tsx (1)

562-562: Verify that circularCrop={false} produces the intended rounded-square output.

The profile photo styling has been updated from circular (rounded-full) to rounded-square (rounded-2xl), and circularCrop is now false. Ensure the ImageCropModal component produces cropped images that align with the rounded-2xl display.

Run a quick visual test to confirm the cropped photo preview matches the rounded-2xl styling when saved.

Also applies to: 565-565, 586-586, 934-934

src/pages/Dashboard/features/ServicePros/ServicePros.tsx (3)

116-116: LGTM! Layout adjusted to support navigation-based flow.

The flex column container enables proper alignment of pagination at the bottom via mt-auto, improving the layout consistency.


133-133: LGTM! Modal replaced with navigation-based flow.

The "Add service pro" button now correctly navigates to the dedicated add route, consistent with the new AddEditServicePro component.


183-189: LGTM! Pagination positioning improved.

Wrapping pagination in a container with mt-auto ensures it stays at the bottom of the flex column layout.

src/App.tsx (1)

67-67: LGTM! Routes correctly configured for Service Pros add/edit flow.

The new routes follow the same pattern as the existing Tenants routes, using a shared AddEditServicePro component for both add and edit operations.

Also applies to: 171-172

src/pages/Dashboard/features/Tenants/Tenants.tsx (4)

103-103: LGTM! Layout adjusted to support pagination positioning.

The flex column container enables proper alignment of pagination at the bottom, consistent with the Service Pros layout.


140-150: LGTM! Sort indicator enhanced with visual chevron.

The explicit "Abc" label with rotating SVG chevron improves UX by making the sort direction more obvious.


180-180: LGTM! Grid layout adjusted to two columns.

The responsive grid now shows 2 columns at large breakpoints instead of 3, likely to accommodate the larger TenantCard design.


198-204: LGTM! Pagination positioned at the bottom.

The mt-auto class ensures pagination stays at the bottom of the flex column container.

src/pages/Dashboard/features/Tenants/components/TenantCard.tsx (2)

11-11: LGTM! New propertyName prop added correctly.

The optional propertyName prop allows displaying rental information when available, enhancing the card's context.

Also applies to: 20-20


62-141: LGTM! Card layout refactored for improved visual hierarchy.

The UI changes enhance the card design with:

  • Rounded-square profile images
  • Redesigned context menu with improved accessibility
  • Info pill displaying contact details
  • Prominent action buttons
  • Conditional rental information display

The refactor aligns with the PR objectives for improved card layout.

src/pages/Dashboard/features/ServicePros/AddEditServicePro.tsx (3)

299-313: LGTM! Validation and submit flow implemented correctly.

The form validates required fields (firstName, lastName, category) and provides clear error feedback. The mock submit logic is appropriate for this stage of development.


244-270: LGTM! Dynamic email/phone fields implemented well.

The implementation correctly limits additional fields to 2 each with add/remove controls, providing a clean UX for managing multiple contact methods.


273-282: Add memory leak prevention for blob URLs.

The profile photo handler creates a blob URL via FileReader.readAsDataURL but doesn't track or revoke it. When users change the photo multiple times, blob URLs accumulate in memory.

Apply this pattern from AddEditTenant.tsx to manage blob URL cleanup:

+const currentBlobUrlRef = useRef<string | null>(null);
+
 const handleProfilePhotoSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
     if (e.target.files && e.target.files[0]) {
         const file = e.target.files[0];
+        
+        // Revoke previous blob URL
+        if (currentBlobUrlRef.current) {
+            URL.revokeObjectURL(currentBlobUrlRef.current);
+        }
+        
         const reader = new FileReader();
         reader.onload = (ev) => {
-            if (ev.target?.result) setProfilePhoto(ev.target.result as string);
+            if (ev.target?.result) {
+                const blobUrl = ev.target.result as string;
+                currentBlobUrlRef.current = blobUrl;
+                setProfilePhoto(blobUrl);
+            }
         };
         reader.readAsDataURL(file);
     }
 };
+
+// Add cleanup effect
+useEffect(() => {
+    return () => {
+        if (currentBlobUrlRef.current) {
+            URL.revokeObjectURL(currentBlobUrlRef.current);
+        }
+    };
+}, []);

Likely an incorrect or invalid review comment.

Comment on lines +273 to +282
const handleProfilePhotoSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = (ev) => {
if (ev.target?.result) setProfilePhoto(ev.target.result as string);
};
reader.readAsDataURL(file);
}
};

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.

🛠️ Refactor suggestion | 🟠 Major

Add file validation for profile photo uploads.

The profile photo handler lacks validation for file type and size, which could lead to errors or poor UX if users upload inappropriate files.

Add validation similar to AddEditTenant.tsx:

 const handleProfilePhotoSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
     if (e.target.files && e.target.files[0]) {
         const file = e.target.files[0];
+        
+        // Validate file type
+        const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp'];
+        if (!allowedTypes.includes(file.type)) {
+            alert('Please select a valid image file (JPEG, PNG, GIF, or WebP)');
+            if (profileInputRef.current) profileInputRef.current.value = '';
+            return;
+        }
+        
+        // Validate file size (max 10MB)
+        const maxSize = 10 * 1024 * 1024;
+        if (file.size > maxSize) {
+            alert('Image size must not exceed 10MB');
+            if (profileInputRef.current) profileInputRef.current.value = '';
+            return;
+        }
+        
         const reader = new FileReader();
         reader.onload = (ev) => {
             if (ev.target?.result) setProfilePhoto(ev.target.result as string);
         };
         reader.readAsDataURL(file);
     }
 };

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

🤖 Prompt for AI Agents
In src/pages/Dashboard/features/ServicePros/AddEditServicePro.tsx around lines
273 to 282, the profile photo handler reads any file without validation; add
file type and size checks before creating the FileReader. Reject files that are
not images (check file.type startsWith "image/") and enforce a max size (e.g.,
2MB or match AddEditTenant.tsx), set an error state or show a toast when
invalid, clear the input selection, and only call reader.readAsDataURL(file)
after the file passes validation.

key={tenant.id}
{...tenant}
image={tenant.image || ''}
propertyName="Sunset Apartments, Unit 4B"

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

Replace hardcoded propertyName with dynamic tenant data.

The property name is hardcoded as "Sunset Apartments, Unit 4B" for all tenants. This should be dynamically sourced from the tenant's lease or unit assignment data.

Consider updating the tenant data structure to include property/unit information:

-propertyName="Sunset Apartments, Unit 4B"
+propertyName={tenant.propertyName || tenant.unit?.property?.name || undefined}

If the backend doesn't yet provide this information, verify whether the tenant object includes lease or unit relationships that can be used to derive the property name.

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

🤖 Prompt for AI Agents
In src/pages/Dashboard/features/Tenants/Tenants.tsx around line 187 the
propertyName is hardcoded to "Sunset Apartments, Unit 4B"; replace this static
string with the tenant's actual property/unit value (e.g. use
tenant.propertyName or derive it from tenant.unit?.property?.name or
tenant.lease?.unit?.property?.name depending on the shape of your data), add a
safe fallback (like "Unknown property") if the nested fields are missing, and if
the backend doesn't yet supply property/unit info update the tenant data fetch
or model to include lease/unit/property relationships so the component can
render the real property name.

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