+ fxFlex="100%">
@@ -224,11 +201,11 @@
+ (anulateCertificateEvent)="showCertificados()">
+ (certificateCreatedEvent)="onCertificateCreated()">
\ No newline at end of file
diff --git a/src/app/professionals/components/professional-form/professional-form.component.ts b/src/app/professionals/components/professional-form/professional-form.component.ts
index 601e0f7..ce70e05 100644
--- a/src/app/professionals/components/professional-form/professional-form.component.ts
+++ b/src/app/professionals/components/professional-form/professional-form.component.ts
@@ -12,72 +12,11 @@ import { InteractionService } from '@professionals/interaction.service';
import { PatientsService } from '@root/app/services/patients.service';
import { CertificatesService } from '@services/certificates.service';
import { PrescriptionsService } from '@services/prescriptions.service';
-import { SnomedSuppliesService } from '@services/snomedSupplies.service';
+
import { PatientFormComponent } from '@shared/components/patient-form/patient-form.component';
import { OrganizacionFormSessionService } from '@professionals/services/organizacion-form-session.service';
-import { of, Subject, Subscription, Observable } from 'rxjs';
-import { map, startWith, catchError, debounceTime, distinctUntilChanged, filter, switchMap, takeUntil } from 'rxjs/operators';
-
-// Validador personalizado para fechas
-function validDateValidator(): ValidatorFn {
- return (control: AbstractControl): { [key: string]: any } | null => {
- if (!control.value) {
- return null; // Si no hay valor, no validamos (required se encarga)
- }
-
- const date = new Date(control.value);
- const isValidDate = date instanceof Date && !isNaN(date.getTime());
-
- if (!isValidDate) {
- return { 'invalidDate': { value: control.value } };
- }
-
- // Validar que la fecha no sea futura para fecha de nacimiento
- const today = new Date();
- if (date > today) {
- return { 'futureDate': { value: control.value } };
- }
-
- // Validar que la fecha sea razonable (no muy antigua)
- const minDate = new Date('1900-01-01');
- if (date < minDate) {
- return { 'tooOldDate': { value: control.value } };
- }
-
- return null;
- };
-}
-
-function medicationSelectedValidator(): ValidatorFn {
- return (control: AbstractControl): { [key: string]: any } | null => {
- if (!control.value) {
- return null;
- }
-
- const supplyGroup = control.parent;
- if (!supplyGroup) {
- return null;
- }
-
- const snomedConcept = supplyGroup.get('snomedConcept');
- if (!snomedConcept || !snomedConcept.value || !snomedConcept.value.conceptId) {
- return { 'medicationNotSelected': { value: control.value } };
- }
-
- return null;
- };
-}
-
-function noWhitespaceValidator(): ValidatorFn {
- return (control: AbstractControl): { [key: string]: any } | null => {
- if (!control.value) {
- return null;
- }
-
- const isWhitespace = (control.value || '').trim().length === 0;
- return isWhitespace ? { 'whitespace': { value: control.value } } : null;
- };
-}
+import { Subject, Subscription, Observable, of } from 'rxjs';
+import { catchError, debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-professional-form',
@@ -88,12 +27,10 @@ function noWhitespaceValidator(): ValidatorFn {
stepLink
]
})
-
-export class ProfessionalFormComponent implements OnInit, OnDestroy, AfterViewInit {
+export class ProfessionalFormComponent implements OnInit, OnDestroy {
obraSocialControl = new FormControl('');
filteredObrasSociales: Observable
;
organizacionControl = new FormControl('');
-
// Suscripciones
private subscriptions: Subscription = new Subscription();
@@ -124,7 +61,7 @@ export class ProfessionalFormComponent implements OnInit, OnDestroy, AfterViewIn
private destroy$ = new Subject();
professionalForm: FormGroup;
- filteredSupplies = [];
+
request;
storedSupplies = [];
today = new Date();
@@ -135,7 +72,7 @@ export class ProfessionalFormComponent implements OnInit, OnDestroy, AfterViewIn
minDate = new Date();
maxDate = new Date(new Date().setMonth(new Date().getMonth() + 1));
isSubmit = false;
- supplySpinner: { show: boolean }[] = [{ show: false }, { show: false }];
+
myPrescriptions: Prescriptions[] = [];
isEditCertificate = false;
isEdit = false;
@@ -155,7 +92,7 @@ export class ProfessionalFormComponent implements OnInit, OnDestroy, AfterViewIn
constructor(
// private suppliesService: SuppliesService,
- private snomedSuppliesService: SnomedSuppliesService,
+
private fBuilder: FormBuilder,
private apiPatients: PatientsService,
private apiPrescriptions: PrescriptionsService, // privado
@@ -228,12 +165,6 @@ export class ProfessionalFormComponent implements OnInit, OnDestroy, AfterViewIn
}
}
- ngAfterViewInit() {
- // Implementation not needed for this case
- }
-
- // Método removido - funcionalidad manejada por patient-form component
-
initProfessionalForm() {
this.today = new Date((new Date()));
this.minDate = new Date();
@@ -275,12 +206,6 @@ export class ProfessionalFormComponent implements OnInit, OnDestroy, AfterViewIn
quantity.updateValueAndValidity();
}
- // Método removido - funcionalidad manejada por patient-form component
-
- // Método removido - funcionalidad manejada por patient-form component
-
- // Método removido - funcionalidad manejada por patient-form component
-
onSubmitProfessionalForm(professionalNgForm: FormGroupDirective): void {
if (this.professionalForm.valid) {
const newPrescription = { ...this.professionalForm.value };
@@ -377,33 +302,6 @@ export class ProfessionalFormComponent implements OnInit, OnDestroy, AfterViewIn
return this.professionalForm.get('supplies') as FormArray;
}
- // Getters removidos - funcionalidad manejada por patient-form component
-
- // Método removido - funcionalidad manejada por patient-form component
-
- displayFn(supply): string {
- return supply ? supply : '';
- }
-
- onSupplySelected(supply, index: number) {
- const control = this.suppliesForm.at(index); // Obtiene el FormGroup en la posición del array
- const supplyControl = control.get('supply');
-
- // Actualiza el valor del 'supply' con el 'term' en el 'name'
- supplyControl.get('name').setValue(supply.term); // Actualiza solo el 'term' en 'name'
-
- // También actualizamos el 'snomedConcept' completo con todos los campos
- supplyControl.setValue({
- name: supply.term, // Solo el 'term' va en 'name'
- snomedConcept: {
- term: supply.term,
- fsn: supply.fsn,
- conceptId: supply.conceptId,
- semanticTag: supply.semanticTag
- }
- });
- supplyControl.get('name').updateValueAndValidity();
- }
addSupply() {
const supplies = this.fBuilder.group({
@@ -419,6 +317,7 @@ export class ProfessionalFormComponent implements OnInit, OnDestroy, AfterViewIn
conceptId: [''],
semanticTag: ['']
}),
+ brand: [null]
}),
quantity: ['', [
Validators.required,
@@ -443,40 +342,10 @@ export class ProfessionalFormComponent implements OnInit, OnDestroy, AfterViewIn
triplicateDataGroup.get('numero')?.disable();
this.suppliesForm.push(supplies);
- this.supplySpinner.push({ show: false });
- this.subscribeToSupplyChanges(supplies, this.suppliesForm.length - 1);
- this.subscribeToTriplicateChanges(supplies, this.suppliesForm.length - 1);
- this.subscribeToDuplicateChanges(supplies, this.suppliesForm.length - 1);
- }
- subscribeToSupplyChanges(control: FormGroup, index: number) {
- control.get('supply.name').valueChanges.pipe(
- debounceTime(300),
- distinctUntilChanged()
- ).subscribe((supply: string) => {
- if (typeof supply === 'string') {
- const snomedConcept = control.get('supply.snomedConcept');
- const currentConceptId = snomedConcept?.get('conceptId')?.value;
-
- if (currentConceptId && supply !== snomedConcept?.get('term')?.value) {
- snomedConcept.reset();
- control.get('supply.name').updateValueAndValidity();
- }
- if (supply.length > 3) {
- this.supplySpinner[index] = { show: true };
- this.snomedSuppliesService.get(supply).pipe(
- catchError(() => {
- this.supplySpinner[index] = { show: false };
- return of([]);
- })
- ).subscribe((res) => {
- this.supplySpinner[index] = { show: false };
- this.filteredSupplies = [...res];
- });
- }
- }
- });
+ this.subscribeToTriplicateChanges(supplies, this.suppliesForm.length - 1);
+ this.subscribeToDuplicateChanges(supplies, this.suppliesForm.length - 1);
}
medicationSelectedValidator(): ValidatorFn {
@@ -509,7 +378,6 @@ export class ProfessionalFormComponent implements OnInit, OnDestroy, AfterViewIn
return isWhitespace ? { 'whitespace': { value: control.value } } : null;
};
}
-
subscribeToTriplicateChanges(control: FormGroup, index: number) {
const triplicateControl = control.get('triplicate');
const triplicateDataGroup = control.get('triplicateData') as FormGroup;
@@ -558,7 +426,7 @@ export class ProfessionalFormComponent implements OnInit, OnDestroy, AfterViewIn
deleteSupply(index: number) {
this.suppliesForm.removeAt(index);
- this.supplySpinner.splice(index, 1);
+
}
// set form with prescriptions values and disabled npt editable fields
diff --git a/src/app/professionals/components/supply-search/supply-search.component.html b/src/app/professionals/components/supply-search/supply-search.component.html
new file mode 100644
index 0000000..3223d22
--- /dev/null
+++ b/src/app/professionals/components/supply-search/supply-search.component.html
@@ -0,0 +1,22 @@
+
+
+ Medicamento
+
+
+
+
+ {{sup.display.name | titlecase}}
+
+ Marca sugerida: {{sup.display.brand | titlecase}}
+
+
+
+
+
+ Debe seleccionar un insumo
+
+
+ {{ parentForm.getError('invalid') }}
+
+
+
\ No newline at end of file
diff --git a/src/app/professionals/components/supply-search/supply-search.component.sass b/src/app/professionals/components/supply-search/supply-search.component.sass
new file mode 100644
index 0000000..7637017
--- /dev/null
+++ b/src/app/professionals/components/supply-search/supply-search.component.sass
@@ -0,0 +1,16 @@
+:host
+ display: block
+ width: 100%
+
+.w-100
+ width: 100%
+
+.item-title
+ font-size: 15px
+ font-weight: 400
+ line-height: normal
+
+.item-subtitle
+ font-size: 14.5px
+ color: #4056b5
+ line-height: normal
diff --git a/src/app/professionals/components/supply-search/supply-search.component.ts b/src/app/professionals/components/supply-search/supply-search.component.ts
new file mode 100644
index 0000000..1d0ae46
--- /dev/null
+++ b/src/app/professionals/components/supply-search/supply-search.component.ts
@@ -0,0 +1,126 @@
+import { Component, Input, OnInit, OnDestroy } from '@angular/core';
+import { FormGroup, FormControl } from '@angular/forms';
+import { SnomedSuppliesService } from '@services/snomedSupplies.service';
+import { Subscription, of } from 'rxjs';
+import { catchError, debounceTime, distinctUntilChanged, filter, switchMap } from 'rxjs/operators';
+import { ThemePalette } from '@angular/material/core';
+
+@Component({
+ selector: 'app-supply-search',
+ templateUrl: './supply-search.component.html',
+ styleUrls: ['./supply-search.component.sass']
+})
+export class SupplySearchComponent implements OnInit, OnDestroy {
+ @Input() parentForm: FormGroup;
+
+ filteredSupplies: any[] = [];
+ loading = false;
+ readonly spinnerColor: ThemePalette = 'primary';
+ private sub: Subscription;
+
+ constructor(private snomedSuppliesService: SnomedSuppliesService) { }
+
+ ngOnInit() {
+ if (this.parentForm && this.parentForm.get('name')) {
+ this.sub = this.parentForm.get('name').valueChanges.pipe(
+ debounceTime(300),
+ distinctUntilChanged(),
+ filter((term: string) => typeof term === 'string' && term.length > 3),
+ switchMap((term: string) => {
+ this.loading = true;
+ return this.snomedSuppliesService.get(term).pipe(
+ catchError(() => {
+ this.loading = false;
+ return of([]);
+ })
+ );
+ })
+ ).subscribe((res) => {
+ this.loading = false;
+ this.filteredSupplies = res.map(supply => {
+ const display = this.parseSupplyTerm(supply);
+ return { ...supply, display };
+ });
+ });
+ }
+ }
+
+
+ private parseSupplyTerm(supply: any): { name: string, brand?: string } {
+ if (supply.semanticTag === 'fármaco de uso clínico comercial') {
+ const match = supply.term.match(/^(.*?) \[(.*?)\] (.*)$/);
+
+ if (match) {
+ const brand = match[1];
+ const generic = match[2];
+ const form = match[3];
+ return {
+ name: `${generic} (${form})`,
+ brand: brand
+ };
+ }
+ }
+ return { name: supply.term };
+ }
+
+
+ ngOnDestroy() {
+ if (this.sub) {
+ this.sub.unsubscribe();
+ }
+ }
+
+ onSupplySelected(event: any) {
+ const supply = event.option.value;
+ const term = this.toTitleCase(supply.term);
+ // Update name with just the term
+ this.parentForm.get('name').setValue(term, { emitEvent: false });
+
+ let snomedConcept = {
+ term: supply.term,
+ fsn: supply.fsn,
+ conceptId: supply.conceptId,
+ semanticTag: supply.semanticTag
+ };
+
+ if (supply.relationships && supply.relationships.length > 0) {
+ const rel = supply.relationships[0];
+ snomedConcept = {
+ term: rel.term,
+ fsn: rel.fsn,
+ conceptId: rel.conceptId,
+ semanticTag: rel.semanticTag
+ };
+ if (!snomedConcept.semanticTag) {
+ const match = rel.fsn.match(/\(([^)]+)\)$/);
+ if (match) {
+ snomedConcept.semanticTag = match[1];
+ }
+ }
+ }
+
+ const formValue: any = {
+ name: term,
+ snomedConcept: snomedConcept,
+ brand: null
+ };
+
+ if (supply.semanticTag === 'fármaco de uso clínico comercial') {
+ const parsed = this.parseSupplyTerm(supply);
+ if (parsed.brand) {
+ formValue.brand = parsed.brand;
+ formValue.name = this.toTitleCase(parsed.name);
+ }
+ }
+
+ // Update the whole form group with known structure
+ this.parentForm.setValue(formValue, { emitEvent: false });
+ }
+
+ private toTitleCase(str: string): string {
+ if (!str) { return ''; }
+ return str.toLowerCase().split(' ').map(word => {
+ return (word.charAt(0).toUpperCase() + word.slice(1));
+ }).join(' ');
+ }
+}
diff --git a/src/app/professionals/professionals.module.ts b/src/app/professionals/professionals.module.ts
index 30cc03c..c02251f 100644
--- a/src/app/professionals/professionals.module.ts
+++ b/src/app/professionals/professionals.module.ts
@@ -37,6 +37,7 @@ import { PracticesFormComponent } from './components/practices-form/practices-fo
import { CertificatePracticePrinterComponent } from './components/certificate-practice-printer/certificate-practice-printer.component';
import { SelectorAmbitoComponent } from './components/selector-ambito/selector-ambito.component';
import { EditUserInfoComponent } from './components/edit-user-info/edit-user-info.component';
+import { SupplySearchComponent } from './components/supply-search/supply-search.component';
import { PatientFormComponent } from '@shared/components/patient-form/patient-form.component';
import { SharedModule } from '@shared/shared.module';
import { PatientNamePipe } from '@shared/pipes/patient-name.pipe';
@@ -56,6 +57,7 @@ import { OrganizacionDialogComponent } from './components/organizacion-dialog/or
OrganizacionesSelectorComponent,
OrganizacionDialogComponent,
PatientFormComponent,
+ SupplySearchComponent
],
imports: [
CommonModule,
diff --git a/src/app/shared/components/unified-printer/unified-printer.component.ts b/src/app/shared/components/unified-printer/unified-printer.component.ts
index 633d462..7a59e42 100644
--- a/src/app/shared/components/unified-printer/unified-printer.component.ts
+++ b/src/app/shared/components/unified-printer/unified-printer.component.ts
@@ -122,6 +122,11 @@ export class UnifiedPrinterComponent {
new Columns([new Txt(`${cant} `).bold().end]).end]).end);
pdf.add(new Txt('\n').end);
+ if (supply.supply.brand) {
+ pdf.add(new Txt('Marca sugerida: ' + supply.supply.brand).end);
+ pdf.add(new Txt('\n').end);
+ }
+
if (supply.diagnostic) {
pdf.add(new Txt('\n').end);
pdf.add(new Txt('Diagnóstico').bold().end);