diff --git a/src/app/components/contacts/contact-delete/contact-delete.component.ts b/src/app/components/contacts/contact-delete/contact-delete.component.ts index c3394c7..e36f391 100644 --- a/src/app/components/contacts/contact-delete/contact-delete.component.ts +++ b/src/app/components/contacts/contact-delete/contact-delete.component.ts @@ -20,6 +20,12 @@ export class ContactDeleteComponent { private router: Router ) {} + /** + * Deletes the current user and performs cleanup operations. + * This method deletes the user with the current user ID from the database, + * navigates to the contacts page, and closes any open dialog flags in the shared service. + * It also checks if the deleted contact was assigned to any tasks and removes them. + */ deleteContact() { this.firebaseService.deleteUser(this.currentUserId); this.router.navigate(['contacts']); @@ -28,6 +34,13 @@ export class ContactDeleteComponent { this.checkIfContactAnAssigned(); } + /** + * Checks if the deleted contact was assigned to any tasks and removes them. + * This method retrieves all tasks from the database, iterates over them, + * and checks if the current user ID is in the task's assigned array. + * If the ID is found, it is removed from the array and the modified task + * is updated in the database. + */ checkIfContactAnAssigned() { const tasks = this.firebaseService.getAllTasks(); @@ -43,6 +56,10 @@ export class ContactDeleteComponent { }); } + /** + * Closes the delete contact dialog by setting the appropriate flags in the shared service. + * If the edit contact dialog is not open, it also closes any open dialog flags in the shared service. + */ cancelDeleteContact() { this.sharedService.isDeleteContactDialogOpen = false; if (!this.sharedService.isEditContactDialogOpen) { diff --git a/src/app/components/contacts/contact-detail/contact-detail.component.ts b/src/app/components/contacts/contact-detail/contact-detail.component.ts index 8e9ce08..ff04c74 100644 --- a/src/app/components/contacts/contact-detail/contact-detail.component.ts +++ b/src/app/components/contacts/contact-detail/contact-detail.component.ts @@ -6,24 +6,17 @@ import { Output, } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { ContactsComponent } from '../contacts.component'; import { Router } from '@angular/router'; import { SharedService } from '../../../services/shared.service'; import { ContactNavComponent } from '../contact-nav/contact-nav.component'; import { FirebaseService } from '../../../services/firebase.service'; import { LanguageService } from '../../../services/language.service'; import { TranslateModule } from '@ngx-translate/core'; -import { ContactEditNewComponent } from '../contact-edit-new/contact-edit-new.component'; + @Component({ selector: 'app-contact-detail', standalone: true, - imports: [ - CommonModule, - ContactsComponent, - ContactEditNewComponent, - ContactNavComponent, - TranslateModule, - ], + imports: [CommonModule, TranslateModule, ContactNavComponent], templateUrl: './contact-detail.component.html', styleUrl: './contact-detail.component.scss', }) @@ -38,34 +31,61 @@ export class ContactDetailComponent { private languageService: LanguageService ) {} + /** + * Closes the contact details view by resetting the current user id in the + * shared service and emitting the closeContactEmitter event. + */ closeUserDetails() { this.sharedService.currentUserId = ''; this.closeContactEmitter.emit(); } + /** + * Checks if a user with the given user id exists in the database. + * @param userId the id of the user + * @returns an array of user objects that match the given user id + */ checkUserData(userId: string) { return this.firebaseService .getAllUsers() .filter((user) => user.id === userId); } + /** + * Toggles the mobile navigation bar on and off. + * @remarks This method is used by the contact details component to toggle + * the mobile navigation bar when the user clicks the three points icon. + */ toggleNav() { this.sharedService.isMobileNavbarOpen = !this.sharedService.isMobileNavbarOpen; } + /** + * Opens the edit contact dialog by setting the appropriate flags in the shared service. + * This method is used by the contact details component to open the edit contact dialog when the user clicks the edit button. + */ openEditDialog() { this.sharedService.isAnyDialogOpen = true; this.sharedService.isEditContactDialogOpen = true; this.sharedService.isMobileNavbarOpen = false; } + /** + * Opens the delete contact dialog by setting the appropriate flags in the shared service. + * This method is used by the contact details component to open the delete contact dialog when the user clicks the delete button. + */ deleteContact() { this.sharedService.isAnyDialogOpen = true; this.sharedService.isDeleteContactDialogOpen = true; this.sharedService.isMobileNavbarOpen = false; } + /** + * Converts a given timestamp to a human-readable date string. + * @param timestamp the timestamp to convert + * @returns a string in the format "DD. MMM. YYYY - HH:mm" in German or "MMM. DD, YYYY - hh:mm A" in English + */ convertTimestamp(timestamp: number) { const date = new Date(timestamp); const monthsEN = [ @@ -111,6 +131,11 @@ export class ContactDetailComponent { } } + /** + * Converts a given timestamp to a human-readable time string in English. + * @param timestamp the timestamp to convert + * @returns a string in the format "hh:mm:ss AM/PM" + */ convertTimestampHourEN(timestamp: number) { const date = new Date(timestamp * 1000); let hour = date.getHours(); @@ -124,6 +149,12 @@ export class ContactDetailComponent { return `${hourWithNull}:${minute}:${second} ${period}`; } + /** + * Converts a given timestamp to a human-readable time string in German. + * + * @param timestamp The timestamp to convert, in seconds. + * @returns A string in the format "HH:mm:ss Uhr". + */ convertTimestampHourDE(timestamp: number) { const date = new Date(timestamp * 1000); let hour = date.getHours(); @@ -134,6 +165,12 @@ export class ContactDetailComponent { } @HostListener('document:click', ['$event']) + /** + * Handles the opening of the contact edit, new contact, and delete contact + * dialogs when the respective buttons are clicked. + * + * @param event The MouseEvent that was triggered. + */ checkOpenContactEdit(event: MouseEvent) { const targetElement = event.target as HTMLElement; @@ -148,6 +185,14 @@ export class ContactDetailComponent { } } + /** + * Updates the dialog status flags in the shared service. + * + * @param anyOpen - Indicates if any dialog is open. + * @param editOpen - Indicates if the edit contact dialog is open. + * @param newOpen - Indicates if the new contact dialog is open. + * @param deleteOpen - Indicates if the delete contact dialog is open. + */ private setDialogStatus( anyOpen: boolean, editOpen: boolean, diff --git a/src/app/components/contacts/contact-edit-new/contact-edit-new.component.ts b/src/app/components/contacts/contact-edit-new/contact-edit-new.component.ts index 16c9039..5f17ae7 100644 --- a/src/app/components/contacts/contact-edit-new/contact-edit-new.component.ts +++ b/src/app/components/contacts/contact-edit-new/contact-edit-new.component.ts @@ -33,15 +33,32 @@ export class ContactEditNewComponent implements OnInit { public sharedService: SharedService ) {} + /** + * Lifecycle hook that is called after data-bound properties of a directive are + * initialized. + * This method sets a random color for the user to be edited. + */ + ngOnInit() { + this.randomColor = this.sharedService.generateRandomColor(); + } + + /** + * This method is an event emitter that listens for changes to the user's initials. + * It is called by the contact form component and updates the userInitials + * variable with the new initials. + * + * @param emitter The new initials of the user. + */ initialsEmitter(emitter: string) { this.userInitials = emitter; } + /** + * Updates the newColor variable with the newly selected color. + * + * @param newColor The new color to be set. + */ updateColor(newColor: string) { this.newColor = newColor; } - - ngOnInit() { - this.randomColor = this.sharedService.generateRandomColor(); - } } diff --git a/src/app/components/contacts/contact-edit-new/contact-form/contact-form.component.ts b/src/app/components/contacts/contact-edit-new/contact-form/contact-form.component.ts index 8ff26f4..3ae68a1 100644 --- a/src/app/components/contacts/contact-edit-new/contact-form/contact-form.component.ts +++ b/src/app/components/contacts/contact-edit-new/contact-form/contact-form.component.ts @@ -58,6 +58,12 @@ export class ContactFormComponent implements OnInit, OnChanges { lastLogin: 0, }; + /** + * OnInit lifecycle hook. + * + * If the currentUserId input property is empty, it sets the color and lastLogin + * properties of the userData object. + */ ngOnInit() { if (!this.currentUserId) { this.userData = { @@ -68,17 +74,33 @@ export class ContactFormComponent implements OnInit, OnChanges { } } + /** + * Lifecycle hook that is called whenever the input properties of the component change. + * + * If the currentUserId input property has changed, it calls the updateContactData method. + * @param changes an object containing the changed input properties + */ ngOnChanges(changes: SimpleChanges) { if (changes['currentUserId']) { this.updateContactData(); } } + /** + * Updates the form data by calling helper methods to update the initials and user data. + */ updateFormData() { this.updateInitials(); this.updateUserData(); } + /** + * Updates the initials property of the userData object by extracting the first letter + * of the firstName and lastName properties of the contactData object. If the + * currentUserId input property is empty, it sets the initials property of the + * userData object, otherwise it sets the initials property of the contactData object. + * Finally, it emits an event containing the initials. + */ updateInitials() { const initials = this.contactData ? this.contactData.firstName.slice(0, 1).toUpperCase() + @@ -98,6 +120,12 @@ export class ContactFormComponent implements OnInit, OnChanges { this.initialsEmitter.emit(initials); } + /** + * Updates the userData or contactData object by copying the properties from the + * other object and capitalizing the first letter of the firstName and lastName + * properties. If the currentUserId input property is empty, it updates the userData + * object. Otherwise, it updates the contactData object. + */ updateUserData() { if (!this.currentUserId) { this.userData = { @@ -116,10 +144,23 @@ export class ContactFormComponent implements OnInit, OnChanges { } } + /** + * Returns a string with the first letter capitalized and the rest of the letters + * in lower case. + * + * @param {string} name - The string to modify. + * @return {string} The modified string. + */ capitalizeFirstLetter(name: string) { return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase(); } + /** + * Validates if the given email address matches the standard email format. + * + * @param {string} emailValue - The email address to validate. + * @return {boolean} Returns true if the email address is in a valid format, otherwise false. + */ checkIfUserEmailIsValid(emailValue: string) { const emailRegex = /^[\w.%+-]+@[\w.-]+\.[a-zA-Z]{2,}$/; if (emailRegex.test(emailValue)) { @@ -129,12 +170,25 @@ export class ContactFormComponent implements OnInit, OnChanges { } } + /** + * Returns an array of users that have the same email address as the given argument. + * This is used to check for duplicate email addresses when adding or editing a contact. + * @param {string} mail - The email address to check. + * @return {User[]} An array of users with the same email address. + */ existEmailOnServer(mail: string) { return this.firebaseService .getAllUsers() .filter((user) => user.email === mail); } + /** + * Updates the contactData object with user details retrieved from the Firebase service. + * It fetches the first name, last name, email, phone number, and initials of the user + * associated with the currentUserId from the Firebase database and assigns these values to the + * respective properties of the contactData object. The values are expected to be arrays and are + * joined into strings using a comma separator. + */ private updateContactData() { this.contactData.firstName = this.firebaseService .getUserDetails(this.currentUserId, 'firstName') @@ -153,6 +207,14 @@ export class ContactFormComponent implements OnInit, OnChanges { .join(', '); } + /** + * Submits the form when the user clicks the submit button. If the form is valid, + * it either updates the existing contact if currentUserId is set, or adds a new + * contact if currentUserId is empty. When the operation is completed, it closes + * the contact dialog. + * + * @param ngForm - The angular form object. + */ onSubmit(ngForm: NgForm) { if (ngForm.submitted && ngForm.form.valid) { if (this.currentUserId) { @@ -178,11 +240,19 @@ export class ContactFormComponent implements OnInit, OnChanges { } } + /** + * Closes the delete contact dialog and the main dialog overlay. + */ deleteContact() { this.sharedService.isDeleteContactDialogOpen = false; this.sharedService.isAnyDialogOpen = false; } + /** + * Closes the contact dialog by updating the shared service flags + * to indicate that no dialog, edit contact dialog, or new contact dialog + * is currently open. + */ closeDialog() { this.sharedService.isAnyDialogOpen = false; this.sharedService.isEditContactDialogOpen = false; diff --git a/src/app/components/contacts/contacts.component.ts b/src/app/components/contacts/contacts.component.ts index a989b4b..9544ce2 100644 --- a/src/app/components/contacts/contacts.component.ts +++ b/src/app/components/contacts/contacts.component.ts @@ -10,7 +10,7 @@ import { TranslateModule } from '@ngx-translate/core'; @Component({ selector: 'app-contacts', standalone: true, - imports: [CommonModule, RouterLink, ContactDetailComponent, TranslateModule], + imports: [CommonModule, ContactDetailComponent, TranslateModule], templateUrl: './contacts.component.html', styleUrl: './contacts.component.scss', }) @@ -26,16 +26,22 @@ export class ContactsComponent { public sharedService: SharedService ) {} + /** + * Trigger onResize when the component is initialized + */ ngOnInit(): void { this.onResize(); } - showUserId(userId: string = '') { - this.sharedService.currentUserId = userId; - this.onResize(); - } - @HostListener('window:resize', ['$event']) + /** + * Toggles the showAllUsers variable based on the window width and the + * currentUserId of the shared service. + * + * If the window width is less than or equal to 1150px and the currentUserId + * is not empty, showAllUsers is set to false. Otherwise, showAllUsers is set + * to true. + */ onResize() { if (window.innerWidth <= 1150 && this.sharedService.currentUserId != '') { this.showAllUsers = false; @@ -44,22 +50,52 @@ export class ContactsComponent { } } + /** + * Updates the shared service current user id and triggers the resize function. + * If the parameter is not given, it resets the current user id to an empty string. + * @param userId - the id of the user to be set as the current user id + */ + showUserId(userId: string = '') { + this.sharedService.currentUserId = userId; + this.onResize(); + } + + /** + * Triggers the resize function to adjust UI elements based on the current state. + */ closeContactEmitter() { this.onResize(); } + /** + * Returns an array of users without the guest user. + * @returns An array of users + */ loadAllUserWithoutGuest(): User[] { return this.firebaseService .getAllUsers() .filter((user) => user.initials !== 'G'); } + /** + * Sorts the users array by the first letter of the user's initials, and filters it by the given sort letter. + * @param sortLetter - the letter to filter the users by + * @returns An array of users with the given sort letter + */ sortUsersByFirstLetter(sortLetter: string) { return this.loadAllUserWithoutGuest().filter( (user) => user.initials.substring(0, 1) === sortLetter ); } + /** + * Returns an array of unique first letters from the sorted array of users. + * The users array is first sorted by the first letter of the user's first name, + * and then the first letter of each user's first name is extracted and + * converted to uppercase. The resulting array is then deduplicated with a + * Set, and the unique letters are returned in an array. + * @returns An array of unique first letters + */ sortFirstLetter() { let filterteArray = this.loadAllUserWithoutGuest().sort((a, b) => a.firstName.localeCompare(b.firstName) @@ -70,6 +106,12 @@ export class ContactsComponent { return usersFirstLetter; } + /** + * Opens the new contact dialog. + * This function sets the isAnyDialogOpen and isNewContactDialogOpen + * properties of the shared service to true, which will open the new contact + * dialog. + */ openNewContactDialog() { this.sharedService.isAnyDialogOpen = true; this.sharedService.isNewContactDialogOpen = true;