Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
11 changes: 10 additions & 1 deletion itenium-socks/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -100,5 +106,8 @@
}
}
}
},
"cli": {
"analytics": false
}
}
13 changes: 13 additions & 0 deletions itenium-socks/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions itenium-socks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Binary file added itenium-socks/public/images/placeholder.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions itenium-socks/src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
@if(!isProduction){
<div style="background-color: red; color: white; padding: 10px;">
<strong>WARNING:</strong> This is a non-production environment!
</div>
}
<router-outlet />
7 changes: 5 additions & 2 deletions itenium-socks/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -9,4 +10,6 @@ import { RouterOutlet } from '@angular/router';
],
templateUrl: './app.component.html',
})
export class AppComponent {}
export class AppComponent{
isProduction = environment.production;
}
32 changes: 17 additions & 15 deletions itenium-socks/src/app/home/latest-socks.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,24 @@ <h2>
</h2>
</div>
<div class="row">
<div class="col-sm-6 col-md-4 col-lg-3" *ngFor="let sock of socks$ | async">
<div class="box">
<a href="/socks/{{ sock.id }}">
<div class="img-box">
<img src="sock-images/Socks-{{ sock.brand }}-{{ sock.variant }}.png" />
</div>
<div class="detail-box">
<h6>{{ sock.name }}</h6>
<h6><span>{{ sock.price }}</span></h6>
</div>
<div class="new">
<span class="favourite" [style.color]="sock.color"><i class="fa fa-star"></i></span>
</div>
</a>
@for(sock of socks$ | async; track sock.id){
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="box">
<a href="/socks/{{ sock.id }}">
<div class="img-box">
<img src="sock-images/Socks-{{ sock.brand }}-{{ sock.variant }}.png" />
</div>
<div class="detail-box">
<h6>{{ sock.name }}</h6>
<h6><span>{{ sock.price | price }}</span></h6>
</div>
<div class="new">
<span class="favourite" [style.color]="sock.color"><i class="fa fa-star"></i></span>
</div>
</a>
</div>
</div>
</div>
}
</div>
<div class="btn-box">
<a routerLink="/socks">
Expand Down
5 changes: 3 additions & 2 deletions itenium-socks/src/app/home/latest-socks.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
17 changes: 17 additions & 0 deletions itenium-socks/src/app/price.pipe.ts
Original file line number Diff line number Diff line change
@@ -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';
}
}

}
27 changes: 24 additions & 3 deletions itenium-socks/src/app/socks/shop.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,33 @@ <h2>
Our Socks
</h2>
</div>
<div>
<label>
Filter by Name:
<input type="text" (input)="onFilterName($event)">
</label>
<label>
Filter by Color:
<input type="text" (input)="onFilterColor($event)">
</label>
<label>
Sort by:
<select (change)="onSortBy($event)">
<option value="name">Name</option>
<option value="price">Price</option>
</select>
</label>
</div>
<div class="row">
<div class="col-sm-6 col-md-4 col-lg-3" *ngFor="let sock of socks$ | async">
<div class="col-sm-6 col-md-4 col-lg-3" *ngFor="let sock of filteredSocks$ | async">
<div class="box">
<a href="/socks/{{ sock.id }}">
<div class="img-box">
<img src="sock-images/Socks-{{ sock.brand }}-{{ sock.variant }}.png" />
<img src="sock-images/Socks-{{ sock.brand }}-{{ sock.variant }}.png" (error)="onImageError($event)" />
</div>
<div class="detail-box">
<h6>{{ sock.name }}</h6>
<h6><span>{{ sock.price }}</span></h6>
<h6><span>{{ sock.price | price }}</span></h6>
</div>
<div class="new">
<span class="favourite" [style.color]="sock.color"><i class="fa fa-star"></i></span>
Expand All @@ -24,4 +41,8 @@ <h6><span>{{ sock.price }}</span></h6>
</div>
</div>
</div>
<div>
<button (click)="onPageChange(currentPageSubject.value - 1)" [disabled]="currentPageSubject.value === 1">Previous</button>
<button (click)="onPageChange(currentPageSubject.value + 1)">Next</button>
</div>
</section>
66 changes: 64 additions & 2 deletions itenium-socks/src/app/socks/shop.component.ts
Original file line number Diff line number Diff line change
@@ -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<Sock[]>;
filteredSocks$!: Observable<Sock[]>;

public nameFilterSubject = new BehaviorSubject<string>('');
public colorFilterSubject = new BehaviorSubject<string>('');
public sortBySubject = new BehaviorSubject<string>('name');
public currentPageSubject = new BehaviorSubject<number>(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;
}
}
2 changes: 1 addition & 1 deletion itenium-socks/src/app/socks/sock-reviews.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ <h5>
{{ review.socksId }}
<i *ngFor="let _ of [].constructor(review.rating)" class="fa fa-star" style="color: gold"></i>
</h5>
<h6>On {{ review.added }} by {{ review.email }}</h6>
<h6>On {{ review.added | timeAgo }} by {{ review.email }}</h6>
</div>
<i class="fa fa-quote-left" aria-hidden="true"></i>
</div>
Expand Down
3 changes: 2 additions & 1 deletion itenium-socks/src/app/socks/sock-reviews.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
32 changes: 32 additions & 0 deletions itenium-socks/src/app/time-ago.pipe.ts
Original file line number Diff line number Diff line change
@@ -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`;
}
}

}
3 changes: 3 additions & 0 deletions itenium-socks/src/environments/environment.development.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const environment = {
production: false
};
3 changes: 3 additions & 0 deletions itenium-socks/src/environments/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const environment = {
production: true
};