modifs complete
This commit is contained in:
274
components/dashboard/add-credits-single-user.tsx
Normal file
274
components/dashboard/add-credits-single-user.tsx
Normal file
@@ -0,0 +1,274 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Search, User, CheckCircle, Coins } from "lucide-react";
|
||||
|
||||
interface UserResult {
|
||||
_id: string;
|
||||
name: string;
|
||||
username: string;
|
||||
email: string;
|
||||
role: string;
|
||||
referent?: string;
|
||||
prenom?: string;
|
||||
nom?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
interface BalanceInfo {
|
||||
tokenCredits: number;
|
||||
lastRefill?: string;
|
||||
}
|
||||
|
||||
export default function AddCreditsSingleUser() {
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [searchResults, setSearchResults] = useState<UserResult[]>([]);
|
||||
const [selectedUser, setSelectedUser] = useState<UserResult | null>(null);
|
||||
const [userBalance, setUserBalance] = useState<BalanceInfo | null>(null);
|
||||
const [searching, setSearching] = useState(false);
|
||||
const [loadingBalance, setLoadingBalance] = useState(false);
|
||||
const [adding, setAdding] = useState(false);
|
||||
const [success, setSuccess] = useState<{ newBalance: number } | null>(null);
|
||||
|
||||
const searchUsers = async () => {
|
||||
if (!searchTerm.trim()) return;
|
||||
|
||||
setSearching(true);
|
||||
setSelectedUser(null);
|
||||
setUserBalance(null);
|
||||
setSuccess(null);
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/collections/users?search=${encodeURIComponent(searchTerm)}&limit=10`
|
||||
);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.data) {
|
||||
setSearchResults(data.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erreur lors de la recherche:", error);
|
||||
} finally {
|
||||
setSearching(false);
|
||||
}
|
||||
};
|
||||
|
||||
const selectUser = async (user: UserResult) => {
|
||||
setSelectedUser(user);
|
||||
setSearchResults([]);
|
||||
setSuccess(null);
|
||||
setLoadingBalance(true);
|
||||
|
||||
try {
|
||||
// Récupérer la balance de l'utilisateur
|
||||
const response = await fetch(
|
||||
`/api/collections/balances?search=${user._id}&limit=1`
|
||||
);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.data && data.data.length > 0) {
|
||||
setUserBalance({
|
||||
tokenCredits: data.data[0].tokenCredits || 0,
|
||||
lastRefill: data.data[0].lastRefill,
|
||||
});
|
||||
} else {
|
||||
setUserBalance({ tokenCredits: 0 });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erreur lors de la récupération de la balance:", error);
|
||||
setUserBalance({ tokenCredits: 0 });
|
||||
} finally {
|
||||
setLoadingBalance(false);
|
||||
}
|
||||
};
|
||||
|
||||
const addCredits = async () => {
|
||||
if (!selectedUser) return;
|
||||
|
||||
if (
|
||||
!confirm(
|
||||
`Êtes-vous sûr de vouloir ajouter 3 millions de crédits à ${selectedUser.name || selectedUser.email} ?`
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
setAdding(true);
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/add-credits-single", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ userId: selectedUser._id }),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
setSuccess({ newBalance: data.newBalance });
|
||||
setUserBalance({ tokenCredits: data.newBalance });
|
||||
} else {
|
||||
alert("Erreur: " + (data.error || data.message));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erreur lors de l'ajout des crédits:", error);
|
||||
alert("Erreur lors de l'ajout des crédits");
|
||||
} finally {
|
||||
setAdding(false);
|
||||
}
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
setSearchTerm("");
|
||||
setSearchResults([]);
|
||||
setSelectedUser(null);
|
||||
setUserBalance(null);
|
||||
setSuccess(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="w-full">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Coins className="h-5 w-5" />
|
||||
Ajouter des Crédits à un Utilisateur
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Rechercher un utilisateur et lui ajouter 3 millions de tokens
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{/* Barre de recherche */}
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
placeholder="Rechercher par nom, email ou username..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && searchUsers()}
|
||||
/>
|
||||
<Button onClick={searchUsers} disabled={searching}>
|
||||
<Search className="h-4 w-4 mr-2" />
|
||||
{searching ? "..." : "Rechercher"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Résultats de recherche */}
|
||||
{searchResults.length > 0 && (
|
||||
<div className="border rounded-lg divide-y">
|
||||
{searchResults.map((user) => (
|
||||
<div
|
||||
key={user._id}
|
||||
className="p-3 hover:bg-gray-50 cursor-pointer flex items-center justify-between"
|
||||
onClick={() => selectUser(user)}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<User className="h-5 w-5 text-gray-400" />
|
||||
<div>
|
||||
<p className="font-medium">{user.name || user.username}</p>
|
||||
<p className="text-sm text-gray-500">{user.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
<Badge variant="outline">{user.role}</Badge>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Utilisateur sélectionné */}
|
||||
{selectedUser && (
|
||||
<div className="border rounded-lg p-4 bg-blue-50 space-y-3">
|
||||
<h4 className="font-semibold text-blue-800 flex items-center gap-2">
|
||||
<CheckCircle className="h-5 w-5" />
|
||||
Utilisateur sélectionné
|
||||
</h4>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 text-sm">
|
||||
<div>
|
||||
<span className="text-gray-600">ID:</span>
|
||||
<p className="font-mono text-xs bg-white p-1 rounded mt-1">
|
||||
{selectedUser._id}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-600">Nom:</span>
|
||||
<p className="font-medium">
|
||||
{selectedUser.name || selectedUser.username}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-600">Email:</span>
|
||||
<p className="font-medium">{selectedUser.email}</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-600">Rôle:</span>
|
||||
<Badge variant="secondary" className="ml-2">
|
||||
{selectedUser.role}
|
||||
</Badge>
|
||||
</div>
|
||||
{selectedUser.referent && (
|
||||
<div>
|
||||
<span className="text-gray-600">Référent:</span>
|
||||
<p className="font-medium">{selectedUser.referent}</p>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<span className="text-gray-600">Crédits actuels:</span>
|
||||
{loadingBalance ? (
|
||||
<p className="text-gray-500">Chargement...</p>
|
||||
) : (
|
||||
<p className="font-bold text-lg text-green-600">
|
||||
{userBalance?.tokenCredits.toLocaleString() || "0"}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Boutons d'action */}
|
||||
<div className="flex gap-2 pt-2 border-t">
|
||||
<Button
|
||||
onClick={addCredits}
|
||||
disabled={adding || loadingBalance}
|
||||
className="flex-1 bg-green-600 hover:bg-green-700"
|
||||
>
|
||||
{adding ? "Ajout en cours..." : "Ajouter 3M tokens"}
|
||||
</Button>
|
||||
<Button onClick={reset} variant="outline">
|
||||
Annuler
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Message de succès */}
|
||||
{success && (
|
||||
<div className="border rounded-lg p-4 bg-green-50">
|
||||
<h4 className="font-semibold text-green-800 flex items-center gap-2">
|
||||
<CheckCircle className="h-5 w-5" />
|
||||
Crédits ajoutés avec succès !
|
||||
</h4>
|
||||
<p className="text-green-700 mt-2">
|
||||
Nouveau solde:{" "}
|
||||
<span className="font-bold">
|
||||
{success.newBalance.toLocaleString()}
|
||||
</span>{" "}
|
||||
tokens
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user