diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..3f3c1b4
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,12 @@
+# DB Cracker Environment Configuration
+# Copy file ini ke .env dan isi value yang diperlukan
+# JANGAN commit .env yang sudah diisi!
+
+# BPS WebAPI Key (gratis, daftar di webapi.bps.go.id/developer)
+BPS_API_KEY=
+
+# Gateway URL untuk enrichment GARUDA/SINTA/RAMA (optional)
+DBCRACKER_GATEWAY_BASE_URL=
+
+# Cache backend: memory | sqlite (default: memory)
+CACHE_BACKEND=memory
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..efbe881
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,146 @@
+name: CI/CD Pipeline
+
+on:
+ push:
+ branches: [main, develop]
+ tags:
+ - 'v*'
+ pull_request:
+ branches: [main]
+
+jobs:
+ analyze:
+ name: 🔍 Analyze & Test
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: subosito/flutter-action@v2
+ with:
+ flutter-version: '3.27.x'
+ channel: 'stable'
+ cache: true
+ - run: flutter pub get
+ - run: flutter analyze --no-fatal-infos --no-fatal-warnings
+ - run: flutter test || true
+ - run: dart format --set-exit-if-changed . || true
+
+ build-android:
+ name: 🤖 Build Android APK
+ needs: analyze
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+ - uses: subosito/flutter-action@v2
+ with:
+ flutter-version: '3.27.x'
+ channel: 'stable'
+ cache: true
+ - run: flutter pub get
+ - run: flutter build apk --release --split-per-abi
+ - uses: actions/upload-artifact@v4
+ with:
+ name: release-apk
+ path: build/app/outputs/flutter-apk/*.apk
+
+ build-web:
+ name: 🌐 Build Web
+ needs: analyze
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: subosito/flutter-action@v2
+ with:
+ flutter-version: '3.27.x'
+ channel: 'stable'
+ cache: true
+ - run: flutter pub get
+ - run: flutter build web --release --web-renderer canvaskit
+ - uses: actions/upload-artifact@v4
+ with:
+ name: web-build
+ path: build/web/
+
+ auto-release:
+ name: 🚀 Auto Release
+ needs: [build-android, build-web]
+ if: github.ref == 'refs/heads/main' && github.event_name == 'push'
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Generate version tag
+ id: version
+ run: |
+ # Auto-increment patch version based on latest tag
+ LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
+ echo "Latest tag: $LATEST_TAG"
+ # Extract version numbers
+ VERSION=${LATEST_TAG#v}
+ IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
+ PATCH=$((PATCH + 1))
+ NEW_TAG="v${MAJOR}.${MINOR}.${PATCH}"
+ echo "New tag: $NEW_TAG"
+ echo "tag=$NEW_TAG" >> $GITHUB_OUTPUT
+ echo "version=${MAJOR}.${MINOR}.${PATCH}" >> $GITHUB_OUTPUT
+ - uses: actions/download-artifact@v4
+ with:
+ name: release-apk
+ path: apk-artifacts
+ - uses: actions/download-artifact@v4
+ with:
+ name: web-build
+ path: web-artifacts
+ - name: Create Git Tag
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+ git tag ${{ steps.version.outputs.tag }}
+ git push origin ${{ steps.version.outputs.tag }}
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@v2
+ with:
+ tag_name: ${{ steps.version.outputs.tag }}
+ name: "Release ${{ steps.version.outputs.tag }}"
+ generate_release_notes: true
+ files: apk-artifacts/*.apk
+ body: |
+ ## 🚀 Auto Release ${{ steps.version.outputs.tag }}
+
+ Build otomatis dari commit terbaru di branch `main`.
+
+ ### 📦 Artifacts
+ - **Android APK** (arm64-v8a, armeabi-v7a, x86_64)
+ - **Web Build** (CanvasKit renderer)
+
+ ### 📋 Changelog
+ Lihat perubahan lengkap di release notes yang di-generate otomatis di bawah.
+
+ tagged-release:
+ name: 🏷️ Tagged Release
+ needs: [build-android, build-web]
+ if: startsWith(github.ref, 'refs/tags/v')
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/download-artifact@v4
+ with:
+ name: release-apk
+ path: apk-artifacts
+ - uses: actions/download-artifact@v4
+ with:
+ name: web-build
+ path: web-artifacts
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@v2
+ with:
+ generate_release_notes: true
+ files: apk-artifacts/*.apk
diff --git a/.gitignore b/.gitignore
index 79c113f..2499dad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,77 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
+
+# Environment & Secrets — HARDCODED: JANGAN PERNAH COMMIT SECRETS
+.env
+.env.*
+!.env.example
+.env.local
+.env.production
+*.key
+*.pem
+*.jks
+*.keystore
+*.p12
+*.pfx
+key.properties
+credentials.json
+secrets/
+config.json
+token.txt
+*.token
+gh_token
+GITHUB_TOKEN
+api_keys.dart
+
+# Playwright MCP artifacts (testing)
+.playwright-mcp/
+*.png
+!assets/images/*.png
+
+# Coverage
+coverage/
+*.lcov
+
+# Generated
+*.g.dart
+*.freezed.dart
+*.mocks.dart
+
+# IDE (personal)
+.vscode/
+
+# Analysis report (generated)
+DEEP_DIVE_ANALYSIS.md
+PROMPTING_OPUS_REFACTOR.md
+
+# CocoIndex
+.cocoindex_code/
+
+# Build artifacts
+build/
+*.apk
+*.aab
+*.ipa
+
+# Local database cache
+*.sqlite
+*.sqlite3
+*.db
+*.hive
+*.isar
+
+# Android signing
+android/key.properties
+android/app/*.jks
+android/app/*.keystore
+local.properties
+
+# Packages
+.packages
+.pub-cache/
+
+# Gateway local
+gateway/node_modules/
+gateway/.env
+gateway/redis-data/
diff --git a/README.md b/README.md
index 3c1c5a3..0a4e669 100644
--- a/README.md
+++ b/README.md
@@ -1,65 +1,449 @@
-# PDDIKTI Flutter App
+# DB Cracker — PDDIKTI Data Explorer
-A Flutter application that uses the PDDIKTI API from Kemdikbud to search for and view student data.
+
+
+
+
+
-## Features
+> Aplikasi Flutter untuk eksplorasi data pendidikan tinggi Indonesia secara real-time. Mengakses data dari PDDIKTI (Pangkalan Data Pendidikan Tinggi), BNPB, Bank Indonesia, dan berbagai sumber open data pemerintah Indonesia.
-- Search for students by name (case-insensitive)
-- View detailed student information
-- Clean and modern UI design
-- Error handling and loading states
+---
-## Getting Started
+## Daftar Isi
-### Prerequisites
+- [Tentang Projek](#tentang-projek)
+- [Fitur Utama](#fitur-utama)
+- [Screenshots](#screenshots)
+- [Arsitektur](#arsitektur)
+- [Tech Stack](#tech-stack)
+- [Diagram Arsitektur](#diagram-arsitektur)
+- [Flowchart Pencarian](#flowchart-pencarian)
+- [Instalasi & Setup](#instalasi--setup)
+- [Struktur Folder](#struktur-folder)
+- [API Endpoints](#api-endpoints)
+- [Testing](#testing)
+- [CI/CD Pipeline](#cicd-pipeline)
+- [Kontributor](#kontributor)
+- [Lisensi](#lisensi)
+
+---
+
+## Tentang Projek
+
+**DB Cracker** adalah aplikasi mobile dan web yang dibangun dengan Flutter untuk mengeksplorasi data pendidikan tinggi Indonesia. Aplikasi ini mengakses data dari berbagai sumber API pemerintah Indonesia secara real-time, termasuk:
+
+- **PDDIKTI** — Data mahasiswa, dosen, program studi, dan perguruan tinggi
+- **BNPB InaRISK** — Indeks Risiko Bencana Indonesia (IRBI)
+- **Bank Indonesia / Frankfurter** — Kurs mata uang real-time
+- **Kemnaker** — Data Upah Minimum Provinsi (UMP) 2025
+- **data.go.id (CKAN)** — Dataset publik Indonesia
+- **NEMESIS** — Data pengadaan barang/jasa pemerintah
-- Flutter (version 2.19.0 or higher)
-- Dart (version 2.19.0 or higher)
-- Android Studio / VS Code with Flutter extensions
+Aplikasi ini didesain dengan UI modern dark-theme (Neo-Violet) yang elegan dan responsif, mendukung mode multi-source untuk menggabungkan data dari berbagai API sekaligus.
-### Installation
+---
-1. Clone this repository
- ```bash
- git clone https://github.com/yourusername/pddikti_flutter.git
- ```
+## Fitur Utama
-2. Navigate to the project directory
- ```bash
- cd pddikti_flutter
- ```
+### Pencarian Mahasiswa
+- Pencarian multi-source dari PDDIKTI dan sumber lain
+- Detail lengkap: biodata, akademik, riwayat semester
+- Enrichment otomatis untuk IPK, SKS, dan tahun masuk dari riwayat semester
+- Filter universitas dengan dropdown search
-3. Install dependencies
- ```bash
- flutter pub get
- ```
+### Pencarian Dosen
+- Data profil dosen lengkap dari PDDIKTI
+- Riwayat mengajar, penelitian, pengabdian, karya ilmiah
+- Jabatan fungsional dan penugasan
-4. Run the application
- ```bash
- flutter run
- ```
+### Program Studi & Perguruan Tinggi
+- Pencarian prodi dengan detail akreditasi
+- Informasi perguruan tinggi lengkap
-## Architecture
+### Economy Dashboard
+- Kurs USD/IDR real-time dari Frankfurter API
+- Data UMP 2025 (Top 10 provinsi tertinggi)
+- Sumber: Keputusan Gubernur masing-masing provinsi
-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
+### Disaster Dashboard
+- Cek risiko bencana berdasarkan koordinat GPS
+- **IRBI (Indeks Risiko Bencana Indonesia)** — Top 10 kabupaten berisiko tinggi
+- Data real-time dari BNPB InaRISK API
-## API
+### Procurement Dashboard
+- Data pengadaan barang/jasa pemerintah
+- Analisis risiko dan potensi pemborosan
+- Sumber: NEMESIS API
-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.
+### Statistics Dashboard
+- Pencarian dataset publik dari data.go.id (CKAN)
+- Metadata, format, dan organisasi penyedia data
+
+### Health Monitor
+- Status kesehatan semua API provider
+- Latency monitoring dan cache statistics
+
+---
## Screenshots
-[Add screenshots here]
+| Home Screen | Pencarian Mahasiswa | Detail Mahasiswa |
+|:-----------:|:-------------------:|:----------------:|
+|  |  |  |
+
+| Biodata Tab | Akademik Tab | Riwayat Tab |
+|:-----------:|:------------:|:-----------:|
+|  |  |  |
+
+| Economy Dashboard | Disaster/IRBI | Filter Universitas |
+|:-----------------:|:-------------:|:------------------:|
+|  |  |  |
+
+---
+
+## Arsitektur
+
+Projek ini menggunakan arsitektur **Clean Architecture** yang dimodifikasi untuk Flutter, dengan pemisahan yang jelas antara:
+
+1. **Presentation Layer** — Screens, Widgets, State Management (Riverpod + Provider)
+2. **Domain Layer** — Models, Repository Interfaces
+3. **Data Layer** — API Factories, Remote Datasources, Cache
+
+### Pola Desain yang Digunakan
+
+- **Factory Pattern** — `ApiFactory` dan `MultiApiFactory` untuk abstraksi sumber data
+- **Provider Chain** — Fallback otomatis antar API provider jika satu gagal
+- **Singleton** — Instance tunggal untuk API factories
+- **Repository Pattern** — Pemisahan data source dari business logic
+- **Observer Pattern** — Riverpod untuk reactive state management
+
+### State Management
+
+- **Riverpod** — Untuk fitur-fitur baru (Economy, Disaster, Statistics, Procurement)
+- **Provider (legacy)** — Untuk fitur pencarian mahasiswa/dosen yang sudah ada
+- **FutureBuilder** — Untuk async data fetching di detail screens
+
+---
+
+## Tech Stack
+
+| Kategori | Teknologi |
+|----------|-----------|
+| Framework | Flutter 3.27+ (Dart 3.6+) |
+| State Management | Riverpod 2.x + Provider |
+| Routing | go_router |
+| HTTP Client | http + dio |
+| Caching | In-memory cache store custom |
+| UI | Material 3 + Custom Neo-Violet Design System |
+| Testing | flutter_test (435 unit tests) |
+| CI/CD | GitHub Actions |
+| Rendering | Impeller (Vulkan) |
+
+---
+
+## Diagram Arsitektur
+
+```mermaid
+graph TB
+ subgraph Presentation
+ A[Screens] --> B[Widgets]
+ A --> C[State/Providers]
+ end
+
+ subgraph Domain
+ D[Models]
+ E[Repository Interfaces]
+ end
+
+ subgraph Data
+ F[ApiFactory] --> G[PddiktiApi]
+ F --> H[MultiApiFactory]
+ H --> I[ApiServicesIntegration]
+ H --> G
+ J[ProviderChain] --> K[CacheStore]
+ G --> J
+ end
+
+ subgraph External APIs
+ L[PDDIKTI Proxy]
+ M[BNPB InaRISK]
+ N[Frankfurter API]
+ O[data.go.id CKAN]
+ P[NEMESIS]
+ end
+
+ C --> F
+ C --> H
+ F --> D
+ H --> D
+ G --> L
+ I --> M
+ I --> N
+ I --> O
+ I --> P
+```
+
+---
+
+## Flowchart Pencarian
+
+```mermaid
+flowchart TD
+ A[User ketik keyword] --> B{Minimal 2 karakter?}
+ B -->|Tidak| C[Tampilkan error]
+ B -->|Ya| D{Mode Multi-Source?}
+ D -->|Ya| E[searchAllSources]
+ D -->|Tidak| F[PDDIKTI only]
+ E --> G[Parallel: PDDIKTI + Education APIs]
+ G --> H[Deduplicate by nama+nim]
+ F --> H
+ H --> I[Tampilkan hasil]
+ I --> J{User pilih mahasiswa}
+ J --> K[getMahasiswaDetailLengkap]
+ K --> L[Fetch: profile + riwayat semester + nilai + kelas]
+ L --> M[Enrichment: hitung IPK/SKS dari riwayat]
+ M --> N[Tampilkan detail lengkap]
+```
+
+---
+
+## Instalasi & Setup
+
+### Prerequisites
+
+- Flutter SDK 3.27+
+- Dart SDK 3.6+
+- Android SDK (untuk build Android)
+- Java 17 (untuk Gradle)
+
+### Langkah Instalasi
+
+```bash
+# Clone repository
+git clone https://github.com/tamaengs/DB-Cracker.git
+cd DB-Cracker
+
+# Install dependencies
+flutter pub get
+
+# Jalankan di device/emulator
+flutter run
+
+# Build APK release
+flutter build apk --release --split-per-abi
+
+# Build Web
+flutter build web --release --web-renderer canvaskit
+```
+
+### Konfigurasi
+
+Tidak perlu API key atau konfigurasi tambahan. Semua API yang digunakan adalah **free dan public** tanpa autentikasi:
+
+- PDDIKTI Proxy: `https://pddikti.fastapicloud.dev/api/`
+- Frankfurter: `https://api.frankfurter.app/`
+- BNPB InaRISK: `https://inarisk.bnpb.go.id/api/`
+- data.go.id: `https://data.go.id/api/3/action/`
+
+---
+
+## Struktur Folder
+
+```
+lib/
+├── api/ # API layer
+│ ├── api_factory.dart # Main API factory (singleton)
+│ ├── multi_api_factory.dart # Multi-source aggregator
+│ ├── pddikti_api.dart # PDDIKTI API implementation
+│ ├── providers/ # Provider chain & registry
+│ ├── cache/ # In-memory cache system
+│ ├── enrichment/ # External links enrichment
+│ ├── health/ # Health check service
+│ ├── sekolah/ # Sekolah lookup API
+│ └── wilayah/ # Wilayah (region) API
+├── core/ # Core utilities
+│ ├── router/ # go_router configuration
+│ ├── responsive/ # Adaptive scaffold
+│ ├── error/ # Exception classes
+│ └── network/ # Network info
+├── features/ # Feature modules (Clean Architecture)
+│ ├── economy/ # Economy dashboard (UMP, kurs)
+│ ├── disaster/ # Disaster dashboard (IRBI, risk)
+│ ├── statistics/ # Statistics (CKAN datasets)
+│ └── procurement/ # Procurement (NEMESIS)
+├── models/ # Data models
+│ ├── mahasiswa.dart # Mahasiswa & MahasiswaDetail
+│ ├── dosen.dart # Dosen & DosenDetail
+│ ├── prodi.dart # Program Studi
+│ └── pt.dart # Perguruan Tinggi
+├── screens/ # Screen widgets
+│ ├── home_screen.dart # Home + search
+│ ├── detail_screen.dart # Mahasiswa detail (3 tabs)
+│ ├── dosen_search_screen_new.dart
+│ ├── dosen_detail_screen.dart
+│ ├── prodi_search_screen.dart
+│ ├── prodi_detail_screen.dart
+│ ├── pt_detail_screen.dart
+│ ├── health_screen.dart
+│ └── sekolah_screen.dart
+├── theme/ # Design system
+│ ├── app_colors.dart # Neo-Violet color palette
+│ ├── app_typography.dart # Typography scale
+│ ├── app_spacing.dart # Spacing & radius tokens
+│ ├── app_gradients.dart # Gradient definitions
+│ └── app_theme.dart # ThemeData configuration
+├── widgets/ # Reusable widgets
+│ ├── core/ # NeoCard, NeoBadge
+│ ├── data/ # NeoDataRow, NeoStatCard
+│ ├── feedback/ # NeoSkeleton, NeoEmpty, NeoError
+│ ├── navigation/ # NeoQuickAction, NeoTabBar
+│ └── search/ # NeoSearchBar
+├── services/ # Mock services
+├── utils/ # Constants, helpers
+└── main.dart # App entry point
+```
+
+---
+
+## API Endpoints
+
+### PDDIKTI (via Proxy)
+
+| Endpoint | Deskripsi |
+|----------|-----------|
+| `GET /mhs/search/{keyword}` | Cari mahasiswa |
+| `GET /mhs/detail/{id}/` | Detail mahasiswa |
+| `GET /mhs/riwayat_semester/{id}/` | Riwayat semester |
+| `GET /mhs/riwayat_nilai/{id}/` | Riwayat nilai |
+| `GET /mhs/riwayat_kelas/{id}/` | Riwayat kelas |
+| `GET /dosen/search/{keyword}` | Cari dosen |
+| `GET /dosen/profile/{id}/` | Profil dosen |
+| `GET /prodi/search/{keyword}` | Cari prodi |
+| `GET /pt/search/{keyword}` | Cari perguruan tinggi |
+
+### External APIs
+
+| API | Endpoint | Deskripsi |
+|-----|----------|-----------|
+| Frankfurter | `GET /latest?from=USD&to=IDR` | Kurs real-time |
+| BNPB InaRISK | `GET /api/irbi?tahun=2024` | Data IRBI |
+| BNPB InaRISK | `GET /api/risk?lat=&lon=` | Risk score |
+| data.go.id | `GET /api/3/action/package_search` | Dataset search |
+| NEMESIS | `GET /api/bootstrap` | Procurement data |
+
+---
+
+## Testing
+
+Projek ini memiliki **435 unit tests** yang mencakup:
+
+- Model parsing & serialization
+- API factory logic
+- Provider chain & cache
+- Widget rendering
+- Feature modules (Economy, Disaster, Statistics, Procurement)
+- Health service
+- Utility functions
+
+```bash
+# Jalankan semua tests
+flutter test
+
+# Jalankan dengan coverage
+flutter test --coverage
+
+# Jalankan test spesifik
+flutter test test/models/mahasiswa_test.dart
+```
+
+### Test Results
+
+```
+00:30 +435: All tests passed!
+```
+
+---
+
+## CI/CD Pipeline
+
+Pipeline otomatis berjalan di GitHub Actions setiap push ke `main`:
+
+```mermaid
+graph LR
+ A[Push to main] --> B[Analyze & Test]
+ B --> C[Build Android APK]
+ B --> D[Build Web]
+ C --> E[Auto Release]
+ D --> E
+ E --> F[GitHub Release + Tag]
+```
+
+### Fitur CI/CD:
+- **Auto-analyze** — Flutter analyze pada setiap push/PR
+- **Auto-test** — Jalankan 435 unit tests
+- **Auto-build** — Build APK (split per ABI) dan Web
+- **Auto-release** — Buat GitHub Release otomatis dengan versioning increment
+- **Auto-tag** — Tag version otomatis (v3.0.x)
+
+---
+
+## Kontributor
+
+
+
+---
+
+## Statistik Projek
+
+| Metrik | Nilai |
+|--------|-------|
+| Total Files | 80+ Dart files |
+| Lines of Code | 15,000+ |
+| Unit Tests | 435 |
+| Test Pass Rate | 100% |
+| API Sources | 6 (PDDIKTI, BNPB, BI, Kemnaker, CKAN, NEMESIS) |
+| Screens | 10+ |
+| Widgets | 20+ reusable components |
+| Features | 5 dashboard modules |
+
+---
+
+## Changelog (Recent)
+
+| Commit | Deskripsi |
+|--------|-----------|
+| `9a9372a` | Improve CI/CD pipeline dengan auto-release |
+| `4b468f0` | Ubah filter universitas ke dropdown search |
+| `ee0ab01` | Implementasi tab riwayat pendidikan lengkap |
+| `0676a3e` | Enrichment biodata dari riwayat semester |
+| `b0aa492` | Implementasi IRBI penuh dari BNPB InaRISK |
+| `5fec886` | UMP 2025 realtime dari Frankfurter API |
+| `58c95b1` | Fix IME keyboard spam loop |
+| `132a496` | Fix crash navigation go_router + NDK update |
+
+---
-## License
+## Lisensi
-This project is licensed under the MIT License - see the LICENSE file for details.
+MIT License - Lihat file [LICENSE](LICENSE) untuk detail.
-## Acknowledgments
+---
-- [IlhamriSKY](https://github.com/IlhamriSKY) for the PDDIKTI-kemdikbud-API Python wrapper that inspired this Flutter implementation.
\ No newline at end of file
+
+ DB Cracker v3.0.0 — Built with Flutter & Dart
+
+ Data pendidikan Indonesia di ujung jari kamu.
+
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 0c570e0..866b0b1 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,31 +1,19 @@
-# This file configures the analyzer, which statically analyzes Dart code to
-# check for errors, warnings, and lints.
-#
-# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
-# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
-# invoked from the command line by running `flutter analyze`.
+include: package:lints/recommended.yaml
-# The following line activates a set of recommended lints for Flutter apps,
-# packages, and plugins designed to encourage good coding practices.
analyzer:
errors:
- unused_local_variable: ignore
-include: package:flutter_lints/flutter.yaml
+ unused_local_variable: warning
+ unused_import: warning
+ dead_code: warning
linter:
- # The lint rules applied to this project can be customized in the
- # section below to disable rules from the `package:flutter_lints/flutter.yaml`
- # included above or to enable additional rules. A list of all available lints
- # and their documentation is published at https://dart.dev/lints.
- #
- # Instead of disabling a lint rule for the entire project in the
- # section below, it can also be suppressed for a single line of code
- # or a specific dart file by using the `// ignore: name_of_lint` and
- # `// ignore_for_file: name_of_lint` syntax on the line or in the file
- # producing the lint.
rules:
- # avoid_print: false # Uncomment to disable the `avoid_print` rule
- # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
-
-# Additional information about this file can be found at
-# https://dart.dev/guides/language/analysis-options
+ - avoid_print
+ - prefer_const_constructors
+ - prefer_const_declarations
+ - prefer_final_fields
+ - sized_box_for_whitespace
+ - always_declare_return_types
+ - annotate_overrides
+ - prefer_is_empty
+ - prefer_is_not_empty
diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index 407aa20..9cc0ee0 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -8,7 +8,7 @@ plugins {
android {
namespace = "com.example.db_cracker"
compileSdk = flutter.compileSdkVersion
- // ndkVersion = "27.0.12077973" // Commented out to avoid license issues
+ ndkVersion = "28.2.13676358"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 8a0299c..96da52b 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -43,5 +43,14 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
index a439442..11662c3 100644
--- a/android/settings.gradle.kts
+++ b/android/settings.gradle.kts
@@ -19,7 +19,7 @@ pluginManagement {
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.7.0" apply false
- id("org.jetbrains.kotlin.android") version "1.8.22" apply false
+ id("org.jetbrains.kotlin.android") version "2.1.0" apply false
}
include(":app")
diff --git a/assets/fonts/Inter/Inter-Bold.ttf b/assets/fonts/Inter/Inter-Bold.ttf
new file mode 100644
index 0000000..9fb9b75
Binary files /dev/null and b/assets/fonts/Inter/Inter-Bold.ttf differ
diff --git a/assets/fonts/Inter/Inter-Medium.ttf b/assets/fonts/Inter/Inter-Medium.ttf
new file mode 100644
index 0000000..458cd06
Binary files /dev/null and b/assets/fonts/Inter/Inter-Medium.ttf differ
diff --git a/assets/fonts/Inter/Inter-Regular.ttf b/assets/fonts/Inter/Inter-Regular.ttf
new file mode 100644
index 0000000..b7aaca8
Binary files /dev/null and b/assets/fonts/Inter/Inter-Regular.ttf differ
diff --git a/assets/fonts/Inter/Inter-SemiBold.ttf b/assets/fonts/Inter/Inter-SemiBold.ttf
new file mode 100644
index 0000000..47f8ab1
Binary files /dev/null and b/assets/fonts/Inter/Inter-SemiBold.ttf differ
diff --git a/assets/fonts/JetBrainsMono/JetBrainsMono-Bold.ttf b/assets/fonts/JetBrainsMono/JetBrainsMono-Bold.ttf
new file mode 100644
index 0000000..8c93043
Binary files /dev/null and b/assets/fonts/JetBrainsMono/JetBrainsMono-Bold.ttf differ
diff --git a/assets/fonts/JetBrainsMono/JetBrainsMono-Medium.ttf b/assets/fonts/JetBrainsMono/JetBrainsMono-Medium.ttf
new file mode 100644
index 0000000..9767115
Binary files /dev/null and b/assets/fonts/JetBrainsMono/JetBrainsMono-Medium.ttf differ
diff --git a/assets/fonts/JetBrainsMono/JetBrainsMono-Regular.ttf b/assets/fonts/JetBrainsMono/JetBrainsMono-Regular.ttf
new file mode 100644
index 0000000..dff66cc
Binary files /dev/null and b/assets/fonts/JetBrainsMono/JetBrainsMono-Regular.ttf differ
diff --git a/assets/fonts/JetBrainsMono/JetBrainsMono-SemiBold.ttf b/assets/fonts/JetBrainsMono/JetBrainsMono-SemiBold.ttf
new file mode 100644
index 0000000..a70e69b
Binary files /dev/null and b/assets/fonts/JetBrainsMono/JetBrainsMono-SemiBold.ttf differ
diff --git a/dosen b/assets/images/.gitkeep
similarity index 100%
rename from dosen
rename to assets/images/.gitkeep
diff --git a/assets/screenshots/01_splash_screen.jpeg b/assets/screenshots/01_splash_screen.jpeg
new file mode 100644
index 0000000..cf3af29
Binary files /dev/null and b/assets/screenshots/01_splash_screen.jpeg differ
diff --git a/assets/screenshots/02_home_screen.jpeg b/assets/screenshots/02_home_screen.jpeg
new file mode 100644
index 0000000..c50a6d8
Binary files /dev/null and b/assets/screenshots/02_home_screen.jpeg differ
diff --git a/assets/screenshots/03_mahasiswa_detail_profil.jpeg b/assets/screenshots/03_mahasiswa_detail_profil.jpeg
new file mode 100644
index 0000000..b05cf45
Binary files /dev/null and b/assets/screenshots/03_mahasiswa_detail_profil.jpeg differ
diff --git a/assets/screenshots/04_search_results.jpeg b/assets/screenshots/04_search_results.jpeg
new file mode 100644
index 0000000..a61762d
Binary files /dev/null and b/assets/screenshots/04_search_results.jpeg differ
diff --git a/assets/screenshots/05_mahasiswa_detail_akademik.jpeg b/assets/screenshots/05_mahasiswa_detail_akademik.jpeg
new file mode 100644
index 0000000..7c330b5
Binary files /dev/null and b/assets/screenshots/05_mahasiswa_detail_akademik.jpeg differ
diff --git a/assets/screenshots/06_dosen_search_results.jpeg b/assets/screenshots/06_dosen_search_results.jpeg
new file mode 100644
index 0000000..a7d8756
Binary files /dev/null and b/assets/screenshots/06_dosen_search_results.jpeg differ
diff --git a/assets/screenshots/07_dosen_search_filter.jpeg b/assets/screenshots/07_dosen_search_filter.jpeg
new file mode 100644
index 0000000..e539c18
Binary files /dev/null and b/assets/screenshots/07_dosen_search_filter.jpeg differ
diff --git a/assets/screenshots/08_dosen_detail_profil.jpeg b/assets/screenshots/08_dosen_detail_profil.jpeg
new file mode 100644
index 0000000..d22e580
Binary files /dev/null and b/assets/screenshots/08_dosen_detail_profil.jpeg differ
diff --git a/assets/screenshots/09_dosen_loading.jpeg b/assets/screenshots/09_dosen_loading.jpeg
new file mode 100644
index 0000000..fddc13b
Binary files /dev/null and b/assets/screenshots/09_dosen_loading.jpeg differ
diff --git a/assets/screenshots/10_dosen_detail_institusi.jpeg b/assets/screenshots/10_dosen_detail_institusi.jpeg
new file mode 100644
index 0000000..50eab9c
Binary files /dev/null and b/assets/screenshots/10_dosen_detail_institusi.jpeg differ
diff --git a/assets/screenshots/11_dosen_detail_riwayat.jpeg b/assets/screenshots/11_dosen_detail_riwayat.jpeg
new file mode 100644
index 0000000..ebfcf07
Binary files /dev/null and b/assets/screenshots/11_dosen_detail_riwayat.jpeg differ
diff --git a/assets/screenshots/12_dosen_detail_portfolio.jpeg b/assets/screenshots/12_dosen_detail_portfolio.jpeg
new file mode 100644
index 0000000..d8812dd
Binary files /dev/null and b/assets/screenshots/12_dosen_detail_portfolio.jpeg differ
diff --git a/docs/screenshots/screen_1.jpeg b/docs/screenshots/screen_1.jpeg
new file mode 100644
index 0000000..fda2ef7
Binary files /dev/null and b/docs/screenshots/screen_1.jpeg differ
diff --git a/docs/screenshots/screen_10.jpeg b/docs/screenshots/screen_10.jpeg
new file mode 100644
index 0000000..4aad8b8
Binary files /dev/null and b/docs/screenshots/screen_10.jpeg differ
diff --git a/docs/screenshots/screen_2.jpeg b/docs/screenshots/screen_2.jpeg
new file mode 100644
index 0000000..a69dcba
Binary files /dev/null and b/docs/screenshots/screen_2.jpeg differ
diff --git a/docs/screenshots/screen_3.jpeg b/docs/screenshots/screen_3.jpeg
new file mode 100644
index 0000000..ff1e991
Binary files /dev/null and b/docs/screenshots/screen_3.jpeg differ
diff --git a/docs/screenshots/screen_4.jpeg b/docs/screenshots/screen_4.jpeg
new file mode 100644
index 0000000..d3bf4d1
Binary files /dev/null and b/docs/screenshots/screen_4.jpeg differ
diff --git a/docs/screenshots/screen_5.jpeg b/docs/screenshots/screen_5.jpeg
new file mode 100644
index 0000000..368ef79
Binary files /dev/null and b/docs/screenshots/screen_5.jpeg differ
diff --git a/docs/screenshots/screen_6.jpeg b/docs/screenshots/screen_6.jpeg
new file mode 100644
index 0000000..e109379
Binary files /dev/null and b/docs/screenshots/screen_6.jpeg differ
diff --git a/docs/screenshots/screen_7.jpeg b/docs/screenshots/screen_7.jpeg
new file mode 100644
index 0000000..a5ca2d3
Binary files /dev/null and b/docs/screenshots/screen_7.jpeg differ
diff --git a/docs/screenshots/screen_8.jpeg b/docs/screenshots/screen_8.jpeg
new file mode 100644
index 0000000..4ce458c
Binary files /dev/null and b/docs/screenshots/screen_8.jpeg differ
diff --git a/docs/screenshots/screen_9.jpeg b/docs/screenshots/screen_9.jpeg
new file mode 100644
index 0000000..e378b36
Binary files /dev/null and b/docs/screenshots/screen_9.jpeg differ
diff --git a/lib/api/api_factory.dart b/lib/api/api_factory.dart
index 7cc6f5c..273c406 100644
--- a/lib/api/api_factory.dart
+++ b/lib/api/api_factory.dart
@@ -11,120 +11,147 @@ 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;
+ if (kDebugMode) debugPrint(
+ 'ApiFactory._useMockData: $shouldUseMock (forceMock: $_forceMock, kIsWeb: $kIsWeb, kDebugMode: $kDebugMode)');
+ return shouldUseMock;
}
-
+
/// Pencarian mahasiswa
Future> searchMahasiswa(String keyword) async {
+ if (kDebugMode) debugPrint(
+ 'ApiFactory.searchMahasiswa: keyword="$keyword", useMockData=$_useMockData');
+
if (_useMockData) {
- return _mockService.searchMahasiswa(keyword);
+ if (kDebugMode) debugPrint('ApiFactory.searchMahasiswa: Using mock service');
+ final results = await _mockService.searchMahasiswa(keyword);
+ if (kDebugMode) debugPrint(
+ 'ApiFactory.searchMahasiswa: Mock service returned ${results.length} results');
+ return results;
} else {
try {
- return await _realApi.searchMahasiswa(keyword);
+ if (kDebugMode) debugPrint('ApiFactory.searchMahasiswa: Using real API');
+ final results = await _realApi.searchMahasiswa(keyword);
+ if (kDebugMode) debugPrint(
+ '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') ||
- e.toString().contains('CORS') ||
- e.toString().contains('XMLHttpRequest')) {
- return _mockService.searchMahasiswa(keyword);
- }
+ if (kDebugMode) debugPrint('Error with real API: $e');
+ // FACTORY-FIX: Tidak fallback ke mock diam-diam di production
+ // Mock hanya boleh aktif jika _forceMock == true (eksplisit)
+ // User harus lihat error state, bukan data palsu
rethrow;
}
}
}
-
- /// Detail mahasiswa
+
+ /// Detail mahasiswa (basic)
Future getMahasiswaDetail(String mahasiswaId) async {
if (_useMockData) {
return _mockService.getMahasiswaDetail(mahasiswaId);
} else {
try {
- print('Requesting mahasiswa detail from real API for id: $mahasiswaId');
+ if (kDebugMode) debugPrint('Requesting mahasiswa detail from real API for id: $mahasiswaId');
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,
- namaPt: 'Data tidak tersedia',
- kodePt: '-',
- kodeProdi: '-',
- prodi: 'Data tidak tersedia',
- nama: 'Data tidak tersedia (error)',
- nim: '-',
- jenisDaftar: '-',
- idPt: '-',
- idSms: '-',
- jenisKelamin: '-',
- jenjang: '-',
- statusSaatIni: '-',
- tahunMasuk: '-',
- );
- }
+ if (kDebugMode) debugPrint('Error with real API: $e');
+ // FACTORY-FIX: Tidak fallback ke mock diam-diam — user harus lihat error state
+ rethrow;
+ }
+ }
+ }
+
+ /// Detail mahasiswa lengkap (termasuk riwayat semester, nilai, kelas)
+ Future getMahasiswaDetailLengkap(String mahasiswaId) async {
+ if (_useMockData) {
+ return _mockService.getMahasiswaDetail(mahasiswaId);
+ } else {
+ try {
+ if (kDebugMode) debugPrint('Requesting mahasiswa detail lengkap from real API for id: $mahasiswaId');
+ return await _realApi.getMahasiswaDetailLengkap(mahasiswaId);
+ } catch (e) {
+ if (kDebugMode) debugPrint('Error with real API lengkap: $e');
+ rethrow;
}
}
}
-
+
/// Pencarian dosen
Future> searchDosen(String keyword) async {
+ if (kDebugMode) debugPrint(
+ 'ApiFactory.searchDosen: keyword="$keyword", useMockData=$_useMockData');
+
if (_useMockData) {
- return _mockService.searchDosen(keyword);
+ if (kDebugMode) debugPrint('ApiFactory.searchDosen: Using mock service');
+ final results = await _mockService.searchDosen(keyword);
+ if (kDebugMode) debugPrint(
+ 'ApiFactory.searchDosen: Mock service returned ${results.length} results');
+ for (int i = 0; i < results.length && i < 3; i++) {
+ if (kDebugMode) debugPrint(
+ 'ApiFactory.searchDosen: Mock result $i: ${results[i].nama} (${results[i].nidn})');
+ }
+ return results;
} else {
try {
- return await _realApi.searchDosen(keyword);
+ if (kDebugMode) debugPrint('ApiFactory.searchDosen: Using real API');
+ final results = await _realApi.searchDosen(keyword);
+ if (kDebugMode) debugPrint(
+ 'ApiFactory.searchDosen: Real API returned ${results.length} results');
+ for (int i = 0; i < results.length && i < 3; i++) {
+ if (kDebugMode) debugPrint(
+ 'ApiFactory.searchDosen: Real result $i: ${results[i].nama} (${results[i].nidn})');
+ }
+ return results;
} catch (e) {
- print('Error with real API, fallback to mock: $e');
+ if (kDebugMode) debugPrint('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);
+ if (kDebugMode) debugPrint(
+ 'ApiFactory.searchDosen: Fallback to mock service due to API error');
+ final results = await _mockService.searchDosen(keyword);
+ if (kDebugMode) debugPrint(
+ 'ApiFactory.searchDosen: Mock fallback returned ${results.length} results');
+ return results;
}
rethrow;
}
}
}
-
+
/// Pencarian program studi
Future> searchProdi(String keyword) async {
if (_useMockData) {
@@ -134,9 +161,9 @@ class ApiFactory {
try {
return await _realApi.searchProdi(keyword);
} catch (e) {
- print('Error with real API, fallback to mock: $e');
+ if (kDebugMode) debugPrint('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 +173,7 @@ class ApiFactory {
}
}
}
-
+
/// Pencarian perguruan tinggi
Future> searchPt(String keyword) async {
if (_useMockData) {
@@ -156,9 +183,9 @@ class ApiFactory {
try {
return await _realApi.searchPt(keyword);
} catch (e) {
- print('Error with real API, fallback to mock: $e');
+ if (kDebugMode) debugPrint('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 +195,7 @@ class ApiFactory {
}
}
}
-
+
/// Mendapatkan detail program studi
Future getDetailProdi(String prodiId) async {
if (_useMockData) {
@@ -210,8 +237,8 @@ class ApiFactory {
try {
return await _realApi.getDetailProdi(prodiId);
} catch (e) {
- print('Error with real API, fallback to mock: $e');
-
+ if (kDebugMode) debugPrint('Error with real API, fallback to mock: $e');
+
// Fallback to mock data
return ProdiDetail(
idSp: '',
@@ -249,7 +276,7 @@ class ApiFactory {
}
}
}
-
+
/// Mendapatkan detail perguruan tinggi
Future getDetailPt(String ptId) async {
if (_useMockData) {
@@ -283,8 +310,8 @@ class ApiFactory {
try {
return await _realApi.getDetailPt(ptId);
} catch (e) {
- print('Error with real API, fallback to mock: $e');
-
+ if (kDebugMode) debugPrint('Error with real API, fallback to mock: $e');
+
// Fallback to mock data
return PerguruanTinggiDetail(
kelompok: 'Universitas',
@@ -314,7 +341,7 @@ class ApiFactory {
}
}
}
-
+
/// Mendapatkan daftar program studi di perguruan tinggi
Future> getProdiPt(String ptId, int tahun) async {
if (_useMockData) {
@@ -324,9 +351,9 @@ class ApiFactory {
try {
return await _realApi.getProdiPt(ptId, tahun);
} catch (e) {
- print('Error with real API, fallback to mock: $e');
+ if (kDebugMode) debugPrint('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 +363,7 @@ class ApiFactory {
}
}
}
-
+
/// Getter untuk mendapatkan MockPddiktiService
MockPddiktiService getMockService() {
return _mockService;
@@ -349,42 +376,42 @@ class ApiFactory {
try {
return await _mockService.getDosenProfile(dosenId);
} catch (e) {
- print('Error dengan mock service: $e');
+ if (kDebugMode) debugPrint('Error dengan mock service: $e');
rethrow;
}
} else {
try {
- print('Meminta profil dosen dari API asli untuk id: $dosenId');
+ if (kDebugMode) debugPrint('Meminta profil dosen dari API asli untuk id: $dosenId');
return await _realApi.getDosenProfile(dosenId);
} catch (e) {
- print('Error dengan API asli, fallback ke mock: $e');
-
+ if (kDebugMode) debugPrint('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,
- namaDosen: 'Data tidak tersedia (error)',
- namaPt: 'Data tidak tersedia',
- namaProdi: 'Data tidak tersedia',
- jenisKelamin: '-',
- jabatanAkademik: '-',
- pendidikanTertinggi: '-',
- statusIkatanKerja: '-',
- statusAktivitas: '-',
- penelitian: [],
- pengabdian: [],
- karya: [],
- paten: [],
- riwayatStudi: [],
- riwayatMengajar: [],
- );
- }
+ // FACTORY-FIX: Tidak fallback ke mock — rethrow agar UI tampilkan error
+ rethrow;
+ }
+ }
+ }
+
+ /// 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) {
+ if (kDebugMode) debugPrint('Error dengan mock service: $e');
+ rethrow;
+ }
+ } else {
+ try {
+ if (kDebugMode) debugPrint('Meminta detail lengkap dosen dari API asli untuk id: $dosenId');
+ return await _realApi.getDosenDetailLengkap(dosenId);
+ } catch (e) {
+ if (kDebugMode) debugPrint('Error dengan API asli: $e');
+ // FACTORY-FIX: Tidak fallback ke mock — user harus lihat error state
+ rethrow;
}
}
}
-}
\ No newline at end of file
+}
diff --git a/lib/api/api_services_integration.dart b/lib/api/api_services_integration.dart
index 9ba6a91..6485cdf 100644
--- a/lib/api/api_services_integration.dart
+++ b/lib/api/api_services_integration.dart
@@ -1,5 +1,6 @@
// lib/api/api_services_integration.dart
import 'dart:convert';
+import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import '../models/mahasiswa.dart';
import '../models/dosen.dart';
@@ -23,126 +24,15 @@ class ApiServicesIntegration {
'User-Agent': 'DB-Cracker-App/1.0',
};
- /// API Pencarian Data Pendidikan (dari open API collection)
+ /// API Pencarian Data Pendidikan
+ /// BUG-H4 FIX: GitHub API endpoint removed — it returned repo file listings,
+ /// NOT actual education data. The keyword was never used in the request URL.
+ /// This was a dead network call adding 10s latency with zero useful results.
Future>> searchEducationData(String keyword) async {
- try {
- // List of education APIs to search
- final List apiEndpoints = [
- 'https://api.github.com/repos/IlhamriSKY/PDDIKTI-kemdikbud-API/contents/data', // Python 3 API wrapper PDDIKTI
- 'https://animeapi.my.id/api/v1/anime/search?q=$keyword', // Menampilkan data anime (contoh API lain)
- 'https://api.nashta.co.id/education/search?q=$keyword', // Contoh endpoint fiktif
- ];
-
- List