modifs complete

This commit is contained in:
Biqoz
2025-11-27 13:58:47 +01:00
parent 5b8b3c84c9
commit 73f97919ac
12 changed files with 931 additions and 524 deletions

View 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>
);
}