diff --git a/frontend/src/app/terminal/portfolio/page.tsx b/frontend/src/app/terminal/portfolio/page.tsx index ed9c0df..70eabea 100755 --- a/frontend/src/app/terminal/portfolio/page.tsx +++ b/frontend/src/app/terminal/portfolio/page.tsx @@ -30,6 +30,9 @@ import { Calendar, Edit3, CheckCircle, + CheckCircle2, + XCircle, + AlertTriangle, Copy, Check, Navigation, @@ -686,6 +689,9 @@ export default function PortfolioPage() { // Mobile const [navDrawerOpen, setNavDrawerOpen] = useState(false) const [expandedRow, setExpandedRow] = useState(null) + + // Health detail overlay + const [selectedHealthDomain, setSelectedHealthDomain] = useState(null) const tier = subscription?.tier || 'scout' const tierName = subscription?.tier_name || tier @@ -949,9 +955,12 @@ export default function PortfolioPage() { return ( + -

- My Portfolio -

-

- Track your domain investments. Add purchase details, monitor values, verify ownership, and list for sale. -

@@ -1151,41 +1195,8 @@ export default function PortfolioPage() {
- {/* TABS - Matching Hunt page style */} -
-
- - -
- - {/* Asset Filters - only show when assets tab active */} + {/* FILTERS BAR - Only for assets tab */} +
{activeTab === 'assets' && (
{[ @@ -1209,6 +1220,11 @@ export default function PortfolioPage() { ))}
)} + {activeTab === 'financials' && ( +
+ Financial Overview & Renewal Costs +
+ )}
{/* TAB CONTENT */} @@ -1744,6 +1760,149 @@ export default function PortfolioPage() { /> )} + {/* HEALTH DETAIL OVERLAY */} + {selectedHealthDomain && ( +
setSelectedHealthDomain(null)} + > +
e.stopPropagation()} + > + {/* Header */} +
+
+
Health Check
+

{selectedHealthDomain.domain}

+
+ +
+ + {/* Body */} +
+ {(() => { + const key = selectedHealthDomain.domain.toLowerCase() + const health = healthByDomain[key] + const isLoading = checkingHealth.has(key) + const cfg = healthConfig[(health?.status as HealthStatus) || 'unknown'] + + return ( + <> + {/* Score Badge */} +
+
+ {cfg.label} +
+ {health?.score !== undefined && ( + Score: {health.score}/100 + )} +
+ + {/* Checks */} + {health ? ( +
+ {/* DNS */} +
+ DNS Resolution + {health.dns?.has_a || health.dns?.has_ns ? ( +
+ + OK +
+ ) : ( +
+ + FAIL +
+ )} +
+ + {/* HTTP */} +
+ HTTP Access + {health.http?.is_reachable ? ( +
+ + OK +
+ ) : ( +
+ + {health.http?.error?.substring(0, 15) || 'FAIL'} +
+ )} +
+ + {/* SSL */} +
+ SSL Certificate + {health.ssl?.has_certificate ? ( +
+ + VALID +
+ ) : ( +
+ + MISSING +
+ )} +
+ + {/* Parked Status */} +
+ Not Parked + {!health.dns?.is_parked && !health.http?.is_parked ? ( +
+ + OK +
+ ) : ( +
+ + PARKED +
+ )} +
+
+ ) : ( +
+ {isLoading ? 'Running health check...' : 'Click below to run health check'} +
+ )} + + {/* Run Check Button */} + + + ) + })()} +
+
+
+ )} + {toast && } ) diff --git a/frontend/src/components/analyze/AnalyzePanel.tsx b/frontend/src/components/analyze/AnalyzePanel.tsx index 63ffc57..199eedf 100644 --- a/frontend/src/components/analyze/AnalyzePanel.tsx +++ b/frontend/src/components/analyze/AnalyzePanel.tsx @@ -20,6 +20,8 @@ import { Eye, ChevronDown, ChevronUp, + CheckCircle2, + XCircle, } from 'lucide-react' import { api } from '@/lib/api' import { useAnalyzePanelStore } from '@/lib/analyze-store' @@ -32,13 +34,13 @@ import type { AnalyzeResponse, AnalyzeSection, AnalyzeItem } from '@/components/ function getStatusColor(status: string) { switch (status) { case 'pass': - return { bg: 'bg-accent/10', text: 'text-accent', border: 'border-accent/30', icon: Check } + return { bg: 'bg-accent/20', text: 'text-accent', border: 'border-accent/40', icon: CheckCircle2 } case 'warn': - return { bg: 'bg-amber-400/10', text: 'text-amber-300', border: 'border-amber-400/30', icon: AlertTriangle } + return { bg: 'bg-amber-400/20', text: 'text-amber-300', border: 'border-amber-400/40', icon: AlertTriangle } case 'fail': - return { bg: 'bg-red-500/10', text: 'text-red-400', border: 'border-red-500/30', icon: X } + return { bg: 'bg-red-500/20', text: 'text-red-400', border: 'border-red-500/40', icon: XCircle } default: - return { bg: 'bg-white/5', text: 'text-white/40', border: 'border-white/10', icon: null } + return { bg: 'bg-white/10', text: 'text-white/50', border: 'border-white/20', icon: null } } } @@ -60,15 +62,15 @@ function getSectionIcon(key: string) { function getSectionColor(key: string) { switch (key) { case 'authority': - return 'text-blue-400' + return { text: 'text-blue-400', bg: 'bg-blue-500/10', border: 'border-blue-500/30' } case 'market': - return 'text-emerald-400' + return { text: 'text-emerald-400', bg: 'bg-emerald-500/10', border: 'border-emerald-500/30' } case 'risk': - return 'text-amber-400' + return { text: 'text-amber-400', bg: 'bg-amber-500/10', border: 'border-amber-500/30' } case 'value': - return 'text-violet-400' + return { text: 'text-violet-400', bg: 'bg-violet-500/10', border: 'border-violet-500/30' } default: - return 'text-white/60' + return { text: 'text-white/60', bg: 'bg-white/5', border: 'border-white/20' } } } @@ -203,28 +205,28 @@ export function AnalyzePanel() { return (
{/* Backdrop */} -
+
- {/* Panel */} -
+ {/* Panel - WIDER & MORE READABLE */} +
{/* Header */} -
+
{/* Top Bar */} -
-
-
- +
+
+
+
-
Analyze
-
+
Domain Analysis
+
{headerDomain}
-
+
- +
- {/* Score Bar */} + {/* Score Bar - LARGER */} {overallScore && !loading && ( -
-
+
+
= 70 ? "text-accent" : overallScore.score >= 40 ? "text-amber-400" : "text-red-400" )}> {overallScore.score}
-
+
Overall Score
+
-
- {overallScore.pass} pass - {overallScore.warn} warn - {overallScore.fail} fail +
+ {overallScore.pass} + {overallScore.warn} + {overallScore.fail}
)} {/* Mode Toggle */} -
+
{data?.cached && ( - - Cached + + ⚡ Cached )}
- {/* Body */} + {/* Body - BETTER SPACING */}
{loading ? ( -
+
- -
Analyzing...
+ +
Analyzing domain...
) : error ? ( -
-
-
Analysis Failed
-
{error}
+
+
+
Analysis Failed
+
{error}
) : !data ? ( -
-
No data
+
+
No data available
) : ( -
+
{visibleSections.map((section) => { const SectionIcon = getSectionIcon(section.key) - const sectionColor = getSectionColor(section.key) + const sectionStyle = getSectionColor(section.key) const isExpanded = expandedSections[section.key] !== false return ( -
- {/* Section Header */} +
+ {/* Section Header - LARGER */} - {/* Section Items */} + {/* Section Items - BETTER CONTRAST */} {isExpanded && ( -
+
{section.items.map((item) => { const statusStyle = getStatusColor(item.status) const StatusIcon = statusStyle.icon @@ -379,53 +385,53 @@ export function AnalyzePanel() { return (
-
- {/* Status Indicator */} +
+ {/* Status Indicator - LARGER */}
- {StatusIcon && } + {StatusIcon && }
- {/* Content */} + {/* Content - BETTER READABILITY */}
-
- +
+ {item.label} - + {item.source}
- {/* Value */} -
+ {/* Value - LARGER TEXT */} +
{isMatrix(item) ? ( -
+
{(item.value as any[]).slice(0, 12).map((row: any) => (
{String(row.domain)} - {row.status === 'available' && } + {row.status === 'available' && }
))}
) : (
{formatValue(item.value)}
@@ -434,11 +440,11 @@ export function AnalyzePanel() { {/* Details Toggle */} {item.details && Object.keys(item.details).length > 0 && ( -
- - View details +
+ + View raw details -
+                                      
                                         {JSON.stringify(item.details, null, 2)}