Files
Handwerksfreund_App/lib/screens/customer_detail_screen.dart
MarcWieland 411ede36ef init
2026-07-26 23:58:12 +02:00

504 lines
17 KiB
Dart

import 'package:flutter/material.dart';
import '../models/customer.dart';
import '../models/order.dart';
import '../theme/app_theme.dart';
class CustomerDetailScreen extends StatelessWidget {
final Customer customer;
const CustomerDetailScreen({
super.key,
required this.customer,
});
String _formatDate(DateTime date) {
final day = date.day.toString().padLeft(2, '0');
final month = date.month.toString().padLeft(2, '0');
return '$day.$month.${date.year}';
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppTheme.backgroundLight,
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.of(context).pop(),
),
title: const Text('Kundendetails'),
centerTitle: true,
actions: [
IconButton(
icon: const Icon(Icons.more_horiz),
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Optionen geöffnet')),
);
},
),
],
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Top Customer Profile Card
_buildProfileHeader(context),
const SizedBox(height: 16),
// Quick Action Buttons
_buildQuickActions(context),
const SizedBox(height: 20),
// Section 1: Kundeninformationen
_buildCustomerInfoSection(),
const SizedBox(height: 20),
// Section 2: Verbaute Teile (letzter Auftrag)
_buildInstalledPartsSection(),
const SizedBox(height: 20),
// Section 3: Aufträge
_buildWorkOrdersSection(),
const SizedBox(height: 24),
],
),
),
);
}
Widget _buildProfileHeader(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
CircleAvatar(
radius: 28,
backgroundColor: AppTheme.primaryBlue,
child: const Icon(
Icons.person,
color: Colors.white,
size: 32,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
customer.name,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: AppTheme.textPrimary,
),
),
const SizedBox(height: 4),
Text(
customer.street,
style: const TextStyle(
fontSize: 13,
color: AppTheme.textSecondary,
),
),
Text(
'${customer.zipCode} ${customer.city}',
style: const TextStyle(
fontSize: 13,
color: AppTheme.textSecondary,
),
),
],
),
),
Container(
decoration: BoxDecoration(
color: AppTheme.backgroundLight,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: AppTheme.cardBorder),
),
child: IconButton(
icon: const Icon(Icons.phone_outlined, color: AppTheme.textPrimary),
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Anruf gestartet: ${customer.phone}')),
);
},
),
),
],
),
),
);
}
Widget _buildQuickActions(BuildContext context) {
return Row(
children: [
_buildActionButton(
context,
icon: Icons.navigation_outlined,
label: 'Route',
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Navigation zu ${customer.fullAddress} gestartet')),
);
},
),
const SizedBox(width: 10),
_buildActionButton(
context,
icon: Icons.phone_outlined,
label: 'Anrufen',
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Anrufen: ${customer.phone}')),
);
},
),
const SizedBox(width: 10),
_buildActionButton(
context,
icon: Icons.email_outlined,
label: 'E-Mail',
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('E-Mail an: ${customer.email}')),
);
},
),
const SizedBox(width: 10),
_buildActionButton(
context,
icon: Icons.edit_outlined,
label: 'Bearbeiten',
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Kunde bearbeiten')),
);
},
),
],
);
}
Widget _buildActionButton(
BuildContext context, {
required IconData icon,
required String label,
required VoidCallback onTap,
}) {
return Expanded(
child: Material(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 14),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppTheme.cardBorder),
),
child: Column(
children: [
Icon(icon, color: AppTheme.primaryBlue, size: 22),
const SizedBox(height: 6),
Text(
label,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: AppTheme.textPrimary,
),
),
],
),
),
),
),
);
}
Widget _buildCustomerInfoSection() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Kundeninformationen',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: AppTheme.textPrimary,
),
),
const SizedBox(height: 10),
Card(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
children: [
_buildInfoRow('Kundennummer', customer.customerNumber),
const Divider(height: 1, color: AppTheme.cardBorder),
_buildInfoRow('Telefon', customer.phone),
const Divider(height: 1, color: AppTheme.cardBorder),
_buildInfoRow('E-Mail', customer.email),
const Divider(height: 1, color: AppTheme.cardBorder),
_buildInfoRow('Erstkontakt', _formatDate(customer.firstContactDate)),
if (customer.notes != null) ...[
const Divider(height: 1, color: AppTheme.cardBorder),
_buildInfoRow('Notizen', customer.notes!),
],
],
),
),
),
],
);
}
Widget _buildInfoRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 120,
child: Text(
label,
style: const TextStyle(
fontSize: 13,
color: AppTheme.textSecondary,
),
),
),
Expanded(
child: Text(
value,
textAlign: TextAlign.end,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppTheme.textPrimary,
),
),
),
],
),
);
}
Widget _buildInstalledPartsSection() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Verbaute Teile (letzter Auftrag)',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: AppTheme.textPrimary,
),
),
const SizedBox(height: 10),
Card(
child: customer.installedParts.isEmpty
? const Padding(
padding: EdgeInsets.all(16),
child: Center(
child: Text(
'Keine verbauten Teile vorhanden.',
style: TextStyle(color: AppTheme.textSecondary),
),
),
)
: ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: customer.installedParts.length,
separatorBuilder: (context, index) =>
const Divider(height: 1, color: AppTheme.cardBorder),
itemBuilder: (context, index) {
final part = customer.installedParts[index];
return Padding(
padding: const EdgeInsets.all(14),
child: Row(
children: [
Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: AppTheme.backgroundLight,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppTheme.cardBorder),
),
child: Icon(
_getPartIcon(part.category),
color: AppTheme.textSecondary,
size: 24,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
part.name,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: AppTheme.textPrimary,
),
),
const SizedBox(height: 2),
Text(
part.category,
style: const TextStyle(
fontSize: 12,
color: AppTheme.textSecondary,
),
),
const SizedBox(height: 2),
Text(
'Seriennr.: ${part.serialNumber}',
style: const TextStyle(
fontSize: 11,
color: AppTheme.textMuted,
),
),
],
),
),
Text(
'${part.quantity} Stk.',
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppTheme.textSecondary,
),
),
],
),
);
},
),
),
],
);
}
IconData _getPartIcon(String category) {
if (category.contains('Heiz') || category.contains('Gas')) {
return Icons.hvac_outlined;
} else if (category.contains('thermostat') || category.contains('Raum')) {
return Icons.thermostat_outlined;
} else if (category.contains('Filter') || category.contains('Wasser')) {
return Icons.filter_alt_outlined;
} else if (category.contains('Pumpe')) {
return Icons.speed_outlined;
}
return Icons.build_outlined;
}
Widget _buildWorkOrdersSection() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Aufträge',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: AppTheme.textPrimary,
),
),
const SizedBox(height: 10),
Card(
child: customer.orders.isEmpty
? const Padding(
padding: EdgeInsets.all(16),
child: Center(
child: Text(
'Keine Aufträge vorhanden.',
style: TextStyle(color: AppTheme.textSecondary),
),
),
)
: ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: customer.orders.length,
separatorBuilder: (context, index) =>
const Divider(height: 1, color: AppTheme.cardBorder),
itemBuilder: (context, index) {
final order = customer.orders[index];
final isCompleted = order.status == OrderStatus.abgeschlossen;
return Padding(
padding: const EdgeInsets.all(14),
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_formatDate(order.date),
style: const TextStyle(
fontSize: 12,
color: AppTheme.textSecondary,
),
),
const SizedBox(height: 4),
Text(
order.title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppTheme.textPrimary,
),
),
],
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 4,
),
decoration: BoxDecoration(
color: isCompleted
? const Color(0xFFDCFCE7)
: const Color(0xFFFEF3C7),
borderRadius: BorderRadius.circular(6),
),
child: Text(
order.status.label,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: isCompleted
? const Color(0xFF166534)
: const Color(0xFF92400E),
),
),
),
const SizedBox(width: 8),
const Icon(
Icons.chevron_right,
color: AppTheme.textSecondary,
size: 20,
),
],
),
);
},
),
),
],
);
}
}