fix: correct learning history data handling and model parsing
This commit is contained in:
parent
e1e9ad37da
commit
210c40812b
|
|
@ -320,10 +320,19 @@ class DioClient {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Response> getLearningHistory(String sectionId, String token) async {
|
||||
Future<Response> getLearningHistory(
|
||||
String sectionId,
|
||||
String token, {
|
||||
int page = 1,
|
||||
int limit = 5,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _dio.get(
|
||||
'/learningHistory/section/$sectionId',
|
||||
queryParameters: {
|
||||
'page': page,
|
||||
'limit': limit,
|
||||
},
|
||||
options: Options(
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
|
|
|
|||
|
|
@ -8,24 +8,33 @@ class HistoryRepository {
|
|||
HistoryRepository(this._dioClient);
|
||||
|
||||
Future<List<LearningHistory>> getLearningHistory(
|
||||
String sectionId, String token) async {
|
||||
String sectionId,
|
||||
String token,
|
||||
) async {
|
||||
try {
|
||||
final response = await _dioClient.getLearningHistory(sectionId, token);
|
||||
final response = await _dioClient.getLearningHistory(
|
||||
sectionId,
|
||||
token,
|
||||
page: 1,
|
||||
limit: 1000,
|
||||
);
|
||||
if (response.statusCode == 200 && response.data != null) {
|
||||
if (response.data['payload'] != null) {
|
||||
final List<dynamic> historyData = response.data['payload'];
|
||||
return historyData
|
||||
.map((json) => LearningHistory.fromJson(json))
|
||||
.toList();
|
||||
} else {
|
||||
throw Exception('No history data available');
|
||||
// Perbaikan disini: langsung mengakses ['payload']['history']
|
||||
final List<dynamic> historyData = response.data['payload']['history'];
|
||||
if (historyData.isEmpty) {
|
||||
return []; // Mengembalikan list kosong jika tidak ada data
|
||||
}
|
||||
return historyData
|
||||
.map((json) => LearningHistory.fromJson(json))
|
||||
.toList();
|
||||
} else {
|
||||
throw Exception(
|
||||
'Failed to load learning history: ${response.statusMessage}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
if (e.response != null) {
|
||||
if (e.response?.statusCode == 404) {
|
||||
return [];
|
||||
} else if (e.response != null) {
|
||||
throw Exception('Server error: ${e.response?.statusMessage}');
|
||||
} else {
|
||||
throw Exception('Network error: ${e.message}');
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ class AppColors {
|
|||
static const Color secondaryColor = Color(0xFF5674ED);
|
||||
static const Color primaryColor = Color(0xFF34C3F9);
|
||||
static const Color yellowButtonColor = Color(0xFFFACC15);
|
||||
static const Color greenColor = Color(0xFF00BC65);
|
||||
|
||||
static LinearGradient get gradientTheme => const LinearGradient(
|
||||
colors: [secondaryColor, primaryColor],
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import 'package:intl/intl.dart';
|
||||
|
||||
class LearningHistory {
|
||||
final int score;
|
||||
final int? score;
|
||||
final String currentLevel;
|
||||
final String? nextLevel;
|
||||
final DateTime? studentFinish;
|
||||
final String topicName;
|
||||
final String sectionName;
|
||||
final bool isPass;
|
||||
|
||||
LearningHistory({
|
||||
required this.score,
|
||||
|
|
@ -15,6 +16,7 @@ class LearningHistory {
|
|||
required this.studentFinish,
|
||||
required this.topicName,
|
||||
required this.sectionName,
|
||||
required this.isPass,
|
||||
});
|
||||
|
||||
factory LearningHistory.fromJson(Map<String, dynamic> json) {
|
||||
|
|
@ -27,6 +29,7 @@ class LearningHistory {
|
|||
: null,
|
||||
topicName: json['TOPIC_NAME'] ?? 'Unknown Topic',
|
||||
sectionName: json['SECTION_NAME'] ?? 'Unknown Section',
|
||||
isPass: json['IS_PASS'] == 1,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,10 @@ class HistoryProvider with ChangeNotifier {
|
|||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> fetchLearningHistory(String token) async {
|
||||
Future<void> fetchLearningHistory(
|
||||
String token, {
|
||||
bool refresh = false,
|
||||
}) async {
|
||||
if (_sectionProvider.sections.isEmpty) {
|
||||
_error = 'No sections available';
|
||||
notifyListeners();
|
||||
|
|
@ -40,8 +43,12 @@ class HistoryProvider with ChangeNotifier {
|
|||
notifyListeners();
|
||||
|
||||
try {
|
||||
final history = await _repository.getLearningHistory(sectionId, token);
|
||||
final history = await _repository.getLearningHistory(
|
||||
sectionId,
|
||||
token,
|
||||
);
|
||||
_learningHistory = history;
|
||||
_error = null;
|
||||
} catch (e) {
|
||||
_error = 'Error fetching learning history: ${e.toString()}';
|
||||
} finally {
|
||||
|
|
@ -50,12 +57,20 @@ class HistoryProvider with ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
Color getColorForLevels(dynamic currentLevel, dynamic nextLevel) {
|
||||
Color getColorForLevels(
|
||||
dynamic currentLevel,
|
||||
dynamic nextLevel,
|
||||
bool isPass,
|
||||
) {
|
||||
if (isPass) {
|
||||
return AppColors.greenColor;
|
||||
}
|
||||
|
||||
int? current = _parseLevel(currentLevel);
|
||||
int? next = _parseLevel(nextLevel);
|
||||
|
||||
if (current == null || next == null) {
|
||||
return AppColors.blackColor; // Default color if parsing fails
|
||||
return AppColors.blackColor;
|
||||
}
|
||||
|
||||
if (current == next) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,17 @@ class HistoryScreen extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _HistoryScreenState extends State<HistoryScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final historyProvider =
|
||||
Provider.of<HistoryProvider>(context, listen: false);
|
||||
final userProvider = Provider.of<UserProvider>(context, listen: false);
|
||||
historyProvider.fetchLearningHistory(userProvider.jwtToken!);
|
||||
});
|
||||
}
|
||||
|
||||
bool isNotFoundError(String error) {
|
||||
return error.toLowerCase().contains('no learning history found') ||
|
||||
error.toLowerCase().contains('not found');
|
||||
|
|
@ -52,68 +63,12 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Your Exercise History!',
|
||||
style: AppTextStyles.blueTextStyle.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Track your progress with a personalized overview of all your workouts.',
|
||||
style: AppTextStyles.greyTextStyle.copyWith(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_buildHeader(),
|
||||
const SizedBox(height: 8),
|
||||
const CustomTabBar(),
|
||||
const SizedBox(height: 8),
|
||||
Expanded(
|
||||
child: Consumer<HistoryProvider>(
|
||||
builder: (context, historyProvider, child) {
|
||||
if (historyProvider.isLoading) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator());
|
||||
} else if (historyProvider.error != null) {
|
||||
if (isNotFoundError(historyProvider.error!)) {
|
||||
return _buildEmptyState(context);
|
||||
} else {
|
||||
return _buildErrorState(historyProvider.error!);
|
||||
}
|
||||
} else if (historyProvider.learningHistory.isEmpty) {
|
||||
return _buildEmptyState(context);
|
||||
} else {
|
||||
return ListView.builder(
|
||||
itemCount: historyProvider.learningHistory.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Column(
|
||||
children: [
|
||||
ExerciseHistoryCard(
|
||||
exercise:
|
||||
historyProvider.learningHistory[index],
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
child: _buildContent(historyProvider),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -124,6 +79,72 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
|||
});
|
||||
}
|
||||
|
||||
Widget _buildContent(HistoryProvider historyProvider) {
|
||||
if (historyProvider.isLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
if (historyProvider.error != null) {
|
||||
return isNotFoundError(historyProvider.error!)
|
||||
? _buildEmptyState(context)
|
||||
: _buildErrorState(historyProvider.error!);
|
||||
}
|
||||
|
||||
if (historyProvider.learningHistory.isEmpty) {
|
||||
return _buildEmptyState(context);
|
||||
}
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
final userProvider = Provider.of<UserProvider>(context, listen: false);
|
||||
await historyProvider.fetchLearningHistory(userProvider.jwtToken!);
|
||||
},
|
||||
child: ListView.builder(
|
||||
itemCount: historyProvider.learningHistory.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Column(
|
||||
children: [
|
||||
ExerciseHistoryCard(
|
||||
exercise: historyProvider.learningHistory[index],
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Your Exercise History!',
|
||||
style: AppTextStyles.blueTextStyle.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Track your progress with a personalized overview of all your workouts.',
|
||||
style: AppTextStyles.greyTextStyle.copyWith(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildErrorState(String error) {
|
||||
return Center(
|
||||
child: Column(
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@ class ExerciseHistoryCard extends StatelessWidget {
|
|||
final historyProvider =
|
||||
Provider.of<HistoryProvider>(context, listen: false);
|
||||
final color = historyProvider.getColorForLevels(
|
||||
exercise.currentLevel, exercise.nextLevel);
|
||||
exercise.currentLevel,
|
||||
exercise.nextLevel,
|
||||
exercise.isPass,
|
||||
);
|
||||
|
||||
return Card(
|
||||
color: AppColors.whiteColor,
|
||||
|
|
@ -54,7 +57,9 @@ class ExerciseHistoryCard extends StatelessWidget {
|
|||
style: AppTextStyles.blackTextStyle.copyWith(),
|
||||
),
|
||||
TextSpan(
|
||||
text: '${exercise.nextLevel}',
|
||||
text: exercise.isPass
|
||||
? 'Topic Finished'
|
||||
: '${exercise.nextLevel}',
|
||||
style: AppTextStyles.blackTextStyle.copyWith(
|
||||
color: color,
|
||||
),
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user