diff --git a/README.md b/README.md
index 3c1c5a3..e3086e5 100644
--- a/README.md
+++ b/README.md
@@ -1,65 +1,259 @@
-# PDDIKTI Flutter App
+# 🎯 DB-Cracker: ctOS Faculty Database Scanner
-A Flutter application that uses the PDDIKTI API from Kemdikbud to search for and view student data.
+
-## Features
+
-- Search for students by name (case-insensitive)
-- View detailed student information
-- Clean and modern UI design
-- Error handling and loading states
+[](https://flutter.dev)
+[](https://dart.dev)
+[](https://developer.android.com)
-## Getting Started
+**🔥 Advanced Faculty & Student Database Intelligence System 🔥**
-### Prerequisites
+*Inspired by Watch Dogs ctOS - Elegant, Futuristic, Powerful*
-- Flutter (version 2.19.0 or higher)
-- Dart (version 2.19.0 or higher)
-- Android Studio / VS Code with Flutter extensions
+
-### Installation
+---
-1. Clone this repository
- ```bash
- git clone https://github.com/yourusername/pddikti_flutter.git
- ```
+## 🚀 **Tentang Proyek**
-2. Navigate to the project directory
- ```bash
- cd pddikti_flutter
- ```
+**DB-Cracker** adalah aplikasi mobile canggih yang dirancang untuk mengakses dan menganalisis database akademik Indonesia dengan antarmuka yang terinspirasi dari sistem ctOS dalam game Watch Dogs. Aplikasi ini menyediakan akses comprehensive ke data dosen dan mahasiswa dari berbagai sumber API pendidikan Indonesia.
-3. Install dependencies
- ```bash
- flutter pub get
- ```
+### 🎨 **Design Philosophy**
+- **ctOS Aesthetic**: Dark theme dengan aksen cyan/hijau neon
+- **Futuristic UI**: Typography monospace, animasi glow, efek hacker
+- **Responsive Layout**: Mengikuti prinsip design Gojek untuk mobile-first experience
+- **Data Visualization**: Presentasi data yang elegan dan mudah dibaca
-4. Run the application
- ```bash
- flutter run
- ```
+---
-## Architecture
+## 🔧 **Update Terbaru (v1.3.0)**
-This application follows a simple architecture with:
-- Models for data representation
-- API service for network requests
-- Screens for UI
-- Widgets for reusable UI components
-- Utils for constants and helper functions
+### ✅ **Perbaikan UI Overflow & Responsive Design**
+- **Fixed**: RenderFlex overflow 76 pixels pada hasil pencarian dosen
+- **Fixed**: RenderFlex overflow 8.7 pixels pada layout footer dan header
+- **Improved**: Responsive design untuk Android device M2102J20SG (1080x2400)
+- **Enhanced**: Text wrapping dan ellipsis untuk nama dosen panjang
+- **Optimized**: Ukuran font dan spacing untuk efisiensi ruang layar
-## API
+### 🔧 **Perbaikan API & Error Handling**
+- **Fixed**: API 404 error saat fetch detail dosen dengan multiple endpoint fallback
+- **Added**: Input validation dan sanitization untuk search query
+- **Enhanced**: Timeout handling 30 detik untuk request API
+- **Improved**: Error messages yang user-friendly berdasarkan jenis error
+- **Added**: Memory leak prevention dengan mounted check
-This app uses the unofficial PDDIKTI API wrapper, which provides access to various data from [PDDIKTI Kemdikbud](https://pddikti.kemdikbud.go.id/). The API allows searching for students, lecturers, universities, and study programs.
+### 🎨 **Komponen UI Baru**
+- **CtOSErrorBoundary**: Widget untuk error handling yang konsisten
+- **CtOSLoadingWidget**: Loading states dengan animasi ctOS theme
+- **CtOSEmptyWidget**: Empty states dengan styling yang seragam
-## Screenshots
+### 🚀 **Peningkatan Performa (v1.2.0)**
+- **Real Data Display**: Hasil pencarian menampilkan data asli dari PDDikti API
+- **Better Error Handling**: Fallback yang lebih baik ketika API mengalami masalah
+- **Consistent UI**: Data yang ditampilkan konsisten antara pencarian dan detail view
-[Add screenshots here]
+---
-## License
+## ✨ **Fitur Utama**
-This project is licensed under the MIT License - see the LICENSE file for details.
+### 🔍 **Database Scanner**
+- **Multi-Source Search**: Pencarian dari berbagai API pendidikan Indonesia
+- **Real-time Results**: Hasil pencarian langsung dengan animasi loading
+- **Smart Filtering**: Filter berdasarkan perguruan tinggi, program studi
+- **Comprehensive Data**: Akses ke semua data yang tersedia dari PDDikti API
-## Acknowledgments
+### 👨🏫 **Profil Dosen Lengkap**
+- ✅ **Informasi Personal**: Nama, NIDN/NIDK, gelar, jenis kelamin, tempat/tanggal lahir
+- ✅ **Status Kepegawaian**: Ikatan kerja, status aktivitas, jabatan akademik
+- ✅ **Riwayat Pendidikan**: S1/S2/S3, perguruan tinggi asal, tahun lulus
+- ✅ **Jabatan Fungsional**: Asisten Ahli, Lektor, Lektor Kepala, Guru Besar
+- ✅ **Sertifikasi Dosen**: Status, tahun, nomor sertifikat
+- ✅ **Riwayat Mengajar**: Mata kuliah, semester, perguruan tinggi
+- ✅ **Portfolio Akademik**: Penelitian, pengabdian, karya ilmiah, paten
+- ✅ **Homebase & Penugasan**: Status homebase, riwayat penugasan
-- [IlhamriSKY](https://github.com/IlhamriSKY) for the PDDIKTI-kemdikbud-API Python wrapper that inspired this Flutter implementation.
\ No newline at end of file
+### 🎓 **Profil Mahasiswa Lengkap**
+- ✅ **Informasi Personal**: Nama, NIM, jenis kelamin, tempat/tanggal lahir, alamat
+- ✅ **Status Akademik**: Aktif, cuti, lulus, DO, semester saat ini
+- ✅ **Perguruan Tinggi**: Nama PT, program studi, akreditasi
+- ✅ **Riwayat Studi**: Tahun masuk, jalur masuk, semester aktif terakhir
+- ✅ **Transkrip Nilai**: Mata kuliah, nilai huruf & angka, SKS, IP per semester
+- ✅ **Riwayat Kelas**: Mata kuliah, nama dosen pengajar, kelas/kelompok
+- ✅ **Data Kelulusan**: Tanggal lulus, nomor ijazah, IPK, predikat, judul skripsi
+
+### 🏛️ **Database Perguruan Tinggi**
+- **Informasi PT**: Nama, status, akreditasi, alamat
+- **Program Studi**: Daftar prodi, akreditasi, jenjang
+- **Statistik**: Jumlah dosen, mahasiswa, lulusan
+
+---
+
+## 🛠️ **Teknologi & Arsitektur**
+
+### **Frontend**
+- **Flutter 3.x**: Cross-platform mobile development
+- **Dart**: Programming language
+- **Material Design 3**: Modern UI components
+- **Custom Widgets**: ctOS-themed components
+
+### **Backend Integration**
+- **PDDikti API**: Sumber data utama Kementerian Pendidikan
+- **Multi-API Factory**: Integrasi berbagai sumber data
+- **HTTP Client**: Networking dengan error handling
+- **JSON Parsing**: Robust data processing
+
+### **Architecture Pattern**
+- **Clean Architecture**: Separation of concerns
+- **Repository Pattern**: Data abstraction layer
+- **Factory Pattern**: API service management
+- **Singleton Pattern**: State management
+
+---
+
+## 🚀 **Instalasi & Setup**
+
+### **Prerequisites**
+```bash
+Flutter SDK >= 3.0.0
+Dart SDK >= 3.0.0
+Android Studio / VS Code
+Android Device / Emulator
+```
+
+### **Clone Repository**
+```bash
+git clone https://github.com/el-pablos/DB-Cracker.git
+cd DB-Cracker
+```
+
+### **Install Dependencies**
+```bash
+flutter pub get
+```
+
+### **Run Application**
+```bash
+# Debug mode
+flutter run
+
+# Release mode
+flutter run --release
+
+# Specific device
+flutter run -d
+```
+
+---
+
+## 🎯 **Penggunaan**
+
+### **1. Pencarian Dosen**
+1. Buka aplikasi dan pilih "Cari Dosen"
+2. Masukkan nama dosen yang ingin dicari
+3. Gunakan filter perguruan tinggi jika diperlukan
+4. Tap pada hasil untuk melihat detail lengkap
+
+### **2. Pencarian Mahasiswa**
+1. Pilih "Cari Mahasiswa" dari menu utama
+2. Masukkan nama atau NIM mahasiswa
+3. Filter berdasarkan perguruan tinggi atau program studi
+4. Akses profil lengkap dengan riwayat akademik
+
+### **3. Database Perguruan Tinggi**
+1. Pilih "Database PT" untuk menjelajahi perguruan tinggi
+2. Cari berdasarkan nama atau lokasi
+3. Lihat detail lengkap termasuk program studi
+
+---
+
+## 🔧 **Konfigurasi**
+
+### **API Configuration**
+```dart
+// lib/utils/constants.dart
+class ApiConstants {
+ static const String pddiktiBaseUrl = 'https://api-pddikti.kemdiktisaintek.go.id';
+ static const int requestTimeout = 30; // seconds
+ static const bool enableMockData = false; // for testing
+}
+```
+
+### **Theme Customization**
+```dart
+// lib/utils/constants.dart
+class CtOSColors {
+ static const Color primary = Color(0xFF00FF41);
+ static const Color secondary = Color(0xFF00D4FF);
+ static const Color background = Color(0xFF0A0A0A);
+ static const Color surface = Color(0xFF1A1A1A);
+}
+```
+
+---
+
+## 🤝 **Contributing**
+
+Kontribusi sangat diterima! Silakan ikuti langkah berikut:
+
+1. **Fork** repository ini
+2. **Create** feature branch (`git checkout -b feature/AmazingFeature`)
+3. **Commit** perubahan (`git commit -m 'Add: AmazingFeature'`)
+4. **Push** ke branch (`git push origin feature/AmazingFeature`)
+5. **Open** Pull Request
+
+### **Commit Convention**
+```
+add: menambahkan fitur baru
+fix: memperbaiki bug
+update: memperbarui fitur yang ada
+remove: menghapus fitur/file
+docs: perubahan dokumentasi
+style: perubahan styling/UI
+refactor: refactoring code
+test: menambahkan/memperbaiki test
+```
+
+---
+
+## 📄 **License**
+
+Distributed under the MIT License. See `LICENSE` for more information.
+
+---
+
+## 👨💻 **Author**
+
+
+
+**Pablos**
+*Full-Stack Developer & Mobile App Specialist*
+
+[](https://github.com/el-pablos)
+[](https://linkedin.com/in/pablos)
+[](mailto:yeteprem.end23juni@gmail.com)
+
+*"Building the future, one line of code at a time"*
+
+
+
+---
+
+## 🙏 **Acknowledgments**
+
+- **Kementerian Pendidikan Indonesia** - Untuk API PDDikti
+- **Flutter Team** - Framework yang luar biasa
+- **Watch Dogs Series** - Inspirasi design ctOS
+- **Gojek Design Team** - Referensi responsive layout
+- **Open Source Community** - Dukungan dan kontribusi
+
+---
+
+
+
+**⭐ Jika proyek ini membantu, jangan lupa berikan star! ⭐**
+
+*Made with ❤️ by Pablos*
+
+
\ No newline at end of file
diff --git a/lib/api/api_factory.dart b/lib/api/api_factory.dart
index 7cc6f5c..4398474 100644
--- a/lib/api/api_factory.dart
+++ b/lib/api/api_factory.dart
@@ -11,61 +11,80 @@ import '../models/pt.dart';
class ApiFactory {
/// Singleton instance
static final ApiFactory _instance = ApiFactory._internal();
-
+
/// Private constructor
ApiFactory._internal();
-
+
/// Factory constructor
factory ApiFactory() {
return _instance;
}
-
+
/// Real API instance
final PddiktiApi _realApi = PddiktiApi();
-
+
/// Mock API instance for web
final MockPddiktiService _mockService = MockPddiktiService();
-
+
/// Flag to force use of mock data
bool _forceMock = false;
-
+
/// Enable mock data for testing
void enableMockData() {
_forceMock = true;
}
-
+
/// Disable mock data
void disableMockData() {
_forceMock = false;
}
-
+
/// Should use mock data?
bool get _useMockData {
- // In web environments, we might want to use mock data to avoid CORS issues
- // Also use mock if it's explicitly forced
- return _forceMock || (kIsWeb && !kDebugMode);
+ // Prioritaskan API asli, hanya gunakan mock jika dipaksa
+ // Untuk web production, tetap coba API asli dulu
+ final shouldUseMock = _forceMock;
+ print(
+ 'ApiFactory._useMockData: $shouldUseMock (forceMock: $_forceMock, kIsWeb: $kIsWeb, kDebugMode: $kDebugMode)');
+ return shouldUseMock;
}
-
+
/// Pencarian mahasiswa
Future> searchMahasiswa(String keyword) async {
+ print(
+ 'ApiFactory.searchMahasiswa: keyword="$keyword", useMockData=$_useMockData');
+
if (_useMockData) {
- return _mockService.searchMahasiswa(keyword);
+ print('ApiFactory.searchMahasiswa: Using mock service');
+ final results = await _mockService.searchMahasiswa(keyword);
+ print(
+ 'ApiFactory.searchMahasiswa: Mock service returned ${results.length} results');
+ return results;
} else {
try {
- return await _realApi.searchMahasiswa(keyword);
+ print('ApiFactory.searchMahasiswa: Using real API');
+ final results = await _realApi.searchMahasiswa(keyword);
+ print(
+ 'ApiFactory.searchMahasiswa: Real API returned ${results.length} results');
+ return results;
} catch (e) {
print('Error with real API, fallback to mock: $e');
// Fallback to mock data if the real API fails with specific errors
- if (e.toString().contains('403') ||
+ if (e.toString().contains('403') ||
e.toString().contains('CORS') ||
e.toString().contains('XMLHttpRequest')) {
- return _mockService.searchMahasiswa(keyword);
+ print(
+ 'ApiFactory.searchMahasiswa: Fallback to mock service due to API error');
+ final results = await _mockService.searchMahasiswa(keyword);
+ print(
+ 'ApiFactory.searchMahasiswa: Mock fallback returned ${results.length} results');
+ return results;
}
rethrow;
}
}
}
-
+
/// Detail mahasiswa
Future getMahasiswaDetail(String mahasiswaId) async {
if (_useMockData) {
@@ -76,13 +95,13 @@ class ApiFactory {
return await _realApi.getMahasiswaDetail(mahasiswaId);
} catch (e) {
print('Error with real API, fallback to mock: $e');
-
+
// Always fallback to mock on detail errors to ensure the UI can show something
try {
return _mockService.getMahasiswaDetail(mahasiswaId);
} catch (mockError) {
print('Error with mock service too: $mockError');
-
+
// If even the mock service fails, create a minimal valid object
return MahasiswaDetail(
id: mahasiswaId,
@@ -104,27 +123,51 @@ class ApiFactory {
}
}
}
-
+
/// Pencarian dosen
Future> searchDosen(String keyword) async {
+ print(
+ 'ApiFactory.searchDosen: keyword="$keyword", useMockData=$_useMockData');
+
if (_useMockData) {
- return _mockService.searchDosen(keyword);
+ print('ApiFactory.searchDosen: Using mock service');
+ final results = await _mockService.searchDosen(keyword);
+ print(
+ 'ApiFactory.searchDosen: Mock service returned ${results.length} results');
+ for (int i = 0; i < results.length && i < 3; i++) {
+ print(
+ 'ApiFactory.searchDosen: Mock result $i: ${results[i].nama} (${results[i].nidn})');
+ }
+ return results;
} else {
try {
- return await _realApi.searchDosen(keyword);
+ print('ApiFactory.searchDosen: Using real API');
+ final results = await _realApi.searchDosen(keyword);
+ print(
+ 'ApiFactory.searchDosen: Real API returned ${results.length} results');
+ for (int i = 0; i < results.length && i < 3; i++) {
+ print(
+ 'ApiFactory.searchDosen: Real result $i: ${results[i].nama} (${results[i].nidn})');
+ }
+ return results;
} catch (e) {
print('Error with real API, fallback to mock: $e');
// Fallback to mock data if the real API fails with specific errors
- if (e.toString().contains('403') ||
+ if (e.toString().contains('403') ||
e.toString().contains('CORS') ||
e.toString().contains('XMLHttpRequest')) {
- return _mockService.searchDosen(keyword);
+ print(
+ 'ApiFactory.searchDosen: Fallback to mock service due to API error');
+ final results = await _mockService.searchDosen(keyword);
+ print(
+ 'ApiFactory.searchDosen: Mock fallback returned ${results.length} results');
+ return results;
}
rethrow;
}
}
}
-
+
/// Pencarian program studi
Future> searchProdi(String keyword) async {
if (_useMockData) {
@@ -136,7 +179,7 @@ class ApiFactory {
} catch (e) {
print('Error with real API, fallback to mock: $e');
// Fallback to mock data if the real API fails with specific errors
- if (e.toString().contains('403') ||
+ if (e.toString().contains('403') ||
e.toString().contains('CORS') ||
e.toString().contains('XMLHttpRequest')) {
// Implementasi mock untuk prodi jika diperlukan
@@ -146,7 +189,7 @@ class ApiFactory {
}
}
}
-
+
/// Pencarian perguruan tinggi
Future> searchPt(String keyword) async {
if (_useMockData) {
@@ -158,7 +201,7 @@ class ApiFactory {
} catch (e) {
print('Error with real API, fallback to mock: $e');
// Fallback to mock data if the real API fails with specific errors
- if (e.toString().contains('403') ||
+ if (e.toString().contains('403') ||
e.toString().contains('CORS') ||
e.toString().contains('XMLHttpRequest')) {
// Implementasi mock untuk PT jika diperlukan
@@ -168,7 +211,7 @@ class ApiFactory {
}
}
}
-
+
/// Mendapatkan detail program studi
Future getDetailProdi(String prodiId) async {
if (_useMockData) {
@@ -211,7 +254,7 @@ class ApiFactory {
return await _realApi.getDetailProdi(prodiId);
} catch (e) {
print('Error with real API, fallback to mock: $e');
-
+
// Fallback to mock data
return ProdiDetail(
idSp: '',
@@ -249,7 +292,7 @@ class ApiFactory {
}
}
}
-
+
/// Mendapatkan detail perguruan tinggi
Future getDetailPt(String ptId) async {
if (_useMockData) {
@@ -284,7 +327,7 @@ class ApiFactory {
return await _realApi.getDetailPt(ptId);
} catch (e) {
print('Error with real API, fallback to mock: $e');
-
+
// Fallback to mock data
return PerguruanTinggiDetail(
kelompok: 'Universitas',
@@ -314,7 +357,7 @@ class ApiFactory {
}
}
}
-
+
/// Mendapatkan daftar program studi di perguruan tinggi
Future> getProdiPt(String ptId, int tahun) async {
if (_useMockData) {
@@ -326,7 +369,7 @@ class ApiFactory {
} catch (e) {
print('Error with real API, fallback to mock: $e');
// Fallback to mock data if the real API fails with specific errors
- if (e.toString().contains('403') ||
+ if (e.toString().contains('403') ||
e.toString().contains('CORS') ||
e.toString().contains('XMLHttpRequest')) {
// Implementasi mock untuk daftar prodi di PT jika diperlukan
@@ -336,7 +379,7 @@ class ApiFactory {
}
}
}
-
+
/// Getter untuk mendapatkan MockPddiktiService
MockPddiktiService getMockService() {
return _mockService;
@@ -358,13 +401,13 @@ class ApiFactory {
return await _realApi.getDosenProfile(dosenId);
} catch (e) {
print('Error dengan API asli, fallback ke mock: $e');
-
+
// Fallback ke mock data
try {
return await _mockService.getDosenProfile(dosenId);
} catch (mockError) {
print('Error dengan mock service juga: $mockError');
-
+
// Jika bahkan mock service gagal, buat objek minimal valid
return DosenDetail(
idSdm: dosenId,
@@ -387,4 +430,58 @@ class ApiFactory {
}
}
}
-}
\ No newline at end of file
+
+ /// Mendapatkan detail lengkap dosen dengan semua data
+ Future getDosenDetailLengkap(String dosenId) async {
+ if (_useMockData) {
+ // Gunakan mock service untuk testing
+ try {
+ return await _mockService.getDosenProfile(dosenId);
+ } catch (e) {
+ print('Error dengan mock service: $e');
+ rethrow;
+ }
+ } else {
+ try {
+ print('Meminta detail lengkap dosen dari API asli untuk id: $dosenId');
+ return await _realApi.getDosenDetailLengkap(dosenId);
+ } catch (e) {
+ print('Error dengan API asli, fallback ke profil dasar: $e');
+
+ // Fallback ke profil dasar
+ try {
+ return await _realApi.getDosenProfile(dosenId);
+ } catch (profileError) {
+ print(
+ 'Error dengan profil dasar juga, fallback ke mock: $profileError');
+
+ // Fallback ke mock data
+ try {
+ return await _mockService.getDosenProfile(dosenId);
+ } catch (mockError) {
+ print('Error dengan mock service juga: $mockError');
+
+ // Jika semua gagal, buat objek minimal valid
+ return DosenDetail(
+ idSdm: dosenId,
+ namaDosen: 'Data tidak tersedia',
+ namaPt: 'Data tidak tersedia',
+ namaProdi: 'Data tidak tersedia',
+ jenisKelamin: '-',
+ jabatanAkademik: '-',
+ pendidikanTertinggi: '-',
+ statusIkatanKerja: '-',
+ statusAktivitas: '-',
+ penelitian: [],
+ pengabdian: [],
+ karya: [],
+ paten: [],
+ riwayatStudi: [],
+ riwayatMengajar: [],
+ );
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/api/multi_api_factory.dart b/lib/api/multi_api_factory.dart
index f3b141d..0f67c80 100644
--- a/lib/api/multi_api_factory.dart
+++ b/lib/api/multi_api_factory.dart
@@ -15,65 +15,66 @@ class MultiApiFactory {
/// Private constructor
MultiApiFactory._internal();
-
+
/// Factory constructor
factory MultiApiFactory() {
return _instance;
}
-
+
/// API Factory untuk PDDIKTI
final ApiFactory _pddiktiApi = ApiFactory();
-
+
/// API Services Integration
final ApiServicesIntegration _apiServices = ApiServicesIntegration();
-
+
/// Base URL untuk API Data Mahasiswa Kemdikbud
final String _kemdikbudApiUrl = 'https://api-frontend.kemdikbud.go.id';
-
+
/// Header untuk request
Map get _headers => {
- 'Accept': 'application/json, text/plain, */*',
- 'Accept-Language': 'en-US,en;q=0.9,id;q=0.8',
- 'Origin': 'https://indonesia-public-static-api.vercel.app',
- 'Referer': 'https://indonesia-public-static-api.vercel.app',
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
- };
-
+ 'Accept': 'application/json, text/plain, */*',
+ 'Accept-Language': 'en-US,en;q=0.9,id;q=0.8',
+ 'Origin': 'https://indonesia-public-static-api.vercel.app',
+ 'Referer': 'https://indonesia-public-static-api.vercel.app',
+ 'User-Agent':
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
+ };
+
/// Encode parameter URL
String _parseString(String text) {
return Uri.encodeComponent(text);
}
-
+
/// Metode utama untuk mencari data mahasiswa dari berbagai sumber API
Future> searchAllSources(String keyword) async {
List results = [];
List>> futures = [];
-
+
// Cari data dari PDDIKTI
futures.add(_pddiktiApi.searchMahasiswa(keyword));
-
+
// Cari data dari Kemdikbud
futures.add(_searchKemdikbud(keyword));
-
+
// Cari data dari API lain dan konversi ke model Mahasiswa
futures.add(_searchFromEducationApis(keyword));
-
+
// Jalankan semua pencarian secara paralel
try {
final responses = await Future.wait(futures);
-
+
// Gabungkan semua hasil
for (var response in responses) {
results.addAll(response);
}
-
+
// Hapus duplikat berdasarkan kombinasi nama dan nim
final uniqueResults = {};
for (var mahasiswa in results) {
final key = '${mahasiswa.nama}-${mahasiswa.nim}';
uniqueResults[key] = mahasiswa;
}
-
+
return uniqueResults.values.toList();
} catch (e) {
print('Error mencari dari semua sumber: $e');
@@ -81,13 +82,13 @@ class MultiApiFactory {
return results;
}
}
-
+
/// Cari data mahasiswa dari API pendidikan lain
Future> _searchFromEducationApis(String keyword) async {
try {
// Dapatkan data dari API pendidikan
final rawData = await _apiServices.searchEducationData(keyword);
-
+
// Konversi ke model Mahasiswa
return _apiServices.convertToMahasiswa(rawData);
} catch (e) {
@@ -95,115 +96,104 @@ class MultiApiFactory {
return [];
}
}
-
+
/// Cari data mahasiswa dari API Kemdikbud
Future> _searchKemdikbud(String keyword) async {
try {
- final Uri url = Uri.parse('$_kemdikbudApiUrl/hit_mhs/${_parseString(keyword)}');
-
- final response = await http.get(
- url,
- headers: _headers,
- ).timeout(
- Duration(seconds: 10),
- );
-
+ final Uri url =
+ Uri.parse('$_kemdikbudApiUrl/hit_mhs/${_parseString(keyword)}');
+
+ final response = await http
+ .get(
+ url,
+ headers: _headers,
+ )
+ .timeout(
+ Duration(seconds: 10),
+ );
+
if (response.statusCode == 200) {
final Map data = jsonDecode(response.body);
-
+
if (data.containsKey('mahasiswa') && data['mahasiswa'] is List) {
final List mahasiswaList = data['mahasiswa'] as List;
-
- return mahasiswaList.map((item) {
- if (item is Map) {
- return Mahasiswa(
- id: item['id_mahasiswa'] ?? '',
- nama: item['nm_mhs'] ?? '',
- nim: item['nipd'] ?? '',
- namaPt: item['nm_pt'] ?? '',
- singkatanPt: item['kode_pt'] ?? '',
- namaProdi: item['nm_prodi'] ?? '',
- );
- }
- return Mahasiswa(
- id: '',
- nama: '',
- nim: '',
- namaPt: '',
- singkatanPt: '',
- namaProdi: '',
- );
- }).where((m) => m.id.isNotEmpty).toList();
+
+ return mahasiswaList
+ .map((item) {
+ if (item is Map) {
+ return Mahasiswa(
+ id: item['id_mahasiswa'] ?? '',
+ nama: item['nm_mhs'] ?? '',
+ nim: item['nipd'] ?? '',
+ namaPt: item['nm_pt'] ?? '',
+ singkatanPt: item['kode_pt'] ?? '',
+ namaProdi: item['nm_prodi'] ?? '',
+ );
+ }
+ return Mahasiswa(
+ id: '',
+ nama: '',
+ nim: '',
+ namaPt: '',
+ singkatanPt: '',
+ namaProdi: '',
+ );
+ })
+ .where((m) => m.id.isNotEmpty)
+ .toList();
}
}
-
+
return [];
} catch (e) {
print('Error mencari dari Kemdikbud: $e');
return [];
}
}
-
+
/// Cari data dosen dari berbagai sumber
Future> searchAllDosen(String keyword) async {
try {
List results = [];
List>> futures = [];
-
+
// Cari dari PDDIKTI
futures.add(_pddiktiApi.searchDosen(keyword));
-
+
// Cari dari API lain
futures.add(_searchDosenFromOtherSources(keyword));
-
+
// Jalankan semua pencarian secara paralel
final responses = await Future.wait(futures);
-
+
// Gabungkan semua hasil
for (var response in responses) {
results.addAll(response);
}
-
+
// Hapus duplikat berdasarkan kombinasi nama dan nidn
final uniqueResults = {};
for (var dosen in results) {
final key = '${dosen.nama}-${dosen.nidn}';
uniqueResults[key] = dosen;
}
-
+
return uniqueResults.values.toList();
} catch (e) {
print('Error mencari dosen: $e');
// Jika terjadi error, coba kembalikan apa saja yang berhasil
List backupResults = [];
-
+
try {
// Coba dapatkan dari mock service sebagai fallback
backupResults = await _pddiktiApi.searchDosen(keyword);
} catch (e2) {
print('Error getting data from PDDIKTI: $e2');
-
- // Jika masih error, coba return data mock sederhana
- backupResults = [
- Dosen(
- id: '1',
- nama: 'Dr. Mock Data',
- nidn: '12345',
- namaPt: 'Universitas Testing',
- singkatanPt: 'UNTEST',
- namaProdi: 'Informatika',
- ),
- Dosen(
- id: '2',
- nama: 'Prof. Dummy Data',
- nidn: '67890',
- namaPt: 'Institut Testing',
- singkatanPt: 'IT',
- namaProdi: 'Teknik Informatika',
- ),
- ];
+
+ // Jika masih error, return empty list daripada data dummy
+ backupResults = [];
}
-
+
return backupResults;
}
}
@@ -213,7 +203,7 @@ class MultiApiFactory {
try {
// Dapatkan data dari API pendidikan
final rawData = await _apiServices.searchEducationData(keyword);
-
+
// Konversi ke model Dosen
return _apiServices.convertToDosen(rawData);
} catch (e) {
@@ -227,7 +217,7 @@ class MultiApiFactory {
try {
// Coba dapatkan dari PDDIKTI terlebih dahulu
final detail = await _pddiktiApi.getMahasiswaDetail(mahasiswaId);
-
+
// Tambahkan data eksternal jika ada
try {
// Coba untuk memperkaya data dengan sumber-sumber lain
@@ -241,11 +231,11 @@ class MultiApiFactory {
print('Gagal mendapatkan data tambahan: $e');
// Tidak perlu melakukan apa-apa, gunakan data yang sudah ada
}
-
+
return detail;
} catch (e) {
print('Error mendapatkan detail dari PDDIKTI: $e');
-
+
// Fallback to minimal detail
return MahasiswaDetail(
id: mahasiswaId,
@@ -266,12 +256,12 @@ class MultiApiFactory {
}
}
- /// Mendapatkan detail dosen dari berbagai sumber
+ /// Mendapatkan detail dosen lengkap dari berbagai sumber
Future getDosenDetailFromAllSources(String dosenId) async {
try {
- // Coba dapatkan dari PDDIKTI terlebih dahulu
- final detail = await _pddiktiApi.getDosenProfile(dosenId);
-
+ // Coba dapatkan detail lengkap dari PDDIKTI terlebih dahulu
+ final detail = await _pddiktiApi.getDosenDetailLengkap(dosenId);
+
// Tambahkan data eksternal jika ada
try {
// Coba untuk memperkaya data dengan sumber-sumber lain jika ada waktu
@@ -280,11 +270,11 @@ class MultiApiFactory {
print('Gagal mendapatkan data tambahan: $e');
// Tidak perlu melakukan apa-apa, gunakan data yang sudah ada
}
-
+
return detail;
} catch (e) {
print('Error mendapatkan detail dari PDDIKTI: $e');
-
+
// Fallback to minimal detail
return DosenDetail(
idSdm: dosenId,
@@ -305,22 +295,25 @@ class MultiApiFactory {
);
}
}
-
+
/// Mencari detail mahasiswa dari API Kemdikbud
Future _searchKemdikbudDetail(String mahasiswaId) async {
try {
- final Uri url = Uri.parse('$_kemdikbudApiUrl/detail_mhs/${_parseString(mahasiswaId)}');
-
- final response = await http.get(
- url,
- headers: _headers,
- ).timeout(
- Duration(seconds: 10),
- );
-
+ final Uri url = Uri.parse(
+ '$_kemdikbudApiUrl/detail_mhs/${_parseString(mahasiswaId)}');
+
+ final response = await http
+ .get(
+ url,
+ headers: _headers,
+ )
+ .timeout(
+ Duration(seconds: 10),
+ );
+
if (response.statusCode == 200) {
final dynamic data = jsonDecode(response.body);
-
+
if (data is Map) {
// Konversi ke model MahasiswaDetail
return MahasiswaDetail(
@@ -341,14 +334,14 @@ class MultiApiFactory {
);
}
}
-
+
return null;
} catch (e) {
print('Error mencari detail dari Kemdikbud: $e');
return null;
}
}
-
+
/// Mendapatkan informasi Perguruan Tinggi
Future getDetailPT(String ptId) async {
try {
@@ -357,7 +350,7 @@ class MultiApiFactory {
return detail;
} catch (e) {
print('Error mendapatkan detail PT: $e');
-
+
// Buat data dummy jika error
return PerguruanTinggiDetail(
kelompok: '-',
@@ -386,7 +379,7 @@ class MultiApiFactory {
);
}
}
-
+
/// Mendapatkan informasi Program Studi
Future getDetailProdi(String prodiId) async {
try {
@@ -395,7 +388,7 @@ class MultiApiFactory {
return detail;
} catch (e) {
print('Error mendapatkan detail Prodi: $e');
-
+
// Buat data dummy jika error
return ProdiDetail(
idSp: '-',
@@ -432,7 +425,7 @@ class MultiApiFactory {
);
}
}
-
+
/// Mencari data Program Studi
Future> searchProdi(String keyword) async {
try {
@@ -443,7 +436,7 @@ class MultiApiFactory {
return [];
}
}
-
+
/// Mencari data Perguruan Tinggi
Future> searchPT(String keyword) async {
try {
@@ -454,7 +447,7 @@ class MultiApiFactory {
return [];
}
}
-
+
/// Mendapatkan daftar Prodi di PT tertentu
Future> getProdiInPT(String ptId, int tahun) async {
try {
@@ -465,7 +458,7 @@ class MultiApiFactory {
return [];
}
}
-
+
/// Mencari data lokasi prodi
Future