Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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>
);
}
}
49 changes: 16 additions & 33 deletions apps/web/src/components/shared/layout/providers.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,31 @@
'use client';

import { useTheme } from 'next-themes';
import dynamic from 'next/dynamic';
import { ThemeProvider, useTheme } from 'next-themes';
import React from 'react';
import { AuthProvider } from '~/hooks/auth/use-auth';
import { StellarProvider } from '~/hooks/stellar/stellar-context';
import { TrustlessWorkProvider } from '~/providers/TrustlessWorkProvider';

const ThemeProvider = dynamic(
() => import('next-themes').then((mod) => ({ default: mod.ThemeProvider })),
{
ssr: false,
loading: () => (
<div className="min-h-screen bg-background text-foreground">
<div className="container mx-auto p-4">Cargando...</div>
</div>
),
}
);
/**
* Syncs resolved theme to theme-portal-root. Must live inside ThemeProvider.
*/
function ThemePortalSync() {
const { resolvedTheme } = useTheme();
React.useEffect(() => {
const portal =
typeof window !== 'undefined' ? document.getElementById('theme-portal-root') : null;
if (portal && resolvedTheme) portal.className = resolvedTheme;
}, [resolvedTheme]);
return null;
}

interface ProvidersProps {
children: React.ReactNode;
}

export function Providers({ children }: ProvidersProps) {
const { resolvedTheme } = useTheme();
const [mounted, setMounted] = React.useState(false);

React.useEffect(() => {
setMounted(true);
}, []);

React.useEffect(() => {
const portal =
typeof window !== 'undefined' ? document.getElementById('theme-portal-root') : null;
if (portal && resolvedTheme) {
portal.className = resolvedTheme;
}
}, [resolvedTheme]);
React.useEffect(() => setMounted(true), []);

if (!mounted) {
return (
Expand All @@ -51,17 +39,12 @@ export function Providers({ children }: ProvidersProps) {
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem={true}
enableSystem
disableTransitionOnChange={false}
storageKey="stellar-rent-theme"
value={{
light: 'light',
dark: 'dark',
system: 'system',
}}
>
<ThemePortalSync />
<StellarProvider>
{/* You can envolve a tanstak provider one layer up of TW in order to use mutations or whatever you need */}
<TrustlessWorkProvider>
<AuthProvider>{children}</AuthProvider>
</TrustlessWorkProvider>
Expand Down
Loading