120 lines
4 KiB
TypeScript
120 lines
4 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useTransition } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import { UserRole } from "@/generated/prisma/enums";
|
|
import { toggleUserActiveAction, updateUserRoleAction } from "../../actions";
|
|
|
|
const ROLE_OPTIONS: { value: string; label: string }[] = [
|
|
{ value: UserRole.OWNER, label: "Propriétaire" },
|
|
{ value: UserRole.CE_MANAGER, label: "CE — Manager" },
|
|
{ value: UserRole.CE_MEMBER, label: "CE — Membre" },
|
|
{ value: UserRole.TOURIST, label: "Touriste" },
|
|
{ value: UserRole.ADMIN, label: "Admin" },
|
|
];
|
|
|
|
export function UserActions({
|
|
id,
|
|
role,
|
|
isActive,
|
|
}: {
|
|
id: string;
|
|
role: string;
|
|
isActive: boolean;
|
|
}) {
|
|
const router = useRouter();
|
|
const [pending, startTransition] = useTransition();
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [selectedRole, setSelectedRole] = useState(role);
|
|
const [confirmDeactivate, setConfirmDeactivate] = useState(false);
|
|
|
|
function changeRole(next: string) {
|
|
setError(null);
|
|
setSelectedRole(next);
|
|
startTransition(async () => {
|
|
const res = await updateUserRoleAction(id, next);
|
|
if (res && res.ok === false) {
|
|
setError(res.error);
|
|
setSelectedRole(role);
|
|
}
|
|
router.refresh();
|
|
});
|
|
}
|
|
|
|
function toggleActive(next: boolean) {
|
|
setError(null);
|
|
startTransition(async () => {
|
|
const res = await toggleUserActiveAction(id, next);
|
|
if (res && res.ok === false) setError(res.error);
|
|
setConfirmDeactivate(false);
|
|
router.refresh();
|
|
});
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-3">
|
|
<div className="flex flex-wrap items-center gap-3">
|
|
<label className="text-[11px] uppercase tracking-wider text-zinc-500">Rôle</label>
|
|
<select
|
|
value={selectedRole}
|
|
disabled={pending}
|
|
onChange={(e) => changeRole(e.target.value)}
|
|
className="rounded-md border border-zinc-300 bg-white px-2 py-1.5 text-sm focus:border-zinc-900 focus:outline-none disabled:opacity-50"
|
|
>
|
|
{ROLE_OPTIONS.map((o) => (
|
|
<option key={o.value} value={o.value}>{o.label}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<span className="text-[11px] uppercase tracking-wider text-zinc-500">État du compte</span>
|
|
{isActive ? (
|
|
confirmDeactivate ? (
|
|
<div className="flex items-center gap-2 rounded border border-amber-300 bg-amber-50 px-2 py-1">
|
|
<span className="text-xs text-amber-900">Désactiver ce compte ?</span>
|
|
<button
|
|
type="button"
|
|
onClick={() => toggleActive(false)}
|
|
disabled={pending}
|
|
className="rounded bg-amber-700 px-2 py-1 text-[11px] font-semibold text-white hover:bg-amber-800 disabled:opacity-50"
|
|
>
|
|
Oui, désactiver
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => setConfirmDeactivate(false)}
|
|
disabled={pending}
|
|
className="text-[11px] text-zinc-500 hover:text-zinc-900"
|
|
>
|
|
Annuler
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<button
|
|
type="button"
|
|
onClick={() => setConfirmDeactivate(true)}
|
|
disabled={pending}
|
|
className="rounded-md border border-rose-300 bg-rose-50 px-3 py-1.5 text-xs font-semibold text-rose-700 hover:bg-rose-100 disabled:opacity-50"
|
|
>
|
|
Désactiver
|
|
</button>
|
|
)
|
|
) : (
|
|
<button
|
|
type="button"
|
|
onClick={() => toggleActive(true)}
|
|
disabled={pending}
|
|
className="rounded-md bg-emerald-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-emerald-700 disabled:opacity-50"
|
|
>
|
|
Réactiver
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{error ? (
|
|
<div className="rounded border border-rose-200 bg-rose-50 px-3 py-2 text-sm text-rose-700">{error}</div>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|