Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
db99731
refactor: remove unused refresh button and associated function from M…
FalkenDev Jan 17, 2026
8e45006
feat: enhance toast notifications with progress bar and duration for …
FalkenDev Jan 17, 2026
78e5316
feat(i18n): integrate vue-i18n for localization across the application
FalkenDev Jan 17, 2026
de79f1b
feat: implement workout session start functionality with error handli…
FalkenDev Jan 17, 2026
11c0b56
feat: update component density to compact for improved UI consistency
FalkenDev Jan 17, 2026
49ea3ec
feat: add logout option to settings with async handling
FalkenDev Jan 17, 2026
1a7bbf0
fix: reorder buttons in AcceptDialog for improved UX
FalkenDev Jan 17, 2026
ee6c017
refactor: simplify BackHeader component usage in AddGlobalExerciseList
FalkenDev Jan 17, 2026
861e328
feat: add account already exists message and improve registration flo…
FalkenDev Jan 17, 2026
0eb780e
feat: enhance user account management with password update and valida…
FalkenDev Jan 17, 2026
8494280
fix: adjust avatar size in HomeHeader for consistency
FalkenDev Jan 17, 2026
2d84830
refactor: replace v-container with div for improved layout in Workout…
FalkenDev Jan 17, 2026
8a632c0
refactor: replace v-container with div for improved layout in Session…
FalkenDev Jan 17, 2026
6302355
refactor: remove unused v-card component from WorkoutDetails for clea…
FalkenDev Jan 18, 2026
8216074
refactor: remove unused note section from session menu and ensure wor…
FalkenDev Jan 18, 2026
91a04de
feat: update ProgressBar to display current week and completed workou…
FalkenDev Jan 18, 2026
dd67b6a
feat: add isCustomized property to ExerciseResponseDto and update exe…
FalkenDev Jan 18, 2026
36c75c7
fix: update comment for sharp dependencies installation in Dockerfile…
FalkenDev Jan 18, 2026
6a9524c
feat: add weekly workout goal and streak tracking to user model, serv…
FalkenDev Jan 18, 2026
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
3 changes: 2 additions & 1 deletion Dockerfile.api
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
FROM node:22-slim

# Install sharp dependencies for Debian ARM
# Install sharp dependencies and procps for Debian ARM
RUN apt-get update && apt-get install -y \
bash \
curl \
git \
libvips-dev \
procps \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app
Expand Down
12 changes: 12 additions & 0 deletions backend/src/v1/auth/dto/UserWithoutPassword.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,24 @@ export class UserWithoutPasswordDto {
@ApiProperty({ default: true })
showRpe: boolean;

@ApiProperty({ default: 3 })
weeklyWorkoutGoal: number;

@ApiProperty({ default: 0 })
currentStreak: number;

@ApiProperty({ default: 0 })
currentWeekWorkouts: number;

constructor(user: User) {
this.id = user.id;
this.email = user.email;
this.firstName = user.firstName;
this.lastName = user.lastName;
this.avatar = user.avatar;
this.showRpe = user.showRpe ?? true;
this.weeklyWorkoutGoal = user.weeklyWorkoutGoal ?? 3;
this.currentStreak = user.currentStreak ?? 0;
this.currentWeekWorkouts = user.currentWeekWorkouts ?? 0;
}
}
6 changes: 6 additions & 0 deletions backend/src/v1/exercise/dto/exerciseResponse.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export class ExerciseResponseDto {
})
isNameCustom?: boolean;

@ApiProperty({
required: false,
description: 'If true, the user has customized this exercise after importing it',
})
isCustomized?: boolean;

@ApiProperty({
required: false,
description:
Expand Down
1 change: 1 addition & 0 deletions backend/src/v1/exercise/exercise.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export class ExerciseService {
name: exercise.name,
i18nKey: exercise.i18nKey,
isNameCustom: exercise.isNameCustom,
isCustomized: exercise.isCustomized,
globalExerciseId: exercise.globalExercise?.id,
description: exercise.description,
image: exercise.image,
Expand Down
52 changes: 52 additions & 0 deletions backend/src/v1/migrations/1768754686-AddStreakToUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm';

export class AddStreakToUser1768754686 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
// Add weeklyWorkoutGoal column
await queryRunner.addColumn(
'user',
new TableColumn({
name: 'weeklyWorkoutGoal',
type: 'int',
default: 3,
}),
);

// Add currentStreak column
await queryRunner.addColumn(
'user',
new TableColumn({
name: 'currentStreak',
type: 'int',
default: 0,
}),
);

// Add lastStreakCheckDate column
await queryRunner.addColumn(
'user',
new TableColumn({
name: 'lastStreakCheckDate',
type: 'timestamp',
isNullable: true,
}),
);

// Add currentWeekWorkouts column
await queryRunner.addColumn(
'user',
new TableColumn({
name: 'currentWeekWorkouts',
type: 'int',
default: 0,
}),
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropColumn('user', 'currentWeekWorkouts');
await queryRunner.dropColumn('user', 'lastStreakCheckDate');
await queryRunner.dropColumn('user', 'currentStreak');
await queryRunner.dropColumn('user', 'weeklyWorkoutGoal');
}
}
15 changes: 14 additions & 1 deletion backend/src/v1/user/dto/UpdateUser.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IsBoolean, IsOptional, IsString } from 'class-validator';
import { IsBoolean, IsEmail, IsOptional, IsString, MinLength } from 'class-validator';

export class UpdateUserDto {
@IsOptional()
Expand All @@ -9,10 +9,23 @@ export class UpdateUserDto {
@IsString()
lastName?: string;

@IsOptional()
@IsEmail()
email?: string;

@IsOptional()
@IsString()
avatar?: string;

@IsOptional()
@IsString()
currentPassword?: string;

@IsOptional()
@IsString()
@MinLength(8)
newPassword?: string;

@IsOptional()
@IsBoolean()
showRpe?: boolean;
Expand Down
38 changes: 37 additions & 1 deletion backend/src/v1/user/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class UserController {
@ApiOkResponse({ type: UserWithoutPasswordDto })
@UseInterceptors(FileInterceptor('file', { storage: undefined }))
async uploadAvatar(
@UploadedFile() file: Express.Multer.File,
@UploadedFile() file: any,
@Req() req: RequestWithUser,
): Promise<UserWithoutPasswordDto> {
if (!req.user?.id) {
Expand All @@ -110,4 +110,40 @@ export class UserController {

return this.userService.updateAvatar(+req.user.id, avatarUrl);
}

@Get('streak')
@ApiOperation({ summary: 'Get user streak information' })
@ApiOkResponse({
description: 'User streak information',
schema: {
example: {
currentStreak: 5,
weeklyWorkoutGoal: 3,
currentWeekWorkouts: 2,
progressPercentage: 67,
},
},
})
getStreak(@Req() req: RequestWithUser) {
if (!req.user?.id) {
throw new UnauthorizedException('User not authenticated');
}
return this.userService.getStreakInfo(+req.user.id);
}

@Put('weekly-goal')
@ApiOperation({ summary: 'Update weekly workout goal' })
@ApiOkResponse({ type: UserWithoutPasswordDto })
updateWeeklyGoal(
@Req() req: RequestWithUser,
@Body() body: { weeklyWorkoutGoal: number },
) {
if (!req.user?.id) {
throw new UnauthorizedException('User not authenticated');
}
return this.userService.updateWeeklyWorkoutGoal(
+req.user.id,
body.weeklyWorkoutGoal,
);
}
}
12 changes: 12 additions & 0 deletions backend/src/v1/user/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ export class User {
@Column({ default: true })
showRpe: boolean;

@Column({ default: 3 })
weeklyWorkoutGoal: number;

@Column({ default: 0 })
currentStreak: number;

@Column({ type: 'timestamp', nullable: true })
lastStreakCheckDate: Date;

@Column({ default: 0 })
currentWeekWorkouts: number;

@CreateDateColumn()
createdAt: Date;

Expand Down
Loading
Loading