Aidra Driver 1.3.5+68
Aidra Driver - Your path to green energy
Loading...
Searching...
No Matches
unloading_process_view.dart
Go to the documentation of this file.
1// import 'dart:async';
2
3import 'package:easy_localization/easy_localization.dart';
4import 'package:flutter/material.dart';
5import 'package:flutter_bloc/flutter_bloc.dart';
6import 'package:flutter_screenutil/flutter_screenutil.dart';
7
8import '../../../../../core/services/check_in_service.dart';
9import '../../../../../core/ui/theme/color_palette.dart';
10import '../../../../../core/ui/widgets/custom_scaffold.dart';
11import '../../../../../core/ui/widgets/custom_slider.dart';
12import '../../../../../core/ui/widgets/custom_snackbar.dart';
13import '../../../../../core/ui/widgets/empty_content.dart';
14import '../../../../../core/ui/widgets/error_widget.dart';
15import '../../../../../core/ui/widgets/loading.dart';
16import '../../../../../core/ui/widgets/stats_card.dart';
17import '../../../../auth/presentation/bloc/authentication_bloc/authentication_bloc.dart';
18import '../../../../auth/presentation/bloc/check_in_out_bloc/check_in_out_bloc.dart';
19import '../../../data/models/collection_voucher_model.dart';
20import '../../../domain/entities/unloading_collection_entity.dart';
21import '../../bloc/unloading_bloc.dart';
22import '../../bloc/unloading_event.dart';
23import '../../bloc/unloading_state.dart';
24import '../../widgets/unloading_confirmation_dialog.dart';
25import '../widgets/unloading_collection_item.dart';
26
27class UnloadingProcessView extends StatefulWidget {
28 const UnloadingProcessView({super.key});
29
30 @override
31 State<UnloadingProcessView> createState() => _UnloadingProcessViewState();
32}
33
34class _UnloadingProcessViewState extends State<UnloadingProcessView> {
35 final Map<int, bool> _selectedItems = {};
36 bool _selectAll = false;
37 final List<UnloadingCollectionEntity> _selectedCollections = [];
39
40 @override
41 void initState() {
42 super.initState();
44 }
45
46 void _updateSelectAllState(List<UnloadingCollectionEntity> collections) {
47 if (collections.isEmpty) {
48 setState(() => _selectAll = false);
49 return;
50 }
51
52 setState(() {
54 _selectedItems[collection.id ?? 0] ?? false);
55 });
56 }
57
59 setState(() {
60 _selectedItems[collection.id ?? 0] = selected ?? false;
61
62 if (selected ?? false) {
63 if (!_selectedCollections.any((item) => item.id == collection.id)) {
65 }
66 } else {
67 _selectedCollections.removeWhere((item) => item.id == collection.id);
68 }
69
70 final state = context.read<UnloadingBloc>().state;
71 if (state is PendingUnloadingCollectionsLoaded) {
72 _updateSelectAllState(state.collections);
73 }
74 });
75 }
76
77 void _handleSelectAll(bool? value, List<UnloadingCollectionEntity> collections) {
78 setState(() {
79 _selectAll = value ?? false;
81
82 for (var collection in collections) {
84 if (_selectAll) {
86 }
87 }
88 });
89 }
90
91 @override
92 Widget build(BuildContext context) {
93 return CustomScaffold(
94 backgroundColor: ColorPalette.antiFlashWhite,
95 body: MultiBlocListener(
96 listeners: [
97 BlocListener<UnloadingBloc, UnloadingState>(
98 listenWhen: (previous, current) => current is SendUnloadingSuccess || current is UnloadingError,
100 ),
101 BlocListener<CheckInOutBloc, CheckInOutState>(
102 listenWhen: (previous, current) => current is SendVehicleCheckSuccessState || current is SendVehicleCheckFailureState,
103 listener: (context, state) {
104 if (state is SendVehicleCheckFailureState) {
106 context,
108 "Checkout failed: ${state.failure.message}"
109 );
110 }
111 },
112 ),
113 ],
114 child: BlocBuilder<UnloadingBloc, UnloadingState>(
115 buildWhen: (previous, current) =>
116 current is UnloadingLoading ||
117 current is SendUnloadingLoading ||
118 current is UnloadingError ||
119 current is PendingUnloadingCollectionsLoaded,
120 builder: (context, state) {
121 if (state is UnloadingLoading || state is SendUnloadingLoading || _isProcessingUnloading) {
122 return const Loading();
123 } else if (state is UnloadingError) {
124 return CustomErrorWidget(
125 message: "unloading.connection_failed".tr(),
126 onReload: _loadPendingUnloading,
127 );
128 } else if (state is PendingUnloadingCollectionsLoaded) {
129 return _buildCollectionsContent(state.collections);
130 } else {
131 return Center(child: Text('unloading.no_data'.tr()));
132 }
133 },
134 ),
135 ),
136 );
137 }
138
139 void _handleBlocStateChanges(BuildContext context, UnloadingState state) {
140 if (state is SendUnloadingSuccess) {
142 context,
144 "unloading.unloading_success".tr(),
145 );
147 _selectedCollections.clear();
148 _selectedItems.clear();
149 _selectAll = false;
150
151 // Perform checkout after successful unloading
153
154 } else if (state is UnloadingError) {
155 setState(() {
157 });
159 context,
161 state.message
162 );
163 }
164 }
165
167 final authState = context.read<AuthenticationBloc>().state;
168 if (authState is AuthenticatedState) {
169 setState(() {
171 });
172 showDialog(
173 context: context,
174 barrierDismissible: false,
175 builder: (BuildContext dialogContext) {
176 return AlertDialog(
177 shape: RoundedRectangleBorder(
178 borderRadius: BorderRadius.circular(16),
179 ),
180 title: Row(
181 children: [
182 Icon(Icons.logout, color: ColorPalette.darkGreen, size: 24.sp),
183 SizedBox(width: 10.sp),
184 Text(
185 'unloading.checkout_required'.tr(),
186 style: TextStyle(
187 fontSize: 18.sp,
188 fontWeight: FontWeight.bold,
190 ),
191 ),
192 ],
193 ),
194 content: Column(
195 mainAxisSize: MainAxisSize.min,
196 crossAxisAlignment: CrossAxisAlignment.start,
197 children: [
198 Text(
199 'unloading.checkout_completed'.tr(),
200 style: TextStyle(fontSize: 16.sp),
201 ),
202 SizedBox(height: 10.sp),
203 Text(
204 'unloading.checkout_instruction'.tr(),
205 style: TextStyle(
206 fontSize: 16.sp,
207 fontWeight: FontWeight.w500,
209 ),
210 ),
211 ],
212 ),
213 actions: [
214 Container(
215 width: double.infinity,
216 padding: EdgeInsets.symmetric(horizontal: 16.sp),
217 child: ElevatedButton(
218 onPressed: () {
219 final checkInService = CheckInService();
220 checkInService.clearCheckInStatus().then((_) {
221 if(dialogContext.mounted) Navigator.of(dialogContext).pop();
222 if(mounted) {
223 context.read<AuthenticationBloc>().add(SignOutEvent());
225 context,
227 "unloading.checkout_success".tr()
228 );
229 }
230 });
231 },
232 style: ElevatedButton.styleFrom(
233 backgroundColor: ColorPalette.lightGreen,
234 padding: EdgeInsets.symmetric(vertical: 12.sp),
235 shape: RoundedRectangleBorder(
236 borderRadius: BorderRadius.circular(8),
237 ),
238 ),
239 child: Text(
240 'unloading.checkout_button'.tr(),
241 style: TextStyle(
242 color: Colors.white,
243 fontSize: 16.sp,
244 fontWeight: FontWeight.bold,
245 ),
246 ),
247 ),
248 ),
249 SizedBox(height: 10.sp),
250 ],
251 );
252 },
253 );
254 }
255}
256
257 Widget _buildCollectionsContent(List<UnloadingCollectionEntity> collections) {
258 // Initialize selection state if needed
259 if (_selectedItems.isEmpty && collections.isNotEmpty) {
260 for (var collection in collections) {
261 _selectedItems[collection.id ?? 0] = false;
262 }
263 }
264
265 return SingleChildScrollView(
266 child: Column(
267 crossAxisAlignment: CrossAxisAlignment.start,
268 children: [
269 SizedBox(height: 16.sp),
273 SizedBox(height: 16.sp),
274 if (collections.isNotEmpty) _buildUnloadingSlider(),
275 SizedBox(height: 16.sp),
276 ],
277 ),
278 );
279 }
280
281 Widget _buildStatsCard(List<UnloadingCollectionEntity> collections) {
282 return Padding(
283 padding: EdgeInsets.symmetric(horizontal: 15.sp),
284 child: StatsCard(
285 gradient: LinearGradient(
286 colors: [
287 ColorPalette.orange.withValues(alpha: 0.9, red: 0.1),
288 ColorPalette.grey.withValues(alpha: 0.9, blue: 0.1),
289 ],
290 ),
291 title: 'unloading.collections_to_unload'.tr(),
292 number: collections.length.toString(),
294 ),
295 );
296 }
297
298 String formatVolume(dynamic volume) {
299 if (volume == null) return "--";
300 double volumeValue = volume is double ? volume : double.tryParse(volume.toString()) ?? 0.0;
301 if (volumeValue > 999) {
302 return '${(volumeValue / 1000).toStringAsFixed(2)} MT';
303 } else {
304 return '$volumeValue KG';
305 }
306 }
307
308 Widget _buildSelectAllOption(List<UnloadingCollectionEntity> collections) {
309 return Padding(
310 padding: EdgeInsets.symmetric(horizontal: 15.sp, vertical: 16.sp),
311 child: Row(
312 children: [
313 SizedBox(
314 width: 24,
315 height: 24,
316 child: Checkbox(
317 value: _selectAll,
318 onChanged: (value) => _handleSelectAll(value, collections),
319 shape: RoundedRectangleBorder(
320 borderRadius: BorderRadius.circular(4),
321 ),
322 side: BorderSide(width: 1.5, color: Colors.grey[400]!),
323 ),
324 ),
325 SizedBox(width: 12.sp),
326 Text(
327 'common.select_all'.tr(),
328 style: TextStyle(
329 fontSize: 16.sp,
330 fontWeight: FontWeight.w500,
331 color: Colors.grey[700],
332 ),
333 ),
334 ],
335 ),
336 );
337 }
338
339 Widget _buildCollectionsList(List<UnloadingCollectionEntity> collections) {
340 if (collections.isEmpty) {
341 return Center(
342 child: Padding(
343 padding: EdgeInsets.only(top: 90.sp),
344 child: EmptyContent(text: 'unloading.no_pending_collections'.tr()),
345 ),
346 );
347 }
348
349 return ListView.builder(
350 shrinkWrap: true,
351 physics: const NeverScrollableScrollPhysics(),
352 itemCount: collections.length,
353 itemBuilder: (context, index) {
354 final collection = collections[index];
355 return UnloadingCollectionItem(
356 collection: collection,
357 number: (index + 1).toString(),
358 isSelected: _selectedItems[collection.id ?? 0] ?? false,
359 onCheckboxChanged: (selected) => _handleItemSelection(selected, collection),
360 );
361 },
362 );
363 }
364
366 return Padding(
367 padding: EdgeInsets.symmetric(horizontal: 15.sp),
368 child: CustomSlider(
369 action: (_) => _handleUnloadingAction(),
370 text: 'unloading.unloading_action'.tr(),
371 ),
372 );
373 }
374
376 if (_selectedCollections.isEmpty) {
378 context,
380 "unloading.select_collection_error".tr(),
381 );
382 return;
383 }
384
385 final authState = context.read<AuthenticationBloc>().state;
386 if (authState is AuthenticatedState) {
388 userId: authState.session.uid.toString(),
389 context: context,
390 onConfirm: (user, firstWeight) {
391 setState(() {
392 _isProcessingUnloading = true;
393 });
394 context.read<UnloadingBloc>().add(
395 SendUnloadingEvent(
396 warehouseResponsibleId: int.parse(user),
397 firstWeight: firstWeight,
398 userId: authState.session.uid ?? 0,
399 collectionVouchers: _selectedCollections.map(
400 (e) => CollectionVoucherModel(
401 id: e.id ?? 0,
402 volumeCollected: e.volumeCollected ?? 0,
403 )
404 ).toList(),
405 ),
406 );
407 },
408 );
409 }
410 }
411
413 final authState = context.read<AuthenticationBloc>().state;
414 if (authState is AuthenticatedState) {
415 context.read<UnloadingBloc>().add(
416 GetPendingUnloadingCollectionsEvent(userId: authState.session.uid ?? 0),
417 );
418 }
419 }
420
421 double _calculateTotalVolume(List<UnloadingCollectionEntity> collections) {
422 double total = 0;
423 for (var collection in collections) {
424 total += collection.volumeCollected ?? 0;
425 }
426 return total;
427 }
428}
override void initState()
class App extends StatefulWidget build(BuildContext context)
Definition app.dart:31
AuthGuard _()
sealed class CheckInOutEvent extends Equatable userId
const SendVehicleCheckFailureState({required this.failure})
static const lightGreen
static const red
static const antiFlashWhite
static const darkGreen
static const orange
static const grey
static ScaffoldFeatureController< SnackBar, SnackBarClosedReason > display(final BuildContext context, final Color color, final String message,)
const UnloadingProcessView({super.key})
override State< UnloadingProcessView > createState()
void _handleBlocStateChanges(BuildContext context, CollectionsState state)
final String volumeCollected
sealed class CollectionsState extends Equatable collections
final Widget child
final EdgeInsets padding
final String subtitle
class Partner String
final Color color
Definition failures.dart:1
final String message
Definition failures.dart:0
final VoidCallback onPressed
final String title
void showUnloadingConfirmationDialog({ required BuildContext context, required Function(String user, double firstWeight) onConfirm, required String userId, })
const GetPendingUnloadingCollectionsEvent({required this.userId})
String formatVolume(dynamic volume)
double _calculateTotalVolume(List< dynamic > history)
void _handleUnloadingAction()
Widget _buildSelectAllOption(List< UnloadingCollectionEntity > collections)
void _handleItemSelection(bool? selected, UnloadingCollectionEntity collection)
void _handleSelectAll(bool? value, List< UnloadingCollectionEntity > collections)
bool _isProcessingUnloading
final List< UnloadingCollectionEntity > _selectedCollections
void _performCheckOut()
Widget _buildStatsCard(List< UnloadingCollectionEntity > collections)
void _updateSelectAllState(List< UnloadingCollectionEntity > collections)
void _loadPendingUnloading()
Widget _buildUnloadingSlider()
class UnloadingProcessView extends StatefulWidget _selectedItems
double _calculateTotalVolume(List< UnloadingCollectionEntity > collections)
Widget _buildCollectionsContent(List< UnloadingCollectionEntity > collections)
const UnloadingError({required this.message})
class SearchWeeklyCollectionsEvent extends WeeklyCollectionsEvent collection
style Text( '${ 'scheduling.reference'.tr()}:${collection.internalCode}', style:Theme.of(context).textTheme.bodySmall,)
style SizedBox(height:2.h)
Widget _buildCollectionsList()
style Column(crossAxisAlignment:CrossAxisAlignment.end, children:[Container(padding:EdgeInsets.symmetric(horizontal:8.w, vertical:4.h), decoration:BoxDecoration(color:ColorPalette.tiffanyBlue.withValues(alpha:0.1), borderRadius:BorderRadius.circular(12),), child:Text(collection.type ?? '', style:Theme.of(context).textTheme.bodySmall?.copyWith(color:ColorPalette.tiffanyBlue, fontWeight:FontWeight.bold,),),),],)