Add rejection retry flow with configurable wait days
- Backend: DELETE /professionals/me resets rejected state so user can retry - Backend: findRejected/findPending now filter by pro_state - Admin settings: rejection_wait_days field (default 7) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
3fd7a33fc1
commit
0899105a52
@@ -263,6 +263,32 @@ export default function SettingsPage() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Solicitudes de profesional */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-base">
|
||||
<Clock size={18} className="text-muted-foreground" />
|
||||
Solicitudes de profesional
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-1">
|
||||
<label className="text-sm text-muted-foreground">Días de espera para reintentar tras rechazo</label>
|
||||
<div className="flex items-center gap-2">
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
className="w-28"
|
||||
value={global.rejection_wait_days ?? 7}
|
||||
onChange={(e) => setG('rejection_wait_days', Number(e.target.value))}
|
||||
/>
|
||||
<span className="text-sm text-muted-foreground">días</span>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">Con 0 el usuario puede reintentar inmediatamente.</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Button onClick={saveGlobal} disabled={savingGlobal}>
|
||||
<Save className="mr-1 h-4 w-4" />
|
||||
{savingGlobal ? 'Guardando...' : 'Guardar ajustes generales'}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Post, Patch, Param, Body, UseGuards, Req, HttpCode, HttpStatus, NotFoundException } from '@nestjs/common';
|
||||
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';
|
||||
@@ -76,6 +76,13 @@ export class ProfessionalsController {
|
||||
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()
|
||||
|
||||
@@ -162,12 +162,22 @@ export class ProfessionalsService {
|
||||
if (!prof) throw new NotFoundException('Profesional no encontrado');
|
||||
|
||||
await this.prisma.$transaction([
|
||||
this.prisma.professionals.update({ where: { id: professionalId }, data: { is_active: false } }),
|
||||
this.prisma.professionals.update({ where: { id: professionalId }, data: { is_active: false, updated_at: new Date() } }),
|
||||
this.prisma.users.update({ where: { id: prof.user_id }, data: { pro_state: 3 } }),
|
||||
]);
|
||||
return { message: 'Solicitud rechazada' };
|
||||
}
|
||||
|
||||
async resetRejected(userId: string) {
|
||||
const prof = await this.prisma.professionals.findUnique({ where: { user_id: userId } });
|
||||
if (!prof) throw new NotFoundException('No se encontró solicitud');
|
||||
await this.prisma.$transaction([
|
||||
this.prisma.professionals.delete({ where: { user_id: userId } }),
|
||||
this.prisma.users.update({ where: { id: userId }, data: { pro_state: 0 } }),
|
||||
]);
|
||||
return { message: 'Solicitud eliminada' };
|
||||
}
|
||||
|
||||
async deactivate(id: string) {
|
||||
const prof = await this.prisma.professionals.findUnique({ where: { id } });
|
||||
if (!prof) throw new NotFoundException('Profesional no encontrado');
|
||||
|
||||
Reference in New Issue
Block a user