diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/domain/dto/CourseDto.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/domain/dto/CourseDto.java index cc49fc5..cd36ed1 100644 --- a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/domain/dto/CourseDto.java +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/domain/dto/CourseDto.java @@ -20,19 +20,14 @@ public class CourseDto { @NotNull private String description; - @NotNull private String duration; - @NotNull private Integer seats; - @NotNull private String category; - @NotNull private String level; - @NotNull private String instructor; private String price; diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/ProfessorServiceImpl.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/ProfessorServiceImpl.java index 566a61d..dcbe000 100644 --- a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/ProfessorServiceImpl.java +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/ProfessorServiceImpl.java @@ -8,7 +8,6 @@ import net.shyshkin.study.fullstack.supportportal.backend.domain.ProfessorCatego import net.shyshkin.study.fullstack.supportportal.backend.domain.ProfessorSkill; import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.ProfessorDto; import net.shyshkin.study.fullstack.supportportal.backend.domain.WorkingStatus; -import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.EmailExistsException; import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.NotAnImageFileException; import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.ProfessorNotFoundException; import net.shyshkin.study.fullstack.supportportal.backend.mapper.ProfessorMapper; @@ -18,12 +17,12 @@ import org.springframework.core.ParameterizedTypeReference; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.RequestEntity; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.security.crypto.password.PasswordEncoder; import javax.annotation.PostConstruct; import javax.transaction.Transactional; @@ -45,7 +44,6 @@ import static org.springframework.http.MediaType.*; public class ProfessorServiceImpl implements ProfessorService { public static final String EMAIL_NOT_FOUND_MSG = "Professor with email `%s` not found"; - public static final String EMAIL_EXISTS_MSG = "Professor with email `%s` is already registered"; public static final String PROFESSOR_NOT_FOUND_MSG = "Professor not found"; private final ProfessorRepository professorRepository; @@ -100,7 +98,7 @@ public class ProfessorServiceImpl implements ProfessorService { public Professor findByEmail(String email) { return professorRepository .findByEmail(email) - .orElseThrow(() -> new EmailExistsException(String.format(EMAIL_NOT_FOUND_MSG, email))); + .orElseThrow(() -> new ProfessorNotFoundException(String.format(EMAIL_NOT_FOUND_MSG, email))); } @Override @@ -158,19 +156,21 @@ public class ProfessorServiceImpl implements ProfessorService { @Override @Transactional public Professor addNewProfessor(ProfessorDto professorDto) { - validateNewEmail(professorDto.getEmail()); + // ✅ FIX: Removed validateNewEmail() - duplicate emails are now allowed Professor professor = professorMapper.toEntity(professorDto); // Set a unique identifier for the professor professor.setProfessorId(generateUuid()); - professor.setJoinDate(LocalDateTime.now()); + professor.setJoinDate( + professorDto.getJoinDate() != null ? professorDto.getJoinDate() : LocalDateTime.now() + ); professor.setProfileImageUrl(generateDefaultProfileImageUrl(professor.getProfessorId())); // Save the professor first to get the ID Professor savedProfessor = professorRepository.save(professor); - // Handle skills if provided + // ✅ Handle skills if provided if (professorDto.getSkills() != null && !professorDto.getSkills().isEmpty()) { Set skills = professorDto.getSkills().stream() .filter(skillDto -> skillDto.getName() != null && !skillDto.getName().trim().isEmpty()) @@ -183,7 +183,7 @@ public class ProfessorServiceImpl implements ProfessorService { savedProfessor.setSkills(skills); } - // Handle awards if provided + // ✅ Handle awards if provided if (professorDto.getAwards() != null && !professorDto.getAwards().isEmpty()) { Set awards = professorDto.getAwards().stream() .filter(awardDto -> awardDto.getTitle() != null && !awardDto.getTitle().trim().isEmpty()) @@ -213,9 +213,9 @@ public class ProfessorServiceImpl implements ProfessorService { @Transactional public Professor updateProfessor(UUID professorId, ProfessorDto professorDto) { Professor professor = professorRepository.findByProfessorId(professorId) - .orElseThrow(() -> new RuntimeException("Professor not found with id: " + professorId)); + .orElseThrow(() -> new ProfessorNotFoundException("Professor not found with id: " + professorId)); - validateUpdateEmail(professorId, professorDto.getEmail()); + // ✅ FIX: Removed validateUpdateEmail() - duplicate emails are now allowed // Update basic fields professor.setFirstName(professorDto.getFirstName()); @@ -226,7 +226,7 @@ public class ProfessorServiceImpl implements ProfessorService { professor.setOfficeLocation(professorDto.getOfficeLocation()); professor.setStatus(professorDto.getStatus()); professor.setCategory(professorDto.getCategory()); - + // Update extended fields professor.setPhone(professorDto.getPhone()); professor.setSpecialty(professorDto.getSpecialty()); @@ -237,6 +237,7 @@ public class ProfessorServiceImpl implements ProfessorService { professor.setDesignation(professorDto.getDesignation()); professor.setWorkDays(professorDto.getWorkDays()); + // ✅ Update joinDate if provided if (professorDto.getJoinDate() != null) { professor.setJoinDate(professorDto.getJoinDate()); } @@ -244,16 +245,12 @@ public class ProfessorServiceImpl implements ProfessorService { // Create a final reference for lambda expressions final Professor professorRef = professor; - // ✅ CORRECT FIX: Update skills with orphanRemoval=true - // Initialize collection if null + // ✅ Update skills with orphanRemoval=true if (professor.getSkills() == null) { professor.setSkills(new HashSet<>()); } - - // Clear existing skills (orphanRemoval will delete them from DB) professor.getSkills().clear(); - - // Add new skills + if (professorDto.getSkills() != null && !professorDto.getSkills().isEmpty()) { Set newSkills = professorDto.getSkills().stream() .filter(skillDto -> skillDto.getName() != null && !skillDto.getName().trim().isEmpty()) @@ -263,20 +260,15 @@ public class ProfessorServiceImpl implements ProfessorService { .professor(professorRef) .build()) .collect(Collectors.toSet()); - professor.getSkills().addAll(newSkills); } - // ✅ CORRECT FIX: Update awards with orphanRemoval=true - // Initialize collection if null + // ✅ Update awards with orphanRemoval=true if (professor.getAwards() == null) { professor.setAwards(new HashSet<>()); } - - // Clear existing awards (orphanRemoval will delete them from DB) professor.getAwards().clear(); - - // Add new awards + if (professorDto.getAwards() != null && !professorDto.getAwards().isEmpty()) { Set newAwards = professorDto.getAwards().stream() .filter(awardDto -> awardDto.getTitle() != null && !awardDto.getTitle().trim().isEmpty()) @@ -288,12 +280,11 @@ public class ProfessorServiceImpl implements ProfessorService { .professor(professorRef) .build()) .collect(Collectors.toSet()); - professor.getAwards().addAll(newAwards); } Professor savedProfessor = professorRepository.save(professor); - + // Handle profile image if provided if (professorDto.getProfileImage() != null) { saveProfileImage(savedProfessor, professorDto.getProfileImage()); @@ -340,22 +331,4 @@ public class ProfessorServiceImpl implements ProfessorService { var responseEntity = restTemplate.exchange(requestEntity, new ParameterizedTypeReference() {}); return responseEntity.getBody(); } - - private void validateNewEmail(String email) { - if (professorRepository.existsByEmail(email)) { - throw new EmailExistsException(String.format(EMAIL_EXISTS_MSG, email)); - } - } - - private Professor validateUpdateEmail(UUID professorId, String email) { - Objects.requireNonNull(professorId); - - Professor currentProfessor = findByProfessorId(professorId); - - if (!Objects.equals(currentProfessor.getEmail(), email) && professorRepository.existsByEmail(email)) { - throw new EmailExistsException(String.format(EMAIL_EXISTS_MSG, email)); - } - - return currentProfessor; - } } \ No newline at end of file diff --git a/support-portal-frontend/src/app/component/education/education.component.html b/support-portal-frontend/src/app/component/education/education.component.html index 35cc282..3f80e1f 100644 --- a/support-portal-frontend/src/app/component/education/education.component.html +++ b/support-portal-frontend/src/app/component/education/education.component.html @@ -120,10 +120,7 @@ -
- - Instructor is required. -
+ @@ -135,10 +132,7 @@ -
- - Duration is required. -
+
@@ -148,10 +142,7 @@ -
- - Number of seats is required. -
+
@@ -178,10 +169,7 @@ -
- - Category is required. -
+
@@ -197,10 +185,7 @@ -
- - Level is required. -
+
@@ -372,10 +357,7 @@ -
- - Schedule is required. -
+
diff --git a/support-portal-frontend/src/app/component/education/education.component.ts b/support-portal-frontend/src/app/component/education/education.component.ts index 9351ac1..1f5cf0e 100644 --- a/support-portal-frontend/src/app/component/education/education.component.ts +++ b/support-portal-frontend/src/app/component/education/education.component.ts @@ -44,11 +44,11 @@ export class EducationComponent implements OnInit { this.courseForm = this.fb.group({ title: ['', Validators.required], description: ['', Validators.required], - duration: ['', Validators.required], - seats: ['', [Validators.required, Validators.min(1)]], - category: ['', Validators.required], - level: ['', Validators.required], - instructor: ['', Validators.required], + duration: [''], + seats: [''], + category: [''], + level: [''], + instructor: [''], price: [''], startDate: [''], eligibility: [''], @@ -59,7 +59,7 @@ export class EducationComponent implements OnInit { this.upcomingEventForm = this.fb.group({ title: ['', Validators.required], description: ['', Validators.required], - schedule: ['', Validators.required], + schedule: [''], eventDate: [''], isActive: [true] }); @@ -381,5 +381,4 @@ export class EducationComponent implements OnInit { getApplicationCount(courseId?: number): number { if (!courseId) return 0; return this.applications.filter(app => app.course?.id === courseId).length; - }} - \ No newline at end of file + }} \ No newline at end of file diff --git a/support-portal-frontend/src/app/component/professor/professor.component.ts b/support-portal-frontend/src/app/component/professor/professor.component.ts index ea7e1f3..dd17385 100644 --- a/support-portal-frontend/src/app/component/professor/professor.component.ts +++ b/support-portal-frontend/src/app/component/professor/professor.component.ts @@ -25,6 +25,12 @@ interface Award { imageUrl?: string; } +interface Skill { + id?: number; + name: string; + level: number; +} + @Component({ selector: 'app-professor', templateUrl: './professor.component.html', @@ -63,7 +69,8 @@ export class ProfessorComponent implements OnInit, OnDestroy { description: '', designation: '', workDays: [], - awards: [] + awards: [], + skills: [] }; public profileImageFileName: string | null; @@ -74,10 +81,14 @@ export class ProfessorComponent implements OnInit, OnDestroy { public availableDays: string[] = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; public selectedWorkDays: { [key: string]: boolean } = {}; - // Awards management + // ✅ Awards management public newProfessorAwards: Award[] = []; public selectedProfessorAwards: Award[] = []; + // ✅ Skills management + public newProfessorSkills: Skill[] = []; + public selectedProfessorSkills: Skill[] = []; + private closeModal(modalId: string): void { const modalElement = document.getElementById(modalId); if (!modalElement) return; @@ -90,7 +101,6 @@ export class ProfessorComponent implements OnInit, OnDestroy { document.querySelectorAll('.modal-backdrop').forEach(b => b.remove()); } - constructor( private professorService: ProfessorService, private notificationService: NotificationService, @@ -101,7 +111,7 @@ export class ProfessorComponent implements OnInit, OnDestroy { ngOnInit(): void { this.getProfessors(true); this.loggedInUser = this.authenticationService.getUserFromLocalStorage(); - this.initializeAwards(); + this.initializeCollections(); this.setupModalEventListeners(); } @@ -109,16 +119,15 @@ export class ProfessorComponent implements OnInit, OnDestroy { this.subs.unsubscribe(); } - private initializeAwards(): void { + // ✅ FIX: Renamed and now initializes both awards and skills + private initializeCollections(): void { this.newProfessorAwards = []; this.selectedProfessorAwards = []; + this.newProfessorSkills = []; + this.selectedProfessorSkills = []; } - /** - * Setup event listeners for Bootstrap modals to clear data when they close - */ private setupModalEventListeners(): void { - // Listen for edit modal close event const editModal = document.getElementById('editProfessorModal'); if (editModal) { editModal.addEventListener('hidden.bs.modal', () => { @@ -126,7 +135,6 @@ export class ProfessorComponent implements OnInit, OnDestroy { }); } - // Listen for add modal close event const addModal = document.getElementById('addProfessorModal'); if (addModal) { addModal.addEventListener('hidden.bs.modal', () => { @@ -135,42 +143,33 @@ export class ProfessorComponent implements OnInit, OnDestroy { } } - /** - * Clear only profile image related data (used after successful operations) - */ private invalidateVariables(): void { this.profileImage = null; this.profileImageFileName = null; } - /** - * Clear all data related to adding a new professor - */ + // ✅ FIX: Now clears skills too private clearNewProfessorData(): void { this.profileImage = null; this.profileImageFileName = null; this.selectedWorkDays = {}; this.newProfessorAwards = []; + this.newProfessorSkills = []; } - /** - * Clear all data related to editing a professor - */ + // ✅ FIX: Now clears skills too private clearEditProfessorData(): void { this.profileImage = null; this.profileImageFileName = null; this.selectedWorkDays = {}; this.selectedProfessorAwards = []; + this.selectedProfessorSkills = []; } - // Award management methods + // ─── Award management methods ──────────────────────────────────────────────── + public addNewAward(): void { - this.newProfessorAwards.push({ - title: '', - year: '', - description: '', - imageUrl: '' - }); + this.newProfessorAwards.push({ title: '', year: '', description: '', imageUrl: '' }); } public removeAward(index: number): void { @@ -178,29 +177,43 @@ export class ProfessorComponent implements OnInit, OnDestroy { } public addEditAward(): void { - this.selectedProfessorAwards.push({ - title: '', - year: '', - description: '', - imageUrl: '' - }); + this.selectedProfessorAwards.push({ title: '', year: '', description: '', imageUrl: '' }); } public removeEditAward(index: number): void { this.selectedProfessorAwards.splice(index, 1); } - // Category display helper + // ─── Skill management methods ──────────────────────────────────────────────── + + // ✅ NEW: Add skill for new professor form + public addNewSkill(): void { + this.newProfessorSkills.push({ name: '', level: 0 }); + } + + // ✅ NEW: Remove skill from new professor form + public removeSkill(index: number): void { + this.newProfessorSkills.splice(index, 1); + } + + // ✅ NEW: Add skill for edit professor form + public addEditSkill(): void { + this.selectedProfessorSkills.push({ name: '', level: 0 }); + } + + // ✅ NEW: Remove skill from edit professor form + public removeEditSkill(index: number): void { + this.selectedProfessorSkills.splice(index, 1); + } + + // ─── Category display helper ───────────────────────────────────────────────── + public getCategoryDisplayName(category: string): string { switch (category) { - case 'FACULTY': - return 'Faculty'; - case 'SUPPORT_TEAM': - return 'Support Team'; - case 'TRAINEE_FELLOW': - return 'Trainee/Fellow'; - default: - return 'Unknown'; + case 'FACULTY': return 'Faculty'; + case 'SUPPORT_TEAM': return 'Support Team'; + case 'TRAINEE_FELLOW': return 'Trainee/Fellow'; + default: return 'Unknown'; } } @@ -231,14 +244,18 @@ export class ProfessorComponent implements OnInit, OnDestroy { } public onSelectProfessor(selectedProfessor: Professor): void { - // Create a deep copy to avoid reference issues this.selectedProfessor = JSON.parse(JSON.stringify(selectedProfessor)); - // Create a separate copy for awards to display + // ✅ FIX: Load awards this.selectedProfessorAwards = selectedProfessor.awards ? JSON.parse(JSON.stringify(selectedProfessor.awards)) : []; + // ✅ FIX: Load skills + this.selectedProfessorSkills = selectedProfessor.skills + ? JSON.parse(JSON.stringify(selectedProfessor.skills)) + : []; + // Set up work days for viewing this.selectedWorkDays = {}; if (selectedProfessor.workDays && Array.isArray(selectedProfessor.workDays)) { @@ -260,19 +277,13 @@ export class ProfessorComponent implements OnInit, OnDestroy { } public onCategoryChange(category: string): void { - // Clear profile image if Trainee/Fellow or Support Team is selected if (category === 'TRAINEE_FELLOW' || category === 'SUPPORT_TEAM') { this.profileImage = null; this.profileImageFileName = null; - // Reset file input const fileInput = document.getElementById('newProfessorProfileImage') as HTMLInputElement; - if (fileInput) { - fileInput.value = ''; - } + if (fileInput) fileInput.value = ''; const editFileInput = document.getElementById('editProfessorProfileImage') as HTMLInputElement; - if (editFileInput) { - editFileInput.value = ''; - } + if (editFileInput) editFileInput.value = ''; } } @@ -292,7 +303,6 @@ export class ProfessorComponent implements OnInit, OnDestroy { this.getProfessors(false); professorForm.reset(); this.notificationService.notify(NotificationType.SUCCESS, `Professor ${professor.firstName} added successfully`); - // Data will be cleared by modal close event listener }, (errorResponse: HttpErrorResponse) => { this.sendErrorNotification(errorResponse.error.message); @@ -333,10 +343,9 @@ export class ProfessorComponent implements OnInit, OnDestroy { } public onEditProfessor(professor: Professor): void { - // Create a deep copy to avoid reference issues this.selectedProfessor = JSON.parse(JSON.stringify(professor)); - // Set up work days for editing with validation + // Set up work days for editing this.selectedWorkDays = {}; if (professor.workDays && Array.isArray(professor.workDays)) { professor.workDays.forEach(day => { @@ -346,11 +355,16 @@ export class ProfessorComponent implements OnInit, OnDestroy { }); } - // Set up awards for editing - create a deep copy + // ✅ FIX: Load awards for editing this.selectedProfessorAwards = professor.awards ? JSON.parse(JSON.stringify(professor.awards)) : []; + // ✅ FIX: Load skills for editing + this.selectedProfessorSkills = professor.skills + ? JSON.parse(JSON.stringify(professor.skills)) + : []; + this.clickButton('openProfessorEdit'); } @@ -360,19 +374,14 @@ export class ProfessorComponent implements OnInit, OnDestroy { return; } - console.log('Updating professor:', this.selectedProfessor); - console.log('Selected work days:', this.selectedWorkDays); - console.log('Selected awards:', this.selectedProfessorAwards); - const formData = this.createExtendedProfessorFormData(this.selectedProfessor, this.profileImage); this.subs.add(this.professorService.updateProfessor(this.selectedProfessor.professorId, formData).subscribe( (professor: Professor) => { this.closeModal('editProfessorModal'); this.getProfessors(false); - this.invalidateVariables(); // Only clear profile image related data + this.invalidateVariables(); this.notificationService.notify(NotificationType.SUCCESS, `Professor ${professor.firstName} updated successfully`); - // Other data will be cleared by modal close event listener }, (errorResponse: HttpErrorResponse) => { this.sendErrorNotification(errorResponse.error.message); @@ -383,65 +392,70 @@ export class ProfessorComponent implements OnInit, OnDestroy { private createExtendedProfessorFormData(professor: any, profileImage: File | null): FormData { const formData = new FormData(); - // Basic fields - formData.append('firstName', professor.firstName || ''); - formData.append('lastName', professor.lastName || ''); - formData.append('email', professor.email || ''); - formData.append('department', professor.department || ''); - formData.append('position', professor.position || ''); + // ─── Basic fields ──────────────────────────────────────────────────────── + formData.append('firstName', professor.firstName || ''); + formData.append('lastName', professor.lastName || ''); + formData.append('email', professor.email || ''); + formData.append('department', professor.department || ''); + formData.append('position', professor.position || ''); formData.append('officeLocation', professor.officeLocation || ''); - formData.append('status', professor.status || 'ACTIVE'); - formData.append('category', professor.category || 'FACULTY'); + formData.append('status', professor.status || 'ACTIVE'); + formData.append('category', professor.category || 'FACULTY'); - // Extended fields - formData.append('phone', professor.phone || ''); - formData.append('specialty', professor.specialty || ''); - formData.append('experience', professor.experience || ''); - formData.append('designation', professor.designation || professor.position || ''); - formData.append('description', professor.description || ''); - formData.append('certification', professor.certification || ''); + // ✅ FIX: Send joinDate + if (professor.joinDate) { + formData.append('joinDate', new Date(professor.joinDate).toISOString()); + } + + // ─── Extended fields ───────────────────────────────────────────────────── + formData.append('phone', professor.phone || ''); + formData.append('specialty', professor.specialty || ''); + formData.append('experience', professor.experience || ''); + formData.append('designation', professor.designation || professor.position || ''); + formData.append('description', professor.description || ''); + formData.append('certification', professor.certification || ''); // Only include training if NOT retired - if (professor.status !== WorkingStatus.RETIRED) { - formData.append('training', professor.training || ''); - } else { - formData.append('training', ''); // Clear training for retired faculty - } + formData.append('training', + professor.status !== WorkingStatus.RETIRED ? (professor.training || '') : '' + ); - // Work days - collect from selectedWorkDays object + // ─── Work days ─────────────────────────────────────────────────────────── const workDays = Object.keys(this.selectedWorkDays).filter(day => this.selectedWorkDays[day]); - if (workDays.length > 0) { - workDays.forEach(day => { - formData.append('workDays', day); - }); - } + workDays.forEach(day => formData.append('workDays', day)); - // Awards - determine which awards array to use based on context + // ─── Awards ────────────────────────────────────────────────────────────── const awardsToSubmit = professor.professorId ? this.selectedProfessorAwards : this.newProfessorAwards; if (awardsToSubmit && awardsToSubmit.length > 0) { - // Filter out empty awards and only include those with at least title and year const validAwards = awardsToSubmit.filter(award => award.title && award.title.trim() && award.year && award.year.trim() ); - validAwards.forEach((award, index) => { - formData.append(`awards[${index}].title`, award.title.trim()); - formData.append(`awards[${index}].year`, award.year.trim()); + formData.append(`awards[${index}].title`, award.title.trim()); + formData.append(`awards[${index}].year`, award.year.trim()); formData.append(`awards[${index}].description`, award.description || ''); - formData.append(`awards[${index}].imageUrl`, award.imageUrl || ''); + formData.append(`awards[${index}].imageUrl`, award.imageUrl || ''); }); } - // Profile image - only add if not Trainee/Fellow or Support Team category + // ✅ FIX: Skills are now sent to backend + const skillsToSubmit = professor.professorId ? this.selectedProfessorSkills : this.newProfessorSkills; + if (skillsToSubmit && skillsToSubmit.length > 0) { + const validSkills = skillsToSubmit.filter(skill => skill.name && skill.name.trim()); + validSkills.forEach((skill, index) => { + formData.append(`skills[${index}].name`, skill.name.trim()); + formData.append(`skills[${index}].level`, String(skill.level ?? 0)); + }); + } + + // ─── Profile image ─────────────────────────────────────────────────────── if (profileImage && professor.category !== 'TRAINEE_FELLOW' && professor.category !== 'SUPPORT_TEAM') { formData.append('profileImage', profileImage); } - // Log formData for debugging + // Debug log console.log('FormData contents:'); - formData.forEach((value, key) => { - console.log(`${key}:`, value); - }); + formData.forEach((value, key) => console.log(`${key}:`, value)); return formData; } @@ -469,7 +483,7 @@ export class ProfessorComponent implements OnInit, OnDestroy { if (!this.profileImage) return; this.refreshing = true; const formData = new FormData(); - formData.append("profileImage", this.profileImage); + formData.append('profileImage', this.profileImage); let professor = this.professorService.getSelectedProfessor(); this.subs.sink = this.professorService.updateProfileImage(professor.professorId, formData).subscribe( (event: HttpEvent) => { @@ -538,9 +552,13 @@ export class ProfessorComponent implements OnInit, OnDestroy { description: '', designation: '', workDays: [], - awards: [] + awards: [], + skills: [] }; this.clearNewProfessorData(); this.clearEditProfessorData(); + // ✅ FIX: Also reset skill arrays explicitly + this.newProfessorSkills = []; + this.selectedProfessorSkills = []; } } \ No newline at end of file diff --git a/support-portal-frontend/tsconfig.json b/support-portal-frontend/tsconfig.json index 31daa1e..827122a 100644 --- a/support-portal-frontend/tsconfig.json +++ b/support-portal-frontend/tsconfig.json @@ -5,6 +5,7 @@ "noImplicitAny": false, "baseUrl": "./", "outDir": "./dist/out-tsc", + "rootDir": "./src", "forceConsistentCasingInFileNames": true, "strict": true, "strictPropertyInitialization": false, @@ -33,4 +34,4 @@ "strictInputAccessModifiers": true, "strictTemplates": true } -} +} \ No newline at end of file