Files
HHA/app/lib/main.dart

425 lines
11 KiB
Dart

import 'package:dartz/dartz.dart' hide State;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'core/theme/app_theme.dart';
import 'domain/entities/tour.dart';
import 'domain/entities/logistic_object.dart';
import 'domain/entities/location.dart';
import 'domain/entities/counter.dart';
import 'domain/repositories/tour_repository.dart';
import 'core/errors/failures.dart';
import 'presentation/blocs/tour/tour_bloc.dart';
import 'presentation/blocs/scan/scan_bloc.dart';
import 'presentation/pages/tours/tours_page.dart';
import 'presentation/pages/tours/dashboard_page.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
// Set preferred orientations
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
// Set system UI overlay style
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.dark,
),
);
runApp(const HHAApp());
}
class HHAApp extends StatelessWidget {
const HHAApp({super.key});
@override
Widget build(BuildContext context) {
final repository = MockTourRepository();
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => TourBloc(
repository: repository,
)..add(const LoadTours()),
),
BlocProvider(
create: (context) => ScanBloc(
repository: repository,
),
),
],
child: MaterialApp(
title: 'HHA Logistics',
debugShowCheckedModeBanner: false,
theme: AppTheme.lightTheme,
home: const MainNavigationPage(),
),
);
}
}
class MainNavigationPage extends StatefulWidget {
const MainNavigationPage({super.key});
@override
State<MainNavigationPage> createState() => _MainNavigationPageState();
}
class _MainNavigationPageState extends State<MainNavigationPage> {
int _currentIndex = 0;
final List<Widget> _pages = const [
DashboardPage(),
ToursPage(),
InventoryPage(),
SettingsPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: _currentIndex,
children: _pages,
),
bottomNavigationBar: NavigationBar(
selectedIndex: _currentIndex,
onDestinationSelected: (index) {
setState(() {
_currentIndex = index;
});
},
destinations: const [
NavigationDestination(
icon: Icon(Icons.dashboard_outlined),
selectedIcon: Icon(Icons.dashboard),
label: 'Dashboard',
),
NavigationDestination(
icon: Icon(Icons.map_outlined),
selectedIcon: Icon(Icons.map),
label: 'Touren',
),
NavigationDestination(
icon: Icon(Icons.inventory_2_outlined),
selectedIcon: Icon(Icons.inventory_2),
label: 'Bestand',
),
NavigationDestination(
icon: Icon(Icons.settings_outlined),
selectedIcon: Icon(Icons.settings),
label: 'Einstellungen',
),
],
),
);
}
}
// Mock Repository for demonstration
class MockTourRepository implements TourRepository {
@override
Future<Either<Failure, List<Tour>>> getTours() async {
await Future.delayed(const Duration(seconds: 1));
return const Right([
Tour(
id: 1,
jobId: 1,
tourId: 1,
version: 1,
state: 0,
type: 'stock_start',
sort: 1,
locationId: 1,
locationCode: 'LAGER001',
locationName: 'Hauptlager Wandsbek',
modified: 0,
),
Tour(
id: 2,
jobId: 1,
tourId: 2,
version: 1,
state: 0,
type: 'veh_start',
sort: 2,
locationId: 2,
locationCode: 'DST001',
locationName: 'Dienststelle Hammerbrook',
modified: 0,
),
Tour(
id: 3,
jobId: 1,
tourId: 3,
version: 1,
state: 0,
type: 'st',
sort: 3,
locationId: 3,
locationCode: 'HALT001',
locationName: 'Hauptbahnhof Nord',
remark: '4 Fahrscheinautomaten',
modified: 0,
),
Tour(
id: 4,
jobId: 1,
tourId: 4,
version: 1,
state: 1,
type: 'st',
sort: 4,
locationId: 4,
locationCode: 'HALT002',
locationName: 'Jungfernstieg',
remark: '2 Fahrscheinautomaten',
modified: 0,
),
Tour(
id: 5,
jobId: 1,
tourId: 5,
version: 1,
state: 0,
type: 'gi',
sort: 5,
locationId: 5,
locationCode: 'BANK001',
locationName: 'Geldinstitut Mitte',
modified: 0,
),
]);
}
@override
Future<Either<Failure, Tour>> getTourById(int tourId) async {
final tours = await getTours();
return tours.fold(
(failure) => Left(failure),
(tourList) {
final tour = tourList.firstWhere(
(t) => t.tourId == tourId,
orElse: () => throw Exception('Tour not found'),
);
return Right(tour);
},
);
}
@override
Future<Either<Failure, void>> updateTourState(int tourId, int state) async {
return const Right(null);
}
@override
Future<Either<Failure, void>> completeTour(int tourId) async {
return const Right(null);
}
@override
Future<Either<Failure, void>> syncData() async {
await Future.delayed(const Duration(seconds: 1));
return const Right(null);
}
@override
Future<Either<Failure, bool>> checkForUpdates() async => const Right(false);
@override
Future<Either<Failure, List<LogisticObject>>> getObjectsByTour(int tourId) async => const Right([]);
@override
Future<Either<Failure, List<LogisticObject>>> getObjectsByState(String state) async => const Right([]);
@override
Future<Either<Failure, LogisticObject?>> getObjectByBarcode(String barcode) async => const Right(null);
@override
Future<Either<Failure, void>> updateObjectState(
int objectId,
String newState, {
int? locationId,
int? refType,
int? refId,
String? containerCode,
}) async => const Right(null);
@override
Future<Either<Failure, void>> createObject({
required int type,
required String code,
bool isManual = true,
}) async => const Right(null);
@override
Future<Either<Failure, List<Location>>> getLocations() async => const Right([]);
@override
Future<Either<Failure, Location>> getLocationById(int locationId) async {
return const Left(NotFoundFailure(message: 'Location not found'));
}
@override
Future<Either<Failure, List<ObjectMetadata>>> getObjectMetadata() async => const Right([]);
@override
Future<Either<Failure, TourStatistics>> getTourStatistics(int tourId) async {
return const Right(TourStatistics(
totalObjects: 10,
completedObjects: 5,
pendingObjects: 5,
objectsByState: {'delivery': 3, 'station': 2},
objectsByType: {'meka': 3, 'beka': 2},
completionPercentage: 50.0,
));
}
@override
Future<Either<Failure, ObjectStatistics>> getObjectStatistics() async {
return const Right(ObjectStatistics(
byState: {},
byType: {},
byLocation: {},
recentObjects: [],
totalCount: 0,
));
}
@override
Future<Either<Failure, CounterOverview>> getCounterOverview(
int tourId,
String tourType, {
String? pageId,
}) async {
return Right(CounterOverview(
tourId: tourId,
pageId: pageId,
groups: const [],
));
}
@override
Future<Either<Failure, List<PickupCount>>> getPickupCounts(
int tourId,
String pageId,
) async => const Right([]);
@override
Future<Either<Failure, List<SwapCount>>> getSwapCounts(
int tourId,
String pageId,
) async => const Right([]);
@override
Future<Either<Failure, List<ContainerInfo>>> getOpenContainers() async => const Right([]);
@override
Future<Either<Failure, void>> addObjectToContainer(
String containerId,
String containerType,
int objectId,
) async => const Right(null);
@override
Future<Either<Failure, void>> closeContainer(
String containerId,
String containerType,
) async => const Right(null);
@override
Future<Either<Failure, List<RecentScan>>> getRecentScans(
int tourId, {
int limit = 10,
}) async => const Right([]);
}
// Placeholder pages
class InventoryPage extends StatelessWidget {
const InventoryPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bestand'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.inventory_2,
size: 80,
color: Colors.grey.shade400,
),
const SizedBox(height: 16),
Text(
'Bestandsübersicht',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 8),
Text(
'Hier wird der aktuelle Bestand angezeigt',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.grey.shade600,
),
),
],
),
),
);
}
}
class SettingsPage extends StatelessWidget {
const SettingsPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Einstellungen'),
),
body: ListView(
children: [
ListTile(
leading: const Icon(Icons.sync),
title: const Text('Daten synchronisieren'),
subtitle: const Text('Letzte Synchronisation: Gerade eben'),
trailing: const Icon(Icons.chevron_right),
onTap: () {
// Trigger sync
},
),
const Divider(),
ListTile(
leading: const Icon(Icons.visibility),
title: const Text('Erledigte Stationen anzeigen'),
trailing: Switch(
value: true,
onChanged: (value) {},
),
),
const Divider(),
ListTile(
leading: const Icon(Icons.info),
title: const Text('Über'),
subtitle: const Text('Version 2.0.0'),
trailing: const Icon(Icons.chevron_right),
onTap: () {},
),
],
),
);
}
}