210 lines
7.2 KiB
Dart
210 lines
7.2 KiB
Dart
|
|
import 'package:flutter/material.dart';
|
||
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||
|
|
import '../../../../core/localization/app_localizations.dart';
|
||
|
|
import '../../../v2/device_list/presentation/widgets/autonomous/work_ui.dart';
|
||
|
|
import '../cubit/area_task_create_cubit.dart';
|
||
|
|
|
||
|
|
class AreaDispatchSection extends StatelessWidget {
|
||
|
|
const AreaDispatchSection({super.key, required this.onReload});
|
||
|
|
final VoidCallback onReload;
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
final cubit = context.read<AreaTaskCreateCubit>();
|
||
|
|
final state = context.watch<AreaTaskCreateCubit>().state;
|
||
|
|
String text(String key) =>
|
||
|
|
AppLocalizations.of(context).translate('work_v2.plan_create.$key');
|
||
|
|
Widget heading(String label, IconData icon) => Row(
|
||
|
|
children: [
|
||
|
|
Icon(icon, color: kWtBlue, size: 18),
|
||
|
|
const SizedBox(width: 8),
|
||
|
|
Expanded(
|
||
|
|
child: Text(
|
||
|
|
label,
|
||
|
|
style: const TextStyle(
|
||
|
|
fontSize: 13,
|
||
|
|
fontWeight: FontWeight.w800,
|
||
|
|
color: kWtNavy,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
);
|
||
|
|
Widget hint(String label) => Text(
|
||
|
|
label,
|
||
|
|
style: const TextStyle(fontSize: 12, color: kWtGray, height: 1.5),
|
||
|
|
);
|
||
|
|
|
||
|
|
if (state.loading) {
|
||
|
|
return const Padding(
|
||
|
|
padding: EdgeInsets.all(24),
|
||
|
|
child: Center(child: CircularProgressIndicator(strokeWidth: 2)),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
if (!state.loaded) {
|
||
|
|
return Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
|
|
children: [
|
||
|
|
hint(state.loadError ?? text('area_load_hint')),
|
||
|
|
Align(
|
||
|
|
alignment: Alignment.centerLeft,
|
||
|
|
child: TextButton.icon(
|
||
|
|
onPressed: onReload,
|
||
|
|
icon: const Icon(Icons.refresh_rounded),
|
||
|
|
label: Text(text('area_reload')),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
);
|
||
|
|
}
|
||
|
|
final routes = state.availableRoutes;
|
||
|
|
final names = {for (final route in state.routes) route.id: route.name};
|
||
|
|
return Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
|
|
children: [
|
||
|
|
Row(
|
||
|
|
children: [
|
||
|
|
Expanded(
|
||
|
|
child: heading(
|
||
|
|
text('area_select_title'),
|
||
|
|
Icons.crop_free_rounded,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
IconButton(
|
||
|
|
tooltip: text('area_reload'),
|
||
|
|
onPressed: state.isSubmitting ? null : onReload,
|
||
|
|
icon: const Icon(Icons.refresh_rounded, size: 19, color: kWtGray),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
hint(text('area_select_hint')),
|
||
|
|
const SizedBox(height: 8),
|
||
|
|
if (state.regions.isEmpty)
|
||
|
|
hint(text('area_empty'))
|
||
|
|
else
|
||
|
|
Wrap(
|
||
|
|
spacing: 8,
|
||
|
|
runSpacing: 6,
|
||
|
|
children: [
|
||
|
|
for (final region in state.regions)
|
||
|
|
FilterChip(
|
||
|
|
label: Text(
|
||
|
|
region.name.isEmpty ? '#${region.id}' : region.name,
|
||
|
|
),
|
||
|
|
selected: state.selectedRegions.contains(region.id),
|
||
|
|
onSelected: state.isSubmitting
|
||
|
|
? null
|
||
|
|
: (_) => cubit.toggleRegion(region.id!),
|
||
|
|
selectedColor: kWtSoftBlue,
|
||
|
|
checkmarkColor: kWtBlue,
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
const SizedBox(height: 18),
|
||
|
|
heading(text('area_routes_title'), Icons.alt_route_rounded),
|
||
|
|
const SizedBox(height: 6),
|
||
|
|
hint(text('area_routes_hint')),
|
||
|
|
const SizedBox(height: 8),
|
||
|
|
if (state.selectedRegions.isEmpty)
|
||
|
|
hint(text('area_choose_first'))
|
||
|
|
else if (routes.isEmpty)
|
||
|
|
hint(text('area_routes_empty'))
|
||
|
|
else
|
||
|
|
Wrap(
|
||
|
|
spacing: 8,
|
||
|
|
runSpacing: 6,
|
||
|
|
children: [
|
||
|
|
for (final route in routes)
|
||
|
|
FilterChip(
|
||
|
|
label: Text(route.name.isEmpty ? '#${route.id}' : route.name),
|
||
|
|
selected: state.orderedRouteIds.contains(route.id),
|
||
|
|
onSelected: state.isSubmitting
|
||
|
|
? null
|
||
|
|
: (_) => cubit.toggleRoute(route.id),
|
||
|
|
selectedColor: kWtSoftBlue,
|
||
|
|
checkmarkColor: kWtBlue,
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
const SizedBox(height: 18),
|
||
|
|
heading(text('area_order_title'), Icons.format_list_numbered_rounded),
|
||
|
|
const SizedBox(height: 6),
|
||
|
|
hint(text('area_order_hint')),
|
||
|
|
const SizedBox(height: 8),
|
||
|
|
if (state.orderedRouteIds.isEmpty)
|
||
|
|
hint(text('area_order_empty'))
|
||
|
|
else
|
||
|
|
ReorderableListView.builder(
|
||
|
|
shrinkWrap: true,
|
||
|
|
physics: const NeverScrollableScrollPhysics(),
|
||
|
|
buildDefaultDragHandles: false,
|
||
|
|
itemCount: state.orderedRouteIds.length,
|
||
|
|
onReorder: cubit.reorder,
|
||
|
|
itemBuilder: (context, index) {
|
||
|
|
final id = state.orderedRouteIds[index];
|
||
|
|
return Container(
|
||
|
|
key: ValueKey('area_task_route_$id'),
|
||
|
|
margin: const EdgeInsets.only(bottom: 8),
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color: kWtSegBg,
|
||
|
|
borderRadius: BorderRadius.circular(10),
|
||
|
|
border: Border.all(color: kWtBorder),
|
||
|
|
),
|
||
|
|
child: ListTile(
|
||
|
|
dense: true,
|
||
|
|
contentPadding: const EdgeInsets.only(left: 10, right: 2),
|
||
|
|
leading: CircleAvatar(
|
||
|
|
radius: 14,
|
||
|
|
backgroundColor: kWtBlue,
|
||
|
|
child: Text(
|
||
|
|
'${index + 1}',
|
||
|
|
style: const TextStyle(fontSize: 12, color: Colors.white),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
title: Text(
|
||
|
|
names[id] ?? '#$id',
|
||
|
|
style: const TextStyle(
|
||
|
|
fontSize: 12.5,
|
||
|
|
fontWeight: FontWeight.w700,
|
||
|
|
color: kWtNavy,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
trailing: Row(
|
||
|
|
mainAxisSize: MainAxisSize.min,
|
||
|
|
children: [
|
||
|
|
IconButton(
|
||
|
|
tooltip: text('area_remove_route'),
|
||
|
|
onPressed: state.isSubmitting
|
||
|
|
? null
|
||
|
|
: () => cubit.toggleRoute(id),
|
||
|
|
icon: const Icon(
|
||
|
|
Icons.close_rounded,
|
||
|
|
size: 17,
|
||
|
|
color: kWtGray,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
ReorderableDragStartListener(
|
||
|
|
index: index,
|
||
|
|
enabled: !state.isSubmitting,
|
||
|
|
child: const Padding(
|
||
|
|
padding: EdgeInsets.symmetric(
|
||
|
|
horizontal: 12,
|
||
|
|
vertical: 16,
|
||
|
|
),
|
||
|
|
child: Icon(
|
||
|
|
Icons.drag_handle_rounded,
|
||
|
|
color: kWtGray,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
},
|
||
|
|
),
|
||
|
|
],
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|