diff --git a/lib/views/widgets/equipment/asset_detail_page.dart b/lib/views/widgets/equipment/asset_detail_page.dart index 14d09949..d81f8eff 100644 --- a/lib/views/widgets/equipment/asset_detail_page.dart +++ b/lib/views/widgets/equipment/asset_detail_page.dart @@ -39,6 +39,7 @@ class _AssetDetailPageState extends State with SingleTickerProv late AnimationController _animationController; int? assetId; AssetProvider? _assetProvider; + AssetByIdModel? assetModel; @override void initState() { @@ -55,10 +56,6 @@ class _AssetDetailPageState extends State with SingleTickerProv super.dispose(); } - int? assetId; - AssetProvider? _assetProvider; - AssetByIdModel? assetModel; - List attachments = []; @override @@ -85,12 +82,12 @@ class _AssetDetailPageState extends State with SingleTickerProv if (snapshot.hasData) { AssetByIdModel assetModel = snapshot.data!; attachments = assetModel?.assetAttachments - ?.map((attachment) => GenericAttachmentModel( - id: attachment.id, - name: attachment.attachmentName, - originalName: attachment.originalName, - )) - .toList() ?? + ?.map((attachment) => GenericAttachmentModel( + id: attachment.id, + name: attachment.attachmentName, + originalName: attachment.originalName, + )) + .toList() ?? []; // Start animation when data is loaded _animationController.forward(from: 0.0); @@ -200,160 +197,199 @@ class _AssetDetailPageState extends State 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(begin: 0.0, end: 1.0).animate( + CurvedAnimation( + parent: _animationController, + curve: const Interval(0.80, 1.0, curve: Curves.easeOut), + ), + ); + + final attachmentSlideAnimation = Tween( + 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, - - if (context.userProvider.isEngineer && !(assetModel!.hasTrainingImageData ?? true)) ...[ - Text( - '*Upload Images for this asset', - style: AppTextStyles.bodyText2.copyWith(color: AppColor.red30), - ), - 4.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)) + Text( + '*Upload Images for this asset', + style: AppTextStyles.bodyText2.copyWith(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, 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), + 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, + 8.width, + 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, + 8.height, - // Divider with animation - FadeTransition( - opacity: datesAnimation, - child: const Divider(color: AppColor.neutral30, height: 1, thickness: 1), - ), - 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), - ], + // 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), + ], + ), + ), ), - ), - ] + + // 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), + ), + ), + ], + + 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()), + ), + ), + ] + ], + ), ], + ).toShadowContainer(context), + ).expanded, + if (context.userProvider.isEngineer && !(assetModel.hasTrainingImageData ?? true)) + FooterActionButton.footerContainer( + context: context, + child: AppFilledButton( + buttonColor: AppColor.primary10, + label: "Upload Images", + onPressed: _upload, + ), ), - FilesList(images: attachments.map((toElement) => URLs.getFileUrl(toElement.name!) ?? '').toList()), - ], - ).toShadowContainer(context), - 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());