Commit ce99ff67 by Augusto

0.0.2

parent 7e09e55c
-- Add BACKOFFICE status to the status enum
ALTER TABLE occurrences MODIFY COLUMN status ENUM(
'OPEN',
'BACKOFFICE',
'ASSIGNED',
'IN_PROGRESS',
'RESOLVED',
'PAUSED',
'CLOSED',
'PARCIAL_RESOLVED',
'CANCELLED'
) NOT NULL DEFAULT 'OPEN';
-- Update occurrence_logs table to include BACKOFFICE status
ALTER TABLE occurrence_logs MODIFY COLUMN status ENUM(
'OPEN',
'BACKOFFICE',
'ASSIGNED',
'IN_PROGRESS',
'RESOLVED',
'PAUSED',
'CLOSED',
'PARCIAL_RESOLVED',
'CANCELLED'
) NOT NULL DEFAULT 'OPEN';
-- Update comments table to include BACKOFFICE status
ALTER TABLE comments MODIFY COLUMN status ENUM(
'OPEN',
'BACKOFFICE',
'ASSIGNED',
'IN_PROGRESS',
'RESOLVED',
'PAUSED',
'CLOSED',
'PARCIAL_RESOLVED',
'CANCELLED'
);
-- Remove unique constraint from conclusions.occurrenceId to allow multiple conclusions per occurrence
ALTER TABLE conclusions DROP INDEX occurrenceId;
-- Add createdBy column to conclusions table to track who created each conclusion
ALTER TABLE conclusions ADD COLUMN createdBy varchar(25) NOT NULL;
-- Add foreign key constraint to users table
ALTER TABLE conclusions ADD CONSTRAINT conclusions_createdBy_fkey
FOREIGN KEY (createdBy) REFERENCES users(id);
-- Add index for performance
ALTER TABLE conclusions ADD INDEX conclusion_created_by_idx (createdBy);
{ {
"name": "unike-form", "name": "unike-form",
"version": "0.0.1", "version": "0.0.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
......
...@@ -21,6 +21,7 @@ export const userRoleEnum = mysqlEnum('user_role', [ ...@@ -21,6 +21,7 @@ export const userRoleEnum = mysqlEnum('user_role', [
]); ]);
export const occurrenceStatusEnum = mysqlEnum('status', [ export const occurrenceStatusEnum = mysqlEnum('status', [
'OPEN', 'OPEN',
'BACKOFFICE',
'ASSIGNED', 'ASSIGNED',
'IN_PROGRESS', 'IN_PROGRESS',
'RESOLVED', 'RESOLVED',
...@@ -155,12 +156,14 @@ export const conclusions = mysqlTable( ...@@ -155,12 +156,14 @@ export const conclusions = mysqlTable(
description: text('description').notNull(), description: text('description').notNull(),
fieldMaterial: boolean('fieldMaterial').notNull().default(false), fieldMaterial: boolean('fieldMaterial').notNull().default(false),
materialUsed: text('materialUsed'), materialUsed: text('materialUsed'),
occurrenceId: varchar('occurrenceId', { length: 25 }).notNull().unique(), occurrenceId: varchar('occurrenceId', { length: 25 }).notNull(),
createdBy: varchar('createdBy', { length: 25 }).notNull(),
createdAt: timestamp('createdAt').notNull().defaultNow(), createdAt: timestamp('createdAt').notNull().defaultNow(),
updatedAt: timestamp('updatedAt').notNull().defaultNow().onUpdateNow(), updatedAt: timestamp('updatedAt').notNull().defaultNow().onUpdateNow(),
}, },
(table) => ({ (table) => ({
occurrenceIdx: index('conclusion_occurrence_idx').on(table.occurrenceId), occurrenceIdx: index('conclusion_occurrence_idx').on(table.occurrenceId),
createdByIdx: index('conclusion_created_by_idx').on(table.createdBy),
}), }),
); );
...@@ -255,7 +258,7 @@ export const occurrencesRelations = relations(occurrences, ({ one, many }) => ({ ...@@ -255,7 +258,7 @@ export const occurrencesRelations = relations(occurrences, ({ one, many }) => ({
occurrenceAssistants: many(occurrenceAssistants), occurrenceAssistants: many(occurrenceAssistants),
comments: many(comments), comments: many(comments),
attachments: many(attachments), attachments: many(attachments),
conclusion: one(conclusions), conclusions: many(conclusions),
logs: many(occurrenceLogs), logs: many(occurrenceLogs),
})); }));
...@@ -282,6 +285,10 @@ export const conclusionsRelations = relations(conclusions, ({ one }) => ({ ...@@ -282,6 +285,10 @@ export const conclusionsRelations = relations(conclusions, ({ one }) => ({
fields: [conclusions.occurrenceId], fields: [conclusions.occurrenceId],
references: [occurrences.id], references: [occurrences.id],
}), }),
createdByUser: one(users, {
fields: [conclusions.createdBy],
references: [users.id],
}),
})); }));
export const occurrenceLogsRelations = relations(occurrenceLogs, ({ one }) => ({ export const occurrenceLogsRelations = relations(occurrenceLogs, ({ one }) => ({
...@@ -339,6 +346,7 @@ export type NewOccurrenceAssistant = typeof occurrenceAssistants.$inferInsert; ...@@ -339,6 +346,7 @@ export type NewOccurrenceAssistant = typeof occurrenceAssistants.$inferInsert;
export type UserRole = 'USER' | 'MODERATOR' | 'ADMIN'; export type UserRole = 'USER' | 'MODERATOR' | 'ADMIN';
export type OccurrenceStatus = export type OccurrenceStatus =
| 'OPEN' | 'OPEN'
| 'BACKOFFICE'
| 'ASSIGNED' | 'ASSIGNED'
| 'IN_PROGRESS' | 'IN_PROGRESS'
| 'RESOLVED' | 'RESOLVED'
...@@ -374,6 +382,7 @@ export const UserRoleEnum = { ...@@ -374,6 +382,7 @@ export const UserRoleEnum = {
export const OccurrenceStatusEnum = { export const OccurrenceStatusEnum = {
OPEN: 'OPEN' as const, OPEN: 'OPEN' as const,
BACKOFFICE: 'BACKOFFICE' as const,
ASSIGNED: 'ASSIGNED' as const, ASSIGNED: 'ASSIGNED' as const,
IN_PROGRESS: 'IN_PROGRESS' as const, IN_PROGRESS: 'IN_PROGRESS' as const,
RESOLVED: 'RESOLVED' as const, RESOLVED: 'RESOLVED' as const,
......
...@@ -21,6 +21,8 @@ import { CreateConclusionDto } from './dto/create-conclusion.dto'; ...@@ -21,6 +21,8 @@ import { CreateConclusionDto } from './dto/create-conclusion.dto';
import { UpdateConclusionDto } from './dto/update-conclusion.dto'; import { UpdateConclusionDto } from './dto/update-conclusion.dto';
import { ConclusionResponseDto } from './dto/conclusion-response.dto'; import { ConclusionResponseDto } from './dto/conclusion-response.dto';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { CurrentUser } from '../auth/decorators/current-user.decorator';
import { UserResponseDto } from '../user/dto/user-response.dto';
@ApiTags('conclusions') @ApiTags('conclusions')
@ApiBearerAuth() @ApiBearerAuth()
...@@ -45,8 +47,13 @@ export class ConclusionController { ...@@ -45,8 +47,13 @@ export class ConclusionController {
async create( async create(
@Param('occurrenceId') occurrenceId: string, @Param('occurrenceId') occurrenceId: string,
@Body() createConclusionDto: CreateConclusionDto, @Body() createConclusionDto: CreateConclusionDto,
@CurrentUser() user: UserResponseDto,
): Promise<ConclusionResponseDto> { ): Promise<ConclusionResponseDto> {
return this.conclusionService.create(occurrenceId, createConclusionDto); return this.conclusionService.create(
occurrenceId,
createConclusionDto,
user.id,
);
} }
@Get('occurrence/:occurrenceId') @Get('occurrence/:occurrenceId')
......
...@@ -5,7 +5,7 @@ import { ...@@ -5,7 +5,7 @@ import {
ConflictException, ConflictException,
} from '@nestjs/common'; } from '@nestjs/common';
import { DrizzleService } from '../../common/drizzle.service'; import { DrizzleService } from '../../common/drizzle.service';
import { conclusions, occurrences } from '../../drizzle/schema'; import { conclusions, occurrences, users } from '../../drizzle/schema';
import { eq } from 'drizzle-orm'; import { eq } from 'drizzle-orm';
import { CreateConclusionDto } from './dto/create-conclusion.dto'; import { CreateConclusionDto } from './dto/create-conclusion.dto';
import { UpdateConclusionDto } from './dto/update-conclusion.dto'; import { UpdateConclusionDto } from './dto/update-conclusion.dto';
...@@ -18,6 +18,7 @@ export class ConclusionService { ...@@ -18,6 +18,7 @@ export class ConclusionService {
async create( async create(
occurrenceId: string, occurrenceId: string,
createConclusionDto: CreateConclusionDto, createConclusionDto: CreateConclusionDto,
createdBy: string,
): Promise<ConclusionResponseDto> { ): Promise<ConclusionResponseDto> {
// Check if occurrence exists // Check if occurrence exists
const [occurrence] = await this.drizzle.db const [occurrence] = await this.drizzle.db
...@@ -32,17 +33,15 @@ export class ConclusionService { ...@@ -32,17 +33,15 @@ export class ConclusionService {
); );
} }
// Check if conclusion already exists for this occurrence // Validate that the user exists
const [existingConclusion] = await this.drizzle.db const [user] = await this.drizzle.db
.select() .select()
.from(conclusions) .from(users)
.where(eq(conclusions.occurrenceId, occurrenceId)) .where(eq(users.id, createdBy))
.limit(1); .limit(1);
if (existingConclusion) { if (!user) {
throw new ConflictException( throw new BadRequestException('User not found');
`Conclusion already exists for occurrence ${occurrenceId}`,
);
} }
// Validate material fields // Validate material fields
...@@ -69,6 +68,7 @@ export class ConclusionService { ...@@ -69,6 +68,7 @@ export class ConclusionService {
id: conclusionId, id: conclusionId,
...createConclusionDto, ...createConclusionDto,
occurrenceId, occurrenceId,
createdBy,
}); });
// Get the created conclusion // Get the created conclusion
......
...@@ -32,6 +32,29 @@ export class ConclusionResponseDto { ...@@ -32,6 +32,29 @@ export class ConclusionResponseDto {
occurrenceId: string; occurrenceId: string;
@ApiProperty({ @ApiProperty({
description: 'ID of the user who created the conclusion',
example: 'clxy123456789',
})
createdBy: string;
@ApiPropertyOptional({
description: 'User who created the conclusion',
type: 'object',
properties: {
id: { type: 'string', example: 'clxy123456789' },
firstName: { type: 'string', example: 'John' },
lastName: { type: 'string', example: 'Doe' },
email: { type: 'string', example: 'john.doe@example.com' },
},
})
createdByUser?: {
id: string;
firstName: string;
lastName: string;
email: string;
};
@ApiProperty({
description: 'Date and time when the conclusion was created', description: 'Date and time when the conclusion was created',
example: '2023-12-01T10:00:00Z', example: '2023-12-01T10:00:00Z',
}) })
......
...@@ -214,8 +214,8 @@ export class OccurrenceResponseDto { ...@@ -214,8 +214,8 @@ export class OccurrenceResponseDto {
}; };
@ApiPropertyOptional({ @ApiPropertyOptional({
description: 'Conclusion for this occurrence', description: 'Conclusions for this occurrence',
type: ConclusionResponseDto, type: [ConclusionResponseDto],
}) })
conclusion?: ConclusionResponseDto | null; conclusions?: ConclusionResponseDto[];
} }
...@@ -129,12 +129,28 @@ export class OccurrenceService { ...@@ -129,12 +129,28 @@ export class OccurrenceService {
.leftJoin(users, eq(occurrenceAssistants.assignedBy, users.id)) .leftJoin(users, eq(occurrenceAssistants.assignedBy, users.id))
.where(eq(occurrenceAssistants.occurrenceId, occurrenceId)); .where(eq(occurrenceAssistants.occurrenceId, occurrenceId));
// Get conclusion if exists // Get conclusions if any exist with user info
const [conclusion] = await this.drizzle.db const conclusionsList = await this.drizzle.db
.select() .select({
id: conclusions.id,
description: conclusions.description,
fieldMaterial: conclusions.fieldMaterial,
materialUsed: conclusions.materialUsed,
occurrenceId: conclusions.occurrenceId,
createdBy: conclusions.createdBy,
createdAt: conclusions.createdAt,
updatedAt: conclusions.updatedAt,
createdByUser: {
id: users.id,
firstName: users.firstName,
lastName: users.lastName,
email: users.email,
},
})
.from(conclusions) .from(conclusions)
.leftJoin(users, eq(conclusions.createdBy, users.id))
.where(eq(conclusions.occurrenceId, occurrenceId)) .where(eq(conclusions.occurrenceId, occurrenceId))
.limit(1); .orderBy(desc(conclusions.createdAt));
// Get counts // Get counts
const [commentCount] = await this.drizzle.db const [commentCount] = await this.drizzle.db
...@@ -153,7 +169,7 @@ export class OccurrenceService { ...@@ -153,7 +169,7 @@ export class OccurrenceService {
assignedTo, assignedTo,
managedBy, managedBy,
assistants: assistantsList.length > 0 ? assistantsList : undefined, assistants: assistantsList.length > 0 ? assistantsList : undefined,
conclusion: conclusion || undefined, conclusions: conclusionsList.length > 0 ? conclusionsList : undefined,
_count: { _count: {
comments: commentCount.count, comments: commentCount.count,
attachments: attachmentCount.count, attachments: attachmentCount.count,
...@@ -443,6 +459,7 @@ export class OccurrenceService { ...@@ -443,6 +459,7 @@ export class OccurrenceService {
try { try {
const statusMap = { const statusMap = {
OPEN: 'Aberta', OPEN: 'Aberta',
BACKOFFICE: 'Backoffice',
ASSIGNED: 'Atribuída', ASSIGNED: 'Atribuída',
IN_PROGRESS: 'Em Progresso', IN_PROGRESS: 'Em Progresso',
RESOLVED: 'Resolvida', RESOLVED: 'Resolvida',
...@@ -772,8 +789,8 @@ export class OccurrenceService { ...@@ -772,8 +789,8 @@ export class OccurrenceService {
dualAssignOccurrenceDto: DualAssignOccurrenceDto, dualAssignOccurrenceDto: DualAssignOccurrenceDto,
performedBy: string, performedBy: string,
): Promise<OccurrenceResponseDto> { ): Promise<OccurrenceResponseDto> {
// Check if occurrence exists // Check if occurrence exists and get current status
await this.findOne(occurrenceId); const currentOccurrence = await this.findOne(occurrenceId);
// Validate that at least one field is provided // Validate that at least one field is provided
if ( if (
...@@ -823,8 +840,17 @@ export class OccurrenceService { ...@@ -823,8 +840,17 @@ export class OccurrenceService {
updateData.managerId = dualAssignOccurrenceDto.managerId; updateData.managerId = dualAssignOccurrenceDto.managerId;
} }
// Set status to ASSIGNED if either assignee or manager is being assigned // Set status based on assignment type and current status
updateData.status = OccurrenceStatusEnum.ASSIGNED; if (dualAssignOccurrenceDto.assigneeId) {
// If assignee is being assigned, set to ASSIGNED
updateData.status = OccurrenceStatusEnum.ASSIGNED;
} else if (dualAssignOccurrenceDto.managerId) {
// If only manager is being assigned, set to BACKOFFICE (unless already ASSIGNED)
if (currentOccurrence.status !== OccurrenceStatusEnum.ASSIGNED) {
updateData.status = OccurrenceStatusEnum.BACKOFFICE;
}
// If already ASSIGNED, don't change status
}
// Update occurrence with provided fields // Update occurrence with provided fields
await this.drizzle.db await this.drizzle.db
...@@ -832,7 +858,7 @@ export class OccurrenceService { ...@@ -832,7 +858,7 @@ export class OccurrenceService {
.set(updateData) .set(updateData)
.where(eq(occurrences.id, occurrenceId)); .where(eq(occurrences.id, occurrenceId));
// Create system comments for assignments // Create system comments for assignments and status changes
try { try {
if (dualAssignOccurrenceDto.assigneeId && assignee) { if (dualAssignOccurrenceDto.assigneeId && assignee) {
await this.commentService.createSystemComment( await this.commentService.createSystemComment(
...@@ -851,6 +877,19 @@ export class OccurrenceService { ...@@ -851,6 +877,19 @@ export class OccurrenceService {
performedBy, performedBy,
); );
} }
// Create system comment for status change to BACKOFFICE
if (
updateData.status === OccurrenceStatusEnum.BACKOFFICE &&
currentOccurrence.status !== OccurrenceStatusEnum.BACKOFFICE
) {
await this.commentService.createSystemComment(
occurrenceId,
'Status alterado para "Backoffice"',
true,
performedBy,
);
}
} catch (commentError) { } catch (commentError) {
console.error( console.error(
'Failed to create system comments for assignments:', 'Failed to create system comments for assignments:',
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
export type UserRole = 'USER' | 'MODERATOR' | 'ADMIN'; export type UserRole = 'USER' | 'MODERATOR' | 'ADMIN';
export type OccurrenceStatus = export type OccurrenceStatus =
| 'OPEN' | 'OPEN'
| 'BACKOFFICE'
| 'ASSIGNED' | 'ASSIGNED'
| 'IN_PROGRESS' | 'IN_PROGRESS'
| 'RESOLVED' | 'RESOLVED'
...@@ -37,6 +38,7 @@ export const UserRoleEnum = { ...@@ -37,6 +38,7 @@ export const UserRoleEnum = {
export const OccurrenceStatusEnum = { export const OccurrenceStatusEnum = {
OPEN: 'OPEN' as const, OPEN: 'OPEN' as const,
BACKOFFICE: 'BACKOFFICE' as const,
ASSIGNED: 'ASSIGNED' as const, ASSIGNED: 'ASSIGNED' as const,
IN_PROGRESS: 'IN_PROGRESS' as const, IN_PROGRESS: 'IN_PROGRESS' as const,
RESOLVED: 'RESOLVED' as const, RESOLVED: 'RESOLVED' as const,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment