Files
CRM_Montzar/apps/api/src/notes/notes.controller.ts
T
MahdiandCursor 4e3dbb4e00 Redesign notes list and editor to match Keep-style mockups.
Add checklist, activity link, location, analyze flag, and audio attachments with a real API-backed masonry UI.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-05 19:13:01 +03:30

177 lines
4.5 KiB
TypeScript

import {
Body,
Controller,
Delete,
Get,
Param,
Patch,
Post,
Query,
StreamableFile,
UploadedFile,
UseGuards,
UseInterceptors,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { memoryStorage } from 'multer';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { CurrentUser } from '../auth/current-user.decorator';
import { CreateNoteDto, ListNotesQueryDto, UpdateNoteDto, ApplyNoteAiSuggestionDto } from './dto';
import { NoteAiService } from './note-ai.service';
import { NotesService } from './notes.service';
type AuthUser = { organizationId: string; userId: string; role: string };
@Controller('notes')
@UseGuards(JwtAuthGuard)
export class NotesController {
constructor(
private readonly notesService: NotesService,
private readonly noteAi: NoteAiService,
) {}
@Get()
list(
@CurrentUser() user: AuthUser,
@Query() query: ListNotesQueryDto,
) {
return this.notesService.list(user.organizationId, user, {
q: query.q,
pinned: query.pinned,
personId: query.personId,
companyId: query.companyId,
dealId: query.dealId,
tag: query.tag,
noteType: query.noteType,
sort: query.sort,
page: query.page ?? 1,
limit: query.limit ?? 50,
});
}
@Get(':id')
get(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.notesService.get(user.organizationId, id, user);
}
@Post()
create(@CurrentUser() user: AuthUser, @Body() dto: CreateNoteDto) {
return this.notesService.create(
user.organizationId,
user.userId,
dto,
user,
);
}
@Patch(':id')
update(
@CurrentUser() user: AuthUser,
@Param('id') id: string,
@Body() dto: UpdateNoteDto,
) {
return this.notesService.update(user.organizationId, id, user, dto);
}
@Patch(':id/pin')
togglePin(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.notesService.togglePin(user.organizationId, id, user);
}
@Post(':id/convert-to-task')
convertToTask(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.notesService.convertToTask(
user.organizationId,
user.userId,
id,
user,
);
}
@Post(':id/analyze')
analyze(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.noteAi.analyze(user.organizationId, id, user);
}
@Get(':id/ai-suggestion')
getAiSuggestion(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.noteAi.getSuggestion(user.organizationId, id, user);
}
@Post(':id/ai-suggestion/apply')
applyAiSuggestion(
@CurrentUser() user: AuthUser,
@Param('id') id: string,
@Body() dto: ApplyNoteAiSuggestionDto,
) {
return this.noteAi.apply(user.organizationId, user.userId, id, user, dto);
}
@Post(':id/ai-suggestion/dismiss')
dismissAiSuggestion(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.noteAi.dismiss(user.organizationId, id, user);
}
@Post(':id/attachments')
@UseInterceptors(
FileInterceptor('file', {
storage: memoryStorage(),
limits: { fileSize: 10 * 1024 * 1024 },
}),
)
uploadAttachment(
@CurrentUser() user: AuthUser,
@Param('id') id: string,
@UploadedFile() file: Express.Multer.File,
) {
return this.notesService.addAttachment(
user.organizationId,
user.userId,
id,
file,
user,
);
}
@Get(':id/attachments/:attachmentId/download')
async downloadAttachment(
@CurrentUser() user: AuthUser,
@Param('id') id: string,
@Param('attachmentId') attachmentId: string,
): Promise<StreamableFile> {
const { stream, fileName, mimeType } =
await this.notesService.openAttachmentDownload(
user.organizationId,
id,
attachmentId,
user,
);
const encoded = encodeURIComponent(fileName);
return new StreamableFile(stream, {
type: mimeType,
disposition: `attachment; filename="${fileName.replace(/"/g, '')}"; filename*=UTF-8''${encoded}`,
});
}
@Delete(':id/attachments/:attachmentId')
async removeAttachment(
@CurrentUser() user: AuthUser,
@Param('id') id: string,
@Param('attachmentId') attachmentId: string,
) {
await this.notesService.removeAttachment(
user.organizationId,
id,
attachmentId,
user,
);
return { ok: true };
}
@Delete(':id')
async remove(@CurrentUser() user: AuthUser, @Param('id') id: string) {
await this.notesService.remove(user.organizationId, id, user);
return { ok: true };
}
}