MIF_E31230549/lib/pages/edit_petugas.dart

395 lines
14 KiB
Dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
class EditPetugasPage extends StatefulWidget {
final Map<String, dynamic> data;
const EditPetugasPage({super.key, required this.data});
@override
State<EditPetugasPage> createState() => _EditPetugasPageState();
}
class _EditPetugasPageState extends State<EditPetugasPage> {
final _nama = TextEditingController();
final _email = TextEditingController();
final _noHp = TextEditingController();
String? _role;
String? _desaId;
String? _dusunId;
// State untuk menampung pesan error validasi
String? _errNama, _errEmail, _errNoHp;
bool _loading = false;
List<Map<String, dynamic>> desaList = [];
List<Map<String, dynamic>> dusunList = [];
final String url =
"http://ta.myhost.id/E31230549/mposyandu_api/petugas/update_petugas.php";
final String desaUrl = "http://ta.myhost.id/E31230549/mposyandu_api/desa/get_desa.php";
final String dusunUrl = "http://ta.myhost.id/E31230549/mposyandu_api/dusun/get_dusun.php";
@override
void initState() {
super.initState();
_nama.text = widget.data["nama"]?.toString() ?? "";
_email.text = widget.data["email"]?.toString() ?? "";
_noHp.text = widget.data["no_hp"]?.toString() ?? "";
_role = widget.data["role"]?.toString();
_desaId = (widget.data["desa_id"] == null ||
widget.data["desa_id"].toString() == "0")
? null
: widget.data["desa_id"].toString();
_dusunId = (widget.data["dusun_id"] == null ||
widget.data["dusun_id"].toString() == "0")
? null
: widget.data["dusun_id"].toString();
_initData();
}
Future<void> _initData() async {
setState(() => _loading = true);
await _fetchDesa();
if (_desaId != null && _desaId != "") {
await _fetchDusun(_desaId!);
}
setState(() => _loading = false);
}
Future<void> _fetchDesa() async {
try {
final res = await http.get(Uri.parse(desaUrl));
if (res.statusCode == 200) {
final jsonData = json.decode(res.body);
if (jsonData["success"] == true) {
setState(() {
desaList = List<Map<String, dynamic>>.from(jsonData["data"]);
});
}
}
} catch (e) {
debugPrint("ERROR DESA: $e");
}
}
Future<void> _fetchDusun(String dId) async {
try {
final res = await http.get(Uri.parse("$dusunUrl?desa_id=$dId"));
if (res.statusCode == 200) {
final jsonData = json.decode(res.body);
if (jsonData["success"] == true) {
setState(() {
dusunList = List<Map<String, dynamic>>.from(jsonData["data"]);
});
}
}
} catch (e) {
debugPrint("ERROR DUSUN: $e");
}
}
// Fungsi validasi sebelum melakukan update data
bool _isValid() {
setState(() {
// 1. Validasi Nama
_errNama = _nama.text.trim().isEmpty ? "Nama tidak boleh kosong" : null;
// 2. Validasi Email
final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (_email.text.trim().isEmpty) {
_errEmail = "Email tidak boleh kosong";
} else if (!emailRegex.hasMatch(_email.text.trim())) {
_errEmail = "Format email salah (misal: nama@email.com)";
} else {
_errEmail = null;
}
// 3. Validasi No HP (Wajib berada di rentang 10 sampai 13 digit)
final noHpLength = _noHp.text.trim().length;
if (_noHp.text.trim().isEmpty) {
_errNoHp = "Nomor HP tidak boleh kosong";
} else if (noHpLength < 10 || noHpLength > 13) {
_errNoHp = "No HP harus berjumlah 10-13 digit";
} else {
_errNoHp = null;
}
});
return _errNama == null && _errEmail == null && _errNoHp == null;
}
Future<void> _update() async {
if (!_isValid()) return; // Batalkan proses jika input tidak lolos validasi
setState(() => _loading = true);
try {
final res = await http.post(
Uri.parse(url),
body: {
"id": widget.data["id"].toString(),
"nama": _nama.text.trim(),
"email": _email.text.trim(),
"no_hp": _noHp.text.trim(),
"role": _role ?? "",
"desa_id": _role == "kader" ? (_desaId ?? "") : "",
"dusun_id": _role == "kader" ? (_dusunId ?? "") : "",
},
);
final data = json.decode(res.body);
if (data["success"] == true) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Berhasil update",
style: GoogleFonts.poppins(fontSize: 12))),
);
Navigator.pop(context, true);
}
} else {
_showSimpleError(data["message"] ?? "Gagal update data");
}
} catch (e) {
debugPrint("Update Error: $e");
_showSimpleError("Terjadi kesalahan jaringan");
} finally {
if (mounted) setState(() => _loading = false);
}
}
void _showSimpleError(String msg) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(msg, style: GoogleFonts.poppins(fontSize: 12))));
}
@override
Widget build(BuildContext context) {
final bool isKader = _role == "kader";
final bool isAdmin = widget.data["role"] == "admin";
return Scaffold(
backgroundColor: const Color(0xfff5f6fa),
appBar: AppBar(
leading: const BackButton(color: Colors.white),
title: Text("",
style: GoogleFonts.poppins(color: Colors.white, fontSize: 16)),
backgroundColor: Colors.blue,
),
body: Center(
child: SingleChildScrollView(
child: Column(
children: [
Text(
"Edit Data Petugas",
style: GoogleFonts.poppins(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
const SizedBox(height: 16),
Container(
width: 500,
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: const [
BoxShadow(color: Colors.black12, blurRadius: 12)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_label("Nama Lengkap"),
_input(_nama, error: _errNama),
const SizedBox(height: 16),
_label("Email"),
_input(_email, error: _errEmail, type: TextInputType.emailAddress),
const SizedBox(height: 16),
_label("Role"),
_dropdown(
value: _role,
items: const [
{"id": "admin", "nama": "admin"},
{"id": "kader", "nama": "kader"},
{"id": "bidan", "nama": "bidan"}
],
onChanged: isAdmin
? null
: (v) {
setState(() {
_role = v;
if (_role != "kader") {
_desaId = null;
_dusunId = null;
}
});
},
disabled: isAdmin,
),
const SizedBox(height: 16),
_label("Desa"),
_dropdown(
value: isKader ? _desaId : null,
items: desaList
.map((e) => {
"id": e["id"].toString(),
"nama": e["nama_desa"]
})
.toList(),
onChanged: isKader
? (v) {
setState(() {
_desaId = v;
_dusunId = null;
dusunList = [];
});
if (v != null) _fetchDusun(v);
}
: null,
disabled: !isKader,
hint: _loading ? "Memuat desa..." : "Pilih Desa",
),
const SizedBox(height: 16),
_label("Dusun"),
_dropdown(
value: isKader ? _dusunId : null,
items: dusunList
.map((e) => {
"id": e["id"].toString(),
"nama": e["nama_dusun"]
})
.toList(),
onChanged:
isKader ? (v) => setState(() => _dusunId = v) : null,
disabled: !isKader,
hint: _loading ? "Memuat dusun..." : "Pilih Dusun",
),
const SizedBox(height: 16),
_label("No HP"),
_input(_noHp,
error: _errNoHp,
type: TextInputType.number,
limit: 13,
isNumberOnly: true),
const SizedBox(height: 32),
SizedBox(
width: double.infinity,
child: OutlinedButton(
onPressed: _loading ? null : _update,
style: OutlinedButton.styleFrom(
backgroundColor: Colors.white,
side:
const BorderSide(color: Colors.orange, width: 2),
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
child: _loading
? const SizedBox(
height: 15,
width: 15,
child: CircularProgressIndicator(
strokeWidth: 2, color: Colors.orange))
: Text("Simpan Perubahan",
style: GoogleFonts.poppins(
color: Colors.orange,
fontWeight: FontWeight.bold,
fontSize: 12)),
),
),
],
),
),
const SizedBox(height: 20),
],
),
),
),
);
}
Widget _label(String t) => Padding(
padding: const EdgeInsets.only(bottom: 6),
child: Text(t,
style:
GoogleFonts.poppins(fontWeight: FontWeight.w600, fontSize: 12)),
);
Widget _input(
TextEditingController c, {
String? error,
TextInputType type = TextInputType.text,
int? limit,
bool isNumberOnly = false,
}) =>
TextField(
controller: c,
maxLength: limit,
keyboardType: type,
inputFormatters: isNumberOnly ? [FilteringTextInputFormatter.digitsOnly] : null,
style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration(
errorText: error,
errorStyle: GoogleFonts.poppins(color: Colors.red, fontSize: 10),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Colors.grey)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Colors.orange, width: 2)),
).copyWith(counterText: ""),
);
Widget _dropdown({
required String? value,
required List<Map<String, dynamic>> items,
required Function(String?)? onChanged,
bool disabled = false,
String hint = "Pilih",
}) {
String? validValue;
if (value != null) {
for (var item in items) {
if (item["id"].toString() == value.toString()) {
validValue = item["id"].toString();
break;
}
}
}
return DropdownButtonFormField<String>(
isExpanded: true,
value: validValue,
hint: Text(hint, style: GoogleFonts.poppins(fontSize: 12)),
style: GoogleFonts.poppins(
fontSize: 12, color: disabled ? Colors.grey : Colors.black),
decoration: InputDecoration(
filled: disabled,
fillColor: disabled ? Colors.grey[200] : Colors.white,
contentPadding: const EdgeInsets.symmetric(horizontal: 12),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
),
items: items.map((e) {
return DropdownMenuItem<String>(
value: e["id"].toString(),
child: Text(e["nama"].toString(), overflow: TextOverflow.ellipsis),
);
}).toList(),
onChanged: onChanged,
);
}
}