Files
prosapp-migration/backend/src/professionals/professionals.controller.ts
T
Lizandro GuarnizoandClaude Sonnet 4.6 d7a0769b46 feat: filter professionals by city and proximity
- findAllActive now accepts city, lat, lng params
- City filter: case-insensitive contains on users.city
- Proximity sort: Haversine within city before services/score ranking
- Controller exposes ?city=&lat=&lng= query params

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-21 20:35:03 -05:00

128 lines
3.5 KiB
TypeScript

import { Controller, Get, Post, Patch, Delete, Param, Body, UseGuards, Req, HttpCode, HttpStatus, NotFoundException } from '@nestjs/common';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { ProfessionalsService } from './professionals.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { CreateProfessionalDto, UpdateProfessionalDto, UpdateSchedulesDto } from './dto/professional.dto';
import { AuthService } from '../auth/auth.service';
@ApiTags('Professionals')
@Controller('professionals')
export class ProfessionalsController {
constructor(
private pros: ProfessionalsService,
private auth: AuthService,
) {}
@Get()
findAllActive(@Req() req) {
const search = req.query.search as string | undefined;
const city = req.query.city as string | undefined;
const lat = req.query.lat ? parseFloat(req.query.lat as string) : undefined;
const lng = req.query.lng ? parseFloat(req.query.lng as string) : undefined;
return this.pros.findAllActive(search, city, lat, lng);
}
@Get('pending')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
findPending(@Req() req) {
const page = +(req.query.page || 1);
const limit = +(req.query.limit || 20);
return this.pros.findPendingApprovals(page, limit);
}
@Get('rejected')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
findRejected(@Req() req) {
const page = +(req.query.page || 1);
const limit = +(req.query.limit || 20);
return this.pros.findRejected(page, limit);
}
@Post(':id/approve')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
approve(@Param('id') id: string) {
return this.pros.approve(id);
}
@Post(':id/deny')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
deny(@Param('id') id: string) {
return this.pros.deny(id);
}
@Post(':id/validate-rethus')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
validateRethus(@Param('id') id: string) {
return this.pros.validateRethus(id);
}
@Get('me')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
async findByMe(@Req() req) {
const profId = await this.auth.getProfessionalId(req.user.sub);
if (!profId) throw new NotFoundException('No eres un profesional');
return this.pros.findByUserId(req.user.sub);
}
@Post('request')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
request(@Req() req, @Body() dto: CreateProfessionalDto) {
return this.pros.requestProfessional(req.user.sub, dto);
}
@Delete('me')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
async deleteMe(@Req() req) {
return this.pros.resetRejected(req.user.sub);
}
@Patch('me/schedules')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
async updateSchedules(@Req() req, @Body() dto: UpdateSchedulesDto) {
const prof = await this.pros.findByUserId(req.user.sub);
return this.pros.updateSchedules(prof.id, dto.schedules);
}
@Patch('me')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
async update(@Req() req, @Body() dto: UpdateProfessionalDto) {
return this.pros.upsert(req.user.sub, dto);
}
@Post(':id/deactivate')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
deactivate(@Param('id') id: string) {
return this.pros.deactivate(id);
}
@Post(':id/set-pending')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
setPending(@Param('id') id: string) {
return this.pros.setPending(id);
}
@Patch(':id')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
updateById(@Param('id') id: string, @Body() dto: UpdateProfessionalDto) {
return this.pros.updateById(id, dto);
}
@Get(':id')
findById(@Param('id') id: string) {
return this.pros.findById(id);
}
}