174 lines
7.3 KiB
TypeScript
174 lines
7.3 KiB
TypeScript
import {
|
|
useGetAnalyticsSummary,
|
|
useGetTopTools,
|
|
useListTools,
|
|
GetTopToolsMetric
|
|
} from "@workspace/api-client-react";
|
|
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card";
|
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
import { Layout } from "@/components/layout";
|
|
import { ToolCard } from "@/components/tool-card";
|
|
import { Star, Wrench, MessageSquare, ArrowRight, Plus } from "lucide-react";
|
|
import { Link } from "wouter";
|
|
import { Button } from "@/components/ui/button";
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
export default function Home() {
|
|
const { t } = useTranslation();
|
|
const { data: summary, isLoading: loadingSummary } = useGetAnalyticsSummary();
|
|
const { data: topTools, isLoading: loadingTopTools } = useGetTopTools({ limit: 4, metric: GetTopToolsMetric.combined });
|
|
const { data: recentTools, isLoading: loadingRecent } = useListTools({ sort: "newest" });
|
|
|
|
return (
|
|
<Layout>
|
|
<div className="space-y-8 pb-8">
|
|
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-end gap-4">
|
|
<div>
|
|
<h1 className="text-3xl font-bold tracking-tight mb-2">
|
|
{t("home.welcome")}
|
|
</h1>
|
|
<p className="text-muted-foreground">{t("home.tagline")}</p>
|
|
</div>
|
|
<Button asChild>
|
|
<Link href="/tools/new">
|
|
<Plus className="w-4 h-4 mr-1" />
|
|
{t("browse.addTool")}
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Stats Banner */}
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<Card className="neon-glow-primary bg-primary/5 border-primary/20">
|
|
<CardContent className="p-6 flex items-center gap-4">
|
|
<div className="p-3 bg-primary/10 rounded-lg text-primary">
|
|
<Wrench className="w-6 h-6" />
|
|
</div>
|
|
<div>
|
|
<p className="text-sm font-medium text-muted-foreground mb-1">{t("home.totalTools")}</p>
|
|
{loadingSummary ? (
|
|
<Skeleton className="h-8 w-16" />
|
|
) : (
|
|
<h3 className="text-3xl font-bold">{summary?.totalTools || 0}</h3>
|
|
)}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card className="neon-glow-primary bg-primary/5 border-primary/20">
|
|
<CardContent className="p-6 flex items-center gap-4">
|
|
<div className="p-3 bg-primary/10 rounded-lg text-primary">
|
|
<MessageSquare className="w-6 h-6" />
|
|
</div>
|
|
<div>
|
|
<p className="text-sm font-medium text-muted-foreground mb-1">{t("home.totalRatings")}</p>
|
|
{loadingSummary ? (
|
|
<Skeleton className="h-8 w-16" />
|
|
) : (
|
|
<h3 className="text-3xl font-bold">{summary?.totalRatings || 0}</h3>
|
|
)}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card className="neon-glow-primary bg-primary/5 border-primary/20">
|
|
<CardContent className="p-6 flex items-center gap-4">
|
|
<div className="p-3 bg-primary/10 rounded-lg text-primary">
|
|
<Star className="w-6 h-6" />
|
|
</div>
|
|
<div>
|
|
<p className="text-sm font-medium text-muted-foreground mb-1">{t("home.avgRating")}</p>
|
|
{loadingSummary ? (
|
|
<Skeleton className="h-8 w-16" />
|
|
) : (
|
|
<h3 className="text-3xl font-bold">{summary?.avgCombined ? summary.avgCombined.toFixed(1) : "0.0"}</h3>
|
|
)}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Top Tools Section */}
|
|
<section>
|
|
<div className="flex justify-between items-end mb-4">
|
|
<div>
|
|
<h2 className="text-2xl font-bold tracking-tight mb-1">{t("home.topRated")}</h2>
|
|
<p className="text-muted-foreground text-sm">{t("home.topRatedSub")}</p>
|
|
</div>
|
|
<Button variant="ghost" className="text-primary hidden sm:flex" asChild>
|
|
<Link href="/tools?sort=top_rated">
|
|
{t("common.viewAll")} <ArrowRight className="w-4 h-4 ml-2" />
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
|
|
{loadingTopTools ? (
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{[1, 2, 3, 4].map(i => (
|
|
<Skeleton key={`sk-top-${i}`} className="h-[200px] w-full rounded-xl" />
|
|
))}
|
|
</div>
|
|
) : topTools && topTools.length > 0 ? (
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{topTools.map(({ tool }) => (
|
|
<ToolCard key={tool.id} tool={tool} />
|
|
))}
|
|
</div>
|
|
) : (
|
|
<Card className="bg-muted/30 border-dashed">
|
|
<CardContent className="flex flex-col items-center justify-center p-12 text-center">
|
|
<Star className="w-12 h-12 text-muted-foreground/30 mb-4" />
|
|
<h3 className="text-lg font-medium">{t("home.noRatingsYet")}</h3>
|
|
<p className="text-muted-foreground max-w-sm mt-1 mb-4">{t("home.noRatingsSub")}</p>
|
|
<Button asChild>
|
|
<Link href="/tools">{t("common.browseTools")}</Link>
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
</section>
|
|
|
|
{/* Recently Added Section */}
|
|
<section>
|
|
<div className="flex justify-between items-end mb-4">
|
|
<div>
|
|
<h2 className="text-2xl font-bold tracking-tight mb-1">{t("home.recentlyAdded")}</h2>
|
|
<p className="text-muted-foreground text-sm">{t("home.recentlyAddedSub")}</p>
|
|
</div>
|
|
<Button variant="ghost" className="text-primary hidden sm:flex" asChild>
|
|
<Link href="/tools?sort=newest">
|
|
{t("common.viewAll")} <ArrowRight className="w-4 h-4 ml-2" />
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
|
|
{loadingRecent ? (
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{[1, 2, 3, 4].map(i => (
|
|
<Skeleton key={`sk-recent-${i}`} className="h-[200px] w-full rounded-xl" />
|
|
))}
|
|
</div>
|
|
) : recentTools && recentTools.length > 0 ? (
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{recentTools.slice(0, 4).map((tool) => (
|
|
<ToolCard key={tool.id} tool={tool} />
|
|
))}
|
|
</div>
|
|
) : (
|
|
<Card className="bg-muted/30 border-dashed">
|
|
<CardContent className="flex flex-col items-center justify-center p-12 text-center">
|
|
<Wrench className="w-12 h-12 text-muted-foreground/30 mb-4" />
|
|
<h3 className="text-lg font-medium">{t("home.noTools")}</h3>
|
|
<p className="text-muted-foreground max-w-sm mt-1 mb-4">{t("home.noToolsSub")}</p>
|
|
<Button asChild>
|
|
<Link href="/tools/new">{t("browse.addTool")}</Link>
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
</section>
|
|
</div>
|
|
</Layout>
|
|
);
|
|
}
|