Skip to content

Commit 2064301

Browse files
committed
fix: #150 Get active entry and update entry
1 parent 16cf3a3 commit 2064301

18 files changed

+734
-14
lines changed

src/app/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { ActivityListComponent } from './modules/activities-management/component
2929
import { CreateActivityComponent } from './modules/activities-management/components/create-activity/create-activity.component';
3030
import { FilterProjectPipe } from './modules/shared/pipes/filter-project/filter-project.pipe';
3131
import { SearchComponent } from './modules/shared/components/search/search.component';
32+
import { EntryFieldsComponent } from './modules/time-clock/components/entry-fields/entry-fields.component';
3233
import { HomeComponent } from './modules/home/home.component';
3334
import { LoginComponent } from './modules/login/login.component';
3435
import { ActivityEffects } from './modules/activities-management/store/activity-management.effects';
@@ -86,6 +87,7 @@ import { InjectTokenInterceptor } from './modules/shared/interceptors/inject.tok
8687
ProjectListComponent,
8788
ProjectTypeListComponent,
8889
CreateProjectTypeComponent,
90+
EntryFieldsComponent,
8991
],
9092
imports: [
9193
CommonModule,

src/app/modules/shared/models/entry.model.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,7 @@ export interface Entry {
1111

1212
export interface NewEntry {
1313
project_id: string;
14-
start_date: string;
14+
start_date?: string;
15+
description?: string;
16+
technologies?: string[];
1517
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()">
2+
<div class="input-group input-group-sm mb-3">
3+
<div class="input-group-prepend">
4+
<span class="input-group-text span-width" id="inputGroup-sizing-sm">Activity</span>
5+
</div>
6+
<select id="activitiesSelect" class="form-control">
7+
<option *ngFor="let activity of activities">{{ activity.name }}</option>
8+
</select>
9+
</div>
10+
<div class="input-group input-group-sm mb-3">
11+
<div class="input-group-prepend">
12+
<span class="input-group-text span-width" id="inputGroup-sizing-sm">Jira Ticket</span>
13+
</div>
14+
<input type="text" class="form-control" aria-label="Small" aria-describedby="inputGroup-sizing-sm" />
15+
</div>
16+
17+
<div class="input-group input-group-sm">
18+
<div class="input-group-prepend">
19+
<span class="input-group-text span-width" id="inputGroup-sizing-sm">Technology</span>
20+
</div>
21+
<input
22+
(keypress)="getTechnologies($event.target.value)"
23+
type="text"
24+
class="form-control"
25+
aria-label="Small"
26+
aria-describedby="inputGroup-sizing-sm"
27+
/>
28+
</div>
29+
30+
<div *ngIf="isLoading">LOADING...</div>
31+
<div #list *ngIf="technology && showlist" class="d-flex flex-column technology-content">
32+
<div
33+
*ngFor="let item of technology.items"
34+
(click)="setTechnology(item.name)"
35+
class="technology-list"
36+
[ngClass]="{ active: selectedTechnology && selectedTechnology.includes(item.name) }"
37+
>
38+
{{ item.name }}
39+
</div>
40+
</div>
41+
<div class="tags-content d-flex flex-wrap">
42+
<div *ngFor="let technology of selectedTechnology; let tagIndex = index" class="tag">
43+
<span class="mr-3">{{ technology }}</span>
44+
<i class="fas fa-times text-white" (click)="removeTag(tagIndex)"></i>
45+
</div>
46+
</div>
47+
48+
<div class="form-group text-left">
49+
<label for="NotesTextarea">Description</label>
50+
<textarea
51+
(blur)="onSubmit()"
52+
formControlName="description"
53+
class="form-control"
54+
id="NotesTextarea"
55+
rows="3"
56+
></textarea>
57+
</div>
58+
</form>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
@import '../../../../../styles/colors.scss';
2+
3+
@mixin tagTechnology() {
4+
background-color: $modal-button-secondary;
5+
color: #ffffff;
6+
7+
&:hover {
8+
opacity: 0.8;
9+
}
10+
}
11+
12+
.span-width {
13+
width: 6rem;
14+
background-image: $background-pantone;
15+
color: white;
16+
}
17+
18+
.hidden {
19+
display: none;
20+
}
21+
22+
.save-button-style {
23+
background-color: $modal-button-primary;
24+
color: white;
25+
}
26+
27+
.close-button-style {
28+
background-color: $modal-button-secondary;
29+
color: white;
30+
}
31+
32+
.technology-content {
33+
box-shadow: 0 3px 8px 0 rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.08);
34+
margin: 0 0 2rem 6rem;
35+
max-height: 7.5rem;
36+
overflow-y: auto;
37+
38+
.technology-list {
39+
cursor: pointer;
40+
font-size: 0.8rem;
41+
margin-bottom: 0.1rem;
42+
padding: 0.2rem 0.5rem;
43+
44+
&:hover {
45+
opacity: 0.7;
46+
}
47+
}
48+
49+
.active {
50+
background-color: #efefef;
51+
}
52+
}
53+
54+
.tags-content {
55+
margin: 2rem 0;
56+
57+
div {
58+
@include tagTechnology();
59+
60+
border-radius: 0.2rem;
61+
box-shadow: 2px 2px 5px 0px rgba(0, 0, 0, 0.75);
62+
font-size: 0.8rem;
63+
padding: 0.1rem 1rem 0.2rem 1.5rem;
64+
position: relative;
65+
margin: 0 0.5rem 0.5rem 0;
66+
67+
i {
68+
cursor: pointer;
69+
}
70+
}
71+
}
72+
73+
.ng-autocomplete {
74+
width: 100%;
75+
}
76+
77+
.autocomplete::ng-deep .autocomplete-container {
78+
border: 1px solid #ced4da;
79+
border-radius: 0 0.25rem 0.25rem 0;
80+
box-shadow: none;
81+
height: 2rem;
82+
83+
.input-container {
84+
height: 100%;
85+
86+
input {
87+
border-radius: 0.25rem;
88+
height: 100%;
89+
}
90+
}
91+
}
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { provideMockStore, MockStore } from '@ngrx/store/testing';
3+
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
4+
5+
import { TechnologyState } from '../../../shared/store/technology.reducers';
6+
import { allTechnologies } from '../../../shared/store/technology.selectors';
7+
import { EntryFieldsComponent } from './entry-fields.component';
8+
import { ProjectState } from '../../../customer-management/components/projects/components/store/project.reducer';
9+
import { allProjects } from '../../../customer-management/components/projects/components/store/project.selectors';
10+
import { allEntries } from '../../store/entry.selectors';
11+
import * as actions from '../../../shared/store/technology.actions';
12+
import * as entryActions from '../../store/entry.actions';
13+
14+
describe('EntryFieldsComponent', () => {
15+
type Merged = TechnologyState & ProjectState;
16+
let component: EntryFieldsComponent;
17+
let fixture: ComponentFixture<EntryFieldsComponent>;
18+
let store: MockStore<Merged>;
19+
let mockTechnologySelector;
20+
let mockProjectsSelector;
21+
let mockEntrySelector;
22+
let length;
23+
24+
const state = {
25+
projects: {
26+
projectList: [{ id: 'id', name: 'name', description: 'description', project_type_id: '123' }],
27+
isLoading: false,
28+
message: '',
29+
projectToEdit: undefined,
30+
},
31+
technologies: {
32+
technologyList: { items: [{ name: 'java' }] },
33+
isLoading: false,
34+
},
35+
activities: {
36+
data: [{ id: 'fc5fab41-a21e-4155-9d05-511b956ebd05', tenant_id: 'ioet', deleted: null, name: 'Training 2' }],
37+
isLoading: false,
38+
message: 'Data fetch successfully!',
39+
activityIdToEdit: '',
40+
},
41+
entries: {
42+
active: {
43+
id: 'id-15',
44+
project_id: 'project-id-15',
45+
description: 'description for active entry',
46+
technologies: ['java', 'typescript'],
47+
},
48+
entryList: [],
49+
isLoading: false,
50+
message: '',
51+
},
52+
};
53+
54+
const entry = {
55+
id: 'id-15',
56+
project_id: 'project-id-15',
57+
description: 'description for active entry',
58+
};
59+
60+
beforeEach(async(() => {
61+
TestBed.configureTestingModule({
62+
declarations: [EntryFieldsComponent],
63+
providers: [provideMockStore({ initialState: state })],
64+
imports: [FormsModule, ReactiveFormsModule],
65+
}).compileComponents();
66+
store = TestBed.inject(MockStore);
67+
mockTechnologySelector = store.overrideSelector(allTechnologies, state.technologies);
68+
mockProjectsSelector = store.overrideSelector(allProjects, state.projects);
69+
mockEntrySelector = store.overrideSelector(allEntries, state.entries);
70+
}));
71+
72+
beforeEach(() => {
73+
fixture = TestBed.createComponent(EntryFieldsComponent);
74+
component = fixture.componentInstance;
75+
fixture.detectChanges();
76+
});
77+
78+
it('should create', () => {
79+
expect(component).toBeTruthy();
80+
});
81+
82+
it('should set data in entryForm', () => {
83+
const entryDataForm = {
84+
description: 'description for active entry',
85+
technologies: null,
86+
};
87+
88+
spyOn(component.entryForm, 'patchValue');
89+
90+
component.setDataToUpdate(entry);
91+
92+
expect(component.entryForm.patchValue).toHaveBeenCalledTimes(1);
93+
expect(component.entryForm.patchValue).toHaveBeenCalledWith({ description: entryDataForm.description });
94+
expect(component.selectedTechnology).toEqual([]);
95+
});
96+
97+
it('should dispatch FindTechnology action #getTechnologies', () => {
98+
const value = 'java';
99+
spyOn(store, 'dispatch');
100+
length = value.length;
101+
component.getTechnologies(value);
102+
103+
expect(component.showlist).toBe(true);
104+
expect(store.dispatch).toHaveBeenCalledWith(new actions.FindTechnology(value));
105+
});
106+
107+
it('should NOT dispatch FindTechnology action #getTechnologies', () => {
108+
const value = 'j';
109+
spyOn(store, 'dispatch');
110+
length = value.length;
111+
component.getTechnologies(value);
112+
113+
expect(store.dispatch).not.toHaveBeenCalledWith(new actions.FindTechnology(value));
114+
});
115+
116+
it('should add a new tag #setTechnology', () => {
117+
spyOn(store, 'dispatch');
118+
const name = 'ngrx';
119+
component.selectedTechnology = ['java', 'javascript'];
120+
component.selectedTechnology.indexOf(name);
121+
length = component.selectedTechnology.length;
122+
123+
const newEntry = {
124+
id: 'id-15',
125+
project_id: 'project-id-15',
126+
};
127+
128+
component.setTechnology(name);
129+
expect(component.selectedTechnology.length).toBe(3);
130+
expect(store.dispatch).toHaveBeenCalledWith(
131+
new entryActions.UpdateActiveEntry({ ...newEntry, technologies: component.selectedTechnology })
132+
);
133+
});
134+
135+
it('should NOT add a new tag #setTechnology', () => {
136+
const name = 'ngrx';
137+
component.selectedTechnology = [
138+
'java',
139+
'javascript',
140+
'angular',
141+
'angular-ui',
142+
'typescript',
143+
'scss',
144+
'bootstrap',
145+
'jasmine',
146+
'karme',
147+
'github',
148+
];
149+
component.selectedTechnology.indexOf(name);
150+
length = component.selectedTechnology.length;
151+
component.setTechnology(name);
152+
expect(component.selectedTechnology.length).toBe(10);
153+
});
154+
155+
it('should call the removeTag function #setTechnology', () => {
156+
const name = 'java';
157+
component.selectedTechnology = ['java', 'javascript'];
158+
const index = component.selectedTechnology.indexOf(name);
159+
spyOn(component, 'removeTag');
160+
component.setTechnology(name);
161+
expect(component.removeTag).toHaveBeenCalledWith(index);
162+
});
163+
164+
it('should dispatch UpdateActiveEntry action #removeTag function', () => {
165+
spyOn(store, 'dispatch');
166+
const index = 1;
167+
const newEntry = {
168+
id: 'id-15',
169+
project_id: 'project-id-15',
170+
};
171+
component.selectedTechnology = ['java', 'angular'];
172+
const technologies = component.selectedTechnology.filter((item) => item !== component.selectedTechnology[index]);
173+
component.removeTag(index);
174+
expect(store.dispatch).toHaveBeenCalledWith(new entryActions.UpdateActiveEntry({ ...newEntry, technologies }));
175+
});
176+
177+
it('should dispatch UpdateActiveEntry action #onSubmit', () => {
178+
spyOn(store, 'dispatch');
179+
component.onSubmit();
180+
expect(store.dispatch).toHaveBeenCalledWith(new entryActions.UpdateActiveEntry(entry));
181+
});
182+
});

0 commit comments

Comments
 (0)