تواجهني مشكلة غريبة لم افهم سببها والتي هي لما ازور صفحة freelancer.html الاحظ ان صفحة تبقى loading profile.... الى ما لا نهاية حتى اعدل سطر كود ويعيد Angular بناء سيرفر ثم يظهر profile ويوجد مشكلة اخرى والتي هي لما احاول اعدل بيانات واضغط ع زر save يبقى saving... ايضا ال ما لا نهاية
import{Component, inject,OnInit} from '@angular/core';import{FormsModule} from '@angular/forms';import{FreelancerService,IFreelancer,IUpdateFreelancer,} from '../../../../core/services/freelancer.service';import{AuthState} from '../../../../core/states/auth.state';import{JobTitle,Skills} from '../../../../core/enums/freelancer.enum';
type ProfileTab='overview'|'projects'|'reviews';interfaceIEditFormErrors{
jobTitle?: string;
bio?: string;
hourlyRate?: string;
skills?: string;}const MAX_SKILLS =10;const MAX_BIO_LENGTH =600;const MAX_HOURLY_RATE =1000;@Component({
selector:'app-freelancer-profile',
standalone:true,
imports:[FormsModule],
templateUrl:'./freelancer.html',
styleUrl:'./freelancer.scss',})exportclassFreelancerProfileimplementsOnInit{private readonly freelancerService = inject(FreelancerService);private readonly authState = inject(AuthState);
readonly user =this.authState.user;
freelancer:IFreelancer|null=null;
activeTab:ProfileTab='overview';
loading =true;
errorMessage ='';
isEditMode =false;
isUpdating =false;
updateMessage ='';
updateErrorMessage ='';
readonly maxSkills = MAX_SKILLS;
readonly maxBioLength = MAX_BIO_LENGTH;
readonly maxHourlyRate = MAX_HOURLY_RATE;
editForm:IUpdateFreelancer={
jobTitle:undefined,
bio:'',
skills:[],
hourlyRate:0,};
formErrors:IEditFormErrors={};
readonly jobTitles =Object.values(JobTitle);
readonly availableSkills =Object.values(Skills);
ngOnInit(){this.loadProfile();}
loadProfile(){this.loading =true;this.errorMessage ='';this.freelancerService.profile().subscribe({
next:(response: any)=>{this.freelancer = response.data;this.loading =false;},
error:(error)=>{
console.error('Failed to load freelancer profile:', error);this.errorMessage = error?.error?.message ||'Failed to load profile. Please try again.';this.loading =false;},});}
setTab(tab:ProfileTab){this.activeTab = tab;}
openEditMode(){if(!this.freelancer){return;}this.editForm ={
jobTitle:this.freelancer.jobTitle,
bio:this.freelancer.bio ??'',
skills:[...this.freelancer.skills],
hourlyRate:this.freelancer.hourlyRate,};this.formErrors ={};this.updateMessage ='';this.updateErrorMessage ='';this.isEditMode =true;}
cancelEdit(){if(this.isUpdating){return;}this.isEditMode =false;this.formErrors ={};this.updateMessage ='';this.updateErrorMessage ='';}
toggleSkill(skill:Skills){const currentSkills =this.editForm.skills ??[];if(skill ===Skills.NO_SKILLS){this.editForm.skills = currentSkills.includes(skill)?[]:[skill];this.formErrors.skills =undefined;return;}if(currentSkills.includes(Skills.NO_SKILLS)){this.editForm.skills = currentSkills.filter((currentSkill)=> currentSkill !==Skills.NO_SKILLS,);}const skills =this.editForm.skills ??[];if(skills.includes(skill)){this.editForm.skills = skills.filter((currentSkill)=> currentSkill !== skill);this.formErrors.skills =undefined;return;}if(skills.length >=this.maxSkills){this.formErrors.skills =`You can select up to ${this.maxSkills} skills.`;return;}this.editForm.skills =[...skills, skill];this.formErrors.skills =undefined;}
isSkillSelected(skill:Skills){returnthis.editForm.skills?.includes(skill)??false;}
isSkillDisabled(skill:Skills){const currentSkills =this.editForm.skills ??[];if(currentSkills.includes(Skills.NO_SKILLS)){return skill !==Skills.NO_SKILLS;}return currentSkills.length >=this.maxSkills &&!currentSkills.includes(skill);}private validateForm(): boolean {const errors:IEditFormErrors={};if(!this.editForm.jobTitle){
errors.jobTitle ='Please select a job title.';}const bioLength =this.editForm.bio?.trim().length ??0;if(bioLength >this.maxBioLength){
errors.bio =`Bio must not exceed ${this.maxBioLength} characters.`;}const rate =Number(this.editForm.hourlyRate);if(this.editForm.hourlyRate ===null||this.editForm.hourlyRate ===undefined||Number.isNaN(rate)){
errors.hourlyRate ='Hourly rate is required.';}elseif(rate <=0){
errors.hourlyRate ='Hourly rate must be greater than 0.';}elseif(rate >this.maxHourlyRate){
errors.hourlyRate =`Hourly rate must not exceed $${this.maxHourlyRate}.`;}const skillsCount =this.editForm.skills?.length ??0;if(skillsCount ===0){
errors.skills ='Select at least one skill.';}elseif(skillsCount >this.maxSkills){
errors.skills =`You can select up to ${this.maxSkills} skills.`;}this.formErrors = errors;returnObject.keys(errors).length ===0;}
updateProfile(){if(!this.freelancer ||this.isUpdating){return;}this.updateMessage ='';this.updateErrorMessage ='';if(!this.validateForm()){this.updateErrorMessage ='Please fix the errors below before saving.';return;}this.isUpdating =true;const payload:IUpdateFreelancer={
jobTitle:this.editForm.jobTitle,
bio:this.editForm.bio?.trim(),
skills:this.editForm.skills ??[],
hourlyRate:Number(this.editForm.hourlyRate),};this.freelancerService.update(payload).subscribe({
next:(response: any)=>{this.freelancer = response.data ??{...this.freelancer!,...payload,};this.updateMessage ='Profile updated successfully.';this.isUpdating =false;this.isEditMode =false;},
error:(error)=>{
console.error('Failed to update freelancer profile:', error);this.updateErrorMessage =
error?.error?.message ||'Failed to save changes. Please try again.';this.isUpdating =false;},});}}
import{BadRequestException,Inject,Injectable,NotFoundException,} from '@nestjs/common';import{UpdateFreelancerDto} from 'src/modules/freelancers/dto/update-freelancer.dto';import{Skills} from 'src/common/enums/freelancer.enum';import{AuthUser} from 'src/types/auth-user.interface';import type {IFreelancerRepository} from 'src/modules/freelancers/repository/interface/freelancer.interface';import{ assertOwnerOrAdmin, response } from 'src/common/libs/helpers/helpers';import{ messages } from 'src/common/libs/messages';@Injectable()exportclassFreelancerService{constructor(@Inject('IFreelancerRepository')private readonly freelancerRepository:IFreelancerRepository,){}async profile(freelancerId: string){const profile =awaitthis.freelancerRepository.findOne({ freelancerId });if(!profile){thrownewNotFoundException(messages.freelancer.notFound);}return response(profile,null);}async update(authUser:AuthUser, data:UpdateFreelancerDto){const profile =awaitthis.freelancerRepository.findOne({
freelancerId: authUser._id,});if(!profile){thrownewNotFoundException(messages.freelancer.notFound);}
assertOwnerOrAdmin({
ownerId: profile.freelancerId,
authUser,
message: messages.freelancer.forbidden,});const hasData =Object.values(data).some((value)=> value !==undefined);if(!hasData){thrownewBadRequestException(messages.freelancer.update.invalidSkills);}if(data.skills){if(data.skills.includes(Skills.NO_SKILLS)&& data.skills.length >1){thrownewBadRequestException(messages.freelancer.update.invalidSkills);}if(data.skills.length ===0){
data.skills =[Skills.NO_SKILLS];}}const updatedProfile =awaitthis.freelancerRepository.update({ freelancerId: authUser._id },
data,);return response(updatedProfile, messages.freelancer.success);}}
import{Body,Controller,Get,Put,Req,UseGuards,UseInterceptors,} from '@nestjs/common';import{UpdateFreelancerDto} from 'src/modules/freelancers/dto/update-freelancer.dto';import{AuthGuard} from 'src/common/guards/auth.guard';import{ResponseInterceptor} from 'src/common/interceptors/response.interceptor';import{FreelancerService} from 'src/modules/freelancers/freelancer.service';import type {RequestWithUser} from 'src/types/express';import{Roles} from 'src/common/decorators/role.decorator';import{UserRole} from 'src/common/enums/user.enum';import{UserActiveGuard} from 'src/common/guards/user-active.guard';import{RolesGuard} from 'src/common/guards/roles.guard';@Controller('/api/freelancers')@UseGuards(AuthGuard,UserActiveGuard,RolesGuard)@UseInterceptors(ResponseInterceptor)exportclassFreelancerController{constructor(private readonly freelancerService:FreelancerService){}@Get()
profile(@Req() req:RequestWithUser){returnthis.freelancerService.profile(req.user._id);}@Put()@Roles(UserRole.FREELANCER)
update(@Req() req:RequestWithUser,@Body() data:UpdateFreelancerDto,){returnthis.freelancerService.update(req.user, data);}}
import{Injectable} from '@nestjs/common';import{InjectModel} from '@nestjs/mongoose';import{Model,UpdateQuery} from 'mongoose';import{IFreelancerRepository} from 'src/modules/freelancers/repository/interface/freelancer.interface';import{UpdateFreelancerDto} from 'src/modules/freelancers/dto/update-freelancer.dto';import{Freelancer,FreelancerDocument} from '../schema/freelancer.schema';@Injectable()exportclassFreelancerRepositoryimplementsIFreelancerRepository{constructor(@InjectModel(Freelancer.name)private readonly freelancerModel:Model<FreelancerDocument>,){}async findOne(filter:Partial<Freelancer>):Promise<Freelancer|null>{returnthis.freelancerModel.findOne(filter).exec();}async update(
filter:Partial<Freelancer>,
data:UpdateQuery<UpdateFreelancerDto>,):Promise<Freelancer|null>{returnthis.freelancerModel.findOneAndUpdate(
filter,{ $set: data },{new:true},);}async deleteAll(where: any ={}):Promise<void>{awaitthis.freelancerModel.deleteMany(where).exec();}}
السؤال
Zen Eddin Allaham
تواجهني مشكلة غريبة لم افهم سببها والتي هي لما ازور صفحة freelancer.html الاحظ ان صفحة تبقى loading profile.... الى ما لا نهاية حتى اعدل سطر كود ويعيد Angular بناء سيرفر ثم يظهر profile ويوجد مشكلة اخرى والتي هي لما احاول اعدل بيانات واضغط ع زر save يبقى saving... ايضا ال ما لا نهاية
وهاد كود NestJS Backend
وهاد كود API
0 أجوبة على هذا السؤال
Recommended Posts
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.