| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- "use client"
- import { useState } from "react"
- import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
- import { Input } from "@/components/ui/input"
- import { Label } from "@/components/ui/label"
- import { Textarea } from "@/components/ui/textarea"
- import { Heart, AlertCircle } from "lucide-react"
- interface MedicalInfoSectionProps {
- formData: {
- phone?: string
- gender?: string
- address?: string
- emergencyContact?: string
- medicalHistory?: string
- allergies?: string
- currentMedications?: string
- }
- onInputChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => void
- userRole?: string
- }
- interface ValidationErrors {
- phone?: string
- emergencyContact?: string
- address?: string
- medicalHistory?: string
- allergies?: string
- currentMedications?: string
- }
- // Exportar función de validación para uso en el hook
- export const validateMedicalInfo = (formData: MedicalInfoSectionProps['formData']): { isValid: boolean; errors: ValidationErrors } => {
- const errors: ValidationErrors = {}
- // Validar teléfono
- if (formData.phone) {
- const phoneRegex = /^[\d\s()+-]+$/
- if (!phoneRegex.test(formData.phone)) {
- errors.phone = "Solo se permiten números, espacios, paréntesis, guiones y +"
- } else {
- const digits = formData.phone.replace(/\D/g, '')
- if (digits.length > 0 && digits.length < 10) {
- errors.phone = "El número debe tener al menos 9 dígitos"
- }
- }
- if (formData.phone.length > 20) {
- errors.phone = "Máximo 20 caracteres"
- }
- }
- // Validar contacto de emergencia
- if (formData.emergencyContact && formData.emergencyContact.length > 100) {
- errors.emergencyContact = "Máximo 100 caracteres"
- }
- // Validar dirección
- if (formData.address && formData.address.length > 200) {
- errors.address = "Máximo 200 caracteres"
- }
- // Validar campos de texto
- if (formData.medicalHistory && formData.medicalHistory.length > 1000) {
- errors.medicalHistory = "Máximo 1000 caracteres"
- }
- if (formData.allergies && formData.allergies.length > 500) {
- errors.allergies = "Máximo 500 caracteres"
- }
- if (formData.currentMedications && formData.currentMedications.length > 1000) {
- errors.currentMedications = "Máximo 1000 caracteres"
- }
- return {
- isValid: Object.keys(errors).length === 0,
- errors
- }
- }
- export default function MedicalInfoSection({
- formData,
- onInputChange,
- userRole
- }: MedicalInfoSectionProps) {
- const [errors, setErrors] = useState<ValidationErrors>({})
- // Solo mostrar para pacientes
- if (userRole !== "PATIENT") {
- return null
- }
- // Validación de teléfono: solo números, espacios, paréntesis, guiones y +
- const validatePhone = (value: string): string | undefined => {
- if (!value) return undefined
-
- const phoneRegex = /^[\d\s()+-]+$/
- if (!phoneRegex.test(value)) {
- return "Solo se permiten números, espacios, paréntesis, guiones y +"
- }
-
- // Verificar que tenga al menos 7 dígitos
- const digits = value.replace(/\D/g, '')
- if (digits.length > 0 && digits.length < 7) {
- return "El número debe tener al menos 7 dígitos"
- }
-
- if (value.length > 20) {
- return "Máximo 20 caracteres"
- }
-
- return undefined
- }
- // Validación de contacto de emergencia
- const validateEmergencyContact = (value: string): string | undefined => {
- if (!value) return undefined
-
- if (value.length > 100) {
- return "Máximo 100 caracteres"
- }
-
- return undefined
- }
- // Validación de dirección
- const validateAddress = (value: string): string | undefined => {
- if (!value) return undefined
-
- if (value.length > 200) {
- return "Máximo 200 caracteres"
- }
-
- return undefined
- }
- // Validación de campos de texto largos
- const validateTextArea = (value: string, maxLength: number): string | undefined => {
- if (!value) return undefined
-
- if (value.length > maxLength) {
- return `Máximo ${maxLength} caracteres`
- }
-
- return undefined
- }
- const handleValidatedChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
- const { name, value } = e.target
- let error: string | undefined
- // Validar según el campo
- switch (name) {
- case 'phone':
- error = validatePhone(value)
- break
- case 'emergencyContact':
- error = validateEmergencyContact(value)
- break
- case 'address':
- error = validateAddress(value)
- break
- case 'medicalHistory':
- error = validateTextArea(value, 1000)
- break
- case 'allergies':
- error = validateTextArea(value, 500)
- break
- case 'currentMedications':
- error = validateTextArea(value, 1000)
- break
- }
- // Actualizar errores
- setErrors(prev => ({
- ...prev,
- [name]: error
- }))
- // Solo llamar onChange si no hay error crítico (permitir seguir escribiendo)
- onInputChange(e)
- }
- return (
- <Card>
- <CardHeader>
- <CardTitle className="flex items-center">
- <Heart className="w-5 h-5 mr-2 text-primary" />
- Información Médica y Personal
- </CardTitle>
- <p className="text-sm text-muted-foreground">
- Esta información ayudará al chatbot a brindarte recomendaciones más precisas
- </p>
- </CardHeader>
- <CardContent className="space-y-4">
- {/* Información Personal */}
- <div className="space-y-4 pb-4 border-b">
- <h3 className="font-semibold text-sm text-foreground">Datos de Contacto</h3>
-
- <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
- <div className="space-y-2">
- <Label htmlFor="phone">Teléfono</Label>
- <Input
- id="phone"
- name="phone"
- type="tel"
- placeholder="096 028 3974"
- value={formData.phone || ""}
- onChange={handleValidatedChange}
- className={errors.phone ? "border-red-500" : ""}
- />
- {errors.phone && (
- <div className="flex items-start gap-1 text-xs text-red-600">
- <AlertCircle className="w-3 h-3 mt-0.5 flex-shrink-0" />
- <span>{errors.phone}</span>
- </div>
- )}
- </div>
- <div className="space-y-2">
- <Label htmlFor="gender">Género</Label>
- <select
- id="gender"
- name="gender"
- value={formData.gender || ""}
- onChange={handleValidatedChange}
- className="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent text-sm bg-background"
- >
- <option value="">Seleccionar...</option>
- <option value="MALE">Masculino</option>
- <option value="FEMALE">Femenino</option>
- <option value="OTHER">Otro</option>
- <option value="PREFER_NOT_TO_SAY">Prefiero no decirlo</option>
- </select>
- </div>
- </div>
- <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
- <div className="space-y-2">
- <Label htmlFor="emergencyContact">Contacto de Emergencia</Label>
- <Input
- id="emergencyContact"
- name="emergencyContact"
- type="text"
- placeholder="Nombre y teléfono"
- value={formData.emergencyContact || ""}
- onChange={handleValidatedChange}
- className={errors.emergencyContact ? "border-red-500" : ""}
- />
- {errors.emergencyContact && (
- <div className="flex items-start gap-1 text-xs text-red-600">
- <AlertCircle className="w-3 h-3 mt-0.5 flex-shrink-0" />
- <span>{errors.emergencyContact}</span>
- </div>
- )}
- <p className="text-xs text-muted-foreground">
- Ejemplo: María López - +57 300 123 4567
- </p>
- </div>
- <div className="space-y-2">
- <Label htmlFor="address">Dirección</Label>
- <Input
- id="address"
- name="address"
- type="text"
- placeholder="Calle, número, ciudad, código postal"
- value={formData.address || ""}
- onChange={handleValidatedChange}
- className={errors.address ? "border-red-500" : ""}
- />
- {errors.address && (
- <div className="flex items-start gap-1 text-xs text-red-600">
- <AlertCircle className="w-3 h-3 mt-0.5 flex-shrink-0" />
- <span>{errors.address}</span>
- </div>
- )}
- </div>
- </div>
- </div>
- {/* Información Médica */}
- <div className="space-y-4 pt-2">
- <h3 className="font-semibold text-sm text-foreground">Información Médica</h3>
-
- <div className="space-y-2">
- <Label htmlFor="medicalHistory">Historial Médico</Label>
- <Textarea
- id="medicalHistory"
- name="medicalHistory"
- placeholder="Enfermedades previas, cirugías, condiciones crónicas, etc."
- value={formData.medicalHistory || ""}
- onChange={handleValidatedChange}
- rows={3}
- className={`resize-none ${errors.medicalHistory ? "border-red-500" : ""}`}
- />
- <div className="flex justify-between items-start">
- <p className="text-xs text-muted-foreground">
- Ejemplo: Diabetes tipo 2, hipertensión, apendicectomía en 2020
- </p>
- <span className="text-xs text-muted-foreground">
- {formData.medicalHistory?.length || 0}/1000
- </span>
- </div>
- {errors.medicalHistory && (
- <div className="flex items-start gap-1 text-xs text-red-600">
- <AlertCircle className="w-3 h-3 mt-0.5 flex-shrink-0" />
- <span>{errors.medicalHistory}</span>
- </div>
- )}
- </div>
- <div className="space-y-2">
- <Label htmlFor="allergies">Alergias</Label>
- <Textarea
- id="allergies"
- name="allergies"
- placeholder="Alergias a medicamentos, alimentos, o sustancias"
- value={formData.allergies || ""}
- onChange={handleValidatedChange}
- rows={2}
- className={`resize-none ${errors.allergies ? "border-red-500" : ""}`}
- />
- <div className="flex justify-between items-start">
- <p className="text-xs text-muted-foreground">
- Ejemplo: Penicilina, mariscos, polen
- </p>
- <span className="text-xs text-muted-foreground">
- {formData.allergies?.length || 0}/500
- </span>
- </div>
- {errors.allergies && (
- <div className="flex items-start gap-1 text-xs text-red-600">
- <AlertCircle className="w-3 h-3 mt-0.5 flex-shrink-0" />
- <span>{errors.allergies}</span>
- </div>
- )}
- </div>
- <div className="space-y-2">
- <Label htmlFor="currentMedications">Medicamentos Actuales</Label>
- <Textarea
- id="currentMedications"
- name="currentMedications"
- placeholder="Medicamentos que estás tomando actualmente"
- value={formData.currentMedications || ""}
- onChange={handleValidatedChange}
- rows={3}
- className={`resize-none ${errors.currentMedications ? "border-red-500" : ""}`}
- />
- <div className="flex justify-between items-start">
- <p className="text-xs text-muted-foreground">
- Ejemplo: Metformina 850mg (2 veces al día), Losartán 50mg (1 vez al día)
- </p>
- <span className="text-xs text-muted-foreground">
- {formData.currentMedications?.length || 0}/1000
- </span>
- </div>
- {errors.currentMedications && (
- <div className="flex items-start gap-1 text-xs text-red-600">
- <AlertCircle className="w-3 h-3 mt-0.5 flex-shrink-0" />
- <span>{errors.currentMedications}</span>
- </div>
- )}
- </div>
- </div>
- </CardContent>
- </Card>
- )
- }
|