Skip to content

Commit 5e276d4

Browse files
feat: Implement treatments management module with full CRUD operations and image handling across frontend and backend.
1 parent 90c5e79 commit 5e276d4

18 files changed

Lines changed: 100 additions & 58 deletions

File tree

backend/src/common/utils/file-system.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// infrastructure/utils/file-system.util.ts
22
import * as fs from 'fs';
33
import { join } from 'path';
4+
import { promisify } from 'util';
45

56
export function fileDelete(relativeFilePath: string): boolean {
67
try {
@@ -14,3 +15,19 @@ export function fileDelete(relativeFilePath: string): boolean {
1415
return false;
1516
}
1617
}
18+
19+
export const unlinkAsync = promisify(fs.unlink);
20+
21+
export async function deleteFileSafe(relativePath: string): Promise<boolean> {
22+
if (!relativePath) return false;
23+
24+
try {
25+
const absolutePath = join(process.cwd(), relativePath);
26+
27+
// await fs.access(absolutePath);
28+
await unlinkAsync(absolutePath);
29+
return true;
30+
} catch (_) {
31+
return false;
32+
}
33+
};

backend/src/modules/treatments/application/treatments.service.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Injectable } from '@nestjs/common';
22
import { Treatments } from '../domain/treatments.entity';
33
import { TreatmentsRepository } from '../infrastructure/repositories/treatments.repository';
4+
import { deleteFileSafe } from '@common/utils';
45

56
@Injectable()
67
export class TreatmentsService {
@@ -27,13 +28,20 @@ export class TreatmentsService {
2728
id: string,
2829
updateTreatmentsDto: Partial<Treatments>,
2930
): Promise<Treatments> {
31+
const existing = await this.treatmentsRepository.findById(id);
32+
if (!existing) {
33+
throw new Error(`Service with ID ${id} not found`);
34+
}
3035
const updated = await this.treatmentsRepository.update(
3136
id,
3237
updateTreatmentsDto,
3338
);
3439
if (!updated) {
3540
throw new Error(`Service with ID ${id} not found`);
3641
}
42+
if (updated?.imageUrl && existing?.imageUrl) {
43+
await deleteFileSafe(existing.imageUrl);
44+
}
3745
return updated;
3846
}
3947

@@ -42,5 +50,16 @@ export class TreatmentsService {
4250
if (!deleted) {
4351
throw new Error(`Service with ID ${id} not found`);
4452
}
53+
if (deleted?.imageUrl) {
54+
await deleteFileSafe(deleted.imageUrl);
55+
}
56+
}
57+
58+
private async deleteFile(filePath: string): Promise<void> {
59+
try {
60+
await deleteFileSafe(filePath);
61+
} catch (error) {
62+
console.error('Error deleting file:', error);
63+
}
4564
}
4665
}

backend/src/modules/treatments/infrastructure/repositories/treatments.repository.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ export class TreatmentsRepository {
4545
return doc ? this.toDomain(doc) : null;
4646
}
4747

48-
async delete(id: string): Promise<boolean> {
48+
async delete(id: string): Promise<Treatments | null> {
4949
const result = await this.treatmentsModel.findByIdAndDelete(id).exec();
50-
return !!result;
50+
return result ? this.toDomain(result) : null;
5151
}
5252

5353
private toDomain(doc: TreatmentsDocument): Treatments {

backend/src/modules/treatments/presentation/dto/create-treatments.dto.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,6 @@ export class CreateServiceDto {
4949
@IsString()
5050
category: string;
5151

52-
@IsOptional()
53-
image: string;
54-
5552
@IsOptional()
5653
@IsString()
5754
imageUrl: string;
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
import { PartialType } from '@nestjs/mapped-types';
22
import { CreateServiceDto } from './create-treatments.dto';
3-
import { Type } from 'class-transformer';
43

54
export class UpdateServiceDto extends PartialType(CreateServiceDto) {
6-
@Type(() => Date)
7-
createdAt?: Date;
8-
9-
@Type(() => Date)
10-
updatedAt?: Date;
115
}

backend/src/modules/treatments/presentation/treatments.controller.ts

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
1+
import { CreateServiceDto as CreateTreatmentDto, Treatments, TreatmentsService, UpdateServiceDto as UpdateTreatmentDto } from '@modules/treatments';
12
import {
3+
Body,
24
Controller,
5+
Delete,
36
Get,
7+
Param,
48
Post,
5-
Body,
69
Put,
7-
Param,
8-
Delete,
910
UploadedFiles,
1011
UseInterceptors,
1112
} from '@nestjs/common';
1213
import { FilesInterceptor } from '@nestjs/platform-express';
1314
import { diskStorage } from 'multer';
1415
import { extname } from 'path';
15-
import { TreatmentsService } from '@modules/treatments';
16-
import { Treatments } from '@modules/treatments';
17-
import { CreateServiceDto as CreateTreatmentDto } from '@modules/treatments';
18-
import { UpdateServiceDto as UpdateTreatmentDto } from '@modules/treatments';
19-
import { title } from 'process';
20-
import { deleteProperties } from '@common/utils';
2116

2217
@Controller('treatments')
2318
export class TreatmentsController {
@@ -55,17 +50,17 @@ export class TreatmentsController {
5550
files && files.length > 0
5651
? `/uploads/treatments/${files[0].filename}`
5752
: '';
58-
59-
const treatment = {
60-
...createTreatmentDto,
61-
imageUrl: imagePath,
62-
} as unknown as Omit<Treatments, 'id' | 'createdAt'>;
63-
return this.treatmentsService.create(treatment);
53+
if (imagePath) {
54+
createTreatmentDto.imageUrl = imagePath;
55+
}
56+
return this.treatmentsService.create(
57+
createTreatmentDto as unknown as Omit<Treatments, 'id' | 'createdAt'>,
58+
);
6459
}
6560

6661
@Put(':id')
6762
@UseInterceptors(
68-
FilesInterceptor('files', 10, {
63+
FilesInterceptor('image', 10, {
6964
storage: diskStorage({
7065
destination: './uploads/treatments',
7166
filename: (req, file, callback) => {
@@ -87,17 +82,13 @@ export class TreatmentsController {
8782
? `/uploads/treatments/${files[0].filename}`
8883
: null;
8984

90-
const treatmentData = {
91-
...updateTreatmentDto,
92-
};
93-
9485
if (imagePath) {
95-
(treatmentData as any).imageUrl = imagePath;
86+
updateTreatmentDto.imageUrl = imagePath;
9687
}
9788

9889
return this.treatmentsService.update(
9990
id,
100-
treatmentData as unknown as Partial<Treatments>,
91+
updateTreatmentDto as unknown as Partial<Treatments>,
10192
);
10293
}
10394

frontend/angular.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
"tsConfig": "tsconfig.json",
2828
"polyfills": [
2929
"@angular/localize/init"
30+
],
31+
"assets": [
32+
"./public"
3033
]
3134
},
3235
"configurations": {
2.5 KB
Loading
2.75 KB
Loading
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export enum TreatmentCategory {
2+
INJECTIONS = "injections",
3+
LASER = "laser",
4+
FACIALS = "facials",
5+
PEELS = "peels",
6+
BODY = "body",
7+
MASSAGE = "massage",
8+
HAIR = "hair",
9+
NAIL = "nail",
10+
MAKEUP = "makeup",
11+
BRIDAL = "bridal",
12+
SKIN = "skin",
13+
}
14+
15+
export enum TreatmentNeckline {
16+
SWEETHEART = "sweetheart",
17+
"V-NECK" = "v-neck",
18+
"HIGH-NECK" = "high-neck",
19+
"LOW-NECK" = "low-neck",
20+
ILLUSION = "illusion",
21+
SQUARE = "square",
22+
ASYMMETRIC = "asymmetric",
23+
PLUNGING = "plunging",
24+
}

0 commit comments

Comments
 (0)