last update admin page

This commit is contained in:
unknown 2025-04-27 01:30:39 +07:00
parent 04f085597b
commit accd4ab167
31 changed files with 1992 additions and 387 deletions

View File

@ -24,10 +24,12 @@ exports.getHamaById = async (req, res) => {
} }
}; };
// 🔹 Fungsi untuk menambahkan hama baru (kode otomatis & kategori default) // Pastikan sudah import 'Hama' model dan multer middleware sebelumnya
exports.createHama = async (req, res) => { exports.createHama = async (req, res) => {
try { try {
const { nama, deskripsi, penanganan } = req.body; const { nama, deskripsi, penanganan } = req.body;
const file = req.file;
// Cek kode terakhir // Cek kode terakhir
const lastHama = await Hama.findOne({ order: [['id', 'DESC']] }); const lastHama = await Hama.findOne({ order: [['id', 'DESC']] });
@ -37,20 +39,28 @@ exports.createHama = async (req, res) => {
newKode = `H${lastNumber.toString().padStart(2, '0')}`; newKode = `H${lastNumber.toString().padStart(2, '0')}`;
} }
// Cek kalau ada file yang diupload
let fotoPath = '';
if (file) {
fotoPath = file.filename;
}
const newHama = await Hama.create({ const newHama = await Hama.create({
kode: newKode, kode: newKode,
nama, nama,
kategori: 'hama', // Default kategori kategori: 'hama', // Default kategori
deskripsi, deskripsi,
penanganan, penanganan,
foto: fotoPath, // ⬅️ Masukkan nama file ke database
}); });
res.status(201).json({ message: 'Hama berhasil ditambahkan', data: newHama }); res.status(201).json({ message: 'Hama berhasil ditambahkan', data: newHama });
} catch (error) { } catch (error) {
res.status(500).json({ message: 'Gagal menambahkan hama', error }); res.status(500).json({ message: 'Gagal menambahkan hama', error: error.message });
} }
}; };
// 🔹 Fungsi untuk mengupdate hama berdasarkan ID // 🔹 Fungsi untuk mengupdate hama berdasarkan ID
exports.updateHama = async (req, res) => { exports.updateHama = async (req, res) => {
try { try {

View File

@ -2,7 +2,7 @@
/** @type {import('sequelize-cli').Migration} */ /** @type {import('sequelize-cli').Migration} */
module.exports = { module.exports = {
async up(queryInterface, Sequelize) { async up(queryInterface, Sequelize) {
await queryInterface.createTable('hamas', { await queryInterface.createTable('hama', {
id: { id: {
allowNull: false, allowNull: false,
autoIncrement: true, autoIncrement: true,
@ -12,7 +12,6 @@ module.exports = {
kode: { kode: {
type: Sequelize.STRING, type: Sequelize.STRING,
allowNull: true allowNull: true
}, },
nama: { nama: {
type: Sequelize.STRING, type: Sequelize.STRING,
@ -28,6 +27,6 @@ module.exports = {
}); });
}, },
async down(queryInterface, Sequelize) { async down(queryInterface, Sequelize) {
await queryInterface.dropTable('hamas'); await queryInterface.dropTable('hama');
} }
}; };

View File

@ -0,0 +1,14 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.addColumn('hama', 'foto', {
type: Sequelize.STRING,
allowNull: true
});
},
async down(queryInterface, Sequelize) {
await queryInterface.removeColumn('hama', 'foto');
}
};

View File

@ -27,6 +27,10 @@ Hama.init(
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: true,
}, },
foto: {
type: DataTypes.STRING,
allowNull: false,
}
}, },
{ {
sequelize, sequelize,

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 952 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -6,56 +6,125 @@ import 'package:frontend/api_services/api_services.dart';
import 'package:frontend/user/login_page.dart'; import 'package:frontend/user/login_page.dart';
class AdminPage extends StatelessWidget { class AdminPage extends StatelessWidget {
Future<void> _logout(BuildContext context) async { Future<void> _logout(BuildContext context) async {
await ApiService.logoutUser(); await ApiService.logoutUser();
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => LoginPage())); Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => LoginPage()),
);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(title: Text('Admin Dashboard')),
title: Text('Admin Dashboard'), drawer: Drawer(
child: Container(
color: Color(0xFFFFFFFF),
child: ListView(
padding: EdgeInsets.zero,
children: [
SizedBox(
height: 70,
child: DrawerHeader(
decoration: BoxDecoration(color: Color(0xFF9DC08D)),
child: Text(
'Menu Admin',
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
),
ListTile(
title: Text('Halaman Hama'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HamaPage()),
);
},
),
ListTile(
title: Text('Halaman Penyakit'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PenyakitPage()),
);
},
),
ListTile(
title: Text('Halaman Gejala'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => GejalaPage()),
);
},
),
ListTile(title: Text('Logout'), onTap: () => _logout(context)),
],
),
),
), ),
body: Center( body: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Selamat datang Admin!',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
SizedBox(height: 24),
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildCard('Jumlah User', '10'),
_buildCard('Jumlah Diagnosa', '25'),
],
),
SizedBox(height: 16), // Spasi antar baris
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildCard('Penyakit', '15'),
_buildCard('Hama', '15'),
],
),
],
),
],
),
),
),
);
}
Widget _buildCard(String title, String count) {
return Card(
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Container(
width: 160,
height: 160,
padding: EdgeInsets.all(12),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
ElevatedButton( Text(
onPressed: () { title,
Navigator.push( textAlign: TextAlign.center,
context, style: TextStyle(fontWeight: FontWeight.bold),
MaterialPageRoute(builder: (context) => HamaPage()),
);
},
child: Text('Halaman Hama'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PenyakitPage()),
);
},
child: Text('Halaman Penyakit'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => GejalaPage()),
);
},
child: Text('Halaman Gejala'),
),
ElevatedButton(
onPressed: () => _logout(context),
child: Text('Logout'),
), ),
SizedBox(height: 10),
Text(count, style: TextStyle(fontSize: 20, color: Colors.green)),
], ],
), ),
), ),
); );
} }
} }

View File

@ -0,0 +1,119 @@
import 'package:flutter/material.dart';
import 'package:frontend/api_services/api_services.dart'; // Pastikan ini di-import ya
class EditHamaPage extends StatefulWidget {
final int idHama;
final String namaAwal;
final String deskripsiAwal;
final String penangananAwal;
final VoidCallback onHamaUpdated;
const EditHamaPage({
Key? key,
required this.idHama,
required this.namaAwal,
required this.deskripsiAwal,
required this.penangananAwal,
required this.onHamaUpdated,
}) : super(key: key);
@override
_EditHamaPageState createState() => _EditHamaPageState();
}
class _EditHamaPageState extends State<EditHamaPage> {
final TextEditingController _namaController = TextEditingController();
final TextEditingController _deskripsiController = TextEditingController();
final TextEditingController _penangananController = TextEditingController();
final ApiService apiService = ApiService();
@override
void initState() {
super.initState();
_namaController.text = widget.namaAwal;
_deskripsiController.text = widget.deskripsiAwal;
_penangananController.text = widget.penangananAwal;
}
@override
void dispose() {
_namaController.dispose();
_deskripsiController.dispose();
_penangananController.dispose();
super.dispose();
}
Future<void> _updateHama() async {
try {
await apiService.updateHama(
widget.idHama,
_namaController.text,
_deskripsiController.text,
_penangananController.text,
);
widget.onHamaUpdated();
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Data hama berhasil diperbarui')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Gagal memperbarui data: $e')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Edit Data Hama'),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: Card(
elevation: 5,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: _namaController,
decoration: InputDecoration(labelText: 'Nama Hama'),
),
SizedBox(height: 12),
TextField(
controller: _deskripsiController,
decoration: InputDecoration(labelText: 'Deskripsi'),
maxLines: 3,
),
SizedBox(height: 12),
TextField(
controller: _penangananController,
decoration: InputDecoration(labelText: 'Penanganan'),
maxLines: 3,
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _updateHama,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green[300],
),
child: Text(
'Simpan Perubahan',
style: TextStyle(color: Colors.black),
),
),
],
),
),
),
),
),
),
);
}
}

View File

@ -0,0 +1,119 @@
import 'package:flutter/material.dart';
import 'package:frontend/api_services/api_services.dart'; // Pastikan ini di-import ya
class EditPenyakitPage extends StatefulWidget {
final int idPenyakit;
final String namaAwal;
final String deskripsiAwal;
final String penangananAwal;
final VoidCallback onPenyakitUpdated;
const EditPenyakitPage({
Key? key,
required this.idPenyakit,
required this.namaAwal,
required this.deskripsiAwal,
required this.penangananAwal,
required this.onPenyakitUpdated,
}) : super(key: key);
@override
_EditPenyakitPageState createState() => _EditPenyakitPageState();
}
class _EditPenyakitPageState extends State<EditPenyakitPage> {
final TextEditingController _namaController = TextEditingController();
final TextEditingController _deskripsiController = TextEditingController();
final TextEditingController _penangananController = TextEditingController();
final ApiService apiService = ApiService();
@override
void initState() {
super.initState();
_namaController.text = widget.namaAwal;
_deskripsiController.text = widget.deskripsiAwal;
_penangananController.text = widget.penangananAwal;
}
@override
void dispose() {
_namaController.dispose();
_deskripsiController.dispose();
_penangananController.dispose();
super.dispose();
}
Future<void> _updatePenyakit() async {
try {
await apiService.updatePenyakit(
widget.idPenyakit,
_namaController.text,
_deskripsiController.text,
_penangananController.text,
);
widget.onPenyakitUpdated();
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Data penyakit berhasil diperbarui')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Gagal memperbarui data: $e')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Edit Data Penyakit'),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: Card(
elevation: 5,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: _namaController,
decoration: InputDecoration(labelText: 'Nama Penyakit'),
),
SizedBox(height: 12),
TextField(
controller: _deskripsiController,
decoration: InputDecoration(labelText: 'Deskripsi'),
maxLines: 3,
),
SizedBox(height: 12),
TextField(
controller: _penangananController,
decoration: InputDecoration(labelText: 'Penanganan'),
maxLines: 3,
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _updatePenyakit,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green[300],
),
child: Text(
'Simpan Perubahan',
style: TextStyle(color: Colors.black),
),
),
],
),
),
),
),
),
),
);
}
}

View File

@ -68,6 +68,53 @@ class _GejalaPageState extends State<GejalaPage> {
}, },
); );
} }
void showEditDialog(BuildContext context, Map<String, dynamic> gejala) {
final TextEditingController editNamaController = TextEditingController(text: gejala['nama'] ?? '');
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(
'Edit Hama',
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: editNamaController,
decoration: InputDecoration(
labelText: 'Nama',
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Batal'),
),
ElevatedButton(
onPressed: () async {
try {
await apiService.updateGejala(
gejala['id'],
editNamaController.text
);
fetchGejala();
Navigator.pop(context);
} catch (e) {
print("Error updating gejala: $e");
}
},
child: Text('Simpan', style: TextStyle(color: Colors.black)),
),
],
);
},
);
}
// 🔹 Hapus gejala dari API // 🔹 Hapus gejala dari API
void _hapusGejala(int id) async { void _hapusGejala(int id) async {
@ -108,59 +155,124 @@ class _GejalaPageState extends State<GejalaPage> {
} }
//pagination
int currentPage = 0;
int rowsPerPage = 10;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( int start = currentPage * rowsPerPage;
appBar: AppBar(title: Text('Halaman Gejala')), int end = (start + rowsPerPage < gejalaList.length)
body: Column( ? start + rowsPerPage
children: [ : gejalaList.length;
SizedBox(height: 20), List currentPageData = gejalaList.sublist(start, end);
Row(
mainAxisAlignment: MainAxisAlignment.end, return Scaffold(
children: [ appBar: AppBar(
Padding( title: Text('Halaman Gejala'),
padding: const EdgeInsets.only(right: 20.0), ),
child: ElevatedButton( body: Column(
onPressed: _tambahGejala, children: [
child: Text('Tambah Gejala'), SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.only(right: 20.0),
child: ElevatedButton(
onPressed: _tambahGejala,
child: Text(
'Tambah Gejala',
style: TextStyle(color: Colors.green[200]),
), ),
), ),
], ),
), ],
SizedBox(height: 20), ),
Expanded( SizedBox(height: 20),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SingleChildScrollView( child: SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.vertical,
child: SizedBox( child: SingleChildScrollView(
width: MediaQuery.of(context).size.width * 0.9, scrollDirection: Axis.horizontal,
child: DataTable( child: DataTable(
columnSpacing: 20, columnSpacing: 20,
headingRowColor: MaterialStateColor.resolveWith((states) => Colors.grey[300]!), headingRowColor: MaterialStateColor.resolveWith(
(states) => const Color(0xFF9DC08D),
),
columns: [ columns: [
DataColumn(label: SizedBox(width: 50, child: Text('No'))), DataColumn(label: SizedBox(width: 35, child: Text('No'))),
DataColumn(label: SizedBox(width: 80, child: Text('Kode'))), DataColumn(label: SizedBox(width: 80, child: Text('Kode'))),
DataColumn(label: SizedBox(width: 150, child: Text('Nama'))), DataColumn(label: SizedBox(width: 150, child: Text('Nama'))),
DataColumn(label: SizedBox(width: 80, child: Text('Aksi'))), DataColumn(label: SizedBox(width: 80, child: Text('Aksi'))),
], ],
rows: gejalaList.map( rows: [
(gejala) => DataRow(cells: [ ...currentPageData.map(
DataCell(Text((gejalaList.indexOf(gejala) + 1).toString())), // Nomor (gejala) => DataRow(
DataCell(Text(gejala['kode'])), // Kode Gejala cells: [
DataCell(Text(gejala['nama'])), // Nama Gejala DataCell(Text((gejalaList.indexOf(gejala) + 1).toString())),
DataCell( DataCell(Text(gejala['kode'] ?? '-')),
IconButton( DataCell(Text(gejala['nama'] ?? '-')),
icon: Icon(Icons.delete, color: Colors.red), DataCell(
onPressed: () => _konfirmasiHapus(gejala['id']), // Hapus data Row(
), children: [
IconButton(
icon: Icon(Icons.edit, color: Color(0xFF9DC08D)),
onPressed: () => showEditDialog(context, gejala),
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () => _konfirmasiHapus(gejala['id']),
),
],
),
),
],
), ),
]), ),
).toList(), DataRow(
cells: [
DataCell(Container()),
DataCell(Container()),
DataCell(
Align(
alignment: Alignment.centerRight,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.chevron_left),
onPressed: currentPage > 0
? () => setState(() => currentPage--)
: null,
),
Text(' ${currentPage + 1}'),
IconButton(
icon: Icon(Icons.chevron_right),
onPressed:
(currentPage + 1) * rowsPerPage < gejalaList.length
? () => setState(() => currentPage++)
: null,
),
],
),
),
),
DataCell(Container()),
],
),
],
), ),
), ),
), ),
), ),
], ),
), ],
); ),
} );
}
} }

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:frontend/api_services/api_services.dart'; import 'package:frontend/api_services/api_services.dart';
import 'tambah_hama_page.dart';
import 'edit_hama_page.dart';
class HamaPage extends StatefulWidget { class HamaPage extends StatefulWidget {
@override @override
@ -39,98 +40,167 @@ class _HamaPageState extends State<HamaPage> {
} }
void _konfirmasiHapus(int id) { void _konfirmasiHapus(int id) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Konfirmasi Hapus'),
content: Text('Apakah Anda yakin ingin menghapus gejala ini?'),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context); // Tutup pop-up tanpa menghapus
},
child: Text('Tidak'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context); // Tutup pop-up
_hapusHama(id); // Lanjutkan proses hapus
},
child: Text('Ya, Hapus'),
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
),
],
);
},
);
}
void _tambahHama() {
TextEditingController namaController = TextEditingController();
TextEditingController penangananController = TextEditingController();
TextEditingController deskripsiController = TextEditingController();
showDialog( showDialog(
context: context, context: context,
builder: (context) { builder: (context) {
return AlertDialog( return AlertDialog(
title: Text('Tambah Hama Baru'), title: Text('Konfirmasi Hapus'),
content: Column( content: Text('Apakah Anda yakin ingin menghapus gejala ini?'),
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: namaController,
decoration: InputDecoration(labelText: 'Nama'),
),
TextField(
controller: deskripsiController,
decoration: InputDecoration(labelText: 'Deskripsi'),
),
TextField(
controller: penangananController,
decoration: InputDecoration(labelText: 'Penanganan'),
),
],
),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.pop(context), onPressed: () {
child: Text('Batal'), Navigator.pop(context); // Tutup pop-up tanpa menghapus
},
child: Text('Tidak'),
), ),
ElevatedButton( ElevatedButton(
onPressed: () async { onPressed: () {
if (namaController.text.isNotEmpty && Navigator.pop(context); // Tutup pop-up
deskripsiController.text.isNotEmpty && _hapusHama(id); // Lanjutkan proses hapus
penangananController.text.isNotEmpty) {
try {
await apiService.createHama(
namaController.text,
deskripsiController.text,
penangananController.text,
);
_fetchHama();
Navigator.pop(context);
} catch (e) {
print("Error adding hama: $e");
}
}
}, },
child: Text('Simpan'), child: Text('Ya, Hapus'),
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
), ),
], ],
); );
}, },
).then((_) { );
namaController.dispose();
deskripsiController.dispose();
penangananController.dispose();
});
} }
// void _tambahHama() {
// TextEditingController namaController = TextEditingController();
// TextEditingController penangananController = TextEditingController();
// TextEditingController deskripsiController = TextEditingController();
// showDialog(
// context: context,
// builder: (context) {
// return AlertDialog(
// title: Text('Tambah Hama Baru'),
// content: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// TextField(
// controller: namaController,
// decoration: InputDecoration(labelText: 'Nama'),
// ),
// TextField(
// controller: deskripsiController,
// decoration: InputDecoration(labelText: 'Deskripsi'),
// ),
// TextField(
// controller: penangananController,
// decoration: InputDecoration(labelText: 'Penanganan'),
// ),
// ],
// ),
// actions: [
// TextButton(
// onPressed: () => Navigator.pop(context),
// child: Text('Batal', style: TextStyle(color: Colors.black)),
// ),
// ElevatedButton(
// onPressed: () async {
// if (namaController.text.isNotEmpty &&
// deskripsiController.text.isNotEmpty &&
// penangananController.text.isNotEmpty) {
// try {
// await apiService.createHama(
// namaController.text,
// deskripsiController.text,
// penangananController.text,
// );
// _fetchHama();
// Navigator.pop(context);
// } catch (e) {
// print("Error adding hama: $e");
// }
// }
// },
// child: Text('Simpan', style: TextStyle(color: Colors.black)),
// ),
// ],
// );
// },
// ).then((_) {
// namaController.dispose();
// deskripsiController.dispose();
// penangananController.dispose();
// });
// }
// void showEditDialog(BuildContext context, Map<String, dynamic> hama) {
// final TextEditingController editNamaController = TextEditingController(
// text: hama['nama'] ?? '',
// );
// final TextEditingController editDeskripsiController = TextEditingController(
// text: hama['deskripsi'] ?? '',
// );
// final TextEditingController editPenangananController =
// TextEditingController(text: hama['penanganan'] ?? '');
// showDialog(
// context: context,
// builder: (context) {
// return AlertDialog(
// title: Text('Edit Hama'),
// content: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// TextField(
// controller: editNamaController,
// decoration: InputDecoration(labelText: 'Nama'),
// ),
// TextField(
// controller: editDeskripsiController,
// decoration: InputDecoration(labelText: 'Deskripsi'),
// ),
// TextField(
// controller: editPenangananController,
// decoration: InputDecoration(labelText: 'Penanganan'),
// ),
// ],
// ),
// actions: [
// TextButton(
// onPressed: () => Navigator.pop(context),
// child: Text('Batal'),
// ),
// ElevatedButton(
// onPressed: () async {
// try {
// await apiService.updateHama(
// hama['id'],
// editNamaController.text,
// editDeskripsiController.text,
// editPenangananController.text,
// );
// _fetchHama();
// Navigator.pop(context);
// } catch (e) {
// print("Error updating hama: $e");
// }
// },
// child: Text('Simpan', style: TextStyle(color: Colors.black)),
// ),
// ],
// );
// },
// );
// }
//pagination
int currentPage = 0;
int rowsPerPage = 10;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
int start = currentPage * rowsPerPage;
int end =
(start + rowsPerPage < hamaList.length)
? start + rowsPerPage
: hamaList.length;
List currentPageData = hamaList.sublist(start, end);
return Scaffold( return Scaffold(
appBar: AppBar(title: Text('Halaman Hama')), appBar: AppBar(title: Text('Halaman Hama')),
body: Column( body: Column(
@ -142,45 +212,147 @@ class _HamaPageState extends State<HamaPage> {
Padding( Padding(
padding: const EdgeInsets.only(right: 20.0), padding: const EdgeInsets.only(right: 20.0),
child: ElevatedButton( child: ElevatedButton(
onPressed: _tambahHama, // Fungsi untuk menambah data hama onPressed: () {
child: Text('Tambah Hama'), Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => TambahHamaPage(
onHamaAdded:
_fetchHama, // Panggil fungsi refresh setelah tambah
),
),
);
},
child: Text(
'Tambah Hama',
style: TextStyle(color: Colors.green[200]),
),
), ),
), ),
], ],
), ),
SizedBox(height: 20), SizedBox(height: 20),
Expanded( Expanded(
child: SingleChildScrollView( child: Padding(
scrollDirection: Axis.horizontal, padding: const EdgeInsets.all(8.0),
child: SizedBox( child: SingleChildScrollView(
width: MediaQuery.of(context).size.width * 0.9, scrollDirection: Axis.vertical,
child: DataTable( child: SingleChildScrollView(
columnSpacing: 20, scrollDirection: Axis.horizontal,
headingRowColor: child: DataTable(
MaterialStateColor.resolveWith((states) => Colors.grey[300]!), columnSpacing: 20,
columns: [ headingRowColor: MaterialStateColor.resolveWith(
DataColumn(label: SizedBox(width: 35, child: Text('No'))), (states) => const Color(0xFF9DC08D),
DataColumn(label: SizedBox(width: 50, child: Text('Kode'))), ),
DataColumn(label: SizedBox(width: 100, child: Text('Nama'))), columns: [
DataColumn(label: SizedBox(width: 100, child: Text('Deskripsi'))), DataColumn(label: SizedBox(width: 35, child: Text('No'))),
DataColumn(label: SizedBox(width: 100, child: Text('Penanganan'))), DataColumn(
DataColumn(label: SizedBox(width: 50, child: Text('Aksi'))), label: SizedBox(width: 50, child: Text('Kode')),
], ),
rows: hamaList.map( DataColumn(
(hama) => DataRow(cells: [ label: SizedBox(width: 100, child: Text('Nama')),
DataCell(Text((hamaList.indexOf(hama) + 1).toString())), // Nomor ),
DataCell(Text(hama['kode'] ?? '-')), // Kode Hama DataColumn(
DataCell(Text(hama['nama'] ?? '-')), // Nama Hama label: SizedBox(width: 100, child: Text('Deskripsi')),
DataCell(Text(hama['deskripsi'] ?? '-')), // Deskripsi ),
DataCell(Text(hama['penanganan'] ?? '-')), // Penanganan DataColumn(
DataCell( label: SizedBox(width: 100, child: Text('Penanganan')),
IconButton( ),
icon: Icon(Icons.delete, color: Colors.red), DataColumn(
onPressed: () => _konfirmasiHapus(hama['id']), // Hapus data label: SizedBox(width: 50, child: Text('Aksi')),
),
],
rows: [
...currentPageData.map(
(hama) => DataRow(
cells: [
DataCell(
Text((hamaList.indexOf(hama) + 1).toString()),
),
DataCell(Text(hama['kode'] ?? '-')),
DataCell(Text(hama['nama'] ?? '-')),
DataCell(Text(hama['deskripsi'] ?? '-')),
DataCell(Text(hama['penanganan'] ?? '-')),
DataCell(
Row(
children: [
IconButton(
icon: Icon(
Icons.edit,
color: Color(0xFF9DC08D),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => EditHamaPage(
idHama:
hama['id'], // pastikan 'hama' adalah Map dari API kamu
namaAwal: hama['nama'] ?? '',
deskripsiAwal:
hama['deskripsi'] ?? '',
penangananAwal:
hama['penanganan'] ?? '',
onHamaUpdated:
_fetchHama, // fungsi untuk refresh list setelah update
),
),
);
},
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed:
() => _konfirmasiHapus(hama['id']),
),
],
),
),
],
), ),
), ),
]), DataRow(
).toList(), cells: [
DataCell(Container()),
DataCell(Container()),
DataCell(
Align(
alignment: Alignment.centerRight,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.chevron_left),
onPressed:
currentPage > 0
? () =>
setState(() => currentPage--)
: null,
),
Text(' ${currentPage + 1}'),
IconButton(
icon: Icon(Icons.chevron_right),
onPressed:
(currentPage + 1) * rowsPerPage <
hamaList.length
? () =>
setState(() => currentPage++)
: null,
),
],
),
),
),
DataCell(Container()),
DataCell(Container()),
DataCell(Container()),
],
),
],
),
), ),
), ),
), ),
@ -189,5 +361,4 @@ class _HamaPageState extends State<HamaPage> {
), ),
); );
} }
} }

View File

@ -1,5 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:frontend/admin/edit_penyakit_page.dart';
import 'package:frontend/api_services/api_services.dart'; import 'package:frontend/api_services/api_services.dart';
import 'tambah_penyakit_page.dart';
import 'edit_penyakit_page.dart';
class PenyakitPage extends StatefulWidget { class PenyakitPage extends StatefulWidget {
@override @override
@ -7,10 +10,10 @@ class PenyakitPage extends StatefulWidget {
} }
class _PenyakitPageState extends State<PenyakitPage> { class _PenyakitPageState extends State<PenyakitPage> {
final ApiService apiService = ApiService(); final ApiService apiService = ApiService();
List<Map<String, dynamic>> penyakitList = []; List<Map<String, dynamic>> penyakitList = [];
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_fetchPenyakit(); _fetchPenyakit();
@ -38,97 +41,173 @@ class _PenyakitPageState extends State<PenyakitPage> {
} }
void _konfirmasiHapus(int id) { void _konfirmasiHapus(int id) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Konfirmasi Hapus'),
content: Text('Apakah Anda yakin ingin menghapus gejala ini?'),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context); // Tutup pop-up tanpa menghapus
},
child: Text('Tidak'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context); // Tutup pop-up
_hapusPenyakit(id); // Lanjutkan proses hapus
},
child: Text('Ya, Hapus'),
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
),
],
);
},
);
}
void _tambahPenyakit() {
TextEditingController namaController = TextEditingController();
TextEditingController penangananController = TextEditingController();
TextEditingController deskripsiController = TextEditingController();
showDialog( showDialog(
context: context, context: context,
builder: (context) { builder: (context) {
return AlertDialog( return AlertDialog(
title: Text('Tambah Penyakit Baru'), title: Text('Konfirmasi Hapus'),
content: Column( content: Text('Apakah Anda yakin ingin menghapus gejala ini?'),
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: namaController,
decoration: InputDecoration(labelText: 'Nama'),
),
TextField(
controller: deskripsiController,
decoration: InputDecoration(labelText: 'Deskripsi'),
),
TextField(
controller: penangananController,
decoration: InputDecoration(labelText: 'Penanganan'),
),
],
),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.pop(context), onPressed: () {
child: Text('Batal'), Navigator.pop(context); // Tutup pop-up tanpa menghapus
},
child: Text('Tidak'),
), ),
ElevatedButton( ElevatedButton(
onPressed: () async { onPressed: () {
if (namaController.text.isNotEmpty && Navigator.pop(context); // Tutup pop-up
deskripsiController.text.isNotEmpty && _hapusPenyakit(id); // Lanjutkan proses hapus
penangananController.text.isNotEmpty) {
try {
await apiService.createPenyakit(
namaController.text,
deskripsiController.text,
penangananController.text,
);
_fetchPenyakit();
Navigator.pop(context);
} catch (e) {
print("Error adding penyakit: $e");
}
}
}, },
child: Text('Simpan'), child: Text('Ya, Hapus'),
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
), ),
], ],
); );
}, },
).then((_) { );
namaController.dispose();
deskripsiController.dispose();
penangananController.dispose();
});
} }
// void _tambahPenyakit() {
// TextEditingController namaController = TextEditingController();
// TextEditingController penangananController = TextEditingController();
// TextEditingController deskripsiController = TextEditingController();
// showDialog(
// context: context,
// builder: (context) {
// return AlertDialog(
// title: Text('Tambah Penyakit Baru'),
// content: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// TextField(
// controller: namaController,
// decoration: InputDecoration(labelText: 'Nama'),
// ),
// TextField(
// controller: deskripsiController,
// decoration: InputDecoration(labelText: 'Deskripsi'),
// ),
// TextField(
// controller: penangananController,
// decoration: InputDecoration(labelText: 'Penanganan'),
// ),
// ],
// ),
// actions: [
// TextButton(
// onPressed: () => Navigator.pop(context),
// child: Text('Batal'),
// ),
// ElevatedButton(
// onPressed: () async {
// if (namaController.text.isNotEmpty &&
// deskripsiController.text.isNotEmpty &&
// penangananController.text.isNotEmpty) {
// try {
// await apiService.createPenyakit(
// namaController.text,
// deskripsiController.text,
// penangananController.text,
// );
// _fetchPenyakit();
// Navigator.pop(context);
// } catch (e) {
// print("Error adding penyakit: $e");
// }
// }
// },
// child: Text('Simpan'),
// ),
// ],
// );
// },
// ).then((_) {
// namaController.dispose();
// deskripsiController.dispose();
// penangananController.dispose();
// });
// }
// void showEditDialog(BuildContext context, Map<String, dynamic> penyakit) {
// final TextEditingController editNamaController = TextEditingController(text: penyakit['nama'] ?? '');
// final TextEditingController editDeskripsiController = TextEditingController(text: penyakit['deskripsi'] ?? '');
// final TextEditingController editPenangananController = TextEditingController(text: penyakit['penanganan'] ?? '');
// showDialog(
// context: context,
// builder: (context) {
// return AlertDialog(
// title: Text(
// 'Edit Penyakit',
// ),
// content: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// TextField(
// controller: editNamaController,
// decoration: InputDecoration(
// labelText: 'Nama',
// ),
// ),
// TextField(
// controller: editDeskripsiController,
// decoration: InputDecoration(
// labelText: 'Deskripsi',
// ),
// ),
// TextField(
// controller: editPenangananController,
// decoration: InputDecoration(
// labelText: 'Penanganan',
// ),
// ),
// ],
// ),
// actions: [
// TextButton(
// onPressed: () => Navigator.pop(context),
// child: Text(
// 'Batal',
// style: TextStyle(color: Colors.black),
// ),
// ),
// ElevatedButton(
// onPressed: () async {
// try {
// await apiService.updatePenyakit(
// penyakit['id'],
// editNamaController.text,
// editDeskripsiController.text,
// editPenangananController.text,
// );
// _fetchPenyakit();
// Navigator.pop(context);
// } catch (e) {
// print("Error updating penyakit: $e");
// }
// },
// child: Text('Simpan', style: TextStyle(color: Colors.black)),
// ),
// ],
// );
// },
// );
// }
//pagination
int currentPage = 0;
int rowsPerPage = 10;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
int start = currentPage * rowsPerPage;
int end =
(start + rowsPerPage < penyakitList.length)
? start + rowsPerPage
: penyakitList.length;
List currentPageData = penyakitList.sublist(start, end);
return Scaffold( return Scaffold(
appBar: AppBar(title: Text('Halaman Penyakit')), appBar: AppBar(title: Text('Halaman Penyakit')),
body: Column( body: Column(
@ -140,45 +219,146 @@ class _PenyakitPageState extends State<PenyakitPage> {
Padding( Padding(
padding: const EdgeInsets.only(right: 20.0), padding: const EdgeInsets.only(right: 20.0),
child: ElevatedButton( child: ElevatedButton(
onPressed: _tambahPenyakit, // Fungsi untuk menambah data penyakit onPressed: () {
child: Text('Tambah Penyakit'), Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => TambahPenyakitPage(
onPenyakitAdded:
_fetchPenyakit, // Panggil fungsi refresh setelah tambah
),
),
);
}, // Fungsi untuk menambah data penyakit
child: Text(
'Tambah Penyakit',
style: TextStyle(color: Colors.green[200]),
),
), ),
), ),
], ],
), ),
SizedBox(height: 20), SizedBox(height: 20),
Expanded( Expanded(
child: SingleChildScrollView( child: Padding(
scrollDirection: Axis.horizontal, padding: const EdgeInsets.all(8.0),
child: SizedBox( child: SingleChildScrollView(
width: MediaQuery.of(context).size.width * 0.95, scrollDirection: Axis.vertical,
child: DataTable( child: SingleChildScrollView(
columnSpacing: 5, scrollDirection: Axis.horizontal,
headingRowColor: child: DataTable(
MaterialStateColor.resolveWith((states) => Colors.grey[300]!), columnSpacing: 20,
columns: [ headingRowColor: MaterialStateColor.resolveWith(
DataColumn(label: SizedBox(width: 35, child: Text('No'))), (states) => const Color(0xFF9DC08D),
DataColumn(label: SizedBox(width: 50, child: Text('Kode'))), ),
DataColumn(label: SizedBox(width: 100, child: Text('Nama'))), columns: [
DataColumn(label: SizedBox(width: 100, child: Text('Deskripsi'))), DataColumn(label: SizedBox(width: 35, child: Text('No'))),
DataColumn(label: SizedBox(width: 100, child: Text('Penanganan'))), DataColumn(
DataColumn(label: SizedBox(width: 50, child: Text('Aksi'))), label: SizedBox(width: 50, child: Text('Kode')),
], ),
rows: penyakitList.map( DataColumn(
(penyakit) => DataRow(cells: [ label: SizedBox(width: 100, child: Text('Nama')),
DataCell(Text((penyakitList.indexOf(penyakit) + 1).toString())), // Nomor ),
DataCell(Text(penyakit['kode'] ?? '-')), // Kode Penyakit DataColumn(
DataCell(Text(penyakit['nama'] ?? '-')), // Nama Penyakit label: SizedBox(width: 100, child: Text('Deskripsi')),
DataCell(Text(penyakit['deskripsi'] ?? '-')), // Deskripsi ),
DataCell(Text(penyakit['penanganan'] ?? '-')), // Penanganan DataColumn(
DataCell( label: SizedBox(width: 100, child: Text('Penanganan')),
IconButton( ),
icon: Icon(Icons.delete, color: Colors.red), DataColumn(
onPressed: () => _konfirmasiHapus(penyakit['id']), // Hapus data label: SizedBox(width: 50, child: Text('Aksi')),
),
],
rows: [
...currentPageData.map(
(penyakit) => DataRow(
cells: [
DataCell(
Text((penyakitList.indexOf(penyakit) + 1).toString()),
),
DataCell(Text(penyakit['kode'] ?? '-')),
DataCell(Text(penyakit['nama'] ?? '-')),
DataCell(Text(penyakit['deskripsi'] ?? '-')),
DataCell(Text(penyakit['penanganan'] ?? '-')),
DataCell(
Row(
children: [
IconButton(
icon: Icon(
Icons.edit,
color: Color(0xFF9DC08D),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => EditPenyakitPage(
idPenyakit:
penyakit['id'], // pastikan 'hama' adalah Map dari API kamu
namaAwal: penyakit['nama'] ?? '',
deskripsiAwal:
penyakit['deskripsi'] ?? '',
penangananAwal:
penyakit['penanganan'] ?? '',
onPenyakitUpdated:
_fetchPenyakit, // fungsi untuk refresh list setelah update
),
),
);
},
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed:
() => _konfirmasiHapus(penyakit['id']),
),
],
),
),
],
), ),
), ),
]), DataRow(
).toList(), cells: [
DataCell(Container()),
DataCell(Container()),
DataCell(
Align(
alignment: Alignment.centerRight,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.chevron_left),
onPressed:
currentPage > 0
? () =>
setState(() => currentPage--)
: null,
),
Text(' ${currentPage + 1}'),
IconButton(
icon: Icon(Icons.chevron_right),
onPressed:
(currentPage + 1) * rowsPerPage <
penyakitList.length
? () =>
setState(() => currentPage++)
: null,
),
],
),
),
),
DataCell(Container()),
DataCell(Container()),
DataCell(Container()),
],
),
],
),
), ),
), ),
), ),

View File

@ -0,0 +1,124 @@
import 'package:flutter/material.dart';
import 'package:frontend/api_services/api_services.dart';
class TambahHamaPage extends StatefulWidget {
final VoidCallback onHamaAdded;
TambahHamaPage({required this.onHamaAdded});
@override
_TambahHamaPageState createState() => _TambahHamaPageState();
}
class _TambahHamaPageState extends State<TambahHamaPage> {
final TextEditingController namaController = TextEditingController();
final TextEditingController deskripsiController = TextEditingController();
final TextEditingController penangananController = TextEditingController();
final ApiService apiService = ApiService();
@override
void dispose() {
namaController.dispose();
deskripsiController.dispose();
penangananController.dispose();
super.dispose();
}
Future<void> _simpanHama() async {
if (namaController.text.isNotEmpty &&
deskripsiController.text.isNotEmpty &&
penangananController.text.isNotEmpty) {
try {
await apiService.createHama(
namaController.text,
deskripsiController.text,
penangananController.text,
);
widget.onHamaAdded();
Navigator.pop(context);
_showDialog('Berhasil', 'Data hama berhasil ditambahkan.');
} catch (e) {
_showDialog('Gagal', 'Gagal menambahkan data hama.');
print("Error adding hama: $e");
}
} else {
_showDialog('Error', 'Semua field harus diisi.');
}
}
void _showDialog(String title, String message) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(title),
content: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('OK'),
)
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Tambah Hama Baru'),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 30.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
child: Container(
width: 320, // atur lebar card box
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
TextField(
controller: namaController,
decoration: InputDecoration(labelText: 'Nama Hama'),
),
SizedBox(height: 15),
TextField(
controller: deskripsiController,
decoration: InputDecoration(labelText: 'Deskripsi Hama'),
maxLines: 3, // Biar lebih panjang untuk deskripsi
),
SizedBox(height: 15),
TextField(
controller: penangananController,
decoration: InputDecoration(labelText: 'Penanganan Hama'),
maxLines: 3,
),
SizedBox(height: 15),
],
),
),
),
SizedBox(height: 30), // jarak antara card dan tombol
ElevatedButton(
onPressed: _simpanHama,
child: Text('Simpan Data'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green[300],
padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15),
textStyle: TextStyle(fontSize: 16, color: Colors.black),
),
),
],
),
),
),
);
}
}

View File

@ -0,0 +1,124 @@
import 'package:flutter/material.dart';
import 'package:frontend/api_services/api_services.dart';
class TambahPenyakitPage extends StatefulWidget {
final VoidCallback onPenyakitAdded;
TambahPenyakitPage({required this.onPenyakitAdded});
@override
_TambahPenyakitPageState createState() => _TambahPenyakitPageState();
}
class _TambahPenyakitPageState extends State<TambahPenyakitPage> {
final TextEditingController namaController = TextEditingController();
final TextEditingController deskripsiController = TextEditingController();
final TextEditingController penangananController = TextEditingController();
final ApiService apiService = ApiService();
@override
void dispose() {
namaController.dispose();
deskripsiController.dispose();
penangananController.dispose();
super.dispose();
}
Future<void> _simpanPenyakit() async {
if (namaController.text.isNotEmpty &&
deskripsiController.text.isNotEmpty &&
penangananController.text.isNotEmpty) {
try {
await apiService.createPenyakit(
namaController.text,
deskripsiController.text,
penangananController.text,
);
widget.onPenyakitAdded();
Navigator.pop(context);
_showDialog('Berhasil', 'Data penyakit berhasil ditambahkan.');
} catch (e) {
_showDialog('Gagal', 'Gagal menambahkan data penyakit.');
print("Error adding penyakit: $e");
}
} else {
_showDialog('Error', 'Semua field harus diisi.');
}
}
void _showDialog(String title, String message) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(title),
content: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('OK'),
)
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Tambah Penyakit Baru'),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 30.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
child: Container(
width: 320, // atur lebar card box
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
TextField(
controller: namaController,
decoration: InputDecoration(labelText: 'Nama Penyakit'),
),
SizedBox(height: 15),
TextField(
controller: deskripsiController,
decoration: InputDecoration(labelText: 'Deskripsi Penyakit'),
maxLines: 3, // Biar lebih panjang untuk deskripsi
),
SizedBox(height: 15),
TextField(
controller: penangananController,
decoration: InputDecoration(labelText: 'Penanganan Penyakit'),
maxLines: 3,
),
SizedBox(height: 15),
],
),
),
),
SizedBox(height: 30), // jarak antara card dan tombol
ElevatedButton(
onPressed: _simpanPenyakit,
child: Text('Simpan Data'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green[300],
padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15),
textStyle: TextStyle(fontSize: 16, color: Colors.black),
),
),
],
),
),
),
);
}
}

View File

@ -281,5 +281,51 @@ class ApiService {
throw Exception('Gagal menghapus penyakit'); throw Exception('Gagal menghapus penyakit');
} }
} }
//registrasi
Future<void> registerUser({
required String name,
required String email,
required String password,
required String alamat,
required String nomorTelepon,
}) async {
final response = await http.post(
Uri.parse('$baseUrl/register'), // Endpoint register
headers: {"Content-Type": "application/json"},
body: jsonEncode({
'name': name,
'email': email,
'password': password,
'alamat': alamat,
'nomorTelepon': nomorTelepon,
'role': 'user', // role default
}),
);
if (response.statusCode != 201) {
throw Exception(jsonDecode(response.body)['message'] ?? 'Gagal mendaftar');
}
}
// Fungsi untuk lupa password
Future<void> forgotPassword({
required String email,
required String newPassword,
}) async {
final response = await http.post(
Uri.parse('$baseUrl/forgot-password'),
headers: {"Content-Type": "application/json"},
body: jsonEncode({
'email': email,
'password': newPassword, // Kirim password baru
}),
);
if (response.statusCode != 200) {
throw Exception(jsonDecode(response.body)['message'] ?? 'Gagal memperbarui password');
}
}
} }

View File

@ -1,4 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'hama_page.dart';
import 'penyakit_page.dart';
class BasisPengetahuanPage extends StatelessWidget { class BasisPengetahuanPage extends StatelessWidget {
@override @override
@ -31,7 +33,12 @@ class BasisPengetahuanPage extends StatelessWidget {
// Button pertama // Button pertama
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
// Aksi untuk button pertama Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HamaPage(),
), // Perbaikan di sini
);
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@ -67,7 +74,12 @@ class BasisPengetahuanPage extends StatelessWidget {
// Button kedua // Button kedua
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
// Aksi untuk button kedua Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PenyakitPage(),
),
);
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(

View File

@ -0,0 +1,108 @@
import 'package:flutter/material.dart';
class DetailHamaPage extends StatelessWidget {
final Map<String, String> detailRiwayat;
const DetailHamaPage({required this.detailRiwayat});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF9DC08D),
appBar: AppBar(
backgroundColor: Color(0xFF9DC08D),
title: Text(
"Detail Hama",
style: TextStyle(color: Colors.white),
),
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.white),
onPressed: () => Navigator.of(context).pop(),
),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
if (detailRiwayat["gambar"] != null && detailRiwayat["gambar"]!.isNotEmpty)
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.asset(
detailRiwayat["gambar"]!,
height: 200,
width: 200, // Biar gambar full lebar
fit: BoxFit.cover,
),
),
SizedBox(height: 16),
// Card Nama Hama
SizedBox(
width: double.infinity, // Bikin card full lebar
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Nama Hama:",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
detailRiwayat["nama hama"] ?? "Nama hama tidak tersedia",
style: TextStyle(fontSize: 16),
),
],
),
),
),
),
SizedBox(height: 16),
// Card Deskripsi + Penanganan
SizedBox(
width: double.infinity, // Bikin card full lebar
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Deskripsi:",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
detailRiwayat["deskripsi"] ?? "Deskripsi tidak tersedia",
style: TextStyle(fontSize: 16),
),
SizedBox(height: 16),
Text(
"Penanganan:",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
detailRiwayat["penanganan"] ?? "Penanganan tidak tersedia",
style: TextStyle(fontSize: 16),
),
],
),
),
),
),
],
),
),
),
);
}
}

View File

@ -0,0 +1,108 @@
import 'package:flutter/material.dart';
class DetailPenyakitPage extends StatelessWidget {
final Map<String, String> detailPenyakit;
const DetailPenyakitPage({required this.detailPenyakit});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF9DC08D),
appBar: AppBar(
backgroundColor: Color(0xFF9DC08D),
title: Text(
"Detail Penyakit",
style: TextStyle(color: Colors.white),
),
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.white),
onPressed: () => Navigator.of(context).pop(),
),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
if (detailPenyakit["gambar"] != null && detailPenyakit["gambar"]!.isNotEmpty)
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.asset(
detailPenyakit["gambar"]!,
height: 200,
width: 200, // Biar gambar full lebar
fit: BoxFit.cover,
),
),
SizedBox(height: 16),
// Card Nama Penyakit
SizedBox(
width: double.infinity, // Bikin card full lebar
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Nama Penyakit:",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
detailPenyakit["nama penyakit"] ?? "Nama penyakit tidak tersedia",
style: TextStyle(fontSize: 16),
),
],
),
),
),
),
SizedBox(height: 16),
// Card Deskripsi + Penanganan
SizedBox(
width: double.infinity, // Bikin card full lebar
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Deskripsi:",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
detailPenyakit["deskripsi"] ?? "Deskripsi tidak tersedia",
style: TextStyle(fontSize: 16),
),
SizedBox(height: 16),
Text(
"Penanganan:",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
detailPenyakit["penanganan"] ?? "Penanganan tidak tersedia",
style: TextStyle(fontSize: 16),
),
],
),
),
),
),
],
),
),
),
);
}
}

View File

@ -1,7 +1,56 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:frontend/api_services/api_services.dart'; // Pastikan path-nya sesuai
class ForgotPasswordPage extends StatefulWidget {
@override
_ForgotPasswordPageState createState() => _ForgotPasswordPageState();
}
class _ForgotPasswordPageState extends State<ForgotPasswordPage> {
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
final ApiService apiService = ApiService();
bool isLoading = false;
void handleForgotPassword() async {
setState(() {
isLoading = true;
});
try {
await apiService.forgotPassword(
email: emailController.text.trim(),
newPassword: passwordController.text.trim(),
);
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text('Berhasil'),
content: Text('Password berhasil direset.'),
actions: [
TextButton(
child: Text('OK'),
onPressed: () {
Navigator.of(context).pop(); // tutup dialog
Navigator.of(context).pop(); // kembali ke halaman sebelumnya
},
),
],
),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString())),
);
} finally {
setState(() {
isLoading = false;
});
}
}
// Halaman Lupa Password
class ForgotPasswordPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -24,6 +73,7 @@ class ForgotPasswordPage extends StatelessWidget {
child: Column( child: Column(
children: [ children: [
TextField( TextField(
controller: emailController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Email', labelText: 'Email',
border: OutlineInputBorder( border: OutlineInputBorder(
@ -32,6 +82,17 @@ class ForgotPasswordPage extends StatelessWidget {
), ),
), ),
SizedBox(height: 20), SizedBox(height: 20),
TextField(
controller: passwordController,
obscureText: true,
decoration: InputDecoration(
labelText: 'Password Baru',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
SizedBox(height: 20),
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
height: 50, height: 50,
@ -42,17 +103,17 @@ class ForgotPasswordPage extends StatelessWidget {
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
), ),
onPressed: () { onPressed: isLoading ? null : handleForgotPassword,
// Logic reset password child: isLoading
}, ? CircularProgressIndicator(color: Colors.white)
child: Text( : Text(
'Reset Password', 'Reset Password',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
), ),
), ),
], ],
@ -64,4 +125,4 @@ class ForgotPasswordPage extends StatelessWidget {
), ),
); );
} }
} }

View File

@ -0,0 +1,90 @@
import 'package:flutter/material.dart';
import 'detail_hama_page.dart';
class HamaPage extends StatelessWidget {
final List<Map<String, String>> 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",
},
];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF9DC08D),
appBar: AppBar(
backgroundColor: Color(0xFF9DC08D),
title: Align(
alignment: Alignment.topCenter,
child: Padding(
padding: const EdgeInsets.only(right: 30),
child: Text(
"Hama",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.white),
),
),
),
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.white),
onPressed: () => Navigator.of(context).pop(),
),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: [
SizedBox(height: 16),
Expanded(
child: ListView.builder(
itemCount: hamaList.length,
itemBuilder: (context, index) {
final diagnosa = hamaList[index];
return Card(
elevation: 4,
margin: const EdgeInsets.symmetric(vertical: 8),
child: ListTile(
title: Text(
diagnosa["nama hama"] ?? "Tidak ada data",
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(diagnosa["deskripsi"] ?? "Deskripsi tidak tersedia"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailHamaPage(detailRiwayat: diagnosa),
),
);
},
),
);
},
),
),
],
),
),
);
}
}

View File

@ -73,14 +73,21 @@ class HomePage extends StatelessWidget {
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column( child: Wrap(
spacing: 20, // Jarak horizontal antar tombol
runSpacing: 20, // Jarak vertikal antar baris tombol
alignment: WrapAlignment.center, // Menempatkan tombol di tengah
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
ButtonMenu( ButtonMenu(
title: "Riwayat Diagnosa", title: "Riwayat Diagnosa",
icon: Icons.medical_services, customIcon: Image.asset(
'assets/images/Order History.png',
width: 48,
height: 48,
),
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
@ -92,7 +99,11 @@ class HomePage extends StatelessWidget {
), ),
ButtonMenu( ButtonMenu(
title: "Profile", title: "Profile",
icon: Icons.bug_report, customIcon: Image.asset(
'assets/images/Test Account.png',
width: 48,
height: 48,
),
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
@ -110,7 +121,11 @@ class HomePage extends StatelessWidget {
children: [ children: [
ButtonMenu( ButtonMenu(
title: "Basis Pengetahuan", title: "Basis Pengetahuan",
icon: Icons.info, customIcon: Image.asset(
'assets/images/Literature.png',
width: 48,
height: 48,
),
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
@ -122,7 +137,11 @@ class HomePage extends StatelessWidget {
), ),
ButtonMenu( ButtonMenu(
title: "Info Pakar", title: "Info Pakar",
icon: Icons.exit_to_app, customIcon: Image.asset(
'assets/images/Businessman.png',
width: 48,
height: 48,
),
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
@ -148,12 +167,12 @@ class HomePage extends StatelessWidget {
// Widget untuk tombol menu // Widget untuk tombol menu
class ButtonMenu extends StatelessWidget { class ButtonMenu extends StatelessWidget {
final String title; final String title;
final IconData icon; final Widget customIcon;
final VoidCallback onTap; final VoidCallback onTap;
const ButtonMenu({ const ButtonMenu({
required this.title, required this.title,
required this.icon, required this.customIcon,
required this.onTap, required this.onTap,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@ -175,10 +194,11 @@ class ButtonMenu extends StatelessWidget {
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon(icon, size: 30, color: Colors.green), // Ikon customIcon,
const SizedBox(height: 5), const SizedBox(height: 5),
Text( Text(
title, title,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
), ),
], ],

View File

@ -0,0 +1,90 @@
import 'package:flutter/material.dart';
import 'detail_penyakit_page.dart';
class PenyakitPage extends StatelessWidget {
final List<Map<String, String>> 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",
},
];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF9DC08D),
appBar: AppBar(
backgroundColor: Color(0xFF9DC08D),
title: Align(
alignment: Alignment.topCenter,
child: Padding(
padding: const EdgeInsets.only(right: 30),
child: Text(
"Penyakit",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.white),
),
),
),
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.white),
onPressed: () => Navigator.of(context).pop(),
),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: [
SizedBox(height: 16),
Expanded(
child: ListView.builder(
itemCount: penyakitList.length,
itemBuilder: (context, index) {
final diagnosa = penyakitList[index];
return Card(
elevation: 4,
margin: const EdgeInsets.symmetric(vertical: 8),
child: ListTile(
title: Text(
diagnosa["nama penyakit"] ?? "Tidak ada data",
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(diagnosa["deskripsi"] ?? "Deskripsi tidak tersedia"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailPenyakitPage(detailPenyakit: diagnosa),
),
);
},
),
);
},
),
),
],
),
),
);
}
}

View File

@ -1,7 +1,16 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:frontend/api_services/api_services.dart';
// Halaman Pendaftaran // Halaman Pendaftaran
class RegisterPage extends StatelessWidget { class RegisterPage extends StatelessWidget {
final TextEditingController nameController = TextEditingController();
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
final TextEditingController alamatController = TextEditingController();
final TextEditingController nomorHpController = TextEditingController();
final ApiService apiService = ApiService();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -30,6 +39,7 @@ class RegisterPage extends StatelessWidget {
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
), ),
controller: nameController,
), ),
SizedBox(height: 20), SizedBox(height: 20),
TextField( TextField(
@ -39,6 +49,7 @@ class RegisterPage extends StatelessWidget {
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
), ),
controller: emailController,
), ),
SizedBox(height: 20), SizedBox(height: 20),
TextField( TextField(
@ -49,6 +60,7 @@ class RegisterPage extends StatelessWidget {
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
), ),
controller: passwordController,
), ),
SizedBox(height: 20), SizedBox(height: 20),
TextField( TextField(
@ -58,6 +70,7 @@ class RegisterPage extends StatelessWidget {
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
), ),
controller: emailController,
), ),
SizedBox(height: 20), SizedBox(height: 20),
TextField( TextField(
@ -67,6 +80,7 @@ class RegisterPage extends StatelessWidget {
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
), ),
controller: alamatController,
), ),
SizedBox(height: 20), SizedBox(height: 20),
TextField( TextField(
@ -76,6 +90,7 @@ class RegisterPage extends StatelessWidget {
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
), ),
controller: nomorHpController,
), ),
SizedBox(height: 20), SizedBox(height: 20),
SizedBox( SizedBox(
@ -88,8 +103,24 @@ class RegisterPage extends StatelessWidget {
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
), ),
onPressed: () { onPressed: () async {
// Logic Pendaftaran try {
await apiService.registerUser(
name: nameController.text,
email: emailController.text,
password: passwordController.text,
alamat: alamatController.text,
nomorTelepon: nomorHpController.text,
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Registrasi berhasil!')),
);
Navigator.pop(context); // kembali ke login page
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Registrasi gagal: $e')),
);
}
}, },
child: Text( child: Text(
'Daftar', 'Daftar',
@ -110,4 +141,4 @@ class RegisterPage extends StatelessWidget {
), ),
); );
} }
} }

View File

@ -7,7 +7,8 @@ class RiwayatDiagnosaPage extends StatelessWidget {
"deskripsi": "Penyakit yang umum pada bayam.", "deskripsi": "Penyakit yang umum pada bayam.",
"penyakit": "Karat Putih", "penyakit": "Karat Putih",
"hama": "Tidak ada hama spesifik", "hama": "Tidak ada hama spesifik",
"penanganan": "Gunakan fungisida sesuai anjuran dan potong daun yang terinfeksi." "penanganan": "Gunakan fungisida sesuai anjuran dan potong daun yang terinfeksi.",
"gambar": "assets/images/karat putih.jpeg",
}, },
{ {
"nama": "Virus Keriting", "nama": "Virus Keriting",
@ -76,11 +77,12 @@ class RiwayatDiagnosaPage extends StatelessWidget {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => HasilDiagnosaPage( builder: (context) => DetailRiwayatPage(
hasilDiagnosa: { detailRiwayat: {
"penyakit": diagnosa["penyakit"] ?? "", "penyakit": diagnosa["penyakit"] ?? "",
"hama": diagnosa["hama"] ?? "", "hama": diagnosa["hama"] ?? "",
"penanganan": diagnosa["penanganan"] ?? "", "penanganan": diagnosa["penanganan"] ?? "",
"gambar": diagnosa["gambar"] ?? "",
}, },
), ),
), ),
@ -98,23 +100,24 @@ class RiwayatDiagnosaPage extends StatelessWidget {
} }
} }
class HasilDiagnosaPage extends StatelessWidget { class DetailRiwayatPage extends StatelessWidget {
final Map<String, String> hasilDiagnosa; final Map<String, String> detailRiwayat;
HasilDiagnosaPage({required this.hasilDiagnosa}); DetailRiwayatPage({required this.detailRiwayat});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: Color(0xFF9DC08D),
appBar: AppBar( appBar: AppBar(
backgroundColor: Color(0xFF9DC08D), backgroundColor: Color(0xFF9DC08D),
title: Align( title: Align(
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
child: Padding( child: Padding(
padding: const EdgeInsets.only(right: 30), // Geser ke kiri padding: const EdgeInsets.only(right: 30),
child: Text( child: Text(
"Hasil Diagnosa", "Hasil Diagnosa",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.white), style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.white),
), ),
), ),
), ),
@ -123,53 +126,67 @@ class HasilDiagnosaPage extends StatelessWidget {
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
), ),
), ),
body: Container( body: SingleChildScrollView(
color: Color(0xFF9DC08D), padding: const EdgeInsets.all(16),
child: Padding( child: Column(
padding: const EdgeInsets.all(16.0), children: [
child: Center( if (detailRiwayat['gambar'] != null)
child: Card( ClipRRect(
elevation: 6,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
child: Image.asset(
detailRiwayat['gambar']!,
height: 200,
width: 200,
fit: BoxFit.cover,
),
), ),
child: Container( SizedBox(height: 16),
width: 400, Card(
height: 300, // Mengatur ukuran card elevation: 6,
padding: EdgeInsets.all(16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Nama Penyakit: ${hasilDiagnosa['penyakit']}", "Nama Penyakit: ${detailRiwayat['penyakit']}",
style: TextStyle( style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black),
fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black),
), ),
SizedBox(height: 10), SizedBox(height: 16),
Text( Text(
"Nama Hama: ${hasilDiagnosa['hama']}", "Nama Hama: ${detailRiwayat['hama']}",
style: TextStyle( style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black),
fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black),
), ),
SizedBox(height: 20), ],
),
),
),
SizedBox(height: 16),
Card(
elevation: 6,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( Text(
"Cara Penanganan:", "Cara Penanganan:",
style: TextStyle( style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black),
fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black),
), ),
SizedBox(height: 10), SizedBox(height: 10),
Text( Text(
hasilDiagnosa['penanganan'] ?? "Data tidak tersedia", detailRiwayat['penanganan'] ?? "Data tidak tersedia",
style: TextStyle(fontSize: 16, color: Colors.black), style: TextStyle(fontSize: 16, color: Colors.black),
), ),
], ],
), ),
), ),
), ),
), ],
), ),
), ),
); );
} }
} }

View File

@ -62,6 +62,13 @@ flutter:
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
assets: assets:
- assets/images/bayam.png - assets/images/bayam.png
- assets/images/Businessman.png
- assets/images/Literature.png
- assets/images/Order History.png
- assets/images/Test Account.png
- assets/images/Virus.png
- assets/images/Caterpillar.png
- assets/images/karat putih.jpeg
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images # https://flutter.dev/to/resolution-aware-images

View File

@ -1,30 +0,0 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:frontend/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}