Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
Sai Srinivas
GEN_ERP_2025
Commits
dc88a3f9
Commit
dc88a3f9
authored
Oct 08, 2025
by
Sai Srinivas
Browse files
gen erp 08-10
parent
d2c9404a
Changes
42
Expand all
Hide whitespace changes
Inline
Side-by-side
lib/Notifiers/splashVersionNotifier.dart
View file @
dc88a3f9
import
'dart:io'
;
import
'dart:math'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
import
'package:generp/Models/VersionsResponse.dart'
;
import
'package:generp/Notifiers/HomeScreenNotifier.dart'
;
import
'package:generp/Notifiers/loginNotifier.dart'
;
import
'package:generp/Utils/SharedpreferencesService.dart'
;
import
'package:generp/screens/HomeScreen.dart'
;
...
...
@@ -55,7 +54,7 @@ class SplashVersionNotifier extends ChangeNotifier {
if
(
data
!=
null
)
{
if
(
kDebugMode
)
{
print
(
"Current Build:
$currentBuild
"
);
print
(
"Server Response:
$data
"
);
print
(
"Server Response:
$
{
data
.latestVersionCode}
"
);
}
if
(
Platform
.
isAndroid
&&
currentBuild
<
(
data
.
latestVersionCode
??
0
))
{
...
...
lib/main.dart
View file @
dc88a3f9
import
'dart:convert'
;
import
'dart:io'
;
import
'package:firebase_core/firebase_core.dart'
;
...
...
@@ -10,56 +11,138 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import
'package:flutter_ringtone_player/flutter_ringtone_player.dart'
;
import
'package:generp/Models/hrmModels/leaveApplicationLIstResponse.dart'
;
import
'package:generp/Utils/app_colors.dart'
;
import
'package:generp/screens/AttendanceScreen.dart'
;
import
'package:generp/screens/crm/LeadDetailsByMode.dart'
;
import
'package:generp/screens/crm/ProspectListByMode.dart'
;
import
'package:generp/screens/crm/crmDashboard.dart'
;
import
'package:generp/screens/hrm/Attendancelist.dart'
;
import
'package:generp/screens/hrm/OrganizationStructureScreen.dart'
;
import
'package:generp/screens/hrm/TourExpensesListScreen.dart'
;
import
'Notifiers/hrmProvider/advanceProvider.dart'
;
import
'screens/notifierExports.dart'
;
import
'package:generp/Utils/SharedpreferencesService.dart'
;
import
'package:generp/screens/splash.dart'
;
import
'package:provider/provider.dart'
;
import
'Utils/commonWidgets.dart'
;
// Navigator Key for global navigation
final
GlobalKey
<
NavigatorState
>
navigatorKey
=
GlobalKey
<
NavigatorState
>();
// Notification Channel
const
AndroidNotificationChannel
channel
=
AndroidNotificationChannel
(
'generp_channel'
,
// id
'generp_channel'
,
'generp_channel_name'
,
importance:
Importance
.
max
,
playSound:
fals
e
,
playSound:
tru
e
,
);
final
FlutterLocalNotificationsPlugin
flutterLocalNotificationsPlugin
=
FlutterLocalNotificationsPlugin
();
FlutterLocalNotificationsPlugin
();
Future
<
void
>
_firebaseMessagingBackgroundHandler
(
RemoteMessage
message
)
async
{
await
Firebase
.
initializeApp
();
String
type
=
message
.
data
[
'type'
]
??
''
;
debugPrint
(
"📩 Background notification received:
${message.data}
"
);
}
if
(
type
==
'offline_reminder'
)
{
FlutterRingtonePlayer
().
play
(
// fromAsset: "assets/offline_reminder.mp3",
ios:
IosSounds
.
glass
,
// Specify the iOS sound
);
}
else
if
(
type
==
'normal'
)
{
FlutterRingtonePlayer
().
play
(
// fromAsset: "assets/notification_sound.mp3",
ios:
IosSounds
.
glass
,
// Specify the iOS sound
);
}
else
if
(
type
==
'web_erp_notification'
)
{
FlutterRingtonePlayer
().
play
(
// fromAsset: "assets/notification_sound.mp3",
ios:
IosSounds
.
glass
,
// Specify the iOS sound
);
}
else
{
FlutterRingtonePlayer
().
play
(
// fromAsset: "assets/notification_sound.mp3",
// will be the sound on Android
ios:
IosSounds
.
glass
,
// will be the sound on iOS
);
}
if
(
kDebugMode
)
{
print
(
'A Background message just showed up:
${message.messageId}
'
);
void
handleNotificationTap
(
Map
<
String
,
dynamic
>
data
)
{
debugPrint
(
"👉 Handling notification tap:
$data
"
);
try
{
String
type
=
data
[
'type'
]
??
''
;
String
?
rollId
=
data
[
'RoleID'
]
??
data
[
'rollId'
];
String
?
notificationId
=
data
[
'notification_id'
]
??
data
[
'notificationId'
];
String
?
url
=
data
[
'url'
];
String
?
mode
=
data
[
'Parameter'
];
debugPrint
(
"👉 Handling notification tap:
$mode
"
);
if
(
type
.
toLowerCase
()
==
"app"
)
{
// 🔥 Navigation based on RoleID
switch
(
rollId
)
{
case
"1"
:
navigatorKey
.
currentState
?.
pushNamed
(
'/crm_lead_list'
);
break
;
case
"4"
:
navigatorKey
.
currentState
?.
pushNamed
(
'/crm_prospect_list'
);
break
;
case
"5"
:
navigatorKey
.
currentState
?.
pushNamed
(
'/crm_prospect_details'
,
arguments:
{
"id"
:
notificationId
},
);
break
;
case
"6"
:
navigatorKey
.
currentState
?.
pushNamed
(
'/crm_lead_details'
,
arguments:
{
"id"
:
notificationId
},
);
break
;
case
"7"
:
navigatorKey
.
currentState
?.
pushNamed
(
'/dashboard'
);
break
;
case
"8"
:
navigatorKey
.
currentState
?.
pushNamed
(
'/crm_new_customer_new_lead_register'
);
break
;
case
"10"
:
navigatorKey
.
currentState
?.
pushNamed
(
'/crm_prospect_list_team'
);
break
;
case
"11"
:
navigatorKey
.
currentState
?.
pushNamed
(
'/crm_prospect_list_admin'
);
break
;
case
"15"
:
navigatorKey
.
currentState
?.
pushNamed
(
'/finance_list_employee'
);
break
;
case
"16"
:
navigatorKey
.
currentState
?.
pushNamed
(
'/finance_list_admin'
);
break
;
case
"17"
:
navigatorKey
.
currentState
?.
pushNamed
(
'/dispatch_list_executive'
);
break
;
case
"312"
:
navigatorKey
.
currentState
?.
pushNamed
(
'/tour_bill_list'
);
break
;
case
"601"
:
navigatorKey
.
currentState
?.
pushNamed
(
'/manual_attendance_request_list'
);
break
;
default
:
navigatorKey
.
currentState
?.
pushNamed
(
'/home'
);
// fallback
}
}
else
if
(
type
.
toLowerCase
()
==
"web"
&&
url
!=
null
&&
url
.
isNotEmpty
)
{
// Open in WebView or browser
navigatorKey
.
currentState
?.
pushNamed
(
'/webview'
,
arguments:
{
"url"
:
url
},
);
}
else
{
// fallback
navigatorKey
.
currentState
?.
pushNamed
(
'/home'
);
}
}
catch
(
e
)
{
debugPrint
(
"❌ Error handling notification tap:
$e
"
);
}
}
// FlutterLocalNotificationsPlugin cannot auto-detect foreground taps
void
showInAppNotification
(
BuildContext
context
,
Map
<
String
,
dynamic
>
data
)
{
final
snackBar
=
SnackBar
(
content:
Text
(
data
[
'type'
]
??
"New Notification"
),
action:
SnackBarAction
(
label:
'Open'
,
onPressed:
()
{
debugPrint
(
"👉 Foreground notification tapped:
$data
"
);
handleNotificationTap
(
data
);
},
),
);
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
snackBar
);
}
void
main
(
)
async
{
WidgetsFlutterBinding
.
ensureInitialized
();
// Firebase init
if
(
Platform
.
isAndroid
)
{
await
Firebase
.
initializeApp
(
options:
FirebaseOptions
(
...
...
@@ -72,60 +155,147 @@ void main() async {
}
else
if
(
Platform
.
isIOS
)
{
await
Firebase
.
initializeApp
();
}
if
(
kDebugMode
)
{
if
(
Firebase
.
apps
.
isNotEmpty
)
{
print
(
"Firebase is initialized"
);
}
else
{
print
(
"Firebase is not initialized"
);
// 🔔 Local Notification Init
const
AndroidInitializationSettings
initSettingsAndroid
=
AndroidInitializationSettings
(
'@mipmap/ic_launcher'
);
const
DarwinInitializationSettings
initSettingsIOS
=
DarwinInitializationSettings
();
const
InitializationSettings
initializationSettings
=
InitializationSettings
(
android:
initSettingsAndroid
,
iOS:
initSettingsIOS
,
);
flutterLocalNotificationsPlugin
.
initialize
(
initializationSettings
,
onDidReceiveNotificationResponse:
(
NotificationResponse
notificationResponse
)
async
{
if
(
notificationResponse
.
payload
!=
null
)
{
handleNotificationTap
(
jsonDecode
(
notificationResponse
.
payload
!));
debugPrint
(
"📩 Notification clicked:
$notificationResponse
"
);
}
}
}
);
// Firebase Messaging
FirebaseMessaging
.
onBackgroundMessage
(
_firebaseMessagingBackgroundHandler
);
FirebaseMessaging
messaging
=
FirebaseMessaging
.
instance
;
await
messaging
.
requestPermission
(
alert:
true
,
badge:
true
,
sound:
true
);
NotificationSettings
settings
=
await
messaging
.
requestPermission
(
alert:
true
,
announcement:
true
,
badge:
true
,
carPlay:
false
,
criticalAlert:
false
,
provisional:
false
,
sound:
false
,
);
// Foreground notification
FirebaseMessaging
.
onMessage
.
listen
((
RemoteMessage
message
)
{
RemoteNotification
?
notification
=
message
.
notification
;
AndroidNotification
?
android
=
message
.
notification
?.
android
;
print
(
"msg"
);
String
type
=
message
.
data
[
'type'
]
??
''
;
if
(
type
==
'offline_reminder'
)
{
FlutterRingtonePlayer
().
play
(
fromAsset:
"assets/offline_reminder.mp3"
,
ios:
IosSounds
.
glass
,
// Specify the iOS sound
);
}
else
if
(
type
==
'normal'
)
{
FlutterRingtonePlayer
().
play
(
fromAsset:
"assets/notification_sound.mp3"
,
ios:
IosSounds
.
glass
,
// Specify the iOS sound
);
}
else
if
(
type
==
'web_erp_notification'
)
{
FlutterRingtonePlayer
().
play
(
fromAsset:
"assets/notification_sound.mp3"
,
ios:
IosSounds
.
glass
,
// Specify the iOS sound
);
}
else
{
FlutterRingtonePlayer
().
play
(
fromAsset:
"assets/notification_sound.mp3"
,
// will be the sound on Android
ios:
IosSounds
.
glass
,
// will be the sound on iOS
final
notification
=
message
.
notification
;
final
android
=
message
.
notification
?.
android
;
debugPrint
(
"😊 Foreground msg received:
${message.data}
"
);
if
(
notification
!=
null
&&
android
!=
null
)
{
// flutterLocalNotificationsPlugin.show(
// notification.hashCode,
// notification.title,
// notification.body,
// NotificationDetails(
// android: AndroidNotificationDetails(
// channel.id,
// channel.name,
// importance: Importance.max,
// priority: Priority.high,
// icon: '@mipmap/ic_launcher',
// ),
// ),
// payload: jsonEncode({
// "type": message.data['type'],
// "rollId": message.data['RoleID'],
// "notificationId": message.data['notification_id'],
// }),
// );
}
// ⚡ Foreground tap alternative:
// Show an in-app banner or SnackBar with a tap action
final
context
=
navigatorKey
.
currentContext
;
if
(
context
!=
null
)
{
// Add haptic feedback
HapticFeedback
.
mediumImpact
();
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
SnackBar
(
content:
Row
(
children:
[
SizedBox
(
child:
Image
.
asset
(
"assets/images/ic_splash.jpg"
,
height:
30
,
width:
30
,
),
),
SizedBox
(
width:
8
),
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
mainAxisSize:
MainAxisSize
.
min
,
children:
[
Text
(
notification
?.
title
??
"New Notification"
,
style:
TextStyle
(
fontSize:
14
,
color:
AppColors
.
app_blue
,
fontFamily:
"JakartaMedium"
,
),
),
if
(
notification
?.
body
!=
null
)
SizedBox
(
height:
2
),
if
(
notification
?.
body
!=
null
)
Text
(
notification
!.
body
!,
style:
TextStyle
(
fontSize:
12
,
color:
AppColors
.
semi_black
,
fontFamily:
"JakartaMedium"
,
),
),
],
),
),
],
),
action:
SnackBarAction
(
label:
"Open"
,
textColor:
AppColors
.
app_blue
,
onPressed:
()
{
// Add haptic feedback for button press
HapticFeedback
.
lightImpact
();
debugPrint
(
"👉 Foreground notification tapped:
${message.data}
"
);
// Use the same payload structure as local notifications
handleNotificationTap
({
"type"
:
message
.
data
[
'type'
],
"rollId"
:
message
.
data
[
'RoleID'
],
"notificationId"
:
message
.
data
[
'notification_id'
],
});
},
),
backgroundColor:
AppColors
.
white
,
behavior:
SnackBarBehavior
.
floating
,
elevation:
6
,
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
8
),
),
margin:
const
EdgeInsets
.
all
(
16
),
duration:
const
Duration
(
seconds:
4
),
),
);
}
});
FirebaseMessaging
.
onBackgroundMessage
(
_firebaseMessagingBackgroundHandler
);
await
flutterLocalNotificationsPlugin
.
resolvePlatformSpecificImplementation
<
AndroidFlutterLocalNotificationsPlugin
>()
AndroidFlutterLocalNotificationsPlugin
>()
?.
createNotificationChannel
(
channel
);
await
FirebaseMessaging
.
instance
.
setForegroundNotificationPresentationOptions
(
...
...
@@ -134,13 +304,27 @@ void main() async {
sound:
true
,
);
await
FirebaseMessaging
.
instance
.
getToken
().
then
((
value
)
{
String
?
token
=
value
;
FirebaseMessaging
.
onMessageOpenedApp
.
listen
((
RemoteMessage
message
)
{
String
type
=
message
.
data
[
'type'
]
??
''
;
String
redirectUrl
=
message
.
data
[
'redirect_url'
]
??
''
;
handleNotificationTap
(
message
.
data
);
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) => Dashboard()),
// );
if
(
kDebugMode
)
{
//
print(
"fbstoken:{$token}"
);
print
(
'A new onMessageOpenedApp event was published!'
);
}
});
SharedpreferencesService
().
saveString
(
"fbstoken"
,
token
!);
// Save FCM Token
await
FirebaseMessaging
.
instance
.
getToken
().
then
((
value
)
{
if
(
value
!=
null
)
{
SharedpreferencesService
().
saveString
(
"fbstoken"
,
value
);
debugPrint
(
"🔑 FCM Token:
$value
"
);
}
});
runApp
(
const
MyApp
());
...
...
@@ -153,19 +337,19 @@ class MyApp extends StatelessWidget {
@override
Widget
build
(
BuildContext
context
)
{
// SystemChrome.setApplicationSwitcherDescription(ApplicationSwitcherDescription());
SystemChrome
.
setEnabledSystemUIMode
(
SystemUiMode
.
edgeToEdge
);
FirebaseMessaging
.
onMessageOpenedApp
.
listen
((
RemoteMessage
message
)
{
String
type
=
message
.
data
[
'type'
]
??
''
;
String
redirectUrl
=
message
.
data
[
'redirect_url'
]
??
''
;
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) => Dashboard()),
// );
if
(
kDebugMode
)
{
print
(
'A new onMessageOpenedApp event was published!'
);
}
});
//
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
//
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
//
String type = message.data['type'] ?? '';
//
String redirectUrl = message.data['redirect_url'] ?? '';
//
//
// Navigator.push(
//
// context,
//
// MaterialPageRoute(builder: (context) => Dashboard()),
//
// );
//
if (kDebugMode) {
//
print('A new onMessageOpenedApp event was published!');
//
}
//
});
return
MultiProvider
(
providers:
[
ChangeNotifierProvider
(
create:
(
_
)
=>
SplashVersionNotifier
()),
...
...
@@ -226,9 +410,7 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider
(
create:
(
_
)
=>
Dispatchorderprovider
()),
ChangeNotifierProvider
(
create:
(
_
)
=>
followUpUpdateProvider
()),
ChangeNotifierProvider
(
create:
(
_
)
=>
Appointmentcalendarprovider
()),
ChangeNotifierProvider
(
create:
(
_
)
=>
Addnewleadsandprospectsprovider
(),
),
ChangeNotifierProvider
(
create:
(
_
)
=>
Addnewleadsandprospectsprovider
()),
ChangeNotifierProvider
(
create:
(
_
)
=>
HrmAccessiblePagesProvider
()),
ChangeNotifierProvider
(
create:
(
_
)
=>
Attendancelistprovider
()),
ChangeNotifierProvider
(
create:
(
_
)
=>
AttendanceDetailsProvider
()),
...
...
@@ -237,12 +419,36 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider
(
create:
(
_
)
=>
RewardListProvider
()),
ChangeNotifierProvider
(
create:
(
_
)
=>
LeaveApplicationListProvider
()),
ChangeNotifierProvider
(
create:
(
_
)
=>
Orgprovider
()),
ChangeNotifierProvider
(
create:
(
_
)
=>
AdvanceListProvider
()),
ChangeNotifierProvider
(
create:
(
_
)
=>
CasualLeaveHistoryProvider
()),
],
child:
Builder
(
builder:
(
BuildContext
context
)
{
return
MaterialApp
(
navigatorKey:
navigatorKey
,
routes:
{
'/home'
:
(
context
)
=>
const
AttendanceScreen
(),
'/chat'
:
(
context
)
=>
AttendanceScreen
(),
'/product_details'
:
(
context
)
=>
AttendanceScreen
(),
'/order_details'
:
(
context
)
=>
AttendanceScreen
(),
'/crm_lead_list'
:
(
context
)
=>
LeadDetailsByMode
(
mode:
""
,
pageTitleName:
"Lead Details"
,
leadId:
""
),
'/crm_prospect_list'
:
(
context
)
=>
ProspectListByMode
(),
'/crm_prospect_details'
:
(
context
)
=>
AttendanceScreen
(),
'/crm_lead_details'
:
(
context
)
=>
AttendanceScreen
(),
'/dashboard'
:
(
context
)
=>
CrmdashboardScreen
(),
'/crm_new_customer_new_lead_register'
:
(
context
)
=>
AttendanceScreen
(),
'/crm_prospect_list_team'
:
(
context
)
=>
AttendanceScreen
(),
'/crm_prospect_list_admin'
:
(
context
)
=>
AttendanceScreen
(),
'/finance_list_employee'
:
(
context
)
=>
AttendanceScreen
(),
'/finance_list_admin'
:
(
context
)
=>
AttendanceScreen
(),
'/dispatch_list_executive'
:
(
context
)
=>
AttendanceScreen
(),
'/tour_bill_list'
:
(
context
)
=>
TourExpensesListScreen
(),
'/manual_attendance_request_list'
:
(
context
)
=>
AttendanceListScreen
(
mode:
""
,),
},
scrollBehavior:
const
MaterialScrollBehavior
().
copyWith
(
dragDevices:
{
PointerDeviceKind
.
touch
,
PointerDeviceKind
.
mouse
},
dragDevices:
{
PointerDeviceKind
.
touch
,
PointerDeviceKind
.
mouse
},
),
navigatorObservers:
[
MyNavigatorObserver
()],
...
...
@@ -261,6 +467,7 @@ class MyApp extends StatelessWidget {
highlightColor:
Colors
.
transparent
,
hoverColor:
Colors
.
transparent
,
scaffoldBackgroundColor:
Colors
.
white
,
dialogBackgroundColor:
Colors
.
white
,
cardColor:
Colors
.
white
,
shadowColor:
Colors
.
white54
,
searchBarTheme:
const
SearchBarThemeData
(),
...
...
@@ -304,8 +511,8 @@ class MyApp extends StatelessWidget {
dragHandleSize:
Size
(
60.0
,
6.0
),
),
colorScheme:
const
ColorScheme
.
light
(
surface
:
Colors
.
white
,
).
copyWith
(
surface
:
Colors
.
white
),
background
:
Colors
.
white
,
).
copyWith
(
background
:
Colors
.
white
),
scrollbarTheme:
ScrollbarThemeData
(
minThumbLength:
20
,
interactive:
true
,
...
...
@@ -316,8 +523,10 @@ class MyApp extends StatelessWidget {
),
),
checkboxTheme:
CheckboxThemeData
(
side:
BorderSide
(
width:
0.5
),
checkColor:
WidgetStatePropertyAll
(
AppColors
.
white
),
),
useMaterial3:
true
,
// inputDecorationTheme: InputDecorationTheme(
...
...
lib/screens/HomeScreen.dart
View file @
dc88a3f9
...
...
@@ -1779,12 +1779,12 @@ class _MyHomePageState extends State<MyHomePage> {
}
Future
<
void
>
_showProfileBottomSheet
(
BuildContext
context
)
{
final
profileNotifier
=
Provider
.
of
<
ProfileNotifer
>(
context
,
listen:
false
);
//
final profileNotifier = Provider.of<ProfileNotifer>(context, listen: false);
profileNotifier
.
fetchJobDescription
(
Provider
.
of
<
HomescreenNotifier
>(
context
,
listen:
false
),
context
,
);
//
profileNotifier.fetchJobDescription(
//
Provider.of<HomescreenNotifier>(context, listen: false),
//
context,
//
);
return
showModalBottomSheet
(
useSafeArea:
true
,
isDismissible:
true
,
...
...
@@ -1975,18 +1975,7 @@ class _MyHomePageState extends State<MyHomePage> {
AppColors
.
semi_black
,
),
),
if
(
profileNotifier
.
response
?.
jobDescription
?.
jobDescription
!=
null
&&
profileNotifier
.
response
!
.
jobDescription
!
.
jobDescription
!=
""
)
if
(
index
==
2
)
// only for Designation
if
(
index
==
2
)
// only for Designation
InkWell
(
onTap:
index
==
2
...
...
lib/screens/crm/LeadDetailsByMode.dart
View file @
dc88a3f9
...
...
@@ -797,7 +797,7 @@ class _LeadDetailsByModeState extends State<LeadDetailsByMode> {
if
(
productsNotEmpty
)
...[
SizedBox
(
width:
double
.
infinity
,
height:
1
3
0
,
height:
1
5
0
,
child:
ListView
.
builder
(
physics:
AlwaysScrollableScrollPhysics
(),
shrinkWrap:
true
,
...
...
@@ -872,7 +872,7 @@ class _LeadDetailsByModeState extends State<LeadDetailsByMode> {
);
},
child:
Container
(
height:
1
3
0
,
height:
1
4
0
,
width:
MediaQuery
.
of
(
context
).
size
.
width
*
0.9
,
...
...
@@ -966,7 +966,7 @@ class _LeadDetailsByModeState extends State<LeadDetailsByMode> {
AppColors
.
grey_semi
,
),
),
SizedBox
(
height:
5
),
SizedBox
(
height:
3
),
DottedLine
(
dashGapLength:
4
,
dashGapColor:
...
...
@@ -988,6 +988,24 @@ class _LeadDetailsByModeState extends State<LeadDetailsByMode> {
.
semi_black
,
),
),
SizedBox
(
height:
5
),
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
Expanded
(
child:
Text
(
provider
.
leadProducts
[
lp
].
remarks
??
""
,
style:
TextStyle
(
fontFamily:
"JakartaMedium"
,
fontSize:
14
,
color:
AppColors
.
semi_black
,
),
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
),
),
],
)
],
),
),
...
...
@@ -3458,6 +3476,34 @@ class _LeadDetailsByModeState extends State<LeadDetailsByMode> {
),
],
),
const
SizedBox
(
height:
10
),
TextWidget
(
context
,
"Remarks"
),
Container
(
margin:
EdgeInsets
.
only
(
bottom:
6
),
decoration:
BoxDecoration
(
color:
AppColors
.
text_field_color
,
borderRadius:
BorderRadius
.
circular
(
14
),
),
child:
TextFormField
(
controller:
editProvider
.
addEditRemarkController
,
maxLines:
3
,
enabled:
true
,
style:
TextStyle
(
color:
Colors
.
black
,
fontSize:
14
,
),
decoration:
InputDecoration
(
hintText:
"Enter remark"
,
hintStyle:
TextStyle
(
color:
Colors
.
grey
.
shade500
,
fontSize:
14
,
),
border:
InputBorder
.
none
,
contentPadding:
EdgeInsets
.
symmetric
(
horizontal:
12
,
vertical:
12
),
),
),
),
// IconButton(
// icon: const Icon(Icons.delete),
// onPressed: editProvider.editProductPriceControllers.length > 1
...
...
@@ -3476,8 +3522,8 @@ class _LeadDetailsByModeState extends State<LeadDetailsByMode> {
provider
.
leadDetails
.
id
!,
type
,
leadProductId
,
editProvider
.
selectedAddEditProductId
,
editProvider
.
selectedAddEditProductId
,
editProvider
.
addEditRemarkController
);
},
child:
Container
(
...
...
lib/screens/crm/LeadListByMode.dart
View file @
dc88a3f9
...
...
@@ -267,24 +267,22 @@ class _LeadlistbymodeState extends State<Leadlistbymode> {
),
),
SizedBox
(
width:
10
),
Expanded
(
flex:
1
,
child:
InkResponse
(
onTap:
()
{
HapticFeedback
.
selectionClick
();
launch
(
'tel://
${crmLists[index].mob1}
'
,
);
},
child:
SizedBox
(
height:
35
,
width:
35
,
child:
SvgPicture
.
asset
(
"assets/svg/crm/lead_list_call_ic.svg"
,
),
),
),
),
Expanded
(
flex:
1
,
child:
InkResponse
(
onTap:
()
{
HapticFeedback
.
selectionClick
();
_showContactOptions
(
context
,
crmLists
[
index
].
mob1
);
},
child:
SizedBox
(
height:
35
,
width:
35
,
child:
SvgPicture
.
asset
(
"assets/svg/crm/lead_list_call_ic.svg"
,
),
),
),
),
],
),
],
...
...
@@ -930,6 +928,91 @@ class _LeadlistbymodeState extends State<Leadlistbymode> {
},
);
}
void
_showContactOptions
(
BuildContext
context
,
String
?
phoneNumber
)
{
if
(
phoneNumber
==
null
||
phoneNumber
.
isEmpty
)
{
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
const
SnackBar
(
content:
Text
(
"No phone number available"
)),
);
return
;
}
showModalBottomSheet
(
context:
context
,
shape:
const
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
vertical
(
top:
Radius
.
circular
(
20
)),
),
backgroundColor:
Colors
.
white
,
builder:
(
BuildContext
context
)
{
return
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
vertical:
16
,
horizontal:
20
),
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
children:
[
Container
(
width:
40
,
height:
4
,
decoration:
BoxDecoration
(
color:
Colors
.
grey
.
shade400
,
borderRadius:
BorderRadius
.
circular
(
10
),
),
),
const
SizedBox
(
height:
16
),
const
Text
(
"Contact Options"
,
style:
TextStyle
(
fontSize:
18
,
fontWeight:
FontWeight
.
w600
,
fontFamily:
"JakartaMedium"
,
color:
Colors
.
black
),
),
const
SizedBox
(
height:
20
),
// --- Call Option ---
ListTile
(
leading:
const
Icon
(
Icons
.
phone
,
color:
Colors
.
green
),
title:
const
Text
(
"Call"
,
style:
TextStyle
(
fontSize:
16
,
fontFamily:
"JakartaMedium"
,
)
),
onTap:
()
async
{
Navigator
.
pop
(
context
);
final
uri
=
Uri
.
parse
(
"tel:
$phoneNumber
"
);
await
launchUrl
(
uri
,
mode:
LaunchMode
.
externalApplication
);
},
),
// --- WhatsApp Option ---
ListTile
(
leading:
const
Icon
(
Icons
.
chat
,
color:
Colors
.
teal
),
title:
const
Text
(
"WhatsApp"
,
style:
TextStyle
(
fontSize:
16
,
fontFamily:
"JakartaMedium"
,
)
),
onTap:
()
async
{
Navigator
.
pop
(
context
);
final
message
=
Uri
.
encodeComponent
(
"Hello, I’d like to connect with you."
);
final
whatsappUri
=
Uri
.
parse
(
"https://wa.me/
$phoneNumber
?text=
$message
"
);
if
(
await
canLaunchUrl
(
whatsappUri
))
{
await
launchUrl
(
whatsappUri
,
mode:
LaunchMode
.
externalApplication
);
}
else
{
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
const
SnackBar
(
content:
Text
(
"WhatsApp not installed or invalid number"
)),
);
}
},
),
],
),
);
},
);
}
bool
_isFilterSelected
(
Leadlistprovider
provider
,
int
index
)
{
switch
(
index
)
{
...
...
lib/screens/crm/ProspectDetailsByMode.dart
View file @
dc88a3f9
...
...
@@ -3375,43 +3375,43 @@ class ProspectDetailsByModeState extends State<ProspectDetailsByMode> {
children:
[
Expanded
(
child:
DropdownButton2
<
String
>(
hint:
Text
(
"Select Status"
,
style:
TextStyle
(
fontSize:
14
),
isExpanded:
true
,
hint:
const
Row
(
children:
[
Expanded
(
child:
Text
(
'Select Lead Status'
,
style:
TextStyle
(
fontSize:
14
),
overflow:
TextOverflow
.
ellipsis
,
),
),
],
),
items:
addleadProvider
.
statusList
.
map
(
(
slist
)
=>
DropdownMenuItem
<
String
>(
value:
slist
,
child:
Text
(
slist
,
style:
TextStyle
(
fontSize:
14
,
)
,
),
),
)
.
toList
(),
<
String
>[
'All'
,
'Cold'
,
'Hot'
,
'Warm'
]
.
map
(
(
value
)
=>
DropdownMenuItem
<
String
>(
value:
value
,
child:
Text
(
value
??
''
,
style:
const
TextStyle
(
fontSize:
14
,
)
,
overflow:
TextOverflow
.
ellipsis
,
),
),
)
.
toList
(),
value:
addleadProvider
.
selectedStatus
,
onChanged:
(
String
?
value
)
{
if
(
value
!=
null
)
{
if
(
addleadProvider
.
statusList
.
isNotEmpty
)
{
addleadProvider
.
selectedStatus
=
value
;
}
}
onChanged:
(
String
?
newValue
)
{
setState
(()
{
addleadProvider
.
selectedStatus
=
newValue
!;
});
},
isExpanded:
true
,
buttonStyleData:
ddtheme
.
buttonStyleData
,
iconStyleData:
ddtheme
.
iconStyleData
,
menuItemStyleData:
ddtheme
.
menuItemStyleData
,
dropdownStyleData:
ddtheme
.
dropdownStyleData
,
menuItemStyleData:
ddtheme
.
menuItemStyleData
,
dropdownStyleData:
ddtheme
.
dropdownStyleData
,
),
),
],
...
...
@@ -3421,6 +3421,34 @@ class ProspectDetailsByModeState extends State<ProspectDetailsByMode> {
errorWidget
(
context
,
addleadProvider
.
statusError
),
],
TextWidget
(
context
,
"Remarks"
),
Container
(
margin:
EdgeInsets
.
only
(
bottom:
6
),
decoration:
BoxDecoration
(
color:
AppColors
.
text_field_color
,
borderRadius:
BorderRadius
.
circular
(
14
),
),
child:
TextFormField
(
controller:
addleadProvider
.
addLeadProductRemarksController
,
maxLines:
3
,
enabled:
true
,
style:
TextStyle
(
color:
Colors
.
black
,
fontSize:
14
,
),
decoration:
InputDecoration
(
hintText:
"Enter remark"
,
hintStyle:
TextStyle
(
color:
Colors
.
grey
.
shade500
,
fontSize:
14
,
),
border:
InputBorder
.
none
,
contentPadding:
EdgeInsets
.
symmetric
(
horizontal:
12
,
vertical:
12
),
),
),
),
InkResponse
(
onTap:
addleadProvider
.
submitLoading
...
...
lib/screens/crm/addLeadProductScreen.dart
View file @
dc88a3f9
...
...
@@ -29,7 +29,6 @@ class _AddleadproductscreenState extends State<Addleadproductscreen> {
@override
void
initState
()
{
// TODO: implement initState
super
.
initState
();
_connectivity
.
initialise
();
_connectivity
.
myStream
.
listen
((
source
)
{
...
...
@@ -47,17 +46,15 @@ class _AddleadproductscreenState extends State<Addleadproductscreen> {
provider
.
addProductPriceController
.
clear
();
provider
.
addQuantityController
.
clear
();
provider
.
addTotalAmountController
.
clear
();
provider
.
remarkController
.
clear
();
// Clear remarks too
}
else
{
provider
.
prefillProductForEdit
(
widget
.
editIndex
!);
}
// provider.addEditInitializeForm(context);
});
}
@override
void
dispose
()
{
// TODO: implement dispose
super
.
dispose
();
_connectivity
.
disposeStream
();
}
...
...
@@ -77,15 +74,15 @@ class _AddleadproductscreenState extends State<Addleadproductscreen> {
}
return
(
connection
==
"Online"
)
?
Platform
.
isAndroid
?
WillPopScope
(
onWillPop:
()
=>
onBackPressed
(
context
),
child:
SafeArea
(
top:
false
,
bottom:
true
,
child:
_scaffold
(
context
),
),
)
:
_scaffold
(
context
)
?
WillPopScope
(
onWillPop:
()
=>
onBackPressed
(
context
),
child:
SafeArea
(
top:
false
,
bottom:
true
,
child:
_scaffold
(
context
),
),
)
:
_scaffold
(
context
)
:
NoNetwork
(
context
);
}
...
...
@@ -94,7 +91,6 @@ class _AddleadproductscreenState extends State<Addleadproductscreen> {
builder:
(
context
,
provider
,
child
)
{
return
Scaffold
(
resizeToAvoidBottomInset:
true
,
appBar:
appbarNew
(
context
,
"Generate Quotation"
,
0xFFFFFFFF
),
backgroundColor:
AppColors
.
scaffold_bg_color
,
body:
SingleChildScrollView
(
...
...
@@ -109,7 +105,6 @@ class _AddleadproductscreenState extends State<Addleadproductscreen> {
horizontal:
10
,
vertical:
10
,
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
20
),
...
...
@@ -129,67 +124,55 @@ class _AddleadproductscreenState extends State<Addleadproductscreen> {
style:
TextStyle
(
fontSize:
14
),
overflow:
TextOverflow
.
ellipsis
,
),
items:
provider
.
productsList
.
map
(
(
ord
)
=>
DropdownMenuItem
<
Products
>(
value:
ord
,
child:
Text
(
"
${ord.name}
"
,
style:
const
TextStyle
(
fontSize:
14
,
),
overflow:
TextOverflow
.
ellipsis
,
),
),
)
.
toList
(),
// provider.selectedOrderIds[index] != null?
// provider
// .orderList
// .firstWhere(
// (product) =>
// product
// .orderId ==
// provider
// .selectedOrderIds[index],
// )
value:
provider
.
selectedProducts
!=
null
?
provider
.
productsList
.
firstWhere
(
(
element
)
=>
element
.
id
==
provider
.
selectedProductsId
,
)
:
null
,
items:
provider
.
productsList
.
map
(
(
ord
)
=>
DropdownMenuItem
<
Products
>(
value:
ord
,
child:
Text
(
"
${ord.name}
"
,
style:
const
TextStyle
(
fontSize:
14
,
),
overflow:
TextOverflow
.
ellipsis
,
),
),
)
.
toList
(),
value:
provider
.
selectedProducts
!=
null
?
provider
.
productsList
.
firstWhere
(
(
element
)
=>
element
.
id
==
provider
.
selectedProductsId
,
)
:
null
,
onChanged:
(
Products
?
value
)
{
if
(
value
!=
null
)
{
provider
.
selectedProducts
=
value
;
provider
.
selectedProductsId
=
value
.
id
!;
provider
.
selectedProductsValue
=
value
.
name
;
provider
.
selectedProductsRemark
=
value
.
remarks
;
provider
.
crmSelectedProductDetailsApiFunction
(
context
,
value
.
id
.
toString
(),
);
}
provider
.
crmSelectedProductDetailsApiFunction
(
context
,
value
!.
id
.
toString
(),
);
},
dropdownSearchData:
DropdownSearchData
(
searchInnerWidgetHeight:
50
,
searchController:
provider
.
productSearchController
,
provider
.
productSearchController
,
searchInnerWidget:
Padding
(
padding:
const
EdgeInsets
.
all
(
8
),
child:
TextFormField
(
controller:
provider
.
productSearchController
,
provider
.
productSearchController
,
decoration:
InputDecoration
(
isDense:
true
,
contentPadding:
const
EdgeInsets
.
symmetric
(
horizontal:
10
,
vertical:
8
,
),
const
EdgeInsets
.
symmetric
(
horizontal:
10
,
vertical:
8
,
),
hintText:
'Search Product...'
,
border:
OutlineInputBorder
(
borderRadius:
BorderRadius
.
circular
(
...
...
@@ -201,10 +184,10 @@ class _AddleadproductscreenState extends State<Addleadproductscreen> {
),
searchMatchFn:
(
item
,
searchValue
)
{
return
item
.
value
?.
name
?.
toLowerCase
()
.
contains
(
searchValue
.
toLowerCase
(),
)
??
?.
toLowerCase
()
.
contains
(
searchValue
.
toLowerCase
(),
)
??
false
;
},
),
...
...
@@ -222,7 +205,6 @@ class _AddleadproductscreenState extends State<Addleadproductscreen> {
],
),
),
const
SizedBox
(
height:
10
),
textControllerWidget
(
context
,
...
...
@@ -255,7 +237,7 @@ class _AddleadproductscreenState extends State<Addleadproductscreen> {
provider
.
addTotalAmountController
,
"Amount"
,
"Total Amount"
,
(
_
)
{},
(
_
)
{},
TextInputType
.
number
,
true
,
FilteringTextInputFormatter
.
digitsOnly
,
...
...
@@ -263,54 +245,133 @@ class _AddleadproductscreenState extends State<Addleadproductscreen> {
),
],
),
// IconButton(
// icon: const Icon(Icons.delete),
// onPressed: provider.editProductPriceControllers.length > 1
// ? () => provider.editRemoveRow(j)
// : null,
// ),
TextWidget
(
context
,
"Remarks"
),
Container
(
margin:
EdgeInsets
.
only
(
bottom:
6
),
decoration:
BoxDecoration
(
color:
AppColors
.
text_field_color
,
borderRadius:
BorderRadius
.
circular
(
14
),
),
child:
TextFormField
(
controller:
provider
.
remarkController
,
maxLines:
3
,
enabled:
true
,
style:
TextStyle
(
color:
Colors
.
black
,
fontSize:
14
,
),
decoration:
InputDecoration
(
hintText:
"Enter remark"
,
hintStyle:
TextStyle
(
color:
Colors
.
grey
.
shade500
,
fontSize:
14
,
),
border:
InputBorder
.
none
,
contentPadding:
EdgeInsets
.
symmetric
(
horizontal:
12
,
vertical:
12
),
),
),
)
],
),
),
],
),
),
floatingActionButtonLocation:
FloatingActionButtonLocation
.
centerFloat
,
FloatingActionButtonLocation
.
centerFloat
,
floatingActionButton:
InkWell
(
onTap:
()
{
HapticFeedback
.
selectionClick
();
if
(
provider
.
selectedProducts
!=
null
)
{
final
productData
=
{
"product_id"
:
provider
.
selectedProductsId
!,
"price"
:
provider
.
addProductPriceController
.
text
,
"qty"
:
provider
.
addQuantityController
.
text
,
"net_price"
:
provider
.
addTotalAmountController
.
text
,
};
if
(
widget
.
editIndex
!=
null
)
{
provider
.
updateProduct
(
widget
.
editIndex
!,
productData
);
}
else
{
provider
.
addProduct
(
productData
);
}
print
(
provider
.
getJsonEncodedProducts
());
Navigator
.
pop
(
context
,
provider
.
getJsonEncodedProducts
());
// Validate required fields
if
(
provider
.
selectedProducts
==
null
)
{
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
SnackBar
(
content:
Text
(
"Please select a product"
),
backgroundColor:
Colors
.
red
,
duration:
Duration
(
seconds:
2
),
),
);
return
;
}
if
(
provider
.
addProductPriceController
.
text
.
isEmpty
)
{
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
SnackBar
(
content:
Text
(
"Please enter product price"
),
backgroundColor:
Colors
.
red
,
duration:
Duration
(
seconds:
2
),
),
);
return
;
}
if
(
provider
.
addQuantityController
.
text
.
isEmpty
)
{
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
SnackBar
(
content:
Text
(
"Please enter quantity"
),
backgroundColor:
Colors
.
red
,
duration:
Duration
(
seconds:
2
),
),
);
return
;
}
// Prepare product data - FIX: Use .text for remarks
final
productData
=
{
"product_id"
:
provider
.
selectedProductsId
!,
"price"
:
provider
.
addProductPriceController
.
text
,
"qty"
:
provider
.
addQuantityController
.
text
,
"net_price"
:
provider
.
addTotalAmountController
.
text
,
"remarks"
:
provider
.
remarkController
.
text
,
// FIXED: Use .text
};
if
(
widget
.
editIndex
!=
null
)
{
provider
.
updateProduct
(
widget
.
editIndex
!,
productData
);
print
(
"Product updated at index
${widget.editIndex}
"
);
}
else
{
provider
.
addProduct
(
productData
);
print
(
"New product added"
);
}
print
(
"Product data:
${provider.getJsonEncodedProducts()}
"
);
// Show success message
ScaffoldMessenger
.
of
(
context
).
showSnackBar
(
SnackBar
(
content:
Text
(
widget
.
editIndex
!=
null
?
"Product updated successfully!"
:
"Product added successfully!"
),
backgroundColor:
Colors
.
green
,
duration:
Duration
(
seconds:
2
),
),
);
// Close screen after a short delay to show the success message
Future
.
delayed
(
Duration
(
milliseconds:
1500
),
()
{
if
(
mounted
)
{
Navigator
.
pop
(
context
,
true
);
// Return true to indicate success
}
});
},
child:
Container
(
alignment:
Alignment
.
center
,
height:
45
,
decoration:
BoxDecoration
(
color:
AppColors
.
app_blue
,
//1487C9
color:
AppColors
.
app_blue
,
borderRadius:
BorderRadius
.
circular
(
14.0
),
),
margin:
EdgeInsets
.
symmetric
(
horizontal:
10
),
child:
Center
(
child:
Text
(
"Submi
t"
,
widget
.
editIndex
!=
null
?
"Update Product"
:
"Add Produc
t"
,
textAlign:
TextAlign
.
center
,
style:
TextStyle
(
color:
Colors
.
white
),
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
16
,
fontWeight:
FontWeight
.
w600
,
),
),
),
),
...
...
@@ -319,4 +380,18 @@ class _AddleadproductscreenState extends State<Addleadproductscreen> {
},
);
}
}
Widget
TextWidget
(
context
,
text
)
{
return
Padding
(
padding:
const
EdgeInsets
.
only
(
bottom:
5.0
,
top:
8.0
),
child:
Text
(
text
,
style:
TextStyle
(
fontSize:
14
,
fontWeight:
FontWeight
.
w500
,
color:
AppColors
.
semi_black
,
),
),
);
}
}
\ No newline at end of file
lib/screens/crm/addLeadsProspectsScreen.dart
View file @
dc88a3f9
...
...
@@ -299,27 +299,28 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
),
errorWidget
(
context
,
provider
.
companynameError
),
textControllerWidget
(
context
,
provider
.
contactPersonNameController
,
"Contact Person Name"
,
"Enter Name"
,
provider
.
onChangeContactPersonName
,
TextInputType
.
name
,
false
,
null
,
focusNodes
[
1
],
focusNodes
[
2
],
TextInputAction
.
next
,
),
errorWidget
(
context
,
provider
.
nameError
),
textControllerWidget
(
context
,
provider
.
contactPersonNameController
,
"Contact Person Name"
,
"Enter Name"
,
(
value
)
=>
provider
.
onChangeContactPersonName
(
context
,
value
),
TextInputType
.
name
,
false
,
null
,
focusNodes
[
1
],
focusNodes
[
2
],
TextInputAction
.
next
,
),
errorWidget
(
context
,
provider
.
nameError
),
textControllerWidget
(
context
,
provider
.
mobileController
,
"Mobile Number"
,
"Enter Mobile Number"
,
provider
.
onChangemobile
,
(
value
)
=>
provider
.
onChangemobile
(
context
,
value
)
,
TextInputType
.
phone
,
false
,
FilteringTextInputFormatter
.
digitsOnly
,
...
...
@@ -931,40 +932,36 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
scrollDirection:
Axis
.
horizontal
,
itemCount:
provider
.
productRows
.
length
,
itemBuilder:
(
context
,
index
)
{
final
product
=
provider
.
productRows
[
index
];
final
productName
=
provider
.
productsList
.
firstWhere
(
(
p
)
=>
p
.
id
==
product
[
'product_id'
],
orElse:
()
=>
Products
(
id:
''
,
name:
'Unknown'
,
),
)
.
name
;
final
product
=
provider
.
productRows
[
index
];
final
productName
=
provider
.
productsList
.
firstWhere
(
(
p
)
=>
p
.
id
==
product
[
'product_id'
],
orElse:
()
=>
Products
(
id:
''
,
name:
'Unknown'
,
),
)
.
name
;
final
prodPrice
=
product
[
'price'
]
??
'-'
;
final
prodQty
=
product
[
'qty'
]
??
'-'
;
final
totalPrice
=
product
[
'net_price'
]
??
'-'
;
final
totalPrice
=
product
[
'net_price'
]
??
'-'
;
// FIX: Get the text from TextEditingController, not the controller itself
final
remark
=
product
[
'remarks'
]
is
TextEditingController
?
(
product
[
'remarks'
]
as
TextEditingController
).
text
:
product
[
'remarks'
]?.
toString
()
??
''
;
return
InkResponse
(
onTap:
()
async
{
var
res
=
await
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
Addleadproductscreen
(
type:
"Edit"
,
editIndex:
index
,
),
builder:
(
context
)
=>
Addleadproductscreen
(
type:
"Edit"
,
editIndex:
index
,
),
settings:
RouteSettings
(
name:
'Generatequotationaddeditproduct'
,
name:
'Generatequotationaddeditproduct'
,
),
),
);
...
...
@@ -973,21 +970,10 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
}
},
child:
Container
(
width:
MediaQuery
.
of
(
context
,
).
size
.
width
*
0.8
,
width:
MediaQuery
.
of
(
context
).
size
.
width
*
0.8
,
margin:
EdgeInsets
.
only
(
left:
index
==
0
?
10
:
5
,
right:
index
==
provider
.
productRows
.
length
-
1
?
10
:
5
,
right:
index
==
provider
.
productRows
.
length
-
1
?
10
:
5
,
bottom:
5
,
),
padding:
EdgeInsets
.
symmetric
(
...
...
@@ -996,15 +982,11 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
),
decoration:
BoxDecoration
(
color:
Color
(
0xFFE6F6FF
),
borderRadius:
BorderRadius
.
circular
(
14
,
),
borderRadius:
BorderRadius
.
circular
(
14
),
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
start
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
mainAxisAlignment:
MainAxisAlignment
.
start
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Expanded
(
flex:
1
,
...
...
@@ -1016,10 +998,8 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
Expanded
(
flex:
6
,
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
mainAxisAlignment:
MainAxisAlignment
.
start
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
mainAxisAlignment:
MainAxisAlignment
.
start
,
children:
[
Row
(
children:
[
...
...
@@ -1028,32 +1008,23 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
child:
Text
(
productName
??
"-"
,
maxLines:
2
,
overflow:
TextOverflow
.
ellipsis
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
fontFamily:
"JakartaMedium"
,
fontFamily:
"JakartaMedium"
,
fontSize:
14
,
color:
AppColors
.
semi_black
,
color:
AppColors
.
semi_black
,
),
),
),
Expanded
(
flex:
3
,
child:
Text
(
textAlign:
TextAlign
.
right
,
textAlign:
TextAlign
.
right
,
"₹
$prodPrice
"
,
style:
TextStyle
(
fontFamily:
"JakartaMedium"
,
fontFamily:
"JakartaMedium"
,
fontSize:
14
,
color:
AppColors
.
semi_black
,
color:
AppColors
.
semi_black
,
),
),
),
...
...
@@ -1062,42 +1033,51 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
Text
(
"x
$prodQty
"
,
style:
TextStyle
(
fontFamily:
"JakartaMedium"
,
fontFamily:
"JakartaMedium"
,
fontSize:
14
,
color:
AppColors
.
grey_semi
,
color:
AppColors
.
grey_semi
,
),
),
SizedBox
(
height:
5
),
DottedLine
(
dashGapLength:
4
,
dashGapColor:
Colors
.
white
,
dashColor:
AppColors
.
grey_semi
,
dashGapColor:
Colors
.
white
,
dashColor:
AppColors
.
grey_semi
,
dashLength:
2
,
lineThickness:
0.5
,
),
SizedBox
(
height:
5
),
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
Text
(
"₹
$totalPrice
"
,
style:
TextStyle
(
fontFamily:
"JakartaMedium"
,
fontFamily:
"JakartaMedium"
,
fontSize:
14
,
color:
AppColors
.
semi_black
,
color:
AppColors
.
semi_black
,
),
),
],
),
SizedBox
(
height:
5
),
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
Expanded
(
child:
Text
(
remark
,
// Now this is a String, not a TextEditingController
style:
TextStyle
(
fontFamily:
"JakartaMedium"
,
fontSize:
14
,
color:
AppColors
.
semi_black
,
),
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
),
),
],
)
],
),
),
...
...
lib/screens/crm/followUpUpdateScreen.dart
View file @
dc88a3f9
...
...
@@ -11,6 +11,8 @@ import 'package:generp/Utils/commonWidgets.dart';
import
'package:generp/Utils/dropdownTheme.dart'
;
import
'package:provider/provider.dart'
;
import
'../order/ordersListByModes.dart'
;
class
Followupupdatescreen
extends
StatefulWidget
{
final
leadID
;
final
mode
;
...
...
@@ -391,7 +393,7 @@ class _FollowupupdatescreenState extends State<Followupupdatescreen> {
),
items:
<
String
>[
//
'Order Gain',
'Order Gain'
,
'Order Lost'
,
'No Requirement'
,
]
...
...
@@ -709,6 +711,9 @@ class _FollowupupdatescreenState extends State<Followupupdatescreen> {
provider
.
selectedCompetitor
,
provider
.
selectedLeadStatus
,
provider
.
selectNextAppointmentType
,
provider
.
followUpFeedbackController
,
provider
.
selectedTime
,
provider
.
currentLocationLatLng
,
provider
.
smsSent
,
widget
.
mode
,
);
...
...
lib/screens/crm/generateQuotationScreen.dart
View file @
dc88a3f9
This diff is collapsed.
Click to expand it.
lib/screens/finance/AllPaymentRequesitionListsByModes.dart
View file @
dc88a3f9
This diff is collapsed.
Click to expand it.
lib/screens/finance/FileViewer.dart
View file @
dc88a3f9
...
...
@@ -10,6 +10,7 @@ import 'package:url_launcher/url_launcher.dart';
import
'package:flutter_pdfview/flutter_pdfview.dart'
;
import
'package:http/http.dart'
as
http
;
import
'dart:typed_data'
;
import
'package:photo_view/photo_view.dart'
;
import
'../../Utils/app_colors.dart'
;
...
...
@@ -24,7 +25,7 @@ class Fileviewer extends StatefulWidget {
class
_FileviewerState
extends
State
<
Fileviewer
>
{
final
Completer
<
InAppWebViewController
>
_controller
=
Completer
<
InAppWebViewController
>();
Completer
<
InAppWebViewController
>();
var
empId
=
""
;
var
sessionId
=
""
;
bool
isLoading
=
true
;
...
...
@@ -36,6 +37,11 @@ class _FileviewerState extends State<Fileviewer> {
bool
pullToRefreshEnabled
=
true
;
final
GlobalKey
webViewKey
=
GlobalKey
();
// Zoom control variables
PhotoViewController
_photoViewController
=
PhotoViewController
();
PhotoViewScaleStateController
_scaleStateController
=
PhotoViewScaleStateController
();
String
getFileExtension
(
String
fileName
)
{
print
(
widget
.
fileUrl
);
return
fileName
.
split
(
'.'
).
last
.
toLowerCase
();
...
...
@@ -51,36 +57,52 @@ class _FileviewerState extends State<Fileviewer> {
}
var
Finalurl
;
@override
void
initState
()
{
// loadData();
pullToRefreshController
=
kIsWeb
?
null
:
PullToRefreshController
(
settings:
pullToRefreshSettings
,
onRefresh:
()
async
{
if
(
defaultTargetPlatform
==
TargetPlatform
.
android
)
{
webViewController
?.
reload
();
}
else
if
(
defaultTargetPlatform
==
TargetPlatform
.
iOS
)
{
webViewController
?.
loadUrl
(
urlRequest:
URLRequest
(
url:
await
webViewController
?.
getUrl
(),
),
);
}
},
);
// print("URL:${widget.url}");
kIsWeb
?
null
:
PullToRefreshController
(
settings:
pullToRefreshSettings
,
onRefresh:
()
async
{
if
(
defaultTargetPlatform
==
TargetPlatform
.
android
)
{
webViewController
?.
reload
();
}
else
if
(
defaultTargetPlatform
==
TargetPlatform
.
iOS
)
{
webViewController
?.
loadUrl
(
urlRequest:
URLRequest
(
url:
await
webViewController
?.
getUrl
(),
),
);
}
},
);
// Initialize photo view controllers
_photoViewController
=
PhotoViewController
();
_scaleStateController
=
PhotoViewScaleStateController
();
super
.
initState
();
}
@override
void
dispose
()
{
_photoViewController
.
dispose
();
_scaleStateController
.
dispose
();
pullToRefreshController
?.
dispose
();
super
.
dispose
();
}
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
resizeToAvoidBottomInset:
true
,
appBar:
appbarNew
(
context
,
"File Viewer"
,
0xFFFFFFFF
),
body:
SafeArea
(
child:
Center
(
child:
fileWidget
(
context
))),
body:
SafeArea
(
child:
Center
(
child:
fileWidget
(
context
)
),
),
);
}
...
...
@@ -91,28 +113,91 @@ class _FileviewerState extends State<Fileviewer> {
case
'jpeg'
:
case
'png'
:
case
'gif'
:
return
CachedNetworkImage
(
imageUrl:
widget
.
fileUrl
,
placeholder:
(
context
,
url
)
=>
const
Center
(
child:
CircularProgressIndicator
()),
errorWidget:
(
context
,
url
,
error
)
=>
const
Icon
(
Icons
.
error
),
fit:
BoxFit
.
contain
,
);
case
'bmp'
:
case
'webp'
:
return
_buildImageViewer
();
case
'pdf'
:
return
Sf
PdfViewer
.
network
(
widget
.
fileUrl
,
key:
GlobalKey
()
);
return
_build
PdfViewer
(
);
case
'doc'
:
case
'docx'
:
case
'xls'
:
case
'xlsx'
:
return
InAppWebView
(
case
'ppt'
:
case
'pptx'
:
return
_buildDocumentViewer
();
default
:
return
_buildUnsupportedViewer
();
}
}
Widget
_buildImageViewer
()
{
return
PhotoView
(
imageProvider:
CachedNetworkImageProvider
(
widget
.
fileUrl
),
loadingBuilder:
(
context
,
event
)
=>
Center
(
child:
Container
(
width:
40
,
height:
40
,
child:
CircularProgressIndicator
(
value:
event
==
null
?
0
:
event
.
cumulativeBytesLoaded
/
(
event
.
expectedTotalBytes
??
1
),
),
),
),
errorBuilder:
(
context
,
error
,
stackTrace
)
=>
Center
(
child:
Column
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
Icon
(
Icons
.
error_outline
,
color:
Colors
.
red
,
size:
50
),
SizedBox
(
height:
10
),
Text
(
'Failed to load image'
,
style:
TextStyle
(
fontSize:
16
,
color:
Colors
.
grey
),
),
],
),
),
backgroundDecoration:
BoxDecoration
(
color:
Colors
.
white
),
minScale:
PhotoViewComputedScale
.
contained
*
0.5
,
maxScale:
PhotoViewComputedScale
.
covered
*
4.0
,
initialScale:
PhotoViewComputedScale
.
contained
,
basePosition:
Alignment
.
center
,
scaleStateController:
_scaleStateController
,
controller:
_photoViewController
,
enableRotation:
true
,
gestureDetectorBehavior:
HitTestBehavior
.
deferToChild
,
filterQuality:
FilterQuality
.
high
,
);
}
Widget
_buildPdfViewer
()
{
return
SfPdfViewer
.
network
(
widget
.
fileUrl
,
key:
GlobalKey
(),
canShowScrollHead:
true
,
canShowPaginationDialog:
true
,
pageLayoutMode:
PdfPageLayoutMode
.
single
,
interactionMode:
PdfInteractionMode
.
pan
,
enableDoubleTapZooming:
true
,
enableTextSelection:
true
,
onZoomLevelChanged:
(
PdfZoomDetails
details
)
{
// Use the correct property name
//print('Zoom level changed: ${details.zoomLevel}');
},
);
}
Widget
_buildDocumentViewer
()
{
return
Stack
(
children:
[
InAppWebView
(
key:
webViewKey
,
initialUrlRequest:
URLRequest
(
url:
WebUri
(
widget
.
fileUrl
)),
androidOnGeolocationPermissionsShowPrompt:
(
InAppWebViewController
controller
,
String
origin
,
)
async
{
InAppWebViewController
controller
,
String
origin
,
)
async
{
return
GeolocationPermissionShowPromptResponse
(
origin:
origin
,
allow:
true
,
...
...
@@ -120,27 +205,41 @@ class _FileviewerState extends State<Fileviewer> {
);
},
initialOptions:
InAppWebViewGroupOptions
(
crossPlatform:
InAppWebViewOptions
(
useShouldOverrideUrlLoading:
true
,
mediaPlaybackRequiresUserGesture:
false
,
javaScriptEnabled:
true
,
clearCache:
true
,
supportZoom:
true
,
),
android:
AndroidInAppWebViewOptions
(
useWideViewPort:
true
,
loadWithOverviewMode:
true
,
allowContentAccess:
true
,
geolocationEnabled:
true
,
allowFileAccess:
true
,
databaseEnabled:
true
,
// Enables the WebView database
domStorageEnabled:
true
,
// Enables DOM storage
builtInZoomControls:
true
,
// Enables the built-in zoom controls
displayZoomControls:
false
,
// Disables displaying zoom controls
safeBrowsingEnabled:
true
,
// Enables Safe Browsing
databaseEnabled:
true
,
domStorageEnabled:
true
,
builtInZoomControls:
true
,
displayZoomControls:
false
,
safeBrowsingEnabled:
true
,
clearSessionCache:
true
,
supportMultipleWindows:
false
,
),
ios:
IOSInAppWebViewOptions
(
allowsInlineMediaPlayback:
true
,
allowsAirPlayForMediaPlayback:
true
,
allowsPictureInPictureMediaPlayback:
true
,
allowsBackForwardNavigationGestures:
true
,
allowsLinkPreview:
true
,
isFraudulentWebsiteWarningEnabled:
true
,
),
ios:
IOSInAppWebViewOptions
(
allowsInlineMediaPlayback:
true
),
),
androidOnPermissionRequest:
(
InAppWebViewController
controller
,
String
origin
,
List
<
String
>
resources
,
)
async
{
InAppWebViewController
controller
,
String
origin
,
List
<
String
>
resources
,
)
async
{
return
PermissionRequestResponse
(
resources:
resources
,
action:
PermissionRequestResponseAction
.
GRANT
,
...
...
@@ -152,18 +251,29 @@ class _FileviewerState extends State<Fileviewer> {
},
pullToRefreshController:
pullToRefreshController
,
onLoadStart:
(
controller
,
url
)
{
return
setState
(()
{
setState
(()
{
isLoading
=
true
;
});
},
onLoadStop:
(
controller
,
url
)
{
pullToRefreshController
?.
endRefreshing
();
return
setState
(()
{
setState
(()
{
isLoading
=
false
;
});
// Enable zooming in WebView
controller
.
evaluateJavascript
(
source
:
"""
var meta = document.createElement('meta');
meta.name = 'viewport';
meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=4.0, user-scalable=yes';
document.getElementsByTagName('head')[0].appendChild(meta);
"""
);
},
onReceivedError:
(
controller
,
request
,
error
)
{
pullToRefreshController
?.
endRefreshing
();
setState
(()
{
isLoading
=
false
;
});
},
onProgressChanged:
(
controller
,
progress
)
{
if
(
progress
==
100
)
{
...
...
@@ -172,21 +282,101 @@ class _FileviewerState extends State<Fileviewer> {
},
onConsoleMessage:
(
controller
,
consoleMessage
)
{
if
(
kDebugMode
)
{
debugPrint
(
"consoleMessage
$
consoleMessage
"
);
debugPrint
(
"consoleMessage
:
${
consoleMessage
.message}
"
);
}
debugPrint
(
"JavaScript console message:
${consoleMessage.message}
"
);
},
);
default
:
return
Container
();
}
),
// Loading indicator for documents
if
(
isLoading
)
Positioned
.
fill
(
child:
Container
(
color:
Colors
.
black
.
withOpacity
(
0.3
),
child:
Center
(
child:
Container
(
padding:
EdgeInsets
.
all
(
20
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
10
),
boxShadow:
[
BoxShadow
(
color:
Colors
.
black26
,
blurRadius:
10
,
),
],
),
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
children:
[
CircularProgressIndicator
(
valueColor:
AlwaysStoppedAnimation
<
Color
>(
AppColors
.
app_blue
),
),
SizedBox
(
height:
10
),
Text
(
'Loading Document...'
,
style:
TextStyle
(
fontSize:
14
,
color:
Colors
.
grey
[
700
],
),
),
],
),
),
),
),
),
],
);
}
Widget
_buildUnsupportedViewer
()
{
return
Center
(
child:
Column
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
Icon
(
Icons
.
insert_drive_file
,
size:
64
,
color:
Colors
.
grey
[
400
],
),
SizedBox
(
height:
16
),
Text
(
'Unsupported File Format'
,
style:
TextStyle
(
fontSize:
18
,
fontWeight:
FontWeight
.
w600
,
color:
Colors
.
grey
[
600
],
),
),
SizedBox
(
height:
8
),
Text
(
'Format:
${getFileExtension(widget.fileName).toUpperCase()}
'
,
style:
TextStyle
(
fontSize:
14
,
color:
Colors
.
grey
[
500
],
),
),
SizedBox
(
height:
16
),
ElevatedButton
.
icon
(
onPressed:
()
{
_launchUrl
(
widget
.
fileUrl
);
},
icon:
Icon
(
Icons
.
open_in_new
),
label:
Text
(
'Open in External App'
),
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
AppColors
.
app_blue
,
foregroundColor:
Colors
.
white
,
),
),
],
),
);
}
Future
<
Uint8List
?>
_loadPdf
(
String
url
)
async
{
try
{
final
response
=
await
http
.
get
(
Uri
.
parse
(
url
));
if
(
response
.
statusCode
==
200
)
{
print
(
response
.
bodyBytes
);
return
response
.
bodyBytes
;
}
}
catch
(
e
)
{
...
...
@@ -194,4 +384,4 @@ class _FileviewerState extends State<Fileviewer> {
}
return
null
;
}
}
}
\ No newline at end of file
lib/screens/finance/PaymentRequestionListDetails.dart
View file @
dc88a3f9
...
...
@@ -1360,14 +1360,14 @@ class _PaymentrequestionlistdetailsState
context
,
provider
.
requestedAmount
,
"Enter Requested Amount"
,
(
p0
)
{},
(
p0
)
{},
),
textControllerWidget
(
context
,
provider
.
approvedAmount
,
"Approved Amount"
,
"Enter Approved Amount"
,
(
p0
)
{
(
p0
)
{
provider
.
onChangeApprov
(
p0
);
},
TextInputType
.
numberWithOptions
(),
...
...
@@ -1383,7 +1383,7 @@ class _PaymentrequestionlistdetailsState
remarks
,
"Remarks"
,
"Enter Remarks"
,
(
p0
)
{
(
p0
)
{
provider
.
remarksError
=
null
;
provider
.
notifyListeners
();
},
...
...
@@ -1408,30 +1408,30 @@ class _PaymentrequestionlistdetailsState
overflow:
TextOverflow
.
ellipsis
,
),
items:
provider
.
paymentsAccounts
.
map
(
(
paymenents
)
=>
DropdownMenuItem
<
PaymentAccounts
>(
value:
paymenents
,
child:
Text
(
paymenents
.
name
??
''
,
style:
const
TextStyle
(
fontSize:
14
,
),
overflow:
TextOverflow
.
ellipsis
,
),
),
)
.
toList
(),
provider
.
paymentsAccounts
.
map
(
(
paymenents
)
=>
DropdownMenuItem
<
PaymentAccounts
>(
value:
paymenents
,
child:
Text
(
paymenents
.
name
??
''
,
style:
const
TextStyle
(
fontSize:
14
,
),
overflow:
TextOverflow
.
ellipsis
,
),
),
)
.
toList
(),
value:
provider
.
paymentsAccounts
.
contains
(
provider
.
selectedPaymentAccounts
,
)
?
provider
.
selectedPaymentAccounts
:
null
,
provider
.
paymentsAccounts
.
contains
(
provider
.
selectedPaymentAccounts
,
)
?
provider
.
selectedPaymentAccounts
:
null
,
// value: provider.selectedPaymentAccounts,
onChanged:
(
PaymentAccounts
?
value
)
{
if
(
value
!=
null
)
{
...
...
@@ -1446,7 +1446,8 @@ class _PaymentrequestionlistdetailsState
provider
.
selectedID
=
value
.
id
!;
provider
.
selectedValue
=
value
.
name
!;
print
(
"hfjkshfg
${provider.selectedID}
"
,
"hfjkshfg"
+
provider
.
selectedID
.
toString
(),
);
}
}
...
...
@@ -1454,35 +1455,35 @@ class _PaymentrequestionlistdetailsState
dropdownSearchData:
DropdownSearchData
(
searchInnerWidgetHeight:
50
,
searchController:
provider
.
paymentAccountSearchController
,
provider
.
paymentAccountSearchController
,
searchInnerWidget:
Padding
(
padding:
const
EdgeInsets
.
all
(
8
),
child:
TextFormField
(
controller:
provider
.
paymentAccountSearchController
,
provider
.
paymentAccountSearchController
,
decoration:
InputDecoration
(
isDense:
true
,
contentPadding:
const
EdgeInsets
.
symmetric
(
horizontal:
10
,
vertical:
8
,
),
const
EdgeInsets
.
symmetric
(
horizontal:
10
,
vertical:
8
,
),
hintText:
'Search account...'
,
border:
OutlineInputBorder
(
borderRadius:
BorderRadius
.
circular
(
8
),
BorderRadius
.
circular
(
8
),
),
),
),
),
searchMatchFn:
(
item
,
searchValue
)
{
return
item
.
value
?.
name
?.
toLowerCase
()
.
contains
(
searchValue
.
toLowerCase
(),
)
??
?.
toLowerCase
()
.
contains
(
searchValue
.
toLowerCase
(),
)
??
false
;
},
...
...
@@ -1497,9 +1498,9 @@ class _PaymentrequestionlistdetailsState
buttonStyleData:
ddtheme
.
buttonStyleData
,
iconStyleData:
ddtheme
.
iconStyleData
,
menuItemStyleData:
ddtheme
.
menuItemStyleData
,
ddtheme
.
menuItemStyleData
,
dropdownStyleData:
ddtheme
.
dropdownStyleData
,
ddtheme
.
dropdownStyleData
,
),
),
],
...
...
@@ -1511,15 +1512,21 @@ class _PaymentrequestionlistdetailsState
),
InkWell
(
onTap:
()
{
print
(
"🖱️ === SUBMIT BUTTON TAPPED ==="
);
print
(
"📋 Mode:
${widget.mode}
"
);
print
(
"📋 Payment ID:
$paymentID
"
);
print
(
"📋 Approved Amount:
${provider.approvedAmount.text}
"
);
print
(
"📋 Remarks:
${remarks.text}
"
);
print
(
"📋 Selected Account ID:
${provider.selectedID}
"
);
provider
.
paymentrequisitionApproveSubmitAPIFunction
(
context
,
widget
.
mode
,
paymentID
,
provider
.
approvedAmount
.
text
,
remarks
.
text
,
provider
.
selectedID
,
);
context
,
widget
.
mode
,
paymentID
,
provider
.
approvedAmount
.
text
,
remarks
.
text
,
provider
.
selectedID
,
);
},
child:
Container
(
alignment:
Alignment
.
center
,
...
...
lib/screens/hrm/AdvanceListScreen.dart
0 → 100644
View file @
dc88a3f9
import
'package:flutter/material.dart'
;
import
'package:provider/provider.dart'
;
import
'../../Notifiers/HomeScreenNotifier.dart'
;
import
'../../Models/hrmModels/advanceListResponse.dart'
;
import
'package:intl/intl.dart'
;
import
'package:flutter_svg/svg.dart'
;
import
'../../Notifiers/hrmProvider/advanceProvider.dart'
;
import
'../../Utils/app_colors.dart'
;
class
AdvanceListScreen
extends
StatefulWidget
{
const
AdvanceListScreen
({
super
.
key
});
@override
State
<
AdvanceListScreen
>
createState
()
=>
_AdvanceListScreenState
();
}
class
_AdvanceListScreenState
extends
State
<
AdvanceListScreen
>
{
final
ScrollController
_scrollController
=
ScrollController
();
@override
void
initState
()
{
super
.
initState
();
final
provider
=
Provider
.
of
<
AdvanceListProvider
>(
context
,
listen:
false
);
final
homeProvider
=
Provider
.
of
<
HomescreenNotifier
>(
context
,
listen:
false
);
WidgetsBinding
.
instance
.
addPostFrameCallback
((
_
)
{
provider
.
fetchAdvanceList
(
context
,
homeProvider
.
session
,
homeProvider
.
empId
);
});
_scrollController
.
addListener
(()
{
if
(
_scrollController
.
position
.
pixels
>=
_scrollController
.
position
.
maxScrollExtent
-
200
)
{
provider
.
loadMoreAdvanceList
(
context
,
homeProvider
.
session
,
homeProvider
.
empId
);
}
});
}
@override
Widget
build
(
BuildContext
context
)
{
final
double
screenWidth
=
MediaQuery
.
of
(
context
).
size
.
width
;
final
double
screenHeight
=
MediaQuery
.
of
(
context
).
size
.
height
;
final
bool
isSmallScreen
=
screenWidth
<
360
;
final
bool
isLargeScreen
=
screenWidth
>
600
;
return
Scaffold
(
appBar:
AppBar
(
automaticallyImplyLeading:
false
,
backgroundColor:
Colors
.
white
,
title:
Row
(
children:
[
InkResponse
(
onTap:
()
=>
Navigator
.
pop
(
context
,
true
),
child:
SvgPicture
.
asset
(
"assets/svg/appbar_back_button.svg"
,
height:
isSmallScreen
?
22
:
25
,
),
),
SizedBox
(
width:
isSmallScreen
?
8
:
10
),
Text
(
"Advance List"
,
style:
TextStyle
(
fontSize:
isSmallScreen
?
16
:
18
,
fontFamily:
"Plus Jakarta Sans"
,
fontWeight:
FontWeight
.
w600
,
color:
Colors
.
black87
,
),
),
],
),
),
backgroundColor:
AppColors
.
scaffold_bg_color
,
body:
Consumer
<
AdvanceListProvider
>(
builder:
(
context
,
provider
,
_
)
{
if
(
provider
.
isLoading
&&
provider
.
advanceList
.
isEmpty
)
{
return
const
Center
(
child:
CircularProgressIndicator
());
}
if
(
provider
.
errorMessage
!=
null
)
{
return
Padding
(
padding:
EdgeInsets
.
symmetric
(
horizontal:
isSmallScreen
?
16
:
20
),
child:
Center
(
child:
Text
(
provider
.
errorMessage
!,
style:
TextStyle
(
color:
Colors
.
red
,
fontSize:
isSmallScreen
?
14
:
16
,
fontFamily:
"Plus Jakarta Sans"
,
),
textAlign:
TextAlign
.
center
,
),
),
);
}
if
(
provider
.
advanceList
.
isEmpty
)
{
return
Padding
(
padding:
EdgeInsets
.
symmetric
(
horizontal:
isSmallScreen
?
16
:
20
),
child:
Center
(
child:
Text
(
"No records found."
,
style:
TextStyle
(
fontSize:
isSmallScreen
?
14
:
16
,
fontFamily:
"Plus Jakarta Sans"
,
color:
AppColors
.
grey_semi
,
),
),
),
);
}
return
ListView
.
builder
(
controller:
_scrollController
,
padding:
EdgeInsets
.
all
(
isSmallScreen
?
8
:
isLargeScreen
?
16
:
12
),
itemCount:
provider
.
advanceList
.
length
+
(
provider
.
isLoading
?
1
:
0
),
// for pagination loader
itemBuilder:
(
context
,
index
)
{
if
(
index
==
provider
.
advanceList
.
length
)
{
return
Padding
(
padding:
EdgeInsets
.
all
(
isSmallScreen
?
12.0
:
16.0
),
child:
const
Center
(
child:
CircularProgressIndicator
()),
);
}
AdvanceList
item
=
provider
.
advanceList
[
index
];
return
_buildAdvanceCard
(
item
,
screenWidth
);
},
);
},
),
);
}
Widget
_buildAdvanceCard
(
AdvanceList
item
,
double
screenWidth
)
{
final
bool
isSmallScreen
=
screenWidth
<
360
;
final
bool
isLargeScreen
=
screenWidth
>
600
;
final
date
=
item
.
createdDatetime
!=
null
?
_formatDate
(
item
.
createdDatetime
!)
:
"-"
;
return
Card
(
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
isSmallScreen
?
12
:
16
)),
margin:
EdgeInsets
.
symmetric
(
vertical:
isSmallScreen
?
5
:
7
,
horizontal:
isLargeScreen
?
4
:
0
,
),
elevation:
0
,
child:
Padding
(
padding:
EdgeInsets
.
symmetric
(
horizontal:
isSmallScreen
?
12
:
isLargeScreen
?
18
:
14
,
vertical:
isSmallScreen
?
10
:
isLargeScreen
?
14
:
12
,
),
child:
Row
(
children:
[
// Circular Avatar - Responsive size
CircleAvatar
(
backgroundColor:
_getAvatarColor
(
item
.
type
),
radius:
isSmallScreen
?
18
:
isLargeScreen
?
26
:
22
,
child:
Text
(
(
item
.
narration
?.
isNotEmpty
==
true
)
?
item
.
type
![
0
].
toUpperCase
()
:
"?"
,
style:
TextStyle
(
color:
_getAvatarTxtColor
(
item
.
type
),
fontWeight:
FontWeight
.
bold
,
fontSize:
isSmallScreen
?
12
:
isLargeScreen
?
16
:
14
,
),
),
),
SizedBox
(
width:
isSmallScreen
?
12
:
isLargeScreen
?
16
:
14
),
// Title + Subtitle
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
item
.
narration
??
"No Title"
,
style:
TextStyle
(
fontFamily:
"JakartaRegular"
,
fontSize:
isSmallScreen
?
13
:
isLargeScreen
?
16
:
14
,
color:
AppColors
.
semi_black
,
),
maxLines:
2
,
overflow:
TextOverflow
.
ellipsis
,
),
SizedBox
(
height:
isSmallScreen
?
2
:
4
),
Text
(
date
,
style:
TextStyle
(
fontFamily:
"JakartaRegular"
,
fontSize:
isSmallScreen
?
12
:
isLargeScreen
?
14
:
13
,
color:
AppColors
.
grey_semi
,
),
),
],
),
),
SizedBox
(
width:
isSmallScreen
?
8
:
isLargeScreen
?
12
:
10
),
// Right side amounts
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
end
,
children:
[
if
(
item
.
issuedAmount
!=
"₹ 0.00"
)
Text
(
"
${item.issuedAmount ?? "0"}
"
,
style:
TextStyle
(
fontFamily:
"JakartaMedium"
,
fontSize:
isSmallScreen
?
13
:
isLargeScreen
?
16
:
14
,
color:
const
Color
(
0xff1487c9
),
),
textAlign:
TextAlign
.
end
,
),
if
(
item
.
deductedAmount
!=
"₹ 0.00"
)
SizedBox
(
height:
isSmallScreen
?
1
:
2
),
if
(
item
.
deductedAmount
!=
"₹ 0.00"
)
Text
(
"-
${item.deductedAmount ?? "0"}
"
,
style:
TextStyle
(
fontFamily:
"JakartaMedium"
,
fontSize:
isSmallScreen
?
13
:
isLargeScreen
?
16
:
14
,
color:
AppColors
.
red
,
),
textAlign:
TextAlign
.
end
,
),
],
),
],
),
),
);
}
String
_formatDate
(
String
dateStr
)
{
try
{
final
date
=
DateTime
.
parse
(
dateStr
);
return
DateFormat
(
"dd MMM yy"
).
format
(
date
);
}
catch
(
_
)
{
return
dateStr
;
}
}
@override
void
dispose
()
{
_scrollController
.
dispose
();
super
.
dispose
();
}
/// Avatar color generator
Color
_getAvatarColor
(
value
)
{
var
color
=
AppColors
.
approved_bg_color
;
switch
(
value
)
{
case
'issued'
:
return
AppColors
.
requested_bg_color
;
case
'received'
:
return
AppColors
.
approved_bg_color
;
}
return
color
;
}
Color
_getAvatarTxtColor
(
value
)
{
var
color
=
AppColors
.
approved_text_color
;
switch
(
value
)
{
case
'issued'
:
return
AppColors
.
requested_text_color
;
case
'received'
:
return
AppColors
.
approved_text_color
;
}
return
color
;
}
String
getText
(
value
)
{
switch
(
value
)
{
case
'issued'
:
return
"I"
;
case
'received'
:
return
"R"
;
default
:
return
"-"
;
}
}
}
\ No newline at end of file
lib/screens/hrm/CasualLeaveHistoryScreen.dart
0 → 100644
View file @
dc88a3f9
This diff is collapsed.
Click to expand it.
lib/screens/hrm/HrmDashboardScreen.dart
View file @
dc88a3f9
...
...
@@ -3,6 +3,8 @@ import 'package:flutter_svg/svg.dart';
import
'package:generp/screens/hrm/Attendancelist.dart'
;
import
'package:provider/provider.dart'
;
import
'../../Utils/app_colors.dart'
;
import
'AdvanceListScreen.dart'
;
import
'CasualLeaveHistoryScreen.dart'
;
import
'LeaveApplicationScreen.dart'
;
import
'TourExpensesListScreen.dart'
;
import
'RewardListScreen.dart'
;
...
...
@@ -24,6 +26,8 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
"Tour Bill List"
,
"Rewards List"
,
"Attendance Request List"
,
"Advance List"
,
"Casual Leave List"
];
@override
...
...
@@ -215,7 +219,7 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
crossAxisCount:
(
constraints
.
maxWidth
/
180
)
.
floor
()
.
clamp
(
2
,
4
),
.
clamp
(
2
,
6
),
crossAxisSpacing:
1
,
mainAxisSpacing:
2
,
childAspectRatio:
1.8
,
...
...
@@ -224,7 +228,7 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
final
page
=
pages
[
index
];
return
_buildTile
(
label:
page
.
pageName
??
""
,
label:
page
.
pageName
??
""
,
//in page number there is 6 items comming from serever it showing only four
subtitle:
_getSubtitle
(
page
.
pageName
??
""
,
),
...
...
@@ -244,6 +248,11 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
);
},
),
////////////
SizedBox
(
height:
40
,)
],
),
],
...
...
@@ -355,6 +364,10 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
return
""
;
case
"Team Attendance Approval"
:
return
""
;
case
"Advance List"
:
return
"Advance Payment"
;
case
"Casual Leave List"
:
return
"Track Casual Leave"
;
default
:
return
""
;
}
...
...
@@ -375,6 +388,10 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
return
"assets/svg/hrm/logout_ic.svg"
;
case
"Team Attendance Approval"
:
return
"assets/svg/hrm/check_ic.svg"
;
case
"Advance List"
:
return
"assets/svg/hrm/advance_list_ic.svg"
;
case
"Casual Leave List"
:
return
"assets/svg/hrm/casual_leave_history_ic.svg"
;
default
:
return
"assets/svg/hrm/groupIc.svg"
;
}
...
...
@@ -432,6 +449,24 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
),
);
break
;
case
"Advance List"
:
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
AdvanceListScreen
(),
),
);
break
;
case
"Casual Leave List"
:
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
CasualLeaveHistoryScreen
(),
),
);
break
;
}
}
}
lib/screens/notifierExports.dart
View file @
dc88a3f9
...
...
@@ -65,6 +65,6 @@ export 'package:generp/Notifiers/hrmProvider/tourExpensesDetailsProvider.dart';
export
'package:generp/Notifiers/hrmProvider/rewardListProvider.dart'
;
export
'package:generp/Notifiers/hrmProvider/LeaveApplicationListProvider.dart'
;
export
'package:generp/Notifiers/hrmProvider/LeaveApplicationDetailsProvider.dart'
;
export
'package:generp/Notifiers/hrmProvider/CasualLeaveHistoryProvider.dart'
;
export
'package:generp/Notifiers/hrmprovider/orgprovider.dart'
;
lib/screens/order/addOrder.dart
View file @
dc88a3f9
This diff is collapsed.
Click to expand it.
lib/services/api_calling.dart
View file @
dc88a3f9
This diff is collapsed.
Click to expand it.
lib/services/api_names.dart
View file @
dc88a3f9
...
...
@@ -181,9 +181,9 @@ const crmDashboardQuotationsUrl = "${baseUrl_test}crm_dashboard_quotations_list"
const
ogcharturl
=
"
${baseUrl_test}
organisation_structures"
;
const
JobDesciptionUrl
=
"
${baseUrl_test}
job_description"
;
///HRM
//Attendance
const
HrmAccessiblePagesUrl
=
"
${baseUrl_test}
hrm_accessible_pages"
;
const
AttendanceRequestListUrl
=
"
${baseUrl_test}
attendance_request_list"
;
const
AttendanceRequestDetailsUrl
=
"
${baseUrl_test}
attendance_request_details"
;
...
...
@@ -203,9 +203,9 @@ const LeaveApplicationDetailsUrl ="${baseUrl_test}leave_request_details";
const
LeaveRequestAdditionUrl
=
"
${baseUrl_test}
add_leave_request"
;
const
LeaveRequestRejectAprroveUrl
=
"
${baseUrl_test}
leaves_approve_reject"
;
const
CasuaLeaveHistoryUrl
=
"
${baseUrl_test}
casual_leave_history"
;
const
AdvanceListUrl
=
"
${baseUrl_test}
advance_list"
;
Prev
1
2
3
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment