Merge branch 'refs/heads/ui_ux_rollout_merge' into ui_ux_rollout_merge_notification_settings

# Conflicts:
#	lib/dashboard_latest/widgets/app_bar_widget.dart
#	lib/modules/cm_module/cm_detail_page.dart
ui_ux_rollout_merge_notification_settings
Sikander Saleem 2 months ago
commit 6087f55cc8

@ -0,0 +1,13 @@
<svg width="130" height="150" viewBox="0 0 130 150" xmlns="http://www.w3.org/2000/svg">
<!-- front face -->
<rect x="30" y="46" width="65" height="48" rx="4" fill="none" stroke="#3D3D3A" stroke-width="2"/>
<!-- screen indicator -->
<rect x="40" y="56" width="26" height="16" rx="2" fill="none" stroke="#3D3D3A" stroke-width="1.2"/>
<!-- bottom face (highlighted) -->
<polygon points="30,94 50,114 115,114 95,94" fill="#F5C4B3" stroke="#993C1D" stroke-width="2"/>
<!-- right side face -->
<polygon points="95,46 115,26 115,74 95,94" fill="none" stroke="#3D3D3A" stroke-width="2"/>
<!-- arrow pointing up into bottom face -->
<line x1="73" y1="142" x2="73" y2="118" stroke="#3D3D3A" stroke-width="3" stroke-linecap="round"/>
<polygon points="73,112 66,124 80,124" fill="#3D3D3A"/>
</svg>

After

Width:  |  Height:  |  Size: 808 B

@ -0,0 +1,13 @@
<svg width="150" height="150" viewBox="0 0 150 150" xmlns="http://www.w3.org/2000/svg">
<!-- front face (highlighted, left side facing camera) -->
<rect x="32" y="52" width="56" height="58" rx="4" fill="#9FE1CB" stroke="#0F6E56" stroke-width="2"/>
<!-- screen indicator -->
<rect x="42" y="62" width="24" height="15" rx="2" fill="none" stroke="#0F6E56" stroke-width="1.2"/>
<!-- top face -->
<polygon points="32,52 52,32 118,32 88,52" fill="none" stroke="#3D3D3A" stroke-width="2"/>
<!-- right side face -->
<polygon points="88,52 118,32 118,90 88,110" fill="none" stroke="#3D3D3A" stroke-width="2"/>
<!-- arrow pointing right onto left face -->
<line x1="5" y1="81" x2="28" y2="81" stroke="#3D3D3A" stroke-width="3" stroke-linecap="round"/>
<polygon points="34,81 22,74 22,88" fill="#3D3D3A"/>
</svg>

After

Width:  |  Height:  |  Size: 824 B

@ -0,0 +1,13 @@
<svg width="150" height="150" viewBox="0 0 150 150" xmlns="http://www.w3.org/2000/svg">
<!-- front-left face -->
<polygon points="28,52 58,32 58,90 28,110" fill="none" stroke="#3D3D3A" stroke-width="2"/>
<!-- top face -->
<polygon points="28,52 58,32 95,32 65,52" fill="none" stroke="#3D3D3A" stroke-width="2"/>
<!-- right face highlighted, facing camera -->
<polygon points="65,52 95,32 95,90 65,110" fill="#FAC775" stroke="#854F0B" stroke-width="2"/>
<!-- screen indicator on front-left face for context -->
<rect x="34" y="60" width="16" height="14" rx="2" fill="none" stroke="#3D3D3A" stroke-width="1"/>
<!-- arrow pointing left onto right face -->
<line x1="145" y1="81" x2="122" y2="81" stroke="#3D3D3A" stroke-width="3" stroke-linecap="round"/>
<polygon points="116,81 128,74 128,88" fill="#3D3D3A"/>
</svg>

After

Width:  |  Height:  |  Size: 837 B

@ -0,0 +1,13 @@
<svg width="130" height="150" viewBox="0 0 130 150" xmlns="http://www.w3.org/2000/svg">
<!-- front face -->
<rect x="30" y="68" width="65" height="48" rx="4" fill="none" stroke="#3D3D3A" stroke-width="2"/>
<!-- screen indicator -->
<rect x="40" y="78" width="26" height="16" rx="2" fill="none" stroke="#3D3D3A" stroke-width="1.2"/>
<!-- top face (highlighted) -->
<polygon points="30,68 50,48 115,48 95,68" fill="#B5D4F4" stroke="#185FA5" stroke-width="2"/>
<!-- right side face -->
<polygon points="95,68 115,48 115,96 95,116" fill="none" stroke="#3D3D3A" stroke-width="2"/>
<!-- arrow pointing down onto top face -->
<line x1="73" y1="8" x2="73" y2="38" stroke="#3D3D3A" stroke-width="3" stroke-linecap="round"/>
<polygon points="73,44 66,32 80,32" fill="#3D3D3A"/>
</svg>

After

Width:  |  Height:  |  Size: 797 B

@ -19,9 +19,9 @@ import 'package:test_sa/new_views/pages/login_page.dart';
class _CachedResponse {
final http.Response response;
final DateTime timestamp;
_CachedResponse(this.response, this.timestamp);
bool isValid(Duration cacheDuration) {
return DateTime.now().difference(timestamp) < cacheDuration;
}
@ -41,26 +41,26 @@ class ApiManager {
User? user;
AssetGroup? assetGroup;
// OPTIMIZATION: HTTP Connection Pooling - Reuse connections instead of creating new ones
// This improves performance by 100-200ms on subsequent API calls
static final http.Client _httpClient = http.Client();
// OPTIMIZATION: Response Caching - Cache lookup/static data to reduce network calls
// This reduces API calls by 60% for frequently accessed but rarely changing data
static final Map<String, _CachedResponse> _cache = {};
static const Duration _defaultCacheDuration = Duration(hours: 1);
/// Clear all cached responses (call on logout or when needed)
void clearCache() {
_cache.clear();
}
/// Clear specific cache entry
void clearCacheEntry(String url) {
_cache.remove(_generateCacheKey(url));
}
/// Generate cache key from URL and user context
String _generateCacheKey(String url) {
// Include user and asset group in cache key to avoid cross-user pollution
@ -68,7 +68,7 @@ class ApiManager {
}
/// GET request with optional caching support
///
///
/// [url] - API endpoint URL
/// [headers] - Optional additional headers
/// [enableToastMessage] - Show toast message from API response
@ -86,12 +86,12 @@ class ApiManager {
headers ??= {};
headers.addAll(_headers);
Uri url0 = Uri.parse(url);
// OPTIMIZATION: Check cache first if enabled
if (useCache && !forceRefresh) {
final cacheKey = _generateCacheKey(url);
final cachedResponse = _cache[cacheKey];
if (cachedResponse != null && cachedResponse.isValid(cacheDuration ?? _defaultCacheDuration)) {
if (kDebugMode) {
print('📦 Cache HIT: $url');
@ -99,10 +99,10 @@ class ApiManager {
return cachedResponse.response;
}
}
// OPTIMIZATION: Use persistent HTTP client for connection pooling
http.Response response = await _httpClient.get(url0, headers: headers);
// OPTIMIZATION: Cache the response if caching is enabled
if (useCache && response.statusCode >= 200 && response.statusCode < 300) {
final cacheKey = _generateCacheKey(url);
@ -310,7 +310,7 @@ class ApiManager {
Uri url0 = Uri.parse(url);
// print(headers);
// log(json.encode(body));
// OPTIMIZATION: Use persistent HTTP client for connection pooling
var request = http.Request('PUT', url0);
request.body = json.encode(body);
@ -377,10 +377,9 @@ class ApiManager {
}
Future<http.Response> multiPart(String url, {Map<String, String>? headers, required Map<String, String> body, required List<Future<MultipartFile>> files}) async {
Map<String, String> headers0 = const {
'Content-Type': 'multipart/form-data',
};
Map<String, String> headers0 = _headers;
headers0["Content-Type"] = 'multipart/form-data';
headers ??= {};
headers.addAll(headers0);
@ -463,7 +462,7 @@ class ApiManager {
Navigator.pop(context);
Navigator.of(context).pushNamedAndRemoveUntil(LoginPage.routeName, (routes) => true);
}
/// Dispose HTTP client (call when app is closing if needed)
/// Note: In most cases, keep client alive for app lifetime for best performance
void dispose() {

@ -1,11 +1,11 @@
class URLs {
URLs._();
static const String appReleaseBuildNumber = "44";
static const String appReleaseBuildNumber = "47";
// static const host1 = "https://atomsm.hmg.com"; // production url
static const host1 = "https://atomsm.hmg.com"; // production url
// static const host1 = "https://atomsmdev.hmg.com"; // local DEV url
static const host1 = "https://atomsmuat.hmg.com"; // local UAT url
// static const host1 = "https://atomsmuat.hmg.com"; // local UAT url
// static final String _baseUrl = "$_host/mobile"; // host local UAT
static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis
@ -66,6 +66,7 @@ class URLs {
static get getFaultDescription => "$_baseUrl/Asset/GetDefectForModelDefinition"; // get
static get getAssetById => "$_baseUrl/Asset/GetAssetById?assetId="; // get
static get getAssetColumnsMetadata => "$_baseUrl/Asset/columns/metadata"; // get asset export columns metadata
static get saveAssetImages => "$_baseUrl/Asset/SaveAssetImages"; // get
static get getModels => "$_baseUrl/ModelDefinition/GetModelDefinitionAsset"; // get ?client=2
static get getAllRequestsAndCount => "$_baseUrl/CallRequest/GetAllRequestsAndCount"; // get
static get updateUserContactInformation => "$_baseUrl/Account/UpdateContactInfo"; // get

@ -742,6 +742,7 @@ class AllRequestsProvider extends ChangeNotifier {
List<int>? statusTransaction,
List<int>? priority,
List<int>? displayData,
bool? isAscending,
}) async {
if (isMultiFilterLoading == true) return -2;
if (_allRequestsAndCount == null || pagination) {
@ -758,6 +759,7 @@ class AllRequestsProvider extends ChangeNotifier {
body["pageNumber"] = pageNum++;
body["pageSize"] = pageItemNumber;
}
if (isAscending != null) body["isAscending"] = isAscending;
response = await ApiManager.instance.post(URLs.getAllRequestsAndCount, body: body);
stateCode = response.statusCode;

@ -9,6 +9,7 @@ import 'package:test_sa/controllers/api_routes/http_status_manger.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/models/device/asset_by_id_model.dart';
import 'package:test_sa/models/device/asset_search.dart';
import 'package:test_sa/models/generic_attachment_model.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/models/user.dart';
import 'package:test_sa/modules/asset_delivery_module/models/technical_inpection_asset_model.dart';
@ -152,9 +153,7 @@ class AssetProvider extends ChangeNotifier {
print(error);
throw (HttpStatusManger.getStatusMessage(status: -1, subtitle: appLocalizations));
}
_stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
// client's request was successfully received
Map<String, dynamic> assetData = json.decode(response.body)["data"];
_assetById = AssetByIdModel.fromJson(assetData);
return _assetById!;
@ -230,4 +229,32 @@ class AssetProvider extends ChangeNotifier {
return [];
}
}
Future<bool> uploadAssetImages(int assetId, TrainingImageData trainingImageData) async {
late Response response;
try {
List<Future<MultipartFile>> files = [];
files.add(MultipartFile.fromPath("LeftImage", trainingImageData.leftImage!.name!));
files.add(MultipartFile.fromPath("RightImage", trainingImageData.rightImage!.name!));
files.add(MultipartFile.fromPath("UpImage", trainingImageData.upImage!.name!));
files.add(MultipartFile.fromPath("DownImage", trainingImageData.downImage!.name!));
for (var i = 0; i < (trainingImageData.additionalImages?.length ?? 0); i++) {
files.add(
MultipartFile.fromPath('AdditionalImage', trainingImageData.additionalImages![i].name!),
);
}
response = await ApiManager.instance.multiPart(URLs.saveAssetImages + "?AssetId=$assetId", body: {}, files: files);
if (response.statusCode >= 200 && response.statusCode < 300) {
// client's request was successfully received
// Map<String, dynamic> assetData = json.decode(response.body)["data"];
// _assetById = AssetByIdModel.fromJson(assetData);
// return _assetById!;
return true;
}
return false;
} catch (error) {
return false;
}
}
}

@ -348,6 +348,7 @@ class DashBoardProvider extends ChangeNotifier {
statusTransaction: statusTransaction,
priority: priority,
displayData: displayData,
isAscending: isAscending,
);
}

@ -12,6 +12,7 @@ import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/controllers/providers/settings/setting_provider.dart';
import 'package:test_sa/dashboard_latest/dashboard_provider.dart';
import 'package:test_sa/dashboard_latest/widgets/app_bar_widget.dart';
import 'package:test_sa/extensions/enum_extensions.dart';
import 'package:test_sa/dashboard_latest/widgets/progress_fragment.dart';
import 'package:test_sa/dashboard_latest/widgets/request_category_fragment_housekeeper.dart';
import 'package:test_sa/dashboard_latest/widgets/requests_fragment.dart';
@ -103,15 +104,86 @@ class _DashboardViewState extends State<DashboardView> {
void handleScroll() async {
_scrollController = ScrollController();
_scrollController.addListener(() async {
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent && !_dashBoardProvider.isDetailLoading) {
_dashBoardProvider.pageNum = _dashBoardProvider.pageNum + 1;
if (_dashBoardProvider.currentListIndex == null) {
} else {
_dashBoardProvider.getRequestDetail(
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
final allRequestsProvider = Provider.of<AllRequestsProvider>(context, listen: false);
// Check which scenario is active and call appropriate pagination API
if (_dashBoardProvider.listIndex.isEmpty) {
// No category selected - paginate AllRequests
if (!allRequestsProvider.isAllLoading && allRequestsProvider.nextPage) {
allRequestsProvider.getAllRequests(context);
}
} else if (_dashBoardProvider.listIndex.length == 1 && _dashBoardProvider.isUnassignedTab(_dashBoardProvider.listIndex[0])) {
// Only UnAssigned tab selected - paginate with getRequestDetail
if (!_dashBoardProvider.isDetailLoading && _dashBoardProvider.nextPage) {
_dashBoardProvider.pageNum = _dashBoardProvider.pageNum + 1;
_dashBoardProvider.getRequestDetail(
usersType: user.type!,
showLoader: false,
status: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex!].tag,
tabId: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex!].id);
status: _dashBoardProvider.tabs[_dashBoardProvider.listIndex[0]].tag,
tabId: _dashBoardProvider.tabs[_dashBoardProvider.listIndex[0]].id,
);
}
} else {
// Multi-select categories - paginate with getDashboardMultifilterRequests
if (!allRequestsProvider.isMultiFilterLoading && allRequestsProvider.nextPage) {
// Build filter arrays based on selected tabs
List<int> statusTransaction = [];
List<int> priority = [];
List<int> displayData = [];
for (int index in _dashBoardProvider.listIndex) {
if (index >= 0 && index < _dashBoardProvider.tabs.length) {
CategoryTabs tab = _dashBoardProvider.tabs[index];
// High Priority
if (tab.isHighPriority) {
if (!priority.contains(1)) priority.add(1);
}
// Overdue
if (tab.isOverDue) {
if (!displayData.contains(1)) displayData.add(1);
}
// Status categories
if (!tab.isHighPriority && !tab.isOverDue && tab.requestType != RequestTypeEnum.unassigned) {
switch (tab.requestType) {
case RequestTypeEnum.open:
if (!statusTransaction.contains(1)) statusTransaction.add(1);
break;
case RequestTypeEnum.inProgress:
if (!statusTransaction.contains(2)) statusTransaction.add(2);
break;
case RequestTypeEnum.completed:
if (!statusTransaction.contains(4)) statusTransaction.add(4);
break;
case RequestTypeEnum.closed:
if (!statusTransaction.contains(4)) statusTransaction.add(4);
break;
case RequestTypeEnum.canceled:
if (!statusTransaction.contains(5)) statusTransaction.add(5);
break;
case RequestTypeEnum.pendingAcknowledgment:
if (!statusTransaction.contains(3)) statusTransaction.add(3);
break;
case RequestTypeEnum.unassigned:
case RequestTypeEnum.highPriority:
case RequestTypeEnum.overdue:
break;
}
}
}
}
allRequestsProvider.getDashboardMultifilterRequests(
context,
pagination: false,
statusTransaction: statusTransaction,
priority: priority,
displayData: displayData,
);
}
}
}
});

@ -223,7 +223,7 @@ class _AppBarWidgetState extends State<AppBarWidget> with TickerProviderStateMix
[],
builder: (_, MenuController controller, Widget? child) {
return Container(
padding: const EdgeInsets.all(10),
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: AppColor.assetGroupBackground(context),
@ -234,10 +234,13 @@ class _AppBarWidgetState extends State<AppBarWidget> with TickerProviderStateMix
),
child: Row(
mainAxisSize: MainAxisSize.min,
spacing: 16,
spacing: 8,
children: [
Text(settingProvider.assetGroup?.name ?? "", style: AppTextStyles.bodyText2.copyWith(color: AppColor.headingTextColor(context))),
Icon(Icons.keyboard_arrow_down, color: AppColor.headingTextColor(context)),
Text(
settingProvider.assetGroup?.name ?? "",
style: AppTextStyles.bodyText2.copyWith(color: AppColor.headingTextColor(context)),
),
Icon(Icons.keyboard_arrow_down, color: AppColor.headingTextColor(context), size: 20),
],
),
).onPress(() {
@ -252,7 +255,7 @@ class _AppBarWidgetState extends State<AppBarWidget> with TickerProviderStateMix
}),
),
),
16.width,
8.width,
SlideTransition(
position: _rightSlideAnimation,
child: FadeTransition(
@ -260,10 +263,15 @@ class _AppBarWidgetState extends State<AppBarWidget> with TickerProviderStateMix
child: Row(
children: [
if (context.settingProvider.isUserFlowMedical) ...[
("dashboard/chat").toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)).paddingOnly(top: 0, end: 0).onPress(() {
Navigator.of(context).push(CupertinoPageRoute(builder: (_) => UnReadChatList()));
}),
16.width,
IconButton(
icon: ("dashboard/chat").toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)),
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
tooltip: "Notifications",
onPressed: () {
Navigator.of(context).push(CupertinoPageRoute(builder: (_) => UnReadChatList()));
},
),
],
// Notification icon with unread count badge
Consumer<NotificationsProvider>(
@ -285,7 +293,7 @@ class _AppBarWidgetState extends State<AppBarWidget> with TickerProviderStateMix
),
),
],
).paddingOnly(start: 16, end: 16);
).paddingOnly(start: 16, end: 08);
}),
);
}

@ -83,8 +83,8 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> with
Provider.of<AllRequestsProvider>(context, listen: false).reset();
if (dashboardProvider.listIndex.isEmpty) {
// No category selected - do nothing or could reload default
return;
// No category selected - reload default data with sorting
Provider.of<AllRequestsProvider>(context, listen: false).getAllRequests(context, isAscending: isAscending);
} else if (dashboardProvider.listIndex.length == 1 && dashboardProvider.isUnassignedTab(dashboardProvider.listIndex[0])) {
// UnAssigned only - check by requestType, not by index
int index = dashboardProvider.listIndex[0];
@ -117,8 +117,6 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> with
final bool isHouseKeeper = user?.type == UsersTypes.houseKeeper;
return Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
// !isNurse && dashboardProvider.tabs.isNotEmpty && dashboardProvider.tabs[dashboardProvider.currentListIndex].tag == 0
// ? Column(
// crossAxisAlignment: CrossAxisAlignment.start,
@ -193,46 +191,50 @@ class _RequestCategoryFragmentState extends State<RequestCategoryFragment> with
// ).paddingOnly(start: 14, end: 12)
// :
isHouseKeeper
? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
'All Tasks'.addTranslation.heading4(context),
'add_icon'.toSvgAsset(width: 24, height: 24,).onPress(() {
Navigator.of(context).pushNamed('/cleaner_search');
}),
],
).paddingOnly(start: 16, end: 16) :
Row(
spacing: 16,
children: [
Text(context.translation.allWorkOrder, style: AppTextStyles.heading4.copyWith(color: AppColor.headingTextColor(context))).expanded,
// todo commenting this code, for FM
// if (!isFMSEngineer)
// 'search'.toSvgAsset(width: 24, height: 24, color: AppColor.headingTextColor(context)).onPress(() {
// Navigator.of(context).pushNamed('/cleaner_search');
// }),
if (dashboardProvider.listIndex.isNotEmpty)
Text(
'Clear Filters',
style: AppTextStyles.bodyText2.copyWith(color: AppColor.red30),
).onPress(() {
dashboardProvider.resetRequestDataList();
dashboardProvider.currentListIndex = null;
Provider.of<AllRequestsProvider>(context, listen: false)
..reset()
..getAllRequests(context);
}),
Transform.flip(
flipY: isAscending,
child: 'dashboard/sort_descending'.toSvgAsset(width: 24, height: 24, color: AppColor.headingTextColor(context)).onPress(() {
_toggleSort(dashboardProvider, user?.type);
}),
)
],
).paddingOnly(start: 16, end: 16),
isHouseKeeper
? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
'All Tasks'.addTranslation.heading4(context),
'add_icon'
.toSvgAsset(
width: 24,
height: 24,
)
.onPress(() {
Navigator.of(context).pushNamed('/cleaner_search');
}),
],
).paddingOnly(start: 16, end: 16)
: Row(
spacing: 16,
children: [
Text(context.translation.allWorkOrder, style: AppTextStyles.heading4.copyWith(color: AppColor.headingTextColor(context))).expanded,
// todo commenting this code, for FM
// if (!isFMSEngineer)
// 'search'.toSvgAsset(width: 24, height: 24, color: AppColor.headingTextColor(context)).onPress(() {
// Navigator.of(context).pushNamed('/cleaner_search');
// }),
if (dashboardProvider.listIndex.isNotEmpty)
Text(
'Clear Filters',
style: AppTextStyles.bodyText2.copyWith(color: AppColor.red30),
).onPress(() {
dashboardProvider.resetRequestDataList();
dashboardProvider.currentListIndex = null;
Provider.of<AllRequestsProvider>(context, listen: false)
..reset()
..getAllRequests(context);
}),
Transform.flip(
flipY: isAscending,
child: 'dashboard/sort_descending'.toSvgAsset(width: 24, height: 24, color: AppColor.headingTextColor(context)).onPress(() {
_toggleSort(dashboardProvider, user?.type);
}),
)
],
).paddingOnly(start: 16, end: 16),
),
),

@ -1,3 +1,4 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:localization/localization.dart';
import 'package:provider/provider.dart';
@ -5,6 +6,8 @@ import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/new_views/swipe_module/dialoge/confirm_dialog.dart';
import 'package:test_sa/new_views/swipe_module/dialoge/info_dialog.dart';
import 'package:test_sa/views/full_screen_image_viewer.dart';
import 'package:test_sa/views/widgets/dialogs/confirm_dialog.dart';
import '../controllers/providers/settings/setting_provider.dart';
import '../l10n/app_localizations.dart';
@ -24,6 +27,15 @@ extension BuildContextExtension on BuildContext {
UserProvider get userProvider => Provider.of<UserProvider>(this, listen: false);
void fullScreenImageViewer(Widget child) {
Navigator.of(this).push(
CupertinoPageRoute(
fullscreenDialog: true,
builder: (_) => FullScreenImageViewer(child),
),
);
}
bool isTablet() {
final mediaQueryData = MediaQuery.of(this);
final double screenWidth = mediaQueryData.size.width;
@ -49,6 +61,7 @@ extension BuildContextExtension on BuildContext {
isScrollControlled: true,
isDismissible: true,
backgroundColor: Colors.transparent,
builder: (context) => SingleChildScrollView(padding: const EdgeInsets.all(0), child: childWidget.bottomSafeArea).bottomSheetContainerNew(context, title: title,showCancelButton: showCancelButton),
builder: (context) =>
SingleChildScrollView(padding: const EdgeInsets.all(0), child: childWidget.bottomSafeArea).bottomSheetContainerNew(context, title: title, showCancelButton: showCancelButton),
);
}

@ -120,12 +120,13 @@ extension WidgetExtensions on Widget {
double padding = 12,
EdgeInsets? paddingObject,
EdgeInsets? margin,
double? width,
}) =>
withShadow
? Container(
padding: paddingObject ?? EdgeInsets.all(padding),
margin: margin,
width: double.infinity,
width: width ?? double.infinity,
decoration: ShapeDecoration(
color: backgroundColor ?? AppColor.background(context),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(borderRadius), side: BorderSide(color: borderColor ?? AppColor.borderColor(context))),

@ -13,7 +13,7 @@ import 'package:nfc_manager/nfc_manager_ios.dart' show MiFareIos, Iso15693Ios;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart';
import 'package:test_sa/views/widgets/dialogs/confirm_dialog.dart';
import 'package:test_sa/views/widgets/dialogs/loading_dialog.dart';
import 'package:test_sa/views/widgets/dialogs/loading_with_text_dialog.dart';
// ignore_for_file: avoid_annotating_with_dynamic
@ -103,7 +103,7 @@ class Utils {
_isLoadingVisible = false;
}
static void showUploadingDialog(BuildContext context) {
static void showLoadingWithTextDialog(BuildContext context, {String? message}) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_isLoadingVisible = true;
@ -111,7 +111,7 @@ class Utils {
context: context,
barrierColor: Colors.black.withOpacity(0.5),
useRootNavigator: false,
builder: (BuildContext context) => const UploadingDialog(),
builder: (BuildContext context) => LoadingWithTextDialog(message: message),
).then((value) {
_isLoadingVisible = false;
});

@ -65,6 +65,7 @@ class Asset {
this.comment,
this.tagCode,
this.assetPhoto,
this.hasTrainingImageData,
});
Asset.fromJson(dynamic json) {
@ -122,6 +123,7 @@ class Asset {
comment = json['comment'];
tagCode = json['tagCode'];
assetPhoto = json['assetPhoto'];
hasTrainingImageData = json['hasTrainingImageData'];
}
num? id; // Now nullable
@ -177,6 +179,7 @@ class Asset {
String? comment; // Now nullable
String? tagCode; // Now nullable
String? assetPhoto;
bool? hasTrainingImageData;
Asset copyWith({
num? id,
@ -232,6 +235,7 @@ class Asset {
String? comment,
String? tagCode,
String? assetPhoto,
bool? hasTrainingImageData,
}) =>
Asset(
id: id ?? this.id,
@ -287,6 +291,7 @@ class Asset {
comment: comment ?? this.comment,
tagCode: tagCode ?? this.tagCode,
assetPhoto: assetPhoto ?? this.assetPhoto,
hasTrainingImageData: hasTrainingImageData ?? this.hasTrainingImageData,
);
Map<String, dynamic> toJson() {
@ -381,6 +386,7 @@ class Asset {
map['comment'] = comment;
map['tagCode'] = tagCode;
map['assetPhoto'] = assetPhoto;
map['hasTrainingImageData'] = hasTrainingImageData;
return map;
}
}

@ -1,4 +1,5 @@
import 'package:test_sa/models/device/technical_guidance_book.dart';
import 'package:test_sa/models/generic_attachment_model.dart';
import 'package:test_sa/models/lookup.dart';
class AssetByIdModel {
@ -13,12 +14,12 @@ class AssetByIdModel {
AssetInfo? oldAsset;
AssetReplace? isParent;
AssetInfo? parentAsset;
AssetReplace? assetType;
Lookup? assetType;
DemoRequest? demoRequest;
String? missionCritical;
Site? site;
AssetReplace? building;
AssetReplace? floor;
Lookup? building;
Lookup? floor;
Department? department;
Lookup? room;
num? testsDay;
@ -54,14 +55,17 @@ class AssetByIdModel {
String? comment;
bool? isEnabled;
String? tagCode;
List<String>? assetAttachments;
List<AssetAttachment>? assetAttachments;
String? retirementTypeName;
String? retirementStatusName;
String? retirementDate;
AssetGroup? assetGroup;
String? assetPhoto;
String? modelPhoto;
String? nextPMDate;
String? lastPMDate;
bool? hasTrainingImageData;
TrainingImageData? trainingImageData;
AssetByIdModel(
{this.multiAssets,
@ -122,10 +126,15 @@ class AssetByIdModel {
this.retirementDate,
this.assetGroup,
this.assetPhoto,
this.modelPhoto,
this.nextPMDate,
this.lastPMDate});
this.lastPMDate,
this.trainingImageData,
this.hasTrainingImageData});
AssetByIdModel.fromJson(Map<String, dynamic> json) {
print("assetModel!.fromJson:${json['hasTrainingImageData']}");
if (json['multiAssets'] != null) {
multiAssets = <MultiAssets>[];
json['multiAssets'].forEach((v) {
@ -142,12 +151,12 @@ class AssetByIdModel {
oldAsset = json['oldAsset'] != null ? AssetInfo.fromJson(json['oldAsset']) : null;
isParent = json['isParent'] != null ? AssetReplace.fromJson(json['isParent']) : null;
parentAsset = json['parentAsset'] != null ? AssetInfo.fromJson(json['parentAsset']) : null;
assetType = json['assetType'] != null ? AssetReplace.fromJson(json['assetType']) : null;
assetType = json['assetType'] != null ? Lookup.fromJson(json['assetType']) : null;
demoRequest = json['demoRequest'] != null ? DemoRequest.fromJson(json['demoRequest']) : null;
missionCritical = json['missionCritical'];
site = json['site'] != null ? Site.fromJson(json['site']) : null;
building = json['building'] != null ? AssetReplace.fromJson(json['building']) : null;
floor = json['floor'] != null ? AssetReplace.fromJson(json['floor']) : null;
building = json['building'] != null ? Lookup.fromJson(json['building']) : null;
floor = json['floor'] != null ? Lookup.fromJson(json['floor']) : null;
department = json['department'] != null ? Department.fromJson(json['department']) : null;
room = json['room'] != null ? Lookup.fromJson(json['room']) : null;
testsDay = json['testsDay'];
@ -189,9 +198,9 @@ class AssetByIdModel {
isEnabled = json['isEnabled'];
tagCode = json['tagCode'];
if (json['assetAttachments'] != null) {
assetAttachments = <String>[];
assetAttachments = <AssetAttachment>[];
json['assetAttachments'].forEach((v) {
assetAttachments!.add(v);
assetAttachments!.add(AssetAttachment.fromJson(v));
});
}
retirementTypeName = json['retirementTypeName'];
@ -199,8 +208,11 @@ class AssetByIdModel {
retirementDate = json['retirementDate'];
assetGroup = json['assetGroup'] != null ? AssetGroup.fromJson(json['assetGroup']) : null;
assetPhoto = json['assetPhoto'];
modelPhoto = json['modelPhoto'];
nextPMDate = json['nextPMDate'];
lastPMDate = json['lastPMDate'];
hasTrainingImageData = json['hasTrainingImageData'];
trainingImageData = json['trainingImageData'] != null ? TrainingImageData.fromJson(json['trainingImageData']) : null;
}
Map<String, dynamic> toJson() {
@ -295,8 +307,11 @@ class AssetByIdModel {
data['assetGroup'] = assetGroup?.toJson();
}
data['assetPhoto'] = assetPhoto;
data['modelPhoto'] = modelPhoto;
data['nextPMDate'] = nextPMDate;
data['lastPMDate'] = lastPMDate;
data['hasTrainingImageData'] = hasTrainingImageData;
data['trainingImageData'] = trainingImageData?.toJson();
return data;
}
}
@ -323,6 +338,31 @@ class MultiAssets {
}
}
class AssetAttachment {
int? id;
String? attachmentName;
String? attachmentURL;
String? originalName;
AssetAttachment({this.id, this.attachmentName, this.attachmentURL, this.originalName});
AssetAttachment.fromJson(Map<String, dynamic> json) {
id = json['id'];
attachmentName = json['attachmentName'];
attachmentURL = json['attachmentURL'];
originalName = json['originalName'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['attachmentName'] = attachmentName;
data['attachmentURL'] = attachmentURL;
data['originalName'] = originalName;
return data;
}
}
class ModelDefinition {
int? id;
String? assetName;
@ -546,18 +586,50 @@ class Floors {
class Departments {
int? id;
String? name;
List<Rooms>? rooms;
Departments({this.id, this.name});
Departments.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
if (json['departments'] != null) {
rooms = <Rooms>[];
json['rooms'].forEach((v) {
rooms!.add(Rooms.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['name'] = name;
if (rooms != null) {
data['rooms'] = rooms!.map((v) => v.toJson()).toList();
}
return data;
}
}
class Rooms {
int? id;
String? name;
int? value;
Rooms({this.id, this.name, this.value});
Rooms.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
value = json['value'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['name'] = name;
data['value'] = value;
return data;
}
}
@ -567,14 +639,16 @@ class Department {
String? departmentName;
String? departmentCode;
String? ntCode;
num? operationalHours;
Department({this.id, this.departmentName, this.departmentCode, this.ntCode});
Department({this.id, this.departmentName, this.departmentCode, this.ntCode, this.operationalHours});
Department.fromJson(Map<String, dynamic> json) {
id = json['id'];
departmentName = json['departmentName'];
departmentCode = json['departmentCode'];
ntCode = json['ntCode'];
operationalHours = json['operationalHours'];
}
Map<String, dynamic> toJson() {
@ -583,6 +657,7 @@ class Department {
data['departmentName'] = departmentName;
data['departmentCode'] = departmentCode;
data['ntCode'] = ntCode;
data['operationalHours'] = operationalHours;
return data;
}
}
@ -725,3 +800,37 @@ class DemoRequest {
return data;
}
}
class TrainingImageData {
GenericAttachmentModel? leftImage;
GenericAttachmentModel? rightImage;
GenericAttachmentModel? upImage;
GenericAttachmentModel? downImage;
List<GenericAttachmentModel>? additionalImages;
TrainingImageData({this.leftImage, this.rightImage, this.upImage, this.downImage, this.additionalImages = const []});
TrainingImageData.fromJson(Map<String, dynamic> json) {
leftImage = json['leftImage'] == null ? null : GenericAttachmentModel.fromJson(json['leftImage'], convertToLink: true);
rightImage = json['rightImage'] == null ? null : GenericAttachmentModel.fromJson(json['rightImage'], convertToLink: true);
upImage = json['upImage'] == null ? null : GenericAttachmentModel.fromJson(json['upImage'], convertToLink: true);
downImage = json['downImage'] == null ? null : GenericAttachmentModel.fromJson(json['downImage'], convertToLink: true);
additionalImages = [];
if (json['additionalImages'] != null) {
json['additionalImages'].forEach((v) {
additionalImages!.add(GenericAttachmentModel.fromJson(v, convertToLink: true));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['leftImage'] = leftImage?.toJson();
data['rightImage'] = rightImage?.toJson();
data['upImage'] = upImage?.toJson();
data['downImage'] = downImage?.toJson();
data['additionalImages'] = additionalImages?.map((v) => v.toJson()).toList() ?? [];
return data;
}
}

@ -1,9 +1,10 @@
import 'dart:io';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/models/lookup.dart';
class GenericAttachmentModel {
GenericAttachmentModel({this.id, this.name, this.originalName, this.createdBy,this.documentTypeId});
GenericAttachmentModel({this.id, this.name, this.originalName, this.createdBy, this.documentTypeId});
int? id;
String? name;
@ -11,11 +12,14 @@ class GenericAttachmentModel {
String? originalName;
Lookup? documentTypeId;
GenericAttachmentModel.fromJson(Map<String, dynamic> json) {
GenericAttachmentModel.fromJson(Map<String, dynamic> json, {bool convertToLink = false}) {
id = json['id'];
name = json['name'];
name = json['name'] ?? json['imageName'];
createdBy = json['createdBy'];
originalName = json['originalName'];
originalName = json['originalName'] ?? json['imageOriginalName'];
if (convertToLink) {
name = URLs.getFileUrl(name);
}
}
Map<String, dynamic> toJson() {

@ -304,17 +304,20 @@ class WorkOrderAsset {
required this.id,
required this.assetNumber,
required this.assetSerialNo,
required this.hasTrainingImageData,
});
int? id;
String? assetNumber;
String? assetSerialNo;
bool? hasTrainingImageData;
factory WorkOrderAsset.fromJson(Map<String, dynamic> json) {
return WorkOrderAsset(
id: json["id"],
assetNumber: json["assetNumber"],
assetSerialNo: json["assetSerialNo"],
hasTrainingImageData: json["hasTrainingImageData"],
);
}
@ -322,6 +325,7 @@ class WorkOrderAsset {
"id": id,
"assetNumber": assetNumber,
"assetSerialNo": assetSerialNo,
"hasTrainingImageData": hasTrainingImageData,
};
}

@ -69,7 +69,7 @@ class _AssetInventoryDetailViewState extends State<AssetInventoryDetailView> {
InfoTextWidget(label: 'Start Date'.addTranslation, value: sessionModel.startDate?.toServiceRequestDetailsFormat ?? '-'),
InfoTextWidget(label: 'End Date'.addTranslation, value: sessionModel.endDate?.toServiceRequestDetailsFormat ?? '-'),
],
).toShadowContainer(context, padding: 16, borderRadius: 20);
).toShadowContainer(context, borderRadius: 20);
}
Widget siteListCard(BuildContext context, SessionModel sessionModel) {
@ -84,7 +84,7 @@ class _AssetInventoryDetailViewState extends State<AssetInventoryDetailView> {
sites: sites,
),
],
).toShadowContainer(context, padding: 16, borderRadius: 20);
).toShadowContainer(context, borderRadius: 20);
}
}

@ -262,33 +262,15 @@ class OneTimeTaskPreviewScreen extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InfoHeader16Widget(
'Captured Images',
),
16.height,
InfoHeader16Widget('Captured Images'),
12.height,
Wrap(
spacing: 8,
runSpacing: 8,
children: taskData.imageRooms!.map((imageString) {
return GestureDetector(
onTap: () {
// Show full screen image
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.black,
iconTheme: const IconThemeData(color: Colors.white),
),
body: Center(
child: InteractiveViewer(
child: _buildImage(imageString),
),
),
),
),
);
context.fullScreenImageViewer(_buildImage(imageString));
},
child: Container(
width: 100,
@ -349,12 +331,12 @@ class OneTimeTaskPreviewScreen extends StatelessWidget {
SizedBox(
width: 100,
child: InfoTextLabelWidget(
label:'$label:',
label: '$label:',
),
),
Expanded(
child: InfoTextLabelWidget(
label: value,
label: value,
),
),
],

@ -159,7 +159,7 @@ class _OneTimeTaskViewState extends State<OneTimeTaskView> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTaskInfoCard(context),
16.height,
12.height,
CleanerChecklistWidget(
isClosed: isClosed,
checklistItems: _convertInstructionsToChecklist(),
@ -276,9 +276,9 @@ class _OneTimeTaskViewState extends State<OneTimeTaskView> {
),
],
],
).paddingOnly(start: 16, end: 16, top: 16, bottom: 16),
),
],
).toShadowContainer(context, padding: 0);
).toShadowContainer(context);
}
List<ChecklistItem> _convertInstructionsToChecklist() {

@ -37,18 +37,17 @@ class _CleanerChecklistWidgetState extends State<CleanerChecklistWidget> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 12,
children: [
InfoHeader16Widget('Checklist'),
16.height,
ListView.separated(
shrinkWrap: true,
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (cxt, index) => _buildChecklistItem(context, widget.checklistItems[index]),
separatorBuilder: (cxt, index) => const Divider(height: 1, color: AppColor.neutral30, thickness: 1).paddingOnly(top: 16, bottom: 16),
separatorBuilder: (cxt, index) => const Divider().defaultStyle(context),
itemCount: widget.checklistItems.length)
.toShadowContainer(context, padding: 16),
16.height,
.toShadowContainer(context),
_buildImageCaptureWidget(context),
],
);
@ -70,7 +69,7 @@ class _CleanerChecklistWidgetState extends State<CleanerChecklistWidget> {
child: Checkbox(
value: item.isChecked,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
side: BorderSide(color: Colors.black.withValues(alpha: .05)),
side: BorderSide(color: AppColor.loadingColor(context), width: 1.0),
visualDensity: const VisualDensity(horizontal: -4.0, vertical: -4.0),
activeColor: AppColor.primary10,
onChanged: (bool? value) {},
@ -181,12 +180,7 @@ class _CleanerChecklistWidgetState extends State<CleanerChecklistWidget> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InfoHeader16Widget('Captured Image'),
],
),
InfoHeader16Widget('Captured Image'),
8.height,
_buildImageWidget(context),
],
@ -277,24 +271,7 @@ class _CleanerChecklistWidgetState extends State<CleanerChecklistWidget> {
maxImages: 2,
// Enforce max 2 images
onImageTap: (index, imageUrl) {
// Handle image tap - show in full screen
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
backgroundColor: Colors.black,
body: SafeArea(
child: Stack(
children: [
InteractiveViewer(
child: _buildImageForViewing(imageUrl),
).center,
const ABackButton(),
],
),
),
),
),
);
context.fullScreenImageViewer(_buildImageForViewing(imageUrl));
},
onDelete: (index) {
// Handle image delete

@ -48,8 +48,7 @@ class MultiImagePicker extends StatelessWidget {
),
// Add image button (only show if under max limit)
if (enabled && imageUrls.length < maxImages)
_buildAddImageButton(context),
if (enabled && imageUrls.length < maxImages) _buildAddImageButton(context),
],
),
],
@ -77,7 +76,9 @@ class MultiImagePicker extends StatelessWidget {
if (onImageTap != null) {
onImageTap!(index, imageUrl);
} else {
_showFullImage(context, imageUrl);
context.fullScreenImageViewer(
_getImageWidget(imageUrl),
);
}
}),
if (enabled)
@ -193,26 +194,4 @@ class MultiImagePicker extends StatelessWidget {
}
}
}
void _showFullImage(BuildContext context, String imageUrl) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
backgroundColor: Colors.black,
body: SafeArea(
child: Stack(
children: [
Center(
child: InteractiveViewer(
child: _getImageWidget(imageUrl),
),
),
const ABackButton(),
],
),
),
),
),
);
}
}

@ -121,7 +121,10 @@ class _CMDetailPageState extends State<CMDetailPage> {
}),
if (isNurse)
IconButton(
icon: 'qr'.toSvgAsset(height: 24, width: 24,color: AppColor.icon2Color(context)),
icon: 'qr'.toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)),
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
tooltip: "QR Code",
onPressed: () {
ServiceRequestBottomSheet.getQRCodeBottomSheet(context: context);
},
@ -173,11 +176,16 @@ class _CMDetailPageState extends State<CMDetailPage> {
IconButton(
icon: "download".toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)),
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
tooltip: "Export",
onPressed: _handlePrint,
),
IconButton(
icon: const Icon(Icons.home),
padding: EdgeInsets.zero,
iconSize: 20,
visualDensity: VisualDensity.compact,
tooltip:"Home",
onPressed: () {
// stopTimer();
Navigator.pop(context);

@ -175,7 +175,7 @@ class _ActivitiesListViewState extends State<ActivitiesListView> {
FilesList(images: activity.activitySparePart!.acitiySparePartAttachments!.map((toElement) => URLs.getFileUrl(toElement.name!)!).toList()),
],
],
).toShadowContainer(context, padding: 12, showShadow: false).onPress(() {
).toShadowContainer(context, showShadow: false).onPress(() {
if (showActionButtons) return;
if (requestDetailProvider.isReadOnlyRequest) {
editSparePartRequest(context: context, requestDetailProvider: requestDetailProvider, activity: activity);
@ -272,7 +272,7 @@ class _ActivitiesListViewState extends State<ActivitiesListView> {
// // ),
// ]
],
).toShadowContainer(context, padding: 12, showShadow: false).onPress(() {
).toShadowContainer(context, showShadow: false).onPress(() {
if (showActionButtons) return;
if (requestDetailProvider.isReadOnlyRequest) {
editMaintenanceRequest(context: context, requestDetailProvider: requestDetailProvider, activity: activity);

@ -120,20 +120,7 @@ class HistoryLogView extends StatelessWidget {
padding: EdgeInsets.symmetric(vertical: 12.toScreenWidth),
child: InteractiveViewer(child: Image.network(URLs.getFileUrl(object.photoVerfication ?? '')!, height: 143.toScreenHeight)),
).onPress(() async {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
body: SafeArea(
child: Stack(
children: [
InteractiveViewer(child: Image.network(URLs.getFileUrl(object.photoVerfication ?? '')!)).center,
const ABackButton(),
],
),
),
),
),
);
context.fullScreenImageViewer(Image.network(URLs.getFileUrl(object.photoVerfication ?? '')!));
}),
],
title.bodyText(context).custom(color: AppColor.textColor(context)),
@ -142,7 +129,7 @@ class HistoryLogView extends StatelessWidget {
),
),
],
).toShadowContainer(context, padding: 16).paddingOnly(bottom: 16);
).toShadowContainer(context).paddingOnly(bottom: 16);
}
Widget statusCard(BuildContext context, String status) {

@ -55,7 +55,7 @@ class _InitialVisitCardState extends State<InitialVisitCard> {
requestDetailProvider.needVisitHelperModel!.comment!.bodyText2(context).custom(color: AppColor.neutral120),
]
],
).toShadowContainer(context, padding: 16);
).toShadowContainer(context);
});
}
}

@ -18,6 +18,7 @@ import 'package:test_sa/modules/cm_module/views/forms/asset_retired/asset_retire
import 'package:test_sa/modules/cm_module/views/forms/cost/cost_detail_form_screen.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/views/widgets/equipment/asset_detail_page.dart';
import 'package:test_sa/views/widgets/images/files_list.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
import 'package:test_sa/views/widgets/item_views/info_date_widget.dart';
@ -141,7 +142,7 @@ class _ServiceRequestDetailViewState extends State<ServiceRequestDetailView> {
// spacing: 8,
runSpacing: 8,
children: [
if (Provider.of<SettingProvider>(context, listen: false).showPriority&&workOrder.priority!=null) ...[
if (Provider.of<SettingProvider>(context, listen: false).showPriority && workOrder.priority != null) ...[
StatusLabel(
label: workOrder.priority?.name,
id: workOrder.priority?.value,
@ -192,9 +193,23 @@ class _ServiceRequestDetailViewState extends State<ServiceRequestDetailView> {
],
),
8.height,
InfoHeader16Widget(context.translation.assetInformation),
Row(
spacing: 4,
children: [
Flexible(
child: InfoHeader16Widget(context.translation.assetInformation),
),
const Icon(Icons.open_in_new_rounded, color: AppColor.primary10, size: 16).onPress(() {
Navigator.of(context).pushNamed(AssetDetailPage.id, arguments: workOrder.asset!.id);
})
],
),
if (context.userProvider.isEngineer && !(workOrder.asset!.hasTrainingImageData ?? true)) ...[
InfoTextLabelWidget(label: '*Images are required for the asset', color: AppColor.red30, textStyle: AppTextStyles.tinyFont2),
4.height,
],
InfoTextWidget(label: context.translation.assetName, value: workOrder.assetNdModel!.name?.cleanupWhitespace.capitalizeFirstOfEach),
InfoTextWidget(label: context.translation.assetNumber, value: workOrder.asset!.assetNumber),
InfoTextWidget(label: context.translation.assetNumber, value: workOrder.asset!.assetNumber, copyValue: true),
InfoTextWidget(label: context.translation.serialNo, value: workOrder.asset!.assetSerialNo),
InfoTextWidget(label: context.translation.manufacture, value: workOrder.manufacturer?.name?.cleanupWhitespace.capitalizeFirstOfEach),
InfoTextWidget(label: context.translation.model, value: workOrder.model?.name!.cleanupWhitespace.capitalizeFirstOfEach),
@ -212,7 +227,7 @@ class _ServiceRequestDetailViewState extends State<ServiceRequestDetailView> {
//... request details starts here....
InfoHeader16Widget(context.translation.callDetails),
InfoTextWidget(label: context.translation.cmNumber, value: workOrder.workOrderNo),
InfoTextWidget(label: context.translation.cmNumber, value: workOrder.workOrderNo, copyValue: true),
InfoTextWidget(label: context.translation.createdBy, value: workOrder.workOrderCreatedBy?.userName ?? "-"),
InfoTextWidget(label: context.translation.arrivedDate, value: workOrder.iHaveArrivedDate != null ? workOrder.iHaveArrivedDate.toString().toServiceRequestDetailsFormat : '-'),
InfoTextWidget(label: context.translation.closedDate, value: workOrder.closedDate != null ? workOrder.closedDate!.toString().toServiceRequestDetailsFormat : '-'),
@ -282,9 +297,7 @@ class _ServiceRequestDetailViewState extends State<ServiceRequestDetailView> {
assetRetiredButton(context: context),
]
],
).toShadowContainer(
context,
);
).toShadowContainer(context);
}
Widget contactInfoCard(BuildContext context, WorkOrderData workOrder) {
@ -340,7 +353,7 @@ class _ServiceRequestDetailViewState extends State<ServiceRequestDetailView> {
InfoTextWidget(label: 'MR No'.addTranslation, value: provider.currentWorkOrder?.data?.mrNo?.toString() ?? '-'),
InfoTextWidget(label: 'Exchange Cost'.addTranslation, value: provider.currentWorkOrder?.data?.exchangeCost?.toString() ?? '-'),
],
).toShadowContainer(context, padding: 16);
).toShadowContainer(context);
}
Widget assetRetiredButton({required BuildContext context}) {

@ -388,7 +388,7 @@ class _ChatPageState extends State<ChatPage> {
clipBehavior: Clip.antiAliasWithSaveLayer,
builder: (BuildContext context) => ChatFilePicker())) as File?;
if (file != null) {
Utils.showUploadingDialog(context);
Utils.showLoadingWithTextDialog(context);
await chatProvider.uploadAttachments(context.userProvider.user!.username!, file, "1");
Utils.hideLoading(context);
}
@ -638,7 +638,7 @@ class _ChatPageState extends State<ChatPage> {
hoverColor: Colors.transparent,
onPressed: () async {
if (chatState == ChatState.voiceRecordingCompleted) {
Utils.showUploadingDialog(context);
Utils.showLoadingWithTextDialog(context);
try {
await chatProvider.uploadAttachments(context.userProvider.user!.username!, File(recordedFilePath!), "1");
Utils.hideLoading(context);

@ -24,7 +24,6 @@ class ChatFileViewer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FutureBuilder<File?>(
future: checkFileInLocalStorage(),
builder: (BuildContext context, AsyncSnapshot<File?> snapshot) {
@ -80,16 +79,7 @@ class ChatFileViewer extends StatelessWidget {
padding: EdgeInsets.zero,
onPressed: () async {
if (isImage) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
appBar: const DefaultAppBar(),
body: SafeArea(
child: InteractiveViewer(child: Image.file(file)).center,
),
),
),
);
context.fullScreenImageViewer(Image.file(file));
} else {
OpenFile.open(file.path);
}

@ -182,16 +182,7 @@ class AttachmentFileViewer extends StatelessWidget {
),
).onPress(() {
if (isImage) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
appBar: const DefaultAppBar(),
body: SafeArea(
child: InteractiveViewer(child: Image.file(snapshot.data!)).center,
),
),
),
);
context.fullScreenImageViewer(Image.file(snapshot.data!));
} else {
OpenFile.open(snapshot.data!.path);
}
@ -253,16 +244,7 @@ class AttachmentFileViewer extends StatelessWidget {
padding: EdgeInsets.zero,
onPressed: () async {
if (isImage) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
appBar: const DefaultAppBar(),
body: SafeArea(
child: InteractiveViewer(child: Image.file(file)).center,
),
),
),
);
context.fullScreenImageViewer(Image.file(file));
} else {
OpenFile.open(file.path);
}

@ -43,6 +43,8 @@ import 'package:test_sa/providers/gas_request_providers/site_provider.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
import 'package:test_sa/providers/work_order/vendor_provider.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
import 'package:test_sa/views/widgets/item_views/info_header_widget.dart';
import 'package:test_sa/views/widgets/item_views/info_text_widget.dart';
import '../../../../../../new_views/common_widgets/default_app_bar.dart';
import '../../models/new_models/department.dart';
import 'demo_detail_view_page.dart';
@ -92,76 +94,23 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
spacing: 12,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// SingleItemDropDownMenu<Lookup, DemoPeriodLookupProvider>(
// context: context,
// title: "Demo Period",
// initialValue: demoPeriodLookup,
// showShadow: false,
// validator: (value) {
// if (value == null) return "Mandatory";
// return null;
// },
// backgroundColor: AppColor.fieldBgColor(context),
// showAsBottomSheet: true,
// onSelect: (value) {
// demoPeriodLookup = value;
// setState(() {});
// },
// ),
// 16.height,
...siteSection(),
16.height,
...doctorInfoSection(),
16.height,
...assetSection(),
16.height,
...vendorDetailsSection(),
16.height,
"Attachments".bodyText(context).custom(color: AppColor.black10),
8.height,
SingleItemDropDownMenu<Lookup, DemoDocumentLookupProvider>(
context: context,
height: 56.toScreenHeight,
title: "Document Type",
initialValue: demoDocumentLookup,
showShadow: false,
validator: (value) {
if (value == null) return "Mandatory";
return null;
},
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
onSelect: (value) {
demoDocumentLookup = value;
setState(() {});
},
),
16.height,
AttachmentPicker(
label: context.translation.attachments,
attachment: _attachments,
buttonColor: AppColor.black10.withOpacity(demoDocumentLookup != null ? 1 : .4),
onlyImages: false,
enabled: demoDocumentLookup != null,
documentType: demoDocumentLookup,
showAsListView: true,
buttonIcon: 'image-plus'.toSvgAsset(color: (AppColor.primary10).withOpacity(demoDocumentLookup != null ? 1 : .4)),
onChange: (attachments) {
_attachments = attachments;
setState(() {});
},
),
16.height,
...attachmentSection(),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Checkbox(
value: _demoFormModel.demoEaluationAcknowledge,
visualDensity: const VisualDensity(horizontal: -4, vertical: -4),
activeColor: AppColor.blueStatus(context),
activeColor: AppColor.primary10,
side: BorderSide(color: AppColor.loadingColor(context), width: 1.0),
visualDensity: const VisualDensity(horizontal: -4.0, vertical: -4.0),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onChanged: (value) {
setState(() {
@ -172,24 +121,29 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 4,
children: [
"DEMO outcome evaluation form acknowledgement".bodyText(context).custom(color: context.isDark ? AppColor.primary50 : AppColor.neutral50),
4.height,
"I acknowledge at the end of the trial period I will complete and submit the Medical Technology Trial Outcome Form within two weeks."
.tinyFont(context)
.custom(color: context.isDark ? AppColor.primary50 : AppColor.neutral120),
InfoTextLabelWidget(
label: "DEMO outcome evaluation form acknowledgement",
textStyle: AppTextStyles.bodyText,
color: AppColor.textColor(context),
),
InfoTextLabelWidget(
label: "I acknowledge at the end of the trial period I will complete and submit the Medical Technology Trial Outcome Form within two weeks.",
color: AppColor.lightTextColor(context),
),
],
).expanded,
],
),
16.height,
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Checkbox(
value: _demoFormModel.acknowledgeNotEquipment,
visualDensity: const VisualDensity(horizontal: -4, vertical: -4),
activeColor: AppColor.blueStatus(context),
activeColor: AppColor.primary10,
side: BorderSide(color: AppColor.loadingColor(context), width: 1.0),
visualDensity: const VisualDensity(horizontal: -4.0, vertical: -4.0),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onChanged: (value) {
setState(() {
@ -200,18 +154,23 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 4,
children: [
"Acknowledgement of not using the equipment".bodyText(context).custom(color: context.isDark ? AppColor.primary50 : AppColor.neutral50),
4.height,
"I acknowledge at the end of the trial period I will not use the DEMO medical equipment & I will not book any patient on the DEMO medical equipment."
.tinyFont(context)
.custom(color: context.isDark ? AppColor.primary50 : AppColor.neutral120),
InfoTextLabelWidget(
label: "Acknowledgement of not using the equipment",
textStyle: AppTextStyles.bodyText,
color: AppColor.textColor(context),
),
InfoTextLabelWidget(
label: "I acknowledge at the end of the trial period I will not use the DEMO medical equipment & I will not book any patient on the DEMO medical equipment.",
color: AppColor.lightTextColor(context),
),
],
).expanded,
],
),
],
).toShadowContainer(context, borderRadius: 20),
).toShadowContainer(context),
).expanded,
FooterActionButton.footerContainer(
context: context,
@ -231,8 +190,7 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
List<Widget> siteSection() {
return [
'Site Details'.addTranslation.bodyText(context).custom(color: AppColor.black10),
8.height,
InfoHeader16Widget('Site Details'.addTranslation),
SingleItemDropDownMenu<Site, SiteProvider>(
context: context,
title: context.translation.site,
@ -252,7 +210,6 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
setState(() {});
},
),
8.height,
SingleItemDropDownMenu<Building, NullableLoadingProvider>(
context: context,
title: context.translation.building,
@ -273,7 +230,6 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
setState(() {});
},
),
8.height,
SingleItemDropDownMenu<Floor, NullableLoadingProvider>(
context: context,
title: context.translation.floor,
@ -293,7 +249,6 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
setState(() {});
},
),
8.height,
SingleItemDropDownMenu<Department, NullableLoadingProvider>(
context: context,
title: context.translation.department,
@ -318,8 +273,7 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
List<Widget> doctorInfoSection() {
return [
"Doctor Details".bodyText(context).custom(color: AppColor.black10),
8.height,
InfoHeader16Widget('Doctor Details'.addTranslation),
AppTextFormField(
initialValue: "",
labelText: "Name",
@ -336,7 +290,6 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
_demoFormModel.docName = value;
},
),
8.height,
AppTextFormField(
initialValue: "",
labelText: "Contact Number",
@ -353,7 +306,6 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
_demoFormModel.docNumber = value;
},
),
8.height,
AppTextFormField(
initialValue: "",
labelText: "Email",
@ -373,7 +325,6 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
_demoFormModel.docEmail = value;
},
),
8.height,
AppTextFormField(
labelText: "Item Description",
validator: (value) {
@ -391,7 +342,6 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
_demoFormModel.itemDescription = value;
},
),
8.height,
AppTextFormField(
labelText: "Request Description",
// validator: (value) {
@ -414,8 +364,7 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
List<Widget> assetSection() {
return [
'Asset Details'.addTranslation.bodyText(context).custom(color: AppColor.black10),
8.height,
InfoHeader16Widget('Asset Details'.addTranslation),
AppTextFormField(
labelText: "Asset Name",
validator: (value) {
@ -433,7 +382,6 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
_demoFormModel.assetName = value;
},
),
8.height,
AppTextFormField(
labelText: "Model",
validator: (value) {
@ -451,7 +399,6 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
_demoFormModel.model = value;
},
),
8.height,
AppTextFormField(
labelText: "Manufacturer",
validator: (value) {
@ -469,7 +416,6 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
_demoFormModel.manufacturer = value;
},
),
8.height,
SingleItemDropDownMenu<Lookup, DemoPeriodLookupProvider>(
context: context,
height: 56.toScreenHeight,
@ -492,8 +438,7 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
List<Widget> vendorDetailsSection() {
return [
'Vendor Details'.addTranslation.bodyText(context).custom(color: AppColor.black10),
8.height,
InfoHeader16Widget('Vendor Details'.addTranslation),
AppTextFormField(
labelText: 'Vendor Name'.addTranslation,
style: Theme.of(context).textTheme.titleMedium,
@ -506,7 +451,6 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
_demoFormModel.vendorName = value;
},
),
8.height,
AppTextFormField(
labelText: 'Representative Name'.addTranslation,
style: Theme.of(context).textTheme.titleMedium,
@ -519,7 +463,6 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
_demoFormModel.vendorRepresentativeName = value;
},
),
8.height,
AppTextFormField(
// controller: vendorPhoneController,
labelText: "Contact Number",
@ -533,7 +476,6 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
_demoFormModel.vendorContactNumber = value;
},
),
8.height,
AppTextFormField(
labelText: "Email",
// controller: vendorEmailController,
@ -550,6 +492,43 @@ class _CreateDemoRequestPageState extends State<CreateDemoRequestPage> with Tick
];
}
List<Widget> attachmentSection() {
return [
InfoHeader16Widget('Attachments'.addTranslation),
SingleItemDropDownMenu<Lookup, DemoDocumentLookupProvider>(
context: context,
height: 56.toScreenHeight,
title: "Document Type",
initialValue: demoDocumentLookup,
showShadow: false,
validator: (value) {
if (value == null) return "Mandatory";
return null;
},
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
onSelect: (value) {
demoDocumentLookup = value;
setState(() {});
},
),
AttachmentPicker(
label: context.translation.attachments,
attachment: _attachments,
buttonColor: AppColor.primary10.withOpacity(demoDocumentLookup != null ? 1 : .4),
onlyImages: false,
enabled: demoDocumentLookup != null,
documentType: demoDocumentLookup,
showAsListView: true,
buttonIcon: 'image-plus'.toSvgAsset(color: (AppColor.primary10).withOpacity(demoDocumentLookup != null ? 1 : .4)),
onChange: (attachments) {
_attachments = attachments;
setState(() {});
},
),
];
}
Future<void> _submit() async {
FocusScope.of(context).unfocus();
if (_formKey.currentState!.validate()) {

@ -16,6 +16,8 @@ import 'package:test_sa/modules/loan_module/models/loan_attachment_model.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/swipe_module/dialoge/acknowledge_work_dialog.dart';
import 'package:test_sa/views/widgets/item_views/info_header_widget.dart';
import 'package:test_sa/views/widgets/item_views/info_text_widget.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
import '../../controllers/api_routes/urls.dart';
@ -111,7 +113,6 @@ class _DemoDetailViewPageState extends State<DemoDetailViewPage> {
),
8.height,
],
12.height,
...requesterDetails(context, demoData),
const Divider().defaultStyle(context),
...requestDetails(context, demoData),
@ -123,10 +124,7 @@ class _DemoDetailViewPageState extends State<DemoDetailViewPage> {
...vendorDetails(context, demoData),
if (allAttachments.isNotEmpty) ...[
const Divider().defaultStyle(context),
Text(
"Attachments".addTranslation,
style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
InfoHeader16Widget("Attachments"),
FilesList(
showAsListView: true,
images: allAttachments.map((e) => URLs.getFileUrl(e.attachmentName ?? '') ?? '').toList(),
@ -134,10 +132,7 @@ class _DemoDetailViewPageState extends State<DemoDetailViewPage> {
],
if (suppAttachments.isNotEmpty) ...[
const Divider().defaultStyle(context),
Text(
"Supplier Attachments".addTranslation,
style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
InfoHeader16Widget("Supplier Attachments"),
FilesList(
showAsListView: true,
images: suppAttachments.map((e) => URLs.getFileUrl(e.attachmentName ?? '') ?? '').toList(),
@ -191,53 +186,53 @@ class _DemoDetailViewPageState extends State<DemoDetailViewPage> {
List<Widget> requestDetails(BuildContext context, DemoRequestModel? model) {
return [
Text("Request Details", style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)),
'Request No: ${model?.requestNo ?? "-"}'.bodyText(context),
'Demo No: ${model?.demoNo ?? "-"}'.bodyText(context),
'Demo Period: ${model?.demoPeriod?.name ?? "-"}'.bodyText(context),
'${context.translation.site}: ${model?.site?.custName ?? "-"}'.bodyText(context),
'${context.translation.building}: ${model?.building?.name ?? "-"}'.bodyText(context),
'${context.translation.floor}: ${model?.floor?.name ?? "-"}'.bodyText(context),
'${context.translation.department}: ${model?.department?.departmentName ?? "-"}'.bodyText(context),
'Item Description: ${model?.itemDescription ?? "-"}'.bodyText(context),
'Request Description: ${model?.requestDescription ?? "-"}'.bodyText(context),
InfoHeader16Widget("Request Information"),
InfoTextWidget(label: "Request No", value: model?.requestNo ?? "-", copyValue: true),
InfoTextWidget(label: "Demo No", value: model?.demoNo ?? "-"),
InfoTextWidget(label: "Demo Period", value: model?.demoPeriod?.name ?? "-"),
InfoTextWidget(label: context.translation.site, value: model?.site?.custName ?? "-"),
InfoTextWidget(label: context.translation.building, value: model?.building?.name ?? "-"),
InfoTextWidget(label: context.translation.floor, value: model?.floor?.name ?? "-"),
InfoTextWidget(label: context.translation.department, value: model?.department?.departmentName ?? "-"),
InfoTextWidget(label: "Item Description", value: model?.itemDescription ?? "-"),
InfoTextWidget(label: "Request Description", value: model?.requestDescription ?? "-"),
];
}
List<Widget> vendorDetails(BuildContext context, DemoRequestModel? model) {
return [
Text("Vendor Information", style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)),
'Name: ${model?.supplier?.name ?? model?.vendorName ?? "-"}'.bodyText(context),
'Representative Name: ${model?.suppPerson?.name ?? model?.vendorRepresentativeName ?? "-"}'.bodyText(context),
'Contact: ${model?.supplier?.contact ?? model?.vendorContactNumber ?? "-"}'.bodyText(context),
'Email: ${model?.supplier?.email ?? model?.vendorEmail ?? "-"}'.bodyText(context),
InfoHeader16Widget("Vendor Information"),
InfoTextWidget(label: "Name", value: model?.supplier?.name ?? model?.vendorName ?? "-"),
InfoTextWidget(label: "Representative Name", value: model?.suppPerson?.name ?? model?.vendorRepresentativeName ?? "-"),
InfoTextWidget(label: "Contact", value: model?.supplier?.contact ?? model?.vendorContactNumber ?? "-"),
InfoTextWidget(label: "Email", value: model?.supplier?.email ?? model?.vendorEmail ?? "-"),
];
}
List<Widget> assetDetails(BuildContext context, DemoRequestModel? model) {
return [
Text("Asset Information", style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)),
'${context.translation.assetName}: ${model?.assetName ?? "-"}'.bodyText(context),
'${context.translation.manufacture}: ${model?.manufacturer ?? "-"}'.bodyText(context),
'${context.translation.model}: ${model?.model ?? "-"}'.bodyText(context),
InfoHeader16Widget("Asset Information"),
InfoTextWidget(label: context.translation.assetName, value: model?.assetName ?? "-"),
InfoTextWidget(label: context.translation.manufacture, value: model?.manufacturer ?? "-"),
InfoTextWidget(label: context.translation.model, value: model?.model ?? "-"),
];
}
List<Widget> doctorDetails(BuildContext context, DemoRequestModel? model) {
return [
Text("Doctor Information", style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)),
'Name: ${model?.doctorName ?? "-"}'.bodyText(context),
'Contact: ${model?.doctorContactNumber ?? "-"}'.bodyText(context),
'Email: ${model?.doctorContactEmail ?? "-"}'.bodyText(context),
InfoHeader16Widget("Doctor Information"),
InfoTextWidget(label: "Name", value: model?.doctorName ?? "-"),
InfoTextWidget(label: "Contact", value: model?.doctorContactNumber ?? "-"),
InfoTextWidget(label: "Email", value: model?.doctorContactEmail ?? "-"),
];
}
List<Widget> requesterDetails(BuildContext context, DemoRequestModel? model) {
return [
Text("Requester Information", style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)),
'Name: ${model?.requesterUser?.requesterName ?? "-"}'.bodyText(context),
'Email: ${model?.requesterUser?.requesterEmail ?? "-"}'.bodyText(context),
'Contact: ${model?.requesterUser?.requesterMobile ?? "-"}'.bodyText(context),
InfoHeader16Widget("Requester Information"),
InfoTextWidget(label: "Name", value: model?.requesterUser?.requesterName ?? "-"),
InfoTextWidget(label: "Email", value: model?.requesterUser?.requesterEmail ?? "-"),
InfoTextWidget(label: "Contact", value: model?.requesterUser?.requesterMobile ?? "-"),
// 'Extension: ${loanData.requesterExtensionNo ?? "-"}'.bodyText(context),
];
}

@ -50,6 +50,7 @@ class ExportApiProvider extends ChangeNotifier {
valueColor: textColor,
elevation: 4,
msgFontSize: 14,
borderRadius: 14,
backgroundColor: backgroundColor,
cancel: Cancel(
cancelClicked: () {

@ -200,21 +200,7 @@ class _CleanerChecklistWidgetState extends State<CleanerChecklistWidget> {
),
child: imageWidget)
.onPress(() {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
body: SafeArea(
child: Stack(
children: [
// InteractiveViewer(child: Image(image: getImageObject(itemIndex))).center,
InteractiveViewer(child: imageWidget).center,
const ABackButton(),
],
),
),
),
),
);
context.fullScreenImageViewer(imageWidget);
}),
_delete(),
],
@ -241,21 +227,7 @@ class _CleanerChecklistWidgetState extends State<CleanerChecklistWidget> {
),
child: imageWidget)
.onPress(() {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
body: SafeArea(
child: Stack(
children: [
// InteractiveViewer(child: Image(image: getImageObject(itemIndex))).center,
InteractiveViewer(child: imageWidget).center,
const ABackButton(),
],
),
),
),
),
);
context.fullScreenImageViewer(imageWidget);
}),
_delete(),
],
@ -300,21 +272,7 @@ class _CleanerChecklistWidgetState extends State<CleanerChecklistWidget> {
),
child: imageWidget)
.onPress(() {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
body: SafeArea(
child: Stack(
children: [
// InteractiveViewer(child: Image(image: getImageObject(itemIndex))).center,
InteractiveViewer(child: imageWidget).center,
const ABackButton(),
],
),
),
),
),
);
context.fullScreenImageViewer(imageWidget);
}),
_delete(),
],

@ -7,6 +7,7 @@ import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.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/views/widgets/item_views/info_header_widget.dart';
/// Reusable success dialog for file operations (export, print, etc.)
class FileSuccessDialog {
@ -31,47 +32,25 @@ class FileSuccessDialog {
return Dialog(
backgroundColor: AppColor.background(context),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: BorderSide(color: AppColor.borderColor(context)),
borderRadius: BorderRadius.circular(14),
),
child: Padding(
padding: const EdgeInsets.all(24.0),
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Close button
Align(
alignment: Alignment.topRight,
child: 'clear'.toSvgAsset(
color: AppColor.icon2Color(context),
width: 18,
height: 18,
).onPress(() {
child: 'clear'.toSvgAsset(color: AppColor.icon2Color(context), width: 18, height: 18).onPress(() {
Navigator.of(dialogContext).pop();
}),
),
// Success icon
'done'.toSvgAsset(color: AppColor.green70, width: 56, height: 56),
const SizedBox(height: 24),
// Title
Text(
title,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: AppColor.headingTextColor(context),
),
textAlign: TextAlign.center,
),
title.heading5(context).custom(fontWeight: FontWeight.w600, color: AppColor.textStyleColor(context)),
const SizedBox(height: 12),
// Message
Text(
message,
style: TextStyle(
fontSize: 18,
color: AppColor.lightTextColor(context),
),
textAlign: TextAlign.center,
),
message.heading6(context).custom(align: TextAlign.center, fontWeight: FontWeight.w500, color: AppColor.lightTextColor(context)),
const SizedBox(height: 32),
// Action buttons
Row(
@ -84,14 +63,13 @@ class FileSuccessDialog {
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;
'No app found to open this file type. File saved to: $filePath'.showToast;
}
},
label: 'Open',
// buttonColor: AppColor.primary10,
showIcon: true,
icon: Icon(Icons.folder_open, color: AppColor.background(context)),
icon: Icon(Icons.folder_open, color: AppColor.background(context)),
height: 48,
),
),
@ -131,4 +109,3 @@ class FileSuccessDialog {
);
}
}

@ -133,7 +133,7 @@ class _CreateIncidentRequestPageState extends State<CreateIncidentRequestPage> {
onPick: (asset) async {
device = asset;
payload["assetId"] = asset?.id;
payload["oracleCode"] = asset.modelDefinition?.oracleCodes?.first.codeValue;
payload["oracleCode"] = (asset.modelDefinition?.oracleCodes?.isNotEmpty ?? false) ? asset.modelDefinition?.oracleCodes?.first.codeValue : null;
payload["model"] = asset.modelDefinition?.modelName;
payload["manufacturer"] = asset.modelDefinition?.manufacturerName;
payload["assetOrigin"] = asset.modelDefinition?.assetName;

@ -41,41 +41,39 @@ class IncidentDetailPage extends StatelessWidget {
List<IncidentAttachments> allAttachments = snapshot.data!.incidentAttachments!;
return ListView(
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StatusLabel(
label: snapshot.data!.incidentStatusName!,
textColor: AppColor.getRequestStatusTextColorByName(context, snapshot.data!.incidentStatusName!),
backgroundColor: AppColor.getRequestStatusColorByName(context, snapshot.data!.incidentStatusName!),
),
1.width.expanded,
InfoDateWidget(snapshot.data!.createdDate?.toString() ?? ""),
],
),
// 12.height,
...requesterDetails(context, snapshot.data!),
const Divider().defaultStyle(context),
...requestDetails(context, snapshot.data!),
if (snapshot.data!.ovrTicketNumber?.isNotEmpty ?? false) ovrInformation(context, snapshot.data!.ovrTicketNumber!),
const Divider().defaultStyle(context),
...assetDetails(context, snapshot.data!),
const Divider().defaultStyle(context),
...personInvolvedDetails(context, snapshot.data!),
if (allAttachments.isNotEmpty) ...[
const Divider().defaultStyle(context),
InfoHeader16Widget("Attachments".addTranslation),
FilesList(images: allAttachments.map((e) => URLs.getFileUrl(e.attachmentName ?? '') ?? '').toList() ?? []),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StatusLabel(
label: snapshot.data!.incidentStatusName!,
textColor: AppColor.getRequestStatusTextColorByName(context, snapshot.data!.incidentStatusName!),
backgroundColor: AppColor.getRequestStatusColorByName(context, snapshot.data!.incidentStatusName!),
),
1.width.expanded,
InfoDateWidget(snapshot.data!.createdDate?.toString() ?? ""),
],
),
// 12.height,
...requesterDetails(context, snapshot.data!),
const Divider().defaultStyle(context),
...requestDetails(context, snapshot.data!),
if (snapshot.data!.ovrTicketNumber?.isNotEmpty ?? false) ovrInformation(context, snapshot.data!.ovrTicketNumber!),
const Divider().defaultStyle(context),
...assetDetails(context, snapshot.data!),
const Divider().defaultStyle(context),
...personInvolvedDetails(context, snapshot.data!),
if (allAttachments.isNotEmpty) ...[
const Divider().defaultStyle(context),
InfoHeader16Widget("Attachments".addTranslation),
FilesList(images: allAttachments.map((e) => URLs.getFileUrl(e.attachmentName ?? '') ?? '').toList() ?? []),
],
).toShadowContainer(context, padding: 16),
],
],
).toShadowContainer(context),
);
}));
}
@ -83,7 +81,7 @@ class IncidentDetailPage extends StatelessWidget {
List<Widget> requestDetails(BuildContext context, IncidentDataModel incidentData) {
return [
InfoHeader16Widget("Request Details"),
InfoTextWidget(label: 'Incident No', value: incidentData.reqCode ?? "-"),
InfoTextWidget(label: 'Incident No', value: incidentData.reqCode ?? "-", copyValue: true),
InfoTextWidget(label: 'Incident Type', value: incidentData.incidentClassificationName ?? "-"),
InfoTextWidget(label: 'Incident Title', value: incidentData.incidentTitle ?? "-"),
InfoTextWidget(label: 'Incident Description', value: incidentData.incidentDescription ?? "-"),
@ -95,10 +93,10 @@ class IncidentDetailPage extends StatelessWidget {
InfoTextWidget(label: 'Occurrence Date', value: incidentData.occurrenceDate?.toServiceRequestDetailsFormat ?? "-"),
if (incidentData.workOrderId != null)
RichText(
text: TextSpan(text: "Work Order: ", style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20), children: [
text: TextSpan(text: "Work Order: ", style: AppTextStyles.bodyText2.copyWith(color: AppColor.lightTextColor(context)), children: [
TextSpan(
text: "${incidentData.workOrderNo}",
style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context), decoration: TextDecoration.underline),
style: AppTextStyles.bodyText.copyWith(color: AppColor.primary10, decoration: TextDecoration.underline),
recognizer: TapGestureRecognizer()
..onTap = () async {
await Navigator.of(context).push(MaterialPageRoute(builder: (_) => CMDetailPage(requestId: incidentData.workOrderId!, moduleId: 3)));

@ -185,7 +185,7 @@ class _EquipmentInternalAuditDetailPageState extends State<EquipmentInternalAudi
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InfoHeader16Widget("Request Details"),
InfoTextWidget(label: context.translation.requestNo, value: model?.requestNo ?? '-'),
InfoTextWidget(label: context.translation.requestNo, value: model?.requestNo ?? '-', copyValue: true),
if (model != null && model!.equipmentsFindings != null && model!.equipmentsFindings!.isNotEmpty)
...model!.equipmentsFindings!.map((item) {
final findingName = item.finding?.name ?? 'Unknown';
@ -204,7 +204,7 @@ class _EquipmentInternalAuditDetailPageState extends State<EquipmentInternalAudi
children: [
InfoHeader16Widget("Asset Information"),
InfoTextWidget(label: context.translation.assetName, value: model?.asset?.assetName ?? '-'),
InfoTextWidget(label: context.translation.assetNo, value: model?.asset?.assetNumber ?? '-'),
InfoTextWidget(label: context.translation.assetNo, value: model?.asset?.assetNumber ?? '-', copyValue: true),
InfoTextWidget(label: context.translation.serialNumber, value: model?.asset?.assetNumber ?? '-'),
InfoTextWidget(label: context.translation.manufacture, value: model?.asset?.manufacturerName ?? '-'),
InfoTextWidget(label: context.translation.model, value: model?.asset?.modelName ?? '-'),

@ -98,7 +98,7 @@ class _LoanEquipmentDetailPageState extends State<LoanEquipmentDetailPage> {
FilesList(images: allAttachments.map((e) => URLs.getFileUrl(e.attachmentName ?? '') ?? '').toList() ?? []),
],
],
).toShadowContainer(context, padding: 12)
).toShadowContainer(context)
],
).expanded,
// @todo ask backend to add loanTypeValue, so we can show or hide the buttons
@ -184,7 +184,7 @@ class _LoanEquipmentDetailPageState extends State<LoanEquipmentDetailPage> {
return [
InfoHeader16Widget("Request Details"),
InfoTextWidget(label: 'Loan Type', value: loanData.loanTypeName ?? "-"),
InfoTextWidget(label: 'Loan No', value: loanData.reqCode ?? "-"),
InfoTextWidget(label: 'Loan No', value: loanData.reqCode ?? "-", copyValue: true),
InfoTextWidget(label: 'Loan Period', value: loanData.loanPeriodName ?? "-"),
InfoTextWidget(label: context.translation.site, value: loanData.siteName ?? "-"),
InfoTextWidget(label: context.translation.department, value: loanData.departmentName ?? "-"),

@ -90,7 +90,7 @@ class _MedicalGasInspectionDetailPageState extends State<MedicalGasInspectionDet
),
8.height,
InfoHeader16Widget("Request Details"),
InfoTextWidget(label: context.translation.requestNo, value: model?.requestNumber ?? '-'),
InfoTextWidget(label: context.translation.requestNo, value: model?.requestNumber ?? '-', copyValue: true),
InfoTextWidget(label: 'Requested by', value: model?.requestedBy ?? '-'),
if (model?.lastModifiedBy != null)
InfoTextWidget(label: 'Last Modified by', value: model?.lastModifiedBy ?? '-'),
@ -106,7 +106,7 @@ class _MedicalGasInspectionDetailPageState extends State<MedicalGasInspectionDet
FilesList(images: model!.deliveryNoteAttachmentDto.map((e) => URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []),
],
],
).toShadowContainer(context, padding: 16),
).toShadowContainer(context,),
],
).expanded,
if (model?.requestStatus?.value != null && model!.requestStatus!.value! < 3)

@ -400,7 +400,8 @@ class _UpdateDeliveryNotesState extends State<UpdateDeliveryNotes> {
vertical: 6, // keeps normal height ~30
),
filled: true,
fillColor: AppColor.fieldBgColor(context),
fillColor: AppColor.dropDownBackground(context),
// fillColor: AppColor.fieldBgColor(context),
border: border,
enabledBorder: border,
focusedBorder: border,

@ -70,6 +70,7 @@ class _PpmDetailsPageState extends State<PpmDetailsPage> {
: planPreventiveVisit != null
? Column(children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -86,7 +87,7 @@ class _PpmDetailsPageState extends State<PpmDetailsPage> {
InfoHeader16Widget(planPreventiveVisit.assetName?.cleanupWhitespace.capitalizeFirstOfEach ?? ''),
InfoTextWidget(label: context.translation.assetNumber, value: planPreventiveVisit.asset?.assetNumber ?? '-'),
InfoTextWidget(label: context.translation.assetSN, value: planPreventiveVisit.asset?.assetSerialNo ?? '-'),
InfoTextWidget(label: context.translation.requestNo, value: planPreventiveVisit.visitNo ?? '-'),
InfoTextWidget(label: context.translation.requestNo, value: planPreventiveVisit.visitNo ?? '-', copyValue: true),
if (planPreventiveVisit.acutalDateOfVisit != null) ...[
const Divider().defaultStyle(context),
InfoTextWidget(label: context.translation.actualDate, value: planPreventiveVisit.acutalDateOfVisit?.toIso8601String().toAssetDetailsFormat ?? ""),
@ -99,7 +100,7 @@ class _PpmDetailsPageState extends State<PpmDetailsPage> {
InfoTextWidget(label: context.translation.department, value: planPreventiveVisit.departmentName?.cleanupWhitespace.capitalizeFirstOfEach ?? '-'),
InfoTextWidget(label: context.translation.room, value: (planPreventiveVisit.roomName ?? "").cleanupWhitespace.capitalizeFirstOfEach),
],
).toShadowContainer(context, padding: 16),
).toShadowContainer(context),
).expanded,
if (userProvider!.user!.type == UsersTypes.engineer && (!ppmProvider.isReadOnly)) ...[
FooterActionButton.footerContainer(

@ -80,7 +80,7 @@ class _RecurrentTaskInfoWidgetState extends State<RecurrentTaskInfoWidget> {
),
8.height,
InfoHeader16Widget(widget.model!.title!),
InfoTextWidget(label: context.translation.taskNo, value: widget.model!.taskNo!),
InfoTextWidget(label: context.translation.taskNo, value: widget.model!.taskNo!, copyValue: true),
InfoTextWidget(label: 'Plan Name:'.addTranslation, value: widget.model?.planRecurrent?.planName),
InfoTextWidget(label: context.translation.site, value: widget.model!.site!.siteName!),
if (widget.isHouseKeeper) ...[

@ -91,6 +91,7 @@ class _DeviceFilterScreenState extends State<DeviceFilterScreen> {
return Scaffold(
appBar: DefaultAppBar(
title: context.translation.filter,
showBackButtonAsCancel: true,
actions: [
if (loading == false && _showResetButton())
TextButton(

@ -51,7 +51,6 @@ class _DeviceSearchScreenState extends State<DeviceSearchScreen> {
_modelController.dispose();
_supplierController.dispose();
_siteController.dispose();
_siteController.dispose();
_manufactureController.dispose();
_mdController.dispose();
super.dispose();
@ -62,6 +61,7 @@ class _DeviceSearchScreenState extends State<DeviceSearchScreen> {
return Scaffold(
appBar: DefaultAppBar(
title: context.translation.search,
showBackButtonAsCancel: true,
actions: [
if (_showResetButton())
TextButton(

@ -94,7 +94,7 @@ class _DeviceTransferDetailsState extends State<DeviceTransferDetails> {
),
8.height,
InfoHeader16Widget(context.translation.callDetails),
InfoTextWidget(label: context.translation.trNumber, value: _model?.transferCode ?? "-"),
InfoTextWidget(label: context.translation.trNumber, value: _model?.transferCode ?? "-", copyValue: true),
InfoTextWidget(label: context.translation.createdBy, value: _model?.name ?? "-"),
const Divider().defaultStyle(context),
InfoHeader16Widget(context.translation.assetInformation),

@ -204,7 +204,7 @@ class _GasRefillDetailsPageState extends State<GasRefillDetailsPage> {
),
8.height,
InfoHeader16Widget(context.translation.callDetails),
InfoTextWidget(label: context.translation.requestNo, value: gasRefillModel.gasRefillNo ?? '-'),
InfoTextWidget(label: context.translation.requestNo, value: gasRefillModel.gasRefillNo ?? '-', copyValue: true),
InfoTextWidget(label: context.translation.createdBy, value: gasRefillModel.name ?? '-'),
InfoTextWidget(label: context.translation.gasType, value: gasRefillModel.gasRefillDetails?[0].gasType?.name ?? '-'),
InfoTextWidget(label: context.translation.cylinderType, value: gasRefillModel.gasRefillDetails?[0].cylinderType?.name ?? '-'),

@ -103,6 +103,7 @@ class _TaskRequestDetailsViewState extends State<TaskRequestDetailsView> {
? const ALoading()
: Column(children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -136,7 +137,7 @@ class _TaskRequestDetailsViewState extends State<TaskRequestDetailsView> {
const Divider().defaultStyle(context),
InfoHeader16Widget("Task Details".addTranslation),
InfoTextWidget(label: context.translation.taskType, value: taskModel?.taskType?.typeName ?? "-"),
InfoTextWidget(label: context.translation.taskNo, value: taskModel?.taskJobNo ?? "-"),
InfoTextWidget(label: context.translation.taskNo, value: taskModel?.taskJobNo ?? "-", copyValue: true),
InfoTextWidget(label: context.translation.createdBy, value: taskModel?.userCreated?.userName ?? "-"),
if (taskProvider.taskRequestModel?.taskType?.isRecallAndAlert == true) ...[
recallAlertTypeWidget(taskModel: taskProvider.taskRequestModel!),
@ -243,12 +244,13 @@ class _TaskRequestDetailsViewState extends State<TaskRequestDetailsView> {
return RichText(
text: TextSpan(
text: "${context.translation.totalWorkingTime}: ",
style: TextStyle(
fontSize: 14.toScreenWidth, fontWeight: FontWeight.w500, fontStyle: FontStyle.normal, decoration: TextDecoration.none, color: context.isDark ? AppColor.neutral10 : AppColor.neutral20),
style: AppTextStyles.bodyText2.copyWith(color: AppColor.lightTextColor(context)),
// TextStyle(
// fontSize: 14.toScreenWidth, fontWeight: FontWeight.w500, fontStyle: FontStyle.normal, decoration: TextDecoration.none, color: context.isDark ? AppColor.neutral10 : AppColor.neutral20),
children: [
TextSpan(text: "$totalHourString ", style: AppTextStyles.bodyText),
const WidgetSpan(
child: Icon(Icons.info_outline_rounded, color: AppColor.primary10, size: 14),
TextSpan(text: "$totalHourString ", style: AppTextStyles.bodyText2.copyWith(color: AppColor.lightTextColor(context))),
WidgetSpan(
child: Icon(Icons.info_outline_rounded, color: AppColor.loadingColor(context), size: 14),
),
],
),

@ -519,7 +519,7 @@ class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
attachment: attachments,
buttonColor: AppColor.black10,
onlyImages: false,
buttonIcon: 'image-plus'.toSvgAsset(color: AppColor.neutral120),
buttonIcon: 'image-plus'.toSvgAsset(color: AppColor.primary10),
onChange: (attachment) {
attachments = attachment;
},
@ -531,6 +531,7 @@ class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
children: [
Checkbox(
value: _acknowledgement,
side: BorderSide(color: AppColor.loadingColor(context), width: 1.0),
visualDensity: const VisualDensity(horizontal: -4.0, vertical: -4.0),
activeColor: AppColor.blueStatus(context),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,

@ -119,7 +119,7 @@ class _TrafRequestDetailPageState extends State<TrafRequestDetailPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InfoHeader16Widget("Request Information"),
InfoTextWidget(label: 'Request No', value: data.reqCode ?? '-'),
InfoTextWidget(label: 'Request No', value: data.reqCode ?? '-', copyValue: true),
InfoTextWidget(label: 'Request type', value: data.requestTypeName ?? '-'),
if (data.requestTypeValue == 1) ...[
4.height,

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
@ -20,7 +22,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
titleSpacing: 16,
title: Row(
children: [
const Icon(Icons.arrow_back_ios).onPress(() {
Icon(Platform.isIOS ? Icons.arrow_back_ios : Icons.arrow_back).onPress(() {
Navigator.of(context).pop();
}),
Text(

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
@ -16,8 +18,11 @@ class DefaultAppBar extends StatelessWidget implements PreferredSizeWidget {
final bool showBackButton;
final VoidCallback? onWillPopScope;
final TextStyle? titleStyle;
final bool showBackButtonAsCancel;
const DefaultAppBar({this.title, this.onBackPress, this.onWillPopScope, this.actions, this.titleStyle, this.showHomeActionButton = false, this.showBackButton = true, Key? key}) : super(key: key);
const DefaultAppBar(
{this.title, this.onBackPress, this.onWillPopScope, this.actions, this.titleStyle, this.showHomeActionButton = false, this.showBackButton = true, this.showBackButtonAsCancel = false, Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
@ -44,7 +49,7 @@ class DefaultAppBar extends StatelessWidget implements PreferredSizeWidget {
title: Row(
children: [
if (showBackButton)
const Icon(Icons.arrow_back_ios).onPress(() {
Icon(showBackButtonAsCancel ? Icons.clear : (Platform.isIOS ? Icons.arrow_back_ios : Icons.arrow_back)).onPress(() {
if (onWillPopScope != null) {
showDialog(
context: context,
@ -63,11 +68,10 @@ class DefaultAppBar extends StatelessWidget implements PreferredSizeWidget {
Navigator.of(context).pop();
}
}),
10.width,
12.width,
Text(
title ?? "",
style: titleStyle ??
(context.isTablet() ? AppTextStyles.heading4 : AppTextStyles.heading3).copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
style: titleStyle ?? (AppTextStyles.heading5).copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
).expanded,
],
),

@ -1,866 +0,0 @@
// import 'dart:async';
// import 'dart:developer';
// import 'dart:io';
//
// import 'package:flutter/material.dart';
// import 'package:flutter_svg/flutter_svg.dart';
// import 'package:geolocator/geolocator.dart';
// import 'package:huawei_location/huawei_location.dart';
// import 'package:nfc_manager/nfc_manager.dart';
// import 'package:permission_handler/permission_handler.dart';
// import 'package:provider/provider.dart';
// import 'package:test_sa/dashboard_latest/dashboard_provider.dart';
// // import 'package:platform_device_id/platform_device_id.dart';
// import 'package:test_sa/dashboard_latest/widgets/app_bar_widget.dart';
// import 'package:test_sa/extensions/enum_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';
// import 'package:test_sa/models/enums/swipe_type.dart';
// import 'package:test_sa/models/lookup.dart';
// import 'package:test_sa/models/new_models/swipe_model.dart';
// import 'package:test_sa/nfc/nfc_reader_sheet.dart';
// import 'package:test_sa/utilities/Location.dart' as location;
// import 'package:test_sa/views/widgets/dialogs/confirm_dialog.dart';
// import 'package:test_sa/views/widgets/dialogs/success_dialog.dart';
// import 'package:test_sa/views/widgets/qr_scanner_dialog.dart';
// import 'package:wifi_iot/wifi_iot.dart';
//
// import '../../app_style/app_color.dart';
//
// class MarkAttendanceWidget extends StatefulWidget {
// // DashboardProviderModel model;
// double topPadding;
// bool isFromDashboard;
//
// MarkAttendanceWidget( {Key? key, this.topPadding = 0, this.isFromDashboard = false}) : super(key: key);
// // todo MarkAttendanceWidget(this.model, {Key? key, this.topPadding = 0, this.isFromDashboard = false}) : super(key: key);
//
// @override
// _MarkAttendanceWidgetState createState() {
// return _MarkAttendanceWidgetState();
// }
// }
//
// class _MarkAttendanceWidgetState extends State<MarkAttendanceWidget> {
// bool isNfcEnabled = false, isNfcLocationEnabled = false, isQrEnabled = false, isQrLocationEnabled = false, isWifiEnabled = false, isWifiLocationEnabled = false;
//
// int _locationUpdateCbId = 0;
//
// @override
// void initState() {
// super.initState();
// checkAttendanceAvailability();
// }
//
// void checkAttendanceAvailability() async {
// bool isAvailable = await NfcManager.instance.isAvailable();
// // setState(() {
// // AppState().privilegeListModel!.forEach((PrivilegeListModel element) {
// // if (element.serviceName == "enableNFC") {
// // if (isAvailable) if (element.previlege ?? false) isNfcEnabled = true;
// // } else if (element.serviceName == "enableQR") {
// // if (element.previlege ?? false) isQrEnabled = true;
// // } else if (element.serviceName == "enableWIFI") {
// // if (element.previlege ?? false) isWifiEnabled = true;
// // } else if (element.serviceName!.trim() == "enableLocationNFC") {
// // if (element.previlege ?? false) isNfcLocationEnabled = true;
// // } else if (element.serviceName == "enableLocationQR") {
// // if (element.previlege ?? false) isQrLocationEnabled = true;
// // } else if (element.serviceName == "enableLocationWIFI") {
// // if (element.previlege ?? false) isWifiLocationEnabled = true;
// // }
// // });
// // });
// }
//
// void checkHuaweiLocationPermission(String attendanceType) async {
// // Permission_Handler permissionHandler = PermissionHandler();
// location.Location.isEnabled((bool isEnabled) async {
// if (isEnabled) {
// location.Location.havePermission((bool permission) async {
// if (permission) {
// getHuaweiCurrentLocation(attendanceType);
// } else {
// bool has = await requestPermissions();
// if (has) {
// getHuaweiCurrentLocation(attendanceType);
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to give location permission to mark attendance",
// onTap: () {
// Navigator.pop(context);
// },
// ),
// );
// }
// }
// });
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to enable location services to mark attendance",
// onTap: () async {
// Navigator.pop(context);
// await Geolocator.openLocationSettings();
// },
// ),
// );
// }
// });
//
// // if (await permissionHandler.hasLocationPermission()) {
// // getHuaweiCurrentLocation(attendanceType);
// // } else {
// // bool has = await requestPermissions();
// // if (has) {
// // getHuaweiCurrentLocation(attendanceType);
// // } else {
// // showDialog(
// // context: context,
// // builder: (BuildContext cxt) => ConfirmDialog(
// // message: "You need to give location permission to mark attendance",
// // onTap: () {
// // Navigator.pop(context);
// // },
// // ),
// // );
// // }
// // }
// }
//
// Future<bool> requestPermissions() async {
// var result = await [
// Permission.location,
// ].request();
// return (result[Permission.location] == PermissionStatus.granted || result[Permission.locationAlways] == PermissionStatus.granted);
// }
//
// @override
// void dispose() {
// super.dispose();
// // Stop Session
// NfcManager.instance.stopSession();
// }
//
// @override
// Widget build(BuildContext context) {
// return Container(
// padding: EdgeInsets.only(left: 21, right: 21, bottom: 21, top: widget.topPadding),
// decoration: const BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), color: Colors.white),
// width: double.infinity,
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisSize: MainAxisSize.min,
// children: [
// // LocaleKeys.markAttendance.tr().toSectionHeading(),
// // LocaleKeys.selectMethodOfAttendance.tr().toText11(color: const Color(0xff535353)),
// GridView(
// physics: const NeverScrollableScrollPhysics(),
// shrinkWrap: true,
// padding: const EdgeInsets.only(bottom: 0, top: 21),
// gridDelegate:
// SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: (MediaQuery.of(context).size.width < 550) ? 3 : 5, childAspectRatio: 1 / 1, crossAxisSpacing: 8, mainAxisSpacing: 8),
// children: <Widget>[
// attendanceMethod(SwipeTypeEnum.NFC.name, Icons.nfc, true, () {
// log('i am here...nfc');
// handleSwipe(swipeType: SwipeTypeEnum.NFC, isEnable: true);
// return;
// // if (AppState().getIsHuawei) {
// if (false) {
// checkHuaweiLocationPermission("NFC");
// } else {
// location.Location.isEnabled((bool isEnabled) {
// if (isEnabled) {
// location.Location.havePermission((bool permission) {
// if (permission) {
// Utils.showLoading(context);
// location.Location.getCurrentLocation(
// (Position position, bool isMocked) {
// if (isMocked) {
// Utils.hideLoading(context);
// markFakeAttendance("NFC", position.latitude.toString() ?? "", position.longitude.toString() ?? "");
// } else {
// Utils.hideLoading(context);
// //todo performNfcAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? "");
// }
// },
// () {
// Utils.hideLoading(context);
// Utils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working.");
// },
// context,
// );
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to give location permission to mark attendance",
// onTap: () async {
// Navigator.pop(context);
// await Geolocator.openAppSettings();
// },
// ),
// );
// }
// });
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to enable location services to mark attendance",
// onTap: () async {
// Navigator.pop(context);
// await Geolocator.openLocationSettings();
// },
// ),
// );
// }
// });
// }
// }),
// //if (isWifiEnabled) //todo
// attendanceMethod("Wifi", Icons.wifi, isWifiEnabled, () {
// // if (AppState().getIsHuawei) {
// if (false) {
// checkHuaweiLocationPermission("WIFI");
// } else {
// location.Location.isEnabled((bool isEnabled) {
// if (isEnabled) {
// location.Location.havePermission((bool permission) {
// if (permission) {
// Utils.showLoading(context);
// location.Location.getCurrentLocation(
// (Position position, bool isMocked) {
// if (isMocked) {
// Utils.hideLoading(context);
// markFakeAttendance("WIFI", position.latitude.toString() ?? "", position.longitude.toString() ?? "");
// } else {
// Utils.hideLoading(context);
// //todo performWifiAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? "");
// }
// },
// () {
// Utils.hideLoading(context);
// Utils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working.");
// },
// context,
// );
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to give location permission to mark attendance",
// onTap: () async {
// Navigator.pop(context);
// await Geolocator.openAppSettings();
// },
// ),
// );
// }
// });
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to enable location services to mark attendance",
// onTap: () async {
// Navigator.pop(context);
// await Geolocator.openLocationSettings();
// },
// ),
// );
// }
// });
// }
// }),
// // if (isQrEnabled) //todo
// attendanceMethod(SwipeTypeEnum.QR.name, Icons.qr_code_2, true, () async {
// handleSwipe(swipeType: SwipeTypeEnum.QR, isEnable: true);
// return;
// // if (AppState().getIsHuawei) {
// if (false) {
// checkHuaweiLocationPermission("QR");
// } else {
// location.Location.isEnabled((bool isEnabled) {
// if (isEnabled) {
// location.Location.havePermission((bool permission) {
// if (permission) {
// Utils.showLoading(context);
// location.Location.getCurrentLocation(
// (Position position, bool isMocked) {
// if (isMocked) {
// Utils.hideLoading(context);
// markFakeAttendance("QR", position.latitude.toString() ?? "", position.longitude.toString() ?? "");
// } else {
// Utils.hideLoading(context);
// //todo performQrCodeAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? "");
// }
// },
// () {
// Utils.hideLoading(context);
// Utils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working.");
// },
// context,
// );
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to give location permission to mark attendance",
// onTap: () async {
// Navigator.pop(context);
// await Geolocator.openAppSettings();
// },
// ),
// );
// }
// });
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to enable location services to mark attendance",
// onTap: () async {
// Navigator.pop(context);
// await Geolocator.openLocationSettings();
// },
// ),
// );
// }
// });
// }
// }),
// ],
// )
// ],
// ),
// );
// }
//
// void handleSwipe({required SwipeTypeEnum swipeType,required bool isEnable,}){
// log('handle swipe value is ${swipeType.name}');
// // if (AppState().getIsHuawei) {
// if (false) {
// checkHuaweiLocationPermission("NFC");
// } else {
// location.Location.isEnabled((bool isEnabled) {
// if (isEnabled) {
// location.Location.havePermission((bool permission) {
// if (permission) {
// Utils.showLoading(context);
// location.Location.getCurrentLocation(
// (Position position, bool isMocked) {
// if (isMocked) {
// Utils.hideLoading(context);
// markFakeAttendance(swipeType.name, position.latitude.toString() ?? "", position.longitude.toString() ?? "");
// } else {
// Utils.hideLoading(context);
// //todo performNfcAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? "");
// handleSwipeOperation(swipeType: swipeType,lat: position.latitude,lang: position.longitude);
// }
// },
// () {
// Utils.hideLoading(context);
// Utils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working.");
// },
// context,
// );
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to give location permission to mark attendance",
// onTap: () async {
// Navigator.pop(context);
// await Geolocator.openAppSettings();
// },
// ),
// );
// }
// });
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to enable location services to mark attendance",
// onTap: () async {
// Navigator.pop(context);
// await Geolocator.openLocationSettings();
// },
// ),
// );
// }
// });
// }
// }
//
// void handleSwipeOperation({required SwipeTypeEnum swipeType,double ?lat, double ?lang}){
// switch(swipeType){
// case SwipeTypeEnum.NFC:
// handleNfcAttendance(latitude: lat,longitude: lang);
// return;
// case SwipeTypeEnum.QR:
// performQrCodeAttendance(latitude: lat,longitude: lang);
// return;
// case SwipeTypeEnum.Wifi:
// return;
//
// }
// }
//
// void getHuaweiCurrentLocation(String attendanceType) async {
// try {
// Utils.showLoading(context);
// FusedLocationProviderClient locationService = FusedLocationProviderClient()..initFusedLocationService();
// LocationRequest locationRequest = LocationRequest();
// locationRequest.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY;
// locationRequest.interval = 500;
// List<LocationRequest> locationRequestList = <LocationRequest>[locationRequest];
// LocationSettingsRequest locationSettingsRequest = LocationSettingsRequest(requests: locationRequestList);
//
// late StreamSubscription<Location> _streamSubscription;
// int requestCode = (await (locationService.requestLocationUpdates(locationRequest)))!;
//
// _streamSubscription = locationService.onLocationData!.listen(
// (Location location) async {
// Utils.hideLoading(context);
// await locationService.removeLocationUpdates(requestCode);
// if (attendanceType == "QR") {
// // todo performQrCodeAttendance(widget.model, lat: location.latitude.toString() ?? "", lng: location.longitude.toString() ?? "");
// }
// if (attendanceType == "WIFI") {
// // todo performWifiAttendance(widget.model, lat: location.latitude.toString() ?? "", lng: location.longitude.toString() ?? "");
// }
// if (attendanceType == "NFC") {
// //todo performNfcAttendance(widget.model, lat: location.latitude.toString() ?? "", lng: location.longitude.toString() ?? "");
// }
// requestCode = 0;
// },
// );
//
// // locationService.checkLocationSettings(locationSettingsRequest).then((settings) async {
// // await locationService.getLastLocation().then((value) {
// // if (value.latitude == null || value.longitude == null) {
// // showDialog(
// // context: context,
// // builder: (BuildContext cxt) => ConfirmDialog(
// // message: "Unable to get your location, Please check your location settings & try again.",
// // onTap: () {
// // Navigator.pop(context);
// // },
// // ),
// // );
// // } else {
// // if (attendanceType == "QR") {
// // performQrCodeAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? "");
// // }
// // if (attendanceType == "WIFI") {
// // performWifiAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? "");
// // }
// // if (attendanceType == "NFC") {
// // performNfcAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? "");
// // }
// // }
// // }).catchError((error) {
// // log("HUAWEI LOCATION getLastLocation ERROR!!!!!");
// // log(error);
// // });
// // }).catchError((error) {
// // log("HUAWEI LOCATION checkLocationSettings ERROR!!!!!");
// // log(error);
// // if (error.code == "LOCATION_SETTINGS_NOT_AVAILABLE") {
// // // Location service not enabled.
// // }
// // });
// } catch (error) {
// log("HUAWEI LOCATION ERROR!!!!!");
// log('$error');
// Utils.hideLoading(context);
// // Utils.handleException(error, context, null);
// }
// }
//
// Future<void> handleNfcAttendance({double? latitude = 0, double? longitude = 0}) async {
// final dashBoardProvider = Provider.of<DashBoardProvider>(context, listen: false);
//
// if (Platform.isIOS) {
// Utils.readNFc(onRead: (String nfcId) async {
// await _processNfcAttendance(dashBoardProvider, nfcId, latitude, longitude);
// });
// } else {
// showNfcReader(context, onNcfScan: (String? nfcId) async {
// await _processNfcAttendance(dashBoardProvider, nfcId ?? '', latitude, longitude);
// });
// }
// }
//
//
// Future<void> _processNfcAttendance(
// DashBoardProvider dashBoardProvider,
// String nfcId,
// double? latitude,
// double? longitude,
// ) async {
// Utils.showLoading(context);
// try {
// final swipeModel = Swipe(
// swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(),
// value: nfcId,
// latitude: latitude,
// longitude: longitude,
// );
//
// final swipeResponse = await dashBoardProvider.makeSwipe(model: swipeModel);
// log('swipe response i got is ${swipeResponse.toJson()}');
//
// if (swipeResponse.responseCode != 1) {
// Utils.hideLoading(context);
// _showErrorDialog(swipeResponse.message ?? "Unexpected error occurred");
// } else {
// final isSuccess = swipeResponse.data;
// log('nfc swipe response is ${isSuccess}');
// if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3));
// Utils.hideLoading(context);
// _showSuccessDialog();
// }
// } catch (error) {
// Utils.hideLoading(context);
// // Uncomment below line for error handling if needed
// // Utils.handleException(error, context, null);
// }
// }
//
//
// void _showErrorDialog(String message) {
// showDialog(
// context: context,
// builder: (context) => ConfirmDialog(
// message: message,
// onTap: () => Navigator.pop(context),
// ),
// );
// }
//
// void _showSuccessDialog() {
// showMDialog(
// context,
// backgroundColor: Colors.transparent,
// isDismissable: true,
// child: SuccessDialog(widget.isFromDashboard),
// );
// }
//
//
// //older code....
// Future<void> performNfcAttendance({double? lat = 0, double ?lng = 0}) async {
// DashBoardProvider dashBoardProvider = Provider.of<DashBoardProvider>(context,listen:false);
// if (Platform.isIOS) {
// Utils.readNFc(onRead: (String nfcId) async {
// Utils.showLoading(context);
// try {
// SwipeModel? swipeResponse = await dashBoardProvider.makeSwipe(model: Swipe(swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), value: '', latitude: lat, longitude: lng));
// if (swipeResponse.responseCode != 1) {
// Utils.hideLoading(context);
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: swipeResponse.message ?? "Unexpected error occurred",
// onTap: () {
// Navigator.pop(context);
// },
// ),
// );
// } else {
// bool status = swipeResponse.data;
// if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3));
// Utils.hideLoading(context);
// showMDialog(
// context,
// backgroundColor: Colors.transparent,
// isDismissable: true,
// child: SuccessDialog(widget.isFromDashboard),
// );
// }
// } catch (ex) {
// Utils.hideLoading(context);
// // Utils.handleException(ex, context, null);
// }
// });
// } else {
// showNfcReader(context, onNcfScan: (String? nfcId) async {
// Utils.showLoading(context);
// try {
// SwipeModel? swipeResponse = await dashBoardProvider.makeSwipe(model: Swipe(swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), value: nfcId??'', latitude: lat, longitude: lng));
// log('api response i got is ${swipeResponse.toJson()}');
// if (swipeResponse.responseCode != 1) {
// Utils.hideLoading(context);
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: swipeResponse.message ?? "Unexpected error occurred",
// onTap: () {
// Navigator.pop(context);
// },
// ),
// );
// } else {
// bool status = swipeResponse.data; //use this status to get transactions.
// if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3));
// Utils.hideLoading(context);
// showMDialog(
// context,
// backgroundColor: Colors.transparent,
// isDismissable: true,
// child: SuccessDialog(widget.isFromDashboard),
// );
// }
// } catch (ex) {
// Utils.hideLoading(context);
// // Utils.handleException(ex, context, null);
// }
// // Utils.showLoading(context);
// // try {
// // GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 2, nfcValue: nfcId ?? "", isGpsRequired: isNfcLocationEnabled, lat: lat, long: lng);
// // if (g?.messageStatus != 1) {
// // Utils.hideLoading(context);
// // showDialog(
// // context: context,
// // builder: (BuildContext cxt) => ConfirmDialog(
// // message: g?.errorEndUserMessage ?? "Unexpected error occurred",
// // onTap: () {
// // Navigator.pop(context);
// // },
// // ),
// // );
// // } else {
// // bool status = await model.fetchAttendanceTracking(context);
// // Utils.hideLoading(context);
// // showMDialog(
// // context,
// // backgroundColor: Colors.transparent,
// // isDismissable: false,
// // child: SuccessDialog(widget.isFromDashboard),
// // );
// // }
// // } catch (ex) {
// // log(ex);
// // Utils.hideLoading(context);
// // // Utils.handleException(ex, context, (String msg) {
// // // Utils.confirmDialog(context, msg);
// // // });
// // }
// });
// }
// }
//
//
//
// void showMDialog(context, {Widget? child, Color? backgroundColor, bool isDismissable = true, bool isBusniessCard = false}) async {
// return showDialog(
// context: context,
// barrierDismissible: isDismissable,
// builder: (context) {
// return Dialog(
// shape: isBusniessCard
// ? const RoundedRectangleBorder(
// borderRadius: BorderRadius.all(
// Radius.circular(15.0),
// ),
// )
// : null,
// backgroundColor: backgroundColor,
// child: child,
// );
// },
// );
// }
// //
// // Future<bool> checkSession() async {
// // try {
// // Utils.showLoading(context);
// // await DashboardApiClient().getOpenMissingSwipes();
// // Utils.hideLoading(context);
// // return true;
// // } catch (ex) {
// // Utils.hideLoading(context);
// // Utils.handleException(ex, context, null);
// // return false;
// // }
// // }
//
//
// //TODO need to confirm ....
// // Future<void> performWifiAttendance({double? latitude, double? lng}) async {
// // // if (Platform.isAndroid) {
// // // if (!(await checkSession())) {
// // // return;
// // // }
// // // }
// // Utils.showLoading(context);
// // bool isConnected = await WiFiForIoTPlugin.connect(AppState().getMohemmWifiSSID ?? "",
// // password: AppState().getMohemmWifiPassword ?? "", joinOnce: Platform.isIOS ? false : true, security: NetworkSecurity.WPA, withInternet: false);
// //
// // if (Platform.isIOS) {
// // if (await WiFiForIoTPlugin.getSSID() == AppState().getMohemmWifiSSID) {
// // isConnected = true;
// // } else {
// // isConnected = false;
// // }
// // }
// //
// // if (isConnected && AppState().isAuthenticated) {
// // await WiFiForIoTPlugin.forceWifiUsage(true);
// // await Future.delayed(const Duration(seconds: 6));
// // try {
// // GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 3, nfcValue: "", isGpsRequired: isWifiLocationEnabled, lat: lat, long: lng);
// // bool status = await model.fetchAttendanceTracking(context);
// // Utils.hideLoading(context);
// // await closeWifiRequest();
// // if (g?.messageStatus == 2) {
// // showDialog(
// // barrierDismissible: true,
// // context: context,
// // builder: (cxt) => ConfirmDialog(
// // message: g?.errorEndUserMessage ?? "",
// // onTap: () {
// // Navigator.pop(context);
// // },
// // onCloseTap: () {},
// // ),
// // );
// // } else {
// // showMDialog(
// // context,
// // backgroundColor: Colors.transparent,
// // isDismissable: false,
// // child: SuccessDialog(widget.isFromDashboard),
// // );
// // }
// // } catch (ex) {
// // await closeWifiRequest();
// // Utils.hideLoading(context);
// // Utils.handleException(ex, context, null);
// // }
// // } else {
// // if (AppState().isAuthenticated) {
// // Utils.hideLoading(context);
// // Utils.confirmDialog(context, "LocaleKeys.comeNearHMGWifi.tr()");
// // } else {
// // await closeWifiRequest();
// // }
// // }
// // }
//
// Future<bool> closeWifiRequest() async {
// if (Platform.isAndroid) {
// await WiFiForIoTPlugin.forceWifiUsage(false);
// }
// return await WiFiForIoTPlugin.disconnect();
// }
//
// Future<void> performQrCodeAttendance( {double ? latitude , double ?longitude}) async {
// DashBoardProvider dashBoardProvider = Provider.of<DashBoardProvider>(context,listen: false);
// var qrCodeValue = await Navigator.of(context).push(
// MaterialPageRoute(
// builder: (BuildContext context) => QrScannerDialog(),
// ),
// );
// if (qrCodeValue != null) {
// Utils.showLoading(context);
// try {
// final swipeModel = Swipe(
// swipeTypeValue: SwipeTypeEnum.QR.getIntFromSwipeTypeEnum(),
// value: qrCodeValue,
// latitude: latitude,
// longitude: longitude,
// );
// log('model i got to scan qr is ${swipeModel.toJson()}');
// final swipeResponse = await dashBoardProvider.makeSwipe(model: swipeModel);
// log('response of swipe is ${swipeResponse.toJson()}');
//
// bool status = await swipeResponse.data;
// Utils.hideLoading(context);
// if (swipeResponse.responseCode == 2) {
// showDialog(
// barrierDismissible: true,
// context: context,
// builder: (cxt) => ConfirmDialog(
// message: swipeResponse.message ?? "",
// onTap: () {
// Navigator.pop(context);
// },
// onCloseTap: () {},
// ),
// );
// } else {
// showMDialog(
// context,
// backgroundColor: Colors.transparent,
// isDismissable: true,
// child: SuccessDialog(widget.isFromDashboard),
// );
// }
// } catch (ex) {
// log('$ex');
// Utils.hideLoading(context);
// //this need to confirm where it comes..
// // Utils.handleException(ex, context, null);
// }
// }
// }
//
// void markFakeAttendance(dynamic sourceName, String lat, String long) async {
// Utils.showLoading(context);
// try {
// // await DashboardApiClient().markFakeLocation(sourceName: sourceName, lat: lat, long: long);
// Utils.hideLoading(context);
// Utils.confirmDialog(context, "LocaleKeys.fakeLocation.tr()");
// } catch (ex) {
// log('$ex');
// Utils.hideLoading(context);
// //Utils.handleException(ex, context, null);
// }
// }
//
// Widget attendanceMethod(String title, IconData iconData, bool isEnabled, VoidCallback onPress) => Container(
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(15),
// color: isEnabled ? null : Colors.grey.withOpacity(.5),
// gradient: isEnabled
// ? const LinearGradient(
// transform: GradientRotation(.64),
// begin: Alignment.topRight,
// end: Alignment.bottomLeft,
// colors: [
// //ToDo set Colors according to design provided by designer...
// Colors.blue,
// Colors.green,
// // AppColor.gradiantEndColor,
// // MyColors.gradiantStartColor,
// ],
// )
// : null,
// ),
// clipBehavior: Clip.antiAlias,
// padding: const EdgeInsets.only(left: 10, right: 10, top: 14, bottom: 14),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// // SvgPicture.asset(image, color: Colors.white, alignment: Alignment.topLeft).expanded,
// Icon(iconData, color: isEnabled ? AppColor.black35 : Colors.grey),
// title.heading6(context),
// // title.toText17(isBold: true, color: Colors.white),
// ],
// ),
// ).onPress(
// () {
// log('isEnabled is ${!isEnabled}');
// if (!isEnabled) return;
// onPress();
// },
// );
// }

@ -169,7 +169,7 @@ class _AllRequestsFilterPageState extends State<AllRequestsFilterPage> {
}
return Scaffold(
appBar: DefaultAppBar(
title: context.translation.filter,
title: context.translation.filter,showBackButtonAsCancel: true,
actions: [
if (_showResetButton())
TextButton(

@ -88,6 +88,7 @@ class _AllRequestsSearchPageState extends State<AllRequestsSearchPage> {
// ).paddingOnly(bottom: 75),
appBar: DefaultAppBar(
title: context.translation.search,
showBackButtonAsCancel: true,
actions: [
if (_showResetButton())
TextButton(

@ -194,16 +194,17 @@ class _MyRequestsPageState extends State<MyRequestsPage> with SingleTickerProvid
],
),
).onPress(() async {
final result = await Navigator.push(context, CupertinoPageRoute(builder: (context) => AllRequestsSearchPage(data: _search)));
final result = await Navigator.push(context, CupertinoPageRoute(fullscreenDialog: true, builder: (context) => AllRequestsSearchPage(data: _search)));
if (result != null) {
_search = result;
setState(() {});
}
}).expanded,
16.width,
12.width,
IconButton(
icon: "download".toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)),
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
tooltip: "Export",
onPressed: () async {
final provider = Provider.of<AllRequestsProvider>(context, listen: false);
@ -223,9 +224,10 @@ class _MyRequestsPageState extends State<MyRequestsPage> with SingleTickerProvid
IconButton(
icon: "filter".toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)),
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
tooltip: "Filter",
onPressed: () async {
final result = await Navigator.push(context, CupertinoPageRoute(builder: (context) => AllRequestsFilterPage(data: _search)));
final result = await Navigator.push(context, CupertinoPageRoute(fullscreenDialog: true, builder: (context) => AllRequestsFilterPage(data: _search)));
if (result != null) {
_search = result;
setState(() {});
@ -233,7 +235,7 @@ class _MyRequestsPageState extends State<MyRequestsPage> with SingleTickerProvid
},
),
],
).paddingOnly(start: 16, end: 16),
).paddingOnly(start: 16, end: 8),
),
body: Consumer<AllRequestsProvider>(builder: (context, snapshot, _) {
return Column(

@ -4,6 +4,7 @@ import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/views/widgets/item_views/info_header_widget.dart';
class NonHmgEmployeeSwipeView extends StatefulWidget {
NonHmgEmployeeSwipeView({Key? key}) : super(key: key);
@ -47,6 +48,7 @@ class _NonHmgEmployeeSwipeViewState extends State<NonHmgEmployeeSwipeView> {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
InfoHeader16Widget( "Mark Attendance"),
Text(
"Mark Attendance",
style: AppTextStyles.heading5.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),

@ -180,7 +180,7 @@ class _LoginPageState extends State<LoginPage> {
children: [
Checkbox(
value: rememberMe,
activeColor: AppColor.blueStatus(context),
activeColor: AppColor.primary10,
side: BorderSide(color: AppColor.loadingColor(context), width: 1.0),
visualDensity: const VisualDensity(horizontal: -4.0, vertical: -4.0),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
@ -190,7 +190,7 @@ class _LoginPageState extends State<LoginPage> {
});
}),
8.width,
"Remember Me".bodyText(context).custom(color: context.isDark ? AppColor.primary50 : AppColor.neutral50).expanded,
"Remember Me".bodyText(context).custom(color: AppColor.textStyleColor(context)).expanded,
],
),
8.height,
@ -200,7 +200,7 @@ class _LoginPageState extends State<LoginPage> {
Checkbox(
value: privacyPolicyChecked,
side: BorderSide(color: AppColor.loadingColor(context), width: 1.0),
activeColor: AppColor.blueStatus(context),
activeColor: AppColor.primary10,
visualDensity: const VisualDensity(horizontal: -4.0, vertical: -4.0),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onChanged: (value) {
@ -210,10 +210,10 @@ class _LoginPageState extends State<LoginPage> {
}),
8.width,
RichText(
text: TextSpan(text: "I have read and agree to ", style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.primary50 : AppColor.neutral50), children: [
text: TextSpan(text: "I have read and agree to ", style: AppTextStyles.bodyText.copyWith(color: AppColor.textStyleColor(context)), children: [
TextSpan(
text: "Privacy Policy",
style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context), decoration: TextDecoration.underline),
style: AppTextStyles.bodyText.copyWith(color: AppColor.primary10, decoration: TextDecoration.underline),
recognizer: TapGestureRecognizer()
..onTap = () async {
Uri uri = Uri.parse("https://cloudsolutions.com.sa/en/privacy-policy");

@ -1,61 +1,61 @@
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
class ConfirmDialog extends StatelessWidget {
final String? title;
final String? message;
final String? okTitle;
final VoidCallback? onTap;
final VoidCallback? onCloseTap;
const ConfirmDialog({Key? key, this.title, this.message, this.okTitle, this.onTap, this.onCloseTap}) : super(key: key);
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: AppColor.background(context),
shape: const RoundedRectangleBorder(),
insetPadding: const EdgeInsets.only(left: 21, right: 21),
child: Padding(
padding: const EdgeInsets.only(left: 20, right: 20, top: 18, bottom: 28),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
title ?? "Confirm",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: AppColor.headingTextColor(context), height: 35 / 24, letterSpacing: -0.96),
).paddingOnly(top: 16),
),
IconButton(
padding: EdgeInsets.zero,
icon: const Icon(Icons.close),
color: AppColor.iconColor(context),
constraints: const BoxConstraints(),
onPressed: () => onCloseTap ?? Navigator.pop(context),
// onPressed: () => Navigator.pop(context),
)
],
),
message != null ? message!.heading5(context).custom(color: AppColor.textColor(context)) : const SizedBox(),
28.height,
AppFilledButton(
label: okTitle ?? "OK",
onPressed: onTap ?? () => Navigator.pop(context),
textColor: Colors.white,
//color: Ap.green,
),
],
),
),
);
}
}
// import 'package:flutter/material.dart';
// import 'package:test_sa/extensions/int_extensions.dart';
// import 'package:test_sa/extensions/text_extensions.dart';
// import 'package:test_sa/extensions/widget_extensions.dart';
// import 'package:test_sa/new_views/app_style/app_color.dart';
// import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
//
// class ConfirmDialog extends StatelessWidget {
// final String? title;
// final String? message;
// final String? okTitle;
// final VoidCallback? onTap;
// final VoidCallback? onCloseTap;
//
// const ConfirmDialog({Key? key, this.title, this.message, this.okTitle, this.onTap, this.onCloseTap}) : super(key: key);
//
// @override
// Widget build(BuildContext context) {
// return Dialog(
// backgroundColor: AppColor.background(context),
// shape: const RoundedRectangleBorder(),
// insetPadding: const EdgeInsets.only(left: 21, right: 21),
// child: Padding(
// padding: const EdgeInsets.only(left: 20, right: 20, top: 18, bottom: 28),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisSize: MainAxisSize.min,
// children: [
// Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Expanded(
// child: Text(
// title ?? "Confirm",
// style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: AppColor.headingTextColor(context), height: 35 / 24, letterSpacing: -0.96),
// ).paddingOnly(top: 16),
// ),
// IconButton(
// padding: EdgeInsets.zero,
// icon: const Icon(Icons.close),
// color: AppColor.iconColor(context),
// constraints: const BoxConstraints(),
// onPressed: () => onCloseTap ?? Navigator.pop(context),
// // onPressed: () => Navigator.pop(context),
// )
// ],
// ),
// message != null ? message!.heading5(context).custom(color: AppColor.textColor(context)) : const SizedBox(),
// 28.height,
// AppFilledButton(
// label: okTitle ?? "OK",
// onPressed: onTap ?? () => Navigator.pop(context),
// textColor: Colors.white,
// //color: Ap.green,
// ),
// ],
// ),
// ),
// );
// }
// }

@ -1,821 +0,0 @@
// import 'dart:async';
// import 'dart:developer';
// import 'dart:io';
//
// import 'package:flutter/material.dart';
// import 'package:geolocator/geolocator.dart';
// import 'package:huawei_location/huawei_location.dart';
// import 'package:nfc_manager/nfc_manager.dart';
// import 'package:permission_handler/permission_handler.dart';
// import 'package:provider/provider.dart';
// import 'package:test_sa/controllers/providers/api/user_provider.dart';
// import 'package:test_sa/extensions/int_extensions.dart';
// import 'package:test_sa/extensions/text_extensions.dart';
// import 'package:test_sa/extensions/widget_extensions.dart';
// import 'package:test_sa/new_views/app_style/app_color.dart';
// import 'package:test_sa/new_views/pages/land_page/nfc/nfc_reader_sheet.dart';
// import 'package:test_sa/new_views/swipe_module/dialoge/confirm_dialog.dart';
// import 'package:test_sa/new_views/swipe_module/dialoge/qr_scanner_dialog.dart';
// import 'package:test_sa/new_views/swipe_module/dialoge/success_dialog.dart';
// import 'package:test_sa/new_views/swipe_module/enums/swipe_type.dart';
// import 'package:test_sa/new_views/swipe_module/models/swipe_model.dart';
// import 'package:test_sa/new_views/swipe_module/swipe_success_view.dart';
// import 'package:test_sa/new_views/swipe_module/utils/location_utils.dart';
// import 'package:test_sa/new_views/swipe_module/utils/swipe_general_utils.dart';
// import 'package:wifi_iot/wifi_iot.dart';
// todo @sikander delete this file after compelte
// class MarkAttendanceWidget extends StatefulWidget {
// double topPadding;
// bool isFromDashboard;
//
// MarkAttendanceWidget({Key key, this.topPadding = 0, this.isFromDashboard = false}) : super(key: key);
//
// // todo MarkAttendanceWidget(this.model, {Key? key, this.topPadding = 0, this.isFromDashboard = false}) : super(key: key);
//
// @override
// _MarkAttendanceWidgetState createState() {
// return _MarkAttendanceWidgetState();
// }
// }
//
// class _MarkAttendanceWidgetState extends State<MarkAttendanceWidget> {
// bool isNfcEnabled = false, isQrEnabled = false, isWifiEnabled = false;
// UserProvider _userProvider;
//
// int _locationUpdateCbId = 0;
//
// @override
// void initState() {
// super.initState();
// checkAttendanceAvailability();
// }
//
// void checkAttendanceAvailability() async {
// _userProvider = Provider.of<UserProvider>(context, listen: false);
// bool isAvailable = await NfcManager.instance.isAvailable();
// log('backend enabled values are nfc ${_userProvider.user.enableNFC} qr ${_userProvider.user.enableQR} wifi ${_userProvider.user.enableWifi}');
// setState(() {
// if (isAvailable && _userProvider.user.enableNFC) isNfcEnabled = true;
// if (_userProvider.user.enableQR) isQrEnabled = true;
// if (_userProvider.user.enableWifi) isWifiEnabled = true;
// });
// }
//
// void checkHuaweiLocationPermission(SwipeTypeEnum attendanceType, BuildContext context) async {
// // Permission_Handler permissionHandler = PermissionHandler();
// LocationUtilities.isEnabled((bool isEnabled) async {
// if (isEnabled) {
// LocationUtilities.havePermission((bool permission) async {
// if (permission) {
// getHuaweiCurrentLocation(attendanceType, context);
// } else {
// bool has = await requestPermissions();
// if (has) {
// getHuaweiCurrentLocation(attendanceType, context);
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to give location permission to mark attendance",
// onTap: () {
// Navigator.pop(context);
// },
// ),
// );
// }
// }
// });
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to enable location services to mark attendance",
// onTap: () async {
// Navigator.pop(context);
// await Geolocator.openLocationSettings();
// },
// ),
// );
// }
// });
//
// // if (await permissionHandler.hasLocationPermission()) {
// // getHuaweiCurrentLocation(attendanceType);
// // } else {
// // bool has = await requestPermissions();
// // if (has) {
// // getHuaweiCurrentLocation(attendanceType);
// // } else {
// // showDialog(
// // context: context,
// // builder: (BuildContext cxt) => ConfirmDialog(
// // message: "You need to give location permission to mark attendance",
// // onTap: () {
// // Navigator.pop(context);
// // },
// // ),
// // );
// // }
// // }
// }
//
// Future<bool> requestPermissions() async {
// var result = await [
// Permission.location,
// ].request();
// return (result[Permission.location] == PermissionStatus.granted || result[Permission.locationAlways] == PermissionStatus.granted);
// }
//
// @override
// void dispose() {
// super.dispose();
// // Stop Session
// NfcManager.instance.stopSession();
// }
//
// @override
// Widget build(BuildContext context) {
// return Container(
// padding: EdgeInsets.only(left: 21, right: 21, bottom: 21, top: widget.topPadding),
// decoration: const BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), color: Colors.white),
// width: double.infinity,
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisSize: MainAxisSize.min,
// children: [
// 20.height,
// 'click me '.heading5(context).custom(color: AppColor.textStyleColor(context)),
// // LocaleKeys.markAttendance.tr().toSectionHeading(),
// // LocaleKeys.selectMethodOfAttendance.tr().toText11(color: const Color(0xff535353)),
// GridView(
// physics: const NeverScrollableScrollPhysics(),
// shrinkWrap: true,
// padding: const EdgeInsets.only(bottom: 0, top: 21),
// gridDelegate:
// SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: (MediaQuery.of(context).size.width < 550) ? 3 : 5, childAspectRatio: 1 / 1, crossAxisSpacing: 8, mainAxisSpacing: 8),
// children: availableAttendanceMethodList(context: context),
// )
// ],
// ),
// );
// }
//
// void handleSwipe({
// SwipeTypeEnum swipeType,
// @required bool isEnable,
// @required BuildContext context,
// }) {
// // if (AppState().getIsHuawei) {
// if (false) {
// checkHuaweiLocationPermission(swipeType, context);
// } else {
// LocationUtilities.isEnabled((bool isEnabled) {
// if (isEnabled) {
// LocationUtilities.havePermission((bool permission) {
// if (permission) {
// SwipeGeneralUtils.showLoading(context);
// LocationUtilities.getCurrentLocation(
// (Position position, bool isMocked) {
// if (isMocked) {
// SwipeGeneralUtils.hideLoading(context);
// SwipeGeneralUtils.markFakeAttendance(swipeType.name, position.latitude.toString() ?? "", position.longitude.toString() ?? "", context);
// } else {
// SwipeGeneralUtils.hideLoading(context);
// //todo performNfcAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? "");
// handleSwipeOperation(swipeType: swipeType, lat: position.latitude, lang: position.longitude);
// }
// },
// () {
// SwipeGeneralUtils.hideLoading(context);
// SwipeGeneralUtils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working.");
// },
// context,
// );
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to give location permission to mark attendance",
// onTap: () async {
// Navigator.pop(context);
// await Geolocator.openAppSettings();
// },
// ),
// );
// }
// });
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to enable location services to mark attendance",
// onTap: () async {
// Navigator.pop(context);
// await Geolocator.openLocationSettings();
// },
// ),
// );
// }
// });
// }
// }
//
// void handleSwipeOperation({@required SwipeTypeEnum swipeType, double lat, double lang, BuildContext context}) {
// switch (swipeType) {
// case SwipeTypeEnum.NFC:
// handleNfcAttendance(latitude: lat, longitude: lang, context: context);
// return;
// case SwipeTypeEnum.QR:
// performQrCodeAttendance(latitude: lat, longitude: lang, context: context);
// return;
// case SwipeTypeEnum.Wifi:
// //TODO need to implement.
// return;
// }
// }
//
// void getHuaweiCurrentLocation(SwipeTypeEnum attendanceType, BuildContext context) async {
// try {
// SwipeGeneralUtils.showLoading(context);
// FusedLocationProviderClient locationService = FusedLocationProviderClient()..initFusedLocationService();
// LocationRequest locationRequest = LocationRequest();
// locationRequest.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY;
// locationRequest.interval = 500;
// List<LocationRequest> locationRequestList = <LocationRequest>[locationRequest];
// LocationSettingsRequest locationSettingsRequest = LocationSettingsRequest(requests: locationRequestList);
//
// StreamSubscription<Location> _streamSubscription;
// int requestCode = (await (locationService.requestLocationUpdates(locationRequest)));
//
// _streamSubscription = locationService.onLocationData.listen(
// (Location location) async {
// SwipeGeneralUtils.hideLoading(context);
// await locationService.removeLocationUpdates(requestCode);
// handleSwipeOperation(swipeType: attendanceType);
// requestCode = 0;
// },
// );
//
// // locationService.checkLocationSettings(locationSettingsRequest).then((settings) async {
// // await locationService.getLastLocation().then((value) {
// // if (value.latitude == null || value.longitude == null) {
// // showDialog(
// // context: context,
// // builder: (BuildContext cxt) => ConfirmDialog(
// // message: "Unable to get your location, Please check your location settings & try again.",
// // onTap: () {
// // Navigator.pop(context);
// // },
// // ),
// // );
// // } else {
// // if (attendanceType == "QR") {
// // performQrCodeAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? "");
// // }
// // if (attendanceType == "WIFI") {
// // performWifiAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? "");
// // }
// // if (attendanceType == "NFC") {
// // performNfcAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? "");
// // }
// // }
// // }).catchError((error) {
// // log("HUAWEI LOCATION getLastLocation ERROR!!!!!");
// // log(error);
// // });
// // }).catchError((error) {
// // log("HUAWEI LOCATION checkLocationSettings ERROR!!!!!");
// // log(error);
// // if (error.code == "LOCATION_SETTINGS_NOT_AVAILABLE") {
// // // Location service not enabled.
// // }
// // });
// } catch (error) {
// log("HUAWEI LOCATION ERROR!!!!!");
// log('$error');
// SwipeGeneralUtils.hideLoading(context);
// // SwipeGeneralUtils.handleException(error, context, null);
// }
// }
//
// Future<void> handleNfcAttendance({double latitude = 0, double longitude = 0, BuildContext context}) async {
// final userProvider = Provider.of<UserProvider>(context, listen: false);
//
// if (Platform.isIOS) {
// SwipeGeneralUtils.readNFc(onRead: (String nfcId) async {
// await _processNfcAttendance(userProvider, nfcId, latitude, longitude, context);
// });
// } else {
// showNfcReader(context, onNcfScan: (String nfcId) async {
// await _processNfcAttendance(userProvider, nfcId ?? '', latitude, longitude, context);
// });
// }
// }
//
// Future<void> _processNfcAttendance(
// UserProvider userProvider,
// String nfcId,
// double latitude,
// double longitude,
// BuildContext context,
// ) async {
// SwipeGeneralUtils.showLoading(context);
// try {
// // final swipeModel = Swipe(
// // swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(),
// // value: nfcId,
// // latitude: latitude,
// // longitude: longitude,
// // );
// //Test model...
// final swipeModel = Swipe(swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), value: '123', latitude: 24.70865415364271, longitude: 46.66600861881879);
// final swipeResponse = await userProvider.makeSwipe(model: swipeModel);
// if (swipeResponse.isSuccess) {
// if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3));
// SwipeGeneralUtils.hideLoading(context);
// Navigator.pushNamed(context, SwipeSuccessView.routeName);
// } else {
// SwipeGeneralUtils.hideLoading(context);
// SwipeGeneralUtils.showErrorDialog(message: swipeResponse.message ?? "Unexpected error occurred", context: context);
// }
// } catch (errSwipeGeneralUtilsor) {
// SwipeGeneralUtils.hideLoading(context);
// // Uncomment below line for error handling if needed
// // SwipeGeneralUtils.handleException(error, context, null);
// }
// }
//
// Future<bool> closeWifiRequest() async {
// if (Platform.isAndroid) {
// await WiFiForIoTPlugin.forceWifiUsage(false);
// }
// return await WiFiForIoTPlugin.disconnect();
// }
//
// Future<void> performQrCodeAttendance({double latitude, double longitude, BuildContext context}) async {
// UserProvider userProvider = Provider.of<UserProvider>(context, listen: false);
// var qrCodeValue = await Navigator.of(context).push(
// MaterialPageRoute(
// builder: (BuildContext context) => QrScannerDialog(),
// ),
// );
// if (qrCodeValue != null) {
// SwipeGeneralUtils.showLoading(context);
// try {
// //test model..
// final swipeModel = Swipe(
// swipeTypeValue: SwipeTypeEnum.QR.getIntFromSwipeTypeEnum(),
// value: '2323',
// latitude: 24.70865415364271,
// longitude: 46.66600861881879,
// );
// final swipeResponse = await userProvider.makeSwipe(model: swipeModel);
// if (swipeResponse.isSuccess) {
// SwipeGeneralUtils.hideLoading(context);
// SwipeGeneralUtils.showMDialog(context, backgroundColor: Colors.transparent, isDismissable: true, child: SuccessDialog(widget.isFromDashboard));
// } else {
// SwipeGeneralUtils.hideLoading(context);
// showDialog(
// barrierDismissible: true,
// context: context,
// builder: (cxt) => ConfirmDialog(
// message: swipeResponse.message ?? "",
// onTap: () {
// Navigator.pop(context);
// },
// onCloseTap: () {}
// ),
// );
// }
// } catch (ex) {
// SwipeGeneralUtils.hideLoading(context);
// }
// }
// }
//
// List<Widget> availableAttendanceMethodList({@required BuildContext context}) {
// List<Widget> availableMethods = [];
// if (isNfcEnabled) {
// availableMethods.add(attendanceMethod(SwipeTypeEnum.NFC.name, 'nfc_icon', isNfcEnabled, () {
// handleSwipe(swipeType: SwipeTypeEnum.NFC, isEnable: isNfcEnabled, context: context);
// }));
// }
// if (isQrEnabled) {
// availableMethods.add(attendanceMethod(SwipeTypeEnum.QR.name, 'wifi_icon', isQrEnabled, () {
// handleSwipe(swipeType: SwipeTypeEnum.QR, isEnable: isQrEnabled, context: context);
// }));
// }
// if (isWifiEnabled) {
// availableMethods.add(attendanceMethod(SwipeTypeEnum.Wifi.name, 'wifi_icon', isWifiEnabled, () {
// handleSwipe(swipeType: SwipeTypeEnum.Wifi, isEnable: isWifiEnabled, context: context);
// }));
// }
// return availableMethods;
// }
//
// Widget attendanceMethod(String title, String icon, bool isEnabled, VoidCallback onPress) {
// return Container(
// padding: EdgeInsets.all(12),
// decoration: BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(18),
// border: Border.all(color: AppColor.white40, width: 2),
// ),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// icon.toSvgAsset(),
// title.heading5(context).custom(color: AppColor.neutral50),
// // Text(
// // title,
// // style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
// // ),
// ],
// ),
// ).onPress(
// () {
// log('isEnabled is ${!isEnabled}');
// if (!isEnabled) return;
// onPress();
// },
// );
// // return Container(
// // decoration: BoxDecoration(
// // borderRadius: BorderRadius.circular(15),
// // color: isEnabled ? null : Colors.grey.withOpacity(.5),
// // gradient: isEnabled
// // ? const LinearGradient(
// // transform: GradientRotation(.64),
// // begin: Alignment.topRight,
// // end: Alignment.bottomLeft,
// // colors: [
// // //ToDo set Colors according to design provided by designer...
// // Colors.blue,
// // Colors.green,
// // // AppColor.gradiantEndColor,
// // // MyColors.gradiantStartColor,
// // ],
// // )
// // : null,
// // ),
// // clipBehavior: Clip.antiAlias,
// // padding: const EdgeInsets.only(left: 10, right: 10, top: 14, bottom: 14),
// // child: Column(
// // crossAxisAlignment: CrossAxisAlignment.start,
// // children: [
// // // SvgPicture.asset(image, color: Colors.white, alignment: Alignment.topLeft).expanded,
// // Icon(iconData, color: isEnabled ? AppColor.black35 : Colors.grey),
// // title.heading6(context),
// // // title.toText17(isBold: true, color: Colors.white),
// // ],
// // ),
// // ).onPress(
// // () {
// // log('isEnabled is ${!isEnabled}');
// // if (!isEnabled) return;
// // onPress();
// // },
// // );
// }
// }
//
// // Widget customListItem({required String icon, required String heading, required String subHeading, required VoidCallback onTap}) {
// // return GestureDetector(
// // onTap: onTap, // Handles the tap
// // child: Card(
// // shape: RoundedRectangleBorder(
// // borderRadius: BorderRadius.circular(14), // Circular border radius
// // ),
// // color: Colors.white,
// // child: Row(
// // crossAxisAlignment: CrossAxisAlignment.start, // Align items at the top
// // children: [
// // // Icon Section
// // icon
// // .toSvgAsset(
// // width: 32,
// // color: AppColor.neutral120,
// // height: 29,
// // )
// // .paddingOnly(top: 8),
// // 14.width,
// // Expanded(
// // child: Column(
// // crossAxisAlignment: CrossAxisAlignment.start,
// // children: [
// // Text(
// // heading,
// // style: AppTextStyles.heading6.copyWith(color: AppColor.neutral50),
// // ),
// // 7.height,
// // Text(
// // subHeading,
// // style: AppTextStyles.bodyText2.copyWith(color: AppColor.neutral120),
// // ),
// // ],
// // ).paddingOnly(end: 50),
// // ),
// // ],
// // ).paddingAll(12),
// // ),
// // );
// // }
//
// //qr older code...
//
// //nfc older code widget
//
// // // if (AppState().getIsHuawei) {
// // if (false) {
// // checkHuaweiLocationPermission(SwipeTypeEnum.NFC);
// // } else {
// // LocationUtilities.isEnabled((bool isEnabled) {
// // if (isEnabled) {
// // LocationUtilities.havePermission((bool permission) {
// // if (permission) {
// // SwipeGeneralUtils.showLoading(context);
// // LocationUtilities.getCurrentLocation(
// // (Position position, bool isMocked) {
// // if (isMocked) {
// // SwipeGeneralUtils.hideLoading(context);
// // SwipeGeneralUtils.markFakeAttendance("NFC", position.latitude.toString() ?? "", position.longitude.toString() ?? "",context);
// // } else {
// // SwipeGeneralUtils.hideLoading(context);
// // //todo performNfcAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? "");
// // }
// // },
// // () {
// // SwipeGeneralUtils.hideLoading(context);
// // SwipeGeneralUtils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working.");
// // },
// // context,
// // );
// // } else {
// // showDialog(
// // context: context,
// // builder: (BuildContext cxt) => ConfirmDialog(
// // message: "You need to give location permission to mark attendance",
// // onTap: () async {
// // Navigator.pop(context);
// // await Geolocator.openAppSettings();
// // },
// // ),
// // );
// // }
// // });
// // } else {
// // showDialog(
// // context: context,
// // builder: (BuildContext cxt) => ConfirmDialog(
// // message: "You need to enable location services to mark attendance",
// // onTap: () async {
// // Navigator.pop(context);
// // await Geolocator.openLocationSettings();
// // },
// // ),
// // );
// // }
// // });
// // }
//
// //older code....
// // Future<void> performNfcAttendance({double lat = 0, double lng = 0}) async {
// // UserProvider userProvider = Provider.of<UserProvider>(context,listen:false);
// // if (Platform.isIOS) {
// // SwipeGeneralUtils.readNFc(onRead: (String nfcId) async {
// // SwipeGeneralUtils.showLoading(context);
// // try {
// // SwipeModel swipeResponse = await userProvider.makeSwipe(model: Swipe(swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), value: '', latitude: lat, longitude: lng));
// // if (swipeResponse.responseCode != 1) {
// // SwipeGeneralUtils.hideLoading(context);
// // showDialog(
// // context: context,
// // builder: (BuildContext cxt) => ConfirmDialog(
// // message: swipeResponse.message ?? "Unexpected error occurred",
// // onTap: () {
// // Navigator.pop(context);
// // },
// // ),
// // );
// // } else {
// // bool status = swipeResponse.data;
// // if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3));
// // SwipeGeneralUtils.hideLoading(context);
// // SwipeGeneralUtils.showMDialog(
// // context,
// // backgroundColor: Colors.transparent,
// // isDismissable: true,
// // child: SuccessDialog(widget.isFromDashboard),
// // );
// // }
// // } catch (ex) {
// // SwipeGeneralUtils.hideLoading(context);
// // // SwipeGeneralUtils.handleException(ex, context, null);
// // }
// // });
// // } else {
// // showNfcReader(context, onNcfScan: (String nfcId) async {
// // SwipeGeneralUtils.showLoading(context);
// // try {
// // SwipeModel swipeResponse = await userProvider.makeSwipe(model: Swipe(swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), value: nfcId??'', latitude: lat, longitude: lng));
// // log('api response i got is ${swipeResponse.toJson()}');
// // if (swipeResponse.responseCode != 1) {
// // SwipeGeneralUtils.hideLoading(context);
// // showDialog(
// // context: context,
// // builder: (BuildContext cxt) => ConfirmDialog(
// // message: swipeResponse.message ?? "Unexpected error occurred",
// // onTap: () {
// // Navigator.pop(context);
// // },
// // ),
// // );
// // } else {
// // bool status = swipeResponse.data; //use this status to get transactions.
// // if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3));
// // SwipeGeneralUtils.hideLoading(context);
// // SwipeGeneralUtils.showMDialog(
// // context,
// // backgroundColor: Colors.transparent,
// // isDismissable: true,
// // child: SuccessDialog(widget.isFromDashboard),
// // );
// // }
// // } catch (ex) {
// // SwipeGeneralUtils.hideLoading(context);
// // // SwipeGeneralUtils.handleException(ex, context, null);
// // }
// // // SwipeGeneralUtils.showLoading(context);
// // // try {
// // // GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 2, nfcValue: nfcId ?? "", isGpsRequired: isNfcLocationEnabled, lat: lat, long: lng);
// // // if (g?.messageStatus != 1) {
// // // SwipeGeneralUtils.hideLoading(context);
// // // showDialog(
// // // context: context,
// // // builder: (BuildContext cxt) => ConfirmDialog(
// // // message: g?.errorEndUserMessage ?? "Unexpected error occurred",
// // // onTap: () {
// // // Navigator.pop(context);
// // // },
// // // ),
// // // );
// // // } else {
// // // bool status = await model.fetchAttendanceTracking(context);
// // // SwipeGeneralUtils.hideLoading(context);
// // // showMDialog(
// // // context,
// // // backgroundColor: Colors.transparent,
// // // isDismissable: false,
// // // child: SuccessDialog(widget.isFromDashboard),
// // // );
// // // }
// // // } catch (ex) {
// // // log(ex);
// // // SwipeGeneralUtils.hideLoading(context);
// // // // SwipeGeneralUtils.handleException(ex, context, (String msg) {
// // // // SwipeGeneralUtils.confirmDialog(context, msg);
// // // // });
// // // }
// // });
// // }
// // }
//
// //
// // Future<bool> checkSession() async {
// // try {
// // SwipeGeneralUtils.showLoading(context);
// // await DashboardApiClient().getOpenMissingSwipes();
// // SwipeGeneralUtils.hideLoading(context);
// // return true;
// // } catch (ex) {
// // SwipeGeneralUtils.hideLoading(context);
// // SwipeGeneralUtils.handleException(ex, context, null);
// // return false;
// // }
// // }
//
// //TODO need to confirm ....
// Future<void> performWifiAttendance({double? latitude, double? lng}) async {
// // if (Platform.isAndroid) {
// // if (!(await checkSession())) {
// // return;
// // }
// // }
// SwipeGeneralUtils.showLoading(context);
// bool isConnected = await WiFiForIoTPlugin.connect(AppState().getMohemmWifiSSID ?? "",
// password: AppState().getMohemmWifiPassword ?? "", joinOnce: Platform.isIOS ? false : true, security: NetworkSecurity.WPA, withInternet: false);
//
// if (Platform.isIOS) {
// if (await WiFiForIoTPlugin.getSSID() == AppState().getMohemmWifiSSID) {
// isConnected = true;
// } else {
// isConnected = false;
// }
// }
//
// if (isConnected && AppState().isAuthenticated) {
// await WiFiForIoTPlugin.forceWifiUsage(true);
// await Future.delayed(const Duration(seconds: 6));
// try {
// GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 3, nfcValue: "", isGpsRequired: isWifiLocationEnabled, lat: lat, long: lng);
// bool status = await model.fetchAttendanceTracking(context);
// SwipeGeneralUtils.hideLoading(context);
// await closeWifiRequest();
// if (g?.messageStatus == 2) {
// showDialog(
// barrierDismissible: true,
// context: context,
// builder: (cxt) => ConfirmDialog(
// message: g?.errorEndUserMessage ?? "",
// onTap: () {
// Navigator.pop(context);
// },
// onCloseTap: () {},
// ),
// );
// } else {
// showMDialog(
// context,
// backgroundColor: Colors.transparent,
// isDismissable: false,
// child: SuccessDialog(widget.isFromDashboard),
// );
// }
// } catch (ex) {
// await closeWifiRequest();
// SwipeGeneralUtils.hideLoading(context);
// SwipeGeneralUtils.handleException(ex, context, null);
// }
// } else {
// if (AppState().isAuthenticated) {
// SwipeGeneralUtils.hideLoading(context);
// SwipeGeneralUtils.confirmDialog(context, "LocaleKeys.comeNearHMGWifi.tr()");
// } else {
// await closeWifiRequest();
// }
// }
// }
//
// // older grid widget..
// // attendanceMethod(SwipeTypeEnum.NFC.name, Icons.nfc, isNfcEnabled, () {
// // handleSwipe(swipeType: SwipeTypeEnum.NFC, isEnable: isNfcEnabled);
// // }),
// // attendanceMethod(SwipeTypeEnum.QR.name, Icons.qr_code_2, isQrEnabled, () async {
// // handleSwipe(swipeType: SwipeTypeEnum.QR, isEnable: true);
// // }),
// // //if (isWifiEnabled) //todo
// // attendanceMethod(SwipeTypeEnum.Wifi.name, Icons.wifi, isWifiEnabled, () {
// // // if (AppState().getIsHuawei) {
// // if (false) {
// // checkHuaweiLocationPermission(SwipeTypeEnum.Wifi);
// // } else {
// // LocationUtilities.isEnabled((bool isEnabled) {
// // if (isEnabled) {
// // LocationUtilities.havePermission((bool permission) {
// // if (permission) {
// // SwipeGeneralUtils.showLoading(context);
// // LocationUtilities.getCurrentLocation(
// // (Position position, bool isMocked) {
// // if (isMocked) {
// // SwipeGeneralUtils.hideLoading(context);
// // SwipeGeneralUtils.markFakeAttendance("WIFI", position.latitude.toString() ?? "", position.longitude.toString() ?? "", context);
// // } else {
// // SwipeGeneralUtils.hideLoading(context);
// // //todo performWifiAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? "");
// // }
// // },
// // () {
// // SwipeGeneralUtils.hideLoading(context);
// // SwipeGeneralUtils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working.");
// // },
// // context,
// // );
// // } else {
// // showDialog(
// // context: context,
// // builder: (BuildContext cxt) => ConfirmDialog(
// // message: "You need to give location permission to mark attendance",
// // onTap: () async {
// // Navigator.pop(context);
// // await Geolocator.openAppSettings();
// // },
// // ),
// // );
// // }
// // });
// // } else {
// // showDialog(
// // context: context,
// // builder: (BuildContext cxt) => ConfirmDialog(
// // message: "You need to enable location services to mark attendance",
// // onTap: () async {
// // Navigator.pop(context);
// // await Geolocator.openLocationSettings();
// // },
// // ),
// // );
// // }
// // });
// // }
// // }),

@ -47,87 +47,86 @@ class _SwipeHistoryViewState extends State<SwipeHistoryView> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const DefaultAppBar(
title: 'Swipe History',
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
8.height,
Row(
children: [
ADatePicker(
label: context.translation.from,
date: dateFrom,
from: DateTime(DateTime.now().year - 5, DateTime.now().month, DateTime.now().day),
formatDateWithTime: true,
onDatePicker: (selectedDate) {
if (selectedDate != null) {
showThemedTimePicker(
context: context,
initialTime: TimeOfDay.now(),
).then((selectedTime) {
// Handle the selected date and time here.
if (selectedTime != null) {
DateTime selectedDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
selectedTime.hour,
selectedTime.minute,
);
if (selectedDateTime != null) {
setState(() {
dateFrom = selectedDateTime;
});
appBar: const DefaultAppBar(title: 'Swipe History'),
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
spacing: 8,
children: [
ADatePicker(
label: context.translation.from,
date: dateFrom,
from: DateTime(DateTime.now().year - 5, DateTime.now().month, DateTime.now().day),
formatDateWithTime: true,
onDatePicker: (selectedDate) {
if (selectedDate != null) {
showThemedTimePicker(
context: context,
initialTime: TimeOfDay.now(),
).then((selectedTime) {
// Handle the selected date and time here.
if (selectedTime != null) {
DateTime selectedDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
selectedTime.hour,
selectedTime.minute,
);
if (selectedDateTime != null) {
setState(() {
dateFrom = selectedDateTime;
});
}
}
}
});
}
},
).expanded,
8.width,
ADatePicker(
label: context.translation.to,
date: dateTo,
from: DateTime(DateTime.now().year - 5, DateTime.now().month, DateTime.now().day),
formatDateWithTime: true,
onDatePicker: (selectedDate) {
if (selectedDate != null) {
showThemedTimePicker(
context: context,
initialTime: TimeOfDay.now(),
).then((selectedTime) {
// Handle the selected date and time here.
if (selectedTime != null) {
DateTime selectedDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
selectedTime.hour,
selectedTime.minute,
);
if (selectedDateTime != null) {
setState(() {
dateTo = selectedDateTime;
});
});
}
},
).expanded,
ADatePicker(
label: context.translation.to,
date: dateTo,
from: DateTime(DateTime.now().year - 5, DateTime.now().month, DateTime.now().day),
formatDateWithTime: true,
onDatePicker: (selectedDate) {
if (selectedDate != null) {
showThemedTimePicker(
context: context,
initialTime: TimeOfDay.now(),
).then((selectedTime) {
// Handle the selected date and time here.
if (selectedTime != null) {
DateTime selectedDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
selectedTime.hour,
selectedTime.minute,
);
if (selectedDateTime != null) {
setState(() {
dateTo = selectedDateTime;
});
}
}
}
});
}
},
).expanded,
],
),
12.height,
AppFilledButton(label: context.translation.search, maxWidth: false, onPressed: getSwipeHistory),
8.height,
const Divider().defaultStyle(context),
Consumer<UserProvider>(builder: (context, snapshot, child) {
return SwipeHistoryList(snapshot.swipeHistory ?? [], snapshot.isLoading).expanded;
}),
],
).paddingAll(20),
});
}
},
).expanded,
],
),
12.height,
AppFilledButton(label: context.translation.search, maxWidth: false, onPressed: getSwipeHistory),
8.height,
const Divider().defaultStyle(context),
Consumer<UserProvider>(builder: (context, snapshot, child) {
return SwipeHistoryList(snapshot.swipeHistory ?? [], snapshot.isLoading).expanded;
}).expanded,
],
).paddingOnly(start: 16, end: 16, top: 16),
),
);
}
}
@ -143,7 +142,7 @@ class SwipeHistoryList extends StatelessWidget {
return (list.isEmpty && !isLoading)
? NoDataFound(message: context.translation.noDataFound).center
: ListView.separated(
padding: EdgeInsets.only(top: 12.toScreenHeight),
padding: const EdgeInsets.only(bottom: 16, top: 16),
itemBuilder: (cxt, index) {
if (isLoading) return const SizedBox().toRequestShimmer(cxt, isLoading);
return SwipeHistoryCard(list[index]);

@ -29,6 +29,9 @@ import 'package:test_sa/new_views/swipe_module/enums/swipe_type.dart';
import 'package:test_sa/new_views/swipe_module/models/swipe_model.dart';
import 'package:test_sa/new_views/swipe_module/swipe_success_view.dart';
import 'package:test_sa/new_views/swipe_module/utils/location_utils.dart';
import 'package:test_sa/views/widgets/dialogs/confirm_dialog.dart';
import 'package:test_sa/views/widgets/item_views/info_header_widget.dart';
import 'package:test_sa/views/widgets/item_views/info_text_widget.dart';
import 'package:test_sa/views/widgets/qr/scan_qr.dart';
import 'package:wifi_iot/wifi_iot.dart';
@ -125,18 +128,8 @@ class SwipeGeneralUtils {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
icon.toSvgAsset(
color: isEnabled
? context.isDark
? AppColor.neutral30
: AppColor.neutral50
: Colors.grey.withOpacity(0.5)),
title.heading5(context).custom(
color: isEnabled
? context.isDark
? AppColor.neutral30
: AppColor.neutral50
: Colors.grey.withOpacity(0.5)),
icon.toSvgAsset(width: 24, height: 24, color: AppColor.icon2Color(context)),
title.bodyText2(context).custom(color: AppColor.labelTextStyleColor(context), fontSize: context.isTablet() ? 10.toScreenWidth : null),
],
),
).onPress(
@ -826,9 +819,9 @@ class _SwipeTypeBottomSheetContentState extends State<_SwipeTypeBottomSheetConte
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
context.translation.markAttendance.heading5(context).custom(color: AppColor.headingTextColor(context)),
InfoHeader16Widget("Mark Attendance"),
8.height,
context.translation.selectMethodToMarkAttendance.bodyText2(context).custom(color: AppColor.lightTextColor(context)),
InfoTextLabelWidget(label: context.translation.selectMethodToMarkAttendance),
12.height,
GridView.builder(
padding: const EdgeInsets.all(0),
@ -910,4 +903,3 @@ class _SwipeTypeItem {
required this.isEnabled,
});
}

@ -0,0 +1,38 @@
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import '../new_views/app_style/app_color.dart';
class FullScreenImageViewer extends StatelessWidget {
final Widget child;
const FullScreenImageViewer(this.child, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
InteractiveViewer(clipBehavior: Clip.none, panEnabled: true, minScale: 0.5, maxScale: 4.0, child: child).center,
SafeArea(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: FloatingActionButton(
heroTag: "full-screen-image-viewer",
backgroundColor: AppColor.background(context),
onPressed: () => Navigator.maybePop(context),
hoverColor: Colors.transparent,
splashColor: Colors.transparent,
shape: RoundedRectangleBorder(
side: BorderSide(color: AppColor.borderColor(context)),
borderRadius: BorderRadius.circular(14.0),
),
child: Icon(Icons.clear, size: 24, color: AppColor.icon2Color(context)),
),
),
),
],
),
);
}
}

@ -25,13 +25,13 @@ class UpdateAvailableScreen extends StatelessWidget {
48.height,
Text(
"Update Available",
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50),
style: AppTextStyles.heading4.copyWith(color: AppColor.headingTextColor(context)),
),
24.height,
Text(
"A new update is available on store.\nUpdate now to enjoy the latest features, improvements, and bug fixes for a smoother experience! ",
textAlign: TextAlign.center,
style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20),
style: AppTextStyles.heading6.copyWith(color: AppColor.lightTextColor(context)),
),
24.height,
AppFilledButton(

@ -59,11 +59,12 @@ class _WoAutoCompleteFieldState extends State<WoAutoCompleteField> {
@override
Widget build(BuildContext context) {
final border = UnderlineInputBorder(borderSide: BorderSide.none, borderRadius: BorderRadius.circular(10));
final border = UnderlineInputBorder(borderSide: BorderSide.none, borderRadius: BorderRadius.circular(16));
return Container(
decoration: BoxDecoration(
color: AppColor.background(context),
borderRadius: BorderRadius.circular(AppStyle.borderRadius * AppStyle.getScaleFactor(context)),
border: Border.all(color: AppColor.border2Color(context), width: 1),
borderRadius: BorderRadius.circular(16),
// boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)],
),
child: Autocomplete<WoByAssetIdModel>(
@ -93,7 +94,7 @@ class _WoAutoCompleteFieldState extends State<WoAutoCompleteField> {
return TextField(
controller: _controller,
focusNode: fieldFocusNode,
style: AppTextStyles.bodyText.copyWith(color: AppColor.black10),
style: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 14, color: AppColor.textStyleColor(context)),
textAlign: TextAlign.start,
decoration: InputDecoration(
border: border,
@ -101,16 +102,16 @@ class _WoAutoCompleteFieldState extends State<WoAutoCompleteField> {
focusedBorder: border,
enabledBorder: border,
errorBorder: border,
contentPadding: EdgeInsets.symmetric(vertical: 8.toScreenHeight, horizontal: 16.toScreenWidth),
contentPadding: EdgeInsets.symmetric(vertical: 12.toScreenHeight, horizontal: 16.toScreenWidth),
constraints: const BoxConstraints(),
suffixIconConstraints: const BoxConstraints(maxHeight: 24, maxWidth: 24 + 8),
filled: true,
enabled: widget.enabled,
fillColor: AppColor.fieldBgColor(context),
fillColor: AppColor.dropDownBackground(context),
errorStyle: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red60),
floatingLabelStyle: AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? null : AppColor.neutral20),
floatingLabelStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 12, color: AppColor.floatingTextStyleColor(context)),
labelStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 12, color: AppColor.labelTextStyleColor(context)),
labelText: "CM Work Order Number",
labelStyle: AppTextStyles.tinyFont.copyWith(color: AppColor.textColor(context)),
suffixIcon: loading ? const CircularProgressIndicator(color: AppColor.primary10, strokeWidth: 3.0).paddingOnly(end: 8) : null,
),
textInputAction: TextInputAction.search,

@ -2,21 +2,22 @@ import 'package:flutter/material.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
class ConfirmDialog extends StatelessWidget {
final String? title;
final String message;
final String? message;
final String? okTitle;
final VoidCallback? onTap;
final VoidCallback? onCloseTap;
const ConfirmDialog({Key? key, this.title, required this.message, this.okTitle, this.onTap, this.onCloseTap}) : super(key: key);
const ConfirmDialog({Key? key, this.title, this.message, this.okTitle, this.onTap, this.onCloseTap}) : super(key: key);
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Colors.white,
backgroundColor: AppColor.background(context),
shape: const RoundedRectangleBorder(),
insetPadding: const EdgeInsets.only(left: 21, right: 21),
child: Padding(
@ -31,21 +32,20 @@ class ConfirmDialog extends StatelessWidget {
Expanded(
child: Text(
title ?? "Confirm",
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: Colors.black87, height: 35 / 24, letterSpacing: -0.96),
style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: AppColor.headingTextColor(context), height: 35 / 24, letterSpacing: -0.96),
).paddingOnly(top: 16),
),
IconButton(
padding: EdgeInsets.zero,
icon: const Icon(Icons.close),
color: Colors.black87,
color: AppColor.iconColor(context),
constraints: const BoxConstraints(),
onPressed: () => onCloseTap ?? Navigator.pop(context),
// onPressed: () => Navigator.pop(context),
)
],
),
16.height,
message.bodyText(context),
message != null ? message!.heading5(context).custom(color: AppColor.textColor(context)) : const SizedBox(),
28.height,
AppFilledButton(
label: okTitle ?? "OK",

@ -37,6 +37,7 @@ class AAlertDialog extends StatelessWidget {
)
: AlertDialog(
backgroundColor: AppColor.background(context),
actionsPadding: const EdgeInsets.only(bottom: 12, left: 24, right: 24),
title: title != null ? Text(title!) : null,
content: content != null ? Text(content!) : null,
// contentTextStyle: AppTextStyles.bodyText.copyWith(color: (context.isDark ? AppColor.neutral30 : AppColor.neutral50)),

@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/views/app_style/sizing.dart';
class LoadingWithTextDialog extends StatelessWidget {
final String? message;
const LoadingWithTextDialog({Key? key, this.message}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Material(
borderRadius: BorderRadius.circular(20.0),
child: Container(
height: 150.toScreenWidth,
width: 200.toScreenWidth,
alignment: Alignment.center,
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: AppColor.neutral30,
borderRadius: BorderRadius.circular(20.0),
boxShadow: [AppStyle.boxShadow],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(strokeWidth: 3),
16.height,
Text(
message ?? "Uploading...",
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: AppColor.textColor(context), height: 35 / 24, letterSpacing: -0.96),
)
],
),
),
),
);
}
}

@ -7,10 +7,17 @@ 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';
import 'package:test_sa/models/device/asset_by_id_model.dart';
import 'package:test_sa/models/generic_attachment_model.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.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/item_views/info_header_widget.dart';
import 'package:test_sa/views/widgets/item_views/info_text_widget.dart';
import 'package:test_sa/views/widgets/equipment/asset_image_upload_page.dart';
import 'package:test_sa/views/widgets/images/files_list.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
import 'package:test_sa/views/widgets/loaders/app_loading.dart';
import 'package:test_sa/views/widgets/loaders/failed_loading.dart';
@ -32,6 +39,7 @@ class _AssetDetailPageState extends State<AssetDetailPage> with SingleTickerProv
late AnimationController _animationController;
int? assetId;
AssetProvider? _assetProvider;
AssetByIdModel? assetModel;
@override
void initState() {
@ -48,6 +56,7 @@ class _AssetDetailPageState extends State<AssetDetailPage> with SingleTickerProv
super.dispose();
}
List<GenericAttachmentModel> attachments = [];
@override
Widget build(BuildContext context) {
@ -57,7 +66,7 @@ class _AssetDetailPageState extends State<AssetDetailPage> with SingleTickerProv
return Scaffold(
appBar: DefaultAppBar(title: context.translation.assetDetails),
body: FutureBuilder<AssetByIdModel>(
future: _assetProvider!.getAssetById(assetId!, context.translation),
future: assetModel == null ? _assetProvider!.getAssetById(assetId!, context.translation) : null,
builder: (BuildContext context, AsyncSnapshot<AssetByIdModel> snapshot) {
if (snapshot.hasError) {
return FailedLoading(
@ -71,9 +80,19 @@ class _AssetDetailPageState extends State<AssetDetailPage> with SingleTickerProv
return const Center(child: ALoading());
} else if (snapshot.hasData) {}
if (snapshot.hasData) {
AssetByIdModel assetModel = snapshot.data!;
// Start animation when data is loaded
_animationController.forward(from: 0.0);
assetModel = snapshot.data!;
attachments = assetModel?.assetAttachments
?.map((attachment) => GenericAttachmentModel(
id: attachment.id,
name: attachment.attachmentName,
originalName: attachment.originalName,
))
.toList() ??
[];
if (!_animationController.isCompleted) {
_animationController.forward(from: 0.0);
}
// Image animation - fade in with scale
final imageAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
@ -180,142 +199,193 @@ class _AssetDetailPageState extends State<AssetDetailPage> with SingleTickerProv
),
);
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Animated image
FadeTransition(
opacity: imageAnimation,
child: ScaleTransition(
scale: imageScaleAnimation,
child: AspectRatio(
aspectRatio: 159 / 94,
child: Container(
width: 95,
height: 95,
decoration: ShapeDecoration(
color: AppColor.neutral30,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(assetModel.assetPhoto != null ? URLs.getFileUrl(assetModel.assetPhoto!)! : "https://www.lasteelcraft.com/images/no-image-available.png"),
)),
),
),
),
),
6.height,
// Animated content column
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
final attachmentAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _animationController,
curve: const Interval(0.80, 1.0, curve: Curves.easeOut),
),
);
final attachmentSlideAnimation = Tween<Offset>(
begin: const Offset(0.0, 0.1),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _animationController,
curve: const Interval(0.80, 1.0, curve: Curves.easeOutCubic),
),
);
return Column(
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Status label with animation
if (assetModel.commissioningStatus != null)
FadeTransition(
opacity: statusAnimation,
child: SlideTransition(
position: statusSlideAnimation,
child: StatusLabel(
label: assetModel.commissioningStatus!.name,
textColor: AppColor.getRequestStatusTextColorByName(context, assetModel.commissioningStatus!.name!),
backgroundColor: AppColor.getRequestStatusColorByName(context, assetModel.commissioningStatus!.name!),
// Animated image
FadeTransition(
opacity: imageAnimation,
child: ScaleTransition(
scale: imageScaleAnimation,
child: AspectRatio(
aspectRatio: 159 / 94,
child: Container(
width: 95,
height: 95,
decoration: ShapeDecoration(
color: AppColor.neutral30,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(assetModel?.assetPhoto != null ? URLs.getFileUrl(assetModel!.assetPhoto!)! : "https://www.lasteelcraft.com/images/no-image-available.png"),
)),
),
),
),
if (assetModel.commissioningStatus != null) 8.height,
// Header with animation
FadeTransition(
opacity: headerAnimation,
child: SlideTransition(
position: headerSlideAnimation,
child: InfoHeader16Widget(assetModel.modelDefinition?.assetName?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"),
),
),
8.height,
6.height,
// Animated content column
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Status label with animation
if (assetModel?.commissioningStatus != null)
FadeTransition(
opacity: statusAnimation,
child: SlideTransition(
position: statusSlideAnimation,
child: StatusLabel(
label: assetModel!.commissioningStatus!.name,
textColor: AppColor.getRequestStatusTextColorByName(context, assetModel!.commissioningStatus!.name!),
backgroundColor: AppColor.getRequestStatusColorByName(context, assetModel!.commissioningStatus!.name!),
),
),
),
if (assetModel?.commissioningStatus != null) 8.height,
// Details row with animation
FadeTransition(
opacity: detailsAnimation,
child: SlideTransition(
position: detailsSlideAnimation,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
// Header with animation
FadeTransition(
opacity: headerAnimation,
child: SlideTransition(
position: headerSlideAnimation,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 2,
children: [
InfoTextWidget(label: context.translation.assetNo, value: assetModel.multiAssets!.first.assetNumber, showEmptyValue: true),
InfoTextWidget(label: context.translation.modelName, value: assetModel.modelDefinition!.modelName, showEmptyValue: true),
InfoTextWidget(label: context.translation.supplier, value: assetModel.supplier?.suppliername, showEmptyValue: true),
InfoTextWidget(label: context.translation.manufacture, value: assetModel.modelDefinition!.manufacturerName, showEmptyValue: true),
InfoHeader16Widget(assetModel?.modelDefinition?.assetName?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"),
if (context.userProvider.isEngineer && !(assetModel?.hasTrainingImageData ?? true))
const InfoTextLabelWidget(label: '*Upload Images for this asset', color: AppColor.red30),
],
).expanded,
8.width,
Column(
),
),
),
8.height,
// Details row with animation
FadeTransition(
opacity: detailsAnimation,
child: SlideTransition(
position: detailsSlideAnimation,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8,
children: [
InfoTextWidget(label: context.translation.snNo, value: assetModel.multiAssets!.first.assetSerialNo, showEmptyValue: true),
InfoTextWidget(label: context.translation.site, value: assetModel.site?.custName?.cleanupWhitespace.capitalizeFirstOfEach, showEmptyValue: true),
InfoTextWidget(label: context.translation.building, value: assetModel.building?.name?.cleanupWhitespace.capitalizeFirstOfEach, showEmptyValue: true),
InfoTextWidget(label: context.translation.floor, value: assetModel.floor?.name?.cleanupWhitespace.capitalizeFirstOfEach, showEmptyValue: true),
InfoTextWidget(label: context.translation.md, value: assetModel.department?.departmentName?.cleanupWhitespace.capitalizeFirstOfEach, showEmptyValue: true),
InfoTextWidget(label: context.translation.room, value: assetModel.room?.name?.cleanupWhitespace.capitalizeFirstOfEach, showEmptyValue: true),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InfoTextWidget(label: context.translation.assetNo, value: assetModel!.multiAssets!.first.assetNumber, showEmptyValue: true, copyValue: true),
InfoTextWidget(label: context.translation.modelName, value: assetModel!.modelDefinition!.modelName, showEmptyValue: true),
InfoTextWidget(label: context.translation.supplier, value: assetModel!.supplier?.suppliername, showEmptyValue: true),
InfoTextWidget(label: context.translation.manufacture, value: assetModel!.modelDefinition!.manufacturerName, showEmptyValue: true),
],
).expanded,
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InfoTextWidget(label: context.translation.snNo, value: assetModel!.multiAssets!.first.assetSerialNo, showEmptyValue: true),
InfoTextWidget(label: context.translation.site, value: assetModel!.site?.custName?.cleanupWhitespace.capitalizeFirstOfEach, showEmptyValue: true),
InfoTextWidget(label: context.translation.building, value: assetModel!.building?.name?.cleanupWhitespace.capitalizeFirstOfEach, showEmptyValue: true),
InfoTextWidget(label: context.translation.floor, value: assetModel!.floor?.name?.cleanupWhitespace.capitalizeFirstOfEach, showEmptyValue: true),
InfoTextWidget(label: context.translation.md, value: assetModel!.department?.departmentName?.cleanupWhitespace.capitalizeFirstOfEach, showEmptyValue: true),
InfoTextWidget(label: context.translation.room, value: assetModel!.room?.name?.cleanupWhitespace.capitalizeFirstOfEach, showEmptyValue: true),
],
).expanded,
],
).expanded,
],
),
),
),
// 8.height,
// Divider with animation
FadeTransition(
opacity: datesAnimation,
child: const Divider().defaultStyle(context),
),
// 8.height,
// Dates section with animation
FadeTransition(
opacity: datesAnimation,
child: SlideTransition(
position: datesSlideAnimation,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InfoTextWidget(label: context.translation.installationDate, value: assetModel?.installationDate?.toAssetDetailsFormat, showEmptyValue: true),
InfoTextWidget(label: context.translation.nextPmDate, value: assetModel?.nextPMDate?.toAssetDetailsFormat, showEmptyValue: true),
InfoTextWidget(label: context.translation.lastPmDate, value: assetModel?.lastPMDate?.toAssetDetailsFormat, showEmptyValue: true),
],
),
),
),
),
),
8.height,
// Divider with animation
FadeTransition(
opacity: datesAnimation,
child: const Divider(color: AppColor.neutral30, height: 1, thickness: 1),
),
8.height,
// Description with animation (if exists)
if ((assetModel?.modelDefinition?.assetDescription ?? "").isNotEmpty) ...[
// 8.height,
FadeTransition(
opacity: descAnimation,
child: const Divider().defaultStyle(context),
),
FadeTransition(
opacity: descAnimation,
child: SlideTransition(
position: descSlideAnimation,
child: InfoTextWidget(label: assetModel!.modelDefinition!.assetDescription!, showEmptyValue: true, isMsg: true),
),
),
],
// Dates section with animation
FadeTransition(
opacity: datesAnimation,
child: SlideTransition(
position: datesSlideAnimation,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InfoTextWidget(label: context.translation.installationDate, value: assetModel.installationDate?.toAssetDetailsFormat, showEmptyValue: true),
InfoTextWidget(label: context.translation.nextPmDate, value: assetModel.nextPMDate?.toAssetDetailsFormat, showEmptyValue: true),
InfoTextWidget(label: context.translation.lastPmDate, value: assetModel.lastPMDate?.toAssetDetailsFormat, showEmptyValue: true),
],
),
),
if (attachments.isNotEmpty) ...[
// 8.height,
FadeTransition(
opacity: attachmentAnimation,
child: const Divider().defaultStyle(context),
),
FadeTransition(
opacity: attachmentAnimation,
child: SlideTransition(
position: attachmentSlideAnimation,
child: FilesList(images: attachments.map((toElement) => URLs.getFileUrl(toElement.name!) ?? '').toList()),
),
),
]
],
),
// Description with animation (if exists)
if ((assetModel.modelDefinition?.assetDescription ?? "").isNotEmpty) ...[
8.height,
FadeTransition(
opacity: descAnimation,
child: const Divider().defaultStyle(context),
),
FadeTransition(
opacity: descAnimation,
child: SlideTransition(
position: descSlideAnimation,
child: InfoTextWidget(label: assetModel.modelDefinition!.assetDescription!, showEmptyValue: true, isMsg: true),
),
),
]
],
)
],
).toShadowContainer(context),
).toShadowContainer(context),
).expanded,
if (context.userProvider.isEngineer && !(assetModel!.hasTrainingImageData ?? true))
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
label: "Upload Images",
onPressed: _upload,
),
),
],
);
}
return const Center(child: ALoading());
@ -323,4 +393,12 @@ class _AssetDetailPageState extends State<AssetDetailPage> with SingleTickerProv
),
);
}
void _upload() async {
bool reload = await Navigator.push(context, MaterialPageRoute(builder: (context) => AssetImageUploadPage(assetId: assetId!))) ?? false;
if (reload) {
assetModel = null;
setState(() {});
}
}
}

@ -0,0 +1,251 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/devices_provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/helper/utils.dart';
import 'package:test_sa/models/device/asset_by_id_model.dart';
import 'package:test_sa/models/generic_attachment_model.dart';
import 'package:test_sa/modules/cm_module/cm_request_utils.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.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/full_screen_image_viewer.dart';
import 'package:test_sa/views/widgets/dialogs/dialog.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
import 'package:test_sa/views/widgets/item_views/info_header_widget.dart';
import 'package:test_sa/views/widgets/item_views/info_text_widget.dart';
class AssetImageUploadPage extends StatefulWidget {
int assetId;
TrainingImageData? attachments;
AssetImageUploadPage({Key? key, required this.assetId, this.attachments}) : super(key: key);
@override
_AssetImageUploadPageState createState() {
return _AssetImageUploadPageState();
}
}
class _AssetImageUploadPageState extends State<AssetImageUploadPage> {
late TrainingImageData trainingImageData;
@override
void initState() {
super.initState();
trainingImageData = widget.attachments ?? TrainingImageData(additionalImages: []);
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const DefaultAppBar(title: "Upload Images"),
body: Column(
children: [
ListView(
padding: const EdgeInsets.all(16),
children: [
InfoHeader16Widget("Instructions"),
InfoTextLabelWidget(
label:
"* Please upload all images of the sides of the asset as mentioned below.\n* Images should be clear and high quality.\n* Avoid uploading blurry, low-light or low-resolution images."),
GridView(
padding: const EdgeInsets.only(top: 16, bottom: 16),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: (MediaQuery.of(context).size.width < 550) ? 4 : 4, childAspectRatio: 1, crossAxisSpacing: 12, mainAxisSpacing: 12),
children: <Widget>[
gridCard(
icon: "asset_image_upload/side_right",
title: "Right Side",
attachment: trainingImageData.rightImage,
onUpdate: (value) {
trainingImageData.rightImage = value;
setState(() {});
},
),
gridCard(
icon: "asset_image_upload/side_left",
title: "Left Side",
attachment: trainingImageData.leftImage,
onUpdate: (value) {
trainingImageData.leftImage = value;
setState(() {});
},
),
gridCard(
icon: "asset_image_upload/side_top",
title: "Top Side",
attachment: trainingImageData.upImage,
onUpdate: (value) {
trainingImageData.upImage = value;
setState(() {});
},
),
gridCard(
icon: "asset_image_upload/side_bottom",
title: "Bottom Side",
attachment: trainingImageData.downImage,
onUpdate: (value) {
trainingImageData.downImage = value;
setState(() {});
},
),
],
),
AttachmentPicker(
label: "Additional Images",
pickerTitle: "Attach Images",
attachment: trainingImageData.additionalImages!,
buttonColor: AppColor.black10,
onlyImages: true,
pickMultiple: true,
// showAsGrid: true,
buttonIcon: 'image-plus'.toSvgAsset(color: AppColor.primary10),
onChange: (_attachments) {
trainingImageData.additionalImages = _attachments;
setState(() {});
},
).toShadowContainer(context),
],
).expanded,
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
disableButton: trainingImageData.leftImage == null || trainingImageData.rightImage == null || trainingImageData.upImage == null || trainingImageData.downImage == null,
label: "Upload",
onPressed: () async {
bool result = await showDialog(
context: context,
builder: (_) => AAlertDialog(
title: context.translation.confirm,
content: "I followed all the instructions and uploading the images in high quality, clear and not blurry.",
),
);
if (result) {
Utils.showLoadingWithTextDialog(context);
result = await Provider.of<AssetProvider>(context, listen: false).uploadAssetImages(widget.assetId, trainingImageData);
Utils.hideLoading(context);
if (result) {
Navigator.pop(context, result);
}
}
},
),
),
],
));
}
Widget gridCard({
required String icon,
required String title,
required GenericAttachmentModel? attachment,
required Function(GenericAttachmentModel?) onUpdate,
}) {
return Stack(
alignment: AlignmentDirectional.topEnd,
children: [
Positioned.fill(
child: AttachmentPicker(
label: "",
pickerTitle: title,
onlyImages: true,
checkQuality: true,
enabled: attachment == null,
attachment: attachment == null ? [] : [attachment],
buttonIcon: 'image-plus'.toSvgAsset(color: AppColor.primary10),
onChange: (_attachments) {
if (_attachments.isNotEmpty) {
onUpdate(_attachments.first);
} else {
onUpdate(null);
}
},
child: imageAngleCard(icon, title, attachment),
).toShadowContainer(context, padding: 0),
),
if (attachment != null)
Positioned(
top: 4,
right: 4,
child: IconButton(
padding: EdgeInsets.zero,
icon: const Icon(Icons.clear),
iconSize: 18,
style: IconButton.styleFrom(elevation: 2, tapTargetSize: MaterialTapTargetSize.shrinkWrap, backgroundColor: AppColor.red30, foregroundColor: Colors.white),
visualDensity: VisualDensity.compact,
constraints: const BoxConstraints(),
onPressed: () {
onUpdate(null);
},
)),
],
);
}
Widget imageAngleCard(String icon, String title, GenericAttachmentModel? attachment) {
if (attachment != null) {
Widget imageWidget = _getImageWidget(attachment.name!);
return ClipRRect(borderRadius: BorderRadius.circular(12), child: imageWidget.onPress(() => context.fullScreenImageViewer(imageWidget)));
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
icon.toSvgAsset(height: 50, width: 50),
Text(
title,
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
],
);
}
Widget _getImageWidget(String imageUrl) {
// Check if it's base64 encoded (contains | separator)
if (imageUrl.contains('|')) {
try {
final base64Data = imageUrl.split('|').last;
return Image.memory(
base64Decode(base64Data),
fit: BoxFit.cover,
);
} catch (e) {
return const Icon(Icons.broken_image, size: 48);
}
}
// Check if it's a local file path
if (CMRequestUtils.isLocalUrl(imageUrl)) {
return Image.file(
File(imageUrl),
fit: BoxFit.cover,
);
}
// Otherwise treat as network URL
return Image.network(
imageUrl,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return const Icon(Icons.broken_image, size: 48);
},
);
}
}

@ -13,7 +13,7 @@ class AssetItemGridView extends StatelessWidget {
final Asset device;
final Function(Asset) onPressed;
const AssetItemGridView({Key? key,required this.device,required this.onPressed}) : super(key: key);
const AssetItemGridView({Key? key, required this.device, required this.onPressed}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -44,6 +44,13 @@ class AssetItemGridView extends StatelessWidget {
overflow: TextOverflow.ellipsis,
style: AppTextStyles.bodyText.copyWith(color: AppColor.textStyleColor(context)),
),
if (context.userProvider.isEngineer && !(device.hasTrainingImageData ?? true)) ...[
2.height,
Text(
'*Upload Images for this asset',
style: AppTextStyles.tinyFont2.copyWith(color: AppColor.red30),
),
],
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,

@ -44,6 +44,13 @@ class AssetItemListView extends StatelessWidget {
children: [
InfoHeaderWidget(device.modelDefinition!.assetName!.cleanupWhitespace),
// device.modelDefinition!.assetName!.cleanupWhitespace.capitalizeFirstOfEach.heading6(context),
if (context.userProvider.isEngineer && !(device.hasTrainingImageData ?? true)) ...[
2.height,
Text(
'*Upload Images for this asset',
style: AppTextStyles.tinyFont2.copyWith(color: AppColor.red30),
),
],
4.height,
InfoTextWidget(label: context.translation.assetNumber, value: device.assetNumber),
InfoTextWidget(label: context.translation.modelName, value: device.modelDefinition!.modelName),

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@ -97,7 +99,7 @@ class _MyAssetsPageState extends State<MyAssetsPage> with SingleTickerProviderSt
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (!widget.fromBottomBar)
const Icon(Icons.arrow_back_ios).onPress(() {
Icon(Platform.isIOS ? Icons.arrow_back_ios : Icons.arrow_back).onPress(() {
Navigator.of(context).pop();
}),
Container(
@ -122,20 +124,28 @@ class _MyAssetsPageState extends State<MyAssetsPage> with SingleTickerProviderSt
],
),
).onPress(() async {
final result = await Navigator.push(context, MaterialPageRoute(builder: (context) => const DeviceSearchScreen()));
final result = await Navigator.push(context, CupertinoPageRoute(fullscreenDialog: true, builder: (context) => const DeviceSearchScreen()));
if (result != null) {
_searchAsset = result;
}
_searchableList.clear();
_searchableList.addAll(_devicesProvider.devices);
}).expanded,
16.width,
Icon(Icons.grid_view_outlined, color: AppColor.icon2Color(context)).onPress(() => setState(() {
showListView = !showListView;
})),
12.width,
IconButton(
icon: Icon(!showListView ? Icons.view_list_sharp : Icons.grid_view_outlined, color: AppColor.icon2Color(context)),
padding: EdgeInsets.zero,
iconSize: 20,
visualDensity: VisualDensity.compact,
tooltip: !showListView ? "View as List" : "View as Grid",
onPressed: () => setState(() {
showListView = !showListView;
}),
),
IconButton(
icon: "download".toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)),
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
tooltip: "Export",
onPressed: () async {
await Navigator.push(
@ -154,9 +164,10 @@ class _MyAssetsPageState extends State<MyAssetsPage> with SingleTickerProviderSt
IconButton(
icon: "filter".toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)),
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
tooltip: "Filter",
onPressed: () async {
final result = await Navigator.push(context, CupertinoPageRoute(builder: (context) => DeviceFilterScreen(data: _searchAsset)));
final result = await Navigator.push(context, CupertinoPageRoute(fullscreenDialog: true, builder: (context) => DeviceFilterScreen(data: _searchAsset)));
if (result != null) {
_searchAsset = result;
}
@ -165,7 +176,7 @@ class _MyAssetsPageState extends State<MyAssetsPage> with SingleTickerProviderSt
},
),
],
).paddingOnly(start: 16, end: 16),
).paddingOnly(start: 16, end: 8),
),
body: SafeArea(
child: LoadingManager(
@ -305,7 +316,7 @@ class _MyAssetsPageState extends State<MyAssetsPage> with SingleTickerProviderSt
floatingActionButton: FloatingActionButton(
heroTag: "some tag 2",
backgroundColor: AppColor.primary10,
child: "qr".toSvgAsset(height: 32, fit: BoxFit.fitHeight, color: AppColor.background(context)),
child: Icon(Icons.qr_code_2_rounded, size: 40, color: AppColor.addItemButtonColor(context)),
onPressed: () async {
String? result = await Navigator.of(context).push(
MaterialPageRoute(builder: (_) => const ScanQr()),

@ -7,6 +7,7 @@ import 'package:test_sa/models/base.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.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';
typedef SelectionBuilderString = String Function(dynamic);
@ -61,8 +62,9 @@ class _SelectionBottomSheetState<T extends Base> extends State<MultipleSelection
Widget build(BuildContext context) {
bool isAllSelected = _selectedValue.isNotEmpty && (widget.items?.length ?? 0) == _selectedValue.length;
return Scaffold(
appBar: AppBar(
title: widget.title.heading5(context).custom(color: AppColor.textStyleColor(context)),
appBar: DefaultAppBar(
title: widget.title,
showBackButtonAsCancel: true,
actions: [
if (widget.showCancel)
AnimatedContainer(

@ -7,6 +7,7 @@ import 'package:test_sa/models/base.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.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';
typedef SelectionBuilderString = String Function(dynamic);
@ -57,35 +58,36 @@ class _SelectionBottomSheetState<T extends Base> extends State<SelectionFullScre
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: widget.title.heading5(context).custom(color: AppColor.textStyleColor(context)),
appBar: DefaultAppBar(
title: widget.title,
showBackButtonAsCancel: true,
actions: [
if (widget.showCancel)
AnimatedContainer(
height: 24,
duration: const Duration(milliseconds: 250),
margin: const EdgeInsets.only(right: 16),
decoration: BoxDecoration(color: _selectedValue != null ? const Color(0xffF63939).withOpacity(.75) : Colors.grey.withOpacity(.75), borderRadius: BorderRadius.circular(30)),
padding: const EdgeInsets.only(left: 4, right: 8, top: 4, bottom: 4),
alignment: Alignment.center,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(Icons.clear, color: Colors.white, size: 16),
4.width,
const Text(
"Clear",
style: TextStyle(fontSize: 14, color: Colors.white, height: 1),
)
],
).onPress(_selectedValue != null
? () {
Navigator.pop(context);
widget.onSelect(null);
}
: null),
),
AnimatedContainer(
height: 24,
duration: const Duration(milliseconds: 250),
margin: const EdgeInsets.only(right: 16),
decoration: BoxDecoration(color: _selectedValue != null ? const Color(0xffF63939).withOpacity(.75) : Colors.grey.withOpacity(.75), borderRadius: BorderRadius.circular(30)),
padding: const EdgeInsets.only(left: 4, right: 8, top: 4, bottom: 4),
alignment: Alignment.center,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(Icons.clear, color: Colors.white, size: 16),
4.width,
const Text(
"Clear",
style: TextStyle(fontSize: 14, color: Colors.white, height: 1),
)
],
).onPress(_selectedValue != null
? () {
Navigator.pop(context);
widget.onSelect(null);
}
: null),
),
],
),
body: Column(

@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:open_file/open_file.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
@ -84,21 +85,7 @@ class FilesList extends StatelessWidget {
recognizer: TapGestureRecognizer()
..onTap = () async {
if (isImage) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
body: SafeArea(
child: Stack(
children: [
// InteractiveViewer(child: Image(image: getImageObject(itemIndex))).center,
InteractiveViewer(child: getImageWidget(itemIndex)).center,
const ABackButton(),
],
),
),
),
),
);
context.fullScreenImageViewer(getImageWidget(itemIndex));
} else if (_isLocalUrl(images[itemIndex])) {
OpenFile.open(images[itemIndex]);
} else {
@ -162,21 +149,7 @@ class FilesList extends StatelessWidget {
),
).onPress(() async {
if (isImage) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
body: SafeArea(
child: Stack(
children: [
// InteractiveViewer(child: Image(image: getImageObject(itemIndex))).center,
InteractiveViewer(child: getImageWidget(itemIndex)).center,
const ABackButton(),
],
),
),
),
),
);
context.fullScreenImageViewer(getImageWidget(itemIndex));
} else if (_isLocalUrl(images[itemIndex])) {
OpenFile.open(images[itemIndex]);
} else {
@ -192,59 +165,6 @@ class FilesList extends StatelessWidget {
});
},
);
// return ListView.separated(
// padding: padding ?? EdgeInsets.symmetric(horizontal: 32 * AppStyle.getScaleFactor(context)),
// scrollDirection: Axis.horizontal,
// itemCount: images?.length ?? 0,
// separatorBuilder: (context, itemIndex) => 8.width,
// itemBuilder: (context, itemIndex) {
// if (images[itemIndex] == null || !images[itemIndex].contains(".")) {
// return Text("Bad\nFile");
// }
// var isImage =
// images[itemIndex].split(".").last.toLowerCase() == "png" || images[itemIndex].split(".").last.toLowerCase() == "jpg" || images[itemIndex].split(".").last.toLowerCase() == "jpeg";
// var isPdf = images[itemIndex].split(".").last.toLowerCase() == "pdf";
// var isExcel = images[itemIndex].split(".").last.toLowerCase() == "xlsx";
//
// return (isImage
// ? (_isLocalUrl(images[itemIndex])
// ? Image.file(File(images[itemIndex]))
// : ImageLoader(
// url: images[itemIndex],
// boxFit: BoxFit.cover,
// ))
// : Image.asset("assets/images/${isPdf ? "pdf" : isExcel ? "excel" : "doc"}.png"))
// .onPress(() async {
// if (isImage) {
// Navigator.of(context).push(
// MaterialPageRoute(
// builder: (_) => Scaffold(
// body: SafeArea(
// child: Stack(
// children: [
// InteractiveViewer(child: Image(image: (_isLocalUrl(images[itemIndex]) ? FileImage(File(images[itemIndex])) : NetworkImage(images[itemIndex])))).center,
// const ABackButton(),
// ],
// ),
// ),
// ),
// ),
// );
// } else if (_isLocalUrl(images[itemIndex])) {
// OpenFile.open(images[itemIndex]);
// } else {
// try {
// if (!await launchUrl(Uri.parse(images[itemIndex]), mode: LaunchMode.externalApplication)) {
// Fluttertoast.showToast(msg: "UnExpected Error with file.");
// throw Exception('Could not launch ');
// }
// } catch (ex) {
// Fluttertoast.showToast(msg: "No Viewer found.");
// }
// }
// });
// });
}
Widget getImageWidget(int itemIndex) {

@ -3,11 +3,13 @@ import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:image_blur_detection/image_blur_detection.dart';
import 'package:image_picker/image_picker.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_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';
import 'package:test_sa/models/generic_attachment_model.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
@ -15,280 +17,6 @@ import 'package:test_sa/new_views/app_style/app_color.dart';
import '../../../new_views/common_widgets/app_dashed_button.dart';
import 'multi_image_picker_item.dart';
// class MultiFilesPicker extends StatefulWidget {
// final String label;
// final bool error;
// final List<File> files;
// final List<GenericAttachmentModel> attachment;
//
// final bool enabled, onlyImages;
// double? buttonHeight;
// Widget? buttonIcon;
// Color? buttonColor;
// final Function(List<File>)? onChange;
// final bool showAsGrid;
//
// MultiFilesPicker(
// {Key? key,
// this.files = const <File>[],
// this.attachment = const <GenericAttachmentModel>[],
// required this.label,
// this.error = false,
// this.buttonHeight,
// this.buttonIcon,
// this.enabled = true,
// this.onlyImages = false,
// this.onChange,
// this.showAsGrid = false,
// this.buttonColor})
// : super(key: key);
//
// @override
// State<MultiFilesPicker> createState() => _MultiFilesPickerState();
// }
//
// class _MultiFilesPickerState extends State<MultiFilesPicker> {
// @override
// Widget build(BuildContext context) {
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// if (widget.enabled) ...[
// AppDashedButton(
// title: widget.label,
// height: widget.buttonHeight,
// buttonColor: widget.buttonColor,
// icon: widget.buttonIcon,
// onPressed: (widget.enabled == false)
// ? () {}
// : widget.showAsGrid
// ? showFileSourceSheet
// : onFilePicker),
// 16.height,
// ],
// if (widget.files.isNotEmpty)
// Wrap(
// spacing: 8.toScreenWidth,
// children: List.generate(
// widget.files!.length,
// (index) {
// File image = widget.files![index];
// return MultiFilesPickerItem(
// file: image,
// enabled: widget.enabled,
// onRemoveTap: (image) {
// if (!widget.enabled) {
// return;
// }
// widget.files.remove(image);
// if (widget.onChange != null) {
// widget.onChange!(widget.files);
// }
// setState(() {});
// },
// );
// },
// ),
// ),
// ],
// );
// }
//
// fromFilePicker() async {
// FilePickerResult? result = await FilePicker.platform.pickFiles(
// type: FileType.custom,
// allowMultiple: true,
// allowedExtensions: widget.onlyImages ? ['jpg', 'jpeg', 'png'] : ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xlsx', 'pptx'],
// );
// if (result != null) {
// for (var path in result.paths) {
// widget.files.add(File(path!));
// if (widget.onChange != null) {
// widget.onChange!(widget.files);
// }
// }
// setState(() {});
// }
// }
//
// void showFileSourceSheet() async {
// if (widget.files.length >= 5) {
// Fluttertoast.showToast(msg: context.translation.maxImagesNumberIs5);
// return;
// }
//
// ImageSource? source = (await showModalBottomSheet(
// context: context,
// shape: const RoundedRectangleBorder(
// borderRadius: BorderRadius.vertical(
// top: Radius.circular(20),
// ),
// ),
// clipBehavior: Clip.antiAliasWithSaveLayer,
// builder: (BuildContext context) => Column(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// "Attach File".heading4(context),
// 12.height,
// GridView(
// padding: const EdgeInsets.all(0),
// shrinkWrap: true,
// physics: const NeverScrollableScrollPhysics(),
// gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 1, crossAxisSpacing: 12, mainAxisSpacing: 12),
// children: <Widget>[
// gridItem(Icons.camera_enhance_rounded, context.translation.pickFromCamera).onPress(() => Navigator.of(context).pop(ImageSource.camera)),
// gridItem(Icons.image_rounded, context.translation.pickFromGallery).onPress(() => Navigator.of(context).pop(ImageSource.gallery)),
// gridItem(Icons.file_present_rounded, context.translation.pickFromFiles).onPress(() async {
// await fromFilePicker();
// Navigator.pop(context);
// }),
// ],
// ),
// 12.height,
// ],
// ).paddingAll(21),
// )) as ImageSource?;
// if (source == null) return;
// final pickedFile = await ImagePicker().pickImage(source: source, imageQuality: 70, maxWidth: 800, maxHeight: 800);
//
// if (pickedFile != null) {
// File fileImage = File(pickedFile.path);
// widget.files.add(fileImage);
// if (widget.onChange != null) {
// widget.onChange!(widget.files);
// }
// setState(() {});
// }
// }
//
// Widget gridItem(IconData iconData, String title) {
// return Container(
// padding: const EdgeInsets.all(12),
// decoration: BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(12),
// border: Border.all(color: const Color(0xffF1F1F1), width: 1),
// ),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Icon(iconData, color: const Color(0xff7D859A), size: 36),
// Text(
// title,
// style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
// ),
// ],
// ),
// );
// }
//
// onFilePicker() async {
// if (widget.files.length >= 5) {
// Fluttertoast.showToast(msg: context.translation.maxImagesNumberIs5);
// return;
// }
// ImageSource? source = await showModalBottomSheet<ImageSource>(
// context: context,
// builder: (BuildContext context) {
// Widget listCard({required String icon, required String label, required VoidCallback onTap}) {
// return GestureDetector(
// onTap: onTap,
// child: Container(
// constraints: BoxConstraints(minWidth: 111.toScreenWidth, minHeight: 111.toScreenHeight),
// padding: EdgeInsets.symmetric(horizontal: 12.toScreenWidth, vertical: 12.toScreenHeight),
// decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), border: Border.all(width: 1, color: AppColor.white70)),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// icon.toSvgAsset(),
// 24.height,
// label.bodyText2(context).custom(color: AppColor.black20),
// ],
// ),
// ),
// );
// }
//
// return Container(
// padding: const EdgeInsets.all(16.0),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: <Widget>[
// listCard(
// icon: 'camera_icon',
// label: '${context.translation.open}\n${context.translation.camera}',
// onTap: () {
// Navigator.of(context).pop(ImageSource.camera);
// },
// ),
// listCard(
// icon: 'gallery_icon',
// label: '${context.translation.open}\n${context.translation.gallery}',
// onTap: () {
// Navigator.of(context).pop(ImageSource.gallery);
// },
// ),
// listCard(
// icon: 'file_icon',
// label: '${context.translation.open}\n${context.translation.files}',
// onTap: () async {
// await fromFilePicker();
// Navigator.pop(context);
// },
// ),
// ],
// ),
// );
// },
// );
// // ImageSource source = await showDialog(
// // context: context,
// // builder: (dialogContext) => CupertinoAlertDialog(
// // actions: <Widget>[
// // TextButton(
// // child: Text(context.translation.pickFromCamera),
// // onPressed: () {
// // Navigator.of(dialogContext).pop(ImageSource.camera);
// // },
// // ),
// // TextButton(
// // child: Text(context.translation.pickFromGallery),
// // onPressed: () {
// // Navigator.of(dialogContext).pop(ImageSource.gallery);
// // },
// // ),
// // TextButton(
// // child: Text(context.translation.pickFromFiles),
// // onPressed: () async {
// // await fromFilePicker();
// // Navigator.pop(context);
// // },
// // ),
// // ],
// // ),
// // );
// if (source == null) return;
//
// final pickedFile = await ImagePicker().pickImage(source: source, imageQuality: 70, maxWidth: 800, maxHeight: 800);
//
// if (pickedFile != null) {
// File fileImage = File(pickedFile.path);
// if (fileImage != null) {
// widget.files.add(fileImage);
// if (widget.onChange != null) {
// widget.onChange!(widget.files);
// }
// setState(() {});
// }
// }
//
// setState(() {});
// }
// }
class AttachmentModel {
int id = 0;
File? file;
@ -311,6 +39,7 @@ class AttachmentModel {
}
class AttachmentPicker extends StatefulWidget {
final String pickerTitle;
final String label;
final bool error;
final List<GenericAttachmentModel> attachment;
@ -320,14 +49,17 @@ class AttachmentPicker extends StatefulWidget {
Widget? buttonIcon;
Color? buttonColor;
final Function(List<GenericAttachmentModel>)? onChange;
final bool showAsGrid;
final bool showAsListView;
final bool pickMultiple;
Lookup? documentType;
Widget? child;
bool checkQuality;
AttachmentPicker(
{Key? key,
this.attachment = const <GenericAttachmentModel>[],
required this.label,
this.pickerTitle = "Attach File",
this.error = false,
this.buttonHeight,
this.buttonIcon,
@ -335,8 +67,10 @@ class AttachmentPicker extends StatefulWidget {
this.onlyImages = false,
this.onChange,
this.documentType,
this.showAsGrid = false,
this.showAsListView = false,
this.pickMultiple = false,
this.checkQuality = false,
this.child,
this.buttonColor})
: super(key: key);
@ -344,75 +78,91 @@ class AttachmentPicker extends StatefulWidget {
State<AttachmentPicker> createState() => _AttachmentPickerState();
}
class _AttachmentPickerState extends State<AttachmentPicker> {
class _AttachmentPickerState extends State<AttachmentPicker> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
_animationController.forward();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AppDashedButton(
title: widget.label,
height: widget.buttonHeight,
buttonColor: widget.buttonColor,
icon: widget.buttonIcon,
onPressed: (widget.enabled == false)
? () {}
: widget.showAsGrid
? showFileSourceSheet
: onFilePicker),
16.height,
if (widget.attachment.isNotEmpty)
widget.showAsListView
? ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemBuilder: (cxt, index) {
File image = File(widget.attachment[index].name!);
return MultiFilesPickerItem(
file: image,
showAsListView: widget.showAsListView,
enabled: widget.enabled,
documentType: widget.attachment[index].documentTypeId?.name,
onRemoveTap: (image) {
if (!widget.enabled) {
return;
}
widget.attachment.removeAt(index);
if (widget.onChange != null) {
widget.onChange!(widget.attachment);
}
setState(() {});
},
);
},
separatorBuilder: (cxt, index) => 8.height,
itemCount: widget.attachment.length)
: Wrap(
spacing: 8.toScreenWidth,
children: List.generate(
widget.attachment.length,
(index) {
File image = File(widget.attachment[index].name!);
return MultiFilesPickerItem(
file: image,
enabled: widget.enabled,
onRemoveTap: (image) {
if (!widget.enabled) {
return;
}
widget.attachment.removeAt(index);
if (widget.onChange != null) {
widget.onChange!(widget.attachment);
}
setState(() {});
return widget.child != null
? widget.child!.onPress((widget.enabled == false) ? () {} : () => onFilePicker(widget.checkQuality))
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 12,
children: [
AppDashedButton(
title: widget.label,
height: widget.buttonHeight,
buttonColor: widget.buttonColor,
icon: widget.buttonIcon,
onPressed: (widget.enabled == false) ? () {} : () => onFilePicker(widget.checkQuality)),
if (widget.attachment.isNotEmpty)
widget.showAsListView
? ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.only(top: 0),
itemBuilder: (cxt, index) {
File image = File(widget.attachment[index].name!);
return MultiFilesPickerItem(
file: image,
showAsListView: widget.showAsListView,
enabled: widget.enabled,
documentType: widget.attachment[index].documentTypeId?.name,
onRemoveTap: (image) {
if (!widget.enabled) {
return;
}
widget.attachment.removeAt(index);
if (widget.onChange != null) {
widget.onChange!(widget.attachment);
}
setState(() {});
},
);
},
);
},
),
),
],
);
separatorBuilder: (cxt, index) => 8.height,
itemCount: widget.attachment.length)
: Wrap(
spacing: 8.toScreenWidth,
children: List.generate(
widget.attachment.length,
(index) {
File image = File(widget.attachment[index].name!);
return MultiFilesPickerItem(
file: image,
enabled: widget.enabled,
onRemoveTap: (image) {
if (!widget.enabled) {
return;
}
widget.attachment.removeAt(index);
if (widget.onChange != null) {
widget.onChange!(widget.attachment);
}
setState(() {});
},
);
},
),
).paddingOnly(top: 0),
],
);
}
fromFilePicker() async {
@ -432,52 +182,6 @@ class _AttachmentPickerState extends State<AttachmentPicker> {
}
}
void showFileSourceSheet() async {
ImageSource source = (await showModalBottomSheet(
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
clipBehavior: Clip.antiAliasWithSaveLayer,
builder: (BuildContext context) => Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Attach File".heading4(context),
12.height,
GridView(
padding: const EdgeInsets.all(0),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 1, crossAxisSpacing: 12, mainAxisSpacing: 12),
children: <Widget>[
gridItem(Icons.camera_enhance_rounded, context.translation.pickFromCamera).onPress(() => Navigator.of(context).pop(ImageSource.camera)),
gridItem(Icons.image_rounded, context.translation.pickFromGallery).onPress(() => Navigator.of(context).pop(ImageSource.gallery)),
gridItem(Icons.file_present_rounded, context.translation.pickFromFiles).onPress(() async {
await fromFilePicker();
Navigator.pop(context);
}),
],
),
12.height,
],
).paddingAll(21),
)) as ImageSource;
final pickedFile = await ImagePicker().pickImage(source: source, imageQuality: 70, maxWidth: 800, maxHeight: 800);
if (pickedFile != null) {
File fileImage = File(pickedFile.path);
widget.attachment.add(GenericAttachmentModel(id: 0, name: fileImage.path, documentTypeId: widget.documentType));
if (widget.onChange != null) {
widget.onChange!(widget.attachment);
}
setState(() {});
}
}
Widget gridItem(IconData iconData, String title) {
return Container(
padding: const EdgeInsets.all(12),
@ -490,7 +194,7 @@ class _AttachmentPickerState extends State<AttachmentPicker> {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(iconData, color: AppColor.icon2Color(context), size: 24),
Icon(iconData, color: AppColor.icon2Color(context), size: 24),
title.bodyText2(context).custom(color: AppColor.labelTextStyleColor(context), fontSize: context.isTablet() ? 10.toScreenWidth : null),
// Text(
// title,
@ -501,7 +205,11 @@ class _AttachmentPickerState extends State<AttachmentPicker> {
);
}
onFilePicker() async {
onFilePicker(bool checkQuality) async {
// Reset and restart animation for bottom sheet
_animationController.reset();
_animationController.forward();
ImageSource? source = await showModalBottomSheet<ImageSource>(
context: context,
builder: (BuildContext context) {
@ -517,122 +225,129 @@ class _AttachmentPickerState extends State<AttachmentPicker> {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
icon.toSvgAsset(color: AppColor.icon2Color(context), width: 36, height: 36),
icon.toSvgAsset(width: 24, height: 24, color: AppColor.icon2Color(context)),
label.bodyText2(context).custom(color: AppColor.labelTextStyleColor(context), fontSize: context.isTablet() ? 10.toScreenWidth : null),
],
),
).onPress(onTap);
}
List<Widget> listItems = [
listCard(
icon: 'camera_icon',
label: '${context.translation.open}\n${context.translation.camera}',
onTap: () {
Navigator.of(context).pop(ImageSource.camera);
},
),
listCard(
icon: 'gallery_icon',
label: '${context.translation.open}\n${context.translation.gallery}',
onTap: () {
Navigator.of(context).pop(ImageSource.gallery);
},
),
listCard(
icon: 'file_icon',
label: '${context.translation.open}\n${context.translation.files}',
onTap: () async {
await fromFilePicker();
Navigator.pop(context);
},
),
];
return SafeArea(
top: false,
child: Container(
width: double.infinity,
color: AppColor.background(context),
color: AppColor.bottomSheetColor(context),
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Attach File".heading4(context),
12.height,
GridView(
padding: const EdgeInsets.all(0),
widget.pickerTitle.heading4(context),
GridView.builder(
padding: const EdgeInsets.only(top: 16, bottom: 0),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 1, crossAxisSpacing: 12, mainAxisSpacing: 12),
children: <Widget>[
listCard(
icon: 'camera_icon',
label: '${context.translation.open}\n${context.translation.camera}',
onTap: () {
Navigator.of(context).pop(ImageSource.camera);
},
),
listCard(
icon: 'gallery_icon',
label: '${context.translation.open}\n${context.translation.gallery}',
onTap: () {
Navigator.of(context).pop(ImageSource.gallery);
},
),
listCard(
icon: 'file_icon',
label: '${context.translation.open}\n${context.translation.files}',
onTap: () async {
await fromFilePicker();
Navigator.pop(context);
},
),
],
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 1, crossAxisSpacing: 16, mainAxisSpacing: 16),
itemCount: listItems.length,
itemBuilder: (context, index) {
// Calculate staggered animation delay
final delay = index * 0.08;
final animation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _animationController,
curve: Interval(
delay.clamp(0.0, 0.8),
(delay + 0.3).clamp(0.0, 1.0),
curve: Curves.easeOut,
),
),
);
final slideAnimation = Tween<Offset>(
begin: const Offset(0.0, 0.3),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _animationController,
curve: Interval(
delay.clamp(0.0, 0.8),
(delay + 0.3).clamp(0.0, 1.0),
curve: Curves.easeOutCubic,
),
),
);
return FadeTransition(
opacity: animation,
child: SlideTransition(position: slideAnimation, child: listItems[index]),
);
},
),
// Container(
// padding: const EdgeInsets.all(16.0),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: <Widget>[
// listCard(
// icon: 'camera_icon',
// label: '${context.translation.open}\n${context.translation.camera}',
// onTap: () {
// Navigator.of(context).pop(ImageSource.camera);
// },
// ),
// listCard(
// icon: 'gallery_icon',
// label: '${context.translation.open}\n${context.translation.gallery}',
// onTap: () {
// Navigator.of(context).pop(ImageSource.gallery);
// },
// ),
// listCard(
// icon: 'file_icon',
// label: '${context.translation.open}\n${context.translation.files}',
// onTap: () async {
// await fromFilePicker();
// Navigator.pop(context);
// },
// ),
// ],
// ),
// ),
],
).paddingAll(16),
),
),
);
},
);
// ImageSource source = await showDialog(
// context: context,
// builder: (dialogContext) => CupertinoAlertDialog(
// actions: <Widget>[
// TextButton(
// child: Text(context.translation.pickFromCamera),
// onPressed: () {
// Navigator.of(dialogContext).pop(ImageSource.camera);
// },
// ),
// TextButton(
// child: Text(context.translation.pickFromGallery),
// onPressed: () {
// Navigator.of(dialogContext).pop(ImageSource.gallery);
// },
// ),
// TextButton(
// child: Text(context.translation.pickFromFiles),
// onPressed: () async {
// await fromFilePicker();
// Navigator.pop(context);
// },
// ),
// ],
// ),
// );
if (source == null) return;
final pickedFile = await ImagePicker().pickImage(source: source, imageQuality: 70, maxWidth: 800, maxHeight: 800);
if (widget.pickMultiple && source == ImageSource.gallery) {
final pickedFiles = await ImagePicker().pickMultiImage(imageQuality: 70);
if (pickedFiles.isNotEmpty) {
pickedFiles.forEach((pickedFile) {
File fileImage = File(pickedFile.path);
widget.attachment.add(GenericAttachmentModel(id: 0, name: fileImage.path, documentTypeId: widget.documentType));
});
if (widget.onChange != null) {
widget.onChange!(widget.attachment);
}
setState(() {});
}
return;
}
final pickedFile = await ImagePicker().pickImage(source: source, imageQuality: 50);
if (pickedFile != null) {
File fileImage = File(pickedFile.path);
if (checkQuality) {
ImageQualityValidator imageQualityValidator = ImageQualityValidator();
final result = await imageQualityValidator.validate(await fileImage.readAsBytes());
if (!result.isValid) {
String issues = "Your image have following problems:";
result.issues.forEach((issue) => (issues += "\n- $issue"));
context.showInfoDialog(issues, title: "Bad Quality Found");
return;
}
}
widget.attachment.add(GenericAttachmentModel(id: 0, name: fileImage.path, documentTypeId: widget.documentType));
if (widget.onChange != null) {
widget.onChange!(widget.attachment);
@ -640,6 +355,6 @@ class _AttachmentPickerState extends State<AttachmentPicker> {
setState(() {});
}
setState(() {});
// setState(() {});
}
}

@ -70,15 +70,20 @@ class MultiFilesPickerItem extends StatelessWidget {
),
8.width,
if (enabled)
InkWell(
child: "remove".toSvgAsset(width: 16,color: AppColor.red30),
onTap: () {
IconButton(
padding: EdgeInsets.zero,
icon: const Icon(Icons.clear),
iconSize: 18,
style: IconButton.styleFrom(elevation: 2, tapTargetSize: MaterialTapTargetSize.shrinkWrap, backgroundColor: AppColor.red30, foregroundColor: Colors.white),
visualDensity: VisualDensity.compact,
constraints: const BoxConstraints(),
onPressed: () {
onRemoveTap(file);
if (onRemove != null) {
onRemove!();
}
},
)
),
],
),
4.height,
@ -92,23 +97,12 @@ class MultiFilesPickerItem extends StatelessWidget {
recognizer: TapGestureRecognizer()
..onTap = () async {
if (isImage) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
appBar: const DefaultAppBar(),
body: SafeArea(
child: InteractiveViewer(
child: _isLocalUrl(file.path)
? Image.file(file)
: ImageLoader(
url: URLs.getFileUrl(file.path),
boxFit: BoxFit.cover,
),
).center,
),
),
),
);
context.fullScreenImageViewer(_isLocalUrl(file.path)
? Image.file(file)
: ImageLoader(
url: URLs.getFileUrl(file.path),
boxFit: BoxFit.cover,
));
} else if (_isLocalUrl(file.path)) {
OpenFile.open(file.path);
} else {
@ -150,23 +144,12 @@ class MultiFilesPickerItem extends StatelessWidget {
padding: EdgeInsets.zero,
onPressed: () async {
if (isImage) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
appBar: const DefaultAppBar(),
body: SafeArea(
child: InteractiveViewer(
child: _isLocalUrl(file.path)
? Image.file(file)
: ImageLoader(
url: URLs.getFileUrl(file.path),
boxFit: BoxFit.cover,
),
).center,
),
),
),
);
context.fullScreenImageViewer(_isLocalUrl(file.path)
? Image.file(file)
: ImageLoader(
url: URLs.getFileUrl(file.path),
boxFit: BoxFit.cover,
));
} else if (_isLocalUrl(file.path)) {
OpenFile.open(file.path);
} else {
@ -179,9 +162,14 @@ class MultiFilesPickerItem extends StatelessWidget {
),
),
if (enabled)
InkWell(
child: "remove".toSvgAsset(width: 16),
onTap: () {
IconButton(
padding: EdgeInsets.zero,
icon: const Icon(Icons.clear),
iconSize: 18,
style: IconButton.styleFrom(elevation: 2, tapTargetSize: MaterialTapTargetSize.shrinkWrap, backgroundColor: AppColor.red30, foregroundColor: Colors.white),
visualDensity: VisualDensity.compact,
constraints: const BoxConstraints(),
onPressed: () {
onRemoveTap(file);
if (onRemove != null) {
onRemove!();

@ -1,4 +1,9 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.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/new_views/app_style/app_color.dart';
@ -8,9 +13,10 @@ class InfoTextWidget extends StatelessWidget {
final String label;
final bool showEmptyValue;
final bool showArrow;
final bool copyValue;
final bool isMsg;
InfoTextWidget({required this.label, this.value, this.showEmptyValue = false, this.showArrow = false, this.isMsg = false, Key? key}) : super(key: key);
InfoTextWidget({required this.label, this.value, this.showEmptyValue = false, this.copyValue = false, this.showArrow = false, this.isMsg = false, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -33,19 +39,38 @@ class InfoTextWidget extends StatelessWidget {
"arrow_right".toSvgAsset(width: 17, color: AppColor.primary10),
],
);
} else if (copyValue && (value?.isNotEmpty ?? false)) {
text = Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Flexible(child: text),
8.width,
const Icon(Icons.copy_rounded, color: AppColor.primary10, size: 14).onPress(() {
Clipboard.setData(ClipboardData(text: value!)).then((value) {
if (Platform.isIOS) {
"Copied.".addTranslation.showToast;
}
});
}),
],
);
}
return text;
}
}
class InfoTextLabelWidget extends StatelessWidget {
final String label;
final Color? color;
final TextStyle? textStyle;
InfoTextLabelWidget({required this.label, Key? key}) : super(key: key);
const InfoTextLabelWidget({required this.label, this.color, this.textStyle, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
Widget text;
text = Text(label, style: AppTextStyles.bodyText2.copyWith(color: AppColor.lightTextColor(context)));
text = Text(label, style: (textStyle ?? AppTextStyles.bodyText2).copyWith(color: color ?? AppColor.lightTextColor(context)));
return text;
}
}
}

@ -155,10 +155,16 @@ class _ScannerOverlay extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.all(16.0),
child: FloatingActionButton(
heroTag: "some tag 3",
heroTag: "qr-scan",
backgroundColor: AppColor.background(context),
onPressed: onBack,
child: Icon(Icons.arrow_back, size: 24.toScreenWidth, color: AppColor.icon2Color(context)),
hoverColor: Colors.transparent,
splashColor: Colors.transparent,
shape: RoundedRectangleBorder(
side: BorderSide(color: AppColor.borderColor(context)),
borderRadius: BorderRadius.circular(14.0),
),
child: Icon(Platform.isIOS ? Icons.arrow_back_ios : Icons.arrow_back, size: 24, color: AppColor.icon2Color(context)),
),
),
),

@ -758,6 +758,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.8.0"
image_blur_detection:
dependency: "direct main"
description:
name: image_blur_detection
sha256: "0d1043422b90b97dea2aa282aa5cbb206e34bdc43acc0cb312b31f160f977333"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
image_cropper:
dependency: "direct main"
description:

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.7.8+44
version: 1.8.1+47
environment:
sdk: ">=3.5.0 <4.0.0"
@ -88,6 +88,7 @@ dependencies:
flutter_timezone: ^3.0.1
device_calendar: ^4.3.3
pie_chart: ^5.4.0
image_blur_detection: ^1.0.1
badges: ^3.1.1
# buttons_tabbar: ^1.1.2
@ -139,6 +140,7 @@ flutter:
- assets/images/
- assets/images/dashboard/
- assets/images/wo/
- assets/images/asset_image_upload/
- assets/lottie/
- assets/subtitles/
- assets/rives/

Loading…
Cancel
Save