Skip to content

Commit b7cffea

Browse files
feat: implement admin settings module with full CRUD functionality and integrate application configuration.
1 parent 2359593 commit b7cffea

11 files changed

Lines changed: 123 additions & 61 deletions

File tree

backend/package-lock.json

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

backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"@nestjs/mapped-types": "^2.1.0",
2727
"@nestjs/mongoose": "^11.0.4",
2828
"@nestjs/platform-express": "^11.0.1",
29+
"axios": "^1.13.4",
2930
"class-transformer": "^0.5.1",
3031
"class-validator": "^0.14.3",
3132
"mongoose": "^9.1.6",

backend/src/admin-settings/application/admin-settings.service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class AdminSettingsService {
1313
}
1414

1515
async updateSettings(
16-
settings: Omit<AdminSettings, 'id'>,
16+
settings: Partial<AdminSettings>,
1717
): Promise<AdminSettings> {
1818
return this.adminSettingsRepository.updateSettings(settings);
1919
}
@@ -23,4 +23,8 @@ export class AdminSettingsService {
2323
): Promise<AdminSettings> {
2424
return this.adminSettingsRepository.createSettings(settings);
2525
}
26+
27+
async deleteSettings(): Promise<boolean> {
28+
return this.adminSettingsRepository.deleteSettings();
29+
}
2630
}
Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
export class AdminSettings {
1+
import {
2+
IAdminLocation,
3+
IAdminSettings,
4+
IOwnerInfo,
5+
} from './interfaces/admin-settings.interface';
6+
7+
export class AdminSettings implements IAdminSettings {
28
constructor(
39
public readonly id: string,
4-
public readonly location: {
5-
address: string;
6-
latitude: number;
7-
longitude: number;
8-
},
9-
public readonly socialLinks: Map<string, string>,
10-
public readonly workHours: Map<string, string>, // e.g., 'Mon': '09:00-18:00'
11-
public readonly ownerInfo: {
12-
name: string;
13-
phoneNumber: string;
14-
},
10+
public readonly location: IAdminLocation,
11+
public readonly socialLinks: Record<string, string>,
12+
public readonly workHours: Record<string, string>,
13+
public readonly ownerInfo: IOwnerInfo,
1514
) {}
1615
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export interface IAdminLocation {
2+
address: string;
3+
latitude: number;
4+
longitude: number;
5+
}
6+
7+
export interface IOwnerInfo {
8+
name: string;
9+
phoneNumber: string;
10+
}
11+
12+
export interface IAdminSettings {
13+
id: string;
14+
location: IAdminLocation;
15+
socialLinks: Record<string, string>;
16+
workHours: Record<string, string>;
17+
ownerInfo: IOwnerInfo;
18+
}

backend/src/admin-settings/infrastructure/repositories/admin-settings.repository.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,39 @@ export class AdminSettingsRepository {
2020
}
2121

2222
async updateSettings(
23-
settings: Omit<AdminSettings, 'id'>,
23+
settings: Partial<AdminSettings>,
2424
): Promise<AdminSettings> {
25-
const doc = await this.settingsModel.findOneAndUpdate(
26-
{},
27-
{ $set: settings },
28-
{ new: true, upsert: true },
29-
);
25+
const doc = await this.settingsModel
26+
.findOneAndUpdate({}, { $set: settings }, { new: true, upsert: true })
27+
.exec();
3028
return this.toDomain(doc);
3129
}
30+
3231
async createSettings(
3332
settings: Omit<AdminSettings, 'id'>,
3433
): Promise<AdminSettings> {
3534
const doc = await this.settingsModel.create(settings);
3635
return this.toDomain(doc);
3736
}
3837

38+
async deleteSettings(): Promise<boolean> {
39+
const result = await this.settingsModel.deleteOne({}).exec();
40+
return result.deletedCount > 0;
41+
}
42+
3943
private toDomain(doc: AdminSettingsDocument): AdminSettings {
44+
const socialLinks = doc.socialLinks
45+
? (Object.fromEntries(doc.socialLinks) as Record<string, string>)
46+
: {};
47+
const workHours = doc.workHours
48+
? (Object.fromEntries(doc.workHours) as Record<string, string>)
49+
: {};
50+
4051
return new AdminSettings(
4152
doc._id.toString(),
4253
doc.location,
43-
doc.socialLinks,
44-
doc.workHours,
54+
socialLinks,
55+
workHours,
4556
doc.ownerInfo,
4657
);
4758
}

backend/src/admin-settings/infrastructure/schemas/admin-settings.schema.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ export type AdminSettingsDocument = HydratedDocument<AdminSettingsSchemaEntity>;
55

66
@Schema({ collection: 'admin_settings', timestamps: true })
77
export class AdminSettingsSchemaEntity {
8+
@Prop({ type: String, default: process.env.SETTINGS_ID })
9+
_id: string;
10+
811
@Prop({ type: Object, required: true })
912
location: {
1013
address: string;
Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Controller, Get, Body, Put, Post } from '@nestjs/common';
1+
import { Controller, Get, Body, Put, Post, Delete } from '@nestjs/common';
22
import { AdminSettingsService } from '../application/admin-settings.service';
33
import { AdminSettings } from '../domain/admin-settings.entity';
44
import { UpdateAdminSettingsDto } from './dto/update-admin-settings.dto';
@@ -17,40 +17,28 @@ export class AdminSettingsController {
1717
async createSettings(
1818
@Body() createAdminSettingsDto: CreateAdminSettingsDto,
1919
): Promise<AdminSettings> {
20-
const settings = {
21-
...createAdminSettingsDto,
22-
socialLinks: createAdminSettingsDto.socialLinks
23-
? new Map(Object.entries(createAdminSettingsDto.socialLinks))
24-
: new Map(),
25-
workHours: createAdminSettingsDto.workHours
26-
? new Map(Object.entries(createAdminSettingsDto.workHours))
27-
: new Map(),
28-
} as unknown as Omit<AdminSettings, 'id'>;
29-
30-
return this.adminSettingsService.createSettings(settings);
20+
// Cast to unknown then Omit to satisfy the compiler because DTO technically doesn't have 'id'
21+
// but the service expects Omit<AdminSettings, 'id'> which matches the structure of the DTO
22+
return this.adminSettingsService.createSettings(
23+
createAdminSettingsDto as unknown as Omit<AdminSettings, 'id'>,
24+
);
3125
}
3226

3327
@Put()
3428
async updateSettings(
3529
@Body() updateAdminSettingsDto: UpdateAdminSettingsDto,
3630
): Promise<AdminSettings> {
37-
const settings = {
38-
...updateAdminSettingsDto,
39-
socialLinks: updateAdminSettingsDto.socialLinks
40-
? new Map(Object.entries(updateAdminSettingsDto.socialLinks))
41-
: undefined,
42-
workHours: updateAdminSettingsDto.workHours
43-
? new Map(Object.entries(updateAdminSettingsDto.workHours))
44-
: undefined,
45-
};
46-
47-
// Remove undefined keys to avoid overriding with undefined
31+
// Clean up undefined values
32+
const settings = { ...updateAdminSettingsDto };
4833
Object.keys(settings).forEach(
4934
(key) => settings[key] === undefined && delete settings[key],
5035
);
5136

52-
return this.adminSettingsService.updateSettings(
53-
settings as unknown as Omit<AdminSettings, 'id'>,
54-
);
37+
return this.adminSettingsService.updateSettings(settings);
38+
}
39+
40+
@Delete()
41+
async deleteSettings(): Promise<boolean> {
42+
return this.adminSettingsService.deleteSettings();
5543
}
5644
}

backend/src/admin-settings/presentation/dto/create-admin-settings.dto.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ import {
77
ValidateNested,
88
IsObject,
99
} from 'class-validator';
10+
import {
11+
IAdminLocation,
12+
IOwnerInfo,
13+
} from '../../domain/interfaces/admin-settings.interface';
1014

11-
class LocationDto {
15+
class LocationDto implements IAdminLocation {
1216
@IsString()
1317
@IsNotEmpty()
1418
address: string;
@@ -20,7 +24,7 @@ class LocationDto {
2024
longitude: number;
2125
}
2226

23-
class OwnerInfoDto {
27+
class OwnerInfoDto implements IOwnerInfo {
2428
@IsString()
2529
@IsNotEmpty()
2630
name: string;

backend/src/common/config/app-config.service.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,7 @@ export class AppConfigService {
8585
''
8686
);
8787
}
88+
get settingsId(): string {
89+
return this.configService.get<string>('SETTINGS_ID', { infer: true }) ?? '';
90+
}
8891
}

0 commit comments

Comments
 (0)