-
+ @if (filter) {
+
-
diff --git a/src/app/checklist/checklist-cta-bar/checklist-cta-bar.component.ts b/src/app/checklist/checklist-cta-bar/checklist-cta-bar.component.ts
index b9545be..c0df2d6 100644
--- a/src/app/checklist/checklist-cta-bar/checklist-cta-bar.component.ts
+++ b/src/app/checklist/checklist-cta-bar/checklist-cta-bar.component.ts
@@ -2,14 +2,14 @@ import { Component, Input, Output, EventEmitter } from '@angular/core';
import { ChecklistFilter } from '../models/checklist.model';
import { MatIcon } from '@angular/material/icon';
import { MatButton } from '@angular/material/button';
-import { NgIf } from '@angular/common';
+import { MatTooltip } from '@angular/material/tooltip';
@Component({
standalone: true,
selector: 'ac-checklist-cta-bar',
templateUrl: './checklist-cta-bar.component.html',
styleUrls: ['./checklist-cta-bar.component.scss'],
- imports: [NgIf, MatButton, MatIcon]
+ imports: [MatButton, MatIcon, MatTooltip]
})
export class ChecklistCtaBarComponent {
@Input() filter: ChecklistFilter;
diff --git a/src/app/checklist/checklist-detail-view/checklist-detail-view.component.html b/src/app/checklist/checklist-detail-view/checklist-detail-view.component.html
index 9558052..d80fb98 100644
--- a/src/app/checklist/checklist-detail-view/checklist-detail-view.component.html
+++ b/src/app/checklist/checklist-detail-view/checklist-detail-view.component.html
@@ -1,14 +1,13 @@
-
-
-
- This practice includes outdated content currently under review. Contributions are welcome.
-
-
-
+@if (item(); as item) {
+
+@if (item.rework) {
+
+ This practice includes outdated content currently under review. Contributions are welcome.
+
+}
+
+}
diff --git a/src/app/checklist/checklist-detail-view/checklist-detail-view.component.ts b/src/app/checklist/checklist-detail-view/checklist-detail-view.component.ts
index 9f276ff..2c9717d 100644
--- a/src/app/checklist/checklist-detail-view/checklist-detail-view.component.ts
+++ b/src/app/checklist/checklist-detail-view/checklist-detail-view.component.ts
@@ -1,6 +1,5 @@
-import { Component, OnInit } from '@angular/core';
-import { select, Store } from '@ngrx/store';
-import { Observable } from 'rxjs';
+import { Component, inject } from '@angular/core';
+import { Store } from '@ngrx/store';
import { ToggleFavorite, ToggleItem } from '../../projects/state/projects.actions';
import { ApplicationState } from '../../state/app.state';
import { ChecklistItem } from '../models/checklist.model';
@@ -9,23 +8,17 @@ import { BannerComponent } from '../../shared/banner/banner.component';
import { ChecklistMetadataComponent } from '../checklist-item-metadata/checklist-metadata.component';
import { ChecklistFavoriteButtonComponent } from '../checklist-favorite-button/checklist-favorite-button.component';
import { MatCheckbox } from '@angular/material/checkbox';
-import { NgIf, AsyncPipe } from '@angular/common';
@Component({
standalone: true,
selector: 'ac-checklist-detail-view',
templateUrl: './checklist-detail-view.component.html',
styleUrls: ['./checklist-detail-view.component.scss'],
- imports: [NgIf, MatCheckbox, ChecklistFavoriteButtonComponent, ChecklistMetadataComponent, BannerComponent, AsyncPipe]
+ imports: [MatCheckbox, ChecklistFavoriteButtonComponent, ChecklistMetadataComponent, BannerComponent]
})
-export class ChecklistDetailViewComponent implements OnInit {
- item$: Observable
;
-
- constructor(private store: Store) {}
-
- ngOnInit() {
- this.item$ = this.store.pipe(select(ChecklistSelectors.getSelectedItem));
- }
+export class ChecklistDetailViewComponent {
+ private store = inject>(Store);
+ item = this.store.selectSignal(ChecklistSelectors.getSelectedItem);
toggleItem(item: ChecklistItem) {
this.store.dispatch(new ToggleItem(item));
diff --git a/src/app/checklist/checklist-favorites-view/checklist-favorites-view.component.html b/src/app/checklist/checklist-favorites-view/checklist-favorites-view.component.html
index f2017af..cf48938 100644
--- a/src/app/checklist/checklist-favorites-view/checklist-favorites-view.component.html
+++ b/src/app/checklist/checklist-favorites-view/checklist-favorites-view.component.html
@@ -1,27 +1,21 @@
-
+
-
-
-
- {{ favorite.category.title }}
-
-
-
-
-
-
-
-
-

-
You have no favorites yet... but they are only a few clicks away!
-
-
-
+@for (favorite of favorites(); track favorite.category.title) {
+
+ {{ favorite.category.title }}
+
+ @for (item of favorite.items; track item.id) {
+
+
+ }
+
+
+} @empty {
+
+

+
You have no favorites yet... but they are only a few clicks away!
+
+}
diff --git a/src/app/checklist/checklist-favorites-view/checklist-favorites-view.component.ts b/src/app/checklist/checklist-favorites-view/checklist-favorites-view.component.ts
index 7cf6ffe..655efa8 100644
--- a/src/app/checklist/checklist-favorites-view/checklist-favorites-view.component.ts
+++ b/src/app/checklist/checklist-favorites-view/checklist-favorites-view.component.ts
@@ -1,6 +1,5 @@
-import { Component, OnInit } from '@angular/core';
-import { select, Store } from '@ngrx/store';
-import { Observable } from 'rxjs';
+import { Component, inject } from '@angular/core';
+import { Store } from '@ngrx/store';
import { ToggleFavorite, ToggleItem } from '../../projects/state/projects.actions';
import { ApplicationState } from '../../state/app.state';
import { ChecklistFilter, ChecklistItem, Favorite } from '../models/checklist.model';
@@ -8,7 +7,6 @@ import { SetFavoritesFilter } from '../state/checklist.actions';
import { ChecklistSelectors } from '../state/checklist.selectors';
import { ChecklistListItemComponent } from '../checklist-list/checklist-list-item.component';
import { ChecklistListComponent } from '../checklist-list/checklist-list.component';
-import { NgIf, NgFor, AsyncPipe } from '@angular/common';
import { ChecklistCtaBarComponent } from '../checklist-cta-bar/checklist-cta-bar.component';
@Component({
@@ -16,18 +14,12 @@ import { ChecklistCtaBarComponent } from '../checklist-cta-bar/checklist-cta-bar
selector: 'ac-checklist-favorites-view',
templateUrl: './checklist-favorites-view.component.html',
styleUrls: ['./checklist-favorites-view.component.scss'],
- imports: [ChecklistCtaBarComponent, NgIf, NgFor, ChecklistListComponent, ChecklistListItemComponent, AsyncPipe]
+ imports: [ChecklistCtaBarComponent, ChecklistListComponent, ChecklistListItemComponent]
})
-export class ChecklistFavoritesViewComponent implements OnInit {
- favorites$: Observable>;
- filter$: Observable;
-
- constructor(private store: Store) {}
-
- ngOnInit() {
- this.favorites$ = this.store.pipe(select(ChecklistSelectors.getFilteredFavorites));
- this.filter$ = this.store.pipe(select(ChecklistSelectors.getFavoritesFilter));
- }
+export class ChecklistFavoritesViewComponent {
+ private store = inject>(Store);
+ favorites = this.store.selectSignal(ChecklistSelectors.getFilteredFavorites);
+ filter = this.store.selectSignal(ChecklistSelectors.getFavoritesFilter);
setFilter(filter: ChecklistFilter) {
this.store.dispatch(new SetFavoritesFilter(filter));
@@ -40,12 +32,4 @@ export class ChecklistFavoritesViewComponent implements OnInit {
toggleFavorite(item: ChecklistItem) {
this.store.dispatch(new ToggleFavorite(item));
}
-
- trackByCategoryTitle(_, favorite: Favorite) {
- return favorite.category.title;
- }
-
- trackById(_, item: ChecklistItem) {
- return item.id;
- }
}
diff --git a/src/app/checklist/checklist-item-metadata/checklist-metadata.component.html b/src/app/checklist/checklist-item-metadata/checklist-metadata.component.html
index d20af15..3432509 100644
--- a/src/app/checklist/checklist-item-metadata/checklist-metadata.component.html
+++ b/src/app/checklist/checklist-item-metadata/checklist-metadata.component.html
@@ -1,2 +1,5 @@
-{{ author.name }}
-Source
+@if (author) {
+{{ author.name }}
+} @if (source) {
+Source
+}
diff --git a/src/app/checklist/checklist-item-metadata/checklist-metadata.component.ts b/src/app/checklist/checklist-item-metadata/checklist-metadata.component.ts
index 75cad18..3e199d7 100644
--- a/src/app/checklist/checklist-item-metadata/checklist-metadata.component.ts
+++ b/src/app/checklist/checklist-item-metadata/checklist-metadata.component.ts
@@ -1,13 +1,11 @@
import { Component, Input } from '@angular/core';
import { Author } from '../models/checklist.model';
-import { NgIf } from '@angular/common';
@Component({
standalone: true,
selector: 'ac-checklist-metadata',
templateUrl: './checklist-metadata.component.html',
- styleUrls: ['./checklist-metadata.component.scss'],
- imports: [NgIf]
+ styleUrls: ['./checklist-metadata.component.scss']
})
export class ChecklistMetadataComponent {
@Input() author: Author;
diff --git a/src/app/checklist/checklist-list-view/checklist-list-view.component.html b/src/app/checklist/checklist-list-view/checklist-list-view.component.html
index c18c5fd..b17d00c 100644
--- a/src/app/checklist/checklist-list-view/checklist-list-view.component.html
+++ b/src/app/checklist/checklist-list-view/checklist-list-view.component.html
@@ -1,6 +1,6 @@
-
+ @for (item of items(); track item.id) {
+
+ }
diff --git a/src/app/checklist/checklist-list-view/checklist-list-view.component.ts b/src/app/checklist/checklist-list-view/checklist-list-view.component.ts
index a227e70..dcb361b 100644
--- a/src/app/checklist/checklist-list-view/checklist-list-view.component.ts
+++ b/src/app/checklist/checklist-list-view/checklist-list-view.component.ts
@@ -1,15 +1,12 @@
-import { Component, OnInit } from '@angular/core';
-import { select, Store } from '@ngrx/store';
-import { combineLatest, Observable } from 'rxjs';
+import { Component, Signal, computed, inject } from '@angular/core';
+import { Store } from '@ngrx/store';
import { CheckAll, ToggleFavorite, ToggleItem, UncheckAll } from '../../projects/state/projects.actions';
import { BreakpointService } from '../../shared/breakpoint.service';
-import { selectOnce } from '../../shared/operators';
import { ApplicationState } from '../../state/app.state';
import { CategoryEntity, ChecklistFilter, ChecklistItem } from '../models/checklist.model';
import { SetCategoriesFilter } from '../state/checklist.actions';
import { ChecklistSelectors } from '../state/checklist.selectors';
import { ChecklistListItemComponent } from '../checklist-list/checklist-list-item.component';
-import { NgFor, AsyncPipe } from '@angular/common';
import { ChecklistListComponent } from '../checklist-list/checklist-list.component';
import { ChecklistCtaBarComponent } from '../checklist-cta-bar/checklist-cta-bar.component';
@@ -18,22 +15,14 @@ import { ChecklistCtaBarComponent } from '../checklist-cta-bar/checklist-cta-bar
selector: 'ac-list-view',
templateUrl: './checklist-list-view.component.html',
styleUrls: ['./checklist-list-view.component.scss'],
- imports: [ChecklistCtaBarComponent, ChecklistListComponent, NgFor, ChecklistListItemComponent, AsyncPipe]
+ imports: [ChecklistCtaBarComponent, ChecklistListComponent, ChecklistListItemComponent]
})
-export class ListViewComponent implements OnInit {
- items$: Observable;
- filter$: Observable;
- showActionButtons$: Observable;
-
- constructor(private store: Store, private breakpointService: BreakpointService) {}
-
- ngOnInit() {
- this.items$ = this.store.pipe(select(ChecklistSelectors.getItemsFromSelectedCategory));
- this.filter$ = this.store.pipe(select(ChecklistSelectors.getCategoriesFilter));
-
- const { medium$, desktop$ } = this.breakpointService.getAllBreakpoints();
- this.showActionButtons$ = combineLatest(medium$, desktop$, (medium, desktop) => medium || desktop);
- }
+export class ListViewComponent {
+ private store = inject>(Store);
+ private breakpointService = inject(BreakpointService);
+ items = this.store.selectSignal(ChecklistSelectors.getItemsFromSelectedCategory);
+ filter = this.store.selectSignal(ChecklistSelectors.getCategoriesFilter);
+ showActionButtons = computed(() => this.breakpointService.medium() || this.breakpointService.desktop());
toggleItem(item: ChecklistItem) {
this.store.dispatch(new ToggleItem(item));
@@ -44,22 +33,20 @@ export class ListViewComponent implements OnInit {
}
checkAllItems() {
- this.getSelectedCategory().subscribe(category => this.store.dispatch(new CheckAll(category)));
+ const categories = this.getSelectedCategory();
+ this.store.dispatch(new CheckAll(categories));
}
uncheckAllItems() {
- this.getSelectedCategory().subscribe(category => this.store.dispatch(new UncheckAll(category)));
+ const categories = this.getSelectedCategory();
+ this.store.dispatch(new UncheckAll(categories));
}
toggleFavorite(item: ChecklistItem) {
this.store.dispatch(new ToggleFavorite(item));
}
- trackById(_, item: ChecklistItem) {
- return item.id;
- }
-
- private getSelectedCategory(): Observable {
- return this.store.pipe(selectOnce(ChecklistSelectors.getSelectedCategory));
+ private get getSelectedCategory(): Signal {
+ return this.store.selectSignal(ChecklistSelectors.getSelectedCategory);
}
}
diff --git a/src/app/checklist/checklist-list/checklist-list-item.component.html b/src/app/checklist/checklist-list/checklist-list-item.component.html
index 41c4449..0cc1ef4 100644
--- a/src/app/checklist/checklist-list/checklist-list-item.component.html
+++ b/src/app/checklist/checklist-list/checklist-list-item.component.html
@@ -1,6 +1,8 @@
{{ item.title }}
-needs rework
+@if (item.rework) {
+needs rework
+}
-
- {{ item.title }}
- chevron_right
-
+@if (breadcrumbs(); as breadcrumb) {
+
+ @for (item of breadcrumb; track trackByTitle($index, item)) {
+ - {{ item.title }}
+ @if (!$last) {
+ - chevron_right
+ } }
+}
diff --git a/src/app/checklist/checklist-overview/checklist-overview.component.ts b/src/app/checklist/checklist-overview/checklist-overview.component.ts
index bb48c80..63f2a70 100644
--- a/src/app/checklist/checklist-overview/checklist-overview.component.ts
+++ b/src/app/checklist/checklist-overview/checklist-overview.component.ts
@@ -1,23 +1,20 @@
import { animate, query, stagger, style, transition, trigger } from '@angular/animations';
-import { Component, OnInit } from '@angular/core';
+import { Component, effect, inject, untracked } from '@angular/core';
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
-import { select, Store } from '@ngrx/store';
-import { Observable, zip } from 'rxjs';
-import { filter, switchMap, tap } from 'rxjs/operators';
-import { selectOnce } from '../../shared/operators';
+import { Store } from '@ngrx/store';
import { extractRouteParams, getActivatedChild } from '../../shared/router.utils';
import { ApplicationState } from '../../state/app.state';
-import { Category, ChecklistItem } from '../models/checklist.model';
+import { BreadcrumbItem } from '../models/checklist.model';
import { ChecklistSelectors } from '../state/checklist.selectors';
import { MatIcon } from '@angular/material/icon';
-import { NgIf, NgFor, AsyncPipe } from '@angular/common';
+import { toSignal } from '@angular/core/rxjs-interop';
@Component({
standalone: true,
selector: 'ac-checklist-overview',
templateUrl: './checklist-overview.component.html',
styleUrls: ['./checklist-overview.component.scss'],
- imports: [NgIf, NgFor, MatIcon, RouterOutlet, AsyncPipe],
+ imports: [MatIcon, RouterOutlet],
animations: [
trigger('breadcrumb', [
transition('* <=> *', [
@@ -50,36 +47,33 @@ import { NgIf, NgFor, AsyncPipe } from '@angular/common';
])
]
})
-export class ChecklistOverviewComponent implements OnInit {
- breadcrumb$: Observable;
+export class ChecklistOverviewComponent {
+ private store = inject>(Store);
+ private router = inject(Router);
+ private route = inject(ActivatedRoute);
+ breadcrumbs = this.store.selectSignal(ChecklistSelectors.getBreadcrumb);
- constructor(private store: Store, private router: Router, private route: ActivatedRoute) {}
+ constructor() {
+ const params = toSignal(this.route.params);
+ const categories = this.store.selectSignal(ChecklistSelectors.getActiveCategories);
+ const entities = this.store.selectSignal(ChecklistSelectors.getActiveCategoryEntities);
+ const editMode = this.store.selectSignal(ChecklistSelectors.getEditMode);
- ngOnInit() {
- this.breadcrumb$ = this.store.pipe(select(ChecklistSelectors.getBreadcrumb));
-
- this.route.params
- .pipe(
- switchMap(_ =>
- zip(
- this.store.pipe(selectOnce(ChecklistSelectors.getActiveCategoryEntities)),
- this.store.pipe(selectOnce(ChecklistSelectors.getActiveCategories)),
- this.store.pipe(selectOnce(ChecklistSelectors.getEditMode))
- )
- ),
- filter(([, categories]) => !!categories.length),
- tap(([entities, categories, editMode]) => {
+ effect(() => {
+ const _ = params();
+ untracked(() => {
+ if (categories().length) {
const { category } = extractRouteParams(this.route.snapshot, 1);
- const categoryDisabled = !category || !entities[category];
+ const categoryDisabled = !category || !entities()[category];
- if (categoryDisabled && !editMode) {
- this.router.navigate([categories[0].slug], {
+ if (categoryDisabled && !editMode()) {
+ this.router.navigate([categories()[0].slug], {
relativeTo: this.route
});
}
- })
- )
- .subscribe();
+ }
+ });
+ });
}
goBack(last: boolean) {
@@ -89,7 +83,7 @@ export class ChecklistOverviewComponent implements OnInit {
}
}
- trackByTitle(_, item: Category | ChecklistItem) {
+ trackByTitle(_, item: BreadcrumbItem) {
return item.title;
}
}
diff --git a/src/app/checklist/checklist-search/checklist-search.component.html b/src/app/checklist/checklist-search/checklist-search.component.html
index 393a310..e5d8a18 100644
--- a/src/app/checklist/checklist-search/checklist-search.component.html
+++ b/src/app/checklist/checklist-search/checklist-search.component.html
@@ -1,17 +1,15 @@
-
+
-
- {{ result.document.category }}
+ @for (result of results(); track result) {
+
+ @if (result.document.category) {
+ {{ result.document.category }}
+ }
+ }
diff --git a/src/app/checklist/checklist-search/checklist-search.component.ts b/src/app/checklist/checklist-search/checklist-search.component.ts
index b4c53fb..de8fe92 100644
--- a/src/app/checklist/checklist-search/checklist-search.component.ts
+++ b/src/app/checklist/checklist-search/checklist-search.component.ts
@@ -1,47 +1,36 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, computed, inject, signal } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete';
import { Router } from '@angular/router';
import * as fuzzysort from 'fuzzysort';
-import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
-import { debounceTime, map, switchMap } from 'rxjs/operators';
+import { debounceTime } from 'rxjs/operators';
import { CategoryEntity, ChecklistItem } from '../models/checklist.model';
import { IndexEntry, SearchResult } from '../search/search.models';
import { SearchService } from '../search/search.service';
import { MatOption } from '@angular/material/core';
-import { NgFor, NgIf, AsyncPipe } from '@angular/common';
+import { toSignal } from '@angular/core/rxjs-interop';
@Component({
standalone: true,
selector: 'ac-checklist-search',
templateUrl: './checklist-search.component.html',
styleUrls: ['./checklist-search.component.scss'],
- imports: [ReactiveFormsModule, MatAutocompleteTrigger, MatAutocomplete, NgFor, MatOption, NgIf, AsyncPipe]
+ imports: [ReactiveFormsModule, MatAutocompleteTrigger, MatAutocomplete, MatOption]
})
-export class ChecklistSearchComponent implements OnInit {
- results$: Observable;
+export class ChecklistSearchComponent {
+ private searchService = inject(SearchService);
+ private router = inject(Router);
searchField = new FormControl('');
+ search = toSignal(this.searchField.valueChanges.pipe(debounceTime(150)));
- focus$ = new BehaviorSubject('INIT');
-
- constructor(private searchService: SearchService, private router: Router) {}
-
- ngOnInit() {
- const search$ = this.searchField.valueChanges.pipe(debounceTime(150));
-
- this.results$ = combineLatest([this.focus$, search$]).pipe(
- map(([, term]) => term),
- switchMap(term => this.searchService.search(term)),
- map(results => results.map(this.mapToSearchResult))
- );
- }
+ results = computed(() => {
+ const search = this.search();
+ const results = this.searchService.search(search);
+ return results.map(this.mapToSearchResult);
+ });
getOptionText(value: SearchResult) {
- if (!value) {
- return '';
- }
-
- return value.document.title;
+ return value?.document?.title || '';
}
optionSelected({ option }: MatAutocompleteSelectedEvent) {
diff --git a/src/app/checklist/checklist.component.html b/src/app/checklist/checklist.component.html
index 5938364..11bf7d0 100644
--- a/src/app/checklist/checklist.component.html
+++ b/src/app/checklist/checklist.component.html
@@ -1,10 +1,10 @@
-
+
-
+
@@ -12,18 +12,20 @@
edit Manage Projects
-
+ @if (mediumUp()) {
+
+ }
-
+
- 🧐 Ups, seems like you're in edit mode!
+ @if (editMode()) {
+ 🧐 Ups, seems like you're in edit mode!
+ }
diff --git a/src/app/checklist/checklist.component.ts b/src/app/checklist/checklist.component.ts
index a4c5948..2bae513 100644
--- a/src/app/checklist/checklist.component.ts
+++ b/src/app/checklist/checklist.component.ts
@@ -1,15 +1,13 @@
-import { Component, OnInit, ViewChild } from '@angular/core';
+import { Component, Signal, computed, inject, signal } from '@angular/core';
import { MatSidenav, MatDrawerMode, MatSidenavContainer, MatSidenavContent } from '@angular/material/sidenav';
import { MatDialog } from '@angular/material/dialog';
import { Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
-import { select, Store } from '@ngrx/store';
-import { BehaviorSubject, combineLatest, Observable, of, zip } from 'rxjs';
-import { filter, map, switchMap } from 'rxjs/operators';
-import { Project } from '../projects/models/projects.model';
+import { Store } from '@ngrx/store';
+import { of } from 'rxjs';
+import { tap } from 'rxjs/operators';
import { ToggleAllFavorites, ToggleCategory } from '../projects/state/projects.actions';
import { ProjectsSelectors } from '../projects/state/projects.selectors';
import { BreakpointService } from '../shared/breakpoint.service';
-import { selectOnce } from '../shared/operators';
import { hasEntities } from '../shared/utils';
import { ApplicationState } from '../state/app.state';
import { ConfirmationDialogComponent } from './confirmation-dialog/confirmation-dialog.component';
@@ -22,7 +20,6 @@ import { MatSlideToggle } from '@angular/material/slide-toggle';
import { MatBadge } from '@angular/material/badge';
import { ScoreChartComponent } from '../shared/score-chart/score-chart.component';
import { ChecklistSearchComponent } from './checklist-search/checklist-search.component';
-import { NgIf, NgFor, AsyncPipe } from '@angular/common';
import {
DropdownStaticOptionsComponent,
DropdownStaticOptionComponent
@@ -53,7 +50,6 @@ enum CategoryListMode {
DropdownStaticOptionsComponent,
DropdownStaticOptionComponent,
RouterLink,
- NgIf,
ChecklistSearchComponent,
MatSidenavContainer,
MatSidenav,
@@ -61,118 +57,69 @@ enum CategoryListMode {
RouterLinkActive,
MatBadge,
MatSlideToggle,
- NgFor,
MatCheckbox,
MatSidenavContent,
RouterOutlet,
- FooterComponent,
- AsyncPipe
+ FooterComponent
],
providers: [SearchService]
})
-export class ChecklistComponent implements OnInit {
- private editMode$ = new BehaviorSubject(CategoryListMode.List);
-
- small$: Observable
;
- mediumUp$: Observable;
- desktop$: Observable;
-
- categories$: Observable>;
- projects$: Observable>;
- selectedProjectId$: Observable;
- favoritesCount$: Observable;
- favoritesScore$: Observable;
- overallScore$: Observable;
-
- editMode = false;
-
- sideNavMode: MatDrawerMode = 'side';
-
- @ViewChild(MatSidenav, { static: true })
- sideNav: MatSidenav;
-
- constructor(
- private store: Store,
- private breakpointService: BreakpointService,
- private dialog: MatDialog,
- private router: Router
- ) {}
-
- ngOnInit() {
- this.categories$ = this.editMode$.pipe(switchMap(mode => this.getCategories(mode)));
- this.projects$ = this.store.pipe(select(ProjectsSelectors.getProjects));
- this.selectedProjectId$ = this.store.pipe(select(ProjectsSelectors.getSelectedProjectId));
- this.favoritesCount$ = this.store.pipe(select(ChecklistSelectors.getFavoritesCount));
- this.favoritesScore$ = this.store.pipe(select(ChecklistSelectors.getFavoritesScore));
-
- const { small$, medium$, desktop$ } = this.breakpointService.getAllBreakpoints();
-
- this.small$ = small$;
- this.desktop$ = desktop$;
- this.mediumUp$ = combineLatest([medium$, desktop$]).pipe(map(([medium, desktop]) => medium || desktop));
-
- desktop$.subscribe(matches => {
- if (!matches) {
- this.sideNav.close();
- this.setSidenavMode('over');
- } else {
- this.sideNav.open();
- this.setSidenavMode('side');
- }
- });
- }
-
- toggleCategory(category: Category) {
- this.store
- .pipe(
- selectOnce(ChecklistSelectors.getFavoriteEntitiesByCategory(category.slug)),
- switchMap((favorites: Array) => {
- if (hasEntities(favorites) && category.enabled) {
- return this.openUserPrompt(favorites);
- }
-
- return of(true);
- }),
- filter(remove => remove)
- )
- .subscribe(() => this.store.dispatch(new ToggleCategory(category.slug)));
+export class ChecklistComponent {
+ private store = inject>(Store);
+ private breakpointService = inject(BreakpointService);
+ private dialog = inject(MatDialog);
+ private router = inject(Router);
+
+ small = this.breakpointService.small;
+ medium = this.breakpointService.medium;
+ desktop = this.breakpointService.desktop;
+ mediumUp = computed(() => this.medium() || this.desktop());
+
+ projects = this.store.selectSignal(ProjectsSelectors.getProjects);
+ selectedProjectId = this.store.selectSignal(ProjectsSelectors.getSelectedProjectId);
+ favoritesCount = this.store.selectSignal(ChecklistSelectors.getFavoritesCount);
+ favoritesScore = this.store.selectSignal(ChecklistSelectors.getFavoritesScore);
+ private activeCategories = this.store.selectSignal(ChecklistSelectors.getActiveCategories);
+ private allCategories = this.store.selectSignal(ChecklistSelectors.getAllCategories);
+
+ categories = computed(() => {
+ const mode = this.editMode() ? CategoryListMode.Edit : CategoryListMode.List;
+ const categories = mode ? this.allCategories : this.activeCategories;
+ return categories();
+ });
+
+ editMode = signal(false);
+
+ sideNavMode = computed(() => (this.mediumUp() ? 'side' : 'over'));
+
+ toggleCategory(category: Category): any {
+ const selector = ChecklistSelectors.getFavoriteEntitiesByCategory(category.slug);
+ const favorites = this.store.selectSignal(selector)() as ChecklistItem[];
+ let observe = of(true);
+ if (hasEntities(favorites) && category.enabled) {
+ observe = this.openUserPrompt(favorites);
+ }
+ observe.subscribe(valid => valid && this.store.dispatch(new ToggleCategory(category.slug)));
}
toggleEditMode() {
- this.editMode = !this.editMode;
+ const editMode = !this.editMode();
+ this.editMode.set(editMode);
this.store.dispatch(new ToggleEditMode());
- this.editMode$.next(this.editMode ? CategoryListMode.Edit : CategoryListMode.List);
-
- of(this.editMode)
- .pipe(
- filter(editMode => !editMode),
- switchMap(_ => this.store.pipe(selectOnce(ChecklistSelectors.getSelectedCategory))),
- filter(category => !!category),
- filter(category => !category.enabled),
- switchMap(_ =>
- zip(
- this.store.pipe(selectOnce(ChecklistSelectors.getActiveCategories)),
- this.store.pipe(selectOnce(ProjectsSelectors.getSelectedProjectId))
- )
- )
- )
- .subscribe(([categories, projectId]) => {
+ if (!editMode) {
+ const category = this.store.selectSignal(ChecklistSelectors.getSelectedCategory)();
+ if (category && !category.enabled) {
+ const categories = this.activeCategories();
+ const projectId = this.selectedProjectId();
this.router.navigate(['/', projectId, 'checklist', categories[0].slug]);
- });
+ }
+ }
}
navigateToProject(project: string) {
this.router.navigate([`/${project}/checklist`]);
}
- trackBySlug(_, category: Category) {
- return category.slug;
- }
-
- trackById(_, item: ChecklistItem) {
- return item.id;
- }
-
private openUserPrompt(favorites: Array) {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
data: {
@@ -182,28 +129,14 @@ export class ChecklistComponent implements OnInit {
}
});
- return dialogRef.afterClosed().pipe(switchMap(result => this.processDialogResult(result, favorites)));
+ return dialogRef.afterClosed().pipe(
+ tap(result => this.processDialogResult(result, favorites))
+ );
}
private processDialogResult(result: boolean, favorites: Array) {
if (result) {
this.store.dispatch(new ToggleAllFavorites(favorites));
}
-
- return of(result);
- }
-
- private getCategories(mode: CategoryListMode) {
- let categories$ = this.store.pipe(select(ChecklistSelectors.getActiveCategories));
-
- if (mode === CategoryListMode.Edit) {
- categories$ = this.store.pipe(select(ChecklistSelectors.getAllCategories));
- }
-
- return categories$;
- }
-
- private setSidenavMode(mode: 'side' | 'over') {
- this.sideNavMode = mode;
}
}
diff --git a/src/app/checklist/confirmation-dialog/confirmation-dialog.component.ts b/src/app/checklist/confirmation-dialog/confirmation-dialog.component.ts
index 5d2b9e6..93a6a79 100644
--- a/src/app/checklist/confirmation-dialog/confirmation-dialog.component.ts
+++ b/src/app/checklist/confirmation-dialog/confirmation-dialog.component.ts
@@ -1,4 +1,4 @@
-import { Component, HostBinding, Inject, OnInit } from '@angular/core';
+import { Component, HostBinding, inject } from '@angular/core';
import {
MAT_DIALOG_DATA,
MatDialogTitle,
@@ -15,15 +15,14 @@ import { MatButton } from '@angular/material/button';
styleUrls: ['./confirmation-dialog.component.scss'],
imports: [MatDialogTitle, MatDialogContent, MatDialogActions, MatButton, MatDialogClose]
})
-export class ConfirmationDialogComponent implements OnInit {
- @HostBinding('style.maxWidth')
- width = '350px';
+export class ConfirmationDialogComponent {
+ public data = inject(MAT_DIALOG_DATA);
- confirmationButtonColor = 'warn';
+ @HostBinding('style.maxWidth') width = '350px';
- constructor(@Inject(MAT_DIALOG_DATA) public data: any) {}
+ confirmationButtonColor = 'warn';
- ngOnInit() {
+ constructor() {
const { width, confirmationButtonColor } = this.data;
if (width) {
diff --git a/src/app/checklist/project-exists.guard.ts b/src/app/checklist/project-exists.guard.ts
index 945b3df..dfd782d 100644
--- a/src/app/checklist/project-exists.guard.ts
+++ b/src/app/checklist/project-exists.guard.ts
@@ -1,4 +1,4 @@
-import { Injectable } from '@angular/core';
+import { Injectable, inject } from '@angular/core';
import { ActivatedRouteSnapshot, Router } from '@angular/router';
import { select, Store } from '@ngrx/store';
import { of } from 'rxjs';
@@ -11,7 +11,8 @@ import { ApplicationState } from '../state/app.state';
providedIn: 'root'
})
export class ProjectExistsGuard {
- constructor(private store: Store, private router: Router) {}
+ private store = inject>(Store);
+ private router = inject(Router);
canActivate(snapshot: ActivatedRouteSnapshot) {
const projectId = snapshot.params.project;
diff --git a/src/app/checklist/search/search.service.ts b/src/app/checklist/search/search.service.ts
index 125de99..6de83be 100644
--- a/src/app/checklist/search/search.service.ts
+++ b/src/app/checklist/search/search.service.ts
@@ -1,4 +1,4 @@
-import { Injectable } from '@angular/core';
+import { inject, Injectable } from '@angular/core';
import { ActionsSubject, select, Store } from '@ngrx/store';
import * as fuzzysort from 'fuzzysort';
import { merge, of, zip } from 'rxjs';
@@ -13,6 +13,8 @@ import { IndexEntry } from './search.models';
@Injectable()
export class SearchService {
+ private store = inject>(Store);
+ private actions = inject(ActionsSubject);
private index: Array>;
private options: Fuzzysort.KeyOptions = {
@@ -22,7 +24,7 @@ export class SearchService {
threshold: -10000
};
- constructor(private store: Store, private actions: ActionsSubject) {
+ constructor() {
const actions$ = this.actions.pipe(filter(action => action.type === ProjectsActionTypes.TOGGLE_CATEGORY));
merge(actions$, of('INIT INDEX'))
@@ -33,7 +35,7 @@ export class SearchService {
}
search(term: string) {
- return of(fuzzysort.go(term, this.index, this.options));
+ return fuzzysort.go(term, this.index, this.options);
}
createIndex(categoryEntities: CategoryEntities, itemEntities: ItemEntities, projectId: string) {
diff --git a/src/app/checklist/state/checklist.selectors.ts b/src/app/checklist/state/checklist.selectors.ts
index cb1bd70..df76e3f 100644
--- a/src/app/checklist/state/checklist.selectors.ts
+++ b/src/app/checklist/state/checklist.selectors.ts
@@ -163,7 +163,7 @@ export namespace ChecklistSelectors {
});
return acc;
- }, []);
+ }, [] as { category: CategoryEntity; items: ChecklistItem[] }[]);
}
);
diff --git a/src/app/projects/project-dialog/project-dialog.component.html b/src/app/projects/project-dialog/project-dialog.component.html
index fa63d29..777275a 100644
--- a/src/app/projects/project-dialog/project-dialog.component.html
+++ b/src/app/projects/project-dialog/project-dialog.component.html
@@ -14,11 +14,15 @@
type="text"
/>
{{ projectName.value?.length || 0 }}/{{ maxLength }}
- Project already exists
-
+ @if (projectName.hasError('projectExists')) {
+ Project already exists
+ } @if (projectName.pending) {
+
+ }
-