From 600aba448f18ff05cc00df5af3497267d9f3fc2f Mon Sep 17 00:00:00 2001 From: Usman Adamu Date: Sun, 28 Sep 2025 14:47:05 +0000 Subject: [PATCH 1/2] feat: add UserProfile module with profile update and avatar upload endpoints --- backend/src/app.module.ts | 18 +------------ .../user-profile/dto/update-profile.dto.ts | 19 ++++++++++++++ .../entities/user-profile.entity.ts | 7 +++++ .../user-profile/user-profile.controller.ts | 26 +++++++++++++++++++ .../src/user-profile/user-profile.module.ts | 9 +++++++ .../src/user-profile/user-profile.service.ts | 20 ++++++++++++++ 6 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 backend/src/user-profile/dto/update-profile.dto.ts create mode 100644 backend/src/user-profile/entities/user-profile.entity.ts create mode 100644 backend/src/user-profile/user-profile.controller.ts create mode 100644 backend/src/user-profile/user-profile.module.ts create mode 100644 backend/src/user-profile/user-profile.service.ts diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 0c2a507f5..1e27a2ae0 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -14,23 +14,7 @@ import { Department } from './departments/department.entity'; isGlobal: true, }), TypeOrmModule.forRootAsync({ - imports: [ConfigModule, - // --- ADD THIS CONFIGURATION --- - I18nModule.forRoot({ - fallbackLanguage: 'en', - loaderOptions: { - path: path.join(__dirname, '/i18n/'), // Directory for translation files - watch: true, // Watch for changes in translation files - }, - resolvers: [ - // Order matters: checks query param, then header, then browser settings - new QueryResolver(['lang', 'l']), - new HeaderResolver(['x-custom-lang-header']), - AcceptLanguageResolver, // Standard 'Accept-Language' header - ], - }), - // --- END OF CONFIGURATION --- - ],], + imports: [ConfigModule], useFactory: (configService: ConfigService) => ({ type: 'postgres', host: configService.get('DB_HOST', 'localhost'), diff --git a/backend/src/user-profile/dto/update-profile.dto.ts b/backend/src/user-profile/dto/update-profile.dto.ts new file mode 100644 index 000000000..732dedf3d --- /dev/null +++ b/backend/src/user-profile/dto/update-profile.dto.ts @@ -0,0 +1,19 @@ +import { IsOptional, IsString, IsEmail, IsPhoneNumber } from 'class-validator'; + +export class UpdateProfileDto { + @IsOptional() + @IsString() + name?: string; + + @IsOptional() + @IsEmail() + email?: string; + + @IsOptional() + @IsPhoneNumber() + phone?: string; + + @IsOptional() + @IsString() + avatarUrl?: string; +} diff --git a/backend/src/user-profile/entities/user-profile.entity.ts b/backend/src/user-profile/entities/user-profile.entity.ts new file mode 100644 index 000000000..4f28dd19d --- /dev/null +++ b/backend/src/user-profile/entities/user-profile.entity.ts @@ -0,0 +1,7 @@ +export class UserProfile { + id: string; + name: string; + email: string; + phone: string; + avatarUrl: string; +} diff --git a/backend/src/user-profile/user-profile.controller.ts b/backend/src/user-profile/user-profile.controller.ts new file mode 100644 index 000000000..551d3b0fb --- /dev/null +++ b/backend/src/user-profile/user-profile.controller.ts @@ -0,0 +1,26 @@ +import { Controller, Get, Put, Body, UseInterceptors, UploadedFile, Post, Req } from '@nestjs/common'; +import { FileInterceptor } from '@nestjs/platform-express'; +import { UpdateProfileDto } from './dto/update-profile.dto'; +import { UserProfileService } from './user-profile.service'; + +@Controller('profile') +export class UserProfileController { + constructor(private readonly userProfileService: UserProfileService) {} + + @Get() + getProfile(@Req() req) { + // TODO: Get user from request + return this.userProfileService.getProfile(req.user.id); + } + + @Put() + updateProfile(@Req() req, @Body() updateProfileDto: UpdateProfileDto) { + return this.userProfileService.updateProfile(req.user.id, updateProfileDto); + } + + @Post('avatar') + @UseInterceptors(FileInterceptor('file')) + uploadAvatar(@Req() req, @UploadedFile() file: any) { + return this.userProfileService.uploadAvatar(req.user.id, file); + } +} diff --git a/backend/src/user-profile/user-profile.module.ts b/backend/src/user-profile/user-profile.module.ts new file mode 100644 index 000000000..23c3566e5 --- /dev/null +++ b/backend/src/user-profile/user-profile.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { UserProfileController } from './user-profile.controller'; +import { UserProfileService } from './user-profile.service'; + +@Module({ + controllers: [UserProfileController], + providers: [UserProfileService], +}) +export class UserProfileModule {} diff --git a/backend/src/user-profile/user-profile.service.ts b/backend/src/user-profile/user-profile.service.ts new file mode 100644 index 000000000..e5fe583ae --- /dev/null +++ b/backend/src/user-profile/user-profile.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@nestjs/common'; +import { UpdateProfileDto } from './dto/update-profile.dto'; + +@Injectable() +export class UserProfileService { + async getProfile(userId: string) { + // TODO: Fetch user profile from DB + return {}; + } + + async updateProfile(userId: string, updateProfileDto: UpdateProfileDto) { + // TODO: Update user profile in DB + return {}; + } + + async uploadAvatar(userId: string, file: any) { + // TODO: Save avatar file and update user profile + return {}; + } +} From bf07fda4df53e2fa64b4c726a38bf0ae58f1f224 Mon Sep 17 00:00:00 2001 From: Usman Adamu Date: Sun, 28 Sep 2025 14:52:04 +0000 Subject: [PATCH 2/2] feat: add Settings module with CRUD endpoints for company-wide system settings --- backend/src/app.module.ts | 6 ++- .../src/settings/dto/create-settings.dto.ts | 12 ++++++ .../src/settings/dto/update-settings.dto.ts | 15 ++++++++ .../src/settings/entities/settings.entity.ts | 6 +++ backend/src/settings/settings.controller.ts | 34 +++++++++++++++++ backend/src/settings/settings.module.ts | 9 +++++ backend/src/settings/settings.service.ts | 38 +++++++++++++++++++ 7 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 backend/src/settings/dto/create-settings.dto.ts create mode 100644 backend/src/settings/dto/update-settings.dto.ts create mode 100644 backend/src/settings/entities/settings.entity.ts create mode 100644 backend/src/settings/settings.controller.ts create mode 100644 backend/src/settings/settings.module.ts create mode 100644 backend/src/settings/settings.service.ts diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 1e27a2ae0..1c9d82565 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -4,6 +4,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { AssetCategoriesModule } from './asset-categories/asset-categories.module'; +import { SettingsModule } from './settings/settings.module'; import { AssetCategory } from './asset-categories/asset-category.entity'; import { DepartmentsModule } from './departments/departments.module'; import { Department } from './departments/department.entity'; @@ -27,8 +28,9 @@ import { Department } from './departments/department.entity'; }), inject: [ConfigService], }), - AssetCategoriesModule, - DepartmentsModule, + AssetCategoriesModule, + DepartmentsModule, + SettingsModule, ], controllers: [AppController], providers: [AppService], diff --git a/backend/src/settings/dto/create-settings.dto.ts b/backend/src/settings/dto/create-settings.dto.ts new file mode 100644 index 000000000..55b81ba2b --- /dev/null +++ b/backend/src/settings/dto/create-settings.dto.ts @@ -0,0 +1,12 @@ +import { IsString } from 'class-validator'; + +export class CreateSettingsDto { + @IsString() + defaultCurrency: string; + + @IsString() + timezone: string; + + @IsString() + depreciationMethod: string; +} diff --git a/backend/src/settings/dto/update-settings.dto.ts b/backend/src/settings/dto/update-settings.dto.ts new file mode 100644 index 000000000..52de23198 --- /dev/null +++ b/backend/src/settings/dto/update-settings.dto.ts @@ -0,0 +1,15 @@ +import { IsOptional, IsString } from 'class-validator'; + +export class UpdateSettingsDto { + @IsOptional() + @IsString() + defaultCurrency?: string; + + @IsOptional() + @IsString() + timezone?: string; + + @IsOptional() + @IsString() + depreciationMethod?: string; +} diff --git a/backend/src/settings/entities/settings.entity.ts b/backend/src/settings/entities/settings.entity.ts new file mode 100644 index 000000000..4d9648a91 --- /dev/null +++ b/backend/src/settings/entities/settings.entity.ts @@ -0,0 +1,6 @@ +export class Settings { + id: string; + defaultCurrency: string; + timezone: string; + depreciationMethod: string; +} diff --git a/backend/src/settings/settings.controller.ts b/backend/src/settings/settings.controller.ts new file mode 100644 index 000000000..f5551f138 --- /dev/null +++ b/backend/src/settings/settings.controller.ts @@ -0,0 +1,34 @@ +import { Controller, Get, Post, Put, Delete, Body, Param } from '@nestjs/common'; +import { SettingsService } from './settings.service'; +import { CreateSettingsDto } from './dto/create-settings.dto'; +import { UpdateSettingsDto } from './dto/update-settings.dto'; + +@Controller('settings') +export class SettingsController { + constructor(private readonly settingsService: SettingsService) {} + + @Get() + findAll() { + return this.settingsService.findAll(); + } + + @Get(':id') + findOne(@Param('id') id: string) { + return this.settingsService.findOne(id); + } + + @Post() + create(@Body() createSettingsDto: CreateSettingsDto) { + return this.settingsService.create(createSettingsDto); + } + + @Put(':id') + update(@Param('id') id: string, @Body() updateSettingsDto: UpdateSettingsDto) { + return this.settingsService.update(id, updateSettingsDto); + } + + @Delete(':id') + remove(@Param('id') id: string) { + return this.settingsService.remove(id); + } +} diff --git a/backend/src/settings/settings.module.ts b/backend/src/settings/settings.module.ts new file mode 100644 index 000000000..63e54df50 --- /dev/null +++ b/backend/src/settings/settings.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { SettingsController } from './settings.controller'; +import { SettingsService } from './settings.service'; + +@Module({ + controllers: [SettingsController], + providers: [SettingsService], +}) +export class SettingsModule {} diff --git a/backend/src/settings/settings.service.ts b/backend/src/settings/settings.service.ts new file mode 100644 index 000000000..e21e1c5c0 --- /dev/null +++ b/backend/src/settings/settings.service.ts @@ -0,0 +1,38 @@ +import { Injectable } from '@nestjs/common'; +import { CreateSettingsDto } from './dto/create-settings.dto'; +import { UpdateSettingsDto } from './dto/update-settings.dto'; + +@Injectable() +export class SettingsService { + private settings = []; + + findAll() { + return this.settings; + } + + findOne(id: string) { + return this.settings.find(s => s.id === id); + } + + create(createSettingsDto: CreateSettingsDto) { + const newSetting = { id: Date.now().toString(), ...createSettingsDto }; + this.settings.push(newSetting); + return newSetting; + } + + update(id: string, updateSettingsDto: UpdateSettingsDto) { + const setting = this.findOne(id); + if (setting) { + Object.assign(setting, updateSettingsDto); + } + return setting; + } + + remove(id: string) { + const idx = this.settings.findIndex(s => s.id === id); + if (idx > -1) { + return this.settings.splice(idx, 1); + } + return null; + } +}