bug fixes and more changes

This commit is contained in:
Raj.Ghag
2026-04-24 10:37:05 +05:30
parent 40e5c38365
commit 7e2a743227
15 changed files with 141 additions and 74 deletions

View File

@@ -13,12 +13,13 @@ class ForgotPasswordBloc extends Bloc<ForgotPasswordEvent, ForgotPasswordState>
super(const ForgotPasswordState()) {
on<ForgotPasswordSubmitted>(_onForgotPasswordSubmitted);
on<ForgotPasswordEmailErrorToggled>(_onEmailErrorToggled);
on<ForgotPasswordReset>(_onReset);
}
Future<void> _onForgotPasswordSubmitted(
ForgotPasswordSubmitted event,
Emitter<ForgotPasswordState> emit,
) async {
ForgotPasswordSubmitted event,
Emitter<ForgotPasswordState> emit,
) async {
emit(state.copyWith(status: ForgotPasswordStatus.loading, errorMessage: null));
try {
@@ -36,9 +37,16 @@ class ForgotPasswordBloc extends Bloc<ForgotPasswordEvent, ForgotPasswordState>
}
void _onEmailErrorToggled(
ForgotPasswordEmailErrorToggled event,
Emitter<ForgotPasswordState> emit,
) {
ForgotPasswordEmailErrorToggled event,
Emitter<ForgotPasswordState> emit,
) {
emit(state.copyWith(showEmailError: event.show));
}
Future<void> _onReset(
ForgotPasswordReset event,
Emitter<ForgotPasswordState> emit,
) async {
emit(const ForgotPasswordState());
}
}

View File

@@ -18,8 +18,16 @@ class ForgotPasswordSubmitted extends ForgotPasswordEvent {
class ForgotPasswordEmailErrorToggled extends ForgotPasswordEvent {
final bool show;
const ForgotPasswordEmailErrorToggled(this.show);
@override
List<Object?> get props => [show];
}
class ForgotPasswordReset extends ForgotPasswordEvent {
const ForgotPasswordReset();
@override
List<Object?> get props => [];
}

View File

@@ -16,14 +16,20 @@ class VerifyOtpBloc extends Bloc<VerifyOtpEvent, VerifyOtpState> {
}
void _onOtpChanged(OtpChanged event, Emitter<VerifyOtpState> emit) {
emit(state.copyWith(otp: event.otp));
emit(state.copyWith(
otp: event.otp,
status: VerifyOtpStatus.initial,
clearError: true,
));
}
Future<void> _onVerifyOtpSubmitted(
VerifyOtpSubmitted event,
Emitter<VerifyOtpState> emit,
) async {
emit(state.copyWith(status: VerifyOtpStatus.loading, errorMessage: null));
if (state.status == VerifyOtpStatus.loading) return;
emit(state.copyWith(status: VerifyOtpStatus.loading, clearError: true));
try {
await _otpRepository.verifyOtp(

View File

@@ -16,11 +16,12 @@ class VerifyOtpState extends Equatable {
VerifyOtpState copyWith({
VerifyOtpStatus? status,
String? errorMessage,
bool clearError = false,
String? otp,
}) {
return VerifyOtpState(
status: status ?? this.status,
errorMessage: errorMessage ?? this.errorMessage,
errorMessage: clearError ? null : (errorMessage ?? this.errorMessage),
otp: otp ?? this.otp,
);
}

View File

@@ -11,7 +11,7 @@ class ForgotPasswordRepository {
data: {"emailAddress": emailAddress},
);
} catch (e) {
throw Exception('Failed to send forgot password request: $e');
throw Exception('$e');
}
}
}

View File

@@ -17,7 +17,7 @@ class OtpRepository {
},
);
} catch (e) {
throw Exception('Failed to verify OTP: $e');
throw Exception('$e');
}
}
}

View File

@@ -20,6 +20,13 @@ class _ForgotPasswordPageState extends State<ForgotPasswordPage> {
final _emailController = TextEditingController();
final _emailFocusNode = FocusNode();
@override
void initState() {
super.initState();
// Reset the form state when entering the page
context.read<ForgotPasswordBloc>().add(ForgotPasswordReset());
}
@override
void dispose() {
_emailController.dispose();
@@ -28,7 +35,8 @@ class _ForgotPasswordPageState extends State<ForgotPasswordPage> {
}
bool _isEmailValid(String email) {
return RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+")
return RegExp(
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$')
.hasMatch(email);
}
@@ -100,7 +108,7 @@ class _ForgotPasswordPageState extends State<ForgotPasswordPage> {
),
SizedBox(height: 8.h),
Text(
"Partners App",
"Partner's App",
style: GoogleFonts.poppins(
color: AppColors.primaryRed,
fontSize: 20.sp,
@@ -124,11 +132,11 @@ class _ForgotPasswordPageState extends State<ForgotPasswordPage> {
),
SizedBox(height: 12.h),
Text(
"Enter your email to update your password\nand secure your account",
"Enter your email to update your password and secure your account",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
color: AppColors.textGrey,
fontSize: 16.sp,
fontSize: 15.sp,
height: 1.4,
),
),
@@ -147,6 +155,7 @@ class _ForgotPasswordPageState extends State<ForgotPasswordPage> {
textInputAction: TextInputAction.done,
readOnly: isLoading,
onChanged: (val) {
// Only validate and update error if user has attempted submission
if (state.showEmailError) {
context.read<ForgotPasswordBloc>().add(
ForgotPasswordEmailErrorToggled(!_isEmailValid(val.trim())),

View File

@@ -32,7 +32,8 @@ class _LoginPageState extends State<LoginPage> {
}
bool _isEmailValid(String email) {
return RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+")
return RegExp(
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$')
.hasMatch(email);
}
@@ -70,6 +71,12 @@ class _LoginPageState extends State<LoginPage> {
body: BlocConsumer<LoginBloc, LoginState>(
listener: (context, state) {
if (state.status == LoginStatus.success) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Login Successful."),
backgroundColor: Colors.green,
),
);
Navigator.pushNamedAndRemoveUntil(
context,
AppRouter.qrScanScreen,
@@ -292,4 +299,4 @@ class _LoginPageState extends State<LoginPage> {
),
);
}
}
}

View File

@@ -30,6 +30,7 @@ class OtpVerificationPage extends StatelessWidget {
arguments: email,
);
} else if (state.status == VerifyOtpStatus.failure) {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.errorMessage ?? "Verification failed"),
@@ -116,6 +117,7 @@ class OtpVerificationPage extends StatelessWidget {
);
},
onSubmit: (String verificationCode) {
if (isLoading) return;
context.read<VerifyOtpBloc>().add(
OtpChanged(otp: verificationCode),
);
@@ -136,7 +138,7 @@ class OtpVerificationPage extends StatelessWidget {
CustomButton(
text: "Verify",
isLoading: isLoading,
onPressed: state.otp.length == 6
onPressed: state.otp.length == 6 && !isLoading
? () {
context.read<VerifyOtpBloc>().add(
VerifyOtpSubmitted(

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../core/app_router.dart';
import '../blocs/onboarding_bloc.dart';
import '../models/onboarding_model.dart';
@@ -58,7 +59,7 @@ class OnboardingPage extends StatelessWidget {
if (current.title != "Manage Booking Seamlessly")
SafeArea(
child: Padding(
padding: const EdgeInsets.all(16.0),
padding: EdgeInsets.all(16.r),
child: Align(
alignment: Alignment.topRight,
child: OutlinedButton(
@@ -66,14 +67,23 @@ class OnboardingPage extends StatelessWidget {
context.read<OnboardingBloc>().add(OnboardingSkipPressed());
},
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.white),
side: BorderSide(color: Colors.white, width: 2.w),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 8,
padding: EdgeInsets.symmetric(
horizontal: 34.w,
vertical: 10.h,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r),
),
),
child: Text(
"Skip",
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w500,
),
),
child: const Text("Skip"),
),
),
),
@@ -83,7 +93,7 @@ class OnboardingPage extends StatelessWidget {
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(16.0),
padding: EdgeInsets.all(16.r),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
transitionBuilder: (Widget child, Animation<double> animation) {

View File

@@ -1,5 +1,6 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class GlassCard extends StatelessWidget {
final String title;
@@ -20,15 +21,15 @@ class GlassCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(10),
borderRadius: BorderRadius.circular(10.r),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 6, sigmaY: 6),
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
padding: EdgeInsets.all(20.r),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.3),
borderRadius: BorderRadius.circular(10),
borderRadius: BorderRadius.circular(10.r),
border: Border.all(color: Colors.white.withOpacity(0.2)),
),
child: Column(
@@ -37,52 +38,52 @@ class GlassCard extends StatelessWidget {
Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontSize: 20.sp,
fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
SizedBox(height: 8.h),
Text(
description,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 14,
fontSize: 14.sp,
),
),
const SizedBox(height: 16),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
total,
(index) => AnimatedContainer(
(index) => AnimatedContainer(
duration: const Duration(milliseconds: 300),
margin: const EdgeInsets.symmetric(horizontal: 4),
height: 6,
width: currentIndex == index ? 24 : 12,
margin: EdgeInsets.symmetric(horizontal: 4.w),
height: 6.h,
width: currentIndex == index ? 24.w : 12.w,
decoration: BoxDecoration(
color: currentIndex == index
? const Color(0xFFE25E5E)
: Colors.white24,
borderRadius: BorderRadius.circular(3),
borderRadius: BorderRadius.circular(3.r),
),
),
),
),
const SizedBox(height: 20),
SizedBox(height: 20.h),
ElevatedButton(
onPressed: onContinue,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFE25E5E),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(12.r),
),
minimumSize: const Size(double.infinity, 48),
minimumSize: Size(double.infinity, 48.h),
),
child: const Text(
child: Text(
'Continue',
style: TextStyle(fontSize: 16, color: Colors.white),
style: TextStyle(fontSize: 16.sp, color: Colors.white),
),
),
],

View File

@@ -36,7 +36,7 @@ class SubmitQrCodeBloc
final success = response['success'] == true;
final message = response['message']?.toString();
final error = response['error']?.toString();
final error = response['suggestedReason']?.toString();
if (success) {
emit(SubmitQrCodeSuccess(

View File

@@ -6,6 +6,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:intl/intl.dart';
import '../../local_peference/local_preference.dart';
import '../bloc/submit_qr_code/submit_qr_code_bloc.dart';
import '../bloc/recent_scan_history/recent_scan_history_bloc.dart';
import '../models/recent_scan_history_model.dart';
@@ -599,7 +600,7 @@ class _QrScanScreenState extends State<QrScanScreen>
SizedBox(height: 25.h),
Text(
"Quick Links",
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16.sp),
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 18.sp),
),
SizedBox(height: 10.h),
Row(
@@ -611,6 +612,7 @@ class _QrScanScreenState extends State<QrScanScreen>
"assets/scan/ticket-fill.png",
AppRouter.ticketRedemptionScreen,
initFraction,
10.h, // Added padding
),
),
SizedBox(width: 8.w),
@@ -621,6 +623,7 @@ class _QrScanScreenState extends State<QrScanScreen>
"assets/scan/support.png",
AppRouter.helpSupportPage,
initFraction,
10.h, // Added padding
),
),
SizedBox(width: 8.w),
@@ -631,10 +634,12 @@ class _QrScanScreenState extends State<QrScanScreen>
"assets/scan/page.png",
AppRouter.bookingPage,
initFraction,
10.h, // Added padding
),
),
],
),
SizedBox(height: 10.h), // Empty space at bottom
],
);
}
@@ -722,7 +727,7 @@ class _QrScanScreenState extends State<QrScanScreen>
final index = entry.key;
final item = entry.value;
final isSuccess = item.status.toLowerCase() == "success";
final label = index == 0 ? "Last Scan" : "Previous";
final label = index == 0 ? "Last Scan" : "Previous Scan";
return GestureDetector(
onTap: () {
@@ -751,7 +756,7 @@ class _QrScanScreenState extends State<QrScanScreen>
Text(
"Quick Links",
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16.sp),
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 18.sp),
),
SizedBox(height: 10.h),
Row(
@@ -763,6 +768,7 @@ class _QrScanScreenState extends State<QrScanScreen>
"assets/scan/support.png",
AppRouter.helpSupportPage,
initFraction,
22.h, // Added padding
),
),
SizedBox(width: 10.w),
@@ -773,6 +779,7 @@ class _QrScanScreenState extends State<QrScanScreen>
"assets/scan/page.png",
AppRouter.bookingPage,
initFraction,
22.h, // Added padding
),
),
],
@@ -787,6 +794,7 @@ class _QrScanScreenState extends State<QrScanScreen>
"assets/scan/ticket-fill.png",
AppRouter.ticketRedemptionScreen,
initFraction,
22.h, // Added padding
),
),
SizedBox(width: 10.w),
@@ -797,6 +805,7 @@ class _QrScanScreenState extends State<QrScanScreen>
"assets/scan/qr.png",
AppRouter.qrScanScreen,
initFraction,
22.h, // Added padding
),
),
],
@@ -865,7 +874,8 @@ class _QrScanScreenState extends State<QrScanScreen>
"Reason: $reason",
style: TextStyle(
fontSize: 12.sp,
color: Colors.red[700],
// color: Colors.red[700],
color: Colors.black87,
),
),
),
@@ -885,14 +895,16 @@ class _QrScanScreenState extends State<QrScanScreen>
}
Widget _quickLink(
BuildContext context,
String label,
String icon,
String route,
double initFraction,
) {
BuildContext context,
String label,
String icon,
String route,
double initFraction,
double? verticalPadding, // Add this parameter
) {
return InkWell(
onTap: () async {
// await LocalPreference.clearAccessToken();
if (label == "Scan Image") {
if (sheetController.isAttached) {
await sheetController.animateTo(
@@ -909,10 +921,12 @@ class _QrScanScreenState extends State<QrScanScreen>
},
child: Container(
decoration: BoxDecoration(
color: Colors.red[100],
color: Colors.red[100]?.withValues(alpha: 0.6),
borderRadius: BorderRadius.circular(10.r),
),
padding: EdgeInsets.symmetric(vertical: 10.h),
padding: EdgeInsets.symmetric(
vertical: verticalPadding ?? 12.h, // Use parameter, default to 12 if not provided
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [

View File

@@ -82,12 +82,22 @@ class ScanHistory {
);
}
/// 🔥 Safe Date Parser
/// 🔥 Safe Date Parser - Handles multiple formats
static DateTime? _parseDate(dynamic date) {
if (date == null) return null;
try {
return DateTime.parse(date.toString());
final dateStr = date.toString().trim();
// Try ISO 8601 format first (2026-04-20T16:21:00)
try {
return DateTime.parse(dateStr);
} catch (e) {
// If that fails, try DD-MM-YYYY HH:MM format (20-04-2026 16:21)
final dateFormat = DateFormat('dd-MM-yyyy HH:mm');
return dateFormat.parse(dateStr);
}
} catch (e) {
print('❌ Failed to parse date: $date, Error: $e');
return null;
}
}
@@ -143,6 +153,6 @@ class ScanHistory {
String get statusColorHex {
if (isSuccess) return '#4CAF50'; // green
if (isFailed) return '#F44336'; // red
return '#9E9E9E'; // grey
return '#9E9E9E'; // gre
}
}

View File

@@ -10,23 +10,20 @@ class ScanHistoryDetailPage extends StatelessWidget {
const ScanHistoryDetailPage({super.key, required this.passId});
TextStyle _headerStyle() => const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 24,
fontSize: 28,
height: 1.0,
);
TextStyle _labelStyle() => const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 14,
fontSize: 18,
color: Color(0xFF9E9E9E),
);
TextStyle _valueStyle() => const TextStyle(
fontWeight: FontWeight.w400,
fontSize: 12,
fontSize: 16,
color: Colors.black,
);
@@ -100,10 +97,10 @@ class ScanHistoryDetailPage extends StatelessWidget {
Expanded(
child: Center(
child: Text(
'#${data.bookingNumber}',
data.bookingNumber,
style: const TextStyle(
fontWeight: FontWeight.w700,
fontSize: 28,
fontSize: 22,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
@@ -175,10 +172,4 @@ class ScanHistoryDetailPage extends StatelessWidget {
],
);
}
}
}