translation, form fix

This commit is contained in:
dimitar 2025-04-03 01:27:07 +02:00
parent c396a00bbc
commit ab17c2bb92
5 changed files with 161 additions and 122 deletions

View File

@ -8,6 +8,7 @@ import {
} from "../../services/api";
import DocumentUpload from "../documentUpload/DocumentUpload";
import { useNavigate } from "react-router-dom";
import { motion } from "framer-motion";
import {
FiUsers,
FiFile,
@ -16,8 +17,10 @@ import {
FiLoader,
FiKey,
} from "react-icons/fi";
import { useRef } from "react";
function AdminPanel() {
const createUserFormRef = useRef(null);
const navigate = useNavigate();
const [activeTab, setActiveTab] = useState("documents");
const [users, setUsers] = useState([]);
@ -87,15 +90,17 @@ function AdminPanel() {
e.preventDefault();
try {
await createUser(newUser);
fetchData();
} catch (err) {
setError("Failed to create user");
} finally {
// setNewUser(initialUserFormState);
setNewUser({
name: "",
email: "",
password: "",
isAdmin: false,
});
fetchData();
} catch (err) {
setError("Failed to create user");
}
};
@ -123,9 +128,9 @@ function AdminPanel() {
if (!isAdmin) return null;
const tabs = [
{ id: "documents", name: "Documents", icon: FiFile },
{ id: "users", name: "Users", icon: FiUsers },
{ id: "upload", name: "Upload Document", icon: FiUpload },
{ id: "documents", name: "Документи", icon: FiFile },
{ id: "users", name: "Клиенти", icon: FiUsers },
{ id: "upload", name: "Прикачи документ", icon: FiUpload },
];
if (loading) {
@ -146,9 +151,13 @@ function AdminPanel() {
<h1 className="text-3xl font-bold text-white mb-2">
Admin Dashboard
</h1>
<p className="text-neutral-400">Manage users and documents</p>
<p className="text-neutral-400">Клиенти и доументи</p>
</header>
{/* <motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
> */}
{/* Tabs */}
<div className="flex space-x-4 mb-6">
{tabs.map(({ id, name, icon: Icon }) => (
@ -180,12 +189,15 @@ function AdminPanel() {
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50">
<div className="bg-primary-800 rounded-xl p-6 w-full max-w-md">
<h3 className="text-xl font-bold text-white mb-4">
Reset Password for {resetPasswordModal.userName}
Ресетирај лозинка за корисникот
</h3>
<h3 className="text-xl font-bold text-white mb-4">
{resetPasswordModal.userName}
</h3>
<form onSubmit={handleResetPassword} className="space-y-4">
<input
type="password"
placeholder="New Password"
placeholder="Нова лозинка"
value={resetPasswordModal.newPassword}
onChange={(e) =>
setResetPasswordModal({
@ -212,7 +224,7 @@ function AdminPanel() {
}
className="px-4 py-2 text-neutral-400 hover:text-white transition-colors"
>
Cancel
Откажи
</button>
<button
type="submit"
@ -221,7 +233,7 @@ function AdminPanel() {
transition-colors shadow-lg"
>
<FiKey className="w-4 h-4" />
<span>Reset Password</span>
<span>Потврди</span>
</button>
</div>
</form>
@ -232,35 +244,40 @@ function AdminPanel() {
<div className="grid gap-6">
{activeTab === "documents" && (
<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-sm text-neutral-400">
Title
</th>
<th className="px-6 py-4 text-left text-sm text-neutral-400">
Status
</th>
<th className="px-6 py-4 text-left text-sm text-neutral-400">
Uploaded By
</th>
<th className="px-6 py-4 text-left text-sm text-neutral-400">
Shared With
</th>
<th className="px-6 py-4 text-left text-sm text-neutral-400">
Created At
</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">
<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"
@ -274,30 +291,31 @@ function AdminPanel() {
>
{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>
</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>
)}
@ -305,27 +323,32 @@ function AdminPanel() {
<>
<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">
Create New User
Креирај корисник
</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="Name"
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"
required
/>
<input
type="email"
placeholder="Email"
id="email"
name="email"
placeholder="Мејл"
value={newUser.email}
onChange={(e) =>
setNewUser({ ...newUser, email: e.target.value })
@ -336,8 +359,10 @@ function AdminPanel() {
required
/>
<input
type="password"
placeholder="Password"
// type="password"
id="password"
name="password"
placeholder="Лозинка"
value={newUser.password}
onChange={(e) =>
setNewUser({ ...newUser, password: e.target.value })
@ -359,7 +384,7 @@ function AdminPanel() {
focus:ring-primary-500"
/>
<label htmlFor="isAdmin" className="text-white">
Is Admin
Дали е Администратор
</label>
</div>
<button
@ -369,7 +394,7 @@ function AdminPanel() {
transition-colors shadow-lg"
>
<FiUserPlus className="w-4 h-4" />
<span>Create User</span>
<span>Креирај корисник</span>
</button>
</form>
</div>
@ -379,16 +404,16 @@ function AdminPanel() {
<thead>
<tr className="border-b border-primary-700">
<th className="px-6 py-4 text-left text-sm text-neutral-400">
Name
Име
</th>
<th className="px-6 py-4 text-left text-sm text-neutral-400">
Email
Мејл
</th>
<th className="px-6 py-4 text-left text-sm text-neutral-400">
Role
Улога
</th>
<th className="px-6 py-4 text-left text-sm text-neutral-400">
Actions
Ресетирај
</th>
</tr>
</thead>
@ -421,7 +446,7 @@ function AdminPanel() {
className="flex items-center space-x-1 text-neutral-400 hover:text-white transition-colors"
>
<FiKey className="w-4 h-4" />
<span>Reset Password</span>
<span>Ресетирај лозинка</span>
</button>
</td>
</tr>

View File

@ -1,15 +1,21 @@
import { useState, useEffect } from 'react';
import { useAuth } from '../../hooks/useAuth';
import { getSharedDocuments } from '../../services/api';
import { format } from 'date-fns';
import { FiFolder, FiFile, FiDownload, FiChevronRight, FiLoader } from 'react-icons/fi';
import { downloadDocument } from '../../services/api';
import { useState, useEffect } from "react";
import { useAuth } from "../../hooks/useAuth";
import { getSharedDocuments } from "../../services/api";
import { format } from "date-fns";
import {
FiFolder,
FiFile,
FiDownload,
FiChevronRight,
FiLoader,
} from "react-icons/fi";
import { downloadDocument } from "../../services/api";
function Dashboard() {
const [documents, setDocuments] = useState({});
const [expandedFolders, setExpandedFolders] = useState({});
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [error, setError] = useState("");
const { user } = useAuth();
useEffect(() => {
@ -22,8 +28,8 @@ function Dashboard() {
const groupedDocs = groupDocumentsByCompanyAndDate(response.data);
setDocuments(groupedDocs);
} catch (err) {
console.error('Error fetching documents:', err);
setError('Failed to fetch documents');
console.error("Error fetching documents:", err);
setError("Failed to fetch documents");
} finally {
setLoading(false);
}
@ -34,12 +40,12 @@ function Dashboard() {
const groupDocumentsByCompanyAndDate = (docs) => {
if (!Array.isArray(docs)) {
console.error('Expected array of documents, received:', docs);
console.error("Expected array of documents, received:", docs);
return {};
}
return docs.reduce((acc, doc) => {
const folderName = `${doc.sharedWith?.name || 'Unknown'}-${format(new Date(doc.createdAt), 'yyyy-MM-dd')}`;
const folderName = `${doc.sharedWith?.name || user.name}-${format(new Date(doc.createdAt), "dd-MM-yyyy")}`;
if (!acc[folderName]) {
acc[folderName] = [];
}
@ -52,15 +58,15 @@ function Dashboard() {
try {
await downloadDocument(s3Key);
} catch (err) {
console.error('Error downloading document:', err);
alert('Failed to download document. Please try again.');
console.error("Error downloading document:", err);
alert("Failed to download document. Please try again.");
}
};
const toggleFolder = (folderName) => {
setExpandedFolders(prev => ({
setExpandedFolders((prev) => ({
...prev,
[folderName]: !prev[folderName]
[folderName]: !prev[folderName],
}));
};
@ -69,7 +75,7 @@ function Dashboard() {
<div className="min-h-screen flex items-center justify-center bg-primary-900">
<div className="flex items-center space-x-3 text-primary-400">
<FiLoader className="w-6 h-6 animate-spin" />
<span className="text-lg font-medium">Loading documents...</span>
<span className="text-lg font-medium">Се вчитува...</span>
</div>
</div>
);
@ -89,8 +95,12 @@ function Dashboard() {
<div className="min-h-screen bg-gradient-to-br from-primary-900 to-primary-800 p-6">
<div className="max-w-7xl mx-auto">
<header className="mb-8 mt-20">
<h1 className="text-3xl font-bold text-white mb-2">Your Documents</h1>
<p className="text-neutral-400">Access and manage your shared documents</p>
<h1 className="text-3xl font-bold text-white mb-2">
Вашите Документи
</h1>
<p className="text-neutral-400">
{/* Access and manage your shared documents */}
</p>
</header>
<div className="grid gap-6">
@ -109,11 +119,13 @@ function Dashboard() {
<div className="flex items-center space-x-3">
<FiFolder className="w-5 h-5 text-primary-400" />
<span className="font-medium">{folderName}</span>
<span className="text-sm text-neutral-400">({docs.length} files)</span>
<span className="text-sm text-neutral-400">
({docs.length} files)
</span>
</div>
<FiChevronRight
className={`w-5 h-5 transition-transform duration-200 ${
expandedFolders[folderName] ? 'rotate-90' : ''
expandedFolders[folderName] ? "rotate-90" : ""
}`}
/>
</button>
@ -145,8 +157,10 @@ function Dashboard() {
</div>
))
) : (
<div className="text-center py-12 bg-neutral-900/80 backdrop-blur-lg rounded-xl
border border-neutral-800">
<div
className="text-center py-12 bg-neutral-900/80 backdrop-blur-lg rounded-xl
border border-neutral-800"
>
<p className="text-neutral-400">No documents available</p>
</div>
)}

View File

@ -64,12 +64,12 @@ function DocumentUpload() {
formData.append("uploadedById", uploadedById);
// Debug log
console.log("Form Data:", {
title,
sharedWithId,
uploadedById,
fileName: file.name,
});
// console.log("Form Data:", {
// title,
// sharedWithId,
// uploadedById,
// fileName: file.name,
// });
try {
await uploadDocument(formData);
@ -90,9 +90,9 @@ function DocumentUpload() {
<div className="max-w-4xl mx-auto">
<header className="mb-8 mt-20">
<h1 className="text-3xl font-bold text-white mb-2">
Upload Document
Сподели документ
</h1>
<p className="text-neutral-400">Share documents with your clients</p>
{/* <p className="text-neutral-400">Share documents with your clients</p> */}
</header>
<motion.div
@ -104,8 +104,8 @@ function DocumentUpload() {
<form onSubmit={handleSubmit} className="space-y-6">
{/* Title Input */}
<div>
<label className="block text-sm font-medium text-white mb-2">
Document Title
<label className="block text-sm font-medium text-white mb-4">
Наслов на документот
</label>
<input
type="text"
@ -120,7 +120,7 @@ function DocumentUpload() {
{/* File Input */}
<div>
<label className="block text-sm font-medium text-white mb-2">
File
Документ
</label>
<div className="flex items-center justify-center w-full">
<label
@ -130,7 +130,7 @@ function DocumentUpload() {
>
<FiUpload className="w-8 h-8 text-neutral-400" />
<span className="mt-2 text-sm text-neutral-400">
{file ? file.name : "Select a file"}
{file ? file.name : "Избери документ"}
</span>
<input
type="file"
@ -145,7 +145,7 @@ function DocumentUpload() {
{/* User Selection */}
<div>
<label className="block text-sm font-medium text-white mb-2">
Share with User
Сподели со
</label>
<div className="relative">
<FiUsers className="absolute top-3 left-3 text-neutral-400" />
@ -156,7 +156,7 @@ function DocumentUpload() {
text-white focus:outline-none focus:border-primary-500"
required
>
<option value="">Select a user</option>
<option value="">Избери корисник</option>
{availableUsers.map((user) => (
<option key={user.id} value={user.id}>
{user.name} ({user.email})
@ -176,7 +176,7 @@ function DocumentUpload() {
{/* Success Message */}
{status === "completed" && (
<div className="p-4 bg-green-500/10 border border-green-500/20 rounded-lg text-green-200">
Document uploaded successfully!
Документот успешно е споделен!
</div>
)}
@ -199,7 +199,7 @@ function DocumentUpload() {
Upload Complete
</>
) : (
"Upload Document"
"Прикачи"
)}
</button>
</form>