content/components/stats/stats-11.tsx
'use client';
import { Box, Edit } from 'lucide-react';
import type React from 'react';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
interface MetricCardProps {
title: string;
value: string;
limit: string;
percentage: number;
status?: string;
statusColor?: string;
progressColor: string;
details?: Array<{ label: string; value: string; color: string }>;
actionLabel: string;
actionIcon: React.ReactNode;
warningMessage?: string;
onActionClick?: () => void;
}
function MetricCard({
title,
value,
limit,
percentage,
status,
statusColor = 'text-emerald-600 dark:text-emerald-400',
progressColor,
details,
actionLabel,
actionIcon,
warningMessage,
onActionClick,
}: MetricCardProps) {
const renderProgressBar = () => {
if (details && title === 'Commands') {
const writes = Number.parseInt(details[0].value.replace(/,/g, ''));
const reads = Number.parseInt(details[1].value.replace(/,/g, ''));
const total = writes + reads;
const writesPercentage = (writes / total) * 100;
const readsPercentage = (reads / total) * 100;
return (
<div className="relative h-1 w-full overflow-hidden rounded-full bg-muted">
<div
className="absolute left-0 h-full w-full origin-left bg-emerald-500 transition-transform duration-200 ease-out"
style={{ transform: `scaleX(${writesPercentage / 100})` }}
/>
<div
className="absolute left-0 h-full w-full origin-left bg-blue-500 transition-transform duration-200 ease-out"
style={{
transform: `translateX(${writesPercentage}%) scaleX(${readsPercentage / 100})`,
}}
/>
</div>
);
}
return (
<div className="relative h-1 w-full overflow-hidden rounded-full bg-muted">
<div
className={`h-full w-full origin-left transition-transform duration-200 ease-out ${progressColor}`}
style={{ transform: `scaleX(${Math.min(percentage, 100) / 100})` }}
/>
</div>
);
};
return (
<Card className="relative max-w-[280px] overflow-hidden shadow-2xs">
<CardContent className="p-4 py-0">
<h5 className="font-normal text-muted-foreground text-xs uppercase leading-none tracking-wide dark:text-foreground/80">
{title}
</h5>
<div className="mt-2 flex items-baseline gap-1">
<div className="font-medium text-[1.2rem] text-foreground tabular-nums leading-none">
{value}
</div>
<div className="text-muted-foreground text-xs leading-none">
/ {limit}
</div>
</div>
<div className="mt-3">
{renderProgressBar()}
{details && (
<div className="my-6 mb-8">
<div className="flex flex-col gap-3">
{details.map((detail, index) => (
<div
className="flex w-full items-center text-muted-foreground text-xs leading-none dark:text-foreground/70"
key={index}
>
<div
className={`mr-[6px] h-2 w-2 rounded-full ${detail.color}`}
/>
<div className="mr-1">{detail.label}</div>
<div className="h-[9px] flex-1 border-border border-b-2 border-dotted" />
<div className="ml-1 tabular-nums">{detail.value}</div>
</div>
))}
</div>
</div>
)}
{status && (
<div className="pt-2">
<div className={statusColor}>{status}</div>
</div>
)}
{warningMessage && (
<div className="pt-2">
<div className="text-amber-700 text-sm dark:text-amber-400">
{warningMessage}
</div>
</div>
)}
</div>
<div className="absolute right-0 bottom-0 left-0">
<Button
className="h-8 w-full justify-start gap-0 rounded-none bg-muted/50 text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300"
onClick={onActionClick}
variant="ghost"
>
{actionIcon}
<span className="ml-1 text-xs">{actionLabel}</span>
</Button>
</div>
</CardContent>
</Card>
);
}
function BudgetDialog({
open,
onOpenChange,
}: {
open: boolean;
onOpenChange: (open: boolean) => void;
}) {
const [budget, setBudget] = useState('150');
const handleUpdate = () => {
onOpenChange(false);
};
return (
<Dialog onOpenChange={onOpenChange} open={open}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Update budget</DialogTitle>
<DialogDescription>
When your monthly cost reaches the max budget, we send an email and
throttle your database. You will not be charged beyond your set
budget for this database.
</DialogDescription>
</DialogHeader>
<div className="space-y-2">
<Label htmlFor="budget">Max budget per month</Label>
<Input
id="budget"
onChange={(e) => setBudget(e.target.value)}
placeholder="150"
type="number"
value={budget}
/>
</div>
<DialogFooter className="pt-2">
<Button onClick={() => onOpenChange(false)} variant="outline">
Cancel
</Button>
<Button onClick={handleUpdate}>Update</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
export default function Stats11() {
const [budgetDialogOpen, setBudgetDialogOpen] = useState(false);
return (
<>
<div className="grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-4">
<MetricCard
actionIcon={<Box className="h-4 w-4" />}
actionLabel="Upgrade"
details={[
{ label: 'Writes', value: '11,276,493', color: 'bg-emerald-500' },
{ label: 'Reads', value: '2,548,921', color: 'bg-blue-500' },
]}
limit="Unlimited"
percentage={67}
progressColor="bg-blue-500"
title="Commands"
value="13.8M"
/>
<MetricCard
actionIcon={<Box className="h-4 w-4" />}
actionLabel="Upgrade"
limit="150 GB"
percentage={94}
progressColor="bg-orange-500"
title="Bandwidth"
value="141 GB"
warningMessage="There will be a charge for the excessive bandwidth over the limit."
/>
<MetricCard
actionIcon={<Box className="h-4 w-4" />}
actionLabel="Upgrade"
limit="500 GB"
percentage={7.4}
progressColor="bg-emerald-500"
status="It's all right."
title="Storage"
value="37 GB"
/>
<MetricCard
actionIcon={<Edit className="h-4 w-4" />}
actionLabel="Change Budget"
limit="$150 Budget"
onActionClick={() => setBudgetDialogOpen(true)}
percentage={48.95}
progressColor="bg-emerald-500"
status="It's all right."
title="Cost"
value="$73.42"
/>
</div>
<BudgetDialog
onOpenChange={setBudgetDialogOpen}
open={budgetDialogOpen}
/>
</>
);
}


























