import 'dart:convert' show base64Decode; import 'package:flutter/material.dart'; import 'package:fotodocumentation/controller/base_controller.dart'; import 'package:fotodocumentation/controller/customer_controller.dart'; import 'package:fotodocumentation/controller/picture_controller.dart'; import 'package:fotodocumentation/dto/customer_dto.dart'; import 'package:fotodocumentation/dto/picture_dto.dart'; import 'package:fotodocumentation/l10n/app_localizations.dart'; import 'package:fotodocumentation/pages/ui_utils/component/customer_back_button.dart'; import 'package:fotodocumentation/pages/customer/picture_delete_dialog.dart'; import 'package:fotodocumentation/pages/ui_utils/component/general_error_widget.dart'; import 'package:fotodocumentation/pages/ui_utils/component/page_header_widget.dart'; import 'package:fotodocumentation/pages/ui_utils/component/waiting_widget.dart'; import 'package:fotodocumentation/pages/ui_utils/general_style.dart'; import 'package:fotodocumentation/utils/di_container.dart'; import 'package:fotodocumentation/utils/global_router.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; class CustomerWidget extends StatefulWidget { final int customerId; const CustomerWidget({super.key, required this.customerId}); @override State createState() => _CustomerWidgetState(); } class _CustomerWidgetState extends State { CustomerController get _customerController => DiContainer.get(); PictureController get _pictureController => DiContainer.get(); GeneralStyle get _generalStyle => DiContainer.get(); late Future _dto; late DateFormat _dateFormat; @override void initState() { super.initState(); _dateFormat = DateFormat('dd MMMM yyyy'); _dto = _customerController.get(id: widget.customerId); } @override Widget build(BuildContext context) { return Scaffold( body: Container( color: _generalStyle.pageBackgroundColor, child: Padding( padding: const EdgeInsets.only(top: 8.0, left: 50.0, right: 50.0, bottom: 8.0), child: _body(context), ), ), ); } Widget _body(BuildContext context) { return FutureBuilder( future: _dto, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState != ConnectionState.done) { return const WaitingWidget(); } if (snapshot.hasData) { CustomerDto? dto = snapshot.data; return _mainWidget(dto); } else if (snapshot.hasError) { var error = snapshot.error; return (error is ServerError) ? GeneralErrorWidget.fromServerError(error) : GeneralErrorWidget(error: snapshot.error.toString()); } return const WaitingWidget(); }, ); } Widget _mainWidget(CustomerDto? dto) { if (dto == null) { return Text( AppLocalizations.of(context)!.customerWidgetNotFound, style: TextStyle( fontFamily: _generalStyle.fontFamily, fontWeight: FontWeight.bold, fontSize: 20, color: _generalStyle.secondaryWidgetBackgroundColor, ), ); } var subText = AppLocalizations.of(context)!.customerWidgetCustomerNumberPrefix(dto.customerNumber); return Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ PageHeaderWidget(text: dto.name, subText: subText), CustomerBackButton(path: GlobalRouter.pathHome), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ _downloadButton(context), ], ), const SizedBox(height: 24), Expanded( child: _customerWidget(dto), ), ], ); } Widget _customerWidget(CustomerDto customerDto) { var pictureDtos = customerDto.pictures; pictureDtos.sort((a, b) => b.pictureDate.compareTo(a.pictureDate)); return Card( margin: EdgeInsets.zero, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Column( children: [ _tableHeaderRow(context), Divider(thickness: 2, height: 1, color: _generalStyle.secondaryWidgetBackgroundColor), Expanded( child: ListView.separated( padding: const EdgeInsets.only(top: 8.0), itemCount: pictureDtos.length, itemBuilder: (BuildContext context, int index) { return _tableDataRow(context, customerDto, pictureDtos[index]); }, separatorBuilder: (BuildContext context, int index) => Divider(color: _generalStyle.secondaryWidgetBackgroundColor), ), ), ], ), ); } Widget _tableHeaderRow(BuildContext context) { final headerStyle = TextStyle( fontFamily: _generalStyle.fontFamily, fontWeight: FontWeight.bold, fontSize: 20, color: _generalStyle.secondaryWidgetBackgroundColor, ); return Padding( padding: const EdgeInsets.all(16.0), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 1, child: Align( alignment: Alignment.centerLeft, child: Text( AppLocalizations.of(context)!.customerWidgetHeaderFoto, style: headerStyle, ), ), ), Expanded( flex: 3, child: Align( alignment: Alignment.centerLeft, child: Text( AppLocalizations.of(context)!.customerWidgetHeaderComment, style: headerStyle, ), ), ), Expanded( flex: 1, child: Align( alignment: Alignment.centerLeft, child: Text( 'Bewertung', style: headerStyle, ), ), ), Expanded( flex: 2, child: Align( alignment: Alignment.centerLeft, child: Text( AppLocalizations.of(context)!.customerWidgetHeaderUploadDate, style: headerStyle, ), ), ), const SizedBox(width: 96), ], ), ); } Widget _tableDataRow(BuildContext context, CustomerDto customerDto, PictureDto pictureDto) { final dataStyle = TextStyle( fontFamily: _generalStyle.fontFamily, fontSize: 16.0, color: _generalStyle.secondaryTextLabelColor, ); final dateStr = _dateFormat.format(pictureDto.pictureDate); final evaluationColor = _generalStyle.evaluationColor(value: pictureDto.evaluation); return InkWell( key: Key("table_row_${customerDto.id}"), onTap: () => _actionSelect(context, customerDto, pictureDto), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( flex: 1, child: Align( alignment: Alignment.centerLeft, child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 70, maxHeight: 70), child: Image.memory( base64Decode(pictureDto.image), fit: BoxFit.contain, ), ), ), ), Expanded( flex: 3, child: Align( alignment: Alignment.centerLeft, child: Text(pictureDto.comment ?? "", style: dataStyle), ), ), Expanded( flex: 1, child: Align( alignment: Alignment.centerLeft, child: Container( width: 20, height: 20, decoration: BoxDecoration( color: evaluationColor, shape: BoxShape.circle, ), ), ), ), Expanded( flex: 2, child: Align( alignment: Alignment.centerLeft, child: Text(dateStr, style: dataStyle), ), ), SizedBox( width: 48, child: IconButton( key: Key("table_row_download_${pictureDto.id}"), icon: Icon( Icons.file_download_outlined, color: _generalStyle.loginFormTextLabelColor, ), onPressed: () => _actionDelete(context, customerDto, pictureDto), ), ), SizedBox( width: 48, child: IconButton( key: Key("table_row_delete_${customerDto.id}"), icon: Icon( Icons.delete_outline, color: _generalStyle.errorColor, ), onPressed: () => _actionDelete(context, customerDto, pictureDto), ), ), ], ), ), ); } Widget _downloadButton(BuildContext context) { return ElevatedButton.icon( key: Key("download_all_button"), onPressed: () => _actionDownload(context), iconAlignment: IconAlignment.end, icon: Icon( Icons.file_download_outlined, color: _generalStyle.primaryButtonTextColor, size: 24, ), label: Text( "Alle herunterladen", style: TextStyle( fontFamily: _generalStyle.fontFamily, fontWeight: FontWeight.bold, fontSize: 16, color: _generalStyle.primaryButtonTextColor, ), ), style: ElevatedButton.styleFrom( backgroundColor: _generalStyle.secondaryWidgetBackgroundColor, elevation: 0, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), shape: const StadiumBorder(), ), ); } Future _actionDelete(BuildContext context, CustomerDto customerDto, PictureDto pictureDto) async { final confirmed = await showDialog( context: context, builder: (BuildContext context) { return PictureDeleteDialog(); }, ); if (confirmed == true) { _pictureController.delete(pictureDto); setState(() { _dto = _customerController.get(id: widget.customerId); }); } } Future _actionSelect(BuildContext context, CustomerDto customerDto, PictureDto pictureDto) async { String uri = "${GlobalRouter.pathHome}${GlobalRouter.pathPicture}/${customerDto.id}/${pictureDto.id}"; context.go(uri); setState(() { _dto = _customerController.get(id: widget.customerId); }); } Future _actionDownload(BuildContext context) async { // FIXME: implement a download from the export } }