504 lines
17 KiB
Dart
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,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|