diff --git a/backend/imk-backend/package.json b/backend/imk-backend/package.json index b193441..269d941 100644 --- a/backend/imk-backend/package.json +++ b/backend/imk-backend/package.json @@ -71,6 +71,10 @@ "tsconfig-paths": "^4.2.0", "typescript": "^5.1.3" }, + + "prisma": { + "seed": "ts-node prisma/seed.ts" + }, "jest": { "moduleFileExtensions": [ "js", diff --git a/backend/imk-backend/prisma/migrations/20241026192156_init/migration.sql b/backend/imk-backend/prisma/migrations/20241026192156_init/migration.sql deleted file mode 100644 index 2a72c04..0000000 --- a/backend/imk-backend/prisma/migrations/20241026192156_init/migration.sql +++ /dev/null @@ -1,27 +0,0 @@ --- CreateTable -CREATE TABLE "User" ( - "id" SERIAL NOT NULL, - "email" TEXT NOT NULL, - "name" TEXT, - "password" TEXT NOT NULL, - "isAdmin" BOOLEAN NOT NULL DEFAULT false, - - CONSTRAINT "User_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Document" ( - "id" SERIAL NOT NULL, - "title" TEXT NOT NULL, - "content" TEXT, - "published" BOOLEAN NOT NULL DEFAULT false, - "authorId" INTEGER NOT NULL, - - CONSTRAINT "Document_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); - --- AddForeignKey -ALTER TABLE "Document" ADD CONSTRAINT "Document_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/backend/imk-backend/prisma/migrations/20241026195914_add_s3key_to_document/migration.sql b/backend/imk-backend/prisma/migrations/20241026195914_add_s3key_to_document/migration.sql deleted file mode 100644 index 48dd249..0000000 --- a/backend/imk-backend/prisma/migrations/20241026195914_add_s3key_to_document/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Document" ADD COLUMN "s3Key" TEXT; diff --git a/backend/imk-backend/prisma/migrations/20241026204919_add_s3key_to_document/migration.sql b/backend/imk-backend/prisma/migrations/20241026204919_add_s3key_to_document/migration.sql deleted file mode 100644 index c6cc7bb..0000000 --- a/backend/imk-backend/prisma/migrations/20241026204919_add_s3key_to_document/migration.sql +++ /dev/null @@ -1,8 +0,0 @@ -/* - Warnings: - - - Made the column `s3Key` on table `Document` required. This step will fail if there are existing NULL values in that column. - -*/ --- AlterTable -ALTER TABLE "Document" ALTER COLUMN "s3Key" SET NOT NULL; diff --git a/backend/imk-backend/prisma/migrations/20241027002657_model_resufle/migration.sql b/backend/imk-backend/prisma/migrations/20241029013255_init/migration.sql similarity index 55% rename from backend/imk-backend/prisma/migrations/20241027002657_model_resufle/migration.sql rename to backend/imk-backend/prisma/migrations/20241029013255_init/migration.sql index da07f5f..78b936e 100644 --- a/backend/imk-backend/prisma/migrations/20241027002657_model_resufle/migration.sql +++ b/backend/imk-backend/prisma/migrations/20241029013255_init/migration.sql @@ -1,13 +1,28 @@ -/* - Warnings: +-- CreateTable +CREATE TABLE "Document" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "content" TEXT, + "published" BOOLEAN NOT NULL DEFAULT false, + "authorId" INTEGER NOT NULL, + "s3Key" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'pending', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, - - Added the required column `updatedAt` to the `Document` table without a default value. This is not possible if the table is not empty. + CONSTRAINT "Document_pkey" PRIMARY KEY ("id") +); -*/ --- AlterTable -ALTER TABLE "Document" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, -ADD COLUMN "status" TEXT NOT NULL DEFAULT 'pending', -ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL; +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "email" TEXT NOT NULL, + "name" TEXT, + "password" TEXT NOT NULL, + "isAdmin" BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); -- CreateTable CREATE TABLE "Notification" ( @@ -26,12 +41,18 @@ CREATE TABLE "_SharedDocuments" ( "B" INTEGER NOT NULL ); +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + -- CreateIndex CREATE UNIQUE INDEX "_SharedDocuments_AB_unique" ON "_SharedDocuments"("A", "B"); -- CreateIndex CREATE INDEX "_SharedDocuments_B_index" ON "_SharedDocuments"("B"); +-- AddForeignKey +ALTER TABLE "Document" ADD CONSTRAINT "Document_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + -- AddForeignKey ALTER TABLE "Notification" ADD CONSTRAINT "Notification_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/backend/imk-backend/prisma/migrations/20241029014126_simplify_document_schema/migration.sql b/backend/imk-backend/prisma/migrations/20241029014126_simplify_document_schema/migration.sql new file mode 100644 index 0000000..6d147e3 --- /dev/null +++ b/backend/imk-backend/prisma/migrations/20241029014126_simplify_document_schema/migration.sql @@ -0,0 +1,28 @@ +/* + Warnings: + + - You are about to drop the column `authorId` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `published` on the `Document` table. All the data in the column will be lost. + - You are about to drop the `_SharedDocuments` table. If the table is not empty, all the data it contains will be lost. + - Added the required column `sharedWithId` to the `Document` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropForeignKey +ALTER TABLE "Document" DROP CONSTRAINT "Document_authorId_fkey"; + +-- DropForeignKey +ALTER TABLE "_SharedDocuments" DROP CONSTRAINT "_SharedDocuments_A_fkey"; + +-- DropForeignKey +ALTER TABLE "_SharedDocuments" DROP CONSTRAINT "_SharedDocuments_B_fkey"; + +-- AlterTable +ALTER TABLE "Document" DROP COLUMN "authorId", +DROP COLUMN "published", +ADD COLUMN "sharedWithId" INTEGER NOT NULL; + +-- DropTable +DROP TABLE "_SharedDocuments"; + +-- AddForeignKey +ALTER TABLE "Document" ADD CONSTRAINT "Document_sharedWithId_fkey" FOREIGN KEY ("sharedWithId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/backend/imk-backend/prisma/schema.prisma b/backend/imk-backend/prisma/schema.prisma index 400e208..dec4f0e 100644 --- a/backend/imk-backend/prisma/schema.prisma +++ b/backend/imk-backend/prisma/schema.prisma @@ -1,9 +1,3 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - -// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? -// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init - generator client { provider = "prisma-client-js" } @@ -13,30 +7,26 @@ datasource db { url = env("DATABASE_URL") } - model Document { - id Int @id @default(autoincrement()) - title String - content String? - published Boolean @default(false) - authorId Int - author User @relation(fields: [authorId], references: [id]) - s3Key String - status String @default("pending") // pending, uploading, completed, failed - sharedWith User[] @relation("SharedDocuments") - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id Int @id @default(autoincrement()) + title String + content String? + s3Key String + status String @default("pending") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + sharedWithId Int + sharedWith User @relation("SharedDocuments", fields: [sharedWithId], references: [id]) } model User { - id Int @id @default(autoincrement()) - email String @unique - name String? - password String - isAdmin Boolean @default(false) - authoredDocuments Document[] @relation("AuthoredDocuments") - sharedDocuments Document[] @relation("SharedDocuments") - notifications Notification[] + id Int @id @default(autoincrement()) + email String @unique + name String? + password String + isAdmin Boolean @default(false) + sharedDocuments Document[] @relation("SharedDocuments") + notifications Notification[] } model Notification { @@ -44,8 +34,6 @@ model Notification { message String read Boolean @default(false) userId Int - user User @relation(fields: [userId], references: [id]) createdAt DateTime @default(now()) + user User @relation(fields: [userId], references: [id]) } - - diff --git a/backend/imk-backend/prisma/seed.ts b/backend/imk-backend/prisma/seed.ts new file mode 100644 index 0000000..ddae7b9 --- /dev/null +++ b/backend/imk-backend/prisma/seed.ts @@ -0,0 +1,30 @@ +import { PrismaClient } from '@prisma/client'; +import * as bcrypt from 'bcrypt'; + +const prisma = new PrismaClient(); + +async function main() { + const hashedPassword = await bcrypt.hash('admin123', 10); + + const admin = await prisma.user.upsert({ + where: { email: 'admin@example.com' }, + update: {}, + create: { + email: 'admin@example.com', + name: 'Admin User', + password: hashedPassword, + isAdmin: true, + }, + }); + + console.log({ admin }); +} + +main() + .catch((e) => { + console.error(e); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + }); diff --git a/backend/imk-backend/src/admin/admin.controller.ts b/backend/imk-backend/src/admin/admin.controller.ts index 7bce7ad..c37b635 100644 --- a/backend/imk-backend/src/admin/admin.controller.ts +++ b/backend/imk-backend/src/admin/admin.controller.ts @@ -4,22 +4,21 @@ import { Post, Body, Param, - Delete, Put, UseInterceptors, UploadedFile, ParseIntPipe, UseGuards, - Request, } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { AdminService } from './admin.service'; -import { CreateDocumentDto } from '../dto/create-document.dto'; +//import { CreateDocumentDto } from '../dto/create-document.dto'; import { UpdateDocumentDto } from '../dto/update-document.dto'; import { AdminGuard } from '../auth/admin.guard'; import { JwtAuthGuard } from '../auth/jwt-auth.guard'; import { CreateUserDto } from '../dto/create-user.dto'; import { S3Service } from 'src/s3/s3.service'; +import { PrismaService } from 'src/prisma/prisma.service'; @Controller('admin') @UseGuards(JwtAuthGuard, AdminGuard) @@ -27,27 +26,9 @@ export class AdminController { constructor( private readonly adminService: AdminService, private readonly s3Service: S3Service, + private readonly prisma: PrismaService, ) {} - @Post('documents') - @UseInterceptors(FileInterceptor('file')) - createDocument( - @Body() createDocumentDto: CreateDocumentDto, - @UploadedFile() file: Express.Multer.File, - ) { - return this.adminService.createDocument(createDocumentDto, file); - } - - // @Post('document') - // @UseInterceptors(FileInterceptor('file')) - // async createDocument( - // @Body() createDocumentDto: CreateDocumentDto, - // @UploadedFile() file: Express.Multer.File, - // ) { - // // The actual file upload is handled by the FileInterceptor - // // We just need to pass the file to the service - // return this.adminService.createDocument(createDocumentDto, file); - // } @Get('documents') getAllDocuments() { return this.adminService.getAllDocuments(); @@ -63,16 +44,27 @@ export class AdminController { return this.adminService.updateDocument(id, updateDocumentDto, file); } - @Delete('documents/:id') - async deleteDocument(@Param('id') id: string) { - await this.adminService.deleteDocument(+id); - return { message: 'Document deleted successfully' }; - } - @Get('users') getAllUsers() { return this.adminService.getAllUsers(); } + @Post('test-document') + async testDocumentCreation() { + try { + const document = await this.prisma.document.create({ + data: { + title: 'Test Document', + s3Key: 'test-key', + status: 'completed', + sharedWithId: 2, // ID of 'pero' user + }, + }); + return document; + } catch (error) { + console.error('Test document creation error:', error); + throw error; + } + } @Post('users') async createUser(@Body() createUserDto: CreateUserDto) { @@ -82,9 +74,9 @@ export class AdminController { @Post('documents/:id/share') async shareDocument( @Param('id') id: string, - @Body() { userIds }: { userIds: number[] }, + @Body() { userId }: { userId: number }, ) { - return this.adminService.shareDocument(+id, userIds); + return this.adminService.shareDocument(+id, userId); } @Put('documents/:id/status') @@ -95,25 +87,33 @@ 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); - // } + @Post('documents') + @UseInterceptors(FileInterceptor('file')) async uploadDocument( @UploadedFile() file: Express.Multer.File, @Body('title') title: string, - @Body('description') description: number[], - @Request() req, + @Body('sharedWith') sharedWithId: string, ) { - return this.adminService.uploadDocument( - file, + console.log('Received upload request:', { + fileExists: !!file, + fileSize: file?.size, title, - req.user.userId, - description, - ); + sharedWithId, + }); + + try { + const document = await this.adminService.uploadDocument( + file, + title, + Number(sharedWithId), + ); + + console.log('Document created:', document); + return document; + } catch (error) { + console.error('Upload error:', error); + throw error; + } } @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 3b93e6d..3a1e5d1 100644 --- a/backend/imk-backend/src/admin/admin.service.ts +++ b/backend/imk-backend/src/admin/admin.service.ts @@ -1,7 +1,7 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { S3Service } from '../s3/s3.service'; -import { CreateDocumentDto } from '../dto/create-document.dto'; +//import { CreateDocumentDto } from '../dto/create-document.dto'; import { UpdateDocumentDto } from '../dto/update-document.dto'; import { CreateUserDto } from '../dto/create-user.dto'; import * as bcrypt from 'bcrypt'; @@ -13,113 +13,89 @@ export class AdminService { private s3Service: S3Service, ) {} - // async createDocument( - // createDocumentDto: CreateDocumentDto, - // file: Express.Multer.File, - // ) { - // const { title, content, clientEmail } = createDocumentDto; - - // console.log('Received createDocumentDto:', createDocumentDto); - - // let author; - // if (clientEmail) { - // author = await this.prisma.user.findUnique({ - // where: { email: clientEmail }, - // }); - - // if (!author) { - // throw new NotFoundException(`User with email ${clientEmail} not found`); - // } - // } - - // const s3Key = await this.s3Service.uploadFile(file, 'documents'); - - // return this.prisma.document.create({ - // data: { - // title, - // content, - // s3Key, - // ...(author && { author: { connect: { id: author.id } } }), - // }, - // }); - // } - async createDocument( - createDocumentDto: CreateDocumentDto, + async uploadDocument( file: Express.Multer.File, + title: string, + sharedWithId: number, ) { - const { title, content, clientEmail } = createDocumentDto; - - console.log('Received createDocumentDto:', createDocumentDto); - - let authorId: number; - if (clientEmail) { - const author = await this.prisma.user.findUnique({ - where: { email: clientEmail }, + try { + // First verify the user exists + console.log('Verifying user:', sharedWithId); + const user = await this.prisma.user.findUnique({ + where: { id: sharedWithId }, }); - if (!author) { - throw new NotFoundException(`User with email ${clientEmail} not found`); + if (!user) { + throw new NotFoundException(`User with ID ${sharedWithId} not found`); } - authorId = author.id; - } else { - // If no clientEmail is provided, we'll use a default admin user - // You should replace this with an appropriate default user ID - const defaultAdmin = await this.prisma.user.findFirst({ - where: { isAdmin: true }, - }); - if (!defaultAdmin) { - throw new NotFoundException('No default admin user found'); - } - authorId = defaultAdmin.id; - } - const s3Key = await this.s3Service.uploadFile(file, 'documents'); + console.log('Found user:', user); - return this.prisma.document.create({ - data: { + // Upload file to S3 + console.log('Uploading file to S3...'); + const s3Key = await this.s3Service.uploadFile(file, 'documents'); + console.log('File uploaded to S3:', s3Key); + + console.log('Creating document record with data:', { title, - content, s3Key, - author: { connect: { id: authorId } }, - }, - }); - } + sharedWithId, + }); - // async getAllDocuments() { - // return this.prisma.document.findMany({ - // include: { - // sharedWith: { - // select: { - // id: true, - // name: true, - // email: true, - // }, - // }, - // }, - // }); - // } + const document = await this.prisma.document.create({ + data: { + title, + s3Key, + status: 'completed', + sharedWithId, + }, + include: { + sharedWith: { + select: { + id: true, + name: true, + email: true, + }, + }, + }, + }); + + console.log('Created document:', document); + return document; + } catch (error) { + console.error('Error in uploadDocument:', error); + if (error.code === 'P2002') { + console.error('Unique constraint violation'); + } + if (error.code === 'P2003') { + console.error('Foreign key constraint violation'); + } + throw error; + } + } async getAllDocuments() { - return this.prisma.document.findMany({ - include: { - author: { - select: { - id: true, - name: true, - email: true, + try { + const documents = await this.prisma.document.findMany({ + include: { + sharedWith: { + select: { + id: true, + name: true, + email: true, + }, }, }, - sharedWith: { - select: { - id: true, - name: true, - email: true, - }, + orderBy: { + createdAt: 'desc', }, - }, - orderBy: { - createdAt: 'desc', - }, - }); + }); + + console.log('Retrieved documents with shared users:', documents); + return documents; + } catch (error) { + console.error('Error fetching documents:', error); + throw error; + } } async updateDocument( @@ -149,57 +125,6 @@ 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({ - where: { id }, - select: { s3Key: true }, - }); - if (document?.s3Key) { - await this.s3Service.deleteFile(document.s3Key); - } - - return this.prisma.document.delete({ where: { id } }); - } - - async getAllUsers() { - return this.prisma.user.findMany(); - } async createUser(createUserDto: CreateUserDto) { const hashedPassword = await bcrypt.hash(createUserDto.password, 10); @@ -211,25 +136,22 @@ export class AdminService { }); } - async shareDocument(documentId: number, userIds: number[]) { - const document = await this.prisma.document.update({ + async shareDocument(documentId: number, userId: number) { + return this.prisma.document.update({ where: { id: documentId }, data: { + sharedWithId: userId, + }, + include: { sharedWith: { - connect: userIds.map((id: number) => ({ id })), + select: { + id: true, + name: true, + email: true, + }, }, }, }); - - // Create notifications for shared users - await this.prisma.notification.createMany({ - data: userIds.map((userId: number) => ({ - userId, - message: `A new document "${document.title}" has been shared with you.`, - })), - }); - - return document; } async updateDocumentStatus(documentId: number, status: string) { @@ -239,100 +161,6 @@ export class AdminService { }); } - // async uploadDocument( - // file: Express.Multer.File, - // title: string, - // userId: number, - // ) { - // try { - // // First create document with pending status - // const document = await this.prisma.document.create({ - // data: { - // title, - // authorId: userId, - // status: 'pending', - // s3Key: '', // Temporary empty key - // }, - // }); - - // // Update status to uploading - // await this.prisma.document.update({ - // where: { id: document.id }, - // data: { status: 'uploading' }, - // }); - - // // Upload to S3 - // const s3Key = await this.s3Service.uploadFile(file, 'documents'); - - // // Update document with s3Key and completed status - // return this.prisma.document.update({ - // where: { id: document.id }, - // data: { - // s3Key, - // status: 'completed', - // }, - // }); - // } catch (error) { - // // If anything fails, update status to failed - // const document = await this.prisma.document.findFirst({ - // where: { title, authorId: userId }, - // }); - // if (document) { - // await this.prisma.document.update({ - // where: { id: document.id }, - // data: { status: 'failed' }, - // }); - // } - // throw error; - // } - // } - - // problem whith upload status writing to db, i will fix it later - - // 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 - // 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', - // sharedWith: { - // connect: sharedWith.map((id: number) => ({ id })), - // }, - // }, - // }); - // } - // throw error; - // } - // } - async getDocumentUrl(documentId: number) { const document = await this.prisma.document.findUnique({ where: { id: documentId }, @@ -342,4 +170,34 @@ export class AdminService { } return this.s3Service.getFileUrl(document.s3Key); } + async getUserWithDocuments(userId: number) { + return this.prisma.user.findUnique({ + where: { id: userId }, + select: { + id: true, + name: true, + email: true, + sharedDocuments: true, + }, + }); + } + + async getAllUsers() { + try { + const users = await this.prisma.user.findMany({ + select: { + id: true, + name: true, + email: true, + isAdmin: true, + sharedDocuments: true, + }, + }); + console.log('All users:', users); + return users; + } catch (error) { + console.error('Error fetching users:', error); + throw error; + } + } } diff --git a/backend/imk-backend/src/app.module.ts b/backend/imk-backend/src/app.module.ts index 09b917b..25ace33 100644 --- a/backend/imk-backend/src/app.module.ts +++ b/backend/imk-backend/src/app.module.ts @@ -13,6 +13,7 @@ import { PrismaService } from './prisma/prisma.service'; import { PrismaModule } from './prisma/prisma.module'; import { ConfigModule } from '@nestjs/config'; import { AuthController } from './auth/auth.controller'; +import { DocumentsController } from './documents/documents.controller'; @Module({ imports: [ @@ -32,7 +33,7 @@ import { AuthController } from './auth/auth.controller'; S3Module, PrismaModule, ], - controllers: [AppController, AuthController], + controllers: [AppController, AuthController, DocumentsController], providers: [ AppService, UploadService, diff --git a/backend/imk-backend/src/client/client.controller.ts b/backend/imk-backend/src/client/client.controller.ts index cfa735c..28a09f8 100644 --- a/backend/imk-backend/src/client/client.controller.ts +++ b/backend/imk-backend/src/client/client.controller.ts @@ -10,6 +10,6 @@ export class ClientController { @Get('documents') async getClientDocuments(@User() user) { - return this.clientService.getClientDocuments(user.id); + return this.clientService.getDocuments(user.id); } } diff --git a/backend/imk-backend/src/client/client.service.ts b/backend/imk-backend/src/client/client.service.ts index 40b8f72..16bee51 100644 --- a/backend/imk-backend/src/client/client.service.ts +++ b/backend/imk-backend/src/client/client.service.ts @@ -5,10 +5,26 @@ import { PrismaService } from '../prisma/prisma.service'; export class ClientService { constructor(private prisma: PrismaService) {} - async getClientDocuments(userId: string) { + // async getClientDocuments(userId: string) { + // return this.prisma.document.findMany({ + // where: { + // authorId: Number(userId), + // }, + // }); + // } + async getDocuments(userId: string) { return this.prisma.document.findMany({ where: { - authorId: Number(userId), + sharedWithId: Number(userId), + }, + include: { + sharedWith: { + select: { + id: true, + name: true, + email: true, + }, + }, }, }); } diff --git a/backend/imk-backend/src/documents/documents.controller.spec.ts b/backend/imk-backend/src/documents/documents.controller.spec.ts new file mode 100644 index 0000000..02050c8 --- /dev/null +++ b/backend/imk-backend/src/documents/documents.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { DocumentsController } from './documents.controller'; + +describe('DocumentsController', () => { + let controller: DocumentsController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [DocumentsController], + }).compile(); + + controller = module.get(DocumentsController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/backend/imk-backend/src/documents/documents.controller.ts b/backend/imk-backend/src/documents/documents.controller.ts new file mode 100644 index 0000000..a9a2a6b --- /dev/null +++ b/backend/imk-backend/src/documents/documents.controller.ts @@ -0,0 +1,12 @@ +import { Controller, Get, Param } from '@nestjs/common'; +import { DocumentsService } from './documents.service'; + +@Controller('documents') +export class DocumentsController { + constructor(private readonly documentsService: DocumentsService) {} + @Get('shared/:userId') + async getSharedDocuments(@Param('userId') userId: string) { + console.log('userId', userId); + return this.documentsService.getClientDocuments(parseInt(userId)); + } +} diff --git a/backend/imk-backend/src/documents/documents.service.ts b/backend/imk-backend/src/documents/documents.service.ts index 13197c5..2af0eb9 100644 --- a/backend/imk-backend/src/documents/documents.service.ts +++ b/backend/imk-backend/src/documents/documents.service.ts @@ -1,14 +1,33 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; -import { Document } from '@prisma/client'; +//import { Document } from '@prisma/client'; @Injectable() export class DocumentsService { constructor(private readonly prisma: PrismaService) {} - async findAllForClient(clientId: number): Promise { + async getClientDocuments(clientId: number) { + // return this.prisma.document.findMany({ + // where: { + // sharedWithId: clientId, + // }, + // include: { + // sharedWith: { + // select: { + // id: true, + // name: true, + // email: true, + // }, + // }, + // }, + // }); return this.prisma.document.findMany({ - where: { authorId: clientId }, + where: { + sharedWithId: clientId, + }, + orderBy: { + createdAt: 'desc', + }, }); } } diff --git a/frontend/imk/src/components/adminPanel/AdminPanel.jsx b/frontend/imk/src/components/adminPanel/AdminPanel.jsx index e53c6ab..e0b5b0a 100644 --- a/frontend/imk/src/components/adminPanel/AdminPanel.jsx +++ b/frontend/imk/src/components/adminPanel/AdminPanel.jsx @@ -148,8 +148,11 @@ function AdminPanel() { {doc.status} - + {/* {doc.sharedWith?.map((user) => user.name).join(', ') || 'None'} + */} + + {doc.sharedWith ? `${doc.sharedWith.name} (${doc.sharedWith.email})` : 'None'} {new Date(doc.createdAt).toLocaleDateString()} diff --git a/frontend/imk/src/components/clients/clients.jsx b/frontend/imk/src/components/clients/clients.jsx index f72d59a..fd658c1 100644 --- a/frontend/imk/src/components/clients/clients.jsx +++ b/frontend/imk/src/components/clients/clients.jsx @@ -1,11 +1,93 @@ -import React from 'react' +import { useState, useEffect } from 'react' +import { useAuth } from '../../hooks/useAuth'; +import { getSharedDocuments } from '../../services/api'; function Clients() { + const [documents, setDocuments] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const { user } = useAuth(); + + useEffect(() => { + const fetchDocuments = async () => { + try { + const response = await getSharedDocuments(user.id); + setDocuments(response.data); + } catch (err) { + setError('Failed to fetch documents'); + console.error('Error fetching documents:', err); + } finally { + setLoading(false); + } + }; + + if (user) { + fetchDocuments(); + } + }, [user]); + + if (loading) { + return ( +
+
+
+ ); + } + + if (error) { + return ( +
+ {error} +
+ ); + } + return ( -
+
+

Your Documents

+ {documents.length === 0 ? ( +

No documents have been shared with you yet.

+ ) : ( +
+ {documents.map((doc) => ( +
+
+

{doc.title}

+ + {doc.status} + +
+ +
+

Created: {new Date(doc.createdAt).toLocaleDateString()}

+ {doc.content && ( +

{doc.content}

+ )} +
+ +
+ +
+
+ ))} +
+ )}
- ) + ); } -export default Clients +export default Clients; + diff --git a/frontend/imk/src/components/documentUpload/DocumentUpload.jsx b/frontend/imk/src/components/documentUpload/DocumentUpload.jsx index dd3c8d3..44f24a5 100644 --- a/frontend/imk/src/components/documentUpload/DocumentUpload.jsx +++ b/frontend/imk/src/components/documentUpload/DocumentUpload.jsx @@ -27,34 +27,45 @@ function DocumentUpload() { setFile(selectedFile); }; - const handleSubmit = async (event) => { +const handleSubmit = async (event) => { event.preventDefault(); + + console.log('Form submission:', { file, title, selectedUsers }); + if (!file || !title || selectedUsers.length === 0) { setErrorMessage('Please provide a title, file, and select at least one user'); return; } - + setStatus('uploading'); setErrorMessage(''); - + const formData = new FormData(); formData.append('file', file); formData.append('title', title); - formData.append('sharedWith', JSON.stringify(selectedUsers)); - + formData.append('sharedWith', selectedUsers[0]); + try { - await uploadDocument(formData); + console.log('Sending request with:', { + title, + sharedWith: selectedUsers[0], + fileSize: file.size + }); + + const response = await uploadDocument(formData); + console.log('Upload response:', response); + setStatus('completed'); setTitle(''); setFile(null); setSelectedUsers([]); event.target.reset(); } catch (error) { + console.error('Upload error:', error); setStatus('failed'); setErrorMessage(error.response?.data?.message || 'Upload failed'); } }; - return (

Upload Document

diff --git a/frontend/imk/src/services/api.js b/frontend/imk/src/services/api.js index dad736c..8625f43 100644 --- a/frontend/imk/src/services/api.js +++ b/frontend/imk/src/services/api.js @@ -4,7 +4,7 @@ import axios from 'axios'; const API_URL = 'http://localhost:3000'; const api = axios.create({ - baseURL: API_URL, + baseURL: API_URL }); api.interceptors.request.use((config) => { @@ -16,26 +16,28 @@ api.interceptors.request.use((config) => { }); export const createUser = (userData) => api.post('/admin/users', userData); export const login = (username, password) => api.post('/auth/login', { username, password }); -// export const getAllUsers = () => api.get('/admin/users'); export const shareDocument = (documentId, userIds) => api.post(`/admin/documents/${documentId}/share`, { userIds }); export const updateDocumentStatus = (documentId, status) => api.put(`/admin/documents/${documentId}/status`, { status }); -// export const uploadDocument = (formData) => api.post('/admin/documents', formData, { -// headers: { 'Content-Type': 'multipart/form-data' }, -// }); - export const uploadDocument = async (formData) => { - const response = await api.post('/admin/documents', formData, { - headers: { - 'Content-Type': 'multipart/form-data', - }, - }); - return response.data; + try { + const response = await api.post('/admin/documents', formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + console.log('Upload API response:', response.data); + return response.data; + } catch (error) { + console.error('Upload API error:', error); + throw error; + } }; export const getUserInfo = () => api.get('/auth/user-info'); -// ... existing code ... + export const getAllDocuments = () => api.get('/admin/documents'); +export const getSharedDocuments = (userId) => api.get(`/documents/shared/${userId}`); export const getAllUsers = () => api.get('/admin/users'); export default api;