fix: correct learning history data handling and model parsing

This commit is contained in:
Naresh Pratista 2024-10-23 15:44:33 +07:00
parent e1e9ad37da
commit 210c40812b
7 changed files with 139 additions and 76 deletions

View File

@ -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',

View File

@ -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}');

View File

@ -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],

View File

@ -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,
);
}

View File

@ -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) {

View File

@ -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(

View File

@ -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,
),