Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
78b90c3
refactor: implement authentication by patient and separate responsabi…
julianosill Dec 16, 2025
de21750
refactor: create new initial migrate with correct datetime type in db
julianosill Dec 19, 2025
b1764f3
refactor(patients): update module to match new use-case pattern
julianosill Dec 19, 2025
39321bc
refactor(patient-supports): update module to match new use-case pattern
julianosill Dec 19, 2025
7c4c3bf
fix(patients): add transaction to ensure consistency if patient suppo…
julianosill Dec 19, 2025
96652da
refactor(patient-requirements): update module to match new use-case p…
julianosill Dec 20, 2025
305d7a5
refactor(users): update module to match new use-case pattern
julianosill Dec 20, 2025
4d43c94
refactor(auth): update module to match new use-case pattern
julianosill Dec 21, 2025
e0bf4f3
refactor: improve readability and simplify use-cases
julianosill Dec 24, 2025
bb109aa
refactor(auth): apply use-case pattern to auth module
julianosill Jan 16, 2026
7bbe7d6
feat(users): create `users/invite` endpoint and update register user …
julianosill Jan 17, 2026
db7c64b
chore(use-cases): rename types to keep consistency
julianosill Jan 17, 2026
6de59e3
chore(auth): improve readability and remove unused schema
julianosill Jan 18, 2026
26b1f8a
feat(auth): create `change-password` endpoint
julianosill Jan 19, 2026
2831f1a
feat(users): create `GET /users` endpoint
julianosill Jan 19, 2026
568ae7b
chore(docs): update swagger endpoints descriptions
julianosill Jan 19, 2026
1b929fe
chore(users): add response DTOs to swagger docs
julianosill Jan 19, 2026
e5b5463
feat(statistics): create `GET /referred-patients-total` endpoint
julianosill Jan 19, 2026
9711a53
chore(docs): add response DTOs to each endpoint
julianosill Jan 20, 2026
4d1a9e5
chore(logs): update logs message to match the pattern
julianosill Jan 20, 2026
1bbbab4
chore(docs): fix prettier errors
julianosill Jan 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ API_PORT=3333

# APP
APP_URL="http://localhost:3000"
APP_LOCAL_URL="http://localhost:3000"

# Secrets
COOKIE_DOMAIN="localhost"
Expand Down
1 change: 0 additions & 1 deletion .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ API_PORT=3333

# APP
APP_URL="http://localhost:3000"
APP_LOCAL_URL="http://localhost:3000"

# Secrets
COOKIE_DOMAIN="localhost"
Expand Down
228 changes: 219 additions & 9 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,231 @@
# Copilot Instructions for ABNMO Platform

## Architecture Overview
NestJS SaaS API managing patients, referrals, appointments, and health tracking. Uses TypeORM with MySQL and Zod schemas for validation.

This is an NestJS application for a SaaS platform managing patients and appointments.
## Architecture: MVC Pattern

- **Stack**: NestJS API with TypeORM and MySQL
**Stack**: NestJS + TypeORM + MySQL + Zod

## Code Patterns & Conventions
Module structure (`/src/app/http/{featureName}`):

Always adhere to the conventions in `/docs` files and follow these patterns:
```
{feature}/
├── {feature}.module.ts # Module definition with imports/providers
├── {feature}.controller.ts # Routes and request handling
├── {feature}.dtos.ts # DTOs created from Zod schemas
└── use-cases/
└── {action}-{feature}.use-case.ts # Action operations, such as get, create, update, remove, cancel, delete
```

- **File Structure**: Follow a consistent file structure for services, routes, and components.
- **Naming Conventions**: Use camelCase for variables and functions, PascalCase for classes and components.
## Module Organization

## Common Gotchas
### 1. Module File (`*.module.ts`)

1. **Database Relations**: Always destructure relations from the main table object, example: `relations: { user: true }`
Register entities, inject TypeORM repositories, and declare use-case providers:

```typescript
@Module({
imports: [TypeOrmModule.forFeature([Entity1, Entity2])],
controllers: [FeatureController],
providers: [
GetFeatureUseCase,
CreateFeatureUseCase,
...
],
})
export class FeatureModule {}
```

### 2. DTOs File (`*.dtos.ts`)

Create DTOs exclusively from Zod schemas in `/domain/schemas`. Use `createZodDto()` and name DTOs explicitly:

```typescript
import { createZodDto } from 'nestjs-zod';
import {
createAppointmentSchema,
updateAppointmentSchema,
getAppointmentsQuerySchema,
} from '@/domain/schemas/appointments/requests';

export class CreateAppointmentDto extends createZodDto(
createAppointmentSchema,
) {}
export class UpdateAppointmentDto extends createZodDto(
updateAppointmentSchema,
) {}
export class GetAppointmentsQuery extends createZodDto(
getAppointmentsQuerySchema,
) {}
```

**Naming**: `{Action}{Entity}Dto` (e.g., `CreateAppointmentDto`, `GetAppointmentsQuery`)

### 3. Controller File (`*.controller.ts`)

Inject all use-cases and call them based on HTTP methods:

```typescript
@Controller('appointments')
export class AppointmentsController {
constructor(
private readonly getAppointmentsUseCase: GetAppointmentsUseCase,
private readonly createAppointmentUseCase: CreateAppointmentUseCase,
...
) {}

@Get()
async get(@Query() query: GetAppointmentsQuery): Promise<Response> {
const data = await this.getAppointmentsUseCase.execute(query);
return { success: true, data };
}

@Post()
async create(@Body() createAppointmentDto: CreateAppointmentDto): Promise<BaseResponse> {
await this.createAppointmentUseCase.execute(createAppointmentDto);
return { success: true };
}
}
```

### 4. Use-Case Files (`use-cases/*.use-case.ts`)

One use-case per file, one responsibility. Define input/output types explicitly:

```typescript
interface GetAppointmentsUseCaseInput {
query: GetAppointmentsQuery;
user: AuthUserDto;
}

interface GetAppointmentsUseCaseOutput = {
appointments: Appointment[]>
}

@Injectable()
export class GetAppointmentsUseCase {
constructor(
@InjectRepository(Appointment)
private readonly appointmentsRepository: Repository<Appointment>,
) {}

async execute(
request: GetAppointmentsUseCaseInput,
): Promise<GetAppointmentsUseCaseOutput> {
// Implementation
}
}
```

**Naming**: `{Action}{Entity}UseCase` (e.g., `CreateAppointmentUseCase`, `GetAppointmentsUseCase`)

## Zod Schemas & Enums

Centralize validation and types in `/domain/schemas` and `/domain/enums`:

- **Schemas** (`/domain/schemas/{entity}/{type}.ts`): Define request/response validation
- **Enums** (`/domain/enums/{entity}.ts`): Define constants and types

Example enum pattern:

```typescript
export const APPOINTMENT_STATUSES = [
'scheduled',
'canceled',
'completed',
] as const;
export type AppointmentStatus = (typeof APPOINTMENT_STATUSES)[number];
```

DTOs inherit validation directly from schemas, no manual definition needed.

## Naming Conventions

Clear, explicit, human-readable names. Reduce cognitive load:

- **Variables/Functions**: `camelCase` (e.g., `getUserAppointments`, `createdAt`)
- **Classes/Types**: `PascalCase` (e.g., `CreateAppointmentDto`, `AppointmentStatus`)
- **Enums/Constants**: `SCREAMING_SNAKE_CASE` (e.g., `APPOINTMENT_STATUSES`, `MAX_RESULTS_LIMIT`)
- **Files**: `kebab-case` (e.g., `create-appointment.use-case.ts`, `appointments.dtos.ts`)

Files should match their exports: `get-total-patients.use-case.ts` exports `GetTotalPatientsUseCase`.

## Database Patterns

### Queries

- **Always select fields**: `select: { id: true, name: true }` — avoid over-fetching
- **Count operations**: Select only `id` for performance
- **Relations**: Destructure explicitly: `relations: { user: true }`

```typescript
const appointments = await this.appointmentsRepository.find({
select: { id: true, date: true, status: true },
relations: { patient: true },
where: { patientId: id },
});
```

### Repository Access

Inject TypeORM repositories directly into use-cases. No separate repository files:

```typescript
@Injectable()
export class CreateAppointmentUseCase {
constructor(
@InjectRepository(Appointment)
private readonly appointmentsRepository: Repository<Appointment>,
) {}
}
```

## Common Patterns

### Error Handling

Use NestJS exceptions with descriptive messages:

- **User-facing messages**: Use Portuguese (pt-BR) for exception messages. These will be displayed in the UI.
- **Internal messages**: Use English for logging. These are for development and debugging.

```typescript
if (!patient) {
throw new NotFoundException('Paciente não encontrado.');
}

if (date > maxDate) {
throw new BadRequestException(
'A data de atendimento deve estar dentro dos próximos 3 meses.',
);
}
```

### Logging

Log significant events in use-cases with English messages:

```typescript
private readonly logger = new Logger(CreateAppointmentUseCase.name);

this.logger.log({ patientId, appointmentId, createdBy }, 'Appointment created successfully');
```

### Query Builders

Use query builders for complex filtering:

```typescript
const where: FindOptionsWhere<Patient> = {
status: status ?? Not('pending'),
};

if (period) {
where.created_at = Between(dateRange.startDate, dateRange.endDate);
}

const result = await this.patientsRepository.find({ where });
```

## Writing Guidelines

Expand Down
Loading