316 lines
11 KiB
TypeScript
316 lines
11 KiB
TypeScript
"use client"
|
|
|
|
import { useState } from "react"
|
|
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet"
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
|
import { UserForm } from "./user-form"
|
|
import { Button } from "@/components/ui/button"
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
AlertDialogTrigger,
|
|
} from "@/components/ui/alert-dialog"
|
|
|
|
|
|
import { Mail, ShieldAlert, Trash2, Ban } from "lucide-react"
|
|
import { User } from "./column"
|
|
import { toast } from "@/hooks/use-toast"
|
|
|
|
interface UserDetailSheetProps {
|
|
user: User
|
|
open: boolean
|
|
onOpenChange: (open: boolean) => void
|
|
}
|
|
|
|
export function UserDetailSheet({ user, open, onOpenChange }: UserDetailSheetProps) {
|
|
const [isDeleting, setIsDeleting] = useState(false)
|
|
const [isResetting, setIsResetting] = useState(false)
|
|
const [isSendingMagic, setIsSendingMagic] = useState(false)
|
|
const [isRemovingMfa, setIsRemovingMfa] = useState(false)
|
|
const [isBanning, setIsBanning] = useState(false)
|
|
|
|
const handleResetPassword = async () => {
|
|
try {
|
|
setIsResetting(true)
|
|
|
|
toast({
|
|
title: "Success",
|
|
description: "Password reset email sent",
|
|
|
|
})
|
|
} catch (error) {
|
|
toast({
|
|
title: "Error",
|
|
description: "Failed to send reset password email",
|
|
variant: "destructive",
|
|
})
|
|
console.error(error)
|
|
} finally {
|
|
setIsResetting(false)
|
|
}
|
|
}
|
|
|
|
const handleSendMagicLink = async () => {
|
|
try {
|
|
setIsSendingMagic(true)
|
|
|
|
toast({
|
|
title: "Success",
|
|
description: "Magic link sent",
|
|
|
|
})
|
|
} catch (error) {
|
|
toast({
|
|
title: "Error",
|
|
description: "Failed to send magic link",
|
|
variant: "destructive",
|
|
})
|
|
console.error(error)
|
|
} finally {
|
|
setIsSendingMagic(false)
|
|
}
|
|
}
|
|
|
|
const handleRemoveMfa = async () => {
|
|
try {
|
|
setIsRemovingMfa(true)
|
|
await removeMfaFactors(user.id)
|
|
toast({
|
|
title: "Success",
|
|
description: "MFA factors removed",
|
|
|
|
})
|
|
} catch (error) {
|
|
toast({
|
|
title: "Error",
|
|
description: "Failed to remove MFA factors",
|
|
variant: "destructive",
|
|
})
|
|
console.error(error)
|
|
} finally {
|
|
setIsRemovingMfa(false)
|
|
}
|
|
}
|
|
|
|
const handleBanUser = async () => {
|
|
try {
|
|
setIsBanning(true)
|
|
await banUser(user.id)
|
|
toast({
|
|
title: "Success",
|
|
description: "User banned successfully",
|
|
|
|
})
|
|
} catch (error) {
|
|
toast({
|
|
title: "Error",
|
|
description: "Failed to ban user",
|
|
variant: "destructive",
|
|
})
|
|
console.error(error)
|
|
} finally {
|
|
setIsBanning(false)
|
|
}
|
|
}
|
|
|
|
const handleDeleteUser = async () => {
|
|
try {
|
|
setIsDeleting(true)
|
|
await deleteUser(user.id)
|
|
toast({
|
|
title: "Success",
|
|
description: "User deleted successfully",
|
|
|
|
})
|
|
onOpenChange(false)
|
|
} catch (error) {
|
|
toast({
|
|
title: "Error",
|
|
description: "Failed to delete user",
|
|
variant: "destructive",
|
|
})
|
|
console.error(error)
|
|
} finally {
|
|
setIsDeleting(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
<SheetContent className="sm:max-w-xl overflow-y-auto bg-[#121212] text-white border-l border-[#2a2a2a]">
|
|
<SheetHeader>
|
|
<SheetTitle className="text-white">User Details</SheetTitle>
|
|
</SheetHeader>
|
|
<div className="py-6">
|
|
<div className="flex items-center justify-between mb-6">
|
|
<div>
|
|
<h2 className="text-xl font-semibold">{user.email}</h2>
|
|
<p className="text-sm text-gray-400">ID: {user.id}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<Tabs defaultValue="details" className="w-full">
|
|
<TabsList className="grid w-full grid-cols-3 bg-[#1c1c1c]">
|
|
<TabsTrigger value="details" className="data-[state=active]:bg-[#2a2a2a]">
|
|
Details
|
|
</TabsTrigger>
|
|
<TabsTrigger value="profile" className="data-[state=active]:bg-[#2a2a2a]">
|
|
Profile
|
|
</TabsTrigger>
|
|
<TabsTrigger value="security" className="data-[state=active]:bg-[#2a2a2a]">
|
|
Security
|
|
</TabsTrigger>
|
|
</TabsList>
|
|
<TabsContent value="details" className="mt-4">
|
|
<UserForm user={user} />
|
|
</TabsContent>
|
|
<TabsContent value="profile" className="mt-4">
|
|
<div className="space-y-4">
|
|
<h3 className="text-lg font-medium">Profile Information</h3>
|
|
<p className="text-sm text-gray-400">Profile data will be loaded here.</p>
|
|
</div>
|
|
</TabsContent>
|
|
<TabsContent value="security" className="mt-4">
|
|
<div className="space-y-6">
|
|
<div className="bg-[#1c1c1c] p-4 rounded-md border border-[#2a2a2a]">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h3 className="font-medium">Reset password</h3>
|
|
<p className="text-sm text-gray-400">Send a password recovery email to the user</p>
|
|
</div>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={handleResetPassword}
|
|
disabled={isResetting}
|
|
className="bg-[#121212] border-[#2a2a2a] hover:bg-[#2a2a2a]"
|
|
>
|
|
<Mail className="mr-2 h-4 w-4" />
|
|
Send password recovery
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-[#1c1c1c] p-4 rounded-md border border-[#2a2a2a]">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h3 className="font-medium">Send magic link</h3>
|
|
<p className="text-sm text-gray-400">Passwordless login via email for the user</p>
|
|
</div>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={handleSendMagicLink}
|
|
disabled={isSendingMagic}
|
|
className="bg-[#121212] border-[#2a2a2a] hover:bg-[#2a2a2a]"
|
|
>
|
|
<Mail className="mr-2 h-4 w-4" />
|
|
Send magic link
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="text-lg font-medium mb-2">Danger zone</h3>
|
|
<p className="text-sm text-gray-400 mb-4">
|
|
Be wary of the following features as they cannot be undone.
|
|
</p>
|
|
|
|
<div className="space-y-4 border border-red-900/50 rounded-md overflow-hidden">
|
|
<div className="p-4 border-b border-red-900/50">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h4 className="font-medium">Remove MFA factors</h4>
|
|
<p className="text-sm text-gray-400">This will log the user out of all active sessions</p>
|
|
</div>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={handleRemoveMfa}
|
|
disabled={isRemovingMfa}
|
|
className="bg-[#121212] border-[#2a2a2a] hover:bg-[#2a2a2a]"
|
|
>
|
|
<ShieldAlert className="mr-2 h-4 w-4" />
|
|
Remove MFA factors
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="p-4 border-b border-red-900/50">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h4 className="font-medium">Ban user</h4>
|
|
<p className="text-sm text-gray-400">Revoke access to the project for a set duration</p>
|
|
</div>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={handleBanUser}
|
|
disabled={isBanning}
|
|
className="bg-[#121212] border-[#2a2a2a] hover:bg-[#2a2a2a]"
|
|
>
|
|
<Ban className="mr-2 h-4 w-4" />
|
|
Ban user
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="p-4">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h4 className="font-medium">Delete user</h4>
|
|
<p className="text-sm text-gray-400">User will no longer have access to the project</p>
|
|
</div>
|
|
<AlertDialog>
|
|
<AlertDialogTrigger asChild>
|
|
<Button
|
|
variant="destructive"
|
|
size="sm"
|
|
className="bg-red-900 hover:bg-red-800 text-white border-0"
|
|
>
|
|
<Trash2 className="mr-2 h-4 w-4" />
|
|
Delete user
|
|
</Button>
|
|
</AlertDialogTrigger>
|
|
<AlertDialogContent className="bg-[#121212] text-white border-[#2a2a2a]">
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
|
<AlertDialogDescription className="text-gray-400">
|
|
This action cannot be undone. This will permanently delete the user account and remove
|
|
their data from our servers.
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel className="bg-[#1c1c1c] text-white border-[#2a2a2a] hover:bg-[#2a2a2a]">
|
|
Cancel
|
|
</AlertDialogCancel>
|
|
<AlertDialogAction
|
|
onClick={handleDeleteUser}
|
|
disabled={isDeleting}
|
|
className="bg-red-900 hover:bg-red-800 text-white border-0"
|
|
>
|
|
Delete
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</TabsContent>
|
|
</Tabs>
|
|
</div>
|
|
</SheetContent>
|
|
</Sheet>
|
|
)
|
|
}
|
|
|