NIM_E31222534/Androidnya/lib/services/artikel_service.dart

479 lines
17 KiB
Dart

import 'package:posyandu/services/api_service.dart';
import 'dart:convert';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:io';
class ArtikelModel {
final int id;
final String judul;
final String? gambarArtikel;
final String isiArtikel;
final DateTime tanggal;
final String? gambarUrl;
final DateTime? createdAt;
final DateTime? updatedAt;
ArtikelModel({
required this.id,
required this.judul,
this.gambarArtikel,
required this.isiArtikel,
required this.tanggal,
this.gambarUrl,
this.createdAt,
this.updatedAt,
});
factory ArtikelModel.fromJson(Map<String, dynamic> json) {
return ArtikelModel(
id: json['id'],
judul: json['judul'],
gambarArtikel: json['gambar_artikel'],
isiArtikel: json['isi_artikel'],
tanggal: DateTime.parse(json['tanggal']),
gambarUrl: json['gambar_url'],
createdAt: json['created_at'] != null ? DateTime.parse(json['created_at']) : null,
updatedAt: json['updated_at'] != null ? DateTime.parse(json['updated_at']) : null,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'judul': judul,
'gambar_artikel': gambarArtikel,
'isi_artikel': isiArtikel,
'tanggal': tanggal.toIso8601String().split('T')[0],
'gambar_url': gambarUrl,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
};
}
}
class ArtikelPagination {
final List<ArtikelModel> data;
final int currentPage;
final int lastPage;
final int perPage;
final int total;
ArtikelPagination({
required this.data,
required this.currentPage,
required this.lastPage,
required this.perPage,
required this.total,
});
factory ArtikelPagination.fromJson(Map<String, dynamic> json) {
try {
print('Memparsing ArtikelPagination dari: $json');
final dataMap = json['data'];
print('Data map: $dataMap');
if (dataMap == null) {
print('dataMap adalah null');
throw Exception('Format data tidak valid: dataMap adalah null');
}
// Handle jika data adalah array dan bukan objek paginasi
if (dataMap is List) {
print('dataMap adalah List, bukan objek paginasi');
return ArtikelPagination(
data: dataMap.map((item) => ArtikelModel.fromJson(item)).toList(),
currentPage: 1,
lastPage: 1,
perPage: dataMap.length,
total: dataMap.length,
);
}
// Pastikan semua field yang dibutuhkan ada
if (!dataMap.containsKey('data') ||
!dataMap.containsKey('current_page') ||
!dataMap.containsKey('last_page') ||
!dataMap.containsKey('per_page') ||
!dataMap.containsKey('total')) {
print('Format data tidak lengkap: $dataMap');
// Jika format tidak sesuai dengan yang diharapkan, coba buat format alternatif
return ArtikelPagination(
data: (dataMap['data'] ?? []).map<ArtikelModel>((item) => ArtikelModel.fromJson(item)).toList(),
currentPage: dataMap['current_page'] ?? 1,
lastPage: dataMap['last_page'] ?? 1,
perPage: dataMap['per_page'] ?? 10,
total: dataMap['total'] ?? 0,
);
}
return ArtikelPagination(
data: (dataMap['data'] as List).map((item) => ArtikelModel.fromJson(item)).toList(),
currentPage: dataMap['current_page'],
lastPage: dataMap['last_page'],
perPage: dataMap['per_page'],
total: dataMap['total'],
);
} catch (e) {
print('Error saat parsing ArtikelPagination: $e');
// Return empty pagination in case of error
return ArtikelPagination(
data: [],
currentPage: 1,
lastPage: 1,
perPage: 10,
total: 0,
);
}
}
}
class ArtikelService {
final ApiService _apiService = ApiService();
// Singleton pattern
static final ArtikelService _instance = ArtikelService._internal();
factory ArtikelService() {
return _instance;
}
ArtikelService._internal();
/// Mendapatkan daftar artikel dengan paginasi
Future<ArtikelPagination> getArtikels({
int perPage = 10,
int page = 1,
String? search,
}) async {
try {
final Map<String, dynamic> queryParameters = {
'per_page': perPage.toString(),
'page': page.toString(),
};
if (search != null && search.isNotEmpty) {
queryParameters['search'] = search;
}
print('Mengambil data artikel dengan parameter: $queryParameters');
// Buat fungsi fallback untuk mengembalikan data kosong jika terjadi error
ArtikelPagination createEmptyPagination() {
print('Mengembalikan pagination kosong untuk fallback');
return ArtikelPagination(
data: [],
currentPage: page,
lastPage: page,
perPage: perPage,
total: 0,
);
}
// Gunakan timeout yang lebih pendek untuk mencegah loading infinit
final response = await _apiService.get('artikel', queryParameters: queryParameters)
.timeout(Duration(seconds: 5), onTimeout: () {
print('Timeout saat mengambil data artikel');
return {
'status': 'success',
'data': [],
'message': 'Timeout saat mengambil data'
};
});
print('Response dari API artikel: $response');
// Coba mendapatkan data terlepas dari format respons
if (response['status'] == 'success') {
// Format response berhasil, coba parse sebagai ArtikelPagination
try {
return ArtikelPagination.fromJson(response);
} catch (e) {
print('Error parsing ArtikelPagination: $e');
// Jika gagal, coba format alternatif
// Jika respons adalah array, langsung gunakan sebagai data
if (response['data'] is List) {
final List<dynamic> dataList = response['data'];
return ArtikelPagination(
data: dataList.map((item) => ArtikelModel.fromJson(item)).toList(),
currentPage: page,
lastPage: dataList.isEmpty ? page : page + 1, // Asumsi ada halaman berikutnya jika ada data
perPage: perPage,
total: dataList.length,
);
}
// Format dengan pagination di dalam data
if (response['data'] is Map &&
(response['data'] as Map).containsKey('data') &&
(response['data'] as Map)['data'] is List) {
final Map<String, dynamic> paginationData = response['data'];
return ArtikelPagination(
data: (paginationData['data'] as List).map((item) => ArtikelModel.fromJson(item)).toList(),
currentPage: paginationData['current_page'] ?? page,
lastPage: paginationData['last_page'] ?? page,
perPage: paginationData['per_page'] ?? perPage,
total: paginationData['total'] ?? (paginationData['data'] as List).length,
);
}
// Jika semua format gagal, kembalikan pagination kosong
print('Semua format gagal, mengembalikan pagination kosong');
return createEmptyPagination();
}
} else {
print('Error status dari API: ${response['message']}');
return createEmptyPagination();
}
} catch (e) {
print('Error lengkap pada getArtikels: $e');
// Kembalikan objek pagination kosong sebagai fallback
return ArtikelPagination(
data: [],
currentPage: page,
lastPage: page,
perPage: perPage,
total: 0,
);
}
}
/// Mendapatkan detail artikel
Future<ArtikelModel> getArtikelDetail(int id) async {
try {
print('Memulai request untuk detail artikel dengan ID: $id');
final response = await _apiService.get('artikel/$id').timeout(Duration(seconds: 3), onTimeout: () {
print('Timeout saat mengambil detail artikel dengan ID: $id');
throw TimeoutException('Waktu habis saat mengambil detail artikel');
});
print('Response dari API artikel detail: $response');
if (response['status'] == 'success') {
// Periksa apakah data dalam format yang diharapkan
if (response['data'] is Map<String, dynamic>) {
return ArtikelModel.fromJson(response['data']);
} else if (response['data'] is List && (response['data'] as List).isNotEmpty) {
// Jika data adalah list, ambil item pertama
return ArtikelModel.fromJson((response['data'] as List).first);
} else {
print('Format data detail artikel tidak dikenali: ${response['data'].runtimeType}');
return _createDummyDetail(id);
}
} else {
print('Error status dari API detail artikel: ${response['message']}');
return _createDummyDetail(id);
}
} catch (e) {
print('Error lengkap pada getArtikelDetail: $e');
return _createDummyDetail(id);
}
}
/// Membuat model artikel dummy untuk fallback
ArtikelModel _createDummyDetail(int id) {
print('Membuat model artikel dummy untuk fallback dengan ID: $id');
return ArtikelModel(
id: id,
judul: 'Artikel Tidak Tersedia',
isiArtikel: 'Maaf, artikel yang Anda cari tidak dapat diakses saat ini. Silakan coba lagi nanti.',
tanggal: DateTime.now(),
gambarUrl: null,
);
}
/// Mendapatkan artikel terbaru
Future<List<ArtikelModel>> getLatestArtikels({int limit = 5}) async {
try {
// Gunakan timeout yang lebih pendek
final response = await _apiService.get('artikel', queryParameters: {'limit': limit.toString(), 'latest': 'true'})
.timeout(Duration(seconds: 5), onTimeout: () {
print('Timeout saat mengambil artikel terbaru');
throw TimeoutException('Waktu habis saat mengambil artikel terbaru');
});
if (response['status'] == 'success' && response['data'] != null) {
final dataMap = response['data'];
try {
final List<dynamic> dataList = dataMap['data'] != null ? dataMap['data'] : dataMap;
return dataList.map((item) => ArtikelModel.fromJson(item)).toList();
} catch (e) {
print('Error parsing artikel terbaru: $e');
throw Exception('Format data artikel terbaru tidak valid');
}
} else {
throw Exception(response['message'] ?? 'Gagal mengambil artikel terbaru');
}
} catch (e) {
print('Error pada getLatestArtikels: $e');
// Return artikel dummy untuk fallback
return _getDummyLatestArtikels(limit);
}
}
/// Membuat artikel terbaru dummy untuk fallback
List<ArtikelModel> _getDummyLatestArtikels(int limit) {
print('Menggunakan artikel terbaru dummy untuk fallback');
final dummyArticles = [
ArtikelModel(
id: 1,
judul: 'Pentingnya ASI Eksklusif untuk Perkembangan Bayi',
isiArtikel: 'ASI eksklusif adalah pemberian ASI saja pada bayi sampai usia 6 bulan. Manfaatnya sangat banyak untuk tumbuh kembang dan kekebalan tubuh bayi.',
tanggal: DateTime.now().subtract(Duration(days: 2)),
gambarUrl: 'https://img.freepik.com/free-photo/young-mother-showing-breastfeeding-her-baby_23-2149046913.jpg',
),
ArtikelModel(
id: 2,
judul: 'Mencegah Stunting Sejak Dini pada Anak',
isiArtikel: 'Stunting dapat dicegah dengan memperhatikan asupan gizi sejak masa kehamilan dan memberikan makanan bergizi seimbang pada anak.',
tanggal: DateTime.now().subtract(Duration(days: 4)),
gambarUrl: 'https://img.freepik.com/free-photo/doctor-check-up-little-boy-office_23-2148982292.jpg',
),
ArtikelModel(
id: 3,
judul: 'Panduan Makanan Bergizi untuk Balita',
isiArtikel: 'Makanan bergizi seimbang sangat penting untuk pertumbuhan optimal balita. Pelajari menu-menu sehat yang bisa diberikan sesuai usia anak.',
tanggal: DateTime.now().subtract(Duration(days: 6)),
gambarUrl: 'https://img.freepik.com/free-photo/close-up-young-attractive-smiling-mother-feeding-her-cute-baby-son-with-spoon-organic-healthy-baby-food-white-kitchen-with-big-window_8353-12056.jpg',
),
ArtikelModel(
id: 4,
judul: 'Jadwal Imunisasi Lengkap untuk Anak',
isiArtikel: 'Imunisasi adalah cara efektif untuk melindungi anak dari berbagai penyakit berbahaya. Ketahui jadwal imunisasi yang tepat untuk anak.',
tanggal: DateTime.now().subtract(Duration(days: 10)),
gambarUrl: 'https://img.freepik.com/free-photo/doctor-vaccinating-little-girl_23-2148982283.jpg',
),
ArtikelModel(
id: 5,
judul: 'Tips Merawat Kesehatan Anak selama Musim Hujan',
isiArtikel: 'Musim hujan meningkatkan risiko beberapa penyakit pada anak. Ikuti tips ini untuk menjaga kesehatan anak tetap optimal.',
tanggal: DateTime.now().subtract(Duration(days: 15)),
gambarUrl: 'https://img.freepik.com/free-photo/cute-child-with-umbrella_1149-537.jpg',
),
];
return dummyArticles.take(limit).toList();
}
/// Membuat artikel baru (memerlukan hak akses admin)
Future<ArtikelModel> createArtikel({
required String judul,
required File gambarArtikel,
required String isiArtikel,
required DateTime tanggal,
}) async {
try {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('token');
if (token == null) {
throw Exception('Tidak memiliki akses untuk membuat artikel');
}
final uri = Uri.parse('${ApiService.baseUrl}/artikel');
// Membuat request multipart untuk upload file
final request = http.MultipartRequest('POST', uri)
..headers.addAll({
'Authorization': 'Bearer $token',
'Accept': 'application/json',
})
..fields['judul'] = judul
..fields['isi_artikel'] = isiArtikel
..fields['tanggal'] = tanggal.toIso8601String().split('T')[0]
..files.add(await http.MultipartFile.fromPath(
'gambar_artikel',
gambarArtikel.path,
));
final streamedResponse = await request.send();
final response = await http.Response.fromStream(streamedResponse);
final responseData = json.decode(response.body);
if (response.statusCode == 201 && responseData['status'] == 'success') {
return ArtikelModel.fromJson(responseData['data']);
} else {
throw Exception(responseData['message'] ?? 'Gagal membuat artikel');
}
} catch (e) {
print('Error pada createArtikel: $e');
throw Exception('Gagal membuat artikel: $e');
}
}
/// Mengupdate artikel (memerlukan hak akses admin)
Future<ArtikelModel> updateArtikel({
required int id,
required String judul,
File? gambarArtikel,
required String isiArtikel,
required DateTime tanggal,
}) async {
try {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('token');
if (token == null) {
throw Exception('Tidak memiliki akses untuk mengupdate artikel');
}
final uri = Uri.parse('${ApiService.baseUrl}/artikel/$id');
// Membuat request multipart untuk upload file
final request = http.MultipartRequest('POST', uri)
..headers.addAll({
'Authorization': 'Bearer $token',
'Accept': 'application/json',
})
..fields['judul'] = judul
..fields['isi_artikel'] = isiArtikel
..fields['tanggal'] = tanggal.toIso8601String().split('T')[0]
..fields['_method'] = 'PUT'; // Laravel menggunakan _method untuk form method spoofing
// Tambahkan file gambar jika ada
if (gambarArtikel != null) {
request.files.add(await http.MultipartFile.fromPath(
'gambar_artikel',
gambarArtikel.path,
));
}
final streamedResponse = await request.send();
final response = await http.Response.fromStream(streamedResponse);
final responseData = json.decode(response.body);
if (response.statusCode == 200 && responseData['status'] == 'success') {
return ArtikelModel.fromJson(responseData['data']);
} else {
throw Exception(responseData['message'] ?? 'Gagal mengupdate artikel');
}
} catch (e) {
print('Error pada updateArtikel: $e');
throw Exception('Gagal mengupdate artikel: $e');
}
}
/// Menghapus artikel (memerlukan hak akses admin)
Future<bool> deleteArtikel(int id) async {
try {
final response = await _apiService.delete('artikel/$id');
if (response['status'] == 'success') {
return true;
} else {
throw Exception(response['message'] ?? 'Gagal menghapus artikel');
}
} catch (e) {
print('Error pada deleteArtikel: $e');
throw Exception('Gagal menghapus artikel: $e');
}
}
}