Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.0.1",
"@nestjs/schedule": "^6.0.0",
"@nestjs/swagger": "^11.0.7",
"@nestjs/swagger": "^11.2.5",
"@nestjs/throttler": "^6.4.0",
"@nestjs/typeorm": "^11.0.0",
"@types/passport-google-oauth20": "^2.0.16",
Expand Down
1 change: 0 additions & 1 deletion backend/src/auth/providers/sign-in.provider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable prettier/prettier */
import {
forwardRef,
Inject,
Expand Down
1 change: 0 additions & 1 deletion backend/src/redis/redis.constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
/* eslint-disable prettier/prettier */
export const REDIS_CLIENT = 'REDIS_CLIENT';
1 change: 0 additions & 1 deletion backend/src/redis/redis.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable prettier/prettier */
import { Global, Module, OnModuleDestroy, Inject } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { redisProvider } from './redis.provider';
Expand Down
1 change: 0 additions & 1 deletion backend/src/redis/redis.provider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable prettier/prettier */
import { Provider } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import Redis from 'ioredis';
Expand Down
62 changes: 61 additions & 1 deletion backend/src/users/dtos/editUserDto.dto.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsOptional, IsString, IsEmail, MinLength } from 'class-validator';
import {
IsOptional,
IsString,
IsEmail,
MinLength,
IsArray,
} from 'class-validator';

export class EditUserDto {
@ApiProperty({
Expand Down Expand Up @@ -27,4 +33,58 @@ export class EditUserDto {
@IsString()
@MinLength(6)
password?: string;

@ApiProperty({
description: 'Country of the user',
required: false,
})
@IsOptional()
@IsString()
country?: string;

@ApiProperty({
description: 'Interests of the user',
required: false,
type: [String],
})
@IsOptional()
@IsArray()
@IsString({ each: true })
interests?: string[];

@ApiProperty({
description: 'Occupation of the user',
required: false,
})
@IsOptional()
@IsString()
occupation?: string;

@ApiProperty({
description: 'Goals of the user',
required: false,
type: [String],
})
@IsOptional()
@IsArray()
@IsString({ each: true })
goals?: string[];

@ApiProperty({
description: 'Available hours for the user',
required: false,
type: [String],
})
@IsOptional()
@IsArray()
@IsString({ each: true })
availableHours?: string[];

@ApiProperty({
description: 'Bio of the user',
required: false,
})
@IsOptional()
@IsString()
bio?: string;
}
6 changes: 6 additions & 0 deletions backend/src/users/providers/update-user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ export class UpdateUserService {
user.username = editUserDto.username ?? user.username;
user.email = editUserDto.email ?? user.email;
user.password = editUserDto.password ?? user.password;
user.country = editUserDto.country ?? user.country;
user.interests = editUserDto.interests ?? user.interests;
user.occupation = editUserDto.occupation ?? user.occupation;
user.goals = editUserDto.goals ?? user.goals;
user.availableHours = editUserDto.availableHours ?? user.availableHours;
user.bio = editUserDto.bio ?? user.bio;

try {
return await this.userRepository.save(user);
Expand Down
44 changes: 43 additions & 1 deletion backend/src/users/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,48 @@ export class User {
@Column('varchar', { length: 50, nullable: true })
ageGroup?: string;

/**
* Country of the user
*/
@ApiProperty({ example: 'United States', required: false })
@Column('varchar', { length: 100, nullable: true })
country?: string;

/**
* User interests
*/
@ApiProperty({ example: ['Coding', 'Design'], required: false })
@Column('simple-array', { nullable: true })
interests?: string[];

/**
* User occupation
*/
@ApiProperty({ example: 'Software Engineer', required: false })
@Column('varchar', { length: 150, nullable: true })
occupation?: string;

/**
* User goals
*/
@ApiProperty({ example: ['Learn NestJS', 'Build an app'], required: false })
@Column('simple-array', { nullable: true })
goals?: string[];

/**
* Available hours for learning
*/
@ApiProperty({ example: ['09:00', '10:00'], required: false })
@Column('simple-array', { nullable: true })
availableHours?: string[];

/**
* User bio
*/
@ApiProperty({ example: 'I am a passionate developer...', required: false })
@Column('text', { nullable: true })
bio?: string;

@Column({ nullable: true })
passwordResetToken?: string;

Expand All @@ -116,4 +158,4 @@ export class User {

@OneToOne(() => Streak, (streak) => streak.user)
streak: Streak;
}
}
88 changes: 88 additions & 0 deletions frontend/app/onboarding/OnboardingContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
'use client';

import React, { createContext, useContext, useState, ReactNode } from 'react';

interface OnboardingData {
challengeLevel: string;
challengeTypes: string[];
additionalInfo: {
country: string;
occupation: string;
interests: string[];
goals: string[];
};
availability: {
availableHours: string[];
bio: string;
};
}

interface OnboardingContextType {
data: OnboardingData;
updateData: (section: keyof OnboardingData, payload: any) => void;
// Specialized updaters for deep nesting
updateAdditionalInfo: (field: keyof OnboardingData['additionalInfo'], value: any) => void;
updateAvailability: (field: keyof OnboardingData['availability'], value: any) => void;
}

const defaultData: OnboardingData = {
challengeLevel: '',
challengeTypes: [],
additionalInfo: {
country: '',
occupation: '',
interests: [],
goals: []
},
availability: {
availableHours: [],
bio: ''
}
};

const OnboardingContext = createContext<OnboardingContextType | undefined>(undefined);

export const OnboardingProvider = ({ children }: { children: ReactNode }) => {
const [data, setData] = useState<OnboardingData>(defaultData);

const updateData = (section: keyof OnboardingData, payload: any) => {
setData((prev) => ({
...prev,
[section]: payload,
}));
};

const updateAdditionalInfo = (field: keyof OnboardingData['additionalInfo'], value: any) => {
setData((prev) => ({
...prev,
additionalInfo: {
...prev.additionalInfo,
[field]: value
}
}));
};

const updateAvailability = (field: keyof OnboardingData['availability'], value: any) => {
setData((prev) => ({
...prev,
availability: {
...prev.availability,
[field]: value
}
}));
};

return (
<OnboardingContext.Provider value={{ data, updateData, updateAdditionalInfo, updateAvailability }}>
{children}
</OnboardingContext.Provider>
);
};

export const useOnboarding = () => {
const context = useContext(OnboardingContext);
if (!context) {
throw new Error('useOnboarding must be used within an OnboardingProvider');
}
return context;
};
Loading