"use client";
import { useState } from "react";
import {
Bell,
TrendingDown,
TrendingUp,
Mail,
Smartphone,
MessageSquare,
Check,
DollarSign,
Calendar,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { cn } from "@/lib/utils";
type AlertCondition = "below" | "above";
type NotificationChannel = {
id: string;
label: string;
icon: React.ElementType;
description: string;
};
const notificationChannels: NotificationChannel[] = [
{ id: "email", label: "Email", icon: Mail, description: "Get notified via email" },
{ id: "push", label: "Push", icon: Smartphone, description: "Mobile push notification" },
{ id: "sms", label: "SMS", icon: MessageSquare, description: "Text message alert" },
];
const expirationOptions = [
{ value: "7", label: "7 days" },
{ value: "30", label: "30 days" },
{ value: "90", label: "90 days" },
{ value: "never", label: "Never" },
];
export const title = "React Dialog Block Price Alert";
export default function DialogPriceAlert() {
const [open, setOpen] = useState(false);
const [saved, setSaved] = useState(false);
const [targetPrice, setTargetPrice] = useState("");
const [condition, setCondition] = useState<AlertCondition>("below");
const [channels, setChannels] = useState<string[]>(["email"]);
const [expiration, setExpiration] = useState("30");
const currentPrice = 149.99;
const handleChannelToggle = (channelId: string) => {
setChannels((prev) =>
prev.includes(channelId)
? prev.filter((c) => c !== channelId)
: [...prev, channelId]
);
};
const handleSave = () => {
console.log({ targetPrice, condition, channels, expiration });
setSaved(true);
};
const handleClose = () => {
setOpen(false);
setTimeout(() => {
setSaved(false);
setTargetPrice("");
setCondition("below");
setChannels(["email"]);
setExpiration("30");
}, 200);
};
const isValidPrice = targetPrice && !isNaN(parseFloat(targetPrice)) && parseFloat(targetPrice) > 0;
const isValid = isValidPrice && channels.length > 0;
const priceDiff = isValidPrice
? ((parseFloat(targetPrice) - currentPrice) / currentPrice * 100).toFixed(1)
: null;
return (
<Dialog open={open} onOpenChange={setOpen}>
<div className="flex min-h-[350px] items-center justify-center">
<DialogTrigger asChild>
<Button variant="outline">
<Bell className="mr-2 h-4 w-4" />
Set Price Alert
</Button>
</DialogTrigger>
</div>
<DialogContent className="sm:max-w-md">
{saved ? (
<>
<DialogHeader className="text-center">
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-primary/10 mb-4">
<Check className="h-6 w-6 text-primary" />
</div>
<DialogTitle>Alert Created</DialogTitle>
<DialogDescription>
We'll notify you when the price goes {condition} ${targetPrice}.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button onClick={handleClose} className="w-full">
Done
</Button>
</DialogFooter>
</>
) : (
<>
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Bell className="h-5 w-5" />
Set Price Alert
</DialogTitle>
<DialogDescription>
Get notified when the price reaches your target.
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="flex items-center justify-between p-3 rounded-lg border bg-muted/30">
<span className="text-sm text-muted-foreground">Current price</span>
<span className="text-lg font-semibold">${currentPrice}</span>
</div>
<div className="space-y-3">
<Label>Alert me when price goes</Label>
<RadioGroup
value={condition}
onValueChange={(value) => setCondition(value as AlertCondition)}
className="grid grid-cols-2 gap-3"
>
<Label
htmlFor="below"
className={cn(
"flex items-center gap-3 rounded-lg border p-3 cursor-pointer transition-colors",
condition === "below" && "border-primary bg-primary/5"
)}
>
<RadioGroupItem value="below" id="below" />
<TrendingDown className="h-4 w-4 text-primary" />
<span className="font-medium">Below</span>
</Label>
<Label
htmlFor="above"
className={cn(
"flex items-center gap-3 rounded-lg border p-3 cursor-pointer transition-colors",
condition === "above" && "border-primary bg-primary/5"
)}
>
<RadioGroupItem value="above" id="above" />
<TrendingUp className="h-4 w-4 text-primary" />
<span className="font-medium">Above</span>
</Label>
</RadioGroup>
</div>
<div className="space-y-2">
<Label htmlFor="target-price">Target price</Label>
<div className="relative">
<DollarSign className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input
id="target-price"
type="number"
placeholder="0.00"
value={targetPrice}
onChange={(e) => setTargetPrice(e.target.value)}
className="pl-9"
step="0.01"
min="0"
/>
</div>
{priceDiff && (
<p className={cn(
"text-xs",
parseFloat(priceDiff) < 0 ? "text-primary" : "text-muted-foreground"
)}>
{parseFloat(priceDiff) < 0 ? "↓" : "↑"} {Math.abs(parseFloat(priceDiff))}% from current price
</p>
)}
</div>
<div className="space-y-3">
<Label>Notify me via</Label>
<div className="space-y-2">
{notificationChannels.map((channel) => (
<Label
key={channel.id}
htmlFor={channel.id}
className={cn(
"flex items-center gap-3 rounded-lg border p-3 cursor-pointer transition-colors",
channels.includes(channel.id) && "border-primary bg-primary/5"
)}
>
<Checkbox
id={channel.id}
checked={channels.includes(channel.id)}
onCheckedChange={() => handleChannelToggle(channel.id)}
/>
<channel.icon className="h-4 w-4 text-muted-foreground" />
<div className="flex-1">
<span className="font-medium">{channel.label}</span>
<p className="text-xs text-muted-foreground">
{channel.description}
</p>
</div>
</Label>
))}
</div>
</div>
<div className="space-y-2">
<Label htmlFor="expiration">Alert expires in</Label>
<Select value={expiration} onValueChange={setExpiration}>
<SelectTrigger id="expiration">
<Calendar className="mr-2 h-4 w-4 text-muted-foreground" />
<SelectValue />
</SelectTrigger>
<SelectContent>
{expirationOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
<DialogFooter className="gap-2 sm:gap-0">
<Button variant="outline" onClick={handleClose}>
Cancel
</Button>
<Button onClick={handleSave} disabled={!isValid}>
Create Alert
</Button>
</DialogFooter>
</>
)}
</DialogContent>
</Dialog>
);
}