TIF_NGANJUK_E41210753/lib/app/views/teacher_page.dart

562 lines
22 KiB
Dart

import 'package:flutter/material.dart';
import 'package:forward_chaining_man_app/app/views/dashboard_teacher.dart';
import 'package:forward_chaining_man_app/app/views/page_login.dart';
import 'package:forward_chaining_man_app/app/views/page_teacher_register.dart';
import 'package:get/get.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:shared_preferences/shared_preferences.dart';
// Import your teacher dashboard or main page
class TeacherLoginController extends GetxController {
final FirebaseAuth _auth = FirebaseAuth.instance;
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
var isLoading = false.obs;
var errorMessage = ''.obs;
// Text controllers for login form
final emailController = TextEditingController();
final passwordController = TextEditingController();
@override
void onInit() {
super.onInit();
checkExistingSession();
}
@override
void onClose() {
emailController.dispose();
passwordController.dispose();
super.onClose();
}
// Check if user is already logged in
Future<void> checkExistingSession() async {
try {
isLoading.value = true;
// Check if we have a stored user_uid and role
final prefs = await SharedPreferences.getInstance();
final storedUid = prefs.getString('user_uid');
final storedRole = prefs.getString('user_role');
if (storedUid != null && storedRole == 'teacher') {
// Verify if user exists in Firebase Auth
User? currentUser = _auth.currentUser;
if (currentUser != null) {
// Check if user data exists in Firestore
final docSnapshot = await _firestore
.collection('teachers')
.doc(currentUser.uid)
.get();
if (docSnapshot.exists) {
// Navigate to teacher dashboard
Get.offAll(() => const TeacherDashboardPage());
return;
}
}
}
} catch (e) {
print('Error checking session: $e');
} finally {
isLoading.value = false;
}
}
// Sign in with email and password
Future<void> signInWithEmailPassword() async {
try {
isLoading.value = true;
errorMessage.value = '';
if (emailController.text.trim().isEmpty ||
passwordController.text.isEmpty) {
errorMessage.value = 'Email dan password tidak boleh kosong';
return;
}
final userCredential = await _auth.signInWithEmailAndPassword(
email: emailController.text.trim(),
password: passwordController.text,
);
if (userCredential.user != null) {
// Check if this user is a teacher
final teacherDoc = await _firestore
.collection('teachers')
.doc(userCredential.user!.uid)
.get();
if (!teacherDoc.exists) {
// This user is not registered as a teacher
errorMessage.value = 'Akun ini tidak terdaftar sebagai guru';
await _auth.signOut();
return;
}
// Save login session
await saveLoginSession(userCredential.user!.uid, teacherDoc);
// Navigate to teacher dashboard
Get.offAll(() => const TeacherDashboardPage());
}
} on FirebaseAuthException catch (e) {
switch (e.code) {
case 'user-not-found':
errorMessage.value = 'Email tidak terdaftar';
break;
case 'wrong-password':
errorMessage.value = 'Password salah';
break;
case 'invalid-email':
errorMessage.value = 'Format email tidak valid';
break;
default:
errorMessage.value = 'Gagal masuk: ${e.message}';
}
} catch (e) {
errorMessage.value = 'Terjadi kesalahan: ${e.toString()}';
} finally {
isLoading.value = false;
}
}
// Save login session to shared preferences
Future<void> saveLoginSession(String uid, DocumentSnapshot teacherDoc) async {
try {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('user_uid', uid);
await prefs.setBool('is_logged_in', true);
await prefs.setString('user_role', 'teacher');
// Extract schoolId from teacher document if it exists
final teacherData = teacherDoc.data() as Map<String, dynamic>?;
if (teacherData != null && teacherData.containsKey('schoolId')) {
final schoolId = teacherData['schoolId'];
if (schoolId != null && schoolId is String && schoolId.isNotEmpty) {
// Save the school ID to shared preferences
await prefs.setString('school_id', schoolId);
}
}
// Update last login timestamp
await _firestore.collection('teachers').doc(uid).update({
'lastLogin': FieldValue.serverTimestamp(),
});
} catch (e) {
print('Error saving session: $e');
}
}
}
class TeacherLoginPage extends StatelessWidget {
const TeacherLoginPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final controller = Get.put(TeacherLoginController());
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Colors.blue.shade800,
Colors.indigo.shade900,
],
),
),
child: SafeArea(
child: Center(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Back button at the top
Align(
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsets.only(top: 8, bottom: 20),
child: TextButton(
onPressed: () {
Get.back();
},
style: TextButton.styleFrom(
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(
vertical: 8, horizontal: 16),
backgroundColor: Colors.white.withOpacity(0.2),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: const [
Icon(Icons.arrow_back, size: 18),
SizedBox(width: 8),
Text('Kembali ke Pemilihan Peran'),
],
),
),
),
),
// App Logo with Hero animation
Hero(
tag: 'app_logo',
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(25),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 15,
offset: const Offset(0, 8),
),
],
),
child: Center(
child: Icon(
Icons.psychology,
size: 60,
color: Colors.blue
.shade700, // Mengubah warna ikon sesuai dengan skema warna biru
),
),
),
),
const SizedBox(height: 20),
// App Title
const Text(
'EduGuide',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.white,
letterSpacing: 1.2,
),
),
const SizedBox(height: 8),
// Teacher Login Subtitle
Container(
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: const Text(
'Portal Guru',
style: TextStyle(
fontSize: 16,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
),
const SizedBox(height: 30),
// Login Card
Obx(() => Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header
Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(12),
),
child: Icon(
Icons.person_pin,
color: Colors.blue.shade400,
size: 24,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'Masuk sebagai Guru',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.blue.shade800,
),
),
const SizedBox(height: 4),
const Text(
'Masukkan kredensial Anda',
style: TextStyle(
fontSize: 14,
color: Colors.black54,
),
),
],
),
),
],
),
const SizedBox(height: 30),
// Email field
CustomTextField(
controller: controller.emailController,
label: 'Email',
prefixIcon: Icons.email_outlined,
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 20),
// Password field
CustomTextField(
controller: controller.passwordController,
label: 'Password',
prefixIcon: Icons.lock_outline,
isPassword: true,
),
const SizedBox(height: 12),
// // Forgot password
// Align(
// alignment: Alignment.centerRight,
// child: TextButton(
// onPressed: () {
// // Handle forgot password
// Get.snackbar(
// 'Lupa Password',
// 'Silakan hubungi administrator untuk reset password',
// backgroundColor: Colors.blue.shade50,
// colorText: Colors.blue.shade800,
// snackPosition: SnackPosition.BOTTOM,
// margin: const EdgeInsets.all(16),
// );
// },
// style: TextButton.styleFrom(
// padding: EdgeInsets.zero,
// minimumSize: const Size(50, 30),
// tapTargetSize:
// MaterialTapTargetSize.shrinkWrap,
// ),
// child: Text(
// 'Lupa Password?',
// style: TextStyle(
// color: Colors.blue.shade700,
// fontWeight: FontWeight.w500,
// fontSize: 14,
// ),
// ),
// ),
// ),
const SizedBox(height: 24),
// Error message
Obx(() => controller
.errorMessage.value.isNotEmpty
? Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.red.shade50,
borderRadius:
BorderRadius.circular(12),
border: Border.all(
color: Colors.red.shade200,
),
),
child: Row(
children: [
Icon(Icons.error_outline,
color: Colors.red.shade800),
const SizedBox(width: 8),
Expanded(
child: Text(
controller.errorMessage.value,
style: TextStyle(
color: Colors.red.shade700,
fontSize: 14,
),
),
),
],
),
)
: const SizedBox.shrink()),
const SizedBox(height: 24),
// Login and Register buttons
Column(
children: [
// Login button
_buildPrimaryButton(
label: 'Masuk',
icon: Icons.login,
isLoading: controller.isLoading.value,
onPressed:
controller.signInWithEmailPassword,
primaryColor: Colors.blue.shade700,
isOutlined: false,
),
const SizedBox(height: 16),
// // Register button
// _buildPrimaryButton(
// label: 'Daftar Akun Baru',
// icon: Icons.person_add,
// isLoading: false,
// onPressed: () {
// Get.to(
// () => const TeacherRegisterPage());
// },
// primaryColor: Colors.blue.shade700,
// isOutlined: true,
// ),
],
),
],
),
),
)),
const SizedBox(height: 24),
],
),
),
),
),
),
),
);
}
// Helper method to build text fields
Widget _buildTextField({
required TextEditingController controller,
required String label,
required IconData prefixIcon,
bool isPassword = false,
TextInputType keyboardType = TextInputType.text,
}) {
return TextField(
controller: controller,
obscureText: isPassword,
keyboardType: keyboardType,
style: const TextStyle(fontSize: 16),
decoration: InputDecoration(
labelText: label,
labelStyle: TextStyle(color: Colors.grey.shade600),
prefixIcon: Icon(prefixIcon, color: Colors.blue.shade300),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: BorderSide(color: Colors.grey.shade300),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: BorderSide(color: Colors.blue.shade500, width: 1.5),
),
filled: true,
fillColor: Colors.grey.shade50,
contentPadding: const EdgeInsets.symmetric(vertical: 16),
),
);
}
// Enhanced helper method to build primary/secondary buttons
Widget _buildPrimaryButton({
required String label,
required IconData icon,
required VoidCallback onPressed,
required bool isLoading,
required Color primaryColor,
required bool isOutlined,
}) {
return ElevatedButton(
onPressed: isLoading ? null : onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: isOutlined ? Colors.white : primaryColor,
foregroundColor: isOutlined ? primaryColor : Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
minimumSize: const Size(double.infinity, 56),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
side: isOutlined
? BorderSide(color: primaryColor, width: 1.5)
: BorderSide.none,
),
elevation: isOutlined ? 0 : 2,
disabledBackgroundColor:
isOutlined ? Colors.grey.shade100 : primaryColor.withOpacity(0.6),
disabledForegroundColor: isOutlined ? Colors.grey : Colors.white70,
),
child: isLoading
? const SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2.5,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 20),
const SizedBox(width: 10),
Text(
label,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
letterSpacing: 0.5,
),
),
],
),
);
}
}