Merge remote-tracking branch 'origin/export_module_ui_ux_enhancement' into export_module_ui_ux_enhancement

main_production_RSG
WaseemAbbasi22 2 months ago
commit 2c90df8d80

@ -197,6 +197,40 @@ class ApiManager {
return streamedResponse;
}
/// POST FormData request that returns StreamedResponse for progress tracking
/// This method is specifically for form-data requests (multipart/form-data)
Future<http.StreamedResponse> postFormDataStreaming(String url, {Map<String, String>? headers, required Map<String, String> body}) async {
// Create headers for form data
Map<String, String> formHeaders = {
'X-Timezone-Offset': DateTime.now().timeZoneOffset.toString().split(".").first,
if (user != null) 'Authorization': 'Bearer ${user!.token}',
if (assetGroup != null) 'AssetGroup': assetGroup!.id.toString(),
};
headers ??= {};
headers.addAll(formHeaders);
Uri url0 = Uri.parse(url);
if (!kReleaseMode) {
print("URL:$url0");
print("Headers:$headers");
print("FormData Body:$body");
}
var request = http.MultipartRequest('POST', url0);
request.fields.addAll(body);
request.headers.addAll(headers);
http.StreamedResponse streamedResponse = await _httpClient.send(request);
// Handle 401 immediately
if (streamedResponse.statusCode == 401) {
showLoginDialog();
}
return streamedResponse;
}
Future<http.Response> delete(String url, {Map<String, String>? headers, required Map<String, dynamic> body}) async {
headers ??= {};

@ -556,5 +556,9 @@ class URLs {
static get getAllWorkOrderColumnsMetadata => "$_baseUrl/CallRequest/columns/metadata";
static get exportAllWorkOrder => "$_baseUrl/CallRequest/Export";
// Print/Report URLs
static get generateReport => "$_baseUrl/ReportPDF/GenerateReport";
//4104 attachment type lookup
}

@ -42,7 +42,11 @@ class _AssetDeliveryStageTabPageState extends State<AssetDeliveryStageTabPage> {
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 16.toScreenWidth, right: 16.toScreenWidth, top: 12.toScreenHeight),
decoration: BoxDecoration(color: context.isDark ? AppColor.neutral50 : AppColor.white10, borderRadius: BorderRadius.circular(10)),
decoration: BoxDecoration(
color: AppColor.tabBarBackground(context),
border: Border.all(color: AppColor.tabBarBorder(context)),
borderRadius: BorderRadius.circular(20),
),
child: TabBar(
padding: EdgeInsets.symmetric(vertical: 4.toScreenHeight, horizontal: 4.toScreenWidth),
labelColor: context.isDark ? AppColor.neutral30 : AppColor.black20,
@ -52,7 +56,11 @@ class _AssetDeliveryStageTabPageState extends State<AssetDeliveryStageTabPage> {
indicatorPadding: EdgeInsets.zero,
indicatorSize: TabBarIndicatorSize.tab,
dividerColor: Colors.transparent,
indicator: BoxDecoration(color: context.isDark ? AppColor.neutral60 : AppColor.neutral110, borderRadius: BorderRadius.circular(7)),
overlayColor: WidgetStateProperty.all(Colors.transparent),
indicator: BoxDecoration(
color: AppColor.background(context),
borderRadius: BorderRadius.circular(15),
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.03), blurRadius: 5, offset: const Offset(0, 0), spreadRadius: 0)]),
tabs: [
Tab(text: context.translation.details, height: 57.toScreenHeight),
Tab(text: context.translation.historyLogs, height: 57.toScreenHeight),
@ -73,7 +81,6 @@ class _AssetDeliveryStageTabPageState extends State<AssetDeliveryStageTabPage> {
));
}
String getPageTitle({AssetDeliveryStageEnum? deliveryStage}) {
if (deliveryStage == AssetDeliveryStageEnum.deliveryInspection) {
return 'Delivery Inspection'.addTranslation;

@ -77,7 +77,11 @@ class _AssetInventoryPageState extends State<AssetInventoryPage> {
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 16.toScreenWidth, right: 16.toScreenWidth, top: 12.toScreenHeight),
decoration: BoxDecoration(color: context.isDark ? AppColor.neutral50 : AppColor.white10, borderRadius: BorderRadius.circular(10)),
decoration: BoxDecoration(
color: AppColor.tabBarBackground(context),
border: Border.all(color: AppColor.tabBarBorder(context)),
borderRadius: BorderRadius.circular(20),
),
child: TabBar(
padding: EdgeInsets.symmetric(vertical: 4.toScreenHeight, horizontal: 4.toScreenWidth),
labelColor: context.isDark ? AppColor.neutral30 : AppColor.black20,
@ -87,7 +91,11 @@ class _AssetInventoryPageState extends State<AssetInventoryPage> {
indicatorPadding: EdgeInsets.zero,
indicatorSize: TabBarIndicatorSize.tab,
dividerColor: Colors.transparent,
indicator: BoxDecoration(color: context.isDark ? AppColor.neutral60 : AppColor.neutral110, borderRadius: BorderRadius.circular(7)),
overlayColor: WidgetStateProperty.all(Colors.transparent),
indicator: BoxDecoration(
color: AppColor.background(context),
borderRadius: BorderRadius.circular(15),
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.03), blurRadius: 5, offset: const Offset(0, 0), spreadRadius: 0)]),
onTap: (index) {},
tabs: [
Tab(text: 'Session Details'.addTranslation, height: 57.toScreenHeight),

@ -1,9 +1,11 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/helper/utils.dart';
@ -12,16 +14,18 @@ import 'package:test_sa/models/enums/work_order_next_step.dart';
import 'package:test_sa/models/helper_data_models/workorder/work_order_helper_models.dart';
import 'package:test_sa/modules/cm_module/cm_detail_provider.dart';
import 'package:test_sa/modules/cm_module/views/components/bottom_sheets/service_request_bottomsheet.dart';
import 'package:test_sa/modules/cx_module/chat/chat_page.dart';
import 'package:test_sa/modules/cx_module/chat/chat_provider.dart';
import 'package:test_sa/modules/cx_module/chat/chat_widget.dart';
import 'package:test_sa/modules/cx_module/survey/survey_page.dart';
import 'package:test_sa/modules/export_module/provider/export_provider.dart';
import 'package:test_sa/modules/loan_module/pages/create_loan_request_page.dart';
import 'package:test_sa/modules/loan_module/pages/loan_equipment_detail_page.dart';
import 'package:test_sa/modules/loan_module/provider/loan_provider.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
import 'package:open_file/open_file.dart';
import 'package:share_plus/share_plus.dart';
import 'dart:developer';
import 'views/components/history_log_view.dart';
import 'views/components/service_request_detail_view.dart';
@ -142,11 +146,11 @@ class _CMDetailPageState extends State<CMDetailPage> {
return Container(
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6),
margin: const EdgeInsets.only(left: 2),
decoration: BoxDecoration(color: AppColor.background(context) ,border: Border.all(color: AppColor.border2Color(context)), borderRadius: BorderRadius.circular(16)),
decoration: BoxDecoration(color: AppColor.background(context), border: Border.all(color: AppColor.border2Color(context)), borderRadius: BorderRadius.circular(16)),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.add, size: 20, color:AppColor.textStyleColor(context)),
Icon(Icons.add, size: 20, color: AppColor.textStyleColor(context)),
Text(
"Loan",
style: AppTextStyles.bodyText2.copyWith(color: AppColor.textStyleColor(context)),
@ -170,6 +174,11 @@ class _CMDetailPageState extends State<CMDetailPage> {
return const SizedBox();
}
}),
// Print Button
IconButton(
icon: const Icon(Icons.print),
onPressed: _handlePrint,
),
IconButton(
icon: const Icon(Icons.home),
onPressed: () {
@ -187,10 +196,14 @@ class _CMDetailPageState extends State<CMDetailPage> {
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 16.toScreenWidth, right: 16.toScreenWidth, top: 12.toScreenHeight),
decoration: BoxDecoration(color: context.isDark ? AppColor.neutral50 : AppColor.white10, borderRadius: BorderRadius.circular(10)),
decoration: BoxDecoration(
color: AppColor.tabBarBackground(context),
border: Border.all(color: AppColor.tabBarBorder(context)),
borderRadius: BorderRadius.circular(20),
),
child: TabBar(
//controller: _tabController,
padding: EdgeInsets.symmetric(vertical: 4.toScreenHeight, horizontal: 4.toScreenWidth),
padding: EdgeInsets.symmetric(vertical: 6.toScreenHeight, horizontal: 6.toScreenWidth),
labelColor: context.isDark ? AppColor.neutral30 : AppColor.black20,
unselectedLabelColor: context.isDark ? AppColor.neutral30 : AppColor.black20,
unselectedLabelStyle: AppTextStyles.bodyText,
@ -198,7 +211,11 @@ class _CMDetailPageState extends State<CMDetailPage> {
indicatorPadding: EdgeInsets.zero,
indicatorSize: TabBarIndicatorSize.tab,
dividerColor: Colors.transparent,
indicator: BoxDecoration(color: context.isDark ? AppColor.neutral60 : AppColor.neutral110, borderRadius: BorderRadius.circular(7)),
overlayColor: WidgetStateProperty.all(Colors.transparent),
indicator: BoxDecoration(
color: AppColor.background(context),
borderRadius: BorderRadius.circular(15),
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.03), blurRadius: 5, offset: const Offset(0, 0), spreadRadius: 0)]),
onTap: (index) {
// setState(() {});
},
@ -237,4 +254,138 @@ class _CMDetailPageState extends State<CMDetailPage> {
cProvider.getUserAutoLoginTokenSilent(moduleId, widget.requestId, title, myEmployeeId, receiver);
}
/// Show success dialog after file is downloaded - using same implementation as export module
Future<void> _showSuccessDialog(String filePath) async {
if (!mounted) return;
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext dialogContext) {
return Dialog(
backgroundColor: AppColor.background(context),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
'assets/images/done.svg',
width: 80,
height: 80,
),
const SizedBox(height: 24),
Text(
'Print Successful',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: AppColor.headingTextColor(context),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 12),
Text(
'Your PDF has been generated successfully',
style: TextStyle(
fontSize: 14,
color: AppColor.lightTextColor(context),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 32),
Row(
children: [
Expanded(
child: AppFilledButton(
onPressed: () async {
Navigator.of(dialogContext).pop();
log('📂 Opening printed file...');
final result = await OpenFile.open(filePath);
if (result.type != ResultType.done) {
log('⚠️ Could not open file: ${result.message}');
'No app found to open this file type. File saved to: $filePath'.showToast;
}
},
label: 'Open',
buttonColor: AppColor.primary10,
showIcon: true,
icon: const Icon(Icons.folder_open, color: Colors.white),
height: 48,
),
),
const SizedBox(width: 12),
Expanded(
child: AppFilledButton(
onPressed: () async {
Navigator.of(dialogContext).pop();
log('📤 Sharing printed file...');
try {
final xFile = XFile(filePath);
await Share.shareXFiles(
[xFile],
text: 'Printed file',
);
log('✅ File shared successfully');
} catch (e) {
log('❌ Error sharing file: $e');
'Failed to share file: $e'.showToast;
}
},
label: 'Share',
buttonColor: AppColor.green40,
textColor: AppColor.green60,
showIcon: true,
icon: Icon(Icons.share, color: AppColor.green60),
height: 48,
),
),
],
),
],
),
),
);
},
);
}
/// Handle print button press
Future<void> _handlePrint() async {
try {
final exportProvider = Provider.of<ExportApiProvider>(context, listen: false);
final filePath = await exportProvider.printWorkOrder(
widget.requestId,
widget.moduleId,
context,
);
if (filePath != null) {
await _showSuccessDialog(filePath);
} else if (exportProvider.errorMessage != null) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(exportProvider.errorMessage!),
backgroundColor: AppColor.red60,
),
);
}
}
} catch (error) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Print failed: $error'),
backgroundColor: AppColor.red60,
),
);
}
}
}
}

@ -35,7 +35,7 @@ class FooterActionButton {
static Widget footerContainer({required Widget child, required BuildContext context}) {
return Container(
alignment: Alignment.bottomCenter,
padding: const EdgeInsets.only(left: 16, right: 16, top: 12, bottom: 8),
padding: const EdgeInsets.only(left: 16, right: 16, top: 12, bottom: 12),
color: AppColor.background(context),
child: SafeArea(child: child),
);
@ -216,8 +216,7 @@ class FooterActionButton {
children: [
AppFilledButton(
label: context.translation.activities,
textColor: context.isDark ? AppColor.black10 : Colors.white,
buttonColor: context.isDark ? AppColor.primary10 : AppColor.neutral50,
buttonColor: AppColor.primary10,
onPressed: () async {
// ServiceRequestBottomSheet.activityTypeBottomSheet(context: context);
Navigator.push(context, MaterialPageRoute(builder: (context) => const ActivitiesListView()));

@ -73,43 +73,43 @@ class _ServiceRequestDetailViewState extends State<ServiceRequestDetailView> {
: requestProvider.currentWorkOrder == null
? const NoDataFound()
: Column(
children: [
SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
workOrderDetailCard(context, requestProvider.currentWorkOrder!.data!, _userProvider, requestProvider),
initialVisitCard(requestDetailProvider: requestProvider, userProvider: _userProvider),
12.height,
assetDetailCard(requestDetailProvider: requestProvider, userProvider: _userProvider),
12.height,
if (context.userProvider.user!.type == UsersTypes.engineer &&
!requestProvider.isReadOnlyRequest &&
requestProvider.currentWorkOrder!.data!.nextStep?.workOrderNextStepEnum == WorkOrderNextStepEnum.activity) ...[
costCard(context, requestProvider),
children: [
SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
workOrderDetailCard(context, requestProvider.currentWorkOrder!.data!, _userProvider, requestProvider),
initialVisitCard(requestDetailProvider: requestProvider, userProvider: _userProvider),
12.height,
assetDetailCard(requestDetailProvider: requestProvider, userProvider: _userProvider),
12.height,
if (context.userProvider.user!.type == UsersTypes.engineer &&
!requestProvider.isReadOnlyRequest &&
requestProvider.currentWorkOrder!.data!.nextStep?.workOrderNextStepEnum == WorkOrderNextStepEnum.activity) ...[
costCard(context, requestProvider),
12.height,
],
contactInfoCard(context, requestProvider.currentWorkOrder!.data!),
20.height,
],
contactInfoCard(context, requestProvider.currentWorkOrder!.data!),
20.height,
],
),
).expanded,
SafeArea(
top: false,
right: false,
left: false,
child: FooterActionButton.requestDetailsFooterWidget(
workOrderNextStepStatus: requestProvider.currentWorkOrder!.data!.nextStep!.workOrderNextStepEnum!,
status: requestProvider.currentWorkOrder?.data?.status,
isEmpIsAssigned: requestProvider.currentWorkOrder!.data!.assignedEmployee != null,
activities: requestProvider.currentWorkOrder!.data?.activities ?? [],
userProvider: _userProvider,
context: context),
).toShadowContainer(context, padding: 0, showShadow: false, borderRadius: 0),
],
);
),
).expanded,
SafeArea(
top: false,
right: false,
left: false,
child: FooterActionButton.requestDetailsFooterWidget(
workOrderNextStepStatus: requestProvider.currentWorkOrder!.data!.nextStep!.workOrderNextStepEnum!,
status: requestProvider.currentWorkOrder?.data?.status,
isEmpIsAssigned: requestProvider.currentWorkOrder!.data!.assignedEmployee != null,
activities: requestProvider.currentWorkOrder!.data?.activities ?? [],
userProvider: _userProvider,
context: context),
)
],
);
});
}

@ -70,8 +70,9 @@ class _MaintenanceRequestFormState extends State<MaintenanceRequestForm> with Si
Container(
margin: EdgeInsets.only(left: 16.toScreenWidth, right: 16.toScreenWidth, top: 12.toScreenHeight),
decoration: BoxDecoration(
color: context.isDark ? AppColor.neutral50 : AppColor.white10,
borderRadius: BorderRadius.circular(10),
color: AppColor.tabBarBackground(context),
border: Border.all(color: AppColor.tabBarBorder(context)),
borderRadius: BorderRadius.circular(20),
),
child: TabBar(
padding: EdgeInsets.symmetric(vertical: 4.toScreenHeight, horizontal: 4.toScreenWidth),
@ -82,10 +83,11 @@ class _MaintenanceRequestFormState extends State<MaintenanceRequestForm> with Si
indicatorPadding: EdgeInsets.zero,
indicatorSize: TabBarIndicatorSize.tab,
dividerColor: Colors.transparent,
overlayColor: WidgetStateProperty.all(Colors.transparent),
indicator: BoxDecoration(
color: context.isDark ? AppColor.neutral60 : AppColor.neutral110,
borderRadius: BorderRadius.circular(7),
),
color: AppColor.background(context),
borderRadius: BorderRadius.circular(15),
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.03), blurRadius: 5, offset: const Offset(0, 0), spreadRadius: 0)]),
tabs: [
Tab(text: context.translation.internal, height: 57.toScreenHeight),
Tab(text: context.translation.external, height: 57.toScreenHeight),

@ -240,4 +240,155 @@ class ExportApiProvider extends ChangeNotifier {
_errorMessage = null;
notifyListeners();
}
/// Print work order data as PDF
Future<String?> printWorkOrder(int workOrderId, int moduleId, BuildContext context) async {
_isExporting = true;
_errorMessage = null;
notifyListeners();
ProgressDialog pd = ProgressDialog(context: context);
pd.show(
max: 100,
msg: 'Preparing print...',
progressBgColor: Colors.transparent,
progressValueColor: AppColor.primary10,
msgFontWeight: FontWeight.normal,
valueColor: AppColor.primary10,
);
IOSink? sink;
File? outputFile;
try {
if (!context.mounted) return null;
pd.update(msg: 'Sending print request...');
log('📡 Sending print request...');
final startTime = DateTime.now();
// Prepare form data
final Map<String, String> formData = {
'properties': json.encode({
'workOrderId': workOrderId,
'moduleId': moduleId,
}),
};
final streamedResponse = await ApiManager.instance.postFormDataStreaming(
URLs.generateReport,
body: formData
);
_stateCode = streamedResponse.statusCode;
final responseTime = DateTime.now().difference(startTime).inMilliseconds;
log('✅ Print response received in ${responseTime}ms - Status: ${streamedResponse.statusCode}');
if (streamedResponse.statusCode >= 200 && streamedResponse.statusCode < 300) {
final contentLength = streamedResponse.contentLength ?? 0;
log('📊 Content length: ${contentLength > 0 ? "${(contentLength / 1024 / 1024).toStringAsFixed(2)} MB" : "Unknown"}');
if (!context.mounted) {
await streamedResponse.stream.drain();
return null;
}
final String dir = (await getApplicationDocumentsDirectory()).path;
final String filePath = "$dir/print_${DateTime.now().millisecondsSinceEpoch}.pdf";
outputFile = File(filePath);
sink = outputFile.openWrite();
int received = 0;
int lastProgress = 15;
final downloadStartTime = DateTime.now();
pd.update(value: 15, msg: 'Generating PDF...');
await for (var chunk in streamedResponse.stream) {
sink.add(chunk);
received += chunk.length;
if (!context.mounted) {
await sink.close();
await outputFile.delete();
return null;
}
if (contentLength > 0) {
final progress = (15 + ((received / contentLength) * 70)).clamp(15, 85).toInt();
if (progress > lastProgress) {
lastProgress = progress;
final downloadedMB = (received / 1024 / 1024).toStringAsFixed(2);
final totalMB = (contentLength / 1024 / 1024).toStringAsFixed(2);
pd.update(
value: progress,
msg: 'Generating PDF: $downloadedMB MB / $totalMB MB',
);
}
} else {
final downloadedMB = (received / 1024 / 1024).toStringAsFixed(2);
pd.update(
msg: 'Generating PDF: $downloadedMB MB',
);
}
}
await sink.close();
sink = null;
final downloadTime = DateTime.now().difference(downloadStartTime).inMilliseconds;
final fileSizeMB = (received / 1024 / 1024).toStringAsFixed(2);
log('✅ PDF generated successfully - Size: ${fileSizeMB} MB');
if (!context.mounted) {
await outputFile.delete();
return null;
}
pd.close();
return filePath;
} else {
if (context.mounted) {
pd.close();
}
try {
final responseBody = await streamedResponse.stream.bytesToString();
final responseData = json.decode(responseBody);
_errorMessage = responseData['message'] ?? 'Print failed';
} catch (e) {
log('❌ Error parsing print error response: $e');
_errorMessage = 'Print failed with status: ${streamedResponse.statusCode}';
}
return null;
}
} catch (error, stackTrace) {
if (sink != null) {
try {
await sink.close();
} catch (e) {
log('⚠️ Error closing sink: $e');
}
}
if (outputFile != null && await outputFile.exists()) {
try {
await outputFile.delete();
log('🗑️ Deleted incomplete file');
} catch (e) {
log('⚠️ Error deleting incomplete file: $e');
}
}
if (context.mounted) {
pd.close();
}
log('❌ Error printing data: $error');
_stateCode = -1;
_errorMessage = 'An error occurred during print: $error';
return null;
} finally {
_isExporting = false;
notifyListeners();
log('🔚 Print process finished');
}
}
}

@ -29,7 +29,11 @@ class PullOutDetailsPage extends StatelessWidget {
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 16.toScreenWidth, right: 16.toScreenWidth, top: 12.toScreenHeight),
decoration: BoxDecoration(color: context.isDark ? AppColor.neutral50 : AppColor.white10, borderRadius: BorderRadius.circular(10)),
decoration: BoxDecoration(
color: AppColor.tabBarBackground(context),
border: Border.all(color: AppColor.tabBarBorder(context)),
borderRadius: BorderRadius.circular(20),
),
child: TabBar(
padding: EdgeInsets.symmetric(vertical: 4.toScreenHeight, horizontal: 4.toScreenWidth),
labelColor: context.isDark ? AppColor.neutral30 : AppColor.black20,
@ -39,7 +43,11 @@ class PullOutDetailsPage extends StatelessWidget {
indicatorPadding: EdgeInsets.zero,
indicatorSize: TabBarIndicatorSize.tab,
dividerColor: Colors.transparent,
indicator: BoxDecoration(color: context.isDark ? AppColor.neutral60 : AppColor.neutral110, borderRadius: BorderRadius.circular(7)),
overlayColor: WidgetStateProperty.all(Colors.transparent),
indicator: BoxDecoration(
color: AppColor.background(context),
borderRadius: BorderRadius.circular(15),
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.03), blurRadius: 5, offset: const Offset(0, 0), spreadRadius: 0)]),
onTap: (index) {
// setState(() {});
},

@ -148,8 +148,9 @@ class _UpdatePpmState extends State<UpdatePpm> with TickerProviderStateMixin {
Container(
margin: EdgeInsets.only(left: 16.toScreenWidth, right: 16.toScreenWidth, top: 16.toScreenHeight, bottom: 8),
decoration: BoxDecoration(
color: context.isDark ? AppColor.neutral50 : AppColor.white10,
borderRadius: BorderRadius.circular(10),
color: AppColor.tabBarBackground(context),
border: Border.all(color: AppColor.tabBarBorder(context)),
borderRadius: BorderRadius.circular(20),
),
child: TabBar(
padding: EdgeInsets.symmetric(vertical: 4.toScreenHeight, horizontal: 4.toScreenWidth),
@ -160,10 +161,11 @@ class _UpdatePpmState extends State<UpdatePpm> with TickerProviderStateMixin {
indicatorPadding: EdgeInsets.zero,
indicatorSize: TabBarIndicatorSize.tab,
dividerColor: Colors.transparent,
overlayColor: WidgetStateProperty.all(Colors.transparent),
indicator: BoxDecoration(
color: context.isDark ? AppColor.neutral60 : AppColor.neutral110,
borderRadius: BorderRadius.circular(7),
),
color: AppColor.background(context),
borderRadius: BorderRadius.circular(15),
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.03), blurRadius: 5, offset: const Offset(0, 0), spreadRadius: 0)]),
onTap: (index) {
tabIndex = index;
if (tabIndex == 0) {

@ -96,10 +96,13 @@ class AppColor {
static const Color openChartColor = Color(0xff226187);
static const Color inProgressChartColor = Color(0xff3DA5E5);
static const Color completeChartColor = Color(0xff92D9F8);
static const Color openColor = Color(0xff3DA5E5);
static const Color inProgressColor = Color(0xffFDE19B);
static const Color completedColor = Color(0xff54C166);
static const Color overdueColor = Color(0xffFFC945);
static const Color highPriorityColor = red30;
static const Color unassignedColor = Color(0xff9F9FA8);
static const Color lowPriorityColor = Color(0xff54C166);
// static Color greenStatus(BuildContext context) => context.isDark ? const Color(0xff1FA269) : green50;
static Color greenStatus(BuildContext context) => context.isDark ? const Color(0xff54C166) : green70.withOpacity(.5);
@ -116,6 +119,10 @@ class AppColor {
static Color background(BuildContext context) => context.isDark ? containerDark : Colors.white;
static Color tabBarBackground(BuildContext context) => context.isDark ? disableDark : neutral90;
static Color tabBarBorder(BuildContext context) => context.isDark ? const Color(0xffCCCCCC) : neutral30;
static Color scaffoldBackground(BuildContext context) => context.isDark ? backgroundDark : backgroundLight;
static Color dropDownBackground(BuildContext context) => context.isDark ? dropDownDark : backgroundLight;

@ -47,7 +47,7 @@ class AppFilledButton extends StatelessWidget {
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: disableButton ?context.isDark ?AppColor.neutral20 :AppColor.neutral140 : (buttonColor ?? AppColor.blueStatus(context)),
color: disableButton ?context.isDark ?AppColor.neutral20 :AppColor.neutral140 : (buttonColor ?? AppColor.primary10),
border: showBorder ? Border.all(color: textColor ?? AppColor.background(context)) : null,
),
child: loading

@ -126,9 +126,10 @@ class _MyRequestsPageState extends State<MyRequestsPage> {
padding: const EdgeInsets.only(left: 16, right: 8),
alignment: Alignment.center,
decoration: ShapeDecoration(
color: context.isDark ? AppColor.neutral50 : AppColor.neutral30,
color: AppColor.dropDownBackground(context),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
borderRadius: BorderRadius.circular(6),
side: BorderSide(color: AppColor.borderColor(context)),
),
),
child: Row(
@ -203,9 +204,9 @@ class _MyRequestsPageState extends State<MyRequestsPage> {
// ),
decoration: ShapeDecoration(
color: //selectedRequest == index ?
AppColor.background(context) ,//: AppColor.unSelectedButtonColor(context).withOpacity(snapshot.isAllLoading ? 0.7 : 1),
AppColor.background(context), //: AppColor.unSelectedButtonColor(context).withOpacity(snapshot.isAllLoading ? 0.7 : 1),
shape: RoundedRectangleBorder(
side: BorderSide(width: 1, color: selectedRequest == index ? AppColor.primary10 : AppColor.borderColor(context)),
side: BorderSide(width: 1, color: selectedRequest == index ? AppColor.primary10 : AppColor.borderColor(context)),
borderRadius: BorderRadius.circular(6),
),
shadows: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 14, offset: const Offset(0, 0), spreadRadius: 0)],

@ -13,7 +13,6 @@ import 'package:test_sa/views/widgets/date_and_time/date_picker.dart';
import 'package:test_sa/views/widgets/loaders/lazy_loading.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
import 'models/swipe_transaction_history.dart';
class SwipeHistoryView extends StatefulWidget {
@ -25,31 +24,33 @@ class SwipeHistoryView extends StatefulWidget {
State<SwipeHistoryView> createState() => _SwipeHistoryViewState();
}
class _SwipeHistoryViewState extends State<SwipeHistoryView> {
DateTime dateFrom = DateTime.now();
DateTime dateTo = DateTime.now();
UserProvider? _userProvider;
@override
void initState() {
getSwipeHistory();
super.initState();
}
void getSwipeHistory () {
_userProvider = Provider.of<UserProvider>(context,listen:false);
void getSwipeHistory() {
_userProvider = Provider.of<UserProvider>(context, listen: false);
WidgetsBinding.instance.addPostFrameCallback((_) async {
await _userProvider!.getSwipeTransactionHistory(userId: _userProvider!.user!.userID!,dateFrom: dateFrom,dateTo: dateTo);
await _userProvider!.getSwipeTransactionHistory(userId: _userProvider!.user!.userID!, dateFrom: dateFrom, dateTo: dateTo);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const DefaultAppBar(title: 'Swipe History',),
return Scaffold(
appBar: const DefaultAppBar(
title: 'Swipe History',
),
body: Column(
crossAxisAlignment:CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
8.height,
Row(
children: [
@ -106,10 +107,8 @@ class _SwipeHistoryViewState extends State<SwipeHistoryView> {
);
if (selectedDateTime != null) {
setState(() {
dateTo = selectedDateTime;
dateTo = selectedDateTime;
});
}
}
});
@ -121,48 +120,46 @@ class _SwipeHistoryViewState extends State<SwipeHistoryView> {
12.height,
AppFilledButton(label: context.translation.search, maxWidth: false, onPressed: getSwipeHistory),
8.height,
const Divider(thickness: 2,color:AppColor.white60 ,),
Consumer<UserProvider>(
builder: (context, snapshot,child) {
return SwipeHistoryList(snapshot.swipeHistory ?? [], snapshot.isLoading).expanded;
}
const Divider(
thickness: 2,
color: AppColor.white60,
),
Consumer<UserProvider>(builder: (context, snapshot, child) {
return SwipeHistoryList(snapshot.swipeHistory ?? [], snapshot.isLoading).expanded;
}),
],
).paddingAll(20),
);
}
}
class SwipeHistoryList extends StatelessWidget {
List<SwipeHistory> list;
bool isLoading;
SwipeHistoryList(this.list, this.isLoading, {Key ?key}) : super(key: key);
SwipeHistoryList(this.list, this.isLoading, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return (list.isEmpty && !isLoading)
? NoDataFound(message: context.translation.noDataFound).center
: ListView.separated(
padding: EdgeInsets.only(top: 12.toScreenHeight),
itemBuilder: (cxt, index) {
if (isLoading) return const SizedBox().toRequestShimmer(cxt, isLoading);
return SwipeHistoryCard(list[index]);
},
separatorBuilder: (cxt, index) => 12.height,
itemCount: isLoading ? 6 : list.length);
padding: EdgeInsets.only(top: 12.toScreenHeight),
itemBuilder: (cxt, index) {
if (isLoading) return const SizedBox().toRequestShimmer(cxt, isLoading);
return SwipeHistoryCard(list[index]);
},
separatorBuilder: (cxt, index) => 12.height,
itemCount: isLoading ? 6 : list.length);
}
}
class SwipeHistoryCard extends StatelessWidget {
final SwipeHistory swipeHistory;
final bool showShadow;
const SwipeHistoryCard(this.swipeHistory, {Key? key, this.showShadow = true}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
@ -172,7 +169,8 @@ class SwipeHistoryCard extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(swipeHistory.swipeTime!.toServiceRequestDetailsFormat, textAlign: TextAlign.end, style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50)),
Text(swipeHistory.swipeTime!.toServiceRequestDetailsFormat,
textAlign: TextAlign.end, style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50)),
],
),
8.height,
@ -185,4 +183,3 @@ class SwipeHistoryCard extends StatelessWidget {
).toShadowContainer(context, showShadow: showShadow);
}
}

@ -8,29 +8,36 @@ import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
class SwipeSuccessView extends StatelessWidget {
static const routeName = '/swipe_success_view';
const SwipeSuccessView({Key ?key}) : super(key: key);
const SwipeSuccessView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
return Scaffold(
body: SafeArea(
child: Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment:CrossAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
'swipe_success'.toSvgAsset(),
17.height,
context.translation.successful.heading4(context).custom(color: AppColor.white936),
Text(
context.translation.successful,
style: AppTextStyles.heading4.copyWith(color: AppColor.headingTextColor(context)),
),
8.height,
context.translation.youHaveSuccessfullyMarkedYourAttendance.bodyText2(context).custom(color: AppColor.neutral120),
Text(
context.translation.youHaveSuccessfullyMarkedYourAttendance,
style: AppTextStyles.bodyText2.copyWith(color: AppColor.lightTextColor(context)),
),
],
).expanded,
AppFilledButton(
label: 'Close',
maxWidth: true,
onPressed:(){
onPressed: () {
Navigator.pop(context);
},
),
@ -41,7 +48,6 @@ class SwipeSuccessView extends StatelessWidget {
}
}
// ClipOval(
// child: Container(
// color: Colors.grey.withOpacity(0.2),

@ -97,9 +97,10 @@ class _MyAssetsPageState extends State<MyAssetsPage> {
padding: const EdgeInsets.only(left: 16, right: 8),
alignment: Alignment.center,
decoration: ShapeDecoration(
color: context.isDark ? AppColor.neutral50 : AppColor.neutral30,
color: AppColor.dropDownBackground(context),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
borderRadius: BorderRadius.circular(6),
side: BorderSide(color: AppColor.borderColor(context)),
),
),
child: Row(

Loading…
Cancel
Save