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
642da22c
"ios/git@183.82.99.133:saisrinivas/gen_rentals.git" did not exist on "2af2b2b7a38417c8c48619f65e811ac56264035b"
Commit
642da22c
authored
Oct 08, 2025
by
Sai Srinivas
Committed by
Sai Srinivas
Oct 13, 2025
Browse files
gen erp 08-10
parent
d2c9404a
Changes
48
Show whitespace changes
Inline
Side-by-side
lib/Notifiers/crmProvider/editProductListProvider.dart
View file @
642da22c
...
...
@@ -141,6 +141,7 @@ class Editproductlistprovider extends ChangeNotifier {
TextEditingController
addEditProductPriceController
=
TextEditingController
();
TextEditingController
addEditQuantityController
=
TextEditingController
();
TextEditingController
addEditTotalAmountController
=
TextEditingController
();
TextEditingController
addEditRemarkController
=
TextEditingController
();
List
<
Products
>
_productsList
=
[];
List
<
LeadProducts
>
_leadProductsList
=
[];
Products
?
_selectedProducts
;
...
...
@@ -150,12 +151,14 @@ class Editproductlistprovider extends ChangeNotifier {
List
<
TextEditingController
>
editProductPriceControllers
=
[];
List
<
TextEditingController
>
editQuantityControllers
=
[];
List
<
TextEditingController
>
editTotalAmountControllers
=
[];
List
<
TextEditingController
>
editRemarkControllers
=
[];
final
List
<
String
?>
_selectedProductIds
=
[];
final
List
<
String
?>
_selectedValues
=
[];
bool
_submitLoading
=
false
;
String
?
qtyError
;
String
?
priceError
;
String
?
productError
;
String
?
remarkError
;
bool
get
editProductDetailsClicked
=>
_editProductDetailsClicked
;
bool
get
editContactDetailsClicked
=>
_editContactDetailsClicked
;
...
...
@@ -234,6 +237,7 @@ class Editproductlistprovider extends ChangeNotifier {
editProductPriceControllers
.
clear
();
editQuantityControllers
.
clear
();
editTotalAmountControllers
.
clear
();
editRemarkControllers
.
clear
();
_selectedProductIds
.
clear
();
_selectedValues
.
clear
();
...
...
@@ -253,6 +257,9 @@ class Editproductlistprovider extends ChangeNotifier {
.
toString
(),
),
);
editRemarkControllers
.
add
(
TextEditingController
(
text:
product
.
remarks
?.
toString
()
??
''
),
);
_selectedProductIds
.
add
(
product
.
productId
);
_selectedValues
.
add
(
product
.
productName
);
}
...
...
@@ -281,6 +288,7 @@ class Editproductlistprovider extends ChangeNotifier {
editProductPriceControllers
.
removeAt
(
index
);
editQuantityControllers
.
removeAt
(
index
);
editTotalAmountControllers
.
removeAt
(
index
);
editRemarkControllers
.
removeAt
(
index
);
_selectedProductIds
.
removeAt
(
index
);
_selectedValues
.
removeAt
(
index
);
notifyListeners
();
...
...
@@ -314,6 +322,7 @@ class Editproductlistprovider extends ChangeNotifier {
"product_id"
:
_selectedProductIds
[
i
]!,
"price"
:
editProductPriceControllers
[
i
].
text
,
"qty"
:
editQuantityControllers
[
i
].
text
,
"remarks"
:
editRemarkControllers
[
i
].
text
,
};
insertData
.
add
(
rowData
);
}
...
...
@@ -401,6 +410,7 @@ class Editproductlistprovider extends ChangeNotifier {
type
,
leadProductId
,
productId
,
prductRemark
,
)
async
{
try
{
if
(!
validateform
(
context
))
{
...
...
@@ -419,6 +429,7 @@ class Editproductlistprovider extends ChangeNotifier {
productId
,
addEditQuantityController
.
text
,
addEditProductPriceController
.
text
,
addEditRemarkController
.
text
,
);
if
(
data
!=
null
&&
data
.
error
==
"0"
)
{
_submitLoading
=
false
;
...
...
@@ -477,10 +488,12 @@ class Editproductlistprovider extends ChangeNotifier {
_quotationDetailsClicked
=
false
;
addEditProductPriceController
.
clear
();
addEditQuantityController
.
clear
();
addEditRemarkController
.
clear
();
addEditTotalAmountController
.
clear
();
editProductPriceControllers
.
clear
();
editQuantityControllers
.
clear
();
editTotalAmountControllers
.
clear
();
editRemarkControllers
.
clear
();
_selectedProductIds
.
clear
();
_selectedProducts
=
null
;
_selectedAddEditProductId
=
null
;
...
...
lib/Notifiers/crmProvider/followUpUpdateProvider.dart
View file @
642da22c
...
...
@@ -9,6 +9,7 @@ import 'package:intl/intl.dart';
import
'package:provider/provider.dart'
;
import
'../../screens/crm/LeadDetailsByMode.dart'
;
import
'../../screens/order/addOrder.dart'
;
import
'../../services/api_calling.dart'
;
import
'crmLeadDetailsProvider.dart'
;
...
...
@@ -165,17 +166,24 @@ class followUpUpdateProvider extends ChangeNotifier {
competitor
,
leadStatus
,
appointmentType
,
feedback
,
inTime
,
loc
,
sms
,
[
mode
,
])
async
{
try
{
// validate form before API call
if
(!
valid
())
{
return
;
}
_submitLoading
=
true
;
notifyListeners
();
final
prov
=
Provider
.
of
<
HomescreenNotifier
>(
context
,
listen:
false
);
final
prov2
=
Provider
.
of
<
crmLeadDetailsProvider
>(
context
,
listen:
false
);
final
data
=
await
ApiCalling
.
crmLeadDetailsAddFollowUpAPI
(
prov
.
session
,
prov
.
empId
,
...
...
@@ -194,24 +202,44 @@ class followUpUpdateProvider extends ChangeNotifier {
sms
,
noteController
.
text
,
);
debugPrint
(
"status ================
$orderStatus
"
);
if
(
data
!=
null
&&
data
.
error
==
"0"
)
{
_submitLoading
=
false
;
resetForm
();
if
(
fromScreen
==
"Pending Tasks"
)
{
if
(
orderStatus
==
"Order Gain"
)
{
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
_
)
=>
AddorderScreen
(
key:
UniqueKey
(),
mode:
mode
,
pageTitleName:
"Add Order"
,
leadId:
leadID
,
feedback:
feedback
,
followupType:
followupType
,
inTime:
inTime
,
loc:
loc
,
),
),
);
}
else
if
(
fromScreen
==
"Pending Tasks"
)
{
Navigator
.
pushAndRemoveUntil
(
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
LeadDetailsByMode
(
builder:
(
_
)
=>
LeadDetailsByMode
(
mode:
"executive"
,
pageTitleName:
"Lead Details"
,
leadId:
leadID
??
"-"
,
),
settings:
RouteSettings
(
name:
"LeadDetailsByMode"
),
settings:
const
RouteSettings
(
name:
"LeadDetailsByMode"
),
),
(
Route
<
dynamic
>
route
)
{
return
route
.
settings
.
name
==
'CrmdashboardScreen'
;
},
(
Route
<
dynamic
>
route
)
=>
route
.
settings
.
name
==
'CrmdashboardScreen'
,
);
}
else
{
Navigator
.
pop
(
context
);
...
...
@@ -229,10 +257,11 @@ class followUpUpdateProvider extends ChangeNotifier {
}
catch
(
e
,
s
)
{
_submitLoading
=
false
;
notifyListeners
();
p
rint
(
"Error:
$e
, Stack:
$s
"
);
debugP
rint
(
"Error:
$e
, Stack:
$s
"
);
}
}
bool
valid
()
{
followupError
=
null
;
followupFeedbackError
=
null
;
...
...
lib/Notifiers/financeProvider/RequesitionLidtDetailsProvider.dart
View file @
642da22c
...
...
@@ -390,45 +390,85 @@ class Requesitionlidtdetailsprovider extends ChangeNotifier {
}
Future
<
void
>
paymentrequisitionApproveSubmitAPIFunction
(
context
,
mode
,
payment
R
equest
I
d
,
approved
A
mount
,
approve
R
emarks
,
proposed
P
ayment
A
ccount
I
d
,
BuildContext
context
,
String
mode
,
String
payment
_r
equest
_i
d
,
String
approved
_a
mount
,
String
approve
_r
emarks
,
String
proposed
_p
ayment
_a
ccount
_i
d
,
)
async
{
print
(
"🎯 === PROVIDER METHOD STARTED ==="
);
try
{
if
(!
validateApproval
(
approvedAmount
,
approveRemarks
,
proposedPaymentAccountId
,
))
{
print
(
"🔍 Starting validation..."
);
if
(!
validateApproval
(
approved_amount
,
approve_remarks
,
proposed_payment_account_id
))
{
print
(
"❌ VALIDATION FAILED - Stopping execution"
);
return
;
}
print
(
"✅ Validation passed"
);
var
provider
=
Provider
.
of
<
HomescreenNotifier
>(
context
,
listen:
false
);
print
(
"👤 Getting home provider..."
);
var
homeProvider
=
Provider
.
of
<
HomescreenNotifier
>(
context
,
listen:
false
);
print
(
"👤 Emp ID:
${homeProvider.empId}
, Session:
${homeProvider.session}
"
);
print
(
"🌐 Calling API..."
);
final
data
=
await
ApiCalling
.
ApprovePaymentRequestSubmitAPI
(
p
rovider
.
empId
,
p
rovider
.
session
,
homeP
rovider
.
empId
,
homeP
rovider
.
session
,
mode
,
payment
R
equest
I
d
,
approved
A
mount
,
approve
R
emarks
,
proposed
P
ayment
A
ccount
I
d
,
payment
_r
equest
_i
d
,
approved
_a
mount
,
approve
_r
emarks
,
proposed
_p
ayment
_a
ccount
_i
d
,
);
print
(
"🌐 API call completed"
);
if
(
data
!=
null
)
{
print
(
"📡 API Response: error=
${data.error}
, message=
${data.message}
"
);
if
(
data
.
error
==
"0"
)
{
paymentRequesitionDetails
(
context
,
paymentRequestId
);
print
(
"✅ API SUCCESS - Processing..."
);
// Check if context is still valid before UI operations
if
(
context
.
mounted
)
{
print
(
"🎯 Context mounted - performing UI operations"
);
paymentRequesitionDetails
(
context
,
payment_request_id
);
resetAll
();
toast
(
context
,
data
.
message
);
Navigator
.
pop
(
context
,
true
);
print
(
"✅ UI operations completed"
);
}
else
{
print
(
"⚠️ Context not mounted - skipping UI operations"
);
}
notifyListeners
();
print
(
"✅ Notify listeners called"
);
}
else
{
print
(
"❌ API returned error:
${data.message}
"
);
if
(
context
.
mounted
)
{
toast
(
context
,
data
.
message
??
"Submission failed"
);
}
}
}
catch
(
e
)
{}
}
else
{
print
(
"❌ NULL response from API"
);
if
(
context
.
mounted
)
{
toast
(
context
,
"No response from server"
);
}
}
}
catch
(
e
,
s
)
{
print
(
"💥 EXCEPTION in provider method:
$e
"
);
print
(
"📋 Stack trace:
$s
"
);
// Show error to user
if
(
context
.
mounted
)
{
toast
(
context
,
"Error:
${e.toString()}
"
);
}
}
print
(
"🎯 === PROVIDER METHOD COMPLETED ==="
);
}
editPrevalues
()
{
editPaymentRequestedAmountController
.
text
=
_requestDetails
.
requestedAmount
??
"-"
;
...
...
@@ -530,35 +570,72 @@ class Requesitionlidtdetailsprovider extends ChangeNotifier {
}
bool
validateApproval
(
approvedAmount
,
approveRemarks
,
proposedPaymentAccountId
,
String
approvedAmount
,
// Add type for clarity
String
approveRemarks
,
// Add type for clarity
String
proposedPaymentAccountId
,
// Add type for clarity
)
{
print
(
"🔍 === VALIDATION STARTED ==="
);
print
(
" Approved Amount: '
$approvedAmount
'"
);
print
(
" Requested Amount: '
${requestedAmount.text}
'"
);
print
(
" Remarks: '
$approveRemarks
'"
);
print
(
" Payment Account ID: '
$proposedPaymentAccountId
'"
);
print
(
" Selected ID: '
$_selectedID
'"
);
remarksError
=
null
;
ApprovedAmountError
=
null
;
selectpaymentAccountError
=
null
;
bool
isValid
=
true
;
if
(
approvedAmount
.
toString
().
trim
().
isEmpty
)
{
// Fix 1: Use the String parameter directly (no .text)
if
(
approvedAmount
.
trim
().
isEmpty
)
{
ApprovedAmountError
=
"Enter Amount"
;
isValid
=
false
;
print
(
"❌ Approved amount is empty"
);
}
if
(
approveRemarks
.
toString
().
trim
().
isEmpty
)
{
// Fix 2: Use the String parameter directly (no .text)
if
(
approveRemarks
.
trim
().
isEmpty
)
{
remarksError
=
"Please Enter Remarks"
;
isValid
=
false
;
print
(
"❌ Remarks are empty"
);
}
// Fix 3: Parse the String parameters, not .text
if
(
approvedAmount
.
trim
().
isNotEmpty
&&
requestedAmount
.
text
.
trim
().
isNotEmpty
)
{
try
{
final
numberFormat
=
NumberFormat
.
decimalPattern
();
if
(
numberFormat
.
parse
(
approvedAmount
.
text
)
>
numberFormat
.
parse
(
requestedAmount
.
text
))
{
ApprovedAmountError
=
"Approved Amount should not be greater than requested amount"
;
double
approved
=
numberFormat
.
parse
(
approvedAmount
.
trim
()).
toDouble
();
double
requested
=
numberFormat
.
parse
(
requestedAmount
.
text
.
trim
()).
toDouble
();
print
(
"💰 Amount Comparison: Approved:
$approved
, Requested:
$requested
"
);
if
(
approved
>
requested
)
{
ApprovedAmountError
=
"Approved Amount should not be greater than requested amount"
;
isValid
=
false
;
print
(
"❌ Approved amount exceeds requested amount"
);
}
}
catch
(
e
)
{
print
(
"💥 Error parsing amounts:
$e
"
);
ApprovedAmountError
=
"Invalid amount format"
;
isValid
=
false
;
}
if
(
_selectedPaymentAccounts
==
null
||
_selectedID
.
isEmpty
)
{
}
// Fix 4: Use the parameter OR check both for consistency
if
(
proposedPaymentAccountId
.
isEmpty
&&
_selectedID
.
isEmpty
)
{
selectpaymentAccountError
=
"Please select an account"
;
isValid
=
false
;
print
(
"❌ No payment account selected"
);
}
else
if
(
proposedPaymentAccountId
.
isNotEmpty
&&
_selectedID
.
isEmpty
)
{
// If parameter has value but _selectedID doesn't, sync them
_selectedID
=
proposedPaymentAccountId
;
print
(
"🔄 Synced selected ID from parameter:
$_selectedID
"
);
}
_submitClicked
=
false
;
notifyListeners
();
print
(
"🔍 === VALIDATION RESULT:
$isValid
==="
);
return
isValid
;
}
...
...
lib/Notifiers/hrmProvider/advanceProvider.dart
0 → 100644
View file @
642da22c
import
'package:flutter/material.dart'
;
import
'../../Models/hrmModels/advanceListResponse.dart'
;
import
'../../services/api_calling.dart'
;
class
AdvanceListProvider
extends
ChangeNotifier
{
bool
_isLoading
=
false
;
bool
get
isLoading
=>
_isLoading
;
String
?
_errorMessage
;
String
?
get
errorMessage
=>
_errorMessage
;
List
<
AdvanceList
>
_advanceList
=
[];
List
<
AdvanceList
>
get
advanceList
=>
_advanceList
;
int
_currentPage
=
1
;
int
get
currentPage
=>
_currentPage
;
bool
_hasMore
=
true
;
bool
get
hasMore
=>
_hasMore
;
/// Fetch Advance List
Future
<
void
>
fetchAdvanceList
(
BuildContext
context
,
String
session
,
String
empId
,
{
int
page
=
1
,
bool
loadMore
=
false
})
async
{
if
(
_isLoading
)
return
;
_isLoading
=
true
;
if
(!
loadMore
)
{
_errorMessage
=
null
;
_advanceList
=
[];
_currentPage
=
1
;
_hasMore
=
true
;
}
notifyListeners
();
try
{
final
response
=
await
ApiCalling
.
advanceListAPI
(
session
,
empId
,
page
);
debugPrint
(
'lenght:
${response?.advanceList?.length}
, empId:
${empId}
, session:
${session}
, pageNumber:
$page
'
);
if
(
response
!=
null
)
{
if
(
response
.
error
==
"0"
)
{
final
newList
=
response
.
advanceList
??
[];
if
(
loadMore
)
{
_advanceList
.
addAll
(
newList
);
}
else
{
_advanceList
=
newList
;
}
_currentPage
=
page
;
_hasMore
=
newList
.
isNotEmpty
;
}
else
{
_errorMessage
=
response
.
message
??
"Something went wrong"
;
}
}
else
{
_errorMessage
=
"No response from server"
;
}
}
catch
(
e
)
{
_errorMessage
=
"Failed to fetch advance list:
$e
"
;
}
finally
{
_isLoading
=
false
;
notifyListeners
();
}
}
/// Refresh Advance List
Future
<
void
>
refreshAdvanceList
(
BuildContext
context
,
String
session
,
String
empId
)
async
{
await
fetchAdvanceList
(
context
,
session
,
empId
,
page:
1
,
loadMore:
false
);
}
/// Load More (Pagination)
Future
<
void
>
loadMoreAdvanceList
(
BuildContext
context
,
String
session
,
String
empId
)
async
{
if
(!
_hasMore
||
_isLoading
)
return
;
await
fetchAdvanceList
(
context
,
session
,
empId
,
page:
_currentPage
+
1
,
loadMore:
true
);
}
/// Clear state
void
clear
()
{
_advanceList
=
[];
_errorMessage
=
null
;
_currentPage
=
1
;
_hasMore
=
true
;
notifyListeners
();
}
}
lib/Notifiers/hrmProvider/casualLeaveHistoryProvider.dart
0 → 100644
View file @
642da22c
import
'package:flutter/material.dart'
;
import
'package:provider/provider.dart'
;
import
'../../Models/hrmModels/casualLeaveHistoryResponse.dart'
;
import
'../../services/api_calling.dart'
;
import
'../HomeScreenNotifier.dart'
;
class
CasualLeaveHistoryProvider
extends
ChangeNotifier
{
/// --- Variables ---
List
<
CasualLeaveHistory
>
casualLeaveHistoryList
=
[];
bool
isLoading
=
false
;
String
?
errorMessage
;
/// --- Fetch Casual Leave History ---
Future
<
void
>
fetchCasualLeaveHistory
(
BuildContext
context
)
async
{
isLoading
=
true
;
errorMessage
=
null
;
notifyListeners
();
try
{
final
homeProvider
=
Provider
.
of
<
HomescreenNotifier
>(
context
,
listen:
false
);
final
response
=
await
ApiCalling
.
casualLeaveHistoryAPI
(
homeProvider
.
session
,
homeProvider
.
empId
,
);
if
(
response
!=
null
)
{
if
(
response
.
error
!=
null
&&
response
.
error
!=
"0"
)
{
errorMessage
=
response
.
message
??
"Something went wrong"
;
casualLeaveHistoryList
=
[];
}
else
{
casualLeaveHistoryList
=
response
.
casualLeaveHistory
??
[];
}
}
else
{
errorMessage
=
"No response from server"
;
}
}
catch
(
e
)
{
errorMessage
=
"Failed to load leave history:
$e
"
;
}
finally
{
isLoading
=
false
;
notifyListeners
();
}
}
/// --- Clear data (optional) ---
void
clearHistory
()
{
casualLeaveHistoryList
.
clear
();
errorMessage
=
null
;
notifyListeners
();
}
}
lib/Notifiers/ordersProvider/addOrderProvider.dart
View file @
642da22c
...
...
@@ -89,7 +89,7 @@ class Addorderprovider extends ChangeNotifier {
List
<
String
>
_tpcApplicable
=
[];
List
<
TpcList
>
_tpcAgent
=
[];
AccountDetails
_accountDetails
=
AccountDetails
();
String
?
selectAccountError
;
String
?
orderDateError
;
...
...
@@ -309,7 +309,6 @@ class Addorderprovider extends ChangeNotifier {
String
?
get
selectedTpcAgentValue
=>
_selectedTpcAgentValue
;
AccountDetails
get
accountDetails
=>
_accountDetails
;
set
accountList
(
List
<
AccountList
>
value
)
{
_accountList
=
value
;
...
...
@@ -529,10 +528,7 @@ class Addorderprovider extends ChangeNotifier {
notifyListeners
();
}
set
accountDetails
(
AccountDetails
value
)
{
_accountDetails
=
value
;
notifyListeners
();
}
set
imagePath
(
File
?
value
)
{
_imageName
=
value
;
...
...
@@ -812,13 +808,18 @@ class Addorderprovider extends ChangeNotifier {
// };
// }
Future
<
void
>
ordersAddOrderAPIViewFunction
(
context
,
mode
)
async
{
Future
<
void
>
ordersAddOrderAPIViewFunction
(
context
,
mode
,
leadId
,
feedback
,
followupType
,
inTime
,
loc
)
async
{
try
{
final
provider
=
Provider
.
of
<
HomescreenNotifier
>(
context
,
listen:
false
);
final
data
=
await
ApiCalling
.
addOrderViewAPI
(
provider
.
empId
,
provider
.
session
,
mode
,
leadId
,
feedback
,
followupType
,
inTime
,
loc
);
if
(
data
!=
null
)
{
if
(
data
.
error
==
"0"
)
{
...
...
@@ -839,6 +840,116 @@ class Addorderprovider extends ChangeNotifier {
}
catch
(
e
)
{}
}
// AccountDetails _accountDetails = AccountDetails();
//
// AccountDetails get accountDetails => _accountDetails;
AccountDetails
?
_accountDetails
;
// nullable
AccountDetails
?
get
accountDetails
=>
_accountDetails
;
set
accountDetails
(
AccountDetails
?
value
)
{
_accountDetails
=
value
;
notifyListeners
();
}
Future
<
void
>
fetchAddOrderViewData
(
BuildContext
context
,
String
empId
,
String
session
,
String
mode
,
leadId
,
feedback
,
followupType
,
inTime
,
loc
)
async
{
debugPrint
(
"🔥 fetchAddOrderViewData CALLED with leadId=
$leadId
, mode=
$mode
"
);
try
{
_isLoading
=
true
;
notifyListeners
();
final
response
=
await
ApiCalling
.
addOrderViewAPI
(
empId
,
session
,
mode
,
leadId
,
feedback
,
followupType
,
inTime
,
loc
);
if
(
response
!=
null
)
{
if
(
response
.
error
==
"0"
)
{
_tpcApplicable
=
[
"Yes"
,
"No"
];
_erectionScope
=
response
.
erectionScope
??
[];
_unloadingScope
=
response
.
unloadingScope
??
[];
_freightScope
=
response
.
freightScope
??
[];
_employees
=
response
.
employees
??
[];
_billingStates
=
response
.
states
??
[];
_dispatchStates
=
response
.
states
??
[];
_saleProducts
=
response
.
saleProducts
??
[];
// getSubLocationAPI(context, response.accountDetails?.subLocality);
// getDistrictAPI(context, response.accountDetails?.district);
// _billingStates = response.accountDetails?.state as List<States>;
checkDropdownselected
();
notifyListeners
();
}
else
{}
}
else
{}
debugPrint
(
" data to all fields##################################"
);
if
(
response
!=
null
)
{
debugPrint
(
"Employees:
${response.accountDetails?.pincode}
"
);
debugPrint
(
"States:
${response.accountDetails?.state}
"
);
debugPrint
(
"Sale Products:
${response.accountDetails?.address}
"
);
debugPrint
(
"Account Details Name:
${response.accountDetails?.name}
"
);
debugPrint
(
"District:
${response.accountDetails?.district}
"
);
debugPrint
(
"Sub location:
${response.accountDetails?.name}
"
);
}
else
{
debugPrint
(
"Response is NULL"
);
}
if
(
response
!=
null
&&
response
.
error
==
"0"
)
{
// Save accountDetails safely
_accountDetails
=
AccountDetails
(
id:
response
.
accountDetails
?.
id
,
name:
response
.
accountDetails
?.
name
,
address:
response
.
accountDetails
?.
address
,
state:
response
.
accountDetails
?.
state
,
pincode:
response
.
accountDetails
?.
pincode
,
date:
response
.
accountDetails
?.
date
,
subLocality:
response
.
accountDetails
?.
subLocality
,
district:
response
.
accountDetails
?.
district
,
);
// Save lists safely
_unloadingScope
=
response
.
unloadingScope
??
[];
_freightScope
=
response
.
freightScope
??
[];
_erectionScope
=
response
.
erectionScope
??
[];
// default selections
if
(
_employees
.
isNotEmpty
)
selectedEmployees
=
_employees
.
first
;
if
(
leadId
!=
""
)
if
(
_billingStates
.
isNotEmpty
)
selecetdBillingStates
=
_billingStates
.
first
;
if
(
_billingDistricts
.
isNotEmpty
)
selectedBillingDistricts
=
_billingDistricts
.
first
;
if
(
_billingStates
.
isNotEmpty
)
selecetdBillingStates
=
_billingStates
.
first
;
if
(
_dispatchStates
.
isNotEmpty
)
selecetdDispatchStates
=
_dispatchStates
.
first
;
if
(
_saleProducts
.
isNotEmpty
)
selectedSaleProducts
=
_saleProducts
.
first
;
if
(
_unloadingScope
.
isNotEmpty
)
selectedUnloadingScope
=
_unloadingScope
.
first
;
if
(
_freightScope
.
isNotEmpty
)
selectedFreightScope
=
_freightScope
.
first
;
if
(
_erectionScope
.
isNotEmpty
)
selectedErectionScope
=
_erectionScope
.
first
;
notifyListeners
();
// notify listeners/UI
}
else
{
debugPrint
(
"API Error:
${response?.message}
"
);
}
}
catch
(
e
)
{
debugPrint
(
"Provider Error in fetchAddOrderViewData:
$e
"
);
}
finally
{
_isLoading
=
false
;
notifyListeners
();
}
}
void
checkDropdownselected
()
{
if
(
_selectedAccountList
!=
null
&&
!
_accountList
.
contains
(
_selectedAccountList
))
{
...
...
@@ -972,12 +1083,12 @@ class Addorderprovider extends ChangeNotifier {
if
(
data
.
error
==
"0"
)
{
_billingDistricts
=
data
.
districts
!;
_selectedBillingDistricts
=
data
.
districts
!.
firstWhere
(
(
e
)
=>
e
.
id
==
accountDetails
.
district
!,
(
e
)
=>
e
.
id
==
accountDetails
?
.
district
!,
);
_selectedBillingDistrictValue
=
data
.
districts
!
.
firstWhere
((
e
)
=>
e
.
id
==
accountDetails
.
district
!)
.
firstWhere
((
e
)
=>
e
.
id
==
accountDetails
?
.
district
!)
.
district
;
notifyListeners
();
...
...
@@ -1020,12 +1131,12 @@ class Addorderprovider extends ChangeNotifier {
if
(
data
.
error
==
"0"
)
{
_billingSubLocations
=
data
.
subLocations
!;
_selectedBillingSubLocations
=
data
.
subLocations
!.
firstWhere
(
(
e
)
=>
e
.
id
==
accountDetails
.
subLocality
!,
(
e
)
=>
e
.
id
==
accountDetails
?
.
subLocality
!,
);
_selectedBillingSubLocValue
=
data
.
subLocations
!
.
firstWhere
((
e
)
=>
e
.
id
==
accountDetails
.
subLocality
!)
.
firstWhere
((
e
)
=>
e
.
id
==
accountDetails
?
.
subLocality
!)
.
subLocality
;
notifyListeners
();
...
...
lib/Notifiers/splashVersionNotifier.dart
View file @
642da22c
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 @
642da22c
import
'dart:convert'
;
import
'dart:io'
;
import
'package:firebase_core/firebase_core.dart'
;
...
...
@@ -10,56 +11,139 @@ 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/HomeScreen.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:
false
,
);
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}
"
);
}
void
handleNotificationTap
(
Map
<
String
,
dynamic
>
data
)
{
debugPrint
(
"👉 Handling notification tap:
$data
"
);
if
(
type
==
'offline_reminder'
)
{
FlutterRingtonePlayer
().
play
(
// fromAsset: "assets/offline_reminder.mp3",
ios:
IosSounds
.
glass
,
// Specify the iOS sound
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
},
);
}
else
if
(
type
==
'normal'
)
{
FlutterRingtonePlayer
().
play
(
// fromAsset: "assets/notification_sound.mp3",
ios:
IosSounds
.
glass
,
// Specify the iOS sound
break
;
case
"6"
:
navigatorKey
.
currentState
?.
pushNamed
(
'/crm_lead_details'
,
arguments:
{
"id"
:
notificationId
},
);
}
else
if
(
type
==
'web_erp_notification'
)
{
FlutterRingtonePlayer
().
play
(
// fromAsset: "assets/notification_sound.mp3",
ios:
IosSounds
.
glass
,
// Specify the iOS sound
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
{
FlutterRingtonePlayer
().
play
(
// fromAsset: "assets/notification_sound.mp3",
// will be the sound on Android
ios:
IosSounds
.
glass
,
// will be the sound on iOS
);
// fallback
navigatorKey
.
currentState
?.
pushNamed
(
'/home'
);
}
if
(
kDebugMod
e
)
{
print
(
'A Background message just showed up:
${message.messageId}
'
);
}
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,75 +156,176 @@ 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:
false
);
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
(
alert:
true
,
badge:
true
,
sound:
tru
e
,
sound:
fals
e
,
);
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 +338,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 +411,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 +420,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
MyHomePage
(),
// '/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 +468,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 +512,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 +524,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 @
642da22c
...
...
@@ -49,6 +49,7 @@ class _MyHomePageState extends State<MyHomePage> {
Future
.
delayed
(
Duration
(
milliseconds:
600
),
()
{
profProv
.
ProfileApiFunction
(
prov
,
context
);
profProv
.
VersionApiFunction
();
profProv
.
fetchMissedCheckoutStrip
(
prov
.
empId
,
prov
.
session
);
});
super
.
initState
();
}
...
...
@@ -255,9 +256,50 @@ class _MyHomePageState extends State<MyHomePage> {
// ),
// ),
// ),
Column
(
mainAxisSize:
MainAxisSize
.
min
,
children:
[
Stack
(
clipBehavior:
Clip
.
none
,
children:
[
// ---------------- MISSED CHECKOUT STRIP BACKGROUND ----------------
if
(
profile
.
missedCheckoutData
?.
error
!=
"1"
)
Container
(
height:
214
,
// Taller than the profile card
decoration:
BoxDecoration
(
color:
const
Color
(
0xFFFFD9D9
),
borderRadius:
const
BorderRadius
.
vertical
(
bottom:
Radius
.
circular
(
20
),
),
),
child:
Align
(
alignment:
Alignment
.
bottomCenter
,
child:
Padding
(
padding:
const
EdgeInsets
.
only
(
bottom:
10
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
SvgPicture
.
asset
(
"assets/svg/missed_warning_ic.svg"
,
height:
20
,
),
const
SizedBox
(
width:
8
),
Text
(
profile
.
missedCheckoutData
?.
visibleText
??
""
,
style:
TextStyle
(
color:
Colors
.
black
,
fontSize:
12
,
fontFamily:
"JakartaMedium"
,
),
),
],
),
),
),
),
// ---------------- PROFILE CARD (FRONT) ----------------
InkResponse
(
onTap:
()
{
HapticFeedback
.
selectionClick
();
...
...
@@ -471,6 +513,9 @@ class _MyHomePageState extends State<MyHomePage> {
),
),
),
],
),
SizedBox
(
child:
Column
(
crossAxisAlignment:
...
...
@@ -1528,7 +1573,7 @@ class _MyHomePageState extends State<MyHomePage> {
],
),
),
SizedBox
(
height:
1
0
),
SizedBox
(
height:
1
2
),
Align
(
alignment:
Alignment
.
bottomCenter
,
child:
Container
(
...
...
@@ -1543,6 +1588,7 @@ class _MyHomePageState extends State<MyHomePage> {
),
),
),
SizedBox
(
height:
10
),
// Expanded(
// flex: 10,
// child: Container(
...
...
@@ -1779,12 +1825,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 +2021,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 @
642da22c
...
...
@@ -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
,
...
...
@@ -939,15 +939,16 @@ class _LeadDetailsByModeState extends State<LeadDetailsByMode> {
),
),
Expanded
(
flex:
2
,
flex:
3
,
child:
Text
(
maxLines:
1
,
textAlign:
TextAlign
.
right
,
"₹
${provider.leadProducts[lp].price ?? "-"}
"
,
style:
TextStyle
(
fontFamily:
"JakartaMedium"
,
fontSize:
1
4
,
fontSize:
1
3
,
color:
AppColors
.
semi_black
,
...
...
@@ -966,7 +967,7 @@ class _LeadDetailsByModeState extends State<LeadDetailsByMode> {
AppColors
.
grey_semi
,
),
),
SizedBox
(
height:
5
),
SizedBox
(
height:
3
),
DottedLine
(
dashGapLength:
4
,
dashGapColor:
...
...
@@ -988,6 +989,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
,
),
),
],
)
],
),
),
...
...
@@ -1211,7 +1230,7 @@ class _LeadDetailsByModeState extends State<LeadDetailsByMode> {
),
),
Expanded
(
flex:
2
,
flex:
3
,
child:
InkResponse
(
onTap:
()
{
HapticFeedback
.
selectionClick
();
...
...
@@ -1894,7 +1913,7 @@ class _LeadDetailsByModeState extends State<LeadDetailsByMode> {
.
astatus
==
"No"
)
...[
Expanded
(
flex:
2
,
flex:
3
,
child:
Container
(
height:
45
,
padding:
...
...
@@ -1937,9 +1956,10 @@ class _LeadDetailsByModeState extends State<LeadDetailsByMode> {
child:
Row
(
children:
[
Expanded
(
flex:
3
,
flex:
4
,
child:
Text
(
"Appointment"
,
maxLines:
1
,
style:
TextStyle
(
fontSize:
14
,
fontFamily:
...
...
@@ -2195,7 +2215,7 @@ class _LeadDetailsByModeState extends State<LeadDetailsByMode> {
if
(
quotationNotEmpty
)
...[
SizedBox
(
width:
double
.
infinity
,
height:
2
00
,
height:
2
44
,
child:
ListView
.
builder
(
physics:
AlwaysScrollableScrollPhysics
(),
shrinkWrap:
true
,
...
...
@@ -2207,7 +2227,7 @@ class _LeadDetailsByModeState extends State<LeadDetailsByMode> {
itemCount:
provider
.
quotationsDetails
.
length
,
itemBuilder:
(
context
,
lp
)
{
return
Container
(
height:
2
00
,
height:
2
42
,
width:
MediaQuery
.
of
(
context
).
size
.
width
*
0.9
,
...
...
@@ -3458,6 +3478,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 +3524,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 @
642da22c
...
...
@@ -272,9 +272,7 @@ class _LeadlistbymodeState extends State<Leadlistbymode> {
child:
InkResponse
(
onTap:
()
{
HapticFeedback
.
selectionClick
();
launch
(
'tel://
${crmLists[index].mob1}
'
,
);
_showContactOptions
(
context
,
crmLists
[
index
].
mob1
);
},
child:
SizedBox
(
height:
35
,
...
...
@@ -930,6 +928,94 @@ 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
),
Text
(
"Contact Options"
,
style:
TextStyle
(
fontSize:
18
,
color:
AppColors
.
app_blue
,
fontFamily:
"JakartaMedium"
,
),
),
const
SizedBox
(
height:
20
),
// --- Call Option ---
ListTile
(
leading:
const
Icon
(
Icons
.
phone
,
color:
Colors
.
green
),
title:
const
Text
(
"Call"
,
style:
TextStyle
(
fontSize:
15
,
fontFamily:
"JakartaMedium"
,
)
),
onTap:
()
async
{
Navigator
.
pop
(
context
);
final
uri
=
Uri
.
parse
(
"tel:
$phoneNumber
"
);
await
launchUrl
(
uri
,
mode:
LaunchMode
.
externalApplication
);
},
),
// --- WhatsApp Option ---
ListTile
(
leading:
SvgPicture
.
asset
(
"assets/svg/whatsapp_ic.svg"
,
width:
23
,
height:
23
,
),
title:
const
Text
(
"WhatsApp"
,
style:
TextStyle
(
fontSize:
15
,
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 @
642da22c
...
...
@@ -3375,43 +3375,43 @@ class ProspectDetailsByModeState extends State<ProspectDetailsByMode> {
children:
[
Expanded
(
child:
DropdownButton2
<
String
>(
hint:
Text
(
"Select Status"
,
isExpanded:
true
,
hint:
const
Row
(
children:
[
Expanded
(
child:
Text
(
'Select Lead Status'
,
style:
TextStyle
(
fontSize:
14
),
overflow:
TextOverflow
.
ellipsis
,
),
),
],
),
items:
addleadProvider
.
statusList
<
String
>[
'All'
,
'Cold'
,
'Hot'
,
'Warm'
]
.
map
(
(
slist
)
=>
DropdownMenuItem
<
String
>(
value:
slist
,
(
value
)
=>
DropdownMenuItem
<
String
>(
value:
value
,
child:
Text
(
slist
,
style:
TextStyle
(
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 @
642da22c
...
...
@@ -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
();
}
...
...
@@ -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,8 +124,7 @@ class _AddleadproductscreenState extends State<Addleadproductscreen> {
style:
TextStyle
(
fontSize:
14
),
overflow:
TextOverflow
.
ellipsis
,
),
items:
provider
.
productsList
items:
provider
.
productsList
.
map
(
(
ord
)
=>
DropdownMenuItem
<
Products
>(
value:
ord
,
...
...
@@ -144,18 +138,7 @@ class _AddleadproductscreenState extends State<Addleadproductscreen> {
),
)
.
toList
(),
// provider.selectedOrderIds[index] != null?
// provider
// .orderList
// .firstWhere(
// (product) =>
// product
// .orderId ==
// provider
// .selectedOrderIds[index],
// )
value:
provider
.
selectedProducts
!=
null
value:
provider
.
selectedProducts
!=
null
?
provider
.
productsList
.
firstWhere
(
(
element
)
=>
element
.
id
==
...
...
@@ -167,12 +150,12 @@ class _AddleadproductscreenState extends State<Addleadproductscreen> {
provider
.
selectedProducts
=
value
;
provider
.
selectedProductsId
=
value
.
id
!;
provider
.
selectedProductsValue
=
value
.
name
;
}
provider
.
selectedProductsRemark
=
value
.
remarks
;
provider
.
crmSelectedProductDetailsApiFunction
(
context
,
value
!
.
id
.
toString
(),
value
.
id
.
toString
(),
);
}
},
dropdownSearchData:
DropdownSearchData
(
searchInnerWidgetHeight:
50
,
...
...
@@ -222,7 +205,6 @@ class _AddleadproductscreenState extends State<Addleadproductscreen> {
],
),
),
const
SizedBox
(
height:
10
),
textControllerWidget
(
context
,
...
...
@@ -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
,
floatingActionButton:
InkWell
(
onTap:
()
{
HapticFeedback
.
selectionClick
();
if
(
provider
.
selectedProducts
!=
null
)
{
// 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
(
provider
.
getJsonEncodedProducts
());
Navigator
.
pop
(
context
,
provider
.
getJsonEncodedProducts
());
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 @
642da22c
...
...
@@ -304,7 +304,7 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
provider
.
contactPersonNameController
,
"Contact Person Name"
,
"Enter Name"
,
provider
.
onChangeContactPersonName
,
(
value
)
=>
provider
.
onChangeContactPersonName
(
context
,
value
)
,
TextInputType
.
name
,
false
,
null
,
...
...
@@ -312,6 +312,7 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
focusNodes
[
2
],
TextInputAction
.
next
,
),
errorWidget
(
context
,
provider
.
nameError
),
textControllerWidget
(
...
...
@@ -319,7 +320,7 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
provider
.
mobileController
,
"Mobile Number"
,
"Enter Mobile Number"
,
provider
.
onChangemobile
,
(
value
)
=>
provider
.
onChangemobile
(
context
,
value
)
,
TextInputType
.
phone
,
false
,
FilteringTextInputFormatter
.
digitsOnly
,
...
...
@@ -931,16 +932,11 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
scrollDirection:
Axis
.
horizontal
,
itemCount:
provider
.
productRows
.
length
,
itemBuilder:
(
context
,
index
)
{
final
product
=
provider
.
productRows
[
index
];
final
productName
=
provider
.
productsList
final
product
=
provider
.
productRows
[
index
];
final
productName
=
provider
.
productsList
.
firstWhere
(
(
p
)
=>
p
.
id
==
product
[
'product_id'
],
orElse:
()
=>
Products
(
(
p
)
=>
p
.
id
==
product
[
'product_id'
],
orElse:
()
=>
Products
(
id:
''
,
name:
'Unknown'
,
),
...
...
@@ -948,23 +944,24 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
.
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
(
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 @
642da22c
...
...
@@ -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 @
642da22c
...
...
@@ -175,14 +175,19 @@ class _GeneratequotationscreenState extends State<Generatequotationscreen> {
),
),
);
if
(
res
!=
null
)
{
if
(
res
!=
null
&&
res
==
true
)
{
print
(
"result
$res
"
);
// provider
// .crmLeadDetailsGenerateQuoteViewAPIFunction(
// context,
// widget.leadId,
// );
// Force refresh the provider and UI
await
provider
.
crmLeadDetailsGenerateQuoteViewAPIFunction
(
context
,
widget
.
leadId
,
);
// Force UI update
if
(
mounted
)
{
setState
(()
{});
}
}
},
child:
Container
(
...
...
@@ -244,15 +249,24 @@ class _GeneratequotationscreenState extends State<Generatequotationscreen> {
),
),
);
if
(
res
!=
null
)
{
if
(
res
!=
null
&&
res
==
true
)
{
print
(
"result
$res
"
);
// Force refresh after editing
await
provider
.
crmLeadDetailsGenerateQuoteViewAPIFunction
(
context
,
widget
.
leadId
,
);
// Force UI update
if
(
mounted
)
{
setState
(()
{});
}
}
},
child:
Container
(
height:
115
,
width:
MediaQuery
.
of
(
context
).
size
.
width
*
0.8
,
height:
125
,
width:
MediaQuery
.
of
(
context
).
size
.
width
*
0.8
,
decoration:
BoxDecoration
(
color:
Color
(
0xFFE6F6FF
),
borderRadius:
BorderRadius
.
circular
(
14
),
...
...
@@ -261,17 +275,12 @@ class _GeneratequotationscreenState extends State<Generatequotationscreen> {
horizontal:
10
,
vertical:
8
,
),
margin:
EdgeInsets
.
symmetric
(
horizontal:
5
,
// vertical: 10,
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
start
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
mainAxisAlignment:
MainAxisAlignment
.
start
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Expanded
(
flex:
1
,
...
...
@@ -282,89 +291,79 @@ class _GeneratequotationscreenState extends State<Generatequotationscreen> {
SizedBox
(
width:
10
),
Expanded
(
flex:
6
,
child:
SizedBox
(
child:
Column
(
// mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
mainAxisAlignment:
MainAxisAlignment
.
start
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
// Changed to spaceBetween
children:
[
// Top section - Product name and price
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Row
(
children:
[
Expanded
(
flex:
4
,
child:
Text
(
provider
.
leadProductsList
[
lp
]
.
productName
??
"-"
,
provider
.
leadProductsList
[
lp
].
productName
??
"-"
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
overflow:
TextOverflow
.
ellipsis
,
style:
TextStyle
(
fontFamily:
"JakartaMedium"
,
fontFamily:
"JakartaMedium"
,
fontSize:
14
,
color:
AppColors
.
semi_black
,
color:
AppColors
.
semi_black
,
),
),
),
Expanded
(
flex:
2
,
flex:
3
,
child:
Text
(
textAlign:
TextAlign
.
right
,
textAlign:
TextAlign
.
right
,
maxLines:
1
,
"₹
${provider.leadProductsList[lp].price ?? "-"}
"
,
style:
TextStyle
(
fontFamily:
"JakartaMedium"
,
fontFamily:
"JakartaMedium"
,
fontSize:
14
,
color:
AppColors
.
semi_black
,
color:
AppColors
.
semi_black
,
),
),
),
],
),
SizedBox
(
height:
1
),
// Reduced spacing
Text
(
"x
${provider.leadProductsList[lp].qty ?? "-"}
"
,
style:
TextStyle
(
fontFamily:
"JakartaMedium"
,
fontSize:
14
,
color:
AppColors
.
grey_semi
,
fontFamily:
"JakartaMedium"
,
fontSize:
12
,
// Slightly smaller font
color:
AppColors
.
grey_semi
,
),
),
SizedBox
(
height:
5
),
DottedLine
(
],
),
// Middle section - Dotted line
Center
(
child:
DottedLine
(
dashGapLength:
4
,
dashGapColor:
Colors
.
white
,
dashColor:
AppColors
.
grey_semi
,
dashColor:
AppColors
.
grey_semi
,
dashLength:
2
,
lineThickness:
0.5
,
),
SizedBox
(
height:
5
),
),
// Bottom section - Total price
Text
(
"₹
${provider.leadProductsList[lp].prodTotalPrice ?? " - "}
"
,
style:
TextStyle
(
fontFamily:
"JakartaMedium"
,
fontFamily:
"JakartaMedium"
,
fontSize:
14
,
color:
AppColors
.
semi_black
,
color:
AppColors
.
semi_black
,
),
),
],
),
),
),
],
),
),
...
...
@@ -374,192 +373,6 @@ class _GeneratequotationscreenState extends State<Generatequotationscreen> {
),
],
errorWidget
(
context
,
provider
.
productRowsError
),
// if (provider.leadProductsList.isNotEmpty ||
// provider
// .editProductPriceControllers
// .isNotEmpty) ...[
// ListView.builder(
// itemCount:
// provider.editProductPriceControllers.length,
// physics: const NeverScrollableScrollPhysics(),
// shrinkWrap: true,
// itemBuilder: (context, j) {
// return Container(
// padding: const EdgeInsets.symmetric(
// horizontal: 10,
// vertical: 10,
// ),
// margin: const EdgeInsets.symmetric(
// vertical: 10,
// ),
// decoration: BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(20),
// ),
// child: Column(
// crossAxisAlignment:
// CrossAxisAlignment.start,
// children: [
// TextWidget(context, "Product"),
// DropdownButtonHideUnderline(
// child: Row(
// children: [
// Expanded(
// child: DropdownButton2<
// LeadProducts
// >(
// isExpanded: true,
// hint: const Text(
// 'Select Product',
// style: TextStyle(
// fontSize: 14,
// ),
// overflow:
// TextOverflow.ellipsis,
// ),
// items:
// provider.leadProductsList
// .map(
// (
// ord,
// ) => DropdownMenuItem<
// LeadProducts
// >(
// value: ord,
// child: Text(
// "(Product Name: ${ord.productName})",
// style:
// const TextStyle(
// fontSize:
// 14,
// ),
// overflow:
// TextOverflow
// .ellipsis,
// ),
// ),
// )
// .toList(),
// value:
// provider.selectedProductIds[j] !=
// null
// ? provider
// .leadProductsList
// .firstWhere(
// (ord) =>
// ord.id ==
// provider
// .selectedProductIds[j],
// orElse:
// () =>
// provider
// .leadProductsList[0],
// )
// : null,
// onChanged: (
// LeadProducts? value,
// ) {
// if (value != null) {
// provider
// .updateSelectedProductIds(
// j,
// value,
// );
// provider.selectedProductIds[j] =
// value.id?.toString() ??
// '';
// provider.updateTotalAmount(
// j,
// );
// }
// },
// buttonStyleData:
// ddtheme.buttonStyleData,
// iconStyleData:
// ddtheme.iconStyleData,
// menuItemStyleData:
// ddtheme.menuItemStyleData,
// dropdownStyleData:
// ddtheme.dropdownStyleData,
// ),
// ),
// ],
// ),
// ),
// const SizedBox(height: 10),
// Row(
// children: [
// Expanded(
// flex: 2,
// child: textControllerWidget(
// context,
// provider
// .editProductPriceControllers[j],
// "Product Price",
// "Enter Product Price",
// (value) =>
// provider.updateTotalAmount(j),
// TextInputType.number,
// false,
// FilteringTextInputFormatter
// .digitsOnly,
// ),
// ),
// ],
// ),
// const SizedBox(height: 10),
// Row(
// children: [
// Expanded(
// flex: 2,
// child: textControllerWidget(
// context,
// provider
// .editQuantityControllers[j],
// "Quantity",
// "Enter Quantity",
// (value) =>
// provider.updateTotalAmount(j),
// TextInputType.number,
// false,
// FilteringTextInputFormatter
// .digitsOnly,
// ),
// ),
// ],
// ),
// const SizedBox(height: 10),
// Row(
// children: [
// Expanded(
// flex: 2,
// child: textControllerWidget(
// context,
// provider
// .editTotalAmountControllers[j],
// "Amount",
// "Total Amount",
// (_) {},
// TextInputType.number,
// true,
// FilteringTextInputFormatter
// .digitsOnly,
// ),
// ),
// ],
// ),
// // IconButton(
// // icon: const Icon(Icons.delete),
// // onPressed: provider.editProductPriceControllers.length > 1
// // ? () => provider.editRemoveRow(j)
// // : null,
// // ),
// ],
// ),
// );
// },
// ),
// ],
],
),
),
...
...
@@ -896,50 +709,6 @@ class _GeneratequotationscreenState extends State<Generatequotationscreen> {
),
),
),
// Expanded(
// child: InkResponse(
// onTap:
// provider.submitLoading
// ? null
// : () {
// //genquotewhatsappbymynum,
// final insertedData = provider.getFormData();
// provider
// .crmLeadDetailsGenerateQuoteSubmitAPIFunction(
// context,
// widget.leadId,
// insertedData,
// "genquotewhatsappbymynum",
// );
// },
// child: Container(
// height: 45,
// alignment: Alignment.center,
// margin: const EdgeInsets.symmetric(
// horizontal: 10,
// vertical: 15,
// ),
// padding: const EdgeInsets.symmetric(
// horizontal: 10,
// vertical: 5,
// ),
// decoration: BoxDecoration(
// // color: AppColors.app_blue,
// borderRadius: BorderRadius.circular(10),
// ),
// child:
// provider.submitLoading
// ? CircularProgressIndicator.adaptive(
// valueColor: AlwaysStoppedAnimation<Color>(
// AppColors.app_blue,
// ),
// )
// : SvgPicture.asset(
// "assets/svg/whatsapp_quote_self.svg",
// ),
// ),
// ),
// ),
],
),
),
...
...
lib/screens/finance/AllPaymentRequesitionListsByModes.dart
View file @
642da22c
...
...
@@ -399,8 +399,10 @@ class _AllpaymentrequesitionlistsbymodesState
settings:
RouteSettings
(
name:
"Paymentrequestionlistdetails"
,
),
),
);
)
).
then
((
_
)
{
provider
.
paymentRequestionListsAPIFunction
(
context
,
widget
.
mode
,
""
,
""
);
});
if
(
routeSettingName
==
"Paymentrequestionlistdetails"
)
{
print
(
"croos refresh"
);
...
...
@@ -442,7 +444,9 @@ class _AllpaymentrequesitionlistsbymodesState
_showLevelRejectionSheet
(
context
,
requestLists
[
index
].
id
,
);
).
then
((
_
)
{
provider
.
paymentRequestionListsAPIFunction
(
context
,
widget
.
mode
,
""
,
""
);
});
},
backgroundColor:
Color
(
0xFFFFE5E5
,
...
...
@@ -521,7 +525,9 @@ class _AllpaymentrequesitionlistsbymodesState
_showLevelApprovalSheet
(
context
,
requestLists
[
index
].
id
,
);
).
then
((
_
)
{
provider
.
paymentRequestionListsAPIFunction
(
context
,
widget
.
mode
,
""
,
""
);
});
},
backgroundColor:
Color
(
0xFFE9FFE8
,
...
...
@@ -792,7 +798,8 @@ class _AllpaymentrequesitionlistsbymodesState
provider
.
selectedID
=
value
.
id
!;
provider
.
selectedValue
=
value
.
name
!;
print
(
"hfjkshfg
${provider.selectedID}
"
,
"hfjkshfg"
+
provider
.
selectedID
.
toString
(),
);
}
}
...
...
@@ -898,9 +905,14 @@ class _AllpaymentrequesitionlistsbymodesState
},
);
},
).
whenComplete
(()
{
).
then
((
value
)
{
// This runs only when the bottom sheet is explicitly popped
print
(
"closing Sheet"
);
// Use the original context with mounted check
if
(
context
.
mounted
)
{
WidgetsBinding
.
instance
.
addPostFrameCallback
((
timeStamp
)
{
if
(
context
.
mounted
)
{
var
provider
=
Provider
.
of
<
Requestionlistprovider
>(
context
,
listen:
false
,
...
...
@@ -917,7 +929,9 @@ class _AllpaymentrequesitionlistsbymodesState
""
,
""
,
);
}
});
}
});
}
...
...
lib/screens/finance/FileViewer.dart
View file @
642da22c
...
...
@@ -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'
;
...
...
@@ -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,9 +57,9 @@ class _FileviewerState extends State<Fileviewer> {
}
var
Finalurl
;
@override
void
initState
()
{
// loadData();
pullToRefreshController
=
kIsWeb
?
null
...
...
@@ -71,16 +77,32 @@ class _FileviewerState extends State<Fileviewer> {
}
},
);
// print("URL:${widget.url}");
// 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,23 +113,86 @@ 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
,
...
...
@@ -120,22 +205,36 @@ 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
,
...
...
@@ -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}
"
);
},
),
// 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
],
),
),
],
),
),
),
),
),
],
);
default
:
return
Container
();
}
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
)
{
...
...
lib/screens/finance/PaymentRequestionListDetails.dart
View file @
642da22c
...
...
@@ -1446,7 +1446,8 @@ class _PaymentrequestionlistdetailsState
provider
.
selectedID
=
value
.
id
!;
provider
.
selectedValue
=
value
.
name
!;
print
(
"hfjkshfg
${provider.selectedID}
"
,
"hfjkshfg"
+
provider
.
selectedID
.
toString
(),
);
}
}
...
...
@@ -1511,6 +1512,12 @@ 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
,
...
...
lib/screens/hrm/AdvanceListScreen.dart
0 → 100644
View file @
642da22c
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
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