3 Commits

Author SHA1 Message Date
d67808e07f cheque-management 2025-05-19 17:54:22 +05:30
98f2270614 card-management 2025-05-19 16:30:27 +05:30
2b2605cecc card-management 2025-05-16 16:21:21 +05:30
12 changed files with 771 additions and 26 deletions

View File

@@ -13,7 +13,7 @@ class AuthInterceptor extends Interceptor {
RequestInterceptorHandler handler,
) async {
// Skip auth header for login and refresh endpoints
if (options.path.contains('/auth/login') ||
if (options.path.contains('/login') ||
options.path.contains('/auth/refresh')) {
return handler.next(options);
}

View File

@@ -20,7 +20,7 @@ class AuthService {
Future<AuthToken> login(AuthCredentials credentials) async {
try {
final response = await _dio.post(
'/auth/login',
'/login',
data: credentials.toJson(),
);
@@ -30,6 +30,9 @@ class AuthService {
throw AuthException('Login failed');
}
} on DioException catch (e) {
if (kDebugMode) {
print(e.toString());
}
if (e.response?.statusCode == 401) {
throw AuthException('Invalid credentials');
}

View File

@@ -10,7 +10,7 @@ import 'data/repositories/auth_repository.dart';
import 'di/injection.dart';
import 'features/auth/controllers/auth_cubit.dart';
import 'features/auth/controllers/auth_state.dart';
import 'features/card/screens/Card_screen.dart';
import 'features/card/screens/card_management_screen.dart';
import 'features/auth/screens/login_screen.dart';
import 'features/service/screens/service_screen.dart';
import 'features/dashboard/screens/dashboard_screen.dart';
@@ -129,7 +129,7 @@ class _NavigationScaffoldState extends State<NavigationScaffold> {
final List<Widget> _pages = [
DashboardScreen(),
CardScreen(),
CardManagementScreen(),
ServiceScreen(),
CustomerInfoScreen()
];

View File

@@ -35,7 +35,7 @@ Future<void> setupDependencies() async {
Dio _createDioClient() {
final dio = Dio(
BaseOptions(
baseUrl: 'https://api.yourbank.com/v1',
baseUrl: 'http://localhost:3000',
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 3),
headers: {

View File

@@ -5,7 +5,7 @@ class AuthCredentials {
AuthCredentials({required this.username, required this.password});
Map<String, dynamic> toJson() => {
'username': username,
'customer_no': username,
'password': password,
};
}

View File

@@ -1,18 +0,0 @@
import 'package:flutter/material.dart';
class CardScreen extends StatefulWidget {
const CardScreen({super.key});
@override
State<CardScreen> createState() => _CardScreen();
}
class _CardScreen extends State<CardScreen>{
@override
Widget build(BuildContext context) {
return Scaffold(
);
}
}

View File

@@ -0,0 +1,200 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
class BlockCardScreen extends StatefulWidget {
const BlockCardScreen({super.key});
@override
State<BlockCardScreen> createState() => _BlockCardScreen();
}
class _BlockCardScreen extends State<BlockCardScreen>{
final _formKey = GlobalKey<FormState>();
final _cardController = TextEditingController();
final _cvvController = TextEditingController();
final _expiryController = TextEditingController();
final _phoneController = TextEditingController();
Future<void> _pickExpiryDate() async {
final now = DateTime.now();
final selectedDate = await showDatePicker(
context: context,
initialDate: now,
firstDate: now,
lastDate: DateTime(now.year + 10),
);
if (selectedDate != null) {
_expiryController.text = DateFormat('dd/MM/yyyy').format(selectedDate);
}
}
void _blockCard() {
if (_formKey.currentState?.validate() ?? false) {
// Call your backend logic here
final snackBar = SnackBar(
content: const Text('Card has been blocked'),
action: SnackBarAction(
label: 'X',
onPressed: () {
// Just close the SnackBar
},
textColor: Colors.white,
),
backgroundColor: Colors.black,
behavior: SnackBarBehavior.floating,
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(icon: const Icon(Symbols.arrow_back_ios_new),
onPressed: () {
Navigator.pop(context);
},),
title: const Text('Block Card', style: TextStyle(color: Colors.black,
fontWeight: FontWeight.w500),),
centerTitle: false,
actions: const [
Padding(
padding: EdgeInsets.only(right: 10.0),
child: CircleAvatar(
backgroundImage: AssetImage('assets/images/avatar.jpg'), // Replace with your image
radius: 20,
),
),
],
),
body: Padding(
padding: const EdgeInsets.all(10.0),
child: Form(
key: _formKey,
child: ListView(
children: [
const SizedBox(height: 10),
TextFormField(
controller: _cardController,
decoration: const InputDecoration(
labelText: 'Card Number',
border: OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2),
),
),
keyboardType: TextInputType.number,
textInputAction: TextInputAction.next,
validator: (value) =>
value != null && value.length == 16 ? null : 'Enter valid card number',
),
const SizedBox(height: 24),
Row(
children: [
Expanded(
child: TextFormField(
controller: _cvvController,
decoration: const InputDecoration(
labelText: 'CVV',
border: OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2),
),
),
keyboardType: TextInputType.number,
textInputAction: TextInputAction.next,
obscureText: true,
validator: (value) =>
value != null && value.length == 3 ? null : 'CVV must be 3 digits',
),
),
const SizedBox(width: 16),
Expanded(
child: TextFormField(
controller: _expiryController,
readOnly: true,
onTap: _pickExpiryDate,
decoration: const InputDecoration(
labelText: 'Expiry Date',
suffixIcon: Icon(Icons.calendar_today),
border: OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2),
),
),
validator: (value) =>
value != null && value.isNotEmpty ? null : 'Select expiry date',
),
),
],
),
const SizedBox(height: 24),
TextFormField(
controller: _phoneController,
decoration: const InputDecoration(
labelText: 'Phone',
prefixIcon: Icon(Icons.phone),
border: OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2),
),
),
textInputAction: TextInputAction.done,
keyboardType: TextInputType.phone,
validator: (value) =>
value != null && value.length >= 10 ? null : 'Enter valid phone number',
),
const SizedBox(height: 45),
Align(
alignment: Alignment.center,
child: SizedBox(
width: 250,
child: ElevatedButton(
onPressed: _blockCard,
style: ElevatedButton.styleFrom(
shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Colors.blue[900],
foregroundColor: Colors.white,
),
child: const Text('Block'),
),
),
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,95 @@
import 'package:flutter/material.dart';
import 'package:kmobile/features/card/screens/block_card_screen.dart';
import 'package:kmobile/features/card/screens/card_pin_change_details_screen.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
class CardManagementScreen extends StatefulWidget {
const CardManagementScreen({super.key});
@override
State<CardManagementScreen> createState() => _CardManagementScreen();
}
class _CardManagementScreen extends State<CardManagementScreen>{
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: const Text('Card Management', style: TextStyle(color: Colors.black,
fontWeight: FontWeight.w500),),
centerTitle: false,
actions: const [
Padding(
padding: EdgeInsets.only(right: 10.0),
child: CircleAvatar(
backgroundImage: AssetImage('assets/images/avatar.jpg'), // Replace with your image
radius: 20,
),
),
],
),
body: ListView(
children: [
CardManagementTile(
icon: Symbols.add,
label: 'Apply Debit Card',
onTap: () {
},
),
const Divider(height: 1,),
CardManagementTile(
icon: Symbols.remove_moderator,
label: 'Block / Unblock Card',
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) => const BlockCardScreen()));
},
),
const Divider(height: 1,),
CardManagementTile(
icon: Symbols.password_2,
label: 'Change Card PIN',
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) => const CardPinChangeDetailsScreen()));
},
),
const Divider(height: 1,),
],
),
);
}
}
class CardManagementTile extends StatelessWidget {
final IconData icon;
final String label;
final VoidCallback onTap;
const CardManagementTile({
super.key,
required this.icon,
required this.label,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return ListTile(
leading: Icon(icon),
title: Text(label),
trailing: const Icon(Symbols.arrow_right, size: 20),
onTap: onTap,
);
}
}

View File

@@ -0,0 +1,189 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:kmobile/features/card/screens/card_pin_set_screen.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
class CardPinChangeDetailsScreen extends StatefulWidget {
const CardPinChangeDetailsScreen({super.key});
@override
State<CardPinChangeDetailsScreen> createState() => _CardPinChangeDetailsScreen();
}
class _CardPinChangeDetailsScreen extends State<CardPinChangeDetailsScreen>{
final _formKey = GlobalKey<FormState>();
final _cardController = TextEditingController();
final _cvvController = TextEditingController();
final _expiryController = TextEditingController();
final _phoneController = TextEditingController();
Future<void> _pickExpiryDate() async {
final now = DateTime.now();
final selectedDate = await showDatePicker(
context: context,
initialDate: now,
firstDate: now,
lastDate: DateTime(now.year + 10),
);
if (selectedDate != null) {
_expiryController.text = DateFormat('dd/MM/yyyy').format(selectedDate);
}
}
void _nextButton() {
if (_formKey.currentState?.validate() ?? false) {
// Call your backend logic here
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => const CardPinSetScreen()),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(icon: const Icon(Symbols.arrow_back_ios_new),
onPressed: () {
Navigator.pop(context);
},),
title: const Text('Card Details', style: TextStyle(color: Colors.black,
fontWeight: FontWeight.w500),),
centerTitle: false,
actions: const [
Padding(
padding: EdgeInsets.only(right: 10.0),
child: CircleAvatar(
backgroundImage: AssetImage('assets/images/avatar.jpg'), // Replace with your image
radius: 20,
),
),
],
),
body: Padding(
padding: const EdgeInsets.all(10.0),
child: Form(
key: _formKey,
child: ListView(
children: [
const SizedBox(height: 10),
TextFormField(
controller: _cardController,
decoration: const InputDecoration(
labelText: 'Card Number',
border: OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2),
),
),
keyboardType: TextInputType.number,
textInputAction: TextInputAction.next,
validator: (value) =>
value != null && value.length == 16 ? null : 'Enter valid card number',
),
const SizedBox(height: 24),
Row(
children: [
Expanded(
child: TextFormField(
controller: _cvvController,
decoration: const InputDecoration(
labelText: 'CVV',
border: OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2),
),
),
keyboardType: TextInputType.number,
textInputAction: TextInputAction.next,
obscureText: true,
validator: (value) =>
value != null && value.length == 3 ? null : 'CVV must be 3 digits',
),
),
const SizedBox(width: 16),
Expanded(
child: TextFormField(
controller: _expiryController,
readOnly: true,
onTap: _pickExpiryDate,
decoration: const InputDecoration(
labelText: 'Expiry Date',
suffixIcon: Icon(Icons.calendar_today),
border: OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2),
),
),
validator: (value) =>
value != null && value.isNotEmpty ? null : 'Select expiry date',
),
),
],
),
const SizedBox(height: 24),
TextFormField(
controller: _phoneController,
decoration: const InputDecoration(
labelText: 'Phone',
prefixIcon: Icon(Icons.phone),
border: OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2),
),
),
textInputAction: TextInputAction.done,
keyboardType: TextInputType.phone,
validator: (value) =>
value != null && value.length >= 10 ? null : 'Enter valid phone number',
),
const SizedBox(height: 45),
Align(
alignment: Alignment.center,
child: SizedBox(
width: 250,
child: ElevatedButton(
onPressed: _nextButton,
style: ElevatedButton.styleFrom(
shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Colors.blue[900],
foregroundColor: Colors.white,
),
child: const Text('Next'),
),
),
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,148 @@
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
class CardPinSetScreen extends StatefulWidget {
const CardPinSetScreen({super.key});
@override
State<CardPinSetScreen> createState() => _CardPinSetScreen();
}
class _CardPinSetScreen extends State<CardPinSetScreen>{
final _formKey = GlobalKey<FormState>();
final _pinController = TextEditingController();
final _confirmPinController = TextEditingController();
void _submit() {
if (_formKey.currentState!.validate()) {
// Handle PIN submission logic here
final snackBar = SnackBar(
content: const Text('PIN set successfully'),
action: SnackBarAction(
label: 'X',
onPressed: () {
// Just close the SnackBar
},
textColor: Colors.white,
),
backgroundColor: Colors.black,
behavior: SnackBarBehavior.floating,
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
@override
void dispose() {
_pinController.dispose();
_confirmPinController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(icon: const Icon(Symbols.arrow_back_ios_new),
onPressed: () {
Navigator.pop(context);
},),
title: const Text('Card PIN', style: TextStyle(color: Colors.black,
fontWeight: FontWeight.w500),),
centerTitle: false,
actions: const [
Padding(
padding: EdgeInsets.only(right: 10.0),
child: CircleAvatar(
backgroundImage: AssetImage('assets/images/avatar.jpg'), // Replace with your image
radius: 20,
),
),
],
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _pinController,
obscureText: true,
decoration: const InputDecoration(
labelText: 'Enter new PIN',
border: OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2),
),
),
keyboardType: TextInputType.number,
textInputAction: TextInputAction.next,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter new PIN';
}
if (value.length < 4) {
return 'PIN must be at least 4 digits';
}
return null;
},
),
const SizedBox(height: 24),
TextFormField(
controller: _confirmPinController,
obscureText: true,
decoration: const InputDecoration(
labelText: 'Enter Again',
border: OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2),
),
),
keyboardType: TextInputType.number,
textInputAction: TextInputAction.done,
validator: (value) {
if (value != _pinController.text) {
return 'PINs do not match';
}
return null;
},
),
const SizedBox(height: 45),
Align(
alignment: Alignment.center,
child: SizedBox(
width: 250,
child: ElevatedButton(
onPressed: _submit,
style: ElevatedButton.styleFrom(
shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Colors.blue[900],
foregroundColor: Colors.white,
),
child: const Text('Submit'),
),
),
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,124 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
class ChequeManagementScreen extends StatefulWidget {
const ChequeManagementScreen({super.key});
@override
State<ChequeManagementScreen> createState() => _ChequeManagementScreen();
}
class _ChequeManagementScreen extends State<ChequeManagementScreen>{
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(icon: const Icon(Symbols.arrow_back_ios_new),
onPressed: () {
Navigator.pop(context);
},),
title: const Text('Cheque Management', style: TextStyle(color: Colors.black,
fontWeight: FontWeight.w500),),
centerTitle: false,
actions: const [
Padding(
padding: EdgeInsets.only(right: 10.0),
child: CircleAvatar(
backgroundImage: AssetImage('assets/images/avatar.jpg'), // Replace with your image
radius: 20,
),
),
],
),
body: ListView(
children: [
const SizedBox(height: 15),
ChequeManagementTile(
icon: Symbols.add,
label: 'Request Checkbook',
onTap: () {
},
),
const Divider(height: 1,),
ChequeManagementTile(
icon: Symbols.data_alert,
label: 'Enquiry',
onTap: () {
// Navigator.push(context, MaterialPageRoute(
// builder: (context) => const BlockCardScreen()));
},
),
const Divider(height: 1,),
ChequeManagementTile(
icon: Symbols.approval_delegation,
label: 'Cheque Deposit',
onTap: () {
},
),
const Divider(height: 1,),
ChequeManagementTile(
icon: Symbols.front_hand,
label: 'Stop Cheque',
onTap: () {
},
),
const Divider(height: 1,),
ChequeManagementTile(
icon: Symbols.cancel_presentation,
label: 'Revoke Stop',
onTap: () {
},
),
const Divider(height: 1,),
ChequeManagementTile(
icon: Symbols.payments,
label: 'Positive Pay',
onTap: () {
},
),
const Divider(height: 1,),
],
),
);
}
}
class ChequeManagementTile extends StatelessWidget {
final IconData icon;
final String label;
final VoidCallback onTap;
const ChequeManagementTile({
super.key,
required this.icon,
required this.label,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return ListTile(
leading: Icon(icon),
title: Text(label),
trailing: const Icon(Symbols.arrow_right, size: 20),
onTap: onTap,
);
}
}

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:kmobile/features/accounts/screens/account_info_screen.dart';
import 'package:kmobile/features/accounts/screens/account_statement_screen.dart';
import 'package:kmobile/features/cheque/screens/cheque_management_screen.dart';
import 'package:kmobile/features/customer_info/screens/customer_info_screen.dart';
import 'package:kmobile/features/beneficiaries/screens/manage_beneficiaries_screen.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
@@ -108,7 +109,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
children: [
_buildQuickLink(Symbols.id_card, "Customer \n Info", () {
Navigator.push(context, MaterialPageRoute(
builder: (context) => const CustomerInfoScreen()));;
builder: (context) => const CustomerInfoScreen()));
}),
_buildQuickLink(Symbols.currency_rupee, "Quick \n Pay",
() => print("")),
@@ -125,7 +126,10 @@ class _DashboardScreenState extends State<DashboardScreen> {
builder: (context) => const AccountStatementScreen()));
}),
_buildQuickLink(Symbols.checkbook, "Handle \n Cheque",
() => print("")),
() {
Navigator.push(context, MaterialPageRoute(
builder: (context) => const ChequeManagementScreen()));
}),
_buildQuickLink(Icons.group, "Manage \n Beneficiary",
() {
Navigator.push(context, MaterialPageRoute(