Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { TimeEntriesComponent } from './modules/time-entries/pages/time-entries.
import { ActivitiesManagementComponent } from './modules/activities-management/pages/activities-management.component';
import { HomeComponent } from './modules/home/home.component';
import { LoginComponent } from './modules/login/login.component';
import { CustomerComponent } from './modules/customer-managment/pages/customer.component';
import { CustomerComponent } from './modules/customer-management/pages/customer.component';

const routes: Routes = [
{
Expand Down
21 changes: 10 additions & 11 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,20 @@ import { ProjectEffects } from './modules/project-management/store/project.effec
import { TechnologyEffects } from './modules/shared/store/technology.effects';
import { reducers, metaReducers } from './reducers';
import { environment } from '../environments/environment';
import { CustomerComponent } from './modules/customer-managment/pages/customer.component';
import { CustomerComponent } from './modules/customer-management/pages/customer.component';
// tslint:disable-next-line: max-line-length
import { CustomerListComponent } from './modules/customer-managment/components/customer-info/components/customer-list/customer-list.component';
import { SearchComponent } from './modules/customer-managment/components/search/search.component';
import { CustomerListComponent } from './modules/customer-management/components/customer-info/components/customer-list/customer-list.component';
// tslint:disable-next-line: max-line-length
import { ManagementCustomerProjectsComponent } from './modules/customer-managment/components/management-customer-projects/management-customer-projects.component';
import { CreateCustomerComponent } from './modules/customer-managment/components/customer-info/components/create-customer/create-customer';
import { ManagementCustomerProjectsComponent } from './modules/customer-management/components/management-customer-projects/management-customer-projects.component';
import { CreateCustomerComponent } from './modules/customer-management/components/customer-info/components/create-customer/create-customer';
// tslint:disable-next-line: max-line-length
import { CreateProjectComponent } from './modules/customer-managment/components/projects/components/create-project/create-project.component';
import { ProjectListComponent } from './modules/customer-managment/components/projects/components/project-list/project-list.component';
import { CreateProjectComponent } from './modules/customer-management/components/projects/components/create-project/create-project.component';
import { ProjectListComponent } from './modules/customer-management/components/projects/components/project-list/project-list.component';
// tslint:disable-next-line: max-line-length
import { ProjectTypeListComponent } from './modules/customer-managment/components/projects-type/components/project-type-list/project-type-list.component';
import { ProjectTypeListComponent } from './modules/customer-management/components/projects-type/components/project-type-list/project-type-list.component';
// tslint:disable-next-line: max-line-length
import { CreateProjectTypeComponent } from './modules/customer-managment/components/projects-type/components/create-project-type/create-project-type.component';
import { CreateProjectTypeComponent } from './modules/customer-management/components/projects-type/components/create-project-type/create-project-type.component';
import { CustomerEffects } from './modules/customer-management/store/customer-management.effects';

@NgModule({
declarations: [
Expand Down Expand Up @@ -77,7 +77,6 @@ import { CreateProjectTypeComponent } from './modules/customer-managment/compone
SearchProjectComponent,
CustomerComponent,
CustomerListComponent,
SearchComponent,
ManagementCustomerProjectsComponent,
CreateCustomerComponent,
CreateProjectComponent,
Expand All @@ -102,7 +101,7 @@ import { CreateProjectTypeComponent } from './modules/customer-managment/compone
maxAge: 15, // Retains last 15 states
})
: [],
EffectsModule.forRoot([ProjectEffects, ActivityEffects, TechnologyEffects]),
EffectsModule.forRoot([ProjectEffects, ActivityEffects, CustomerEffects, TechnologyEffects]),
],
providers: [],
bootstrap: [AppComponent],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<div class="container">
<form style="width: 600px;" [formGroup]="customerForm" (ngSubmit)="onSubmit(customerForm.value)">
<div class="form-group">
<div
*ngIf="showAlert && messageToShow !== ''"
[ngClass]="{'bg-secondary': messageToShow == 'Customer created successfully!', 'bg-primary': messageToShow != 'Customer created successfully!'}"
class="alert alert-dismissible fade fade-in show text-white"
role="alert"
>
<strong>{{ messageToShow }}</strong>
</div>

<input
class="form-control form-control-sm"
id="name"
type="text"
formControlName="name"
placeholder="Customer name"
[class.is-invalid]="customerForm.invalid && customerForm.touched"
required
/>
<span
class="badge badge-pill badge-light text-danger"
*ngIf="(customerForm.dirty || customerForm.touched) && customerForm.invalid"
>Activity name is required</span
>
<textarea
class="form-control form-control-sm mt-2"
id="description"
rows="3"
formControlName="description"
placeholder="Customer description"
></textarea>
<button type="submit" class="btn btn-sm btn-primary" [disabled]="!customerForm.valid">Save</button>
<button (click)="resetCustomerForm()" id="cancel" type="button" class="btn btn-sm btn-secondary mb-2 ml-2 mt-2">
Cancel
</button>
</div>
</form>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FormBuilder } from '@angular/forms';
import { MockStore, provideMockStore } from '@ngrx/store/testing';

import { CreateCustomerComponent } from './create-customer';
import { CustomerState, CreateCustomer } from 'src/app/modules/customer-management/store';
import * as models from 'src/app/modules/shared/models/index';

describe('CreateCustomerComponent', () => {
let component: CreateCustomerComponent;
let fixture: ComponentFixture<CreateCustomerComponent>;
let store: MockStore<CustomerState>;

const state = {
data: [],
isLoading: false,
message: '',
};

const customerData: models.Customer = {
name: 'aa',
description: 'bb',
tenant_id: 'cc',
};

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [CreateCustomerComponent],
providers: [FormBuilder, provideMockStore({ initialState: state })],
}).compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(CreateCustomerComponent);
component = fixture.componentInstance;
fixture.detectChanges();

store = TestBed.inject(MockStore);
store.setState(state);
});

afterEach(() => {
fixture.destroy();
});

it('component should be created', () => {
expect(component).toBeTruthy();
});

it('should call resetCustomerForm', () => {
spyOn(component.customerForm, 'reset');

component.resetCustomerForm();

expect(component.customerForm.reset).toHaveBeenCalled();
});

it('onSubmit and dispatch CreateCustomer action', () => {
spyOn(store, 'dispatch');

component.onSubmit(customerData);

expect(store.dispatch).toHaveBeenCalledTimes(1);
expect(store.dispatch).toHaveBeenCalledWith(new CreateCustomer(customerData));
});

it('should call resetCustomerForm', () => {
spyOn(component.customerForm, 'reset');

component.resetCustomerForm();

expect(component.customerForm.reset).toHaveBeenCalled();
});

it('should be enable tabs and show message Customer created successfully! ', () => {
component.areTabsActive = false;
component.messageToShow = '';

spyOn(store, 'dispatch');

component.ngOnInit();

component.onSubmit(customerData);

component.setStatusOnScreen('Customer created successfully!');

expect(component.messageToShow).toEqual('Customer created successfully!');
expect(component.areTabsActive).toBeTrue();
});

it('should be disabled tabs and show message An error occurred, try again later. ', () => {
component.areTabsActive = false;
component.messageToShow = '';

spyOn(store, 'dispatch');

component.ngOnInit();

component.onSubmit(customerData);

component.setStatusOnScreen('An error occurred, try again later.');

expect(component.messageToShow).toEqual('An error occurred, try again later.');
expect(component.areTabsActive).toBeFalse();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Component, Input, Output, EventEmitter, OnDestroy, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sort imports by module.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

import { Store, select } from '@ngrx/store';

import { Subscription } from 'rxjs';
import { CustomerState, CreateCustomer } from 'src/app/modules/customer-management/store';
import { getStatusMessage } from 'src/app/modules/customer-management/store/customer-management.selectors';

@Component({
selector: 'app-create-customer',
templateUrl: './create-customer.html',
styleUrls: ['./create-customer.scss'],
})
export class CreateCustomerComponent implements OnInit, OnDestroy {
customerForm: FormGroup;
@Input() areTabsActive: boolean;
@Output() changeValueAreTabsActives = new EventEmitter<boolean>();
showAlert = false;
messageToShow = '';
saveSubscription: Subscription;

constructor(private formBuilder: FormBuilder, private store: Store<CustomerState>) {
this.customerForm = this.formBuilder.group({
name: ['', Validators.required],
description: [''],
});
}

ngOnInit() {
const messages$ = this.store.pipe(select(getStatusMessage));
this.saveSubscription = messages$.subscribe((valueMessage) => {
this.setStatusOnScreen(valueMessage);
});
}

ngOnDestroy() {
this.areTabsActive = false;
this.saveSubscription.unsubscribe();
}

onSubmit(customerData) {
this.store.dispatch(new CreateCustomer(customerData));
this.showAlert = true;
}

setStatusOnScreen(message: string) {
if (message === 'Customer created successfully!') {
this.messageToShow = message;
this.areTabsActive = true;
this.changeValueAreTabsActives.emit(this.areTabsActive);
} else if (message === 'An error occurred, try again later.') {
this.messageToShow = message;
this.areTabsActive = false;
this.changeValueAreTabsActives.emit(this.areTabsActive);
}
}

resetCustomerForm() {
this.showAlert = false;
this.customerForm.reset();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
</li>
<li class="nav-item">
<a
class="nav-link nav-active text-black font-weight-bold"
class="nav-link text-black font-weight-bold"
[ngClass]="{ 'nav-active': areTabsActive, disabled: !areTabsActive }"
id="projects-type-tab"
data-toggle="tab"
href="#projectsType"
Expand All @@ -24,7 +25,8 @@
</li>
<li class="nav-item">
<a
class="nav-link nav-active text-black font-weight-bold"
class="nav-link text-black font-weight-bold"
[ngClass]="{ 'nav-active': areTabsActive, disabled: !areTabsActive }"
id="projects-tab"
data-toggle="tab"
href="#projects"
Expand All @@ -41,7 +43,10 @@
role="tabpanel"
aria-labelledby="customer-information-tab"
>
<app-create-customer></app-create-customer>
<app-create-customer
[areTabsActive]="areTabsActive"
(changeValueAreTabsActives)="areTabsActive = $event"
></app-create-customer>
</div>
<div class="tab-pane fade mt-3" id="projects" role="tabpanel" aria-labelledby="projects-tab">
<app-create-project></app-create-project>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ import { Component } from '@angular/core';
styleUrls: ['./management-customer-projects.component.scss'],
})
export class ManagementCustomerProjectsComponent {
areTabsActive = false;

constructor() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
<hr />
<div class="row text-right">
<hr />
<app-search></app-search>
<div class="col-5 text-right">
<app-search-project></app-search-project>
</div>
</div>
<app-project-list></app-project-list>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ <h4>Customers</h4>
<div class="col">
<button (click)="activateCustomerForm()" type="button" class="btn btn-primary">Add new customer</button>
</div>
<app-search></app-search>
<div class="col-5 text-right">
<app-search-project></app-search-project>
</div>
</div>
<app-customer-list></app-customer-list>
<div class="row" *ngIf="showCustomerForm">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { TestBed, inject } from '@angular/core/testing';

import { CustomerService } from './customer.service';
import { Customer } from '../../shared/models/customer.model';

describe('CustomerService', () => {
let service: CustomerService;
let httpMock: HttpTestingController;

beforeEach(() => {
TestBed.configureTestingModule({ imports: [HttpClientTestingModule] });
service = TestBed.inject(CustomerService);
httpMock = TestBed.inject(HttpTestingController);
});

afterEach(() => {
httpMock.verify();
});

it('services are ready to be used', inject(
[HttpClientTestingModule, CustomerService],
(httpClient: HttpClientTestingModule, customerService: CustomerService) => {
expect(customerService).toBeTruthy();
expect(httpClient).toBeTruthy();
}
));

it('create customer using POST from baseUrl', () => {
const customer: Customer[] = [{ name: 'aa', description: 'bb', tenant_id: 'cc' }];

service.baseUrl = 'customers';

service.createCustomer(customer).subscribe((response) => {
expect(response.length).toBe(1);
});

const createCustomerRequest = httpMock.expectOne(service.baseUrl);
expect(createCustomerRequest.request.method).toBe('POST');
createCustomerRequest.flush(customer);
});
});
24 changes: 24 additions & 0 deletions src/app/modules/customer-management/services/customer.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

import { environment } from './../../../../environments/environment';
import { AzureAdB2CService } from 'src/app/modules/login/services/azure.ad.b2c.service';
import { Observable } from 'rxjs';

@Injectable({
providedIn: 'root',
})
export class CustomerService {
baseUrl = `${environment.timeTrackerApiUrl}/customers`;

constructor(private http: HttpClient, private service: AzureAdB2CService) {}

createCustomer(customerData): Observable<any> {
const body = {
...customerData,
tenant_id: this.service.getTenantId(),
};
console.log(this.service.getTenantId());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the console.log please.

return this.http.post(this.baseUrl, body);
}
}
Loading