You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
458 lines
14 KiB
458 lines
14 KiB
import 'package:flutter/material.dart'; |
|
import 'package:provider/provider.dart'; |
|
import 'package:shared_preferences/shared_preferences.dart'; |
|
import 'package:flutter/services.dart'; |
|
|
|
import 'package:siopas/theme.dart'; |
|
|
|
import '../../models/ip_domain_model.dart'; |
|
import '../../models/user_model.dart'; |
|
import '../../providers/auth_provider.dart'; |
|
import '../../widget/loading_button.dart'; |
|
import 'controller/loginController.dart'; |
|
|
|
class SignInPage extends StatefulWidget { |
|
@override |
|
State<SignInPage> createState() => _SignInPageState(); |
|
} |
|
|
|
class _SignInPageState extends State<SignInPage> { |
|
TextEditingController emailController = TextEditingController(text: ''); |
|
TextEditingController passwordController = TextEditingController(text: ''); |
|
bool isLoading = false; |
|
bool _isPasswordVisible = false; |
|
|
|
// Create controllers for IP Address and Port |
|
TextEditingController ipAddressController = TextEditingController(); |
|
TextEditingController portController = TextEditingController(); |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
final bool isSmallScreen = MediaQuery.of(context).size.width < 600; |
|
AuthProvider authProvider = Provider.of<AuthProvider>(context); |
|
UserModel user = authProvider.user; |
|
|
|
// Function to show the SettingsModal |
|
void _showSettingsModal(BuildContext context) { |
|
showDialog( |
|
context: context, |
|
builder: (BuildContext context) { |
|
return SettingsModal(); |
|
}, |
|
); |
|
} |
|
|
|
return Scaffold( |
|
body: Center( |
|
child: isSmallScreen |
|
? SingleChildScrollView( |
|
reverse: true, |
|
scrollDirection: Axis.vertical, // Tambahkan ini |
|
child: Column( |
|
mainAxisSize: MainAxisSize.min, |
|
children: const [ |
|
_Logo(), |
|
_FormContent(), |
|
], |
|
), |
|
) |
|
: Container( |
|
padding: const EdgeInsets.all(30.0), |
|
constraints: const BoxConstraints(maxWidth: 800), |
|
child: Row( |
|
children: [ |
|
Expanded(child: _Logo()), |
|
Expanded( |
|
child: Center(child: _FormContent()), |
|
), |
|
], |
|
), |
|
), |
|
), |
|
bottomNavigationBar: BottomAppBar( |
|
color: Colors.grey[50], |
|
elevation: 0, |
|
child: Row( |
|
mainAxisAlignment: MainAxisAlignment.center, |
|
children: [ |
|
TextButton( |
|
onPressed: () { |
|
_showSettingsModal(context); // Show the modal |
|
}, |
|
child: Text('Pengaturan'), |
|
), |
|
], |
|
), |
|
), |
|
); |
|
} |
|
} |
|
|
|
class _Logo extends StatelessWidget { |
|
const _Logo({Key? key}) : super(key: key); |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
final bool isSmallScreen = MediaQuery.of(context).size.width < 600; |
|
|
|
return Column( |
|
mainAxisSize: MainAxisSize.min, |
|
children: [ |
|
Container( |
|
// margin: EdgeInsets.only(top: 30), |
|
child: Image.asset( |
|
'assets/img/siopas_apps.png', |
|
height: isSmallScreen ? 135 : 200, |
|
), |
|
), |
|
Padding( |
|
padding: const EdgeInsets.all(16.0), |
|
child: Column( |
|
children: [ |
|
Text( |
|
"Siopas ISTW", |
|
textAlign: TextAlign.center, |
|
style: TextStyle( |
|
fontSize: isSmallScreen ? 18 : 22, |
|
fontWeight: semiBold, |
|
color: Colors.black, |
|
), |
|
), |
|
SizedBox( |
|
height: |
|
8.0), // Berikan sedikit jarak antara teks "Siopas ISTW" dan versi |
|
Text( |
|
"Version 1.0.0", // Gantilah dengan versi aktual |
|
textAlign: TextAlign.center, |
|
style: TextStyle( |
|
fontSize: isSmallScreen ? 14 : 16, |
|
color: Colors.grey, |
|
), |
|
), |
|
], |
|
), |
|
), |
|
], |
|
); |
|
} |
|
} |
|
|
|
class _FormContent extends StatefulWidget { |
|
const _FormContent({Key? key}) : super(key: key); |
|
|
|
@override |
|
State<_FormContent> createState() => __FormContentState(); |
|
} |
|
|
|
class __FormContentState extends State<_FormContent> { |
|
TextEditingController emailController = TextEditingController(text: ''); |
|
TextEditingController passwordController = TextEditingController(text: ''); |
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); |
|
|
|
bool isLoading = false; |
|
|
|
bool _isPasswordVisible = false; |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
AuthProvider authProvider = Provider.of<AuthProvider>(context); |
|
UserModel user = authProvider.user; |
|
|
|
handleSignIn() async { |
|
setState(() { |
|
isLoading = true; |
|
}); |
|
|
|
if (await authProvider.login( |
|
email: emailController.text, |
|
password: passwordController.text, |
|
)) { |
|
UserModel user = authProvider.user; |
|
// Simpan token pengguna ke SharedPreferences |
|
SharedPreferences prefs = await SharedPreferences.getInstance(); |
|
prefs.setString('token', user.token!); // Pastikan user.token tidak null |
|
print('token dapat login: ${user.token}'); |
|
|
|
if (user != null) { |
|
String roleName = user.role!.name!.toLowerCase(); |
|
if (roleName == 'operator' || roleName == 'Operator') { |
|
print('Berhasil login HALAMAN USER'); |
|
if (context != null) { |
|
Navigator.pushReplacementNamed(context, '/home'); |
|
} else { |
|
print('Context is null'); |
|
} |
|
} else { |
|
print('Tidak ada informasi peran (roles) yang tersedia'); |
|
} |
|
} else { |
|
print('User tidak valid'); |
|
} |
|
} else { |
|
ScaffoldMessenger.of(context).showSnackBar( |
|
SnackBar( |
|
backgroundColor: alertColor, |
|
content: Row( |
|
mainAxisAlignment: MainAxisAlignment.center, |
|
children: [ |
|
Icon( |
|
Icons.warning, |
|
color: Colors.white, |
|
), |
|
SizedBox(width: 8), // Jarak antara ikon dan teks |
|
Text( |
|
'Gagal Login, Email dan Password anda salah', |
|
textAlign: TextAlign.center, |
|
), |
|
], |
|
), |
|
), |
|
); |
|
Future.delayed(Duration(seconds: 3), () { |
|
ScaffoldMessenger.of(context).hideCurrentSnackBar(); |
|
}); |
|
} |
|
|
|
setState(() { |
|
isLoading = false; |
|
}); |
|
} |
|
|
|
return Container( |
|
constraints: const BoxConstraints(maxWidth: 300), |
|
child: Form( |
|
key: _formKey, |
|
child: Column( |
|
mainAxisSize: MainAxisSize.min, |
|
// mainAxisAlignment: MainAxisAlignment.start, |
|
crossAxisAlignment: CrossAxisAlignment.start, |
|
children: [ |
|
_gap(), |
|
TextFormField( |
|
validator: (value) { |
|
// add email validation |
|
if (value == null || value.isEmpty) { |
|
return 'Silakan masukkan email anda'; |
|
} |
|
|
|
bool emailValid = RegExp( |
|
r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+") |
|
.hasMatch(value); |
|
if (!emailValid) { |
|
return 'Tolong masukkan email yang benar'; |
|
} |
|
|
|
return null; |
|
}, |
|
controller: emailController, |
|
decoration: const InputDecoration( |
|
hintText: 'Masukkan email Anda', |
|
prefixIcon: Icon(Icons.email_outlined), |
|
border: OutlineInputBorder(), |
|
), |
|
keyboardType: TextInputType.emailAddress, |
|
), |
|
_gap(), |
|
TextFormField( |
|
validator: (value) { |
|
if (value == null || value.isEmpty) { |
|
return 'Silakan masukkan password anda'; |
|
} |
|
|
|
if (value.length < 6) { |
|
return 'Kata sandi minimal harus 6 karakter'; |
|
} |
|
return null; |
|
}, |
|
obscureText: !_isPasswordVisible, |
|
controller: passwordController, |
|
decoration: InputDecoration( |
|
hintText: 'Masukkan kata sandi Anda', |
|
prefixIcon: const Icon(Icons.lock_outline_rounded), |
|
border: const OutlineInputBorder(), |
|
suffixIcon: IconButton( |
|
icon: Icon(_isPasswordVisible |
|
? Icons.visibility_off |
|
: Icons.visibility), |
|
onPressed: () { |
|
setState(() { |
|
_isPasswordVisible = !_isPasswordVisible; |
|
}); |
|
}, |
|
)), |
|
), |
|
_gap(), |
|
SizedBox( |
|
width: double.infinity, |
|
child: ElevatedButton( |
|
style: ElevatedButton.styleFrom( |
|
shape: RoundedRectangleBorder( |
|
borderRadius: BorderRadius.circular(4), |
|
), |
|
primary: Colors.indigo, // Ganti warna tombol menjadi indigo |
|
alignment: Alignment.center, // Menyusun isi tombol ke tengah |
|
), |
|
child: Padding( |
|
padding: const EdgeInsets.all(10.0), |
|
child: Row( |
|
mainAxisAlignment: |
|
MainAxisAlignment.center, // Tengahkan teks 'Login' |
|
children: [ |
|
Icon(Icons.login), |
|
SizedBox( |
|
width: |
|
8.0), // Berikan sedikit jarak antara ikon dan teks |
|
Text( |
|
'Masuk', |
|
style: TextStyle( |
|
fontSize: 16, fontWeight: FontWeight.bold), |
|
), |
|
], |
|
), |
|
), |
|
onPressed: () { |
|
if (_formKey.currentState!.validate()) { |
|
if (emailController.text.isEmpty || |
|
passwordController.text.isEmpty) { |
|
ScaffoldMessenger.of(context).showSnackBar( |
|
SnackBar( |
|
backgroundColor: alertColor, |
|
content: Row( |
|
children: [ |
|
Icon( |
|
Icons.warning, |
|
color: Colors.white, |
|
), |
|
SizedBox(width: 8), // Jarak antara ikon dan teks |
|
Text( |
|
'Email dan Password tidak boleh kosong', |
|
textAlign: TextAlign.center, |
|
), |
|
], |
|
), |
|
), |
|
); |
|
|
|
Future.delayed(Duration(seconds: 2), () { |
|
ScaffoldMessenger.of(context).hideCurrentSnackBar(); |
|
}); |
|
} else { |
|
handleSignIn(); |
|
} |
|
} |
|
}, |
|
), |
|
), |
|
], |
|
), |
|
), |
|
); |
|
} |
|
|
|
Widget _gap() => const SizedBox(height: 14); |
|
} |
|
|
|
class SettingsModal extends StatefulWidget { |
|
const SettingsModal({Key? key}) : super(key: key); |
|
|
|
@override |
|
State<SettingsModal> createState() => _SettingsModalState(); |
|
} |
|
|
|
class _SettingsModalState extends State<SettingsModal> { |
|
TextEditingController ipDomainController = TextEditingController(); |
|
List<dynamic>? getIpDomainList; |
|
bool loading = true; |
|
|
|
@override |
|
void initState() { |
|
super.initState(); |
|
ipDomainList(); |
|
} |
|
|
|
Future ipDomainList() async { |
|
await ControllerLogin().fetchData().then((value) { |
|
setState(() { |
|
getIpDomainList = value; |
|
loading = false; |
|
|
|
// Set nilai ipDomainController dengan ID terbaru jika ada data |
|
if (getIpDomainList != null && getIpDomainList!.isNotEmpty) { |
|
ipDomainController.text = |
|
getIpDomainList![0]['ipOrDomain'].toString(); |
|
} |
|
}); |
|
}); |
|
} |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
return AlertDialog( |
|
title: Row( |
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
children: [ |
|
Text('Pengaturan'), |
|
GestureDetector( |
|
onTap: () { |
|
Navigator.of(context).pop(); // Close the modal |
|
}, |
|
child: Icon(Icons.close), |
|
), |
|
], |
|
), |
|
actions: [ |
|
ElevatedButton( |
|
onPressed: () async { |
|
IpDomain ipDomainModel = IpDomain( |
|
id: null, |
|
ipOrDomain: ipDomainController.text, |
|
); |
|
|
|
// Panggil fungsi deleteAllData untuk menghapus data sebelumnya |
|
await ControllerLogin().deleteAllData(); |
|
|
|
// Panggil fungsi addData untuk menyimpan data baru |
|
int result = await ControllerLogin().addData(ipDomainModel); |
|
|
|
if (result > 0) { |
|
print("Success"); |
|
|
|
// Gunakan setState untuk memicu pembaruan widget setelah data disimpan |
|
setState(() { |
|
loading = true; // Sinyal bahwa sedang memuat |
|
}); |
|
|
|
// Panggil fungsi ipDomainList untuk mengambil ulang data |
|
await ipDomainList(); |
|
|
|
// Setelah data diambil ulang, nonaktifkan loading |
|
setState(() { |
|
loading = false; |
|
}); |
|
|
|
// Setelah data disimpan dan diambil ulang, lakukan Navigator.pop |
|
Navigator.pop(context); // Kembali ke halaman sebelumnya |
|
} else { |
|
print("Failed"); |
|
} |
|
}, |
|
child: Text("Save"), |
|
), |
|
], |
|
content: Column( |
|
mainAxisSize: MainAxisSize.min, |
|
children: [ |
|
TextFormField( |
|
controller: ipDomainController, |
|
inputFormatters: [ |
|
FilteringTextInputFormatter.allow( |
|
RegExp(r'[a-zA-Z0-9@#$%^&*()_+={}|\[\]\\:;"<>,.?/~`-]'), |
|
), |
|
], |
|
decoration: InputDecoration(labelText: 'Domain atau IP:Port'), |
|
), |
|
], |
|
), |
|
); |
|
} |
|
}
|
|
|