feat: fungsi rule

This commit is contained in:
unknown 2025-04-29 01:56:27 +07:00
parent accd4ab167
commit 4271210c03
13 changed files with 552 additions and 238 deletions

View File

@ -8,6 +8,7 @@ const authRoutes = require('./routes/authRoutes');
const gejalaRoutes = require('./routes/gejalaRoute'); const gejalaRoutes = require('./routes/gejalaRoute');
const hamaRoutes = require('./routes/hamaRoutes'); const hamaRoutes = require('./routes/hamaRoutes');
const penyakitRoutes = require('./routes/penyakitRoutes'); const penyakitRoutes = require('./routes/penyakitRoutes');
const ruleRoutes = require('./routes/ruleRoutes');
const swaggerDocs = require('./swagger'); const swaggerDocs = require('./swagger');
dotenv.config(); dotenv.config();
@ -24,6 +25,8 @@ app.use("/api/auth", authRoutes);
app.use("/api/gejala", gejalaRoutes); app.use("/api/gejala", gejalaRoutes);
app.use("/api/hama", hamaRoutes); app.use("/api/hama", hamaRoutes);
app.use("/api/penyakit", penyakitRoutes); app.use("/api/penyakit", penyakitRoutes);
app.use("/api/rules", ruleRoutes);
// Swagger Documentation // Swagger Documentation
swaggerDocs(app); // Setup Swagger UI documentation swaggerDocs(app); // Setup Swagger UI documentation

View File

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

View File

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

View File

@ -1,32 +1,33 @@
const { Model, DataTypes } = require('sequelize'); const { Model, DataTypes } = require('sequelize');
const sequelize = require('../config/database');
class Gejala extends Model {} module.exports = (sequelize) => {
class Gejala extends Model {}
Gejala.init( Gejala.init(
{ {
id: { id: {
allowNull: false, allowNull: false,
autoIncrement: true, autoIncrement: true,
primaryKey: true, primaryKey: true,
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
},
kode: {
type: DataTypes.STRING,
allowNull: false,
},
nama: {
type: DataTypes.STRING,
allowNull: false,
},
}, },
kode: { {
type: DataTypes.STRING, sequelize,
allowNull: false, modelName: 'Gejala',
}, tableName: 'gejala',
nama: { timestamps: false, // Jika ingin menggunakan createdAt & updatedAt, ubah ke true
type: DataTypes.STRING, paranoid: false, // Jika menggunakan soft delete, ubah ke true
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
}
);
module.exports = Gejala; return Gejala; // Pastikan model dikembalikan dengan benar
};

View File

@ -1,44 +1,45 @@
const { Model, DataTypes } = require('sequelize'); const { Model, DataTypes } = require('sequelize');
const sequelize = require('../config/database');
class Hama extends Model {}
Hama.init( module.exports = (sequelize) => {
{ class Hama extends Model {}
id: {
allowNull: false, Hama.init(
autoIncrement: true, {
primaryKey: true, id: {
type: DataTypes.INTEGER, 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, sequelize,
allowNull: false, modelName: 'Hama',
}, tableName: 'hama',
nama: { timestamps: false, // Jika ingin menggunakan createdAt & updatedAt, ubah ke true
type: DataTypes.STRING, paranoid: false, // Jika menggunakan soft delete, ubah ke true
allowNull: false,
},
deskripsi: {
type: DataTypes.STRING,
allowNull: true,
},
penanganan: {
type: DataTypes.STRING,
allowNull: true,
},
foto: {
type: DataTypes.STRING,
allowNull: false,
} }
}, );
{ return Hama;
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;

View File

@ -1,40 +1,42 @@
const { Model, DataTypes } = require('sequelize'); const { Model, DataTypes } = require('sequelize');
const sequelize = require('../config/database');
class Penyakit extends Model {}
Penyakit.init( module.exports =(sequelize) => {
{ class Penyakit extends Model {}
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 = Penyakit; 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;
}

70
backend/models/rule.js Normal file
View File

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

View File

@ -1,53 +1,54 @@
const { Model, DataTypes } = require('sequelize'); const { Model, DataTypes } = require('sequelize');
const sequelize = require('../config/database');
class User extends Model {}
User.init( module.exports = (sequelize) => {
{ class User extends Model {}
id: {
allowNull: false, User.init(
autoIncrement: true, {
primaryKey: true, id: {
type: DataTypes.INTEGER, allowNull: false,
}, autoIncrement: true,
name: { primaryKey: true,
type: DataTypes.STRING, type: DataTypes.INTEGER,
allowNull: true, },
}, name: {
email: { type: DataTypes.STRING,
type: DataTypes.STRING, allowNull: true,
allowNull: false, },
unique: true, email: {
validate: { type: DataTypes.STRING,
isEmail: true, 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: { sequelize,
type: DataTypes.STRING, modelName: 'User',
allowNull: false, tableName: 'users',
}, timestamps: false, // Pastikan timestamps aktif jika menggunakan paranoid
alamat: { paranoid: false, // Hanya gunakan ini jika timestamps: true
type: DataTypes.STRING,
allowNull: true, }
}, );
nomorTelepon: { return User;
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;

View File

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

View File

@ -1,9 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class DetailHamaPage extends StatelessWidget { class DetailHamaPage extends StatelessWidget {
final Map<String, String> detailRiwayat; final Map<String, dynamic> detailHama;
const DetailHamaPage({required this.detailRiwayat}); const DetailHamaPage({required this.detailHama});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -25,11 +25,11 @@ class DetailHamaPage extends StatelessWidget {
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Column( child: Column(
children: [ children: [
if (detailRiwayat["gambar"] != null && detailRiwayat["gambar"]!.isNotEmpty) if (detailHama["gambar"] != null && detailHama["gambar"]!.isNotEmpty)
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
child: Image.asset( child: Image.asset(
detailRiwayat["gambar"]!, detailHama["gambar"]!,
height: 200, height: 200,
width: 200, // Biar gambar full lebar width: 200, // Biar gambar full lebar
fit: BoxFit.cover, fit: BoxFit.cover,
@ -54,7 +54,7 @@ class DetailHamaPage extends StatelessWidget {
), ),
SizedBox(height: 8), SizedBox(height: 8),
Text( Text(
detailRiwayat["nama hama"] ?? "Nama hama tidak tersedia", detailHama["nama"] ?? "Nama hama tidak tersedia",
style: TextStyle(fontSize: 16), style: TextStyle(fontSize: 16),
), ),
], ],
@ -81,7 +81,7 @@ class DetailHamaPage extends StatelessWidget {
), ),
SizedBox(height: 8), SizedBox(height: 8),
Text( Text(
detailRiwayat["deskripsi"] ?? "Deskripsi tidak tersedia", detailHama["deskripsi"] ?? "Deskripsi tidak tersedia",
style: TextStyle(fontSize: 16), style: TextStyle(fontSize: 16),
), ),
SizedBox(height: 16), SizedBox(height: 16),
@ -91,7 +91,7 @@ class DetailHamaPage extends StatelessWidget {
), ),
SizedBox(height: 8), SizedBox(height: 8),
Text( Text(
detailRiwayat["penanganan"] ?? "Penanganan tidak tersedia", detailHama["penanganan"] ?? "Penanganan tidak tersedia",
style: TextStyle(fontSize: 16), style: TextStyle(fontSize: 16),
), ),
], ],

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class DetailPenyakitPage extends StatelessWidget { class DetailPenyakitPage extends StatelessWidget {
final Map<String, String> detailPenyakit; final Map<String, dynamic> detailPenyakit;
const DetailPenyakitPage({required this.detailPenyakit}); const DetailPenyakitPage({required this.detailPenyakit});
@ -54,7 +54,7 @@ class DetailPenyakitPage extends StatelessWidget {
), ),
SizedBox(height: 8), SizedBox(height: 8),
Text( Text(
detailPenyakit["nama penyakit"] ?? "Nama penyakit tidak tersedia", detailPenyakit["nama"] ?? "Nama penyakit tidak tersedia",
style: TextStyle(fontSize: 16), style: TextStyle(fontSize: 16),
), ),
], ],

View File

@ -1,33 +1,20 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'detail_hama_page.dart'; import 'detail_hama_page.dart';
import 'package:frontend/api_services/api_services.dart';
class HamaPage extends StatelessWidget { class HamaPage extends StatefulWidget {
final List<Map<String, String>> hamaList = [ @override
{ _HamaPageState createState() => _HamaPageState();
"nama hama": "Karat Putih", }
"deskripsi": "Penyakit yang umum pada bayam.",
"penanganan": "Gunakan fungisida sesuai anjuran dan potong daun yang terinfeksi.", class _HamaPageState extends State<HamaPage> {
"gambar": "assets/images/karat putih.jpeg", late Future<List<Map<String, dynamic>>> _hamaListFuture;
},
{ @override
"nama hama": "Virus Keriting", void initState() {
"deskripsi": "Disebabkan oleh infeksi virus.", super.initState();
"penanganan": "Musnahkan tanaman terinfeksi dan kontrol vektor seperti kutu daun.", _hamaListFuture = ApiService().getHama();
"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",
},
];
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -52,11 +39,19 @@ class HamaPage extends StatelessWidget {
), ),
body: Padding( body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0), padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column( child: FutureBuilder<List<Map<String, dynamic>>>(
children: [ future: _hamaListFuture,
SizedBox(height: 16), builder: (context, snapshot) {
Expanded( if (snapshot.connectionState == ConnectionState.waiting) {
child: ListView.builder( 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, itemCount: hamaList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final diagnosa = hamaList[index]; final diagnosa = hamaList[index];
@ -65,7 +60,7 @@ class HamaPage extends StatelessWidget {
margin: const EdgeInsets.symmetric(vertical: 8), margin: const EdgeInsets.symmetric(vertical: 8),
child: ListTile( child: ListTile(
title: Text( title: Text(
diagnosa["nama hama"] ?? "Tidak ada data", diagnosa["nama"] ?? "Tidak ada data",
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(fontWeight: FontWeight.bold),
), ),
subtitle: Text(diagnosa["deskripsi"] ?? "Deskripsi tidak tersedia"), subtitle: Text(diagnosa["deskripsi"] ?? "Deskripsi tidak tersedia"),
@ -73,18 +68,18 @@ class HamaPage extends StatelessWidget {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => DetailHamaPage(detailRiwayat: diagnosa), builder: (context) => DetailHamaPage(detailHama: diagnosa),
), ),
); );
}, },
), ),
); );
}, },
), );
), }
], },
), ),
), ),
); );
} }
} }

View File

@ -1,33 +1,20 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'detail_penyakit_page.dart'; import 'detail_penyakit_page.dart';
import 'package:frontend/api_services/api_services.dart';
class PenyakitPage extends StatelessWidget { class PenyakitPage extends StatefulWidget {
final List<Map<String, String>> penyakitList= [ @override
{ _PenyakitPageState createState() => _PenyakitPageState();
"nama penyakit": "Karat Putih", }
"deskripsi": "Penyakit yang umum pada bayam.",
"penanganan": "Gunakan fungisida sesuai anjuran dan potong daun yang terinfeksi.", class _PenyakitPageState extends State<PenyakitPage> {
"gambar": "assets/images/karat putih.jpeg", late Future<List<Map<String, dynamic>>> _penyakitListFuture;
},
{ @override
"nama penyakit": "Virus Keriting", void initState() {
"deskripsi": "Disebabkan oleh infeksi virus.", super.initState();
"penanganan": "Musnahkan tanaman terinfeksi dan kontrol vektor seperti kutu daun.", _penyakitListFuture = ApiService().getPenyakit();
"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",
},
];
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -52,39 +39,47 @@ class PenyakitPage extends StatelessWidget {
), ),
body: Padding( body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0), padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column( child: FutureBuilder<List<Map<String, dynamic>>>(
children: [ future: _penyakitListFuture,
SizedBox(height: 16), builder: (context, snapshot) {
Expanded( if (snapshot.connectionState == ConnectionState.waiting) {
child: ListView.builder( 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, itemCount: penyakitList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final diagnosa = penyakitList[index]; final penyakit = penyakitList[index];
return Card( return Card(
elevation: 4, elevation: 4,
margin: const EdgeInsets.symmetric(vertical: 8), margin: const EdgeInsets.symmetric(vertical: 8),
child: ListTile( child: ListTile(
title: Text( title: Text(
diagnosa["nama penyakit"] ?? "Tidak ada data", penyakit["nama"] ?? "Tidak ada data",
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(fontWeight: FontWeight.bold),
), ),
subtitle: Text(diagnosa["deskripsi"] ?? "Deskripsi tidak tersedia"), subtitle: Text(penyakit["deskripsi"] ?? "Deskripsi tidak tersedia"),
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => DetailPenyakitPage(detailPenyakit: diagnosa), builder: (context) => DetailPenyakitPage(detailPenyakit: penyakit),
), ),
); );
}, },
), ),
); );
}, },
), );
), }
], },
), ),
), ),
); );
} }
} }