refactor: SharedService refactored and dissolved, created ButtonStateService, renamed ContactEditDialog to ContactOverlay and moved to Overlays
This commit is contained in:
parent
8659c16517
commit
db242700e0
44 changed files with 209 additions and 726 deletions
|
|
@ -1 +1 @@
|
|||
<router-outlet></router-outlet> <app-overlay></app-overlay>
|
||||
<router-outlet></router-outlet>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { OverlayComponent } from './shared/components/overlay/overlay.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [RouterOutlet, OverlayComponent],
|
||||
imports: [RouterOutlet],
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.scss',
|
||||
})
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import { OverlayService } from '../../services/overlay.service';
|
|||
import { FormBtnComponent } from '../../shared/components/buttons/form-btn/form-btn.component';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { SharedService } from '../../services/shared.service';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { firstValueFrom, map } from 'rxjs';
|
||||
import { TaskService } from '../../services/task.service';
|
||||
|
|
@ -49,7 +48,6 @@ export class AddTaskComponent implements OnInit {
|
|||
constructor(
|
||||
public firebaseService: FirebaseService,
|
||||
private overlayService: OverlayService,
|
||||
private sharedService: SharedService,
|
||||
private taskService: TaskService,
|
||||
private apiService: ApiService,
|
||||
private authService: AuthService,
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
<section>
|
||||
<div class="dialog">
|
||||
<p>
|
||||
{{ "contactDeleteDialog.deleteContact" | translate }}
|
||||
</p>
|
||||
<div class="btns">
|
||||
<div class="btn">
|
||||
<div class="btn-cancel" (click)="cancelDeleteContact()">
|
||||
{{ "contactDeleteDialog.btnCancel" | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn">
|
||||
<div class="btn-delete-contact" (click)="deleteContact()">
|
||||
{{ "contactDeleteDialog.btnDelete" | translate }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
section {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: var(--white);
|
||||
width: 600px;
|
||||
height: 200px;
|
||||
border-radius: 26px;
|
||||
margin: 24px;
|
||||
border: 2px solid rgba($color: #000000, $alpha: 0.1);
|
||||
p {
|
||||
text-align: center;
|
||||
font-size: 27px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 12px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.btns {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 111px;
|
||||
height: 57px;
|
||||
margin: 0 24px;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
padding: 12px;
|
||||
margin: 12px 12px;
|
||||
border-radius: 10px;
|
||||
border-color: var(--black);
|
||||
background-color: var(--dark-blue);
|
||||
color: var(--white);
|
||||
font-size: 23px;
|
||||
font-weight: 400;
|
||||
transition: 125ms ease-in-out;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: var(--light-blue);
|
||||
box-shadow: 2px 2px 2px 0px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-delete-contact {
|
||||
padding: 12px;
|
||||
margin: 12px 12px;
|
||||
border-radius: 10px;
|
||||
color: var(--red);
|
||||
background-color: var(--white);
|
||||
border: 1px solid var(--red);
|
||||
font-size: 23px;
|
||||
font-weight: 400;
|
||||
transition: 125ms ease-in-out;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: var(--white);
|
||||
background-color: var(--red);
|
||||
border-color: var(--black);
|
||||
box-shadow: 2px 2px 2px 0px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 450px) {
|
||||
.dialog {
|
||||
p {
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 350px) {
|
||||
.dialog {
|
||||
width: 280px;
|
||||
p {
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.btn-delete-contact,
|
||||
.btn-cancel {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ContactDeleteComponent } from './contact-delete.component';
|
||||
|
||||
describe('ContactDeleteComponent', () => {
|
||||
let component: ContactDeleteComponent;
|
||||
let fixture: ComponentFixture<ContactDeleteComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ContactDeleteComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ContactDeleteComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
import { Component, Input } from '@angular/core';
|
||||
import { SharedService } from '../../../services/shared.service';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { FirebaseService } from '../../../services/firebase.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-contact-delete',
|
||||
standalone: true,
|
||||
imports: [TranslateModule],
|
||||
templateUrl: './contact-delete.component.html',
|
||||
styleUrl: './contact-delete.component.scss',
|
||||
})
|
||||
export class ContactDeleteComponent {
|
||||
@Input() currentUserId: string = '';
|
||||
|
||||
constructor(
|
||||
private sharedService: SharedService,
|
||||
private firebaseService: FirebaseService,
|
||||
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']);
|
||||
this.sharedService.isDeleteContactDialogOpen = false;
|
||||
this.sharedService.isAnyDialogOpen = false;
|
||||
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();
|
||||
|
||||
tasks.forEach((task) => {
|
||||
const index = task.assigned.indexOf(this.currentUserId);
|
||||
if (index !== -1) {
|
||||
task.assigned.splice(index, 1);
|
||||
const { id, ...taskWithoutIds } = task;
|
||||
if (task.id) {
|
||||
this.firebaseService.replaceTaskData(task.id, taskWithoutIds);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.sharedService.isAnyDialogOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -49,17 +49,17 @@
|
|||
@if(selectedUser.isContactOnly || currentUser && selectedUser.id ===
|
||||
currentUser.id) {
|
||||
<div class="btns">
|
||||
<div class="btn btn-edit">
|
||||
<div class="btn btn-edit" (click)="editContact(selectedUser)">
|
||||
<img src="./../../../../assets/img/contact/edit.svg" alt="" />
|
||||
<p (click)="openEditDialog()">
|
||||
<p>
|
||||
{{ "contacts.btnEdit0" | translate }}
|
||||
</p>
|
||||
</div>
|
||||
@if (selectedUser.isContactOnly || currentUser && selectedUser.id !==
|
||||
currentUser.id) {
|
||||
<div class="btn btn-delete">
|
||||
<div class="btn btn-delete" (click)="openDeleteContactDialog()">
|
||||
<img src="./../../../../assets/img/contact/delete.svg" alt="" />
|
||||
<p (click)="deleteContact()">
|
||||
<p>
|
||||
{{ "contacts.btnDelete0" | translate }}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -96,10 +96,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
} @if (sharedService.isMobileNavbarOpen) {
|
||||
<app-contact-nav
|
||||
[deleteContact]="deleteContact"
|
||||
[openEditDialog]="openEditDialog"
|
||||
></app-contact-nav>
|
||||
} @if (isMobileNavbarOpen) {
|
||||
<!-- TODO -->
|
||||
}
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -1,26 +1,22 @@
|
|||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Input,
|
||||
Output,
|
||||
SimpleChanges,
|
||||
} from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
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 { UserService } from '../../../services/user.service';
|
||||
import { finalize } from 'rxjs';
|
||||
import { User } from '../../../interfaces/user.interface';
|
||||
import { OverlayService } from '../../../services/overlay.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-contact-detail',
|
||||
standalone: true,
|
||||
imports: [CommonModule, TranslateModule, ContactNavComponent],
|
||||
imports: [CommonModule, TranslateModule],
|
||||
templateUrl: './contact-detail.component.html',
|
||||
styleUrl: './contact-detail.component.scss',
|
||||
})
|
||||
|
|
@ -31,10 +27,11 @@ export class ContactDetailComponent {
|
|||
|
||||
isLoading: boolean = false;
|
||||
selectedUser: User | null = null;
|
||||
isMobileNavbarOpen: boolean = false;
|
||||
|
||||
constructor(
|
||||
public sharedService: SharedService,
|
||||
public firebaseService: FirebaseService,
|
||||
private overlayService: OverlayService,
|
||||
private userService: UserService
|
||||
) {}
|
||||
|
||||
|
|
@ -84,68 +81,24 @@ export class ContactDetailComponent {
|
|||
* the mobile navigation bar when the user clicks the three points icon.
|
||||
*/
|
||||
toggleNav() {
|
||||
this.sharedService.isMobileNavbarOpen =
|
||||
!this.sharedService.isMobileNavbarOpen;
|
||||
this.isMobileNavbarOpen = !this.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;
|
||||
editContact(userData: User) {
|
||||
this.overlayService.setOverlayData('contactOverlay', userData);
|
||||
this.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;
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
if (targetElement.closest('.btn-edit')) {
|
||||
this.setDialogStatus(true, true, false, false);
|
||||
} else if (targetElement.closest('.btn-new')) {
|
||||
this.setDialogStatus(true, false, true, false);
|
||||
} else if (targetElement.closest('.btn-delete')) {
|
||||
this.setDialogStatus(true, false, false, true);
|
||||
} else if (!targetElement.closest('.dialog')) {
|
||||
this.setDialogStatus(false, false, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
newOpen: boolean,
|
||||
deleteOpen: boolean
|
||||
) {
|
||||
this.sharedService.isAnyDialogOpen = anyOpen;
|
||||
this.sharedService.isEditContactDialogOpen = editOpen;
|
||||
this.sharedService.isNewContactDialogOpen = newOpen;
|
||||
this.sharedService.isDeleteContactDialogOpen = deleteOpen;
|
||||
openDeleteContactDialog() {
|
||||
// TODO
|
||||
this.isMobileNavbarOpen = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
<nav class="navbar">
|
||||
<div class="link btn-edit">
|
||||
<span (click)="openEditDialog()">{{
|
||||
"contacts.btnEdit1" | translate
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="link btn-delete">
|
||||
<span (click)="deleteContact()">{{
|
||||
"contacts.btnDelete1" | translate
|
||||
}}</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
.navbar {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
bottom: 146px;
|
||||
right: 80px;
|
||||
width: 110px;
|
||||
height: 108px;
|
||||
background-color: var(--bgSidebar);
|
||||
border-radius: 20px 20px 0 20px;
|
||||
box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.1);
|
||||
padding: 10px;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 46px;
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
color: var(--white);
|
||||
cursor: pointer;
|
||||
span {
|
||||
text-align: center;
|
||||
width: 118px;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
white-space: normal;
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--very-dark-blue);
|
||||
span {
|
||||
color: var(--light-blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ContactNavComponent } from './contact-nav.component';
|
||||
|
||||
describe('ContactNavComponent', () => {
|
||||
let component: ContactNavComponent;
|
||||
let fixture: ComponentFixture<ContactNavComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ContactNavComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ContactNavComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
import { Component, Input } from '@angular/core';
|
||||
import { SharedService } from '../../../services/shared.service';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-contact-nav',
|
||||
standalone: true,
|
||||
imports: [TranslateModule],
|
||||
templateUrl: './contact-nav.component.html',
|
||||
styleUrl: './contact-nav.component.scss',
|
||||
})
|
||||
export class ContactNavComponent {
|
||||
@Input() deleteContact!: () => any;
|
||||
@Input() openEditDialog!: () => any;
|
||||
|
||||
constructor(public sharedService: SharedService) {}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
'd-none': !showAllUsers && selectedUserId != null
|
||||
}"
|
||||
>
|
||||
<button class="btn-new" type="submit" (click)="openNewContactDialog()">
|
||||
<button class="btn-new" type="submit" (click)="addNewContact()">
|
||||
<div class="btn-inside">
|
||||
<span>{{ "contacts.btnNew" | translate }}</span>
|
||||
<img src="./../../../assets/img/contact/add.svg" alt="check" />
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
import { Component, HostListener } from '@angular/core';
|
||||
import { User } from '../../interfaces/user.interface';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||
import { ContactDetailComponent } from './contact-detail/contact-detail.component';
|
||||
import { SharedService } from '../../services/shared.service';
|
||||
import { FirebaseService } from '../../services/firebase.service';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { finalize } from 'rxjs';
|
||||
import { OverlayService } from '../../services/overlay.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-contacts',
|
||||
|
|
@ -27,7 +25,7 @@ export class ContactsComponent {
|
|||
constructor(
|
||||
public firebaseService: FirebaseService,
|
||||
private userService: UserService,
|
||||
public sharedService: SharedService
|
||||
private overlayService: OverlayService
|
||||
) {}
|
||||
|
||||
/**
|
||||
|
|
@ -95,15 +93,8 @@ export class ContactsComponent {
|
|||
return uniqueFirstLetters;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
addNewContact() {
|
||||
this.overlayService.setOverlayData('contactOverlay', 'new');
|
||||
}
|
||||
|
||||
@HostListener('window:resize', ['$event'])
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@
|
|||
[disabled]="
|
||||
mail.invalid ||
|
||||
!pwResetData.mail ||
|
||||
sharedService.isBtnDisabled ||
|
||||
isButtonDisabled() ||
|
||||
!checkIfUserEmailIsValid(pwResetData.mail)
|
||||
"
|
||||
></app-form-btn>
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import { FooterComponent } from '../footer/footer.component';
|
|||
import { HeaderComponent } from '../header/header.component';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { LoginService } from '../../../services/login.service';
|
||||
import { SharedService } from '../../../services/shared.service';
|
||||
import { BtnBackComponent } from '../../../shared/components/buttons/btn-back/btn-back.component';
|
||||
import { ButtonStateService } from '../../../services/button-state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-forgot-pw',
|
||||
|
|
@ -31,16 +31,20 @@ export class ForgotPwComponent {
|
|||
|
||||
constructor(
|
||||
public loginService: LoginService,
|
||||
public sharedService: SharedService
|
||||
private buttonStateService: ButtonStateService
|
||||
) {}
|
||||
|
||||
onSubmit(ngForm: NgForm) {
|
||||
this.sharedService.isBtnDisabled = true;
|
||||
this.buttonStateService.enableButton();
|
||||
if (ngForm.submitted && ngForm.form.valid) {
|
||||
this.loginService.passwordReset(this.pwResetData.mail.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
isButtonDisabled() {
|
||||
return this.buttonStateService.isButtonDisabled;
|
||||
}
|
||||
|
||||
checkIfUserEmailIsValid(emailValue: string) {
|
||||
const emailRegex = /^[\w.%+-]+@[\w.-]+\.[a-zA-Z]{2,}$/;
|
||||
if (emailRegex.test(emailValue)) {
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@
|
|||
!pwResetData.password ||
|
||||
passwordConfirm.invalid ||
|
||||
!pwResetData.passwordConfirm ||
|
||||
sharedService.isBtnDisabled ||
|
||||
isButtonDisabled() ||
|
||||
pwResetData.password.length < 6 ||
|
||||
pwResetData.password !== pwResetData.passwordConfirm
|
||||
"
|
||||
|
|
|
|||
|
|
@ -5,11 +5,10 @@ import { FormBtnComponent } from '../../../../shared/components/buttons/form-btn
|
|||
import { FooterComponent } from '../../footer/footer.component';
|
||||
import { HeaderComponent } from '../../header/header.component';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { FirebaseService } from '../../../../services/firebase.service';
|
||||
import { LoginService } from '../../../../services/login.service';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { SharedService } from '../../../../services/shared.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ButtonStateService } from '../../../../services/button-state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pw-reset',
|
||||
|
|
@ -35,11 +34,9 @@ export class PwResetComponent {
|
|||
};
|
||||
|
||||
constructor(
|
||||
private firebaseService: FirebaseService,
|
||||
public loginService: LoginService,
|
||||
public sharedService: SharedService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router
|
||||
private buttonStateService: ButtonStateService,
|
||||
private route: ActivatedRoute
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
|
@ -50,8 +47,12 @@ export class PwResetComponent {
|
|||
);
|
||||
}
|
||||
|
||||
isButtonDisabled() {
|
||||
return this.buttonStateService.isButtonDisabled;
|
||||
}
|
||||
|
||||
onSubmit(ngForm: NgForm) {
|
||||
this.sharedService.isBtnDisabled = true;
|
||||
this.buttonStateService.enableButton();
|
||||
if (ngForm.submitted && ngForm.form.valid) {
|
||||
this.loginService.newPassword(this.pwResetData.password, this.oobCode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@if (sharedService.isBtnDisabled) {
|
||||
@if (isButtonDisabled()) {
|
||||
<div class="loading-dialog">
|
||||
<div id="loading" class="loader"></div>
|
||||
<div id="loadingText" class="loading-text">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { SharedService } from '../../../services/shared.service';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ButtonStateService } from '../../../services/button-state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-loading-dialog',
|
||||
|
|
@ -10,5 +10,9 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||
styleUrl: './loading-dialog.component.scss',
|
||||
})
|
||||
export class LoadingDialogComponent {
|
||||
constructor(public sharedService: SharedService) {}
|
||||
constructor(private buttonStateService: ButtonStateService) {}
|
||||
|
||||
isButtonDisabled() {
|
||||
return this.buttonStateService.isButtonDisabled;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,14 +33,14 @@
|
|||
<img class="custom-img" alt="" />
|
||||
}
|
||||
<div class="error-msg">
|
||||
@if (!mail.valid && mail.touched && !sharedService.isBtnDisabled) {
|
||||
@if (!mail.valid && mail.touched && !isButtonDisabled()) {
|
||||
<p>{{ "login.errorMail0" | translate }}</p>
|
||||
} @if (loginService.errorCode == 'auth/invalid-email' &&
|
||||
!sharedService.isBtnDisabled) {
|
||||
!isButtonDisabled()) {
|
||||
<p>{{ "login.errorMail1" | translate }}</p>
|
||||
} @if (mail.valid && mail.touched &&
|
||||
!checkIfUserEmailIsValid(loginData.email.toLowerCase()) &&
|
||||
!sharedService.isBtnDisabled) {
|
||||
!isButtonDisabled()) {
|
||||
<p>{{ "login.errorMail2" | translate }}</p>
|
||||
}
|
||||
</div>
|
||||
|
|
@ -73,11 +73,10 @@
|
|||
/>
|
||||
}
|
||||
<div class="error-msg">
|
||||
@if (!password.valid && password.touched &&
|
||||
!sharedService.isBtnDisabled) {
|
||||
@if (!password.valid && password.touched && !isButtonDisabled()) {
|
||||
<p>{{ "login.errorPassword0" | translate }}</p>
|
||||
} @if (loginService.errorCode == 'auth/invalid-credential' &&
|
||||
!sharedService.isBtnDisabled) {
|
||||
!isButtonDisabled()) {
|
||||
<p>{{ "login.errorPassword1" | translate }}</p>
|
||||
}
|
||||
</div>
|
||||
|
|
@ -120,7 +119,7 @@
|
|||
!loginData.email ||
|
||||
password.invalid ||
|
||||
!loginData.password ||
|
||||
sharedService.isBtnDisabled ||
|
||||
isButtonDisabled() ||
|
||||
!checkIfUserEmailIsValid(loginData.email)
|
||||
"
|
||||
></app-form-btn>
|
||||
|
|
@ -128,7 +127,7 @@
|
|||
[class]="'btn-guest-login'"
|
||||
[type]="'button'"
|
||||
[value]="'login.guest' | translate"
|
||||
[disabled]="sharedService.isBtnDisabled"
|
||||
[disabled]="isButtonDisabled()"
|
||||
(click)="guestLogin()"
|
||||
></app-form-btn>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@ import { CommonModule } from '@angular/common';
|
|||
import { Component } from '@angular/core';
|
||||
import { FormsModule, NgForm } from '@angular/forms';
|
||||
import { FormBtnComponent } from '../../shared/components/buttons/form-btn/form-btn.component';
|
||||
import { FirebaseService } from '../../services/firebase.service';
|
||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||
import { LoginService } from '../../services/login.service';
|
||||
import { SharedService } from '../../services/shared.service';
|
||||
import { FooterComponent } from './footer/footer.component';
|
||||
import { HeaderComponent } from './header/header.component';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
|
@ -13,6 +11,7 @@ import { LoadingDialogComponent } from './loading-dialog/loading-dialog.componen
|
|||
import { OverlayService } from '../../services/overlay.service';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { TokenService } from '../../services/token.service';
|
||||
import { ButtonStateService } from '../../services/button-state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
|
|
@ -43,8 +42,8 @@ export class LoginComponent {
|
|||
private authService: AuthService,
|
||||
public loginService: LoginService,
|
||||
private tokenService: TokenService,
|
||||
public sharedService: SharedService,
|
||||
private overlayService: OverlayService,
|
||||
private buttonStateService: ButtonStateService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router
|
||||
) {}
|
||||
|
|
@ -65,26 +64,30 @@ export class LoginComponent {
|
|||
});
|
||||
}
|
||||
|
||||
isButtonDisabled() {
|
||||
return this.buttonStateService.isButtonDisabled;
|
||||
}
|
||||
|
||||
deleteTokens() {
|
||||
this.tokenService.deleteAuthToken();
|
||||
this.tokenService.deleteUserId();
|
||||
}
|
||||
|
||||
async onSubmit(ngForm: NgForm) {
|
||||
this.sharedService.isBtnDisabled = true;
|
||||
this.buttonStateService.enableButton();
|
||||
if (ngForm.submitted && ngForm.form.valid) {
|
||||
try {
|
||||
await this.authService.login(this.loginData, this.checkboxRememberMe);
|
||||
this.router.navigate(['/summary']);
|
||||
this.sharedService.isBtnDisabled = false;
|
||||
this.buttonStateService.disableButton();
|
||||
} catch (error) {
|
||||
this.sharedService.isBtnDisabled = false;
|
||||
this.buttonStateService.disableButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guestLogin() {
|
||||
this.sharedService.isBtnDisabled = true;
|
||||
this.buttonStateService.enableButton();
|
||||
this.loginData.email = 'guest@guestaccount.com';
|
||||
this.loginData.password = 'guest@guestaccount.com';
|
||||
this.isPasswordIconVisible = !this.isPasswordIconVisible;
|
||||
|
|
|
|||
|
|
@ -39,11 +39,10 @@
|
|||
<img class="custom-img" alt="" />
|
||||
}
|
||||
<div class="error-msg">
|
||||
@if (name.hasError('pattern') && name.touched &&
|
||||
!sharedService.isBtnDisabled) {
|
||||
@if (name.hasError('pattern') && name.touched && !isButtonDisabled())
|
||||
{
|
||||
<p>{{ "register.errorPattern" | translate }}</p>
|
||||
} @else { @if (!name.valid && name.touched &&
|
||||
!sharedService.isBtnDisabled) {
|
||||
} @else { @if (!name.valid && name.touched && !isButtonDisabled()) {
|
||||
<p>{{ "register.errorName" | translate }}</p>
|
||||
}}
|
||||
</div>
|
||||
|
|
@ -70,15 +69,15 @@
|
|||
<div class="error-msg">
|
||||
@if(!mail.valid && mail.touched &&
|
||||
!checkIfUserEmailIsValid(registerData.mail.toLowerCase()) &&
|
||||
!sharedService.isBtnDisabled) {
|
||||
!isButtonDisabled()) {
|
||||
<p>{{ "register.errorMail0" | translate }}</p>
|
||||
} @else { @if (mail.touched &&
|
||||
!checkIfUserEmailIsValid(registerData.mail.toLowerCase()) &&
|
||||
!sharedService.isBtnDisabled) {
|
||||
!isButtonDisabled()) {
|
||||
<p>{{ "register.errorMail1" | translate }}</p>
|
||||
} @else { @if
|
||||
(existEmailOnServer(registerData.mail.toLowerCase()).length > 0 &&
|
||||
!sharedService.isBtnDisabled) {
|
||||
!isButtonDisabled()) {
|
||||
<p>{{ "register.errorMail2" | translate }}</p>
|
||||
} } }
|
||||
</div>
|
||||
|
|
@ -109,11 +108,10 @@
|
|||
/>
|
||||
}
|
||||
<div class="error-msg">
|
||||
@if (!password.valid && password.touched &&
|
||||
!sharedService.isBtnDisabled) {
|
||||
@if (!password.valid && password.touched && !isButtonDisabled()) {
|
||||
<p>{{ "register.errorPassword0" | translate }}</p>
|
||||
} @else { @if (registerData.password.length < 6 && password.touched &&
|
||||
!sharedService.isBtnDisabled) {
|
||||
!isButtonDisabled()) {
|
||||
<p>{{ "register.errorPassword1" | translate }}</p>
|
||||
} }
|
||||
</div>
|
||||
|
|
@ -145,11 +143,11 @@
|
|||
}
|
||||
<div class="error-msg">
|
||||
@if (!passwordConfirm.valid && passwordConfirm.touched &&
|
||||
!sharedService.isBtnDisabled) {
|
||||
!isButtonDisabled()) {
|
||||
<p>{{ "register.errorPassword0" | translate }}</p>
|
||||
} @if (registerData.password !== registerData.passwordConfirm &&
|
||||
registerData.password !== "" && registerData.passwordConfirm !== "" &&
|
||||
!sharedService.isBtnDisabled) {
|
||||
!isButtonDisabled()) {
|
||||
<p>{{ "register.errorPassword2" | translate }}</p>
|
||||
}
|
||||
</div>
|
||||
|
|
@ -188,7 +186,7 @@
|
|||
password.invalid ||
|
||||
!registerData.password ||
|
||||
!checkbox.valid ||
|
||||
sharedService.isBtnDisabled ||
|
||||
isButtonDisabled() ||
|
||||
registerData.password.length < 6 ||
|
||||
registerData.password !== registerData.passwordConfirm ||
|
||||
!checkIfUserEmailIsValid(registerData.mail)
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ import { CommonModule } from '@angular/common';
|
|||
import { FormBtnComponent } from '../../../shared/components/buttons/form-btn/form-btn.component';
|
||||
import { FirebaseService } from '../../../services/firebase.service';
|
||||
import { LoginService } from '../../../services/login.service';
|
||||
import { SharedService } from '../../../services/shared.service';
|
||||
import { BtnBackComponent } from '../../../shared/components/buttons/btn-back/btn-back.component';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { LoadingDialogComponent } from '../loading-dialog/loading-dialog.component';
|
||||
import { ButtonStateService } from '../../../services/button-state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-register',
|
||||
|
|
@ -43,18 +43,22 @@ export class RegisterComponent {
|
|||
constructor(
|
||||
private firebaseService: FirebaseService,
|
||||
public loginService: LoginService,
|
||||
public sharedService: SharedService,
|
||||
public translateService: TranslateService
|
||||
public translateService: TranslateService,
|
||||
private buttonStateService: ButtonStateService
|
||||
) {}
|
||||
|
||||
onSubmit(ngForm: NgForm) {
|
||||
this.sharedService.isBtnDisabled = true;
|
||||
this.buttonStateService.enableButton();
|
||||
if (ngForm.submitted && ngForm.form.valid) {
|
||||
this.splitName();
|
||||
this.loginService.register(this.registerData);
|
||||
}
|
||||
}
|
||||
|
||||
isButtonDisabled() {
|
||||
return this.buttonStateService.isButtonDisabled;
|
||||
}
|
||||
|
||||
splitName() {
|
||||
const names = this.registerData.name.split(' ');
|
||||
this.registerData.firstName = names[0];
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
<div
|
||||
[ngClass]="{
|
||||
'blur-background': sharedService.isAnyDialogOpen
|
||||
}"
|
||||
>
|
||||
<div>
|
||||
<div class="container">
|
||||
<app-sidebar></app-sidebar>
|
||||
<app-header></app-header>
|
||||
|
|
@ -12,23 +8,4 @@
|
|||
</div>
|
||||
<app-sidebar-mobile></app-sidebar-mobile>
|
||||
</div>
|
||||
@if (sharedService.isNewContactDialogOpen) {
|
||||
<app-contact-edit
|
||||
[ngClass]="{
|
||||
'blur-background': sharedService.isDeleteContactDialogOpen,
|
||||
'd-none': sharedService.isDeleteContactDialogOpen
|
||||
}"
|
||||
></app-contact-edit>
|
||||
} @if (sharedService.isEditContactDialogOpen) {
|
||||
<app-contact-edit
|
||||
[currentUserId]="sharedService.currentUserId"
|
||||
[ngClass]="{
|
||||
'blur-background': sharedService.isDeleteContactDialogOpen,
|
||||
'd-none': sharedService.isDeleteContactDialogOpen
|
||||
}"
|
||||
></app-contact-edit>
|
||||
} @if (sharedService.isDeleteContactDialogOpen) {
|
||||
<app-contact-delete
|
||||
[currentUserId]="sharedService.currentUserId"
|
||||
></app-contact-delete>
|
||||
}
|
||||
<app-overlay></app-overlay>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { NavigationEnd, Router, RouterOutlet } from '@angular/router';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { filter } from 'rxjs';
|
||||
import { FirebaseService } from '../../services/firebase.service';
|
||||
|
||||
import { LanguageService } from '../../services/language.service';
|
||||
import { SharedService } from '../../services/shared.service';
|
||||
import { SidebarMobileComponent } from '../../shared/components/sidebar/sidebar-mobile/sidebar-mobile.component';
|
||||
import { SidebarComponent } from '../../shared/components/sidebar/sidebar.component';
|
||||
import { ContactDeleteComponent } from '../contacts/contact-delete/contact-delete.component';
|
||||
import { ContactEditNewComponent } from '../contacts/contact-edit-new/contact-edit-new.component';
|
||||
import { HeaderComponent } from '../../shared/components/header/header.component';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { User } from '../../interfaces/user.interface';
|
||||
import { OverlayComponent } from '../../shared/components/overlay/overlay.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-main-layout',
|
||||
|
|
@ -19,83 +18,27 @@ import { HeaderComponent } from '../../shared/components/header/header.component
|
|||
HeaderComponent,
|
||||
SidebarComponent,
|
||||
SidebarMobileComponent,
|
||||
ContactEditNewComponent,
|
||||
ContactDeleteComponent,
|
||||
CommonModule,
|
||||
OverlayComponent,
|
||||
],
|
||||
templateUrl: './main-layout.component.html',
|
||||
styleUrl: './main-layout.component.scss',
|
||||
})
|
||||
export class MainLayoutComponent {
|
||||
isLoggedIn: string = '';
|
||||
currentUser: User | null = null;
|
||||
|
||||
constructor(
|
||||
public langService: LanguageService,
|
||||
public sharedService: SharedService,
|
||||
private firebaseService: FirebaseService,
|
||||
private router: Router
|
||||
) {
|
||||
this.checkAndClearLocalStorage();
|
||||
this.isLoggedIn = this.firebaseService.getCurrentUserId();
|
||||
private userService: UserService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadCurrentUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle hook that is called after the component has been initialized.
|
||||
*
|
||||
* Checks if the user is logged in by verifying the `isLoggedIn` property.
|
||||
*/
|
||||
ngOnInit() {
|
||||
if (this.isLoggedIn === null) {
|
||||
this.checkPwResetRoute();
|
||||
} else {
|
||||
this.router.navigate(['/summary']);
|
||||
this.sharedService.isBtnDisabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the local storage after a certain time period (12h) has
|
||||
* passed since the last login.
|
||||
*/
|
||||
checkAndClearLocalStorage() {
|
||||
const startTime = localStorage.getItem('sessionTimeJOIN');
|
||||
|
||||
if (startTime) {
|
||||
const startTimeMills = parseInt(startTime);
|
||||
const currentTime = new Date().getTime();
|
||||
const timeDifference = 12 * 60 * 60 * 1000; // 12h
|
||||
if (currentTime - startTimeMills > timeDifference) {
|
||||
localStorage.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to router events and checks if the current route is one of the
|
||||
* allowed routes for password reset, registration, forgotten password,
|
||||
* or login. If it is not, it navigates to the login page.
|
||||
*/
|
||||
checkPwResetRoute() {
|
||||
this.router.events
|
||||
.pipe(filter((event) => event instanceof NavigationEnd))
|
||||
.subscribe(() => {
|
||||
const urlTree = this.router.parseUrl(this.router.url);
|
||||
const firstSegment = this.getFirstSegment(urlTree);
|
||||
const allowedRoutes = ['pw-reset', 'register', 'forgot-pw', 'login'];
|
||||
|
||||
if (!allowedRoutes.includes(firstSegment)) {
|
||||
this.router.navigate(['/login']);
|
||||
}
|
||||
loadCurrentUser(): void {
|
||||
this.userService.getCurrentUser().subscribe((userData) => {
|
||||
this.currentUser = userData;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first segment of the current URL or an empty string if it does not exist.
|
||||
*
|
||||
* @param urlTree The current URL tree.
|
||||
* @returns The first segment of the current URL or an empty string if it does not exist.
|
||||
*/
|
||||
private getFirstSegment(urlTree: any): string {
|
||||
return urlTree.root.children['primary']?.segments[0]?.path || '';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
src/app/services/button-state.service.ts
Normal file
16
src/app/services/button-state.service.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ButtonStateService {
|
||||
isButtonDisabled: boolean = false;
|
||||
|
||||
disableButton() {
|
||||
this.isButtonDisabled = true;
|
||||
}
|
||||
|
||||
enableButton() {
|
||||
this.isButtonDisabled = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ColorService {
|
||||
generateRandomColor(): string {
|
||||
const letters = '0123456789ABCDEF';
|
||||
let color = '#';
|
||||
let isGrayScale = true;
|
||||
|
||||
while (isGrayScale) {
|
||||
for (let i = 0; i < 6; i++) {
|
||||
color += letters[Math.floor(Math.random() * 16)];
|
||||
}
|
||||
const rgb = this.hexToRgb(color);
|
||||
if (rgb.r !== rgb.g || rgb.g !== rgb.b) {
|
||||
isGrayScale = false;
|
||||
} else {
|
||||
color = '#';
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
hexToRgb(hex: string): { r: number; g: number; b: number } {
|
||||
const bigint = parseInt(hex.substring(1), 16);
|
||||
const r = (bigint >> 16) & 255;
|
||||
const g = (bigint >> 8) & 255;
|
||||
const b = bigint & 255;
|
||||
return { r, g, b };
|
||||
}
|
||||
}
|
||||
|
|
@ -19,13 +19,12 @@ import {
|
|||
updateDoc,
|
||||
where,
|
||||
} from '@angular/fire/firestore';
|
||||
import { SharedService } from './shared.service';
|
||||
import { User } from '../interfaces/user.interface';
|
||||
import { Router } from '@angular/router';
|
||||
import CryptoES from 'crypto-es';
|
||||
import { CryptoESSecretKey } from '../environments/config';
|
||||
import { catchError, map, Observable, of } from 'rxjs';
|
||||
import { ColorService } from './color.service';
|
||||
import { ButtonStateService } from './button-state.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
|
@ -40,8 +39,7 @@ export class LoginService {
|
|||
|
||||
constructor(
|
||||
private firebaseService: FirebaseService,
|
||||
private colorService: ColorService,
|
||||
public sharedService: SharedService,
|
||||
private buttonStateService: ButtonStateService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
|
|
@ -83,7 +81,7 @@ export class LoginService {
|
|||
})
|
||||
.catch((error) => {
|
||||
this.errorCode = error.code;
|
||||
this.sharedService.isBtnDisabled = false;
|
||||
this.buttonStateService.disableButton();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -126,13 +124,13 @@ export class LoginService {
|
|||
? registerData.firstName.slice(0, 1).toUpperCase() +
|
||||
registerData.lastName.slice(0, 1).toUpperCase()
|
||||
: '',
|
||||
color: this.colorService.generateRandomColor(),
|
||||
color: '',
|
||||
lastLogin: new Date().getTime(),
|
||||
};
|
||||
this.createUserInFirestore(userDataToSave);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.sharedService.isBtnDisabled = false;
|
||||
this.buttonStateService.disableButton();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -171,7 +169,7 @@ export class LoginService {
|
|||
isOnline: true,
|
||||
phone: '',
|
||||
initials: firstName.slice(0, 1) + lastName.slice(0, 1),
|
||||
color: this.colorService.generateRandomColor(),
|
||||
color: '',
|
||||
lastLogin: 0,
|
||||
});
|
||||
} else {
|
||||
|
|
@ -180,7 +178,7 @@ export class LoginService {
|
|||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.sharedService.isBtnDisabled = false;
|
||||
this.buttonStateService.disableButton();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -333,10 +331,10 @@ export class LoginService {
|
|||
sendPasswordResetEmail(auth, email, actionCodeSettings)
|
||||
.then(() => {
|
||||
this.router.navigate(['/login/notice/pw-send']);
|
||||
this.sharedService.isBtnDisabled = false;
|
||||
this.buttonStateService.disableButton();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.sharedService.isBtnDisabled = false;
|
||||
this.buttonStateService.disableButton();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -357,10 +355,10 @@ export class LoginService {
|
|||
confirmPasswordReset(auth, oobCode, newPW)
|
||||
.then(() => {
|
||||
this.router.navigate(['/login/notice/pw-change']);
|
||||
this.sharedService.isBtnDisabled = false;
|
||||
this.buttonStateService.disableButton();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.sharedService.isBtnDisabled = false;
|
||||
this.buttonStateService.disableButton();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class SharedService {
|
||||
isBtnDisabled: boolean = false;
|
||||
isAnyDialogOpen: boolean = false;
|
||||
isMobileNavbarOpen: boolean = false;
|
||||
isNewContactDialogOpen: boolean = false;
|
||||
isEditContactDialogOpen: boolean = false;
|
||||
isDeleteContactDialogOpen: boolean = false;
|
||||
currentUserId: string = '';
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
<div class="btn" (click)="closeClicked()">
|
||||
<div class="btn">
|
||||
<img src="./../../../../assets/img/close.svg" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Component, Input } from '@angular/core';
|
||||
import { SharedService } from '../../../../services/shared.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-btn-close',
|
||||
|
|
@ -9,19 +8,5 @@ import { SharedService } from '../../../../services/shared.service';
|
|||
styleUrl: './btn-close.component.scss',
|
||||
})
|
||||
export class BtnCloseComponent {
|
||||
@Input() isContactDialogOpen: boolean = false;
|
||||
|
||||
constructor(private sharedService: SharedService) {}
|
||||
|
||||
/**
|
||||
* Called when the close button is clicked.
|
||||
* Sets isAnyDialogOpen to false and sets
|
||||
* isEditContactDialogOpen and isNewContactDialogOpen
|
||||
* to the value of isContactDialogOpen.
|
||||
*/
|
||||
closeClicked() {
|
||||
this.sharedService.isAnyDialogOpen = false;
|
||||
this.sharedService.isEditContactDialogOpen = this.isContactDialogOpen;
|
||||
this.sharedService.isNewContactDialogOpen = this.isContactDialogOpen;
|
||||
}
|
||||
constructor() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,13 +121,12 @@
|
|||
}
|
||||
</div>
|
||||
<div class="btns">
|
||||
@if (currentUserId && currentUserId !==
|
||||
firebaseService.getCurrentUserId()){
|
||||
@if (selectedUser && selectedUser !== firebaseService.getCurrentUserId()){
|
||||
<app-form-btn
|
||||
class="btn-delete"
|
||||
type="button"
|
||||
value="{{ 'contactDialogForm.btnDelete' | translate }}"
|
||||
(click)="deleteContact()"
|
||||
(click)="closeDialog()"
|
||||
></app-form-btn>
|
||||
} @else {
|
||||
<app-form-btn
|
||||
|
|
@ -9,12 +9,10 @@ import {
|
|||
} from '@angular/core';
|
||||
import { FormsModule, NgForm } from '@angular/forms';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { SharedService } from '../../../../services/shared.service';
|
||||
import { FirebaseService } from '../../../../services/firebase.service';
|
||||
import { FormBtnComponent } from '../../../../shared/components/buttons/form-btn/form-btn.component';
|
||||
import { User } from '../../../../interfaces/user.interface';
|
||||
import { Router } from '@angular/router';
|
||||
import { ResizeService } from '../../../../services/resize.service';
|
||||
import { FormBtnComponent } from '../../../buttons/form-btn/form-btn.component';
|
||||
import { User } from '@sentry/angular';
|
||||
import { FirebaseService } from '../../../../../services/firebase.service';
|
||||
import { ResizeService } from '../../../../../services/resize.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-contact-form',
|
||||
|
|
@ -24,20 +22,17 @@ import { ResizeService } from '../../../../services/resize.service';
|
|||
styleUrl: './contact-form.component.scss',
|
||||
})
|
||||
export class ContactFormComponent implements OnInit, OnChanges {
|
||||
@Input() currentUserId: string = '';
|
||||
@Input() selectedUser: any = [];
|
||||
@Input() randomColor: string = '';
|
||||
@Input() newColor: string = '';
|
||||
@Input() currentColor: string = '';
|
||||
@Output() initialsEmitter = new EventEmitter<string>();
|
||||
@Output() closeDialogEmitter = new EventEmitter<string>();
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
public firebaseService: FirebaseService,
|
||||
public resizeService: ResizeService,
|
||||
public sharedService: SharedService
|
||||
) {
|
||||
this.updateContactData();
|
||||
}
|
||||
public resizeService: ResizeService
|
||||
) {}
|
||||
|
||||
contactData = {
|
||||
firstName: '',
|
||||
|
|
@ -68,7 +63,8 @@ export class ContactFormComponent implements OnInit, OnChanges {
|
|||
* properties of the userData object.
|
||||
*/
|
||||
ngOnInit() {
|
||||
if (!this.currentUserId) {
|
||||
this.updateContactData();
|
||||
if (!this.selectedUser) {
|
||||
this.userData = {
|
||||
...this.userData,
|
||||
color: this.randomColor,
|
||||
|
|
@ -84,7 +80,7 @@ export class ContactFormComponent implements OnInit, OnChanges {
|
|||
* @param changes an object containing the changed input properties
|
||||
*/
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes['currentUserId']) {
|
||||
if (changes['selectedUser']) {
|
||||
this.updateContactData();
|
||||
}
|
||||
}
|
||||
|
|
@ -109,7 +105,7 @@ export class ContactFormComponent implements OnInit, OnChanges {
|
|||
? this.contactData.firstName.slice(0, 1).toUpperCase() +
|
||||
this.contactData.lastName.slice(0, 1).toUpperCase()
|
||||
: '';
|
||||
if (!this.currentUserId) {
|
||||
if (!this.selectedUser) {
|
||||
this.userData = {
|
||||
...this.userData,
|
||||
initials: initials,
|
||||
|
|
@ -130,7 +126,7 @@ export class ContactFormComponent implements OnInit, OnChanges {
|
|||
* object. Otherwise, it updates the contactData object.
|
||||
*/
|
||||
updateUserData() {
|
||||
if (!this.currentUserId) {
|
||||
if (!this.selectedUser) {
|
||||
this.userData = {
|
||||
...this.userData,
|
||||
firstName: this.capitalizeFirstLetter(this.contactData.firstName),
|
||||
|
|
@ -193,21 +189,11 @@ export class ContactFormComponent implements OnInit, OnChanges {
|
|||
* joined into strings using a comma separator.
|
||||
*/
|
||||
private updateContactData() {
|
||||
this.contactData.firstName = this.firebaseService
|
||||
.getUserDetails(this.currentUserId, 'firstName')
|
||||
.join(', ');
|
||||
this.contactData.lastName = this.firebaseService
|
||||
.getUserDetails(this.currentUserId, 'lastName')
|
||||
.join(', ');
|
||||
this.contactData.email = this.firebaseService
|
||||
.getUserDetails(this.currentUserId, 'email')
|
||||
.join(', ');
|
||||
this.contactData.phone = this.firebaseService
|
||||
.getUserDetails(this.currentUserId, 'phone')
|
||||
.join(', ');
|
||||
this.contactData.initials = this.firebaseService
|
||||
.getUserDetails(this.currentUserId, 'initials')
|
||||
.join(', ');
|
||||
this.contactData.firstName = this.selectedUser.firstName;
|
||||
this.contactData.lastName = this.selectedUser.lastName;
|
||||
this.contactData.email = this.selectedUser.email;
|
||||
this.contactData.phone = this.selectedUser.phone;
|
||||
this.contactData.initials = this.selectedUser.initials;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -220,45 +206,15 @@ export class ContactFormComponent implements OnInit, OnChanges {
|
|||
*/
|
||||
onSubmit(ngForm: NgForm) {
|
||||
if (ngForm.submitted && ngForm.form.valid) {
|
||||
if (this.currentUserId) {
|
||||
console.log(this.newColor, this.currentColor);
|
||||
|
||||
this.newColor !== ''
|
||||
? (this.contactData.color = this.newColor)
|
||||
: (this.contactData.color = this.currentColor);
|
||||
this.firebaseService.updateUserData(
|
||||
this.currentUserId,
|
||||
this.contactData
|
||||
);
|
||||
} else {
|
||||
this.newColor !== ''
|
||||
? (this.userData.color = this.newColor)
|
||||
: (this.userData.color = this.randomColor);
|
||||
const { id, ...taskWithoutIds } = this.userData;
|
||||
this.firebaseService.addNewUser(taskWithoutIds).then((docRef) => {
|
||||
this.router.navigate([`/contacts/${docRef.id}`]);
|
||||
});
|
||||
}
|
||||
console.log('Save');
|
||||
this.closeDialog();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
this.sharedService.isNewContactDialogOpen = false;
|
||||
this.closeDialogEmitter.emit('');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +1,22 @@
|
|||
@if (currentUserId) { @for (user of
|
||||
firebaseService.getUserDataFromId(currentUserId); track user) {
|
||||
@if (overlayData !== "new") {
|
||||
<section>
|
||||
<div class="dialog">
|
||||
<div class="header">
|
||||
<img src="./../../../../assets/img/logo.svg" alt="" />
|
||||
<p>{{ "contacts.editContact" | translate }}</p>
|
||||
<app-btn-close [isContactDialogOpen]="false"></app-btn-close>
|
||||
<app-btn-close (click)="closeDialog()"></app-btn-close>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="badge">
|
||||
<div class="picker">
|
||||
<input
|
||||
[(colorPicker)]="currentColor"
|
||||
[style.background]="newColor === '' ? user.color : newColor"
|
||||
[(colorPicker)]="overlayData.color"
|
||||
[style.background]="newColor === '' ? overlayData.color : newColor"
|
||||
(colorPickerChange)="updateColor($event)"
|
||||
/>
|
||||
<div class="initials">
|
||||
@if (!userInitials) {
|
||||
{{ user.initials }} } @else {
|
||||
{{ overlayData.initials }} } @else {
|
||||
{{ userInitials }}
|
||||
}
|
||||
</div>
|
||||
|
|
@ -26,22 +25,23 @@ firebaseService.getUserDataFromId(currentUserId); track user) {
|
|||
<div class="form">
|
||||
<app-contact-form
|
||||
(initialsEmitter)="initialsEmitter($event)"
|
||||
[currentUserId]="currentUserId"
|
||||
(closeDialogEmitter)="closeDialog()"
|
||||
[selectedUser]="overlayData"
|
||||
[newColor]="newColor"
|
||||
[currentColor]="user.color"
|
||||
[currentColor]="overlayData.color"
|
||||
></app-contact-form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notice">{{ "contacts.notice" | translate }}</div>
|
||||
</div>
|
||||
</section>
|
||||
} } @else {
|
||||
} @else {
|
||||
<section>
|
||||
<div class="dialog">
|
||||
<div class="header">
|
||||
<img src="./../../../../assets/img/logo.svg" alt="" />
|
||||
<p>{{ "contacts.addContact" | translate }}</p>
|
||||
<app-btn-close [isContactDialogOpen]="false"></app-btn-close>
|
||||
<app-btn-close (click)="closeDialog()"></app-btn-close>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="badge">
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ContactEditNewComponent } from './contact-edit-new.component';
|
||||
import { ContactOverlayComponent } from './contact-overlay.component';
|
||||
|
||||
describe('ContactEditComponent', () => {
|
||||
let component: ContactEditNewComponent;
|
||||
let fixture: ComponentFixture<ContactEditNewComponent>;
|
||||
describe('ContactOverlayComponent', () => {
|
||||
let component: ContactOverlayComponent;
|
||||
let fixture: ComponentFixture<ContactOverlayComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ContactEditNewComponent],
|
||||
imports: [ContactOverlayComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ContactEditNewComponent);
|
||||
fixture = TestBed.createComponent(ContactOverlayComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
|
@ -1,15 +1,13 @@
|
|||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { BtnCloseComponent } from '../../../shared/components/buttons/btn-close/btn-close.component';
|
||||
import { FirebaseService } from '../../../services/firebase.service';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ContactFormComponent } from './contact-form/contact-form.component';
|
||||
import { SharedService } from '../../../services/shared.service';
|
||||
import { BtnCloseComponent } from '../../buttons/btn-close/btn-close.component';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ColorPickerModule } from 'ngx-color-picker';
|
||||
import { ColorService } from '../../../services/color.service';
|
||||
import { FirebaseService } from '../../../../services/firebase.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-contact-edit',
|
||||
selector: 'app-contact-overlay',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
|
@ -18,22 +16,19 @@ import { ColorService } from '../../../services/color.service';
|
|||
TranslateModule,
|
||||
ColorPickerModule,
|
||||
],
|
||||
templateUrl: './contact-edit-new.component.html',
|
||||
styleUrl: './contact-edit-new.component.scss',
|
||||
templateUrl: './contact-overlay.component.html',
|
||||
styleUrl: './contact-overlay.component.scss',
|
||||
})
|
||||
export class ContactEditNewComponent implements OnInit {
|
||||
@Input() currentUserId: string = '';
|
||||
@Input() currentColor: string = '';
|
||||
export class ContactOverlayComponent implements OnInit {
|
||||
@Input() overlayData: any = [];
|
||||
@Input() overlayType: string = '';
|
||||
@Output() closeDialogEmitter = new EventEmitter<string>();
|
||||
|
||||
randomColor: string = '';
|
||||
userInitials: string = '';
|
||||
newColor: string = '';
|
||||
|
||||
constructor(
|
||||
public firebaseService: FirebaseService,
|
||||
public sharedService: SharedService,
|
||||
private colorService: ColorService
|
||||
) {}
|
||||
constructor(public firebaseService: FirebaseService) {}
|
||||
|
||||
/**
|
||||
* Lifecycle hook that is called after data-bound properties of a directive are
|
||||
|
|
@ -41,7 +36,16 @@ export class ContactEditNewComponent implements OnInit {
|
|||
* This method sets a random color for the user to be edited.
|
||||
*/
|
||||
ngOnInit() {
|
||||
this.randomColor = this.colorService.generateRandomColor();
|
||||
this.randomColor = this.getRandomColor();
|
||||
}
|
||||
|
||||
getRandomColor(): string {
|
||||
const letters = '0123456789ABCDEF';
|
||||
let color = '#';
|
||||
for (let i = 0; i < 6; i++) {
|
||||
color += letters[Math.floor(Math.random() * 16)];
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -55,6 +59,10 @@ export class ContactEditNewComponent implements OnInit {
|
|||
this.userInitials = emitter;
|
||||
}
|
||||
|
||||
closeDialog() {
|
||||
this.closeDialogEmitter.emit('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the newColor variable with the newly selected color.
|
||||
*
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { SharedService } from '../../../../services/shared.service';
|
||||
import { BtnCloseComponent } from '../../buttons/btn-close/btn-close.component';
|
||||
import { Router } from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
|
@ -16,7 +15,7 @@ export class DialogOverlayComponent {
|
|||
@Input() overlayType: string = '';
|
||||
@Output() closeDialogEmitter = new EventEmitter<string>();
|
||||
|
||||
constructor(public sharedService: SharedService, private router: Router) {}
|
||||
constructor(private router: Router) {}
|
||||
|
||||
/**
|
||||
* Navigates to the login route and emits an empty string via the
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@
|
|||
[overlayType]="overlayType"
|
||||
(closeDialogEmitter)="onCloseOverlay($event)"
|
||||
></app-dialog-overlay>
|
||||
} @if (overlayType === "contactOverlay") {
|
||||
<app-contact-overlay
|
||||
[overlayData]="overlayData"
|
||||
[overlayType]="overlayType"
|
||||
(closeDialogEmitter)="onCloseOverlay($event)"
|
||||
></app-contact-overlay>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { TaskOverlayComponent } from './task-overlay/task-overlay.component';
|
|||
import { TaskEditOverlayComponent } from './task-edit-overlay/task-edit-overlay.component';
|
||||
import { Router } from '@angular/router';
|
||||
import { DialogOverlayComponent } from './dialog-overlay/dialog-overlay.component';
|
||||
import { ContactOverlayComponent } from './contact-overlay/contact-overlay.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-overlay',
|
||||
|
|
@ -14,6 +15,7 @@ import { DialogOverlayComponent } from './dialog-overlay/dialog-overlay.componen
|
|||
TaskOverlayComponent,
|
||||
TaskEditOverlayComponent,
|
||||
DialogOverlayComponent,
|
||||
ContactOverlayComponent,
|
||||
],
|
||||
templateUrl: './overlay.component.html',
|
||||
styleUrl: './overlay.component.scss',
|
||||
|
|
|
|||
Loading…
Reference in a new issue