Skip to content

Commit d2abf68

Browse files
authored
Merge pull request #131 from ioet/114/Refactor-project-list-hover
fix: #114 Refactor project list grid
2 parents 16e0108 + bc82f7d commit d2abf68

12 files changed

+142
-50
lines changed

package-lock.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@ngrx/effects": "^9.0.0",
2424
"@ngrx/store": "^9.0.0",
2525
"@ngrx/store-devtools": "^9.0.0",
26+
"angular-ng-autocomplete": "^2.0.1",
2627
"bootstrap": "^4.4.1",
2728
"jquery": "^3.4.1",
2829
"minimist": "^1.2.5",

src/app/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { EffectsModule } from '@ngrx/effects';
88
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
99

1010
import { NgxPaginationModule } from 'ngx-pagination';
11+
import { AutocompleteLibModule } from 'angular-ng-autocomplete';
1112

1213
import { AppRoutingModule } from './app-routing.module';
1314
import { AppComponent } from './app.component';
@@ -92,6 +93,7 @@ import { CreateProjectTypeComponent } from './modules/customer-managment/compone
9293
ReactiveFormsModule,
9394
HttpClientModule,
9495
NgxPaginationModule,
96+
AutocompleteLibModule,
9597
StoreModule.forRoot(reducers, {
9698
metaReducers,
9799
}),

src/app/modules/shared/components/details-fields/details-fields.component.html

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()">
2-
<div *ngIf="formType === 'entries'" class="input-group input-group-sm mb-3">
2+
<div *ngIf="formType === 'entries'" class="input-group input-group-sm mb-3 flex-nowrap">
33
<div class="input-group-prepend">
44
<span class="input-group-text span-width" id="inputGroup-sizing-sm">Project</span>
55
</div>
6-
<input
7-
id="project"
8-
formControlName="project"
9-
type="text"
10-
class="form-control"
11-
aria-label="Small"
12-
aria-describedby="inputGroup-sizing-sm"
13-
/>
6+
<div class="ng-autocomplete">
7+
<ng-autocomplete
8+
class="autocomplete"
9+
[data]="listProjects && listProjects"
10+
[searchKeyword]="keyword"
11+
[itemTemplate]="itemTemplate"
12+
[notFoundTemplate]="notFoundTemplate"
13+
>
14+
</ng-autocomplete>
15+
<ng-template #itemTemplate let-item>
16+
<a [innerHTML]="item.name"></a>
17+
</ng-template>
18+
<ng-template #notFoundTemplate let-notFound>
19+
<div [innerHTML]="notFound"></div>
20+
</ng-template>
21+
</div>
1422
</div>
1523
<div class="input-group input-group-sm mb-3">
1624
<div class="input-group-prepend">

src/app/modules/shared/components/details-fields/details-fields.component.scss

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,23 @@
6969
}
7070
}
7171
}
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+
}

src/app/modules/shared/components/details-fields/details-fields.component.spec.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,28 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
55
import { TechnologyState } from '../../store/technology.reducers';
66
import { allTechnologies } from '../../store/technology.selectors';
77
import { DetailsFieldsComponent } from './details-fields.component';
8-
import { FilterProjectPipe } from '../../../shared/pipes';
98
import * as actions from '../../store/technology.actions';
9+
import { ProjectState } from '../../../project-management/store/project.reducer';
10+
import { allProjects } from '../../../project-management/store/project.selectors';
1011

1112
describe('DetailsFieldsComponent', () => {
13+
type Merged = TechnologyState & ProjectState;
1214
let component: DetailsFieldsComponent;
1315
let fixture: ComponentFixture<DetailsFieldsComponent>;
14-
let store: MockStore<TechnologyState>;
16+
let store: MockStore<Merged>;
1517
let mockTechnologySelector;
18+
let mockProjectsSelector;
1619
let length;
1720

1821
const state = {
19-
technologyList: { items: [{ name: 'java' }] },
20-
isLoading: false,
22+
projects: {
23+
projectList: [{ id: 'id', name: 'name', description: 'description' }],
24+
isLoading: false,
25+
},
26+
technologies: {
27+
technologyList: { items: [{ name: 'java' }] },
28+
isLoading: false,
29+
},
2130
};
2231

2332
const initialData = {
@@ -36,12 +45,13 @@ describe('DetailsFieldsComponent', () => {
3645

3746
beforeEach(async(() => {
3847
TestBed.configureTestingModule({
39-
declarations: [DetailsFieldsComponent, FilterProjectPipe],
48+
declarations: [DetailsFieldsComponent],
4049
providers: [provideMockStore({ initialState: state })],
4150
imports: [FormsModule, ReactiveFormsModule],
4251
}).compileComponents();
4352
store = TestBed.inject(MockStore);
44-
mockTechnologySelector = store.overrideSelector(allTechnologies, state);
53+
mockTechnologySelector = store.overrideSelector(allTechnologies, state.technologies);
54+
mockProjectsSelector = store.overrideSelector(allProjects, state.projects);
4555
}));
4656

4757
beforeEach(() => {

src/app/modules/shared/components/details-fields/details-fields.component.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@ import { Store, select } from '@ngrx/store';
44
import * as actions from '../../store/technology.actions';
55

66
import { allTechnologies } from '../../store/technology.selectors';
7-
import { Technology } from '../../models';
7+
import { Technology, Project } from '../../models';
8+
9+
import { ProjectState } from '../../../project-management/store/project.reducer';
10+
import { TechnologyState } from '../../store/technology.reducers';
11+
import { allProjects } from '../../../project-management/store/project.selectors';
12+
import * as projectActions from '../../../project-management/store/project.actions';
13+
14+
type Merged = TechnologyState & ProjectState;
815

916
@Component({
1017
selector: 'app-details-fields',
@@ -20,8 +27,10 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
2027
technology: Technology;
2128
selectedTechnology: string[] = [];
2229
isLoading = false;
30+
listProjects: Project[] = [];
31+
keyword = 'name';
2332

24-
constructor(private formBuilder: FormBuilder, private store: Store<Technology>) {
33+
constructor(private formBuilder: FormBuilder, private store: Store<Merged>) {
2534
this.entryForm = this.formBuilder.group({
2635
project: '',
2736
activity: '',
@@ -36,6 +45,12 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
3645
this.isLoading = response.isLoading;
3746
this.technology = response.technologyList;
3847
});
48+
49+
this.store.dispatch(new projectActions.LoadProjects());
50+
const projects$ = this.store.pipe(select(allProjects));
51+
projects$.subscribe((response) => {
52+
this.listProjects = response.projectList;
53+
});
3954
}
4055

4156
ngOnChanges(): void {
Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
1-
<app-search-project (changeFilterProject)="filterProjects = $event"></app-search-project>
2-
<ul class="list-group content-projects">
3-
<li
4-
class="list-group-item list-group-item-action justify-content-between align-items-center"
5-
*ngFor="let item of listProjects | filterProject: filterProjects; let i = index"
6-
(mouseenter)="showButton = i"
7-
(mouseleave)="showButton = -1"
8-
(click)="clockIn(item.id)"
9-
[ngClass]="{ active: selectedId === item.id }"
1+
<div class="ng-autocomplete">
2+
<ng-autocomplete
3+
class="autocomplete"
4+
(selected)="showButton = ''"
5+
[data]="listProjects && listProjects"
6+
[searchKeyword]="keyword"
7+
[itemTemplate]="itemTemplate"
8+
[notFoundTemplate]="notFoundTemplate"
109
>
11-
{{ item.name }}
12-
<span *ngIf="showButton === i && selectedId !== item.id" class="badge badge-light">Clock In</span>
13-
</li>
14-
</ul>
10+
</ng-autocomplete>
11+
<ng-template #itemTemplate let-item>
12+
<div class="d-flex">
13+
<div
14+
[innerHTML]="item.name"
15+
(mouseenter)="showButton = item.name"
16+
(mouseleave)="showButton = ''"
17+
(click)="clockIn(item.id)"
18+
class="text-left w-100 py-2 px-3"
19+
></div>
20+
<span *ngIf="showButton === item.name" class="badge badge-light d-flex align-items-center m-2">Clock In</span>
21+
</div>
22+
</ng-template>
23+
<ng-template #notFoundTemplate let-notFound>
24+
<div [innerHTML]="notFound"></div>
25+
</ng-template>
26+
</div>

src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.scss

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,21 @@
1717
padding: 0 0.3rem;
1818
}
1919

20-
.active {
21-
background-image: $list-item-selected;
22-
border-color: transparent;
20+
.ng-autocomplete {
21+
width: 100%;
22+
}
23+
24+
.autocomplete::ng-deep .autocomplete-container {
25+
border: 1px solid #ced4da;
26+
border-radius: 0.25rem;
27+
box-shadow: none;
28+
29+
.input-container {
30+
height: 100%;
31+
32+
input {
33+
border-radius: 0.25rem;
34+
height: 100%;
35+
}
36+
}
2337
}

src/app/modules/time-clock/components/project-list-hover/project-list-hover.component.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@ export class ProjectListHoverComponent implements OnInit {
1414
@Output() showFields = new EventEmitter<boolean>();
1515

1616
selectedId: string;
17-
showButton: number;
18-
filterProjects = '';
1917
listProjects: Project[] = [];
2018
isLoading: boolean;
19+
filterProjects = '';
20+
showButton = '';
21+
keyword = 'name';
2122

22-
constructor(private store: Store<ProjectState>) {
23-
this.showButton = -1;
24-
}
23+
constructor(private store: Store<ProjectState>) {}
2524

2625
ngOnInit(): void {
2726
this.store.dispatch(new actions.LoadProjects());

0 commit comments

Comments
 (0)