ultisuite-client/components/admin/settings/sections/users-bulk-toolbar.tsx
R3D347HR4Y 9e9fd208ad
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat(admin-settings): enhance admin settings with new components and layout improvements
- Introduced new components for managing admin settings, including AdminListControls, AdminSettingsCard, and TechBrandSelectLabel.
- Implemented dynamic loading for admin settings sections to optimize performance.
- Enhanced the layout of various admin settings sections for better user experience.
- Updated the AiAssistantSection to include LLM provider management and improved model selection.
- Refactored authentication settings to streamline configuration and improve accessibility.
2026-06-15 00:22:20 +02:00

243 lines
7.5 KiB
TypeScript

"use client"
import { useState } from "react"
import { Ban, RotateCcw, Trash2, UserCog, UsersRound, X } from "lucide-react"
import { toast } from "sonner"
import type { AdminBulkUsersAction, AdminUserGroup, AdminUserRole } from "@/lib/api/admin-types"
import { useBulkAdminUsers } from "@/lib/api/hooks/use-admin-mutations"
import {
USER_ROLE_ICONS,
USER_ROLE_LABELS,
} from "@/lib/admin/user-role"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import { Label } from "@/components/ui/label"
const ROLE_OPTIONS: AdminUserRole[] = ["admin", "user", "guest", "suspended"]
export function UsersBulkToolbar({
selectedIds,
groups,
onClear,
}: {
selectedIds: string[]
groups: AdminUserGroup[]
onClear: () => void
}) {
const bulk = useBulkAdminUsers()
const [roleDialogOpen, setRoleDialogOpen] = useState(false)
const [groupDialogOpen, setGroupDialogOpen] = useState(false)
const [groupAction, setGroupAction] = useState<"add_to_group" | "remove_from_group">(
"add_to_group"
)
const [selectedRole, setSelectedRole] = useState<AdminUserRole>("user")
const [selectedGroupId, setSelectedGroupId] = useState<string>("")
const count = selectedIds.length
if (count === 0) return null
async function run(action: AdminBulkUsersAction, extra?: { role?: AdminUserRole; group_id?: string }) {
try {
const result = await bulk.mutateAsync({
user_ids: selectedIds,
action,
...extra,
})
if (result.failed?.length) {
toast.warning(
`${result.success_count} réussi(s), ${result.failed.length} échec(s)`
)
} else {
toast.success(`${result.success_count} utilisateur(s) mis à jour`)
}
onClear()
} catch {
toast.error("Action de masse impossible")
}
}
return (
<>
<div className="mb-3 flex flex-wrap items-center gap-2 rounded-lg border bg-muted/30 px-3 py-2">
<Button variant="ghost" size="icon" className="size-8" onClick={onClear}>
<X className="size-4" />
</Button>
<span className="text-sm font-medium">
{count} sélectionné{count > 1 ? "s" : ""}
</span>
<div className="ml-auto flex flex-wrap gap-1">
<Button
variant="outline"
size="sm"
disabled={bulk.isPending}
onClick={() => void run("disable")}
>
<Ban className="mr-2 size-4" />
Suspendre
</Button>
<Button
variant="outline"
size="sm"
disabled={bulk.isPending}
onClick={() => void run("reactivate")}
>
<RotateCcw className="mr-2 size-4" />
Réactiver
</Button>
<Button
variant="outline"
size="sm"
disabled={bulk.isPending}
onClick={() => setRoleDialogOpen(true)}
>
<UserCog className="mr-2 size-4" />
Changer le type
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" disabled={bulk.isPending || groups.length === 0}>
<UsersRound className="mr-2 size-4" />
Groupes
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => {
setGroupAction("add_to_group")
setSelectedGroupId(groups[0]?.id ?? "")
setGroupDialogOpen(true)
}}
>
Ajouter à un groupe
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
setGroupAction("remove_from_group")
setSelectedGroupId(groups[0]?.id ?? "")
setGroupDialogOpen(true)
}}
>
Retirer d&apos;un groupe
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Button
variant="outline"
size="sm"
className="text-destructive"
disabled={bulk.isPending}
onClick={() => {
if (confirm(`Supprimer ${count} utilisateur(s) ?`)) {
void run("delete")
}
}}
>
<Trash2 className="mr-2 size-4" />
Supprimer
</Button>
</div>
</div>
<Dialog open={roleDialogOpen} onOpenChange={setRoleDialogOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Changer le type</DialogTitle>
</DialogHeader>
<div className="space-y-2">
<Label>Type d&apos;utilisateur</Label>
<Select value={selectedRole} onValueChange={(v) => setSelectedRole(v as AdminUserRole)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{ROLE_OPTIONS.map((role) => {
const Icon = USER_ROLE_ICONS[role]
return (
<SelectItem key={role} value={role}>
<span className="flex items-center gap-2">
<Icon className="size-4" />
{USER_ROLE_LABELS[role]}
</span>
</SelectItem>
)
})}
</SelectContent>
</Select>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setRoleDialogOpen(false)}>
Annuler
</Button>
<Button
onClick={() => {
void run("set_role", { role: selectedRole })
setRoleDialogOpen(false)
}}
>
Appliquer
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<Dialog open={groupDialogOpen} onOpenChange={setGroupDialogOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>
{groupAction === "add_to_group" ? "Ajouter au groupe" : "Retirer du groupe"}
</DialogTitle>
</DialogHeader>
<div className="space-y-2">
<Label>Groupe</Label>
<Select value={selectedGroupId} onValueChange={setSelectedGroupId}>
<SelectTrigger>
<SelectValue placeholder="Choisir un groupe" />
</SelectTrigger>
<SelectContent>
{groups.map((group) => (
<SelectItem key={group.id} value={group.id}>
{group.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setGroupDialogOpen(false)}>
Annuler
</Button>
<Button
disabled={!selectedGroupId}
onClick={() => {
void run(groupAction, { group_id: selectedGroupId })
setGroupDialogOpen(false)
}}
>
Appliquer
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</>
)
}