import { SearchDTO } from './../../../../models/masters/masters'; import { SubsidiaryDTO } from './../../../../models/account.model'; import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core'; import { ConfirmationService, MessageService } from 'primeng/api'; import { TableModule } from 'primeng/table'; import { Dialog } from 'primeng/dialog'; import { Ripple } from 'primeng/ripple'; import { ButtonModule, Button } from 'primeng/button'; import { ToastModule } from 'primeng/toast'; import { ToolbarModule } from 'primeng/toolbar'; import { ConfirmDialog } from 'primeng/confirmdialog'; import { InputTextModule } from 'primeng/inputtext'; import { TextareaModule } from 'primeng/textarea'; import { CommonModule } from '@angular/common'; import { FileUpload } from 'primeng/fileupload'; import { SelectModule } from 'primeng/select'; import { Tag } from 'primeng/tag'; import { RadioButton } from 'primeng/radiobutton'; import { Rating } from 'primeng/rating'; import { Skeleton } from 'primeng/skeleton'; import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { InputNumber } from 'primeng/inputnumber'; import { IconFieldModule } from 'primeng/iconfield'; import { InputIconModule } from 'primeng/inputicon'; import { Table } from 'primeng/table'; import { DropdownModule } from 'primeng/dropdown'; import { CompanyService } from '../../../../services/account/company/company.service'; import { FloatLabelModule } from "primeng/floatlabel"; import { ValidationService } from '../../../../services/utilities/validation.service'; import { TooltipModule } from 'primeng/tooltip'; import { AutoCompleteModule } from 'primeng/autocomplete'; import { MasterService } from '../../../../services/masters/master.service'; import { CityDTO } from '../../../../models/masters/masters'; import { debounceTime, Subject } from 'rxjs'; import { Request } from '../../../../models/request.model'; interface Column { field: string; header: string; customExportHeader?: string; } interface ExportColumn { title: string; dataKey: string; } @Component({ selector: 'app-subsidiary', templateUrl: './subsidiary.component.html', standalone: true, imports: [TableModule, Dialog, Ripple, SelectModule, ToastModule, ToolbarModule, ConfirmDialog, InputTextModule, TextareaModule, CommonModule, FileUpload, DropdownModule, Tag, RadioButton, Rating, InputTextModule, InputNumber, IconFieldModule, InputIconModule, Button, FloatLabelModule, FormsModule, ReactiveFormsModule, TooltipModule, Skeleton, AutoCompleteModule], providers: [MessageService, ConfirmationService], styleUrl: './subsidiary.component.css' }) export class SubsidiaryComponent implements OnInit{ subsidiaryForm: FormGroup; subsidiaryDialog: boolean = false; subsidiaries!: SubsidiaryDTO[]; subsidiary!: SubsidiaryDTO; selectedSubsidiaries!: SubsidiaryDTO[] | null; submitted: boolean = false; isLoading: boolean = true; skeletonData: any[] = Array(10).fill({}); suggestions: CityDTO[] = []; private searchSubject = new Subject(); statuses!: any[]; @ViewChild('dt') dt!: Table; cols!: Column[]; exportColumns!: ExportColumn[]; constructor( private subsidiaryService: CompanyService, private masterService: MasterService, private messageService: MessageService, private confirmationService: ConfirmationService, private cd: ChangeDetectorRef, private fb: FormBuilder, ) { this.subsidiaryForm = this.fb.group({ id: [{ value: '', disabled: true }], cityId: [{ value: null, disabled: true }], code: [{ value: '', disabled: false }, [Validators.required, ValidationService.codeValidator()]], name: [{ value: '', disabled: false }, [Validators.required, ValidationService.nameValidator()]], officeNo: [{ value: '', disabled: false }], street: [{ value: '', disabled: false }], locality: [{ value: '', disabled: false }], cityName: [{ value: '', disabled: false }], stateName: [{ value: '', disabled: true }], pinCode: [{ value: '', disabled: false }, [ValidationService.pincodeValidator()]], emailId: [{ value: '', disabled: false }, [ValidationService.emailValidator()]], contactNo: [{ value: '', disabled: false }, [ValidationService.mobileValidator()]], contactPerson: [{ value: '', disabled: false }, [ValidationService.contactPersonValidator()]], panNo: [{ value: '', disabled: false }, [ValidationService.panValidator()]], cinNo: [{ value: '', disabled: false }, [ValidationService.cinValidator()]], msmeNo: [{ value: '', disabled: false }, [ValidationService.msmeValidator()]], stateId: [{ value: '', disabled: true }], stateCode: [{ value: '', disabled: true }], gstCode: [{ value: '', disabled: true }] }); } exportCSV() { this.dt.exportCSV(); } searchCities(event: any) { const query = event.query; this.searchSubject.next(query); } onSelectCity(event: any) { const city = event.value as CityDTO; this.subsidiaryForm.patchValue({ cityId: city.id, stateId: city.stateId, cityName: city.cityName, stateName: city.stateName, stateCode: city.stateCode, gstCode: city.gstCode }); } ngOnInit() { this.loadAllSubsidiaries(); } loadAllSubsidiaries() { this.isLoading = true; this.subsidiaryService.getAllSubsidiaries().subscribe({ next: (data) => { this.isLoading = false; this.subsidiaries = data; this.cd.markForCheck(); }, error: (err) => { this.isLoading = false; console.error(err); } }); this.statuses = [ { label: 'Active', value: true }, { label: 'Inactive', value: false } ]; this.cols = [ { field: 'code', header: 'Code', customExportHeader: 'Code' }, { field: 'name', header: 'Name' }, { field: 'officeNo', header: 'Office No / Building / Floor' }, { field: 'street', header: 'Street / Road' }, { field: 'locality', header: 'Locality' }, { field: 'cityName', header: 'City' }, { field: 'emailId', header: 'Email' }, { field: 'contactNo', header: 'Contact No' }, { field: 'contactPerson', header: 'Contact Person' }, { field: 'panNo', header: 'PAN No.' }, { field: 'cinNo', header: 'CIN No.' }, { field: 'msmeNo', header: 'MSME No.' }, { field: 'updatedAt', header: 'Last Updated At' }, { field: 'updatedUser', header: 'Last Updated By' } ]; this.exportColumns = this.cols.map((col) => ({ title: col.header, dataKey: col.field })); this.searchSubject.pipe(debounceTime(300)).subscribe(query => { if (query && query.length >= 2) { const requestPayload: Request = { data: { searchBy: 'CITY', searchValue: query }, compressed: true, target: 'models.commons.Search' }; this.masterService.searchCityStates(requestPayload).subscribe({ next: (cities) => { this.suggestions = cities.map(city => ({ ...city, display: `${city.cityName}, ${city.stateName}` })); this.cd.markForCheck(); }, error: (err) => { console.error('Error searching cities', err); this.suggestions = []; } }); } else { this.suggestions = []; } }); } openNew() { this.subsidiary = {}; this.subsidiaryForm.reset(); this.submitted = false; this.subsidiaryDialog = true; } editSubsidiary(subsidiary: SubsidiaryDTO) { this.subsidiaryForm.reset(); this.subsidiary = { ...subsidiary }; this.subsidiaryForm.patchValue(subsidiary); this.subsidiaryDialog = true; console.log(this.subsidiaryForm.getRawValue()); } hideDialog() { this.subsidiaryDialog = false; this.submitted = false; } toggleActive(subsidiary: SubsidiaryDTO) { const isActivating = !subsidiary.active; this.confirmationService.confirm({ message: `Are you sure you want to ${isActivating ? 'activate' : 'deactivate'} ` + subsidiary.name + '?', header: 'Confirm', icon: 'pi pi-exclamation-triangle', rejectButtonProps: { label: 'No', severity: 'secondary', variant: 'text' }, acceptButtonProps: { severity: isActivating ? 'success' : 'danger', label: 'Yes' }, accept: () => { this.subsidiaryService.activateDeactivateSubsidiary(subsidiary.id?? '', isActivating).subscribe({ next: (updatedSubsidiary) => { subsidiary.active = updatedSubsidiary.active; this.subsidiaries = [...this.subsidiaries]; this.messageService.add({ severity: 'success', summary: 'Successful', detail: `Subsidiary ${isActivating ? 'Activated' : 'Deactivated'}`, life: 3000 }); }, error: (err) => { console.error('Error toggling subsidiary active status', err); this.messageService.add({ severity: 'error', summary: 'Error', detail: 'Failed to update subsidiary status', life: 3000 }); } }); } }); } getSeverity(status: boolean) { switch (status) { case true: return 'success'; case false: return 'warn'; } } getErrorMessage(fieldName: string): string { const control = this.subsidiaryForm.get(fieldName); return control ? ValidationService.getErrorMessage(control, fieldName) : ''; } isFieldInvalid(fieldName: string): boolean { const control = this.subsidiaryForm.get(fieldName); return !!(control && control.invalid && (control.dirty || control.touched || this.submitted)); } saveSubsidiary() { this.submitted = true; if (this.subsidiaryForm.valid) { const subsidiaryData = this.subsidiaryForm.getRawValue(); const existing = this.subsidiaries.find(sub => sub.id !== subsidiaryData.id && (sub.code === subsidiaryData.code || sub.name === subsidiaryData.name)); if (existing) { this.messageService.add({ severity: 'error', summary: 'Validation Error', detail: 'Subsidiary code or name already exists', life: 3000 }); return; } this.subsidiaryService.saveSubsidiary(subsidiaryData).subscribe({ next: (newSubsidiary) => { this.subsidiaries.push(newSubsidiary); this.subsidiaries = [...this.subsidiaries]; this.messageService.add({ severity: 'success', summary: 'Successful', detail: 'Subsidiary Created', life: 3000 }); this.subsidiaryDialog = false; this.subsidiary = {}; }, error: (err) => { console.error('Error saving subsidiary', err); this.messageService.add({ severity: 'error', summary: 'Error', detail: 'Failed to save subsidiary', life: 3000 }); } }); } } }