Skip to content

Commit a42cbae

Browse files
feat(frontend): apply FSD, luxury theme, unify components and fix backend integrations
- Enforce Feature Sliced Design patterns with unified EntityCardComponent and EntityListComponent. - Implement luxury Mavluda Beauty theme utilizing Tailwind CSS and Flowbite. - Configure asset management utilizing NgOptimizedImage for lazy loading and optimized image serving. - Fix all POST API integration to properly use `API_ENDPOINTS` helper constants linking to NestJS endpoints for `auth.service`, `gallery.service`, `treatments.service`, and `veil.service`. - Refactor catalog screens to fully align with standard unified list structures. Co-authored-by: beginwebdev2002 <102213457+beginwebdev2002@users.noreply.github.com>
1 parent 5f6bea4 commit a42cbae

14 files changed

Lines changed: 1380 additions & 69 deletions

frontend/package-lock.json

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

frontend/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,18 @@
2727
"@ngrx/store": "^21.1.0",
2828
"crypto": "^1.0.1",
2929
"express": "^5.2.1",
30+
"flowbite": "^4.0.1",
3031
"jwt-decode": "^4.0.0",
31-
"rxjs": "^7.8.1",
32-
"tailwindcss": "latest"
32+
"rxjs": "^7.8.1"
3333
},
3434
"devDependencies": {
3535
"@analogjs/vite-plugin-angular": "^2.4.0",
3636
"@playwright/test": "^1.59.1",
3737
"@types/node": "^22.14.0",
38+
"autoprefixer": "^10.4.27",
3839
"jsdom": "^29.0.2",
40+
"postcss": "^8.5.9",
41+
"tailwindcss": "^3.4.19",
3942
"typescript": "~5.8.2",
4043
"vite": "^6.2.0",
4144
"vitest": "^4.1.2"

frontend/postcss.config.cjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
plugins: {
3+
tailwindcss: {},
4+
autoprefixer: {},
5+
},
6+
}

frontend/src/entities/gallery/gallery.service.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import { HttpClient } from "@angular/common/http";
33
import { Observable, tap } from "rxjs";
44
import { Gallery } from "@shared/models";
55

6+
import { API_ENDPOINTS } from "@core/constants";
7+
68
@Injectable({
79
providedIn: "root",
810
})
911
export class GalleryService {
1012
private http = inject(HttpClient);
11-
private apiUrl = "/gallery";
13+
private apiUrl = API_ENDPOINTS.GALLERY.BASE;
1214

1315
// State
1416
private _images = signal<Gallery[]>([]);
@@ -21,7 +23,7 @@ export class GalleryService {
2123
}
2224

2325
getImage(id: string): Observable<Gallery> {
24-
return this.http.get<Gallery>(`${this.apiUrl}/${id}`);
26+
return this.http.get<Gallery>(API_ENDPOINTS.GALLERY.URL_BY_ID(id));
2527
}
2628

2729
// Use this for both create and update if sending full object, or create specific methods
@@ -35,7 +37,7 @@ export class GalleryService {
3537

3638
updateImage(id: string, formData: FormData): Observable<Gallery> {
3739
return this.http
38-
.put<Gallery>(`${this.apiUrl}/${id}`, formData)
40+
.put<Gallery>(API_ENDPOINTS.GALLERY.URL_BY_ID(id), formData)
3941
.pipe(
4042
tap((updatedImage) =>
4143
this._images.update((imgs) =>
@@ -47,7 +49,7 @@ export class GalleryService {
4749

4850
deleteImage(id: string): Observable<void> {
4951
return this.http
50-
.delete<void>(`${this.apiUrl}/${id}`)
52+
.delete<void>(API_ENDPOINTS.GALLERY.URL_BY_ID(id))
5153
.pipe(
5254
tap(() =>
5355
this._images.update((imgs) => imgs.filter((img) => img.id !== id)),

frontend/src/entities/user/auth.service.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Router } from '@angular/router';
44
import { tap } from 'rxjs/operators';
55
import { User, AuthResponse } from './model/user.model';
66
import { jwtDecode } from 'jwt-decode';
7+
import { API_ENDPOINTS } from '@core/constants';
78

89
@Injectable({
910
providedIn: 'root'
@@ -25,13 +26,13 @@ export class AuthService {
2526
}
2627

2728
login(credentials: any) {
28-
return this.http.post<AuthResponse>('/auth/login', credentials).pipe(
29+
return this.http.post<AuthResponse>(API_ENDPOINTS.AUTH.LOGIN, credentials).pipe(
2930
tap(response => this.setSession(response.access_token))
3031
);
3132
}
3233

3334
register(data: any) {
34-
return this.http.post<AuthResponse>('/auth/register', data).pipe(
35+
return this.http.post<AuthResponse>(API_ENDPOINTS.AUTH.REGISTER, data).pipe(
3536
tap(response => this.setSession(response.access_token))
3637
);
3738
}
Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
1-
<div class="entity-card">
2-
<div class="image-container" *ngIf="imageUrl">
3-
<img [ngSrc]="imageUrl" width="200" height="200" [alt]="title">
1+
<div class="bg-black-deep border border-primary-gold/20 rounded-lg overflow-hidden shadow-lg hover:shadow-primary-gold/20 transition-shadow duration-300 flex flex-col h-full group">
2+
<div class="relative w-full h-64 overflow-hidden" *ngIf="imageUrl">
3+
<img
4+
[ngSrc]="imageUrl"
5+
fill
6+
[priority]="priority"
7+
[alt]="title"
8+
class="object-cover group-hover:scale-105 transition-transform duration-500"
9+
>
410
</div>
5-
<div class="content">
6-
<h3>{{ title }}</h3>
7-
<p>{{ description }}</p>
11+
<div class="p-6 flex-grow flex flex-col">
12+
<h3 class="text-xl font-serif text-primary-gold mb-3">{{ title }}</h3>
13+
<p class="text-gray-300 font-sans flex-grow">{{ description }}</p>
814
</div>
9-
<div class="actions">
10-
<button (click)="onEdit()">Edit</button>
11-
<button (click)="onDelete()">Delete</button>
15+
<div class="p-6 pt-0 flex justify-between gap-4 mt-auto">
16+
<button
17+
(click)="onEdit()"
18+
class="flex-1 px-4 py-2 bg-transparent border border-primary-gold text-primary-gold rounded hover:bg-primary-gold hover:text-black-deep transition-colors duration-300 font-sans text-sm uppercase tracking-wider"
19+
>
20+
Edit
21+
</button>
22+
<button
23+
(click)="onDelete()"
24+
class="flex-1 px-4 py-2 bg-transparent border border-red-500 text-red-500 rounded hover:bg-red-500 hover:text-white transition-colors duration-300 font-sans text-sm uppercase tracking-wider"
25+
>
26+
Delete
27+
</button>
1228
</div>
1329
</div>
Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +0,0 @@
1-
.entity-card {
2-
border: 1px solid #ccc;
3-
border-radius: 8px;
4-
overflow: hidden;
5-
margin: 10px;
6-
display: flex;
7-
flex-direction: column;
8-
9-
.image-container {
10-
width: 100%;
11-
height: 200px;
12-
position: relative;
13-
img {
14-
object-fit: cover;
15-
}
16-
}
17-
18-
.content {
19-
padding: 10px;
20-
flex-grow: 1;
21-
}
22-
23-
.actions {
24-
padding: 10px;
25-
display: flex;
26-
justify-content: space-between;
27-
background-color: #f9f9f9;
28-
}
29-
}

frontend/src/shared/ui/entity-card/entity-card.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export class EntityCardComponent {
1313
@Input() imageUrlPath: string = 'image';
1414
@Input() titlePath: string = 'name';
1515
@Input() descriptionPath: string = 'description';
16+
@Input() priority: boolean = false;
1617

1718
@Output() edit = new EventEmitter<any>();
1819
@Output() delete = new EventEmitter<any>();
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
<div class="entity-list">
1+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8">
22
<app-entity-card
3-
*ngFor="let entity of entities"
3+
*ngFor="let entity of entities; let i = index"
44
[entity]="entity"
55
[imageUrlPath]="imageUrlPath"
66
[titlePath]="titlePath"
77
[descriptionPath]="descriptionPath"
8+
[priority]="i === 0"
89
(edit)="onEdit($event)"
910
(delete)="onDelete($event)"
11+
class="h-full"
1012
></app-entity-card>
1113
</div>
Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +0,0 @@
1-
.entity-list {
2-
display: flex;
3-
flex-wrap: wrap;
4-
gap: 20px;
5-
}

0 commit comments

Comments
 (0)