feat: Implement core application structure with new dashboard, settings, and help pages, and enhance opportunities management with persistence and filtering.

This commit is contained in:
2026-02-03 20:05:30 +00:00
parent 609b9da020
commit 885bbbf954
21 changed files with 1282 additions and 106 deletions

View File

@@ -0,0 +1,132 @@
"use client"
import { useQuery } from "convex/react"
import { api } from "@/convex/_generated/api"
import { useProject } from "@/components/project-context"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
export default function Page() {
const { selectedProjectId } = useProject()
const projects = useQuery(api.projects.getProjects)
const analysis = useQuery(
api.analyses.getLatestByProject,
selectedProjectId ? { projectId: selectedProjectId as any } : "skip"
)
const selectedProject = projects?.find((project) => project._id === selectedProjectId)
const isLoading = selectedProjectId && analysis === undefined
if (!selectedProjectId && projects && projects.length === 0) {
return (
<div className="flex flex-1 items-center justify-center p-10 text-center">
<div className="space-y-2">
<h2 className="text-xl font-semibold">No projects yet</h2>
<p className="text-muted-foreground">Complete onboarding to create your first project.</p>
</div>
</div>
)
}
if (isLoading) {
return (
<div className="flex flex-1 items-center justify-center p-10 text-center text-muted-foreground">
Loading analysis...
</div>
)
}
if (!analysis) {
return (
<div className="flex flex-1 items-center justify-center p-10 text-center">
<div className="space-y-2">
<h2 className="text-xl font-semibold">No analysis yet</h2>
<p className="text-muted-foreground">Run onboarding to analyze a product for this project.</p>
</div>
</div>
)
}
return (
<div className="flex flex-1 flex-col gap-6 p-4 lg:p-8">
<div className="space-y-2">
<div className="flex items-center gap-2">
<h1 className="text-2xl font-semibold">{analysis.productName}</h1>
{selectedProject?.name && (
<Badge variant="outline">{selectedProject.name}</Badge>
)}
</div>
<p className="text-muted-foreground">{analysis.tagline}</p>
<p className="max-w-3xl text-sm text-muted-foreground">{analysis.description}</p>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-5">
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm">Features</CardTitle>
</CardHeader>
<CardContent className="text-2xl font-semibold">{analysis.features.length}</CardContent>
</Card>
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm">Keywords</CardTitle>
</CardHeader>
<CardContent className="text-2xl font-semibold">{analysis.keywords.length}</CardContent>
</Card>
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm">Personas</CardTitle>
</CardHeader>
<CardContent className="text-2xl font-semibold">{analysis.personas.length}</CardContent>
</Card>
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm">Competitors</CardTitle>
</CardHeader>
<CardContent className="text-2xl font-semibold">{analysis.competitors.length}</CardContent>
</Card>
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm">Use Cases</CardTitle>
</CardHeader>
<CardContent className="text-2xl font-semibold">{analysis.useCases.length}</CardContent>
</Card>
</div>
<div className="grid gap-4 lg:grid-cols-2">
<Card>
<CardHeader>
<CardTitle className="text-base">Top Features</CardTitle>
</CardHeader>
<CardContent className="space-y-2">
{analysis.features.slice(0, 6).map((feature, index) => (
<div key={`${feature.name}-${index}`} className="flex items-start gap-2">
<span className="mt-1 h-2 w-2 rounded-full bg-primary" />
<div>
<p className="text-sm font-medium">{feature.name}</p>
<p className="text-xs text-muted-foreground">{feature.description}</p>
</div>
</div>
))}
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-base">Top Problems Solved</CardTitle>
</CardHeader>
<CardContent className="space-y-2">
{analysis.problemsSolved.slice(0, 6).map((problem, index) => (
<div key={`${problem.problem}-${index}`} className="flex items-start gap-2">
<span className="mt-1 h-2 w-2 rounded-full bg-primary" />
<div>
<p className="text-sm font-medium">{problem.problem}</p>
<p className="text-xs text-muted-foreground">{problem.emotionalImpact}</p>
</div>
</div>
))}
</CardContent>
</Card>
</div>
</div>
)
}