diff --git a/backend/imk-backend/src/admin/admin.controller.ts b/backend/imk-backend/src/admin/admin.controller.ts index aa163e3..7bce7ad 100644 --- a/backend/imk-backend/src/admin/admin.controller.ts +++ b/backend/imk-backend/src/admin/admin.controller.ts @@ -11,7 +11,6 @@ import { ParseIntPipe, UseGuards, Request, - Req, } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { AdminService } from './admin.service'; @@ -96,12 +95,25 @@ export class AdminController { return this.adminService.updateDocumentStatus(+id, status); } + // async uploadDocument( + // @UploadedFile() file: Express.Multer.File, + // @Body('title') title: string, + // @Request() req, + // ) { + // return this.adminService.uploadDocument(file, title, req.user.userId); + // } async uploadDocument( @UploadedFile() file: Express.Multer.File, @Body('title') title: string, + @Body('description') description: number[], @Request() req, ) { - return this.adminService.uploadDocument(file, title, req.user.userId); + return this.adminService.uploadDocument( + file, + title, + req.user.userId, + description, + ); } @Get('test-s3-connection') async testS3Connection() { diff --git a/backend/imk-backend/src/admin/admin.service.ts b/backend/imk-backend/src/admin/admin.service.ts index b33a3bd..3b93e6d 100644 --- a/backend/imk-backend/src/admin/admin.service.ts +++ b/backend/imk-backend/src/admin/admin.service.ts @@ -85,8 +85,41 @@ export class AdminService { }); } + // async getAllDocuments() { + // return this.prisma.document.findMany({ + // include: { + // sharedWith: { + // select: { + // id: true, + // name: true, + // email: true, + // }, + // }, + // }, + // }); + // } async getAllDocuments() { - return this.prisma.document.findMany(); + return this.prisma.document.findMany({ + include: { + author: { + select: { + id: true, + name: true, + email: true, + }, + }, + sharedWith: { + select: { + id: true, + name: true, + email: true, + }, + }, + }, + orderBy: { + createdAt: 'desc', + }, + }); } async updateDocument( @@ -116,6 +149,41 @@ export class AdminService { }, }); } + async uploadDocument( + file: Express.Multer.File, + title: string, + userId: number, + sharedWith: number[], + ) { + let s3Key; + try { + s3Key = await this.s3Service.uploadFile(file, 'documents'); + + // Log the sharedWith array to verify the data + console.log('Sharing document with users:', sharedWith); + + const document = await this.prisma.document.create({ + data: { + title, + authorId: userId, + s3Key, + status: 'completed', + sharedWith: { + connect: sharedWith.map((id) => ({ id })), + }, + }, + include: { + sharedWith: true, // Include this to verify the relation was created + }, + }); + + console.log('Created document:', document); + return document; + } catch (error) { + console.error('Error in uploadDocument:', error); + throw error; + } + } async deleteDocument(id: number) { const document = await this.prisma.document.findUnique({ @@ -221,42 +289,49 @@ export class AdminService { // problem whith upload status writing to db, i will fix it later - async uploadDocument( - file: Express.Multer.File, - title: string, - userId: number, - ) { - let s3Key; - try { - // First upload to S3 - s3Key = await this.s3Service.uploadFile(file, 'documents'); + // async uploadDocument( + // file: Express.Multer.File, + // title: string, + // userId: number, + // sharedWith: number[], + // ) { + // let s3Key; + // try { + // // First upload to S3 + // s3Key = await this.s3Service.uploadFile(file, 'documents'); - // Then create document with completed status and s3Key - const document = await this.prisma.document.create({ - data: { - title, - authorId: userId, - s3Key, - status: 'completed', // Set status to completed immediately after successful upload - }, - }); + // // Then create document with completed status and s3Key + // const document = await this.prisma.document.create({ + // data: { + // title, + // authorId: userId, + // s3Key, + // status: 'completed', // Set status to completed immediately after successful upload + // sharedWith: { + // connect: sharedWith.map((id: number) => ({ id })), + // }, + // }, + // }); - return document; - } catch (error) { - // Create document with failed status if upload fails - if (title && userId) { - await this.prisma.document.create({ - data: { - title, - authorId: userId, - s3Key: s3Key || '', - status: 'failed', - }, - }); - } - throw error; - } - } + // return document; + // } catch (error) { + // // Create document with failed status if upload fails + // if (title && userId) { + // await this.prisma.document.create({ + // data: { + // title, + // authorId: userId, + // s3Key: s3Key || '', + // status: 'failed', + // sharedWith: { + // connect: sharedWith.map((id: number) => ({ id })), + // }, + // }, + // }); + // } + // throw error; + // } + // } async getDocumentUrl(documentId: number) { const document = await this.prisma.document.findUnique({ diff --git a/frontend/imk/src/components/UserCreate.jsx b/frontend/imk/src/components/UserCreate.jsx new file mode 100644 index 0000000..e3633ee --- /dev/null +++ b/frontend/imk/src/components/UserCreate.jsx @@ -0,0 +1,84 @@ +import { useState } from 'react'; +import { createUser } from '../services/api'; + +// eslint-disable-next-line react/prop-types +function UserCreate({ onUserCreated }) { + const [formData, setFormData] = useState({ + name: '', + email: '', + password: '', + }); + const [error, setError] = useState(''); + const [loading, setLoading] = useState(false); + + const handleSubmit = async (e) => { + e.preventDefault(); + setLoading(true); + setError(''); + + try { + await createUser(formData); + setFormData({ name: '', email: '', password: '' }); + if (onUserCreated) onUserCreated(); + } catch (err) { + setError(err.response?.data?.message || 'Failed to create user'); + } finally { + setLoading(false); + } + }; + + return ( +
+

Create New User

+
+
+ + setFormData({ ...formData, name: e.target.value })} + className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" + required + /> +
+ +
+ + setFormData({ ...formData, email: e.target.value })} + className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" + required + /> +
+ +
+ + setFormData({ ...formData, password: e.target.value })} + className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" + required + /> +
+ + {error && ( +
{error}
+ )} + + +
+
+ ); +} + +export default UserCreate; \ No newline at end of file diff --git a/frontend/imk/src/components/adminPanel/AdminPanel.jsx b/frontend/imk/src/components/adminPanel/AdminPanel.jsx index 2a29ab5..e53c6ab 100644 --- a/frontend/imk/src/components/adminPanel/AdminPanel.jsx +++ b/frontend/imk/src/components/adminPanel/AdminPanel.jsx @@ -1,86 +1,42 @@ -// import { useState, useEffect } from 'react'; -// import { getAllUsers, uploadDocument } from '../../services/api'; -// import DocumentUpload from '../documentUpload/DocumentUpload'; -// function AdminPanel() { -// const [users, setUsers] = useState([]); -// const [file, setFile] = useState(null); -// const [title, setTitle] = useState(''); - -// useEffect(() => { -// fetchUsers(); -// }, []); - -// const fetchUsers = async () => { -// try { -// const response = await getAllUsers(); -// setUsers(response.data); -// } catch (error) { -// console.error('Failed to fetch users:', error); -// } -// }; - -// const handleFileUpload = async (e) => { -// e.preventDefault(); -// if (!file) return; - -// const formData = new FormData(); -// formData.append('file', file); -// formData.append('title', title); - -// try { -// await uploadDocument(formData); -// alert('Document uploaded successfully'); -// } catch (error) { -// console.error('Failed to upload document:', error); -// } -// }; - -// return ( -//
-//

Users

-// - -//

Upload Document

-//
-// setTitle(e.target.value)} -// placeholder="Document Title" -// required -// /> -// setFile(e.target.files[0])} -// required -// /> -// -//
-// -//
-// ); -// } - -// export default AdminPanel; - import { useState, useEffect } from 'react'; -import { getAllUsers, getAllDocuments } from '../../services/api'; +import { getAllUsers, getAllDocuments, getUserInfo } from '../../services/api'; import DocumentUpload from '../documentUpload/DocumentUpload'; +import UserCreate from '../UserCreate'; +import { useNavigate } from 'react-router-dom'; function AdminPanel() { + const navigate = useNavigate(); const [activeTab, setActiveTab] = useState('documents'); const [users, setUsers] = useState([]); const [documents, setDocuments] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); + const [isAdmin, setIsAdmin] = useState(false); useEffect(() => { - fetchData(); - }, [activeTab]); + checkAdminStatus(); + }, []); + + const checkAdminStatus = async () => { + try { + const response = await getUserInfo(); + if (!response?.data?.isAdmin) { + navigate('/'); + } else { + setIsAdmin(true); + await fetchData(); + } + } catch (error) { + console.error('Admin check failed:', error); + navigate('/'); + } + }; + + useEffect(() => { + if (isAdmin) { + fetchData(); + } + }, [activeTab, isAdmin]); const fetchData = async () => { setLoading(true); @@ -110,6 +66,10 @@ function AdminPanel() { return colors[status] || 'bg-gray-100 text-gray-800'; }; + if (!isAdmin) { + return null; + } + return (
@@ -175,7 +135,7 @@ function AdminPanel() { Title Status - Author + Shared With Created At @@ -188,7 +148,9 @@ function AdminPanel() { {doc.status} - {doc.authorId} + + {doc.sharedWith?.map((user) => user.name).join(', ') || 'None'} + {new Date(doc.createdAt).toLocaleDateString()} @@ -200,29 +162,36 @@ function AdminPanel() { )} {activeTab === 'users' && ( -
- - - - - - - - - - {users.map((user) => ( - - - - - - ))} - -
NameEmailRole
{user.name}{user.email} - - {user.isAdmin ? 'Admin' : 'User'} - -
+
+
+
+ + + + + + + + + + {users.map((user) => ( + + + + + + ))} + +
NameEmailRole
{user.name}{user.email} + + {user.isAdmin ? 'Admin' : 'User'} + +
+
+
+
+ +
)} diff --git a/frontend/imk/src/components/documentUpload/DocumentUpload.jsx b/frontend/imk/src/components/documentUpload/DocumentUpload.jsx index 8b50777..dd3c8d3 100644 --- a/frontend/imk/src/components/documentUpload/DocumentUpload.jsx +++ b/frontend/imk/src/components/documentUpload/DocumentUpload.jsx @@ -1,12 +1,27 @@ -import { useState } from 'react'; -import { uploadDocument } from '../../services/api'; +import { useState, useEffect } from 'react'; +import { uploadDocument, getAllUsers } from '../../services/api'; function DocumentUpload() { const [file, setFile] = useState(null); const [title, setTitle] = useState(''); + const [selectedUsers, setSelectedUsers] = useState([]); + const [availableUsers, setAvailableUsers] = useState([]); const [status, setStatus] = useState('idle'); // idle, uploading, completed, failed const [errorMessage, setErrorMessage] = useState(''); + useEffect(() => { + fetchUsers(); + }, []); + + const fetchUsers = async () => { + try { + const response = await getAllUsers(); + setAvailableUsers(response.data.filter(user => !user.isAdmin)); + } catch (error) { + setErrorMessage('Failed to load users'); + } + }; + const handleFileChange = (event) => { const selectedFile = event.target.files[0]; setFile(selectedFile); @@ -14,8 +29,8 @@ function DocumentUpload() { const handleSubmit = async (event) => { event.preventDefault(); - if (!file || !title) { - setErrorMessage('Please provide both a title and a file'); + if (!file || !title || selectedUsers.length === 0) { + setErrorMessage('Please provide a title, file, and select at least one user'); return; } @@ -25,17 +40,18 @@ function DocumentUpload() { const formData = new FormData(); formData.append('file', file); formData.append('title', title); + formData.append('sharedWith', JSON.stringify(selectedUsers)); try { await uploadDocument(formData); setStatus('completed'); setTitle(''); setFile(null); - // Reset form + setSelectedUsers([]); event.target.reset(); } catch (error) { setStatus('failed'); - setErrorMessage(error.response?.data?.message || 'Upload failed. Please try again.'); + setErrorMessage(error.response?.data?.message || 'Upload failed'); } }; @@ -69,6 +85,25 @@ function DocumentUpload() { />
+
+ + +
+ {errorMessage && (
{errorMessage}