feat: implement onboarding flow with complex creation and email verification, and add Zustand for state management

This commit is contained in:
Jose Selesan
2026-04-21 09:14:45 -03:00
parent 23e4218f6f
commit 07408dbbbe
29 changed files with 843 additions and 71 deletions

View File

@@ -10,48 +10,33 @@ import {
} from '@/components/ui/dropdown-menu';
import { apiClient } from '@/lib/api-client';
import { useAuth } from '@/lib/auth';
import {
getCurrentComplexSlug,
setCurrentComplexSlug as persistCurrentComplexSlug,
} from '@/lib/current-complex';
import { useCurrentComplexStore } from '@/lib/stores/current-complex-store';
import { useQuery } from '@tanstack/react-query';
import { Link, Outlet, useNavigate } from '@tanstack/react-router';
import { LogOut, Menu, UserRound } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react';
import { useMemo } from 'react';
import { ThemeSwitcher } from './theme-switcher';
export function RootLayout() {
const navigate = useNavigate();
const { isAuthenticated, displayName, initials, avatarUrl, signOut } = useAuth();
const [currentComplexSlug, setCurrentComplexSlug] = useState<string | null>(null);
const { currentComplexSlug, setCurrentComplex } = useCurrentComplexStore();
const myComplexesQuery = useQuery({
queryKey: ['my-complexes'],
enabled: isAuthenticated,
queryFn: () => apiClient.complexes.listMine(),
});
useEffect(() => {
setCurrentComplexSlug(getCurrentComplexSlug());
}, []);
useEffect(() => {
if (currentComplexSlug) return;
const fallbackSlug = myComplexesQuery.data?.[0]?.complexSlug;
if (fallbackSlug) {
setCurrentComplexSlug(fallbackSlug);
persistCurrentComplexSlug(fallbackSlug);
}
}, [currentComplexSlug, myComplexesQuery.data]);
const complexSlug = currentComplexSlug || myComplexesQuery.data?.[0]?.complexSlug;
const currentComplexName = useMemo(() => {
const complexes = myComplexesQuery.data ?? [];
if (complexes.length === 0) return 'Mi complejo';
const selected = complexes.find((complex) => complex.complexSlug === currentComplexSlug);
const selected = complexes.find((complex) => complex.complexSlug === complexSlug);
return selected?.complexName ?? complexes[0]?.complexName ?? 'Mi complejo';
}, [currentComplexSlug, myComplexesQuery.data]);
}, [complexSlug, myComplexesQuery.data]);
const handleSignOut = async () => {
await signOut();
@@ -110,6 +95,26 @@ export function RootLayout() {
<DropdownMenuContent align="end" className="w-52">
<DropdownMenuLabel>Mi cuenta</DropdownMenuLabel>
<DropdownMenuSeparator />
{myComplexesQuery.data && myComplexesQuery.data.length > 1 && (
<>
<DropdownMenuLabel className="text-xs font-normal text-muted-foreground">
Cambiar complejo
</DropdownMenuLabel>
{myComplexesQuery.data.map((complex) => (
<DropdownMenuItem
key={complex.id}
onSelect={async () => {
await apiClient.complexes.select({ complexId: complex.id });
setCurrentComplex(complex.complexSlug);
}}
>
{complex.complexSlug === complexSlug && <span className="mr-2"></span>}
{complex.complexName}
</DropdownMenuItem>
))}
<DropdownMenuSeparator />
</>
)}
<DropdownMenuItem
onSelect={() => {
void navigate({ to: '/profile' });