Skip to content

Commit 5d5d2fb

Browse files
feat: Implement full-stack Veil entity management with dedicated backend services and frontend UI components.
1 parent 6315c28 commit 5d5d2fb

24 files changed

Lines changed: 208 additions & 114 deletions

File tree

backend/src/veil/domain/veil.entity.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export interface VeilProps {
44
description: string;
55
price: number;
66
rentalPrice: number;
7-
images: string[];
7+
image: string;
88
category: string;
99
isAvailable?: boolean;
1010
sku?: string;
@@ -23,7 +23,7 @@ export class Veil {
2323
public readonly description: string;
2424
public readonly price: number;
2525
public readonly rentalPrice: number;
26-
public readonly images: string[];
26+
public readonly image: string;
2727
public readonly category: string;
2828
public readonly isAvailable: boolean;
2929
public readonly sku?: string;
@@ -41,7 +41,7 @@ export class Veil {
4141
this.description = props.description;
4242
this.price = props.price;
4343
this.rentalPrice = props.rentalPrice;
44-
this.images = props.images;
44+
this.image = props.image;
4545
this.category = props.category;
4646
this.isAvailable = props.isAvailable ?? true;
4747
this.sku = props.sku;

backend/src/veil/infrastructure/repositories/veil.repository.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export class VeilRepository {
5757
description,
5858
price,
5959
rentalPrice,
60-
images,
60+
image,
6161
category,
6262
isAvailable,
6363
sku,
@@ -76,7 +76,7 @@ export class VeilRepository {
7676
description,
7777
price,
7878
rentalPrice,
79-
images,
79+
image,
8080
category,
8181
isAvailable,
8282
sku,

backend/src/veil/infrastructure/schemas/veil.schema.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ export class VeilSchemaEntity {
1717
@Prop({ required: true })
1818
rentalPrice: number;
1919

20-
@Prop([String])
21-
images: string[];
20+
@Prop({ required: true })
21+
image: string;
2222

2323
@Prop({ required: true })
2424
category: string;

backend/src/veil/presentation/dto/create-veil.dto.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {
22
IsString,
33
IsNotEmpty,
44
IsNumber,
5-
IsArray,
65
IsOptional,
76
IsBoolean,
87
} from 'class-validator';
@@ -27,14 +26,9 @@ export class CreateVeilDto {
2726
@Type(() => Number)
2827
rentalPrice: number;
2928

30-
@IsArray()
3129
@IsOptional()
32-
@IsString({ each: true })
33-
@Transform(({ value }: { value: string | string[] }) => {
34-
if (!!value && Array.isArray(value)) return value;
35-
return typeof value === 'string' ? value.split(',') : [];
36-
})
37-
images: string[];
30+
@IsString()
31+
image: string;
3832

3933
@IsString()
4034
@IsNotEmpty()

backend/src/veil/presentation/veil.controller.ts

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ export class VeilController {
5454
@Body() createVeilDto: CreateVeilDto,
5555
@UploadedFiles() files: Array<Express.Multer.File>,
5656
): Promise<Veil> {
57-
const imagePaths = files
58-
? files.map((file) => `/uploads/veils/${file.filename}`)
59-
: [];
57+
const imagePath =
58+
files && files.length > 0 ? `/uploads/veils/${files[0].filename}` : null;
59+
6060
const veilData = {
6161
...createVeilDto,
62-
images: [...(createVeilDto.images || []), ...imagePaths],
62+
image: imagePath || createVeilDto.image,
6363
};
6464
const veil = veilData as unknown as Omit<Veil, 'id' | 'createdAt'>;
6565
return this.veilService.create(veil);
@@ -84,23 +84,17 @@ export class VeilController {
8484
@Body() updateVeilDto: UpdateVeilDto,
8585
@UploadedFiles() files: Array<Express.Multer.File>,
8686
): Promise<Veil> {
87-
const imagePaths = files
88-
? files.map((file) => `/uploads/veils/${file.filename}`)
89-
: [];
90-
let existingImages: string[] = [];
91-
if (updateVeilDto.images) {
92-
if (Array.isArray(updateVeilDto.images)) {
93-
existingImages = updateVeilDto.images;
94-
} else {
95-
existingImages = [updateVeilDto.images];
96-
}
97-
}
87+
const imagePath =
88+
files && files.length > 0 ? `/uploads/veils/${files[0].filename}` : null;
9889

9990
const veilData = {
10091
...updateVeilDto,
101-
images: [...existingImages, ...imagePaths],
10292
};
10393

94+
if (imagePath) {
95+
veilData.image = imagePath;
96+
}
97+
10498
return this.veilService.update(id, veilData as unknown as Partial<Veil>);
10599
}
106100

frontend/src/app.routes.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,33 @@ export const routes: Routes = [
2929
{
3030
path: "dashboard",
3131
loadComponent: () =>
32-
import("./pages/dashboard/dashboard.component").then(
33-
(m) => m.DashboardComponent,
34-
),
32+
import("@pages/dashboard").then((m) => m.DashboardComponent),
33+
},
34+
{
35+
path: "veil",
36+
loadComponent: () =>
37+
import("@pages/veil").then((m) => m.VeilPageComponent),
38+
},
39+
{
40+
path: "services",
41+
loadComponent: () =>
42+
import("@pages/services").then((m) => m.ServicesPageComponent),
43+
},
44+
{
45+
path: "clients",
46+
loadComponent: () =>
47+
import("@pages/dashboard").then((m) => m.DashboardComponent),
48+
},
49+
{
50+
path: "gallery",
51+
loadComponent: () =>
52+
import("@pages/gallery").then((m) => m.GalleryComponent),
53+
},
54+
{
55+
path: "settings",
56+
loadComponent: () =>
57+
import("@pages/settings").then((m) => m.SettingsComponent),
3558
},
36-
{ path: "veil", component: VeilPageComponent },
37-
{ path: "services", component: ServicesPageComponent },
38-
{ path: "clients", component: DashboardComponent },
39-
{ path: "gallery", component: GalleryComponent },
40-
{ path: "settings", component: SettingsComponent },
4159
],
4260
},
4361

frontend/src/core/interceptors/api.interceptor.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1-
import { HttpInterceptorFn } from '@angular/common/http';
2-
import { environment } from '@environments/environment';
1+
import { HttpInterceptorFn } from "@angular/common/http";
2+
import { environment } from "@environments/environment";
3+
import { linkServerConvert } from "@shared/lib";
34

45
export const apiInterceptor: HttpInterceptorFn = (req, next) => {
5-
if (req.url.startsWith('/')) {
6-
const token = typeof localStorage !== 'undefined' ? localStorage.getItem('token') : null;
6+
if (req.url.startsWith("/")) {
7+
const token =
8+
typeof localStorage !== "undefined"
9+
? localStorage.getItem("token")
10+
: null;
711
let headers = req.headers;
812
if (token) {
9-
headers = headers.set('Authorization', `Bearer ${token}`);
13+
headers = headers.set("Authorization", `Bearer ${token}`);
1014
}
1115

1216
const apiReq = req.clone({
13-
url: `${environment.apiUrl}${req.url}`,
14-
headers
17+
url: linkServerConvert(req.url),
18+
headers,
1519
});
1620
return next(apiReq);
1721
}

frontend/src/entities/veil/veil.service.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Injectable, inject, signal } from "@angular/core";
22
import { HttpClient } from "@angular/common/http";
33
import { Observable, tap } from "rxjs";
44
import { Veil } from "@features/veil";
5+
import { formDataExcludeProperty, objectExcludePropety } from "@shared/lib";
56

67
@Injectable({
78
providedIn: "root",
@@ -32,7 +33,12 @@ export class VeilService {
3233
);
3334
}
3435

35-
updateVeil(id: string, veil: Partial<Veil> | FormData): Observable<Veil> {
36+
updateVeil(id: string, veil: FormData): Observable<Veil> {
37+
const updatedVeil = formDataExcludeProperty(veil, [
38+
"id",
39+
"createdAt",
40+
"updatedAt",
41+
]);
3642
return this.http
3743
.put<Veil>(`${this.apiUrl}/${id}`, veil)
3844
.pipe(

frontend/src/features/veil/model/veil.data.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export interface Veil {
44
description?: string;
55
price: number;
66
rentalPrice: number;
7-
images?: string[];
7+
image?: string;
88
category: string;
99
isAvailable: boolean;
1010
sku: string;

frontend/src/pages/user-home/user-home.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export class UserHomeComponent implements OnInit {
2121
http = inject(HttpClient);
2222

2323
ngOnInit(): void {
24-
this.http.get(API_ENDPOINTS.AdminSettings).subscribe((res) => {
24+
this.http.get(API_ENDPOINTS.ADMIN.SETTINGS).subscribe((res) => {
2525
console.log(res);
2626
});
2727
}

0 commit comments

Comments
 (0)