Skip to content

Commit 307a906

Browse files
authored
Merge pull request #465 from bounswe/feat/289-mentor-functionalities-mobile
Feat/289 mentor functionalities mobile
2 parents 767e80a + 778ed8f commit 307a906

31 files changed

+3095
-1591
lines changed

apps/jobboard-mobile/lib/core/constants/app_constants.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class AppConstants {
66
static String get baseUrl => _devBaseUrl;
77

88
static String _getBaseUrl() {
9-
return 'https://jobboard-backend-728855696411.europe-west1.run.app/api';
9+
return 'https://jobboard-backend-test-728855696411.europe-west1.run.app/api';
1010
//return 'https://jobboard-backend-test-728855696411.europe-west1.run.app/api';
1111
//return 'http://localhost:8080/api';
1212
//return 'http://10.0.2.2:8080/api';
Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,61 @@
11
import 'dart:convert';
22
import 'user.dart';
3+
import 'mentor_review.dart';
34

45
class MentorProfile {
5-
final int id;
6-
final User user;
7-
final int capacity;
8-
final int currentMenteeCount;
6+
final String id;
7+
final String username;
8+
final List<String> expertise;
9+
final int currentMentees;
10+
final int maxMentees;
911
final double averageRating;
1012
final int reviewCount;
11-
final bool isAvailable;
13+
final List<MentorReview> reviews;
14+
15+
/// Derived availability: backend no longer has `isAvailable`,
16+
/// so we infer it as "has capacity left".
17+
bool get isAvailable => currentMentees < maxMentees;
1218

1319
MentorProfile({
1420
required this.id,
15-
required this.user,
16-
required this.capacity,
17-
required this.currentMenteeCount,
21+
required this.username,
22+
required this.expertise,
23+
required this.currentMentees,
24+
required this.maxMentees,
1825
required this.averageRating,
1926
required this.reviewCount,
20-
required this.isAvailable,
27+
this.reviews = const [],
2128
});
2229

2330
factory MentorProfile.fromJson(Map<String, dynamic> json) {
2431
return MentorProfile(
25-
id: json['id'],
26-
user: User.fromJson(json['user']),
27-
capacity: json['capacity'],
28-
currentMenteeCount: json['currentMenteeCount'],
29-
averageRating: json['averageRating']?.toDouble() ?? 0.0,
30-
reviewCount: json['reviewCount'],
31-
isAvailable: json['isAvailable'],
32+
id: json['id']?.toString() ?? '',
33+
username: json['username'] ?? '',
34+
expertise: (json['expertise'] as List<dynamic>?)
35+
?.map((e) => e.toString())
36+
.toList() ??
37+
const [],
38+
currentMentees: json['currentMentees'] ?? 0,
39+
maxMentees: json['maxMentees'] ?? 0,
40+
averageRating: (json['averageRating'] ?? 0.0).toDouble(),
41+
reviewCount: json['reviewCount'] ?? 0,
42+
reviews: (json['reviews'] as List<dynamic>?)
43+
?.map((e) => MentorReview.fromJson(e))
44+
.toList() ??
45+
const [],
3246
);
3347
}
3448

3549
Map<String, dynamic> toJson() {
3650
return {
3751
'id': id,
38-
'user': user.toJson(),
39-
'capacity': capacity,
40-
'currentMenteeCount': currentMenteeCount,
52+
'username': username,
53+
'expertise': expertise,
54+
'currentMentees': currentMentees,
55+
'maxMentees': maxMentees,
4156
'averageRating': averageRating,
4257
'reviewCount': reviewCount,
43-
'isAvailable': isAvailable,
58+
'reviews': reviews.map((r) => r.toJson()).toList(),
4459
};
4560
}
4661
}

apps/jobboard-mobile/lib/core/models/mentor_review.dart

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,38 @@ import 'user.dart';
33

44
class MentorReview {
55
final int id;
6-
final User mentor;
7-
final User mentee;
8-
final int rating;
6+
final String reviewerUsername;
7+
final double rating;
98
final String? comment;
109
final DateTime createdAt;
11-
final DateTime? updatedAt;
1210

1311
MentorReview({
1412
required this.id,
15-
required this.mentor,
16-
required this.mentee,
13+
required this.reviewerUsername,
1714
required this.rating,
1815
this.comment,
1916
required this.createdAt,
20-
this.updatedAt,
2117
});
2218

2319
factory MentorReview.fromJson(Map<String, dynamic> json) {
2420
return MentorReview(
25-
id: json['id'] as int,
26-
mentor: User.fromJson(json['mentor'] as Map<String, dynamic>),
27-
mentee: User.fromJson(json['mentee'] as Map<String, dynamic>),
28-
rating: json['rating'] as int,
29-
comment: json['comment'] as String?,
21+
id: json['id'] is int
22+
? json['id'] as int
23+
: int.tryParse(json['id']?.toString() ?? '0') ?? 0,
24+
reviewerUsername: json['reviewerUsername'] ?? '',
25+
rating: (json['rating'] ?? 0.0).toDouble(),
26+
comment: json['comment'],
3027
createdAt: DateTime.parse(json['createdAt'] as String),
31-
updatedAt:
32-
json['updatedAt'] == null
33-
? null
34-
: DateTime.parse(json['updatedAt'] as String),
3528
);
3629
}
3730

3831
Map<String, dynamic> toJson() {
3932
return {
4033
'id': id,
41-
'mentor': mentor,
42-
'mentee': mentee,
34+
'reviewerUsername': reviewerUsername,
4335
'rating': rating,
4436
'comment': comment,
4537
'createdAt': createdAt.toIso8601String(),
46-
'updatedAt': updatedAt?.toIso8601String(),
4738
};
4839
}
4940
}
Lines changed: 84 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,114 @@
11
import 'dart:convert';
22
import 'user.dart';
3-
43
enum MentorshipRequestStatus {
54
PENDING,
65
ACCEPTED,
76
REJECTED,
8-
COMPLETED,
97
CANCELLED,
8+
COMPLETED,
9+
UNKNOWN,
1010
}
1111

12+
MentorshipRequestStatus _parseStatus(String? raw) {
13+
if (raw == null) return MentorshipRequestStatus.UNKNOWN;
14+
final upper = raw.toUpperCase();
15+
switch (upper) {
16+
case 'PENDING':
17+
return MentorshipRequestStatus.PENDING;
18+
case 'ACCEPTED':
19+
return MentorshipRequestStatus.ACCEPTED;
20+
case 'REJECTED':
21+
return MentorshipRequestStatus.REJECTED;
22+
case 'CANCELLED':
23+
return MentorshipRequestStatus.CANCELLED;
24+
case 'COMPLETED':
25+
return MentorshipRequestStatus.COMPLETED;
26+
default:
27+
return MentorshipRequestStatus.UNKNOWN;
28+
}
29+
}
1230
class MentorshipRequest {
13-
final int id;
14-
final User mentor;
15-
final User mentee;
16-
final String message;
31+
/// Primary ID (string for generic requests, mentee view `mentorshipRequestId`)
32+
final String id;
33+
34+
/// Requester user ID (if present in response)
35+
final String? requesterId;
36+
String? requesterUsername;
37+
38+
39+
/// Mentor user ID (string or int in payload; normalized to string)
40+
final String mentorId;
41+
42+
/// Request status
1743
final MentorshipRequestStatus status;
44+
45+
/// When the request was created
1846
final DateTime createdAt;
19-
final DateTime? updatedAt;
20-
final String? channelId;
47+
48+
/// Extra mentee-view fields:
49+
final String? mentorUsername;
50+
final int? resumeReviewId;
51+
final String? reviewStatus;
52+
final int? conversationId;
2153

2254
MentorshipRequest({
2355
required this.id,
24-
required this.mentor,
25-
required this.mentee,
26-
required this.message,
56+
required this.requesterId,
57+
required this.requesterUsername,
58+
required this.mentorId,
2759
required this.status,
2860
required this.createdAt,
29-
this.updatedAt,
30-
this.channelId,
61+
this.mentorUsername,
62+
this.resumeReviewId,
63+
this.reviewStatus,
64+
this.conversationId,
65+
3166
});
3267

3368
factory MentorshipRequest.fromJson(Map<String, dynamic> json) {
69+
// Mentee view (`mentorshipRequestId` etc.)
70+
if (json.containsKey('mentorshipRequestId')) {
71+
return MentorshipRequest(
72+
id: json['mentorshipRequestId'].toString(),
73+
requesterId: null, // not present here
74+
requesterUsername: json['requesterUsername']?.toString() ?? '',
75+
mentorId: json['mentorId']?.toString() ?? '',
76+
status: _parseStatus(json['requestStatus']?.toString()),
77+
createdAt: DateTime.parse(json['requestCreatedAt'] as String),
78+
mentorUsername: json['mentorUsername']?.toString(),
79+
resumeReviewId: json['resumeReviewId'] as int?,
80+
reviewStatus: json['reviewStatus']?.toString(),
81+
conversationId: json['conversationId'] as int?,
82+
);
83+
}
84+
85+
// Generic request shape
3486
return MentorshipRequest(
35-
id: json['id'],
36-
mentor: User.fromJson(json['mentor']),
37-
mentee: User.fromJson(json['mentee']),
38-
message: json['message'],
39-
status: MentorshipRequestStatus.values.firstWhere(
40-
(e) => e.toString().split('.').last == json['status'],
41-
orElse: () => MentorshipRequestStatus.PENDING,
42-
),
43-
createdAt: DateTime.parse(json['createdAt']),
44-
updatedAt:
45-
json['updatedAt'] != null ? DateTime.parse(json['updatedAt']) : null,
46-
channelId: json['channelId'],
87+
id: json['id']?.toString() ?? '',
88+
requesterId: json['requesterId']?.toString(),
89+
requesterUsername: json['requesterUsername']?.toString() ?? '',
90+
mentorId: json['mentorId']?.toString() ?? '',
91+
status: _parseStatus(json['status']?.toString()),
92+
createdAt: DateTime.parse(json['createdAt'] as String),
93+
mentorUsername: null,
94+
resumeReviewId: null,
95+
reviewStatus: null,
96+
conversationId: null,
4797
);
4898
}
4999

50100
Map<String, dynamic> toJson() {
51101
return {
52102
'id': id,
53-
'mentor': mentor,
54-
'mentee': mentee,
55-
'message': message,
56-
'status': status.toString().split('.').last,
103+
'requesterId': requesterId,
104+
'requesterUsername': requesterUsername,
105+
'mentorId': mentorId,
106+
'status': status.name,
57107
'createdAt': createdAt.toIso8601String(),
58-
'updatedAt': updatedAt?.toIso8601String(),
59-
'channelId': channelId,
108+
'mentorUsername': mentorUsername,
109+
'resumeReviewId': resumeReviewId,
110+
'reviewStatus': reviewStatus,
111+
'conversationId': conversationId,
60112
};
61113
}
62-
}
114+
}

apps/jobboard-mobile/lib/core/models/profile.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ class Profile {
3838
.toList() ?? [];
3939

4040
return Profile(
41-
id: json['id'],
42-
userId: json['userId'],
41+
id: json['id'] ?? json['userId'],
42+
userId: json['userId'] ?? json['id'],
4343
fullName: fullName,
4444
phone: json['phone'],
4545
location: json['location'],

apps/jobboard-mobile/lib/core/models/user.dart

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class User {
1414
final String? employerId; // Added: Specific ID for employer operations
1515
final MentorshipStatus? mentorshipStatus;
1616
final int? maxMenteeCount;
17+
final List<String>? expertise;
18+
1719
// Add other fields later: profilePicUrl, education, skills, etc.
1820

1921
User({
@@ -29,6 +31,7 @@ class User {
2931
this.employerId, // Add to constructor
3032
this.mentorshipStatus,
3133
this.maxMenteeCount,
34+
this.expertise,
3235
});
3336

3437
// Computed property to get full name
@@ -59,6 +62,7 @@ class User {
5962
? MentorshipStatus.values.byName(json['mentorshipStatus'])
6063
: null,
6164
maxMenteeCount: json['maxMenteeCount'],
65+
expertise: List<String>.from(json['expertise'] ?? []),
6266
);
6367
}
6468

@@ -76,6 +80,40 @@ class User {
7680
'employerId': employerId,
7781
'mentorshipStatus': mentorshipStatus?.name,
7882
'maxMenteeCount': maxMenteeCount,
83+
'expertise': expertise,
7984
};
8085
}
86+
87+
User copyWith({
88+
String? id,
89+
String? username,
90+
String? email,
91+
UserType? role,
92+
String? firstName,
93+
String? lastName,
94+
String? jobTitle,
95+
String? company,
96+
String? bio,
97+
String? employerId,
98+
MentorshipStatus? mentorshipStatus,
99+
int? maxMenteeCount,
100+
List<String>? expertise,
101+
}) {
102+
return User(
103+
id: id ?? this.id,
104+
username: username ?? this.username,
105+
email: email ?? this.email,
106+
role: role ?? this.role,
107+
firstName: firstName ?? this.firstName,
108+
lastName: lastName ?? this.lastName,
109+
jobTitle: jobTitle ?? this.jobTitle,
110+
company: company ?? this.company,
111+
bio: bio ?? this.bio,
112+
employerId: employerId ?? this.employerId,
113+
mentorshipStatus: mentorshipStatus ?? this.mentorshipStatus,
114+
maxMenteeCount: maxMenteeCount ?? this.maxMenteeCount,
115+
expertise: expertise ?? this.expertise,
116+
);
117+
}
118+
81119
}

0 commit comments

Comments
 (0)