deconstructing admin panel :)

This commit is contained in:
dimitar 2025-04-03 02:44:16 +02:00
parent ab17c2bb92
commit 275a7873d2
4 changed files with 410 additions and 209 deletions

View File

@ -7,6 +7,7 @@ import {
resetUserPassword,
} from "../../services/api";
import DocumentUpload from "../documentUpload/DocumentUpload";
import Documents from "../documentUpload/Documents";
import { useNavigate } from "react-router-dom";
import { motion } from "framer-motion";
import {
@ -241,84 +242,14 @@ function AdminPanel() {
</div>
)}
<div className="grid gap-6">
{/* <div className="grid gap-6"> */}
<>
{activeTab === "documents" && (
<div className="bg-primary-800/50 backdrop-blur-lg rounded-xl overflow-hidden shadow-xl">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b border-primary-700">
<th className="px-6 py-4 text-left text-md text-neutral-400">
Име
</th>
{/* <th className="px-6 py-4 text-left text-md text-neutral-400">
Статус
</th> */}
<th className="px-6 py-4 text-left text-md text-neutral-400">
Прикачено од
</th>
<th className="px-6 py-4 text-left text-md text-neutral-400">
Споделено со
</th>
<th className="px-6 py-4 text-left text-md text-neutral-400">
Креирано на
</th>
</tr>
</thead>
<tbody>
{documents.map((doc) => (
<tr
key={doc.id}
className="border-b border-primary-700/50 hover:bg-primary-700/30"
>
<td className="px-6 py-4 text-white">{doc.title}</td>
{/* <td className="px-6 py-4">
<span
className={`px-3 py-1 rounded-full text-xs font-medium ${
doc.status === "completed"
? "bg-green-500/20 text-green-300"
: doc.status === "pending"
? "bg-yellow-500/20 text-yellow-300"
: doc.status === "uploading"
? "bg-primary-500/20 text-primary-300"
: "bg-neutral-500/20 text-neutral-300"
}`}
>
{doc.status}
</span>
</td> */}
<td className="px-6 py-4 text-neutral-300">
{doc.uploadedBy?.name} ({doc.uploadedBy?.email})
</td>
<td className="px-6 py-4 text-neutral-300">
{doc.sharedWith && doc.sharedWith.length > 0
? doc.sharedWith.map((user) => (
<div
key={user.id}
className="whitespace-nowrap"
>
{user.name} ({user.email})
</div>
))
: "None"}
</td>
<td className="px-6 py-4 text-neutral-300">
{new Date(doc.createdAt).toLocaleString()}
</td>
</tr>
))}
</tbody>
</table>
</div>
</motion.div>
<div className="bg-primary-800/50 backdrop-blur-lg rounded-xl p-6 shadow-xl">
<Documents />
</div>
)}
</>
{activeTab === "users" && (
<>
<div className="bg-primary-800/50 backdrop-blur-lg rounded-xl p-6 mb-6 shadow-xl">
@ -464,7 +395,6 @@ function AdminPanel() {
)}
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,135 @@
import { useState, useEffect } from "react";
import { motion } from "framer-motion";
import { getAllDocuments } from "../../services/api";
import { getAllUsers } from "../../services/api";
import {
FiUsers,
FiFile,
FiUpload,
FiKey,
FiLoader,
FiUserPlus,
} from "react-icons/fi";
function Documents() {
const [documents, setDocuments] = useState([]);
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const [documentsResponse, usersResponse] = await Promise.all([
getAllDocuments(),
getAllUsers(),
]);
// Make sure we're getting the data property from the response
setDocuments(
Array.isArray(documentsResponse.data) ? documentsResponse.data : [],
);
setUsers(Array.isArray(usersResponse.data) ? usersResponse.data : []);
} catch (error) {
console.error("Error fetching data:", error);
setError("Failed to load data");
} finally {
setLoading(false);
}
};
fetchData();
}, []);
if (loading) {
return (
<div className="flex items-center justify-center p-8">
<FiLoader className="w-8 h-8 animate-spin text-primary-500" />
</div>
);
}
if (error) {
return (
<div className="p-4 bg-red-500/10 border border-red-500/20 rounded-lg text-red-200">
{error}
</div>
);
}
return (
<div className="bg-primary-800/50 backdrop-blur-lg rounded-xl overflow-hidden shadow-xl">
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b border-primary-700">
<th className="px-6 py-4 text-left text-md text-neutral-400">
Име
</th>
<th className="px-6 py-4 text-left text-md text-neutral-400">
Прикачено од
</th>
<th className="px-6 py-4 text-left text-md text-neutral-400">
Споделено со
</th>
<th className="px-6 py-4 text-left text-md text-neutral-400">
Креирано на
</th>
</tr>
</thead>
<tbody>
{documents.length > 0 ? (
documents.map((doc) => (
<tr
key={doc.id}
className="border-b border-primary-700/50 hover:bg-primary-700/30"
>
<td className="px-6 py-4 text-white">{doc.title || "N/A"}</td>
<td className="px-6 py-4 text-neutral-300">
{doc.uploadedBy ? (
<>
{doc.uploadedBy.name} ({doc.uploadedBy.email})
</>
) : (
"N/A"
)}
</td>
<td className="px-6 py-4 text-neutral-300">
{doc.sharedWith && doc.sharedWith.length > 0 ? (
<div className="whitespace-nowrap">
{doc.sharedWith.map((user) => (
<div key={user.id}>
{user.name} ({user.email})
</div>
))}
</div>
) : (
"None"
)}
</td>
<td className="px-6 py-4 text-neutral-300">
{doc.createdAt
? new Date(doc.createdAt).toLocaleString()
: "N/A"}
</td>
</tr>
))
) : (
<tr>
<td
colSpan="4"
className="px-6 py-4 text-center text-neutral-400"
>
No documents available
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
);
}
export default Documents;

View File

@ -0,0 +1,136 @@
import { useState } from "react";
function Users() {
const [newUser, setNewUser] = useState({});
return (
<>
<div className="bg-primary-800/50 backdrop-blur-lg rounded-xl p-6 mb-6 shadow-xl">
<h2 className="text-xl font-bold text-white mb-4">Креирај корисник</h2>
<form
ref={createUserFormRef}
onSubmit={handleCreateUser}
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"
>
<input
type="text"
placeholder="Име"
value={newUser.name}
id="name"
name="name"
required
onChange={(e) => setNewUser({ ...newUser, name: e.target.value })}
className="bg-primary-700/30 border border-primary-600 rounded-lg px-4 py-2
text-white placeholder-neutral-400 focus:outline-none focus:border-primary-500
focus:ring-1 focus:ring-primary-500"
/>
<input
type="email"
id="email"
name="email"
placeholder="Мејл"
value={newUser.email}
onChange={(e) => setNewUser({ ...newUser, email: e.target.value })}
className="bg-primary-700/30 border border-primary-600 rounded-lg px-4 py-2
text-white placeholder-neutral-400 focus:outline-none focus:border-primary-500
focus:ring-1 focus:ring-primary-500"
required
/>
<input
// type="password"
id="password"
name="password"
placeholder="Лозинка"
value={newUser.password}
onChange={(e) =>
setNewUser({ ...newUser, password: e.target.value })
}
className="bg-primary-700/30 border border-primary-600 rounded-lg px-4 py-2
text-white placeholder-neutral-400 focus:outline-none focus:border-primary-500
focus:ring-1 focus:ring-primary-500"
required
/>
<div className="flex items-center space-x-2">
<input
type="checkbox"
id="isAdmin"
checked={newUser.isAdmin}
onChange={(e) =>
setNewUser({ ...newUser, isAdmin: e.target.checked })
}
className="rounded border-primary-600 bg-primary-700/30 text-primary-500
focus:ring-primary-500"
/>
<label htmlFor="isAdmin" className="text-white">
Дали е Администратор
</label>
</div>
<button
type="submit"
className="flex items-center justify-center space-x-2 px-4 py-2
bg-primary-600 hover:bg-primary-700 text-white rounded-lg
transition-colors shadow-lg"
>
<FiUserPlus className="w-4 h-4" />
<span>Креирај корисник</span>
</button>
</form>
</div>
<div className="bg-primary-800/50 backdrop-blur-lg rounded-xl overflow-hidden shadow-xl">
<table className="w-full">
<thead>
<tr className="border-b border-primary-700">
<th className="px-6 py-4 text-left text-sm text-neutral-400">
Име
</th>
<th className="px-6 py-4 text-left text-sm text-neutral-400">
Мејл
</th>
<th className="px-6 py-4 text-left text-sm text-neutral-400">
Улога
</th>
<th className="px-6 py-4 text-left text-sm text-neutral-400">
Ресетирај
</th>
</tr>
</thead>
<tbody>
{users.map((user) => (
<tr
key={user.id}
className="border-b border-primary-700/50 hover:bg-primary-700/30"
>
<td className="px-6 py-4 text-white">{user.name}</td>
<td className="px-6 py-4 text-white">{user.email}</td>
<td className="px-6 py-4">
<span
className={`px-3 py-1 rounded-full text-xs font-medium
${user.isAdmin ? "bg-primary-500/20 text-primary-300" : "bg-neutral-500/20 text-neutral-300"}`}
>
{user.isAdmin ? "Admin" : "User"}
</span>
</td>
<td className="px-6 py-4">
<button
onClick={() =>
setResetPasswordModal({
isOpen: true,
userId: user.id,
userName: user.name,
newPassword: "",
})
}
className="flex items-center space-x-1 text-neutral-400 hover:text-white transition-colors"
>
<FiKey className="w-4 h-4" />
<span>Ресетирај лозинка</span>
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</>
);
}