Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions apps/web/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import './globals.css';
import type { Metadata } from 'next';
import { Geist } from 'next/font/google';
import './globals.css';

import { Toaster } from 'react-hot-toast';
// CORRECCIΓ“N: La ruta es ../providers porque estΓ‘n al mismo nivel que components
import { Providers } from '~/components/shared/layout/providers';
import { Navbar } from '../components/layout/Navbar';
import { RightSidebar } from '../components/layout/RightSidebar';

const geist = Geist({ subsets: ['latin'] });
const geist = Geist({
subsets: ['latin'],
});

export const metadata: Metadata = {
title: 'StellaRent',
description: 'Plataforma de alquiler de propiedades',
title: 'Stellar Rent',
Copy link
Contributor

Choose a reason for hiding this comment

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

variables in english

description: 'Alquileres con USDC en la red Stellar',
};

export default function RootLayout({
Expand All @@ -17,14 +23,18 @@ export default function RootLayout({
children: React.ReactNode;
}) {
return (
<html lang="es" suppressHydrationWarning>
<html lang="en" suppressHydrationWarning>
<head>
<meta name="color-scheme" content="light dark" />
</head>
<body className={`${geist.className} min-h-screen bg-[#0B1320] text-white antialiased`}>
<body className={`${geist.className} bg-[#0B1221] min-h-screen antialiased text-white`}>
<div id="theme-portal-root" />
<Providers>
<main className="flex-1 flex flex-col">{children}</main>
<Navbar />
<div className="flex flex-1 overflow-hidden pt-14">
<main className="flex-1 flex flex-col min-w-0">{children}</main>
<RightSidebar />
</div>
<Toaster position="top-right" />
</Providers>
</body>
Expand Down
42 changes: 42 additions & 0 deletions apps/web/src/app/messages/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use client';

import { Search } from 'lucide-react';
import { useState } from 'react';

export default function MessagesPage() {
const [searchQuery, setSearchQuery] = useState('');

return (
/* CORRECCIΓ“N: Se cambiΓ³ 64px por 56px para coincidir con la altura real del Navbar (h-14) */
Copy link
Contributor

Choose a reason for hiding this comment

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

delete this comment

<div className="flex h-[calc(100vh-56px)] w-full bg-[#0B1221] text-white overflow-hidden font-sans">
{/* Columna Izquierda */}
<div className="w-80 border-r border-gray-800 flex flex-col bg-[#0F172A]">
<div className="p-4 border-b border-gray-800">
<div className="relative">
<Search className="absolute left-4 top-2.5 h-4 w-4 text-gray-500" aria-hidden="true" />
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search chat"
aria-label="Search chats"
className="w-full bg-[#161F2F] border-none rounded-full py-2 pl-12 pr-4 text-sm focus:ring-1 focus:ring-blue-500 outline-none placeholder:text-gray-500"
/>
</div>
</div>

{/* no_chats: Posicionado arriba con padding para balance visual */}
<div className="flex-1 flex flex-col items-center pt-8">
<span className="text-gray-500 text-sm font-light italic">No chats available</span>
</div>
</div>

{/* Columna Derecha */}
<div className="flex-1 flex items-center justify-center bg-[#0B1221]">
<span className="text-gray-500 text-sm font-light tracking-[0.2em] uppercase opacity-60">
Select a chat
</span>
</div>
</div>
);
}
25 changes: 7 additions & 18 deletions apps/web/src/app/search/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import PropertyGrid from '@/components/search/PropertyGrid';
import { PropertyGrid } from '@/components/search/PropertyGrid';
import type { LatLngTuple } from 'leaflet';
import dynamic from 'next/dynamic';
import { useSearchParams } from 'next/navigation';
Expand Down Expand Up @@ -32,17 +32,13 @@ export default function SearchPage() {
{ position: [-34.6, -58.37], title: 'Cozy Studio Apartment' },
];

// Filter & sort properties with memoization
const filteredSortedProperties = useMemo(() => {
let result = [...MOCK_PROPERTIES];

const location = searchParams.get('location')?.toLowerCase() || '';
if (location) {
result = result.filter((p) => p.location.toLowerCase().includes(location));
}

result = result.filter((p) => p.price >= filters.price);

const selectedAmenities = Object.entries(filters.amenities)
.filter(([, checked]) => checked)
.map(([key]) => key.toLowerCase());
Expand All @@ -52,22 +48,12 @@ export default function SearchPage() {
selectedAmenities.every((am) => p.amenities.map((a) => a.toLowerCase()).includes(am))
);
}

if (filters.rating > 0) {
result = result.filter((p) => p.rating >= filters.rating);
}

if (sort === 'price_asc') result.sort((a, b) => a.price - b.price);
if (sort === 'price_desc') result.sort((a, b) => b.price - a.price);
if (sort === 'rating') result.sort((a, b) => b.rating - a.rating);
if (sort === 'distance') {
result.sort((a, b) => {
const aDist = Number.parseFloat(a.distance);
const bDist = Number.parseFloat(b.distance);
return aDist - bDist;
});
}

return result;
}, [filters, sort, searchParams]);

Expand All @@ -81,14 +67,17 @@ export default function SearchPage() {
setTimeout(() => {
setPage((prev) => prev + 1);
setIsLoading(false);
}, 200); // simulate load
}, 200);
}, [isLoading]);

const minMax = useMemo(() => {
const sorted = [...MOCK_PROPERTIES].sort((a, b) => a.price - b.price);
return [sorted[0]?.price || 0, sorted.at(-1)?.price || 0] as [number, number];
}, []);

// Alias para evitar el error de IntrinsicAttributes
Copy link
Contributor

Choose a reason for hiding this comment

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

could be in english please

const Grid = PropertyGrid as any;

return (
<main className="px-4 py-6 mt-10 space-y-6">
<div className="flex flex-col lg:flex-row gap-3 md:gap-6">
Expand All @@ -110,7 +99,7 @@ export default function SearchPage() {

<div className="flex flex-col lg:flex-row">
<div className="w-full">
<PropertyGrid properties={visibleProperties} onLoadMore={loadNextPage} />
<Grid properties={visibleProperties} onLoadMore={loadNextPage} />
{isLoading && <p className="text-center my-4">Loading more properties...</p>}
</div>

Expand All @@ -122,4 +111,4 @@ export default function SearchPage() {
</div>
</main>
);
}
}
203 changes: 52 additions & 151 deletions apps/web/src/constants/menu-items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,158 +7,59 @@ export interface MenuItem {
withContainer?: boolean;
}

export const GUEST_MENU_ITEMS: MenuItem[] = [
{ id: 'menu', src: '/icons/menu.webp', alt: 'Menu', label: 'Menu', href: '#' },
{
id: 'search',
src: '/icons/search.webp',
alt: 'Find a Property',
label: 'Find a Property',
href: '/search',
withContainer: true,
},
];
const ICON_MENU = { id: 'menu', src: '/icons/menu.webp', alt: 'Menu', label: 'Menu', href: '#' };
const ICON_SEARCH = {
id: 'search',
src: '/icons/search.webp',
alt: 'Search',
label: 'Find a Property',
href: '/search',
withContainer: true,
};
const ICON_FAVORITES = {
id: 'favorites',
src: '/icons/heart.webp',
alt: 'Favorites',
label: 'Favorites',
href: '/dashboard/guest?tab=bookings',
};
Comment on lines +21 to +27
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 | 🟑 Minor

Semantic mismatch: "Favorites" label points to bookings tab.

ICON_FAVORITES has label "Favorites" but navigates to /dashboard/guest?tab=bookings. This mirrors the previously flagged Settings/invitations mismatch. Consider either:

  • Renaming the label to reflect the destination (e.g., "Bookings")
  • Updating the href to an actual favorites page (e.g., /dashboard/guest?tab=favorites)
πŸ€– Prompt for AI Agents
In `@apps/web/src/constants/menu-items.ts` around lines 19 - 25, ICON_FAVORITES
currently has a semantic mismatch: its label "Favorites" points to
/dashboard/guest?tab=bookings; update the constant (ICON_FAVORITES) so label and
href matchβ€”either change label to "Bookings" to reflect href or change href to
/dashboard/guest?tab=favorites to match the "Favorites" label; ensure the alt
and id remain consistent with the chosen meaning.

const ICON_MESSAGES = {
id: 'messages',
src: '/icons/send.webp',
alt: 'Messages',
label: 'Messages',
href: '/messages',
withContainer: true,
};

export const TENANT_MENU_ITEMS: MenuItem[] = [
// TODO: Wire menu item to navigation drawer
{ id: 'menu', src: '/icons/menu.webp', alt: 'Menu', label: 'Menu', href: '#' },
{
id: 'search',
src: '/icons/search.webp',
alt: 'Find a Property',
label: 'Find a Property',
href: '/search',
withContainer: true,
},
{
id: 'calendar',
src: '/icons/lock.webp',
alt: 'My Calendar',
label: 'My Calendar',
href: '/dashboard/guest?tab=calendar',
},
{
id: 'messages',
src: '/icons/message.webp',
alt: 'Messages',
label: 'Messages',
href: '/messages',
},
{
id: 'applications',
src: '/icons/send.webp',
alt: 'Applications',
label: 'Applications',
href: '/applications',
},
{
id: 'invitations',
src: '/icons/settings.webp',
alt: 'Guest Invitations',
label: 'Guest Invitations',
href: '/invitations',
},
{
id: 'bookings',
src: '/icons/heart.webp',
alt: 'My Bookings',
label: 'My Bookings',
href: '/dashboard/guest?tab=bookings',
},
];
// CORRECCIΓ“N: Renombramos 'Settings' a 'Invitations' para que coincida con la ruta /invitations
const ICON_INVITATIONS = {
id: 'invitations',
src: '/icons/settings.webp',
alt: 'Invitations',
label: 'Invitations',
href: '/invitations',
};
Comment on lines +39 to +45
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 | 🟑 Minor

Icon asset doesn't match the "Invitations" semantics.

While the label/href mismatch has been corrected, the icon source still references /icons/settings.webp. For visual consistency, consider using an invitation-themed icon (e.g., envelope or bell icon) instead of the settings gear.

πŸ€– Prompt for AI Agents
In `@apps/web/src/constants/menu-items.ts` around lines 36 - 42, ICON_INVITATIONS
currently points to the settings icon; update the asset and alt text to match
the "Invitations" semantics by changing ICON_INVITATIONS.src to an
invitation-themed file (for example '/icons/invitations.webp' or
'/icons/envelope.webp') and adjust ICON_INVITATIONS.alt to "Invitations" or
"Envelope" as appropriate so the icon, alt and label/href all align.


export const HOST_MENU_ITEMS: MenuItem[] = [
// TODO: Wire menu item to navigation drawer
{ id: 'menu', src: '/icons/menu.webp', alt: 'Menu', label: 'Menu', href: '#' },
{
id: 'properties',
src: '/icons/search.webp',
alt: 'My Properties',
label: 'My Properties',
href: '/dashboard/host',
withContainer: true,
},
{
id: 'calendar',
src: '/icons/lock.webp',
alt: 'Property Calendar',
label: 'Property Calendar',
href: '/dashboard/host?tab=calendar',
},
{
id: 'messages',
src: '/icons/message.webp',
alt: 'Messages',
label: 'Messages',
href: '/messages',
},
{
id: 'applications',
src: '/icons/send.webp',
alt: 'Applications',
label: 'Booking Requests',
href: '/applications',
},
{
id: 'list',
src: '/icons/settings.webp',
alt: 'List Property',
label: 'List Property',
href: '/list',
},
{
id: 'bookings',
src: '/icons/heart.webp',
alt: 'Bookings',
label: 'Bookings',
href: '/dashboard/host?tab=bookings',
},
];
const ICON_LOCK = { id: 'lock', src: '/icons/lock.webp', alt: 'Lock', label: 'Private', href: '#' };
const ICON_APPLICATIONS = {
id: 'applications',
src: '/icons/message.webp',
alt: 'Applications',
label: 'Applications',
href: '/applications',
};

export const DUAL_MENU_ITEMS: MenuItem[] = [
// TODO: Wire menu item to navigation drawer
{ id: 'menu', src: '/icons/menu.webp', alt: 'Menu', label: 'Menu', href: '#' },
{
id: 'search',
src: '/icons/search.webp',
alt: 'Browse',
label: 'Browse Properties',
href: '/search',
withContainer: true,
},
{
id: 'my-bookings',
src: '/icons/heart.webp',
alt: 'My Bookings',
label: 'My Bookings',
href: '/dashboard/guest',
},
{
id: 'my-properties',
src: '/icons/lock.webp',
alt: 'My Properties',
label: 'My Properties',
href: '/dashboard/host',
},
{
id: 'messages',
src: '/icons/message.webp',
alt: 'Messages',
label: 'Messages',
href: '/messages',
},
{
id: 'applications',
src: '/icons/send.webp',
alt: 'Applications',
label: 'Applications',
href: '/applications',
},
{
id: 'calendar',
src: '/icons/settings.webp',
alt: 'Calendar',
label: 'Calendar',
href: '/dashboard/guest?tab=calendar',
},
export const GUEST_MENU_ITEMS: MenuItem[] = [
ICON_MENU,
ICON_SEARCH,
ICON_FAVORITES,
ICON_MESSAGES,
ICON_INVITATIONS, // Ahora el nombre es semΓ‘nticamente correcto
ICON_LOCK,
ICON_APPLICATIONS,
];

export const TENANT_MENU_ITEMS: MenuItem[] = [...GUEST_MENU_ITEMS];
export const HOST_MENU_ITEMS: MenuItem[] = [...GUEST_MENU_ITEMS];
export const DUAL_MENU_ITEMS: MenuItem[] = [...GUEST_MENU_ITEMS];
Loading