Files
ember-market-frontend/components/dashboard/content.tsx
NotII 130ecac208 Add Chromebook compatibility fixes and optimizations
Implemented comprehensive Chromebook-specific fixes including viewport adjustments, enhanced touch and keyboard detection, improved scrolling and keyboard navigation hooks, and extensive CSS optimizations for better usability. Updated chat and dashboard interfaces for larger touch targets, better focus management, and responsive layouts. Added documentation in docs/CHROMEBOOK-FIXES.md and new hooks for Chromebook scroll and keyboard handling.
2025-10-26 18:29:23 +00:00

181 lines
6.3 KiB
TypeScript

"use client"
import { useState, useEffect } from "react"
import OrderStats from "./order-stats"
import { getGreeting } from "@/lib/utils/general"
import { statsConfig } from "@/config/dashboard"
import { getRandomQuote } from "@/config/quotes"
import type { OrderStatsData } from "@/lib/types"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { ShoppingCart, RefreshCcw } from "lucide-react"
import { Button } from "@/components/ui/button"
import { useToast } from "@/components/ui/use-toast"
import { Skeleton } from "@/components/ui/skeleton"
import { clientFetch } from "@/lib/api"
interface ContentProps {
username: string
orderStats: OrderStatsData
}
interface TopProduct {
id: string;
name: string;
price: number;
image: string;
count: number;
revenue: number;
}
export default function Content({ username, orderStats }: ContentProps) {
const [greeting, setGreeting] = useState("");
const [topProducts, setTopProducts] = useState<TopProduct[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const { toast } = useToast();
// Initialize with a random quote from the quotes config
const [randomQuote, setRandomQuote] = useState(getRandomQuote());
// Fetch top-selling products data
const fetchTopProducts = async () => {
try {
setIsLoading(true);
const data = await clientFetch('/orders/top-products');
setTopProducts(data);
} catch (err) {
console.error("Error fetching top products:", err);
setError(err instanceof Error ? err.message : "Failed to fetch top products");
toast({
title: "Error loading top products",
description: "Please try refreshing the page",
variant: "destructive"
});
} finally {
setIsLoading(false);
}
};
// Initialize greeting and fetch data on component mount
useEffect(() => {
setGreeting(getGreeting());
fetchTopProducts();
}, []);
// Retry fetching top products data
const handleRetry = () => {
fetchTopProducts();
};
return (
<div className="space-y-6">
<div>
<h1 className="text-2xl font-semibold text-foreground">
{greeting}, {username}!
</h1>
<p className="text-muted-foreground mt-1 italic text-sm">
"{randomQuote.text}" <span className="font-medium">{randomQuote.author}</span>
</p>
</div>
{/* Order Statistics */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 lg:gap-6">
{statsConfig.map((stat) => (
<OrderStats
key={stat.title}
title={stat.title}
value={orderStats[stat.key as keyof OrderStatsData].toLocaleString()}
icon={stat.icon}
/>
))}
</div>
{/* Best Selling Products Section */}
<div className="mt-8">
<Card>
<CardHeader className="flex flex-row items-center justify-between pb-2">
<div>
<CardTitle>Your Best Selling Products</CardTitle>
<CardDescription>Products with the highest sales from your store</CardDescription>
</div>
{error && (
<Button
variant="outline"
size="sm"
onClick={handleRetry}
className="flex items-center gap-1"
>
<RefreshCcw className="h-3 w-3" />
<span>Retry</span>
</Button>
)}
</CardHeader>
<CardContent>
{isLoading ? (
// Loading skeleton
<div className="space-y-4">
{[...Array(5)].map((_, i) => (
<div key={i} className="flex items-center gap-4">
<Skeleton className="h-12 w-12 rounded-md" />
<div className="space-y-2">
<Skeleton className="h-4 w-40" />
<Skeleton className="h-4 w-20" />
</div>
<div className="ml-auto text-right">
<Skeleton className="h-4 w-16 ml-auto" />
<Skeleton className="h-4 w-16 ml-auto mt-2" />
</div>
</div>
))}
</div>
) : error ? (
// Error state
<div className="py-8 text-center">
<div className="text-muted-foreground mb-4">Failed to load products</div>
</div>
) : topProducts.length === 0 ? (
// Empty state
<div className="py-8 text-center">
<ShoppingCart className="h-12 w-12 mx-auto text-muted-foreground mb-4" />
<h3 className="text-lg font-medium mb-2">No products sold yet</h3>
<p className="text-muted-foreground">
Your best-selling products will appear here after you make some sales.
</p>
</div>
) : (
// Data view
<div className="space-y-4">
{topProducts.map((product) => (
<div key={product.id} className="flex items-center gap-4 py-2 border-b last:border-0">
<div
className="h-12 w-12 bg-cover bg-center rounded-md border flex-shrink-0 flex items-center justify-center overflow-hidden"
style={{
backgroundImage: product.image
? `url(/api/products/${product.id}/image)`
: 'none'
}}
>
{!product.image && (
<ShoppingCart className="h-6 w-6 text-muted-foreground" />
)}
</div>
<div className="flex-grow min-w-0">
<h4 className="font-medium truncate">{product.name}</h4>
</div>
<div className="text-right">
<div className="font-medium">{product.count} sold</div>
</div>
</div>
))}
</div>
)}
</CardContent>
</Card>
</div>
</div>
);
}