From 295bd5e31e13a7e698254419bcf8175cafd66de3 Mon Sep 17 00:00:00 2001 From: Adrian Flores Cortes Date: Tue, 3 Feb 2026 23:49:26 -0600 Subject: [PATCH] [SPRINT-1] feat: Resolve routing and improve auth session management SUBTASK-001: Routing fixes - Add lazy-loaded route for /portfolio/:portfolioId - Add navigation links from PortfolioDashboard to portfolio detail - Verify /settings/billing is intentional dual-route (no changes needed) SUBTASK-002: Auth improvements - Extend ActiveSession type with device details (deviceType, browser, os, location) - DeviceCard now uses backend data when available, falls back to userAgent parsing Co-Authored-By: Claude Opus 4.5 --- src/App.tsx | 2 + src/modules/auth/components/DeviceCard.tsx | 16 +++++++- .../portfolio/pages/PortfolioDashboard.tsx | 40 ++++++++++++++----- src/services/auth.service.ts | 5 +++ 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 8925352..9aabe07 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -41,6 +41,7 @@ const Assistant = lazy(() => import('./modules/assistant/pages/Assistant')); // Lazy load modules - Portfolio const PortfolioDashboard = lazy(() => import('./modules/portfolio/pages/PortfolioDashboard')); +const PortfolioDetailPage = lazy(() => import('./modules/portfolio/pages/PortfolioDetailPage')); const CreatePortfolio = lazy(() => import('./modules/portfolio/pages/CreatePortfolio')); const CreateGoal = lazy(() => import('./modules/portfolio/pages/CreateGoal')); const EditAllocations = lazy(() => import('./modules/portfolio/pages/EditAllocations')); @@ -112,6 +113,7 @@ function App() { } /> } /> } /> + } /> } /> {/* Education */} diff --git a/src/modules/auth/components/DeviceCard.tsx b/src/modules/auth/components/DeviceCard.tsx index 420d56c..bec9707 100644 --- a/src/modules/auth/components/DeviceCard.tsx +++ b/src/modules/auth/components/DeviceCard.tsx @@ -55,9 +55,21 @@ interface DeviceCardProps { export function DeviceCard({ session, isRevoking, onRevoke }: DeviceCardProps) { const [showConfirm, setShowConfirm] = useState(false); - const deviceInfo = authService.parseUserAgent(session.userAgent); + const parsedInfo = authService.parseUserAgent(session.userAgent); const relativeTime = authService.formatRelativeTime(session.lastActiveAt); + // Use backend-provided device info if available, otherwise use parsed info + const deviceInfo = { + type: (session.deviceType as 'desktop' | 'mobile' | 'tablet' | 'unknown') || parsedInfo.type, + os: session.os || parsedInfo.os, + browser: session.browser || parsedInfo.browser, + }; + + // Format location from backend data + const location = session.city && session.countryCode + ? `${session.city}, ${session.countryCode}` + : session.countryCode || null; + // Get device icon based on type const DeviceIcon = { desktop: DesktopIcon, @@ -117,7 +129,7 @@ export function DeviceCard({ session, isRevoking, onRevoke }: DeviceCardProps) { - {session.ipAddress || 'Unknown IP'} + {location ? `${location} (${session.ipAddress || 'Unknown IP'})` : session.ipAddress || 'Unknown IP'}

diff --git a/src/modules/portfolio/pages/PortfolioDashboard.tsx b/src/modules/portfolio/pages/PortfolioDashboard.tsx index d7ec413..327312b 100644 --- a/src/modules/portfolio/pages/PortfolioDashboard.tsx +++ b/src/modules/portfolio/pages/PortfolioDashboard.tsx @@ -195,17 +195,29 @@ export default function PortfolioDashboard() { {portfolios.length > 1 && (

{portfolios.map((p) => ( - +
+ + + + +
))}
)} @@ -388,6 +400,12 @@ export default function PortfolioDashboard() { + + Ver Detalle Completo + diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index e145677..1db0359 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -14,6 +14,11 @@ export interface ActiveSession { id: string; userAgent: string; ipAddress: string; + deviceType?: string; + browser?: string; + os?: string; + countryCode?: string; + city?: string; createdAt: string; lastActiveAt: string; isCurrent: boolean;