show credential pw
This commit is contained in:
@@ -215,24 +215,80 @@ export default function SettingsPage() {
|
||||
`Viewing staff member:\n${staff.name} (${staff.email || "No email"})`,
|
||||
);
|
||||
|
||||
// MANAGE USER
|
||||
// MANAGE USERS (admin only)
|
||||
const [newUsername, setNewUsername] = useState("");
|
||||
const [newPassword, setNewPassword] = useState("");
|
||||
|
||||
const {
|
||||
data: allUsers = [],
|
||||
refetch: refetchUsers,
|
||||
} = useQuery<{ id: number; username: string }[]>({
|
||||
queryKey: ["/api/users/list"],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest("GET", "/api/users/list");
|
||||
if (!res.ok) return [];
|
||||
return res.json();
|
||||
},
|
||||
enabled: false, // loaded lazily below
|
||||
});
|
||||
|
||||
const { user: currentUser } = useAuth();
|
||||
const isAdmin = currentUser?.username === "admin";
|
||||
|
||||
useEffect(() => {
|
||||
if (isAdmin) refetchUsers();
|
||||
}, [isAdmin]);
|
||||
|
||||
const addUserMutation = useMutation({
|
||||
mutationFn: async (data: { username: string; password: string }) => {
|
||||
const res = await apiRequest("POST", "/api/users/", data);
|
||||
if (!res.ok) {
|
||||
const err = await res.json().catch(() => null);
|
||||
throw new Error(err?.error || "Failed to create user");
|
||||
}
|
||||
return res.json();
|
||||
},
|
||||
onSuccess: () => {
|
||||
setNewUsername("");
|
||||
setNewPassword("");
|
||||
refetchUsers();
|
||||
toast({ title: "User Created", description: "New user added successfully." });
|
||||
},
|
||||
onError: (err: any) => {
|
||||
toast({ title: "Error", description: err?.message || "Failed to create user", variant: "destructive" });
|
||||
},
|
||||
});
|
||||
|
||||
const deleteUserMutation = useMutation({
|
||||
mutationFn: async (id: number) => {
|
||||
const res = await apiRequest("DELETE", `/api/users/${id}`);
|
||||
if (!res.ok) throw new Error("Failed to delete user");
|
||||
},
|
||||
onSuccess: () => {
|
||||
refetchUsers();
|
||||
toast({ title: "User Deleted", description: "User removed successfully." });
|
||||
},
|
||||
onError: (err: any) => {
|
||||
toast({ title: "Error", description: err?.message || "Failed to delete user", variant: "destructive" });
|
||||
},
|
||||
});
|
||||
|
||||
// MANAGE USER (own account)
|
||||
const [usernameUser, setUsernameUser] = useState("");
|
||||
|
||||
//fetch user
|
||||
const { user } = useAuth();
|
||||
useEffect(() => {
|
||||
if (user?.username) {
|
||||
setUsernameUser(user.username);
|
||||
if (currentUser?.username) {
|
||||
setUsernameUser(currentUser.username);
|
||||
}
|
||||
}, [user]);
|
||||
}, [currentUser]);
|
||||
|
||||
//update user mutation
|
||||
const updateUserMutate = useMutation({
|
||||
mutationFn: async (
|
||||
updates: Partial<{ username: string; password: string }>,
|
||||
) => {
|
||||
if (!user?.id) throw new Error("User not loaded");
|
||||
const res = await apiRequest("PUT", `/api/users/${user.id}`, updates);
|
||||
if (!currentUser?.id) throw new Error("User not loaded");
|
||||
const res = await apiRequest("PUT", `/api/users/${currentUser.id}`, updates);
|
||||
if (!res.ok) {
|
||||
const errorData = await res.json().catch(() => null);
|
||||
throw new Error(errorData?.error || "Failed to update user");
|
||||
@@ -303,6 +359,77 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Manage Users section (admin only) */}
|
||||
{isAdmin && (
|
||||
<Card className="mt-6">
|
||||
<CardContent className="space-y-4 py-6">
|
||||
<h3 className="text-lg font-semibold">Manage Users</h3>
|
||||
|
||||
{/* Existing users list */}
|
||||
<div className="border rounded divide-y">
|
||||
{allUsers.length === 0 && (
|
||||
<p className="text-sm text-gray-500 p-3">No users found.</p>
|
||||
)}
|
||||
{allUsers.map((u) => (
|
||||
<div key={u.id} className="flex items-center justify-between px-3 py-2">
|
||||
<span className="text-sm font-medium">{u.username}</span>
|
||||
{u.username !== "admin" && (
|
||||
<button
|
||||
className="text-sm text-red-600 hover:underline"
|
||||
onClick={() => deleteUserMutation.mutate(u.id)}
|
||||
disabled={deleteUserMutation.isPending}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Add new user form */}
|
||||
<form
|
||||
className="space-y-3 pt-2"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
if (!newUsername.trim() || !newPassword.trim()) return;
|
||||
addUserMutation.mutate({ username: newUsername.trim(), password: newPassword.trim() });
|
||||
}}
|
||||
>
|
||||
<h4 className="text-sm font-semibold text-gray-700">Add New User</h4>
|
||||
<div>
|
||||
<label className="block text-sm font-medium">Username</label>
|
||||
<input
|
||||
type="text"
|
||||
value={newUsername}
|
||||
onChange={(e) => setNewUsername(e.target.value)}
|
||||
className="mt-1 p-2 border rounded w-full"
|
||||
placeholder="Enter username"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
value={newPassword}
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
className="mt-1 p-2 border rounded w-full"
|
||||
placeholder="••••••••"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
|
||||
disabled={addUserMutation.isPending}
|
||||
>
|
||||
{addUserMutation.isPending ? "Adding..." : "Add User"}
|
||||
</button>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* User Setting section */}
|
||||
<Card className="mt-6">
|
||||
<CardContent className="space-y-4 py-6">
|
||||
|
||||
Reference in New Issue
Block a user