Skip to content

Feat/property page#92

Merged
respp merged 11 commits intoStellar-Rent:mainfrom
CMI-James-OD:feat/property-page
Jul 9, 2025
Merged

Feat/property page#92
respp merged 11 commits intoStellar-Rent:mainfrom
CMI-James-OD:feat/property-page

Conversation

@CMI-James
Copy link
Contributor

@CMI-James CMI-James commented Jun 28, 2025

StellarRent Logo

Pull Request | StellarRent

📝 Summary

Provide a brief description of what this PR accomplishes.

🔗 Related Issues

Closes #87 .

🔄 Changes Made

Implemented a complete, responsive Property Detail page with all essential components to enhance the user experience and prepare for future blockchain integration. This includes:

  • Property Detail Page with full property info, amenities, booking flow, and responsive design.
  • Image Gallery with modal view, zoom, keyboard/touch navigation, and error handling.
  • Calendar with interactive date selection, availability display, and accessibility features.
  • Reviews Section with mock data, rating breakdown, and blockchain-ready structure.
  • Map Component for location context with external map support.
  • Enhanced Property Cards with status indicators and hover effects.

==

🖼️ Current Output

Provide visual evidence of the changes:
https://www.loom.com/share/8a3fb93476a94b2a9fd34dc2f3b29761?sid=15c73797-981e-4d25-b552-8590ef351e68

🧪 Testing

If applicable, describe the tests performed. Include screenshots, test outputs, or any resources that help reviewers understand how the changes were tested.

✅ Testing Checklist

  • Unit tests added/modified
  • Integration tests performed
  • Manual tests executed
  • All tests pass in CI/CD

⚠️ Potential Risks

List any possible issues that might arise with this change.

🚀 Next Steps & Improvements

This change lays a solid foundation for further optimizations. Some areas that could benefit from future improvements include:

  • 🔹 Performance optimization
  • 🔹 Increased test coverage
  • 🔹 Potential user experience enhancements

💬 Comments

Any additional context, questions, or considerations for reviewers.

Summary by CodeRabbit

  • New Features

    • Introduced a property image gallery with interactive navigation and full-screen modal view.
    • Added an interactive property calendar for date selection with unavailable dates and minimum nights.
    • Implemented a property map section with address display, directions, and neighborhood info.
    • Added a reviews section with rating breakdown, on-chain verification badge, and review listing.
    • Introduced a badge UI component for consistent status indicators.
  • Enhancements

    • Significantly improved the property detail page with a responsive three-column layout, expanded property information, image gallery, reviews, map integration, booking calendar, and richer interactivity.
    • Enhanced property cards with dynamic data, availability status badges, improved image handling, animations, and additional property info.
    • Updated property page to handle asynchronous loading of property identifiers for smoother metadata and content rendering.
  • Chores

    • Updated a development dependency version range for improved package compatibility.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 28, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

This update introduces a comprehensive revamp of the property details feature. It adds a modular image gallery, interactive calendar, reviews section, and map component. The PropertyCard now visually indicates availability. The PropertyDetail component is refactored for richer data and interactivity. Supporting UI components and styles are added or updated for a cohesive experience.

Changes

File(s) Change Summary
apps/web/src/app/property/[id]/page.tsx Updated params to be asynchronous; adjusted usage in generateMetadata and PropertyPage to await the promise.
apps/web/src/components/features/properties/FeaturedProperties.tsx Enhanced PropertyCard with availability status, badge, improved image handling, additional info, and refined UI/UX.
apps/web/src/components/features/properties/PropertyCalendar.tsx New component: Interactive date selection calendar with availability and minimum nights logic.
apps/web/src/components/features/properties/PropertyDetail.tsx Major refactor: Expanded property data, modularized with new components (gallery, calendar, map, reviews), improved layout, added interactivity (date, guest, favorite), and enhanced UI.
apps/web/src/components/features/properties/PropertyImageGallery.tsx New component: Image gallery with navigation, modal view, error handling, and accessibility features.
apps/web/src/components/features/properties/PropertyMap.tsx New component: Map section with address, coordinates, Google Maps links, and neighborhood info.
apps/web/src/components/features/properties/PropertyReviewsSection.tsx New component: Reviews section with mock data, rating breakdown, placeholders, and future blockchain integration notice.
apps/web/src/components/ui/badge.tsx New component: Badge UI element with variant styling for use in status indicators and labels.
package.json Updated @biomejs/biome devDependency to allow minor/patch upgrades.
apps/web/src/lib/data/properties.ts Added mock properties and reviews data with utility functions for retrieval and availability status.
apps/web/src/lib/types/property.ts Added comprehensive TypeScript interfaces and types for property data and related UI components.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant PropertyPage
    participant PropertyDetail
    participant PropertyImageGallery
    participant PropertyCalendar
    participant PropertyMap
    participant PropertyReviewsSection

    User->>PropertyPage: Navigate to /property/[id]
    PropertyPage->>PropertyDetail: Render with property id
    PropertyDetail->>PropertyImageGallery: Show property images
    PropertyDetail->>PropertyCalendar: Show availability and select dates
    PropertyDetail->>PropertyMap: Show location and map info
    PropertyDetail->>PropertyReviewsSection: Show reviews and ratings
    User->>PropertyDetail: Interact (expand description, select dates, etc.)
    PropertyDetail->>User: Display booking card and interactive UI
Loading

Assessment against linked issues

Objective Addressed Explanation
Implement image gallery with navigation/swiping (#87)
Render complete property information: description, amenities, policies, map (#87)
Integrate real-time availability calendar and date selection (#87)
Prominent "Book Now" button, styled and functional (#87)
ReviewsSection placeholder for future on-chain reviews (#87)
PropertyCard: optimized images, key info, availability status (#87)
Responsive, accessible, modular components; consistent styling (#87)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes found.

Poem

In a warren of code, new features appear,
With calendars, maps, and reviews oh so clear!
Properties gleam with badges and flair,
Galleries shimmer—swipe here, swipe there.
Bookings and badges, all ready to go,
This bunny approves—let the new details show! 🐰✨


📜 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 b2c26ee and a7a2837.

📒 Files selected for processing (3)
  • apps/web/package.json (1 hunks)
  • apps/web/src/components/features/properties/PropertyDetail.tsx (2 hunks)
  • package.json (1 hunks)
✨ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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: 6

🧹 Nitpick comments (9)
apps/web/src/components/features/properties/PropertyImageGallery.tsx (1)

154-161: Improve thumbnail key generation for better uniqueness.

The current key generation approach using regex replacement could lead to collisions if multiple images have similar URLs after sanitization.

Consider using a more robust key generation approach:

-              // Create a unique key using image URL hash or fallback to index
-              const imageKey = `thumbnail-${image.replace(/[^a-zA-Z0-9]/g, '')}-${index}`;
+              // Create a unique key using index and a hash of the URL
+              const imageKey = `thumbnail-${index}-${btoa(image).slice(0, 8)}`;

Alternatively, if the image URLs are stable, you could simply use the index:

-              const imageKey = `thumbnail-${image.replace(/[^a-zA-Z0-9]/g, '')}-${index}`;
+              const imageKey = `thumbnail-${index}`;
apps/web/src/components/features/properties/PropertyMap.tsx (1)

22-22: Remove or implement unused mapError state.

The mapError state is initialized but never used (indicated by the _setMapError naming). Either implement error handling or remove this unused state.

If error handling isn't needed yet:

-  const [mapError, _setMapError] = useState(false);

Or if you plan to implement error handling:

-  const [mapError, _setMapError] = useState(false);
+  const [mapError, setMapError] = useState(false);

And add proper error handling logic where needed.

apps/web/src/components/features/properties/PropertyCalendar.tsx (2)

105-146: Consider extracting complex date selection logic.

The handleDateClick function is quite complex and handles multiple scenarios. Consider extracting this logic into smaller, more focused functions for better maintainability and testing.

+  const createNormalizedDate = (date: Date) => {
+    const normalized = new Date(date);
+    normalized.setHours(0, 0, 0, 0);
+    return normalized;
+  };
+
+  const calculateDaysDiff = (from: Date, to: Date) => {
+    return Math.ceil((to.getTime() - from.getTime()) / (1000 * 60 * 60 * 24));
+  };
+
+  const handleNewSelection = (date: Date) => {
+    onDateSelect?.({ from: createNormalizedDate(date), to: undefined });
+  };
+
+  const handleRangeSelection = (date: Date, existingFrom: Date) => {
+    const normalizedDate = createNormalizedDate(date);
+    const normalizedFrom = createNormalizedDate(existingFrom);
+    
+    // Rest of the logic...
+  };

  const handleDateClick = (date: Date) => {
    if (getDateStatus(date) === 'unavailable') return;

    if (!selectedDates?.from || (selectedDates.from && selectedDates.to)) {
-      onDateSelect?.({ from: normalizedDate, to: undefined });
+      handleNewSelection(date);
      return;
    }

    if (selectedDates.from && !selectedDates.to) {
-      // Complex logic here...
+      handleRangeSelection(date, selectedDates.from);
    }
  };

157-172: Consider internationalization for month and day names.

The month and day names are hardcoded in English. For better internationalization support, consider using the browser's built-in localization or a localization library.

-  const monthNames = [
-    'January', 'February', 'March', 'April', 'May', 'June',
-    'July', 'August', 'September', 'October', 'November', 'December',
-  ];
-
-  const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
+  const monthNames = Array.from({ length: 12 }, (_, i) =>
+    new Date(2000, i, 1).toLocaleDateString('en-US', { month: 'long' })
+  );
+
+  const dayNames = Array.from({ length: 7 }, (_, i) =>
+    new Date(2000, 0, i + 1).toLocaleDateString('en-US', { weekday: 'short' })
+  );
apps/web/src/components/features/properties/FeaturedProperties.tsx (1)

173-177: Consider using property data for quick info section.

The guest capacity and bedroom count are hardcoded. Consider using actual property data or adding a TODO comment for future implementation.

If property data is available in the type definition, use it:

-            <span>Up to 4 guests</span>
-            <span>2 bedrooms</span>
+            <span>Up to {property.maxGuests || 4} guests</span>
+            <span>{property.bedrooms || 2} bedrooms</span>

Or add a TODO comment:

+          {/* TODO: Replace with actual property data when available */}
           <div className="flex items-center justify-between text-xs text-muted-foreground mb-3">
apps/web/src/components/features/properties/PropertyReviewsSection.tsx (1)

1-7: Maintain consistent import style.

The imports mix absolute paths (@/components) and tilde aliases (~/components). Use a consistent approach.

-import { Button } from '@/components/ui/button';
-import { Card } from '@/components/ui/card';
+import { Button } from '~/components/ui/button';
+import { Card } from '~/components/ui/card';
import { MessageCircle, Shield, Star, ThumbsUp } from 'lucide-react';
import { Badge } from '~/components/ui/badge';
apps/web/src/components/features/properties/PropertyDetail.tsx (3)

119-121: Add more mock properties or TODO comment.

Only one property is defined in the mock data, limiting testing capabilities.

Add a TODO comment or more mock properties:

     },
-    // Add more mock properties as needed
+    // TODO: Add more mock properties for testing different scenarios
+    '2': {
+      // ... property data
+    },
   };

186-196: Add icon mapping for "Washer & dryer" amenity.

The mock data includes "Washer & dryer" but it's not in the icon map, so it will show the default Shield icon.

Add the missing mapping:

     const iconMap: Record<string, React.ReactNode> = {
       'Wi-Fi': <Wifi className="w-5 h-5" />,
       'Air conditioning': <Wind className="w-5 h-5" />,
       'Fully equipped kitchen': <Utensils className="w-5 h-5" />,
+      'Washer & dryer': <Home className="w-5 h-5" />,
       'Free parking': <Car className="w-5 h-5" />,
       'Smart TV': <Tv className="w-5 h-5" />,
       Pool: <Waves className="w-5 h-5" />,
     };

229-238: Consider fallback for browsers without share API.

The share button uses the Web Share API but doesn't provide feedback when it's not supported.

Add fallback behavior:

             <Button
               variant="outline"
               size="sm"
               onClick={() =>
-                navigator.share?.({ title: property.title, url: window.location.href })
+                navigator.share?.({ title: property.title, url: window.location.href }) ||
+                navigator.clipboard.writeText(window.location.href).then(() => {
+                  // You could show a toast notification here
+                  console.log('Link copied to clipboard');
+                })
               }
             >
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2e9aae0 and 37b116b.

📒 Files selected for processing (9)
  • apps/web/src/app/property/[id]/page.tsx (2 hunks)
  • apps/web/src/components/features/properties/FeaturedProperties.tsx (1 hunks)
  • apps/web/src/components/features/properties/PropertyCalendar.tsx (1 hunks)
  • apps/web/src/components/features/properties/PropertyDetail.tsx (3 hunks)
  • apps/web/src/components/features/properties/PropertyImageGallery.tsx (1 hunks)
  • apps/web/src/components/features/properties/PropertyMap.tsx (1 hunks)
  • apps/web/src/components/features/properties/PropertyReviewsSection.tsx (1 hunks)
  • apps/web/src/components/ui/badge.tsx (1 hunks)
  • package.json (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
apps/web/src/app/property/[id]/page.tsx (1)
apps/backend/src/services/property.service.ts (1)
  • getPropertyById (218-301)
apps/web/src/components/ui/badge.tsx (1)
apps/web/src/lib/utils.ts (1)
  • cn (4-6)
apps/web/src/components/features/properties/PropertyReviewsSection.tsx (3)
apps/web/src/components/ui/badge.tsx (1)
  • Badge (33-33)
apps/web/src/components/ui/card.tsx (1)
  • Card (85-85)
apps/web/src/components/ui/button.tsx (1)
  • Button (59-59)
apps/web/src/components/features/properties/PropertyMap.tsx (2)
apps/web/src/components/ui/card.tsx (1)
  • Card (85-85)
apps/web/src/components/ui/button.tsx (1)
  • Button (59-59)
apps/web/src/components/features/properties/PropertyCalendar.tsx (2)
apps/web/src/components/ui/card.tsx (1)
  • Card (85-85)
apps/web/src/components/ui/button.tsx (1)
  • Button (59-59)
🔇 Additional comments (11)
apps/web/src/components/ui/badge.tsx (1)

1-34: LGTM! Well-implemented Badge component.

The Badge component follows excellent patterns with proper TypeScript typing, variant-based styling using class-variance-authority, and good accessibility features including focus ring styling. The implementation is clean and reusable.

apps/web/src/components/features/properties/PropertyImageGallery.tsx (1)

29-51: LGTM! Excellent keyboard navigation implementation.

The keyboard event handling is well-implemented with proper cleanup, preventDefault calls, and modal state checking. The useEffect dependency array correctly includes all necessary dependencies.

apps/web/src/components/features/properties/PropertyMap.tsx (1)

25-33: LGTM! Proper URL encoding for Google Maps integration.

The Google Maps URL generation functions correctly encode the address parameter to handle special characters and spaces safely.

apps/web/src/components/features/properties/PropertyCalendar.tsx (1)

33-51: LGTM! Excellent use of useMemo for calendar optimization.

The calendar days generation is properly memoized and efficiently creates a 6-week grid. The dependency array correctly includes only currentMonth.

apps/web/src/app/property/[id]/page.tsx (2)

12-12: LGTM! Correct Next.js 15 async params implementation.

The change from synchronous { id: string } to Promise<{ id: string }> correctly reflects the Next.js 15 pattern for dynamic route parameters.


16-16: LGTM! Proper async handling of params.

Both functions correctly await the params promise before destructuring the id value, maintaining the existing functionality while supporting the new async parameter pattern.

Also applies to: 27-27

apps/web/src/components/features/properties/FeaturedProperties.tsx (1)

124-159: Well-implemented UI enhancements!

The hover effects, image optimizations, and overlay gradients improve the user experience. Good use of Next.js Image component features.

apps/web/src/components/features/properties/PropertyReviewsSection.tsx (1)

72-211: Excellent component implementation!

Well-structured component with:

  • Proper accessibility with semantic HTML
  • Clear empty states
  • Good visual hierarchy
  • Blockchain verification emphasis aligns with PR objectives
apps/web/src/components/features/properties/PropertyDetail.tsx (3)

3-4: Good use of TypeScript type imports!

Using import type for type-only imports helps with tree-shaking and build optimization.


165-184: Well-implemented booking calculations!

The date calculations and cost breakdown are correctly implemented with clear structure.


394-429: Excellent accessibility implementation!

Great use of:

  • Proper label associations
  • ARIA labels for buttons
  • aria-live for dynamic content updates
  • Semantic HTML structure

},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@biomejs/biome": "^1.9.4",
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

Loosened version constraint may reduce build determinism—confirm lock-file coverage

Changing @biomejs/biome from an exact version to a caret range will auto-upgrade to future minor/patch releases. While this keeps tooling current, it can also introduce unexpected lint/format differences and impair reproducibility if the lock-file isn’t committed or regenerated in CI.

Please verify that:

  1. A lock-file (bun.lockb, package-lock.json, etc.) is checked into the repo.
  2. CI regenerates/validates the lock-file to catch breaking changes early.

Run this quick check:


🏁 Script executed:

#!/bin/bash
# Verify a lock-file exists and pins a specific biome version.
fd -H bun.lockb -t f || echo "⚠️  bun.lockb not found"
grep -A2 '"@biomejs/biome"' bun.lockb | head -n 5

Length of output: 117


Here’s a broader check across all common lock-file formats:


🏁 Script executed:

#!/bin/bash
for lockfile in bun.lockb package-lock.json yarn.lock pnpm-lock.yaml; do
  if [ -f "$lockfile" ]; then
    echo "🔍 Found $lockfile – scanning for @biomejs/biome…
"
    grep -R "@biomejs/biome" "$lockfile" || echo "⚠️ No reference to @biomejs/biome in $lockfile"
  else
    echo "⚠️ $lockfile not found"
  fi
done

Length of output: 634


Missing lockfile: build reproducibility risk

Loosening @biomejs/biome to a caret range (^1.9.4) means future minor/patch upgrades will be pulled in automatically, which can introduce formatting or lint changes and break builds if no lock-file pins dependencies.

Checks across all common lock-file formats found none committed:

  • ⚠️ bun.lockb not found
  • ⚠️ package-lock.json not found
  • ⚠️ yarn.lock not found
  • ⚠️ pnpm-lock.yaml not found

Please ensure:

  1. A lock-file is checked into version control (e.g., bun.lockb, package-lock.json, yarn.lock, or pnpm-lock.yaml).
  2. CI validates or regenerates the lock-file on every run to catch unexpected upgrades early.
🤖 Prompt for AI Agents
In package.json at line 22, the dependency version for @biomejs/biome is
specified with a caret range (^1.9.4) but no lockfile is committed, risking
build reproducibility. To fix this, generate and commit a lockfile appropriate
for your package manager (e.g., package-lock.json for npm, yarn.lock for Yarn,
pnpm-lock.yaml for pnpm, or bun.lockb for Bun) to version control. Additionally,
update your CI configuration to validate or regenerate this lockfile on each run
to detect unexpected dependency upgrades early.

Comment on lines 190 to 200
// // Fallback loading component
// const PropertyCardSkeleton = () => (
// <Card className="overflow-hidden h-full">
// <div className="h-48 w-full bg-muted animate-pulse" />
// <div className="p-4">
// <div className="h-6 bg-muted animate-pulse rounded mb-2" />
// <div className="h-4 bg-muted animate-pulse rounded mb-4 w-3/4" />
// <div className="h-10 bg-muted animate-pulse rounded" />
// </div>
// </Card>
// );
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

Remove or restore the skeleton loader.

The skeleton loader is commented out without explanation. Loading states improve perceived performance.

Either remove the commented code entirely or restore it for better UX:

-// // Fallback loading component
-// const PropertyCardSkeleton = () => (
-//   <Card className="overflow-hidden h-full">
-//     <div className="h-48 w-full bg-muted animate-pulse" />
-//     <div className="p-4">
-//       <div className="h-6 bg-muted animate-pulse rounded mb-2" />
-//       <div className="h-4 bg-muted animate-pulse rounded mb-4 w-3/4" />
-//       <div className="h-10 bg-muted animate-pulse rounded" />
-//     </div>
-//   </Card>
-// );

Or restore it and use it for loading states when fetching real data.

📝 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
// // Fallback loading component
// const PropertyCardSkeleton = () => (
// <Card className="overflow-hidden h-full">
// <div className="h-48 w-full bg-muted animate-pulse" />
// <div className="p-4">
// <div className="h-6 bg-muted animate-pulse rounded mb-2" />
// <div className="h-4 bg-muted animate-pulse rounded mb-4 w-3/4" />
// <div className="h-10 bg-muted animate-pulse rounded" />
// </div>
// </Card>
// );
🤖 Prompt for AI Agents
In apps/web/src/components/features/properties/FeaturedProperties.tsx around
lines 190 to 200, the PropertyCardSkeleton component is commented out, which
removes the loading state UI. To fix this, either delete the commented code
entirely if the skeleton loader is not needed, or uncomment and restore the
PropertyCardSkeleton component and integrate it into the loading state rendering
logic to improve user experience during data fetching.

@CMI-James
Copy link
Contributor Author

@respp please review.

@CMI-James
Copy link
Contributor Author

Hey @respp

Copy link
Contributor

@respp respp left a comment

Choose a reason for hiding this comment

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

Hey @CMI-James great job on this contribution, the property page looks really good
This could be ready to merge after addressing a couple of small things:

Just fix the messages flagged by Code Rabbit to avoid potential silent bugs down the line

The key used in the thumbnail component works, but it could be simplified a bit

Once that’s done, we’re good to go. Keep it up

@CMI-James
Copy link
Contributor Author

Hey @CMI-James great job on this contribution, the property page looks really good This could be ready to merge after addressing a couple of small things:

Just fix the messages flagged by Code Rabbit to avoid potential silent bugs down the line

The key used in the thumbnail component works, but it could be simplified a bit

Once that’s done, we’re good to go. Keep it up

Thanks for the review. I will work on the requested changes.

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: 4

🧹 Nitpick comments (1)
apps/web/src/components/features/properties/PropertyDetail.tsx (1)

3-3: Remove unused React type import.

The React type import is not used in the component.

-import type React from "react";
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 37b116b and 1ede15e.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • apps/web/src/components/features/properties/FeaturedProperties.tsx (1 hunks)
  • apps/web/src/components/features/properties/PropertyCalendar.tsx (1 hunks)
  • apps/web/src/components/features/properties/PropertyDetail.tsx (2 hunks)
  • apps/web/src/components/features/properties/PropertyImageGallery.tsx (1 hunks)
  • apps/web/src/components/features/properties/PropertyMap.tsx (1 hunks)
  • apps/web/src/components/features/properties/PropertyReviewsSection.tsx (1 hunks)
  • apps/web/src/lib/data/properties.ts (1 hunks)
  • apps/web/src/lib/types/property.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/src/lib/data/properties.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/components/features/properties/PropertyCalendar.tsx
  • apps/web/src/components/features/properties/PropertyReviewsSection.tsx
🧰 Additional context used
🧬 Code Graph Analysis (3)
apps/web/src/components/features/properties/PropertyMap.tsx (3)
apps/web/src/lib/types/property.ts (1)
  • PropertyMapProps (74-79)
apps/web/src/components/ui/card.tsx (1)
  • Card (85-85)
apps/web/src/components/ui/button.tsx (1)
  • Button (59-59)
apps/web/src/components/features/properties/PropertyDetail.tsx (9)
apps/web/src/lib/types/property.ts (1)
  • PropertyDetailProps (70-72)
apps/web/src/lib/data/properties.ts (1)
  • getPropertyById (327-329)
apps/web/src/components/ui/button.tsx (1)
  • Button (59-59)
apps/web/src/components/features/properties/PropertyImageGallery.tsx (1)
  • PropertyImageGallery (10-256)
apps/web/src/components/ui/card.tsx (1)
  • Card (85-85)
apps/web/src/components/features/properties/PropertyCalendar.tsx (1)
  • PropertyCalendar (16-333)
apps/web/src/components/features/properties/PropertyMap.tsx (1)
  • PropertyMap (9-152)
apps/web/src/components/features/properties/PropertyReviewsSection.tsx (1)
  • PropertyReviewsSection (10-198)
apps/web/src/components/ui/badge.tsx (1)
  • Badge (33-33)
apps/web/src/components/features/properties/PropertyImageGallery.tsx (3)
apps/web/src/lib/types/property.ts (1)
  • PropertyImageGalleryProps (96-100)
apps/web/src/components/ui/button.tsx (1)
  • Button (59-59)
apps/web/src/components/ui/dialog.tsx (3)
  • Dialog (112-112)
  • DialogContent (117-117)
  • DialogTitle (120-120)
🪛 Biome (1.9.4)
apps/web/src/components/features/properties/PropertyImageGallery.tsx

[error] 162-162: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation.

(lint/suspicious/noArrayIndexKey)

apps/web/src/lib/types/property.ts

[error] 90-90: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)


[error] 91-91: Unexpected any. Specify a different type.

any disables many type checking rules. Its use should be avoided.

(lint/suspicious/noExplicitAny)

🔇 Additional comments (9)
apps/web/src/components/features/properties/FeaturedProperties.tsx (2)

22-22: LGTM! Availability status is now deterministic.

The use of getAvailabilityStatus(property.id) resolves the previous issue where random status generation caused UI flickering on each render.


128-147: Excellent refactor to dynamic data.

The transition from hardcoded mock data to getFeaturedProperties() provides better consistency across the application and sets up the component for real data integration.

apps/web/src/components/features/properties/PropertyImageGallery.tsx (1)

28-50: Excellent keyboard navigation implementation.

The keyboard event handling for arrow keys and escape provides great accessibility and user experience.

apps/web/src/components/features/properties/PropertyMap.tsx (3)

12-19: Neighborhood data is now configurable via props.

Good improvement from the previous hardcoded approach. The default values provide sensible fallbacks while allowing dynamic data to be passed in.


68-87: Placeholder map implementation is appropriate for current scope.

The gradient placeholder with coordinates display provides a good interim solution until full map integration is implemented.


22-22: Unused state variable is properly indicated.

The underscore prefix on _setMapError correctly indicates this is intentionally unused, likely reserved for future error handling implementation.

apps/web/src/lib/types/property.ts (1)

1-103: Excellent type organization and comprehensive coverage.

The type definitions provide strong typing for the entire property feature set, promoting type safety and consistency across components.

apps/web/src/components/features/properties/PropertyDetail.tsx (2)

160-292: Excellent component integration and responsive design.

The integration of PropertyImageGallery, PropertyCalendar, PropertyMap, and PropertyReviewsSection creates a comprehensive property detail experience. The responsive layout with mobile-specific calendar placement shows thoughtful UX consideration.


68-88: Well-implemented calculation helpers.

The calculateNights and calculateTotal functions provide clean separation of concerns and make the pricing logic easy to understand and maintain.


const getImageSrc = (image: string, index: number) => {
if (imageErrors.has(index)) {
return "/images/house1.jpg?height=400&width=600";
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

Verify fallback image exists.

The component uses /images/house1.jpg as fallback when images fail to load. Ensure this file exists in the public directory to prevent broken image displays.

Also applies to: 86-86, 173-173, 202-202


🏁 Script executed:

#!/bin/bash
# Check if the fallback image exists
if [ -f "public/images/house1.jpg" ]; then
  echo "Fallback image exists"
else
  echo "Fallback image not found at public/images/house1.jpg"
  echo "Looking for alternative images in public/images/:"
  fd -t f "house1" public/images/ 2>/dev/null || echo "No house1 images found"
fi

Length of output: 348


Fallback image missing – add or correct /images/house1.jpg
The component’s hard-coded fallback at /images/house1.jpg can’t be found in public/images/ (checked via shell; no matching files). Without it, any failed load will show a broken image.

Please address this in apps/web/src/components/features/properties/PropertyImageGallery.tsx at:

  • Line 58
  • Line 86
  • Line 173
  • Line 202

Either:

  • Add house1.jpg to public/images/
  • Or update the returned path to point at an existing image resource
🤖 Prompt for AI Agents
In apps/web/src/components/features/properties/PropertyImageGallery.tsx at lines
58, 86, 173, and 202, the fallback image path "/images/house1.jpg" is used but
the file does not exist in public/images/. To fix this, either add the missing
house1.jpg image file to the public/images/ directory or update all fallback
image paths at these lines to point to an existing image resource that is
available in the public/images/ folder.

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: 2

🧹 Nitpick comments (3)
apps/web/src/components/features/properties/PropertyDetail.tsx (3)

76-77: Consider making fees configurable instead of hardcoded.

The cleaning fee and service fee are hardcoded values that should ideally be configurable or come from the property data.

Consider moving these to property data or configuration:

-    const cleaningFee = 150;
-    const serviceFee = 100;
+    const cleaningFee = property.fees?.cleaning ?? 150;
+    const serviceFee = property.fees?.service ?? 100;

This would require updating the property type definition to include optional fees.


101-385: Consider breaking down this large component into smaller, focused components.

The component is quite large (285 lines of JSX) and handles multiple responsibilities. Consider extracting some sections into separate components for better maintainability and reusability.

Consider extracting these sections into separate components:

  • PropertyHeader (lines 113-155)
  • PropertyInfoGrid (lines 164-183)
  • PropertyDescription (lines 186-212)
  • PropertyAmenities (lines 215-227)
  • PropertyPolicies (lines 230-251)
  • BookingCard (lines 280-380)

This would make the component more modular and easier to test and maintain.


310-341: Enhance accessibility for the guest selector.

The guest selector has good accessibility attributes but could be improved with better labeling and keyboard navigation support.

Consider these accessibility improvements:

                <label htmlFor="guest-selector" className="block text-sm font-medium mb-2">
                  Guests
                </label>
                <div
                  id="guest-selector"
                  className="flex items-center justify-between border rounded-lg p-3"
+                 role="group"
+                 aria-labelledby="guest-selector-label"
                >
+                 <span id="guest-selector-label">Guests</span>
-                 <span>Guests</span>
                  <div className="flex items-center gap-3">
                    <Button
                      variant="outline"
                      size="sm"
                      onClick={() => setGuests(Math.max(1, guests - 1))}
                      disabled={guests <= 1}
                      aria-label="Decrease number of guests"
+                     aria-describedby="guest-count"
                    >
                      -
                    </Button>
                    <span
+                     id="guest-count"
                      className="w-8 text-center"
                      aria-live="polite"
                      aria-label={`${guests} guests selected`}
                    >
                      {guests}
                    </span>
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1ede15e and 65cde59.

📒 Files selected for processing (3)
  • apps/web/src/components/features/properties/PropertyDetail.tsx (2 hunks)
  • apps/web/src/components/features/properties/PropertyImageGallery.tsx (1 hunks)
  • apps/web/src/lib/types/property.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/components/features/properties/PropertyImageGallery.tsx
  • apps/web/src/lib/types/property.ts

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: 1

🧹 Nitpick comments (3)
apps/web/src/components/features/properties/PropertyDetail.tsx (3)

74-86: Consider making fee calculations configurable.

The hardcoded cleaning fee ($150) and service fee ($100) values reduce flexibility and make the component less reusable. These values should ideally come from property data or configuration.

Consider making fees configurable:

  const calculateTotal = () => {
    const nights = calculateNights();
    const subtotal = property.price * nights;
-   const cleaningFee = 150;
-   const serviceFee = 100;
+   const cleaningFee = property.fees?.cleaning || 150;
+   const serviceFee = property.fees?.service || 100;
    return {
      nights,
      subtotal,
      cleaningFee,
      serviceFee,
      total: subtotal + cleaningFee + serviceFee,
    };
  };

88-98: Improve amenity icon fallback logic.

The fallback Shield icon for unknown amenities may not be semantically appropriate. Consider using a more generic icon or implementing a "no icon" state.

Consider using a more appropriate fallback:

  const getAmenityIcon = (amenity: string) => {
    const iconMap: Record<string, React.ReactNode> = {
      'Wi-Fi': <Wifi className="w-5 h-5" />,
      'Air conditioning': <Wind className="w-5 h-5" />,
      'Fully equipped kitchen': <Utensils className="w-5 h-5" />,
      'Free parking': <Car className="w-5 h-5" />,
      'Smart TV': <Tv className="w-5 h-5" />,
      Pool: <Waves className="w-5 h-5" />,
    };
-   return iconMap[amenity] || <Shield className="w-5 h-5" />;
+   return iconMap[amenity] || <div className="w-5 h-5 rounded-full bg-gray-200 dark:bg-gray-700" />;
  };

37-392: Consider breaking down the large component into smaller, focused components.

The PropertyDetail component is quite large (355+ lines) and handles multiple responsibilities. Consider extracting some sections into separate components for better maintainability and reusability.

Consider extracting these sections into separate components:

  • Property header with title, rating, and action buttons
  • Property info cards (guests, bedrooms, bathrooms, payment)
  • Booking card with calendar and pricing
  • Policies section

This would improve code organization and make testing easier.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 65cde59 and 79cd11a.

📒 Files selected for processing (1)
  • apps/web/src/components/features/properties/PropertyDetail.tsx (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/web/src/components/features/properties/PropertyDetail.tsx (9)
apps/web/src/lib/types/property.ts (1)
  • PropertyDetailProps (72-74)
apps/web/src/lib/data/properties.ts (1)
  • getPropertyById (327-329)
apps/web/src/components/ui/button.tsx (1)
  • Button (59-59)
apps/web/src/components/features/properties/PropertyImageGallery.tsx (1)
  • PropertyImageGallery (10-241)
apps/web/src/components/ui/card.tsx (1)
  • Card (85-85)
apps/web/src/components/features/properties/PropertyCalendar.tsx (1)
  • PropertyCalendar (16-333)
apps/web/src/components/features/properties/PropertyMap.tsx (1)
  • PropertyMap (9-152)
apps/web/src/components/features/properties/PropertyReviewsSection.tsx (1)
  • PropertyReviewsSection (10-198)
apps/web/src/components/ui/badge.tsx (1)
  • Badge (33-33)
🔇 Additional comments (3)
apps/web/src/components/features/properties/PropertyDetail.tsx (3)

134-148: Great improvement on share functionality error handling.

The share functionality now properly handles feature detection and includes error handling as suggested in previous reviews. This addresses the potential runtime errors in unsupported browsers.


182-182: Good use of Bath icon component.

The Bath icon is now properly imported and used instead of an emoji, which addresses the previous review comment about consistency and cross-platform rendering.


324-325: Add validation for guest count against property maximum.

The current validation only checks against 1 as minimum, but should also validate against the property's maximum guest capacity during decrement to prevent inconsistent state.

  <Button
    variant="outline"
    size="sm"
-   onClick={() => setGuests(Math.max(1, guests - 1))}
+   onClick={() => {
+     const newGuests = Math.max(1, guests - 1);
+     if (newGuests <= property.maxGuests) {
+       setGuests(newGuests);
+     }
+   }}
    disabled={guests <= 1}
    aria-label="Decrease number of guests"
  >

Likely an incorrect or invalid review comment.

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: 2

🧹 Nitpick comments (2)
apps/web/src/components/features/properties/PropertyDetail.tsx (2)

116-116: Optimize cost calculation with useMemo.

The calculateTotal() function is called on every render, which could impact performance. Consider memoizing this calculation.

+ import { useMemo } from 'react';

  // ... other code ...

- const costs = calculateTotal();
+ const costs = useMemo(() => calculateTotal(), [selectedDates, property.price]);

118-390: Consider breaking down this large component.

The component is well-structured and functional, but at 390+ lines, it could benefit from being split into smaller, more focused components for better maintainability and reusability.

Consider extracting these sections into separate components:

  • Property header with actions (lines 130-161)
  • Property info cards (lines 170-187)
  • Property description section (lines 189-216)
  • Property amenities section (lines 218-231)
  • Property policies section (lines 233-255)
  • Booking card (lines 282-384)

This would make the component more modular and easier to test.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 79cd11a and a56c344.

📒 Files selected for processing (3)
  • apps/web/package.json (1 hunks)
  • apps/web/src/components/features/properties/PropertyDetail.tsx (2 hunks)
  • apps/web/src/components/features/properties/PropertyImageGallery.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/components/features/properties/PropertyImageGallery.tsx
🔇 Additional comments (6)
apps/web/src/components/features/properties/PropertyDetail.tsx (6)

3-36: Well-organized imports with proper TypeScript patterns.

The imports are comprehensive and properly structured. Good use of import type for type-only imports and logical grouping of related imports.


38-44: Clean state management with proper TypeScript typing.

The state variables are well-organized and properly typed. The synchronous property fetching is appropriate for the current mock data implementation.


89-99: Well-implemented amenity icon mapping.

Good use of Record type for type-safe icon mapping with a fallback icon for unmapped amenities.


100-115: Excellent share functionality with proper error handling.

The share functionality correctly implements feature detection, proper error handling, and user feedback via toast notifications. This addresses all the concerns from previous reviews.


308-346: Excellent accessibility implementation.

Great use of accessibility attributes like aria-label, aria-live, and htmlFor for the guest selector controls. This ensures the component is usable by screen readers and other assistive technologies.


372-378: Good UX with disabled state and clear call-to-action.

The booking button correctly disables when dates aren't selected and uses a clear, branded color scheme. The Link integration ensures proper navigation.

@CMI-James
Copy link
Contributor Author

CMI-James commented Jul 3, 2025

Hey, @respp done, Kindly review.

@CMI-James
Copy link
Contributor Author

@respp, kindly review.

@respp respp merged commit 39767f8 into Stellar-Rent:main Jul 9, 2025
5 of 8 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jan 30, 2026
4 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.

2 participants