"use client";
import { useState } from "react";
import {
Link2,
Unlink,
Check,
ExternalLink,
Loader2,
AlertCircle,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Separator } from "@/components/ui/separator";
import { cn } from "@/lib/utils";
type Provider = {
id: string;
name: string;
icon: string;
description: string;
connected: boolean;
account?: {
name: string;
email: string;
avatar?: string;
};
connectedAt?: Date;
};
const initialProviders: Provider[] = [
{
id: "google",
name: "Google",
icon: "G",
description: "Sign in and sync with Google services",
connected: true,
account: {
name: "John Doe",
email: "[email protected]",
},
connectedAt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
},
{
id: "github",
name: "GitHub",
icon: "GH",
description: "Access repositories and gists",
connected: true,
account: {
name: "johndoe",
email: "[email protected]",
},
connectedAt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
},
{
id: "slack",
name: "Slack",
icon: "S",
description: "Send notifications to Slack channels",
connected: false,
},
{
id: "microsoft",
name: "Microsoft",
icon: "M",
description: "Connect with Microsoft 365 services",
connected: false,
},
{
id: "twitter",
name: "Twitter",
icon: "X",
description: "Share updates to Twitter",
connected: false,
},
];
export const title = "React Dialog Block Connect Account";
export default function DialogConnectAccount() {
const [open, setOpen] = useState(false);
const [providers, setProviders] = useState<Provider[]>(initialProviders);
const [connectingId, setConnectingId] = useState<string | null>(null);
const [disconnectProvider, setDisconnectProvider] = useState<Provider | null>(null);
const connectedCount = providers.filter((p) => p.connected).length;
const handleConnect = async (providerId: string) => {
setConnectingId(providerId);
// Simulate OAuth flow
await new Promise((resolve) => setTimeout(resolve, 1500));
setProviders((prev) =>
prev.map((p) =>
p.id === providerId
? {
...p,
connected: true,
account: {
name: "Connected User",
email: `user@${providerId}.com`,
},
connectedAt: new Date(),
}
: p
)
);
setConnectingId(null);
};
const handleDisconnect = async () => {
if (!disconnectProvider) return;
setProviders((prev) =>
prev.map((p) =>
p.id === disconnectProvider.id
? { ...p, connected: false, account: undefined, connectedAt: undefined }
: p
)
);
setDisconnectProvider(null);
};
const formatDate = (date: Date) => {
return new Intl.DateTimeFormat("en-US", {
month: "short",
day: "numeric",
year: "numeric",
}).format(date);
};
return (
<>
<Dialog open={open} onOpenChange={setOpen}>
<div className="flex min-h-[350px] items-center justify-center">
<DialogTrigger asChild>
<Button variant="outline">
<Link2 className="mr-2 h-4 w-4" />
Connected Accounts
</Button>
</DialogTrigger>
</div>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Link2 className="h-5 w-5" />
Connected Accounts
</DialogTitle>
<DialogDescription>
Manage your linked accounts and services.
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">
{connectedCount} of {providers.length} accounts connected
</span>
</div>
<div className="space-y-3">
{providers.map((provider) => (
<div
key={provider.id}
className={cn(
"rounded-lg border p-4 transition-colors",
provider.connected && "border-primary/50"
)}
>
<div className="flex items-start justify-between">
<div className="flex items-start gap-3">
<div
className={cn(
"flex h-10 w-10 items-center justify-center rounded-lg text-sm font-bold",
provider.connected
? "bg-primary text-primary-foreground"
: "bg-muted text-muted-foreground"
)}
>
{provider.icon}
</div>
<div className="space-y-1">
<div className="flex items-center gap-2">
<span className="font-medium">{provider.name}</span>
{provider.connected && (
<Badge variant="secondary" className="text-xs">
<Check className="mr-1 h-3 w-3" />
Connected
</Badge>
)}
</div>
<p className="text-xs text-muted-foreground">
{provider.description}
</p>
{provider.connected && provider.account && (
<div className="flex items-center gap-2 mt-2">
<Avatar className="h-5 w-5">
<AvatarImage src={provider.account.avatar} />
<AvatarFallback className="text-xs">
{provider.account.name.charAt(0)}
</AvatarFallback>
</Avatar>
<span className="text-xs text-muted-foreground">
{provider.account.email}
</span>
</div>
)}
{provider.connectedAt && (
<p className="text-xs text-muted-foreground">
Connected {formatDate(provider.connectedAt)}
</p>
)}
</div>
</div>
<div>
{provider.connected ? (
<Button
variant="ghost"
size="sm"
onClick={() => setDisconnectProvider(provider)}
>
<Unlink className="mr-2 h-4 w-4" />
Disconnect
</Button>
) : (
<Button
variant="outline"
size="sm"
onClick={() => handleConnect(provider.id)}
disabled={connectingId === provider.id}
>
{connectingId === provider.id ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Connecting...
</>
) : (
<>
<ExternalLink className="mr-2 h-4 w-4" />
Connect
</>
)}
</Button>
)}
</div>
</div>
</div>
))}
</div>
</div>
</DialogContent>
</Dialog>
<AlertDialog
open={!!disconnectProvider}
onOpenChange={(open) => !open && setDisconnectProvider(null)}
>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle className="flex items-center gap-2">
<AlertCircle className="h-5 w-5 text-destructive" />
Disconnect {disconnectProvider?.name}?
</AlertDialogTitle>
<AlertDialogDescription>
This will remove access to your {disconnectProvider?.name} account.
You can reconnect at any time.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={handleDisconnect}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
>
<Unlink className="mr-2 h-4 w-4" />
Disconnect
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
);
}