diff --git a/admin/src/app/professionals/[id]/page.tsx b/admin/src/app/professionals/[id]/page.tsx index 57d9109..9204fba 100644 --- a/admin/src/app/professionals/[id]/page.tsx +++ b/admin/src/app/professionals/[id]/page.tsx @@ -10,7 +10,7 @@ import { Badge } from '@/components/ui/badge'; import { Input } from '@/components/ui/input'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { toast } from 'sonner'; -import { ArrowLeft, Pencil, X, Check } from 'lucide-react'; +import { ArrowLeft, Pencil, X, Check, ShieldCheck, ShieldAlert, ExternalLink } from 'lucide-react'; interface User { id: string; name: string; email?: string; phone?: string; city?: string; picture?: string; } interface Schedule { id: string; day_of_week: number; enabled: boolean; range1_hour1?: string; range1_hour2?: string; } @@ -19,6 +19,7 @@ interface PaymentMethod { id: string; nequi: boolean; datafono: boolean; transfe interface Professional { id: string; user_id: string; is_active: boolean; profession?: string; identification?: string; address?: string; + rethus_code?: string; rethus_validated?: boolean; rate?: number; average_score?: number; users?: User; schedules?: Schedule[]; specializations?: Specialization[]; payment_methods?: PaymentMethod[]; @@ -28,7 +29,7 @@ interface Service { id: string; description?: string; rate?: number; status: str const DAY_NAMES = ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado']; const PAYMENT_LABELS: Record = { nequi: 'Nequi', datafono: 'Datáfono', transferencia: 'Transferencia' }; -interface ProForm { profession: string; identification: string; address: string; rate: string; } +interface ProForm { profession: string; identification: string; address: string; rate: string; rethus_code: string; } interface UserForm { name: string; email: string; phone: string; city: string; } export default function ProfessionalDetailPage() { @@ -43,7 +44,8 @@ export default function ProfessionalDetailPage() { const [editingUser, setEditingUser] = useState(false); const [savingPro, setSavingPro] = useState(false); const [savingUser, setSavingUser] = useState(false); - const [proForm, setProForm] = useState({ profession: '', identification: '', address: '', rate: '' }); + const [validatingRethus, setValidatingRethus] = useState(false); + const [proForm, setProForm] = useState({ profession: '', identification: '', address: '', rate: '', rethus_code: '' }); const [userForm, setUserForm] = useState({ name: '', email: '', phone: '', city: '' }); const load = useCallback(() => { @@ -61,6 +63,7 @@ export default function ProfessionalDetailPage() { identification: prof.identification || '', address: prof.address || '', rate: prof.rate != null ? String(prof.rate) : '', + rethus_code: prof.rethus_code || '', }); setUserForm({ name: prof.users?.name || '', @@ -84,6 +87,7 @@ export default function ProfessionalDetailPage() { identification: proForm.identification || undefined, address: proForm.address || undefined, rate: proForm.rate ? Number(proForm.rate) : undefined, + rethus_code: proForm.rethus_code || undefined, }); toast.success('Perfil profesional actualizado'); setEditingPro(false); @@ -95,6 +99,19 @@ export default function ProfessionalDetailPage() { } }; + const validateRethus = async () => { + setValidatingRethus(true); + try { + await api.post(`/professionals/${id}/validate-rethus`); + toast.success('RETHUS marcado como validado'); + load(); + } catch (e: any) { + toast.error(e?.message || 'Error al validar RETHUS'); + } finally { + setValidatingRethus(false); + } + }; + const saveUser = async () => { if (!professional?.user_id) return; setSavingUser(true); @@ -247,6 +264,71 @@ export default function ProfessionalDetailPage() { Puntuación promedio

{professional.average_score != null ? `${professional.average_score.toFixed(1)} / 5` : '—'}

+
+ Código RETHUS + {editingPro + ? setProForm({ ...proForm, rethus_code: e.target.value })} placeholder="Ej: 123456789-1" /> + :

{professional.rethus_code || '—'}

} +
+ + + + {/* RETHUS */} + + + + {professional.rethus_validated + ? + : } + Validación RETHUS + + {professional.rethus_validated ? 'Validado' : 'Pendiente'} + + + + + {professional.rethus_code ? ( + <> +
+ Código registrado: + {professional.rethus_code} +
+
+ + {!professional.rethus_validated && ( + + )} +
+ {!professional.rethus_validated && ( +

+ Consulta el código en el portal de Minsalud, verifica que coincida con el profesional y luego marca como validado. +

+ )} + + ) : ( +

Este profesional no proporcionó código RETHUS (campo opcional).

+ )}
diff --git a/admin/src/app/professionals/page.tsx b/admin/src/app/professionals/page.tsx index 9e6c8f0..1de6a52 100644 --- a/admin/src/app/professionals/page.tsx +++ b/admin/src/app/professionals/page.tsx @@ -8,6 +8,8 @@ import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { toast } from 'sonner'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import Link from 'next/link'; +import { ShieldCheck, ShieldAlert } from 'lucide-react'; interface Professional { id: string; @@ -15,6 +17,8 @@ interface Professional { is_active: boolean; profession?: string; identification?: string; + rethus_code?: string; + rethus_validated?: boolean; rate?: number; users?: { id: string; name: string; email?: string; phone?: string }; } @@ -112,7 +116,7 @@ export default function ProfessionalsPage() { Nombre Email Teléfono - Solicitud + RETHUS Acción @@ -124,11 +128,29 @@ export default function ProfessionalsPage() { ) : ( pending.map((p) => ( - {p.users?.name} + + + {p.users?.name} + + {p.users?.email || '—'} {p.users?.phone || '—'} - {new Date().toLocaleDateString()} + + {p.rethus_code ? ( +
+ {p.rethus_validated + ? + : } + {p.rethus_code} +
+ ) : ( + No aplica + )} +
+ + + diff --git a/backend/prisma/migrations/0001_add_rethus_code/migration.sql b/backend/prisma/migrations/0001_add_rethus_code/migration.sql new file mode 100644 index 0000000..f1ca04c --- /dev/null +++ b/backend/prisma/migrations/0001_add_rethus_code/migration.sql @@ -0,0 +1,3 @@ +-- Add RETHUS fields to professionals +ALTER TABLE "professionals" ADD COLUMN IF NOT EXISTS "rethus_code" VARCHAR(50); +ALTER TABLE "professionals" ADD COLUMN IF NOT EXISTS "rethus_validated" BOOLEAN NOT NULL DEFAULT false; diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 63a19aa..a9262d8 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -82,6 +82,8 @@ model professionals { id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid user_id String @unique @db.Uuid identification String? @db.VarChar(50) + rethus_code String? @db.VarChar(50) + rethus_validated Boolean? @default(false) address String? additional_address String? profession String? @db.VarChar(255) diff --git a/backend/src/professionals/dto/professional.dto.ts b/backend/src/professionals/dto/professional.dto.ts index cd66e01..850c8ac 100644 --- a/backend/src/professionals/dto/professional.dto.ts +++ b/backend/src/professionals/dto/professional.dto.ts @@ -15,6 +15,10 @@ export class CreateProfessionalDto { @IsString() additional_address?: string; + @IsOptional() + @IsString() + rethus_code?: string; + @IsOptional() @IsString() identification_picture?: string; @@ -41,6 +45,14 @@ export class UpdateProfessionalDto { @IsString() profession?: string; + @IsOptional() + @IsString() + rethus_code?: string; + + @IsOptional() + @IsBoolean() + rethus_validated?: boolean; + @IsOptional() @IsNumber() rate?: number; diff --git a/backend/src/professionals/professionals.controller.ts b/backend/src/professionals/professionals.controller.ts index 9e23f35..43f8e07 100644 --- a/backend/src/professionals/professionals.controller.ts +++ b/backend/src/professionals/professionals.controller.ts @@ -43,6 +43,13 @@ export class ProfessionalsController { 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() diff --git a/backend/src/professionals/professionals.service.ts b/backend/src/professionals/professionals.service.ts index f608efb..4958fa1 100644 --- a/backend/src/professionals/professionals.service.ts +++ b/backend/src/professionals/professionals.service.ts @@ -149,6 +149,13 @@ export class ProfessionalsService { return { message: 'Solicitud rechazada' }; } + async validateRethus(id: string) { + const prof = await this.prisma.professionals.findUnique({ where: { id } }); + if (!prof) throw new NotFoundException('Profesional no encontrado'); + await this.prisma.professionals.update({ where: { id }, data: { rethus_validated: true } }); + return { message: 'RETHUS validado correctamente' }; + } + async requestProfessional(userId: string, data: any) { const existing = await this.prisma.professionals.findUnique({ where: { user_id: userId } }); if (existing) throw new BadRequestException('Ya tienes una solicitud de profesional'); @@ -161,6 +168,7 @@ export class ProfessionalsService { profession: data.profession, address: data.address, additional_address: data.additional_address, + rethus_code: data.rethus_code ?? null, identification_picture: data.identification_picture, certificate_picture: data.certificate_picture, },