Skip to content

Commit 944d29c

Browse files
feat: implement full-stack veil product management including frontend UI components and backend API with data models, schemas, and repositories.
1 parent 9d3c778 commit 944d29c

12 files changed

Lines changed: 223 additions & 133 deletions

File tree

backend/src/app.module.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,16 @@ import { PartnershipModule } from './partnership/partnership.module';
3131
InventoryModule,
3232
PartnershipModule,
3333
ServeStaticModule.forRoot({
34-
rootPath: join(__dirname, '..', 'uploads'),
34+
rootPath: join(process.cwd(), 'uploads'),
3535
serveRoot: '/uploads',
3636
}),
3737
],
3838
controllers: [AppController],
3939
providers: [AppService],
4040
})
41-
export class AppModule {}
41+
export class AppModule {
42+
constructor() {
43+
console.log(join(process.cwd(), 'uploads'));
44+
console.log(process.cwd());
45+
}
46+
}

backend/src/main.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ async function bootstrap() {
1212
whitelist: true,
1313
forbidNonWhitelisted: true,
1414
transform: true,
15+
transformOptions: { enableImplicitConversion: true },
1516
}),
1617
);
1718
app.enableCors({
Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,56 @@
1+
export interface VeilProps {
2+
id: string;
3+
name: string;
4+
description: string;
5+
price: number;
6+
rentalPrice: number;
7+
images: string[];
8+
category: string;
9+
isAvailable?: boolean;
10+
sku?: string;
11+
silhouette?: string;
12+
neckline?: string;
13+
fabric?: string;
14+
trainLength?: string;
15+
stock?: number;
16+
createdAt?: Date;
17+
updatedAt?: Date;
18+
}
19+
120
export class Veil {
2-
constructor(
3-
public readonly id: string,
4-
public readonly name: string,
5-
public readonly description: string,
6-
public readonly price: number,
7-
public readonly rentalPrice: number,
8-
public readonly images: string[],
9-
public readonly category: string,
10-
public readonly isAvailable: boolean = true,
11-
public readonly sku?: string,
12-
public readonly silhouette?: string,
13-
public readonly neckline?: string,
14-
public readonly fabric?: string,
15-
public readonly trainLength?: string,
16-
public readonly stock?: number,
17-
public readonly createdAt: Date = new Date(),
18-
) {}
21+
public readonly id: string;
22+
public readonly name: string;
23+
public readonly description: string;
24+
public readonly price: number;
25+
public readonly rentalPrice: number;
26+
public readonly images: string[];
27+
public readonly category: string;
28+
public readonly isAvailable: boolean;
29+
public readonly sku?: string;
30+
public readonly silhouette?: string;
31+
public readonly neckline?: string;
32+
public readonly fabric?: string;
33+
public readonly trainLength?: string;
34+
public readonly stock?: number;
35+
public readonly createdAt: Date;
36+
public readonly updatedAt: Date;
37+
38+
constructor(props: VeilProps) {
39+
this.id = props.id;
40+
this.name = props.name;
41+
this.description = props.description;
42+
this.price = props.price;
43+
this.rentalPrice = props.rentalPrice;
44+
this.images = props.images;
45+
this.category = props.category;
46+
this.isAvailable = props.isAvailable ?? true;
47+
this.sku = props.sku;
48+
this.silhouette = props.silhouette;
49+
this.neckline = props.neckline;
50+
this.fabric = props.fabric;
51+
this.trainLength = props.trainLength;
52+
this.stock = props.stock;
53+
this.createdAt = props.createdAt ?? new Date();
54+
this.updatedAt = props.updatedAt ?? new Date();
55+
}
1956
}

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

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,42 @@ export class VeilRepository {
5151
}
5252

5353
private toDomain(doc: VeilDocument): Veil {
54-
const d = doc as any;
55-
return new Veil(
56-
d._id.toString(),
57-
d.name,
58-
d.description,
59-
d.price,
60-
d.rentalPrice,
61-
d.images,
62-
d.category,
63-
d.isAvailable,
64-
d.createdAt,
65-
);
54+
const {
55+
_id,
56+
name,
57+
description,
58+
price,
59+
rentalPrice,
60+
images,
61+
category,
62+
isAvailable,
63+
sku,
64+
silhouette,
65+
neckline,
66+
fabric,
67+
trainLength,
68+
stock,
69+
createdAt,
70+
updatedAt,
71+
} = doc as VeilDocument;
72+
73+
return new Veil({
74+
id: _id.toString(),
75+
name,
76+
description,
77+
price,
78+
rentalPrice,
79+
images,
80+
category,
81+
isAvailable,
82+
sku,
83+
silhouette,
84+
neckline,
85+
fabric,
86+
trainLength,
87+
stock,
88+
createdAt,
89+
updatedAt,
90+
});
6691
}
6792
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ export class VeilSchemaEntity {
4343

4444
@Prop()
4545
stock: number;
46+
47+
@Prop({ default: Date.now })
48+
createdAt: Date;
49+
50+
@Prop({ default: Date.now })
51+
updatedAt: Date;
4652
}
4753

4854
export const VeilSchema = SchemaFactory.createForClass(VeilSchemaEntity);

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
IsOptional,
77
IsBoolean,
88
} from 'class-validator';
9-
import { Type } from 'class-transformer';
9+
import { Transform, Type } from 'class-transformer';
1010

1111
export class CreateVeilDto {
1212
@IsString()
@@ -28,8 +28,12 @@ export class CreateVeilDto {
2828
rentalPrice: number;
2929

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

3539
@IsString()
@@ -38,6 +42,7 @@ export class CreateVeilDto {
3842

3943
@IsBoolean()
4044
@IsOptional()
45+
@Transform(({ value }: { value: boolean | string }) => value === 'true')
4146
isAvailable?: boolean;
4247

4348
@IsString()
@@ -53,15 +58,12 @@ export class CreateVeilDto {
5358
neckline?: string;
5459

5560
@IsString()
56-
@IsOptional()
5761
fabric?: string;
5862

5963
@IsString()
60-
@IsOptional()
6164
trainLength?: string;
6265

6366
@IsNumber()
64-
@IsOptional()
6567
@Type(() => Number)
6668
stock?: number;
6769
}
Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
export const environment = {
1+
export const environment: Environment = {
22
production: false,
3-
apiUrl: 'http://localhost:4100',
4-
telegramBotName: 'test_bot'
3+
apiUrl: "http://localhost:4100",
4+
telegramBotName: "test_bot",
55
};
6+
7+
export interface Environment {
8+
production: boolean;
9+
apiUrl: string;
10+
telegramBotName: string;
11+
}

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ export interface Veil {
33
name: string;
44
description?: string;
55
price: number;
6-
rentalPrice?: number;
7-
images: string[];
6+
rentalPrice: number;
7+
images?: string[];
88
category: string;
99
isAvailable: boolean;
1010
sku: string;
@@ -16,12 +16,11 @@ export interface Veil {
1616
}
1717

1818
export const veilFormData: Veil = {
19-
id: "",
2019
name: "Just Name of Veil",
2120
sku: "lorem",
2221
price: 2500,
22+
rentalPrice: 1000,
2323
stock: 10,
24-
images: ["image_url"],
2524
silhouette: "lorem",
2625
neckline: "lorem",
2726
fabric: "lorem",
@@ -32,12 +31,11 @@ export const veilFormData: Veil = {
3231
};
3332

3433
export const resetVeilData: Veil = {
35-
id: "",
3634
name: "",
3735
sku: "",
3836
price: 0,
37+
rentalPrice: 0,
3938
stock: 0,
40-
images: [],
4139
silhouette: "",
4240
neckline: "",
4341
fabric: "",

frontend/src/pages/veil/ui/veil-card/veil-card.component.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,37 @@
1-
import { CommonModule } from '@angular/common';
2-
import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';
3-
import { Veil } from '@features/veil/model/veil.data';
1+
import { CommonModule } from "@angular/common";
2+
import {
3+
ChangeDetectionStrategy,
4+
Component,
5+
inject,
6+
input,
7+
OnInit,
8+
output,
9+
signal,
10+
} from "@angular/core";
11+
import { environment } from "@environments/environment";
12+
import { Veil } from "@features/veil/model/veil.data";
413

514
@Component({
6-
selector: 'app-veil-card',
15+
selector: "app-veil-card",
716
standalone: true,
817
imports: [CommonModule],
918
changeDetection: ChangeDetectionStrategy.OnPush,
10-
templateUrl: './veil-card.component.html',
11-
styleUrl: './veil-card.component.scss'
19+
templateUrl: "./veil-card.component.html",
20+
styleUrl: "./veil-card.component.scss",
1221
})
13-
export class VeilCardComponent {
22+
export class VeilCardComponent implements OnInit {
1423
veil = input.required<Veil>();
1524
index = input<number>(0);
1625
edit = output<Veil>();
1726
viewImage = output<string>();
27+
env = signal(environment);
28+
29+
ngOnInit(): void {}
1830

1931
safeImageUrl(): string {
20-
return this.veil().images && this.veil().images.length > 0 ? this.veil().images[0] : 'assets/placeholder-gown.png';
32+
return this.veil().images && this.veil().images.length > 0
33+
? this.env().apiUrl + this.veil().images[0]
34+
: "assets/placeholder-gown.png";
2135
}
2236

2337
onEdit() {

0 commit comments

Comments
 (0)