diff --git a/itenium-socks/angular.json b/itenium-socks/angular.json index e7cfa50..788afa0 100644 --- a/itenium-socks/angular.json +++ b/itenium-socks/angular.json @@ -57,7 +57,13 @@ "development": { "optimization": false, "extractLicenses": false, - "sourceMap": true + "sourceMap": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.development.ts" + } + ] } }, "defaultConfiguration": "production" @@ -100,5 +106,8 @@ } } } + }, + "cli": { + "analytics": false } } diff --git a/itenium-socks/package-lock.json b/itenium-socks/package-lock.json index 295c374..a0436b2 100644 --- a/itenium-socks/package-lock.json +++ b/itenium-socks/package-lock.json @@ -18,6 +18,7 @@ "@angular/router": "^18.0.0", "@fortawesome/angular-fontawesome": "^0.15.0", "@fortawesome/fontawesome-free": "^6.5.2", + "ngx-pagination": "^6.0.3", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" @@ -9054,6 +9055,18 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/ngx-pagination": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/ngx-pagination/-/ngx-pagination-6.0.3.tgz", + "integrity": "sha512-lONjTQ7hFPh1SyhwDrRd5ZwM4NMGQ7bNR6vLrs6mrU0Z8Q1zCcWbf/pvyp4DOlGyd9uyZxRy2wUsSZLeIPjbAw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": ">=13.0.0", + "@angular/core": ">=13.0.0" + } + }, "node_modules/nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", diff --git a/itenium-socks/package.json b/itenium-socks/package.json index 6b3a9d6..9da08d2 100644 --- a/itenium-socks/package.json +++ b/itenium-socks/package.json @@ -20,6 +20,7 @@ "@angular/router": "^18.0.0", "@fortawesome/angular-fontawesome": "^0.15.0", "@fortawesome/fontawesome-free": "^6.5.2", + "ngx-pagination": "^6.0.3", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" diff --git a/itenium-socks/public/images/placeholder.jpg b/itenium-socks/public/images/placeholder.jpg new file mode 100644 index 0000000..9064588 Binary files /dev/null and b/itenium-socks/public/images/placeholder.jpg differ diff --git a/itenium-socks/src/app/app.component.html b/itenium-socks/src/app/app.component.html index 67e7bd4..6d00511 100644 --- a/itenium-socks/src/app/app.component.html +++ b/itenium-socks/src/app/app.component.html @@ -1 +1,6 @@ +@if(!isProduction){ +
+ WARNING: This is a non-production environment! +
+} diff --git a/itenium-socks/src/app/app.component.ts b/itenium-socks/src/app/app.component.ts index 8ad7283..081903d 100644 --- a/itenium-socks/src/app/app.component.ts +++ b/itenium-socks/src/app/app.component.ts @@ -1,5 +1,6 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { RouterOutlet } from '@angular/router'; +import { environment } from '../environments/environment'; @Component({ selector: 'app-root', @@ -9,4 +10,6 @@ import { RouterOutlet } from '@angular/router'; ], templateUrl: './app.component.html', }) -export class AppComponent {} +export class AppComponent{ + isProduction = environment.production; +} diff --git a/itenium-socks/src/app/home/latest-socks.component.html b/itenium-socks/src/app/home/latest-socks.component.html index 9cd2eb7..6362409 100644 --- a/itenium-socks/src/app/home/latest-socks.component.html +++ b/itenium-socks/src/app/home/latest-socks.component.html @@ -6,22 +6,24 @@

-
diff --git a/itenium-socks/src/app/home/latest-socks.component.ts b/itenium-socks/src/app/home/latest-socks.component.ts index 9b03950..f817894 100644 --- a/itenium-socks/src/app/home/latest-socks.component.ts +++ b/itenium-socks/src/app/home/latest-socks.component.ts @@ -2,13 +2,14 @@ import { Component, OnInit } from '@angular/core'; import { SocksService } from '../socks/socks.service'; import { Observable } from 'rxjs'; import { Sock } from '../socks/sock.model'; -import { AsyncPipe, NgFor } from '@angular/common'; +import { AsyncPipe } from '@angular/common'; import { RouterLink } from '@angular/router'; +import { PricePipe } from '../price.pipe'; @Component({ selector: 'app-latest-socks', standalone: true, - imports: [NgFor, AsyncPipe, RouterLink], + imports: [AsyncPipe, RouterLink, PricePipe], templateUrl: './latest-socks.component.html' }) export class LatestSocksComponent implements OnInit { diff --git a/itenium-socks/src/app/price.pipe.ts b/itenium-socks/src/app/price.pipe.ts new file mode 100644 index 0000000..43e56c4 --- /dev/null +++ b/itenium-socks/src/app/price.pipe.ts @@ -0,0 +1,17 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'price', + standalone: true +}) +export class PricePipe implements PipeTransform { + + transform(value: number): string { + if (value) { + return `€${value.toFixed(2)}`; + } else { + return '€0.00'; + } + } + +} diff --git a/itenium-socks/src/app/socks/shop.component.html b/itenium-socks/src/app/socks/shop.component.html index a0139e2..9c89adf 100644 --- a/itenium-socks/src/app/socks/shop.component.html +++ b/itenium-socks/src/app/socks/shop.component.html @@ -5,16 +5,33 @@

Our Socks

+
+ + + +
-
+ +
+ + +
diff --git a/itenium-socks/src/app/socks/shop.component.ts b/itenium-socks/src/app/socks/shop.component.ts index 33c897b..7424621 100644 --- a/itenium-socks/src/app/socks/shop.component.ts +++ b/itenium-socks/src/app/socks/shop.component.ts @@ -1,21 +1,83 @@ import { Component } from '@angular/core'; import { SocksService } from './socks.service'; -import { Observable } from 'rxjs'; +import { Observable,BehaviorSubject, combineLatest } from 'rxjs'; import { Sock } from './sock.model'; import { AsyncPipe, NgFor } from '@angular/common'; +import { NgxPaginationModule } from 'ngx-pagination'; +import { map } from 'rxjs/operators'; +import { PricePipe } from '../price.pipe'; @Component({ selector: 'app-shop', standalone: true, - imports: [NgFor, AsyncPipe], + imports: [NgFor, AsyncPipe, NgxPaginationModule, PricePipe], templateUrl: './shop.component.html' }) export class ShopComponent { socks$!: Observable; + filteredSocks$!: Observable; + + public nameFilterSubject = new BehaviorSubject(''); + public colorFilterSubject = new BehaviorSubject(''); + public sortBySubject = new BehaviorSubject('name'); + public currentPageSubject = new BehaviorSubject(1); + public pageSize = 10; + + placeholderImageUrl = 'images/placeholder.jpg'; constructor(private socksService: SocksService) {} ngOnInit(): void { this.socks$ = this.socksService.get(); + + this.filteredSocks$ = combineLatest([ + this.socks$, + this.nameFilterSubject.asObservable(), + this.colorFilterSubject.asObservable(), + this.sortBySubject.asObservable(), + this.currentPageSubject.asObservable() + ]).pipe( + map(([socks, nameFilter, colorFilter, sortBy, currentPage]) => { + let filtered = socks.filter(sock => + sock.name.toLowerCase().includes(nameFilter.toLowerCase()) && + sock.variant.toLowerCase().includes(colorFilter.toLowerCase()) + ); + + filtered = filtered.sort((a, b) => { + if (sortBy === 'name') { + return a.name.localeCompare(b.name); + } else if (sortBy === 'price') { + return a.price - b.price; + } + return 0; + }); + + const startIndex = (currentPage - 1) * this.pageSize; + return filtered.slice(startIndex, startIndex + this.pageSize); + }) + ); + } + + onFilterName(event: Event) { + const name = (event.target as HTMLInputElement).value; + this.nameFilterSubject.next(name); + } + + onFilterColor(event: Event) { + const color = (event.target as HTMLInputElement).value; + this.colorFilterSubject.next(color); + } + + onSortBy(event: Event) { + const sortBy = (event.target as HTMLInputElement).value; + this.sortBySubject.next(sortBy); + } + + onPageChange(page: number) { + this.currentPageSubject.next(page); + } + + onImageError(event: Event) { + (event.target as HTMLImageElement).src = this.placeholderImageUrl; } } diff --git a/itenium-socks/src/app/socks/sock-reviews.component.html b/itenium-socks/src/app/socks/sock-reviews.component.html index 696aab3..774dafd 100644 --- a/itenium-socks/src/app/socks/sock-reviews.component.html +++ b/itenium-socks/src/app/socks/sock-reviews.component.html @@ -17,7 +17,7 @@
{{ review.socksId }}
-
On {{ review.added }} by {{ review.email }}
+
On {{ review.added | timeAgo }} by {{ review.email }}
diff --git a/itenium-socks/src/app/socks/sock-reviews.component.ts b/itenium-socks/src/app/socks/sock-reviews.component.ts index fe89cb9..63722ca 100644 --- a/itenium-socks/src/app/socks/sock-reviews.component.ts +++ b/itenium-socks/src/app/socks/sock-reviews.component.ts @@ -3,11 +3,12 @@ import { Component } from '@angular/core'; import { Observable } from 'rxjs'; import { SocksService } from './socks.service'; import { Review } from './sock.model'; +import { TimeAgoPipe } from '../time-ago.pipe'; @Component({ selector: 'app-sock-reviews', standalone: true, - imports: [NgFor, AsyncPipe], + imports: [NgFor, AsyncPipe, TimeAgoPipe], templateUrl: './sock-reviews.component.html' }) export class SockReviewsComponent { diff --git a/itenium-socks/src/app/time-ago.pipe.ts b/itenium-socks/src/app/time-ago.pipe.ts new file mode 100644 index 0000000..1d1e3b9 --- /dev/null +++ b/itenium-socks/src/app/time-ago.pipe.ts @@ -0,0 +1,32 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'timeAgo', + standalone: true +}) +export class TimeAgoPipe implements PipeTransform { + + transform(value: number | Date | string): string { + if (!value) return 'N/A'; + + const time = new Date(value).getTime(); + const now = new Date().getTime(); + const difference = Math.floor((now - time) / 1000); + + if (difference < 30) { + return 'Just now'; + } else if (difference < 60) { + return 'A few seconds ago'; + } else if (difference < 3600) { + const minutes = Math.floor(difference / 60); + return `${minutes} minute${minutes > 1 ? 's' : ''} ago`; + } else if (difference < 86400) { + const hours = Math.floor(difference / 3600); + return `${hours} hour${hours > 1 ? 's' : ''} ago`; + } else { + const days = Math.floor(difference / 86400); + return `${days} day${days > 1 ? 's' : ''} ago`; + } + } + +} diff --git a/itenium-socks/src/environments/environment.development.ts b/itenium-socks/src/environments/environment.development.ts new file mode 100644 index 0000000..ffe8aed --- /dev/null +++ b/itenium-socks/src/environments/environment.development.ts @@ -0,0 +1,3 @@ +export const environment = { + production: false +}; diff --git a/itenium-socks/src/environments/environment.ts b/itenium-socks/src/environments/environment.ts new file mode 100644 index 0000000..3612073 --- /dev/null +++ b/itenium-socks/src/environments/environment.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true +};