diff --git a/backend/app.js b/backend/app.js index 3652741..0347483 100644 --- a/backend/app.js +++ b/backend/app.js @@ -8,6 +8,7 @@ const authRoutes = require('./routes/authRoutes'); const gejalaRoutes = require('./routes/gejalaRoute'); const hamaRoutes = require('./routes/hamaRoutes'); const penyakitRoutes = require('./routes/penyakitRoutes'); +const ruleRoutes = require('./routes/ruleRoutes'); const swaggerDocs = require('./swagger'); dotenv.config(); @@ -24,6 +25,8 @@ app.use("/api/auth", authRoutes); app.use("/api/gejala", gejalaRoutes); app.use("/api/hama", hamaRoutes); app.use("/api/penyakit", penyakitRoutes); +app.use("/api/rules", ruleRoutes); + // Swagger Documentation swaggerDocs(app); // Setup Swagger UI documentation diff --git a/backend/controller/rulesController.js b/backend/controller/rulesController.js new file mode 100644 index 0000000..212be7e --- /dev/null +++ b/backend/controller/rulesController.js @@ -0,0 +1,81 @@ +const { Rule, gejala, penyakit, hama } = require('../models'); + +// 🔥 Buat aturan baru +exports.createRule = async (req, res) => { + try { + const { id_gejala, id_penyakit, id_hama, nilai_pakar } = req.body; + + // Validasi minimal + if (!id_gejala || (!id_penyakit && !id_hama) || !nilai_pakar) { + return res.status(400).json({ message: 'Data tidak lengkap' }); + } + + const newRule = await Rule.create({ + id_gejala, + id_penyakit, + id_hama, + nilai_pakar + }); + + res.status(201).json({ message: 'Rule berhasil dibuat', data: newRule }); + } catch (error) { + console.error('Error createRule:', error); + res.status(500).json({ message: 'Terjadi kesalahan server', error: error.message }); + } +}; + +// 🔥 Ambil semua aturan +exports.getRules = async (req, res) => { + try { + const rules = await Rule.findAll(); + + res.status(200).json({ message: 'Daftar Rules', data: rules }); + } catch (error) { + console.error('Error getRules:', error); + res.status(500).json({ message: 'Terjadi kesalahan server', error: error.message }); + } +}; + +// 🔥 Update aturan +exports.updateRule = async (req, res) => { + try { + const { id } = req.params; + const { id_gejala, id_penyakit, id_hama, nilai_pakar } = req.body; + + const rule = await Rule.findByPk(id); + if (!rule) { + return res.status(404).json({ message: 'Rule tidak ditemukan' }); + } + + await rule.update({ + id_gejala, + id_penyakit, + id_hama, + nilai_pakar + }); + + res.status(200).json({ message: 'Rule berhasil diperbarui', data: rule }); + } catch (error) { + console.error('Error updateRule:', error); + res.status(500).json({ message: 'Terjadi kesalahan server', error: error.message }); + } +}; + +// 🔥 Hapus aturan +exports.deleteRule = async (req, res) => { + try { + const { id } = req.params; + + const rule = await Rule.findByPk(id); + if (!rule) { + return res.status(404).json({ message: 'Rule tidak ditemukan' }); + } + + await rule.destroy(); + + res.status(200).json({ message: 'Rule berhasil dihapus' }); + } catch (error) { + console.error('Error deleteRule:', error); + res.status(500).json({ message: 'Terjadi kesalahan server', error: error.message }); + } +}; diff --git a/backend/migrations/20250428153341-create-rule.js b/backend/migrations/20250428153341-create-rule.js new file mode 100644 index 0000000..c674ce1 --- /dev/null +++ b/backend/migrations/20250428153341-create-rule.js @@ -0,0 +1,42 @@ +'use strict'; +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('Rules', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + id_gejala: { + type: Sequelize.INTEGER, + references:{ + model: 'gejala', + key: 'id' + } + }, + nilai_pakar: { + type: Sequelize.FLOAT, + allowNull: false + }, + id_penyakit: { + type: Sequelize.INTEGER, + references:{ + model: 'penyakit', + key:'id' + } + }, + id_hama: { + type: Sequelize.INTEGER, + references:{ + model: 'hama', + key: 'id' + } + }, + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('Rules'); + } +}; \ No newline at end of file diff --git a/backend/models/gejala.js b/backend/models/gejala.js index 448bf6d..9d11022 100644 --- a/backend/models/gejala.js +++ b/backend/models/gejala.js @@ -1,32 +1,33 @@ const { Model, DataTypes } = require('sequelize'); -const sequelize = require('../config/database'); -class Gejala extends Model {} +module.exports = (sequelize) => { + class Gejala extends Model {} -Gejala.init( - { - id: { - allowNull: false, - autoIncrement: true, - primaryKey: true, - type: DataTypes.INTEGER, + Gejala.init( + { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + kode: { + type: DataTypes.STRING, + allowNull: false, + }, + nama: { + type: DataTypes.STRING, + allowNull: false, + }, }, - kode: { - type: DataTypes.STRING, - allowNull: false, - }, - nama: { - type: DataTypes.STRING, - allowNull: false, - }, - }, - { - sequelize, - modelName: 'Gejala', - tableName: 'gejala', - timestamps: false, // Jika ingin menggunakan createdAt & updatedAt, ubah ke true - paranoid: false, // Jika menggunakan soft delete, ubah ke true - } -); + { + sequelize, + modelName: 'Gejala', + tableName: 'gejala', + timestamps: false, // Jika ingin menggunakan createdAt & updatedAt, ubah ke true + paranoid: false, // Jika menggunakan soft delete, ubah ke true + } + ); -module.exports = Gejala; + return Gejala; // Pastikan model dikembalikan dengan benar +}; diff --git a/backend/models/hama.js b/backend/models/hama.js index 5e0d7fb..f15e6c7 100644 --- a/backend/models/hama.js +++ b/backend/models/hama.js @@ -1,44 +1,45 @@ const { Model, DataTypes } = require('sequelize'); -const sequelize = require('../config/database'); -class Hama extends Model {} -Hama.init( - { - id: { - allowNull: false, - autoIncrement: true, - primaryKey: true, - type: DataTypes.INTEGER, +module.exports = (sequelize) => { + class Hama extends Model {} + + Hama.init( + { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + kode: { + type: DataTypes.STRING, + allowNull: false, + }, + nama: { + type: DataTypes.STRING, + allowNull: false, + }, + deskripsi: { + type: DataTypes.STRING, + allowNull: true, + }, + penanganan: { + type: DataTypes.STRING, + allowNull: true, + }, + foto: { + type: DataTypes.STRING, + allowNull: false, + } }, - kode: { - type: DataTypes.STRING, - allowNull: false, - }, - nama: { - type: DataTypes.STRING, - allowNull: false, - }, - deskripsi: { - type: DataTypes.STRING, - allowNull: true, - }, - penanganan: { - type: DataTypes.STRING, - allowNull: true, - }, - foto: { - type: DataTypes.STRING, - allowNull: false, + { + sequelize, + modelName: 'Hama', + tableName: 'hama', + timestamps: false, // Jika ingin menggunakan createdAt & updatedAt, ubah ke true + paranoid: false, // Jika menggunakan soft delete, ubah ke true } - }, - { - sequelize, - modelName: 'Hama', - tableName: 'hama', - timestamps: false, // Jika ingin menggunakan createdAt & updatedAt, ubah ke true - paranoid: false, // Jika menggunakan soft delete, ubah ke true - } -); - -module.exports = Hama; \ No newline at end of file + ); + return Hama; +} \ No newline at end of file diff --git a/backend/models/penyakit.js b/backend/models/penyakit.js index e633f87..1076f07 100644 --- a/backend/models/penyakit.js +++ b/backend/models/penyakit.js @@ -1,40 +1,42 @@ const { Model, DataTypes } = require('sequelize'); -const sequelize = require('../config/database'); -class Penyakit extends Model {} -Penyakit.init( - { - id: { - allowNull: false, - autoIncrement: true, - primaryKey: true, - type: DataTypes.INTEGER, - }, - kode: { - type: DataTypes.STRING, - allowNull: false, - }, - nama: { - type: DataTypes.STRING, - allowNull: false, - }, - deskripsi: { - type: DataTypes.STRING, - allowNull: true, - }, - penanganan: { - type: DataTypes.STRING, - allowNull: true, - }, - }, - { - sequelize, - modelName: 'Penyakit', - tableName: 'penyakit', - timestamps: false, // Jika ingin menggunakan createdAt & updatedAt, ubah ke true - paranoid: false, // Jika menggunakan soft delete, ubah ke true - } -); +module.exports =(sequelize) => { + class Penyakit extends Model {} -module.exports = Penyakit; \ No newline at end of file + Penyakit.init( + { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + kode: { + type: DataTypes.STRING, + allowNull: false, + }, + nama: { + type: DataTypes.STRING, + allowNull: false, + }, + deskripsi: { + type: DataTypes.STRING, + allowNull: true, + }, + penanganan: { + type: DataTypes.STRING, + allowNull: true, + }, + }, + { + sequelize, + modelName: 'Penyakit', + tableName: 'penyakit', + timestamps: false, // Jika ingin menggunakan createdAt & updatedAt, ubah ke true + paranoid: false, // Jika menggunakan soft delete, ubah ke true + } + ); + + return Penyakit; + } \ No newline at end of file diff --git a/backend/models/rule.js b/backend/models/rule.js new file mode 100644 index 0000000..e8ce53d --- /dev/null +++ b/backend/models/rule.js @@ -0,0 +1,70 @@ +const { Model, DataTypes } = require('sequelize'); + + +module.exports = (sequelize) => { + +class Rule extends Model { + static associate(models) { + // Asosiasi dengan model Gejala + Rule.belongsTo(models.Gejala, { + foreignKey: 'id_gejala', + as: 'gejala', // Nama asosiasi yang bisa digunakan saat melakukan query + }); + + // Asosiasi dengan model Penyakit + Rule.belongsTo(models.Penyakit, { + foreignKey: 'id_penyakit', + as: 'penyakit', + }); + + // Asosiasi dengan model Hama + Rule.belongsTo(models.Hama, { + foreignKey: 'id_hama', + as: 'hama', + }); + } +} + +Rule.init( + { + id_gejala: { + type: DataTypes.INTEGER, + allowNull: true, + references: { + model: 'gejala', // Mengacu ke tabel 'gejala' + key: 'id', // Mengacu ke kolom 'id' pada tabel 'gejala' + }, + }, + nilai_pakar: { + type: DataTypes.FLOAT, + allowNull: false, + }, + id_penyakit: { + type: DataTypes.INTEGER, + allowNull: true, + references: { + model: 'penyakit', // Mengacu ke tabel 'penyakit' + key: 'id', // Mengacu ke kolom 'id' pada tabel 'penyakit' + }, + }, + id_hama: { + type: DataTypes.INTEGER, + allowNull: true, + references: { + model: 'hama', // Mengacu ke tabel 'hama' + key: 'id', // Mengacu ke kolom 'id' pada tabel 'hama' + }, + }, + }, + { + sequelize, + modelName: 'Rule', + tableName: 'rules', + timestamps: false, + paranoid: false, + } +); + +return Rule; + +} diff --git a/backend/models/user.js b/backend/models/user.js index 1dbc36c..2412bfa 100644 --- a/backend/models/user.js +++ b/backend/models/user.js @@ -1,53 +1,54 @@ const { Model, DataTypes } = require('sequelize'); -const sequelize = require('../config/database'); -class User extends Model {} - User.init( - { - id: { - allowNull: false, - autoIncrement: true, - primaryKey: true, - type: DataTypes.INTEGER, - }, - name: { - type: DataTypes.STRING, - allowNull: true, - }, - email: { - type: DataTypes.STRING, - allowNull: false, - unique: true, - validate: { - isEmail: true, +module.exports = (sequelize) => { + class User extends Model {} + + User.init( + { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + name: { + type: DataTypes.STRING, + allowNull: true, + }, + email: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + validate: { + isEmail: true, + }, + }, + password: { + type: DataTypes.STRING, + allowNull: false, + }, + alamat: { + type: DataTypes.STRING, + allowNull: true, + }, + nomorTelepon: { + type: DataTypes.STRING, + allowNull: true, + }, + role: { + type: DataTypes.STRING, + allowNull: false, + }, }, - }, - password: { - type: DataTypes.STRING, - allowNull: false, - }, - alamat: { - type: DataTypes.STRING, - allowNull: true, - }, - nomorTelepon: { - type: DataTypes.STRING, - allowNull: true, - }, - role: { - type: DataTypes.STRING, - allowNull: false, - }, - }, - { - sequelize, - modelName: 'User', - tableName: 'users', - timestamps: false, // Pastikan timestamps aktif jika menggunakan paranoid - paranoid: false, // Hanya gunakan ini jika timestamps: true - - } - ); - - module.exports = User; + { + sequelize, + modelName: 'User', + tableName: 'users', + timestamps: false, // Pastikan timestamps aktif jika menggunakan paranoid + paranoid: false, // Hanya gunakan ini jika timestamps: true + + } + ); + return User; +} diff --git a/backend/routes/ruleRoutes.js b/backend/routes/ruleRoutes.js new file mode 100644 index 0000000..df17ecb --- /dev/null +++ b/backend/routes/ruleRoutes.js @@ -0,0 +1,123 @@ +const express = require('express'); +const router = express.Router(); +const ruleController = require('../controller/rulesController'); + +/** + * @swagger + * tags: + * name: Rules + * description: API untuk mengelola aturan (rules) + */ + +/** + * @swagger + * /api/rules: + * post: + * summary: Membuat aturan baru + * tags: [Rules] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - id_gejala + * - nilai_pakar + * properties: + * id_gejala: + * type: integer + * id_penyakit: + * type: integer + * id_hama: + * type: integer + * nilai_pakar: + * type: number + * format: float + * responses: + * 201: + * description: Rule berhasil dibuat + * 400: + * description: Data tidak lengkap + * 500: + * description: Terjadi kesalahan server + */ +router.post('/', ruleController.createRule); + +/** + * @swagger + * /api/rules: + * get: + * summary: Menampilkan semua aturan + * tags: [Rules] + * responses: + * 200: + * description: Daftar rules berhasil diambil + * 500: + * description: Terjadi kesalahan server + */ +router.get('/', ruleController.getRules); + +/** + * @swagger + * /api/rules/{id}: + * put: + * summary: Memperbarui aturan berdasarkan ID + * tags: [Rules] + * parameters: + * - in: path + * name: id + * required: true + * description: ID rule yang ingin diperbarui + * schema: + * type: integer + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * id_gejala: + * type: integer + * id_penyakit: + * type: integer + * id_hama: + * type: integer + * nilai_pakar: + * type: number + * format: float + * responses: + * 200: + * description: Rule berhasil diperbarui + * 404: + * description: Rule tidak ditemukan + * 500: + * description: Terjadi kesalahan server + */ +router.put('/:id', ruleController.updateRule); + +/** + * @swagger + * /api/rules/{id}: + * delete: + * summary: Menghapus aturan berdasarkan ID + * tags: [Rules] + * parameters: + * - in: path + * name: id + * required: true + * description: ID rule yang ingin dihapus + * schema: + * type: integer + * responses: + * 200: + * description: Rule berhasil dihapus + * 404: + * description: Rule tidak ditemukan + * 500: + * description: Terjadi kesalahan server + */ +router.delete('/:id', ruleController.deleteRule); + +module.exports = router; diff --git a/frontend/lib/user/detail_hama_page.dart b/frontend/lib/user/detail_hama_page.dart index a4fee49..a05370a 100644 --- a/frontend/lib/user/detail_hama_page.dart +++ b/frontend/lib/user/detail_hama_page.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; class DetailHamaPage extends StatelessWidget { - final Map detailRiwayat; + final Map detailHama; - const DetailHamaPage({required this.detailRiwayat}); + const DetailHamaPage({required this.detailHama}); @override Widget build(BuildContext context) { @@ -25,11 +25,11 @@ class DetailHamaPage extends StatelessWidget { padding: const EdgeInsets.all(16.0), child: Column( children: [ - if (detailRiwayat["gambar"] != null && detailRiwayat["gambar"]!.isNotEmpty) + if (detailHama["gambar"] != null && detailHama["gambar"]!.isNotEmpty) ClipRRect( borderRadius: BorderRadius.circular(12), child: Image.asset( - detailRiwayat["gambar"]!, + detailHama["gambar"]!, height: 200, width: 200, // Biar gambar full lebar fit: BoxFit.cover, @@ -54,7 +54,7 @@ class DetailHamaPage extends StatelessWidget { ), SizedBox(height: 8), Text( - detailRiwayat["nama hama"] ?? "Nama hama tidak tersedia", + detailHama["nama"] ?? "Nama hama tidak tersedia", style: TextStyle(fontSize: 16), ), ], @@ -81,7 +81,7 @@ class DetailHamaPage extends StatelessWidget { ), SizedBox(height: 8), Text( - detailRiwayat["deskripsi"] ?? "Deskripsi tidak tersedia", + detailHama["deskripsi"] ?? "Deskripsi tidak tersedia", style: TextStyle(fontSize: 16), ), SizedBox(height: 16), @@ -91,7 +91,7 @@ class DetailHamaPage extends StatelessWidget { ), SizedBox(height: 8), Text( - detailRiwayat["penanganan"] ?? "Penanganan tidak tersedia", + detailHama["penanganan"] ?? "Penanganan tidak tersedia", style: TextStyle(fontSize: 16), ), ], diff --git a/frontend/lib/user/detail_penyakit_page.dart b/frontend/lib/user/detail_penyakit_page.dart index d8ccd70..3a110b9 100644 --- a/frontend/lib/user/detail_penyakit_page.dart +++ b/frontend/lib/user/detail_penyakit_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; class DetailPenyakitPage extends StatelessWidget { - final Map detailPenyakit; + final Map detailPenyakit; const DetailPenyakitPage({required this.detailPenyakit}); @@ -54,7 +54,7 @@ class DetailPenyakitPage extends StatelessWidget { ), SizedBox(height: 8), Text( - detailPenyakit["nama penyakit"] ?? "Nama penyakit tidak tersedia", + detailPenyakit["nama"] ?? "Nama penyakit tidak tersedia", style: TextStyle(fontSize: 16), ), ], diff --git a/frontend/lib/user/hama_page.dart b/frontend/lib/user/hama_page.dart index bd57c8d..dc80897 100644 --- a/frontend/lib/user/hama_page.dart +++ b/frontend/lib/user/hama_page.dart @@ -1,33 +1,20 @@ import 'package:flutter/material.dart'; import 'detail_hama_page.dart'; +import 'package:frontend/api_services/api_services.dart'; -class HamaPage extends StatelessWidget { - final List> hamaList = [ - { - "nama hama": "Karat Putih", - "deskripsi": "Penyakit yang umum pada bayam.", - "penanganan": "Gunakan fungisida sesuai anjuran dan potong daun yang terinfeksi.", - "gambar": "assets/images/karat putih.jpeg", - }, - { - "nama hama": "Virus Keriting", - "deskripsi": "Disebabkan oleh infeksi virus.", - "penanganan": "Musnahkan tanaman terinfeksi dan kontrol vektor seperti kutu daun.", - "gambar": "assets/images/virus_keriting.jpeg", - }, - { - "nama hama": "Kekurangan Mangan", - "deskripsi": "Kekurangan unsur hara mikro.", - "penanganan": "Tambahkan pupuk yang mengandung mangan (Mn).", - "gambar": "assets/images/kekurangan_mangan.jpeg", - }, - { - "nama hama": "Downy Mildew", - "deskripsi": "Penyakit jamur pada bayam.", - "penanganan": "Gunakan fungisida berbahan aktif metalaxyl dan perbaiki drainase tanah.", - "gambar": "assets/images/downy_mildew.jpeg", - }, - ]; +class HamaPage extends StatefulWidget { + @override + _HamaPageState createState() => _HamaPageState(); +} + +class _HamaPageState extends State { + late Future>> _hamaListFuture; + + @override + void initState() { + super.initState(); + _hamaListFuture = ApiService().getHama(); + } @override Widget build(BuildContext context) { @@ -52,11 +39,19 @@ class HamaPage extends StatelessWidget { ), body: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Column( - children: [ - SizedBox(height: 16), - Expanded( - child: ListView.builder( + child: FutureBuilder>>( + future: _hamaListFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Center(child: CircularProgressIndicator(color: Colors.white)); + } else if (snapshot.hasError) { + return Center(child: Text('Gagal memuat data', style: TextStyle(color: Colors.white))); + } else if (!snapshot.hasData || snapshot.data!.isEmpty) { + return Center(child: Text('Tidak ada data tersedia', style: TextStyle(color: Colors.white))); + } else { + final hamaList = snapshot.data!; + + return ListView.builder( itemCount: hamaList.length, itemBuilder: (context, index) { final diagnosa = hamaList[index]; @@ -65,7 +60,7 @@ class HamaPage extends StatelessWidget { margin: const EdgeInsets.symmetric(vertical: 8), child: ListTile( title: Text( - diagnosa["nama hama"] ?? "Tidak ada data", + diagnosa["nama"] ?? "Tidak ada data", style: TextStyle(fontWeight: FontWeight.bold), ), subtitle: Text(diagnosa["deskripsi"] ?? "Deskripsi tidak tersedia"), @@ -73,18 +68,18 @@ class HamaPage extends StatelessWidget { Navigator.push( context, MaterialPageRoute( - builder: (context) => DetailHamaPage(detailRiwayat: diagnosa), + builder: (context) => DetailHamaPage(detailHama: diagnosa), ), ); }, ), ); }, - ), - ), - ], + ); + } + }, ), ), ); } -} \ No newline at end of file +} diff --git a/frontend/lib/user/penyakit_page.dart b/frontend/lib/user/penyakit_page.dart index 51b7973..bbfb8d2 100644 --- a/frontend/lib/user/penyakit_page.dart +++ b/frontend/lib/user/penyakit_page.dart @@ -1,33 +1,20 @@ import 'package:flutter/material.dart'; import 'detail_penyakit_page.dart'; +import 'package:frontend/api_services/api_services.dart'; -class PenyakitPage extends StatelessWidget { - final List> penyakitList= [ - { - "nama penyakit": "Karat Putih", - "deskripsi": "Penyakit yang umum pada bayam.", - "penanganan": "Gunakan fungisida sesuai anjuran dan potong daun yang terinfeksi.", - "gambar": "assets/images/karat putih.jpeg", - }, - { - "nama penyakit": "Virus Keriting", - "deskripsi": "Disebabkan oleh infeksi virus.", - "penanganan": "Musnahkan tanaman terinfeksi dan kontrol vektor seperti kutu daun.", - "gambar": "assets/images/virus_keriting.jpeg", - }, - { - "nama penyakit": "Kekurangan Mangan", - "deskripsi": "Kekurangan unsur hara mikro.", - "penanganan": "Tambahkan pupuk yang mengandung mangan (Mn).", - "gambar": "assets/images/kekurangan_mangan.jpeg", - }, - { - "nama penyakit": "Downy Mildew", - "deskripsi": "Penyakit jamur pada bayam.", - "penanganan": "Gunakan fungisida berbahan aktif metalaxyl dan perbaiki drainase tanah.", - "gambar": "assets/images/downy_mildew.jpeg", - }, - ]; +class PenyakitPage extends StatefulWidget { + @override + _PenyakitPageState createState() => _PenyakitPageState(); +} + +class _PenyakitPageState extends State { + late Future>> _penyakitListFuture; + + @override + void initState() { + super.initState(); + _penyakitListFuture = ApiService().getPenyakit(); + } @override Widget build(BuildContext context) { @@ -52,39 +39,47 @@ class PenyakitPage extends StatelessWidget { ), body: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Column( - children: [ - SizedBox(height: 16), - Expanded( - child: ListView.builder( + child: FutureBuilder>>( + future: _penyakitListFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Center(child: CircularProgressIndicator(color: Colors.white)); + } else if (snapshot.hasError) { + return Center(child: Text('Gagal memuat data', style: TextStyle(color: Colors.white))); + } else if (!snapshot.hasData || snapshot.data!.isEmpty) { + return Center(child: Text('Tidak ada data tersedia', style: TextStyle(color: Colors.white))); + } else { + final penyakitList = snapshot.data!; + + return ListView.builder( itemCount: penyakitList.length, itemBuilder: (context, index) { - final diagnosa = penyakitList[index]; + final penyakit = penyakitList[index]; return Card( elevation: 4, margin: const EdgeInsets.symmetric(vertical: 8), child: ListTile( title: Text( - diagnosa["nama penyakit"] ?? "Tidak ada data", + penyakit["nama"] ?? "Tidak ada data", style: TextStyle(fontWeight: FontWeight.bold), ), - subtitle: Text(diagnosa["deskripsi"] ?? "Deskripsi tidak tersedia"), + subtitle: Text(penyakit["deskripsi"] ?? "Deskripsi tidak tersedia"), onTap: () { Navigator.push( context, MaterialPageRoute( - builder: (context) => DetailPenyakitPage(detailPenyakit: diagnosa), + builder: (context) => DetailPenyakitPage(detailPenyakit: penyakit), ), ); }, ), ); }, - ), - ), - ], + ); + } + }, ), ), ); } -} \ No newline at end of file +}