Add vendor selection and rates to order creation flow
Introduces a Vendor entity and integrates vendor selection into the one-time order creation and order editing flows. Vendor-specific rates are now displayed and used for position roles, and UI components have been updated to allow users to select a vendor and see corresponding rates. Mock vendor data is used for demonstration purposes.
This commit is contained in:
@@ -643,6 +643,9 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
|
||||
late List<Map<String, dynamic>> _positions;
|
||||
|
||||
List<Vendor> _vendors = const <Vendor>[];
|
||||
Vendor? _selectedVendor;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -661,6 +664,39 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
'location': null,
|
||||
},
|
||||
];
|
||||
|
||||
// Mock vendors initialization
|
||||
_vendors = const <Vendor>[
|
||||
Vendor(
|
||||
id: 'v1',
|
||||
name: 'Elite Staffing',
|
||||
rates: <String, double>{
|
||||
'Server': 25.0,
|
||||
'Bartender': 30.0,
|
||||
'Cook': 28.0,
|
||||
'Busser': 18.0,
|
||||
'Host': 20.0,
|
||||
'Barista': 22.0,
|
||||
'Dishwasher': 17.0,
|
||||
'Event Staff': 19.0,
|
||||
},
|
||||
),
|
||||
Vendor(
|
||||
id: 'v2',
|
||||
name: 'Premier Workforce',
|
||||
rates: <String, double>{
|
||||
'Server': 22.0,
|
||||
'Bartender': 28.0,
|
||||
'Cook': 25.0,
|
||||
'Busser': 16.0,
|
||||
'Host': 18.0,
|
||||
'Barista': 20.0,
|
||||
'Dishwasher': 15.0,
|
||||
'Event Staff': 18.0,
|
||||
},
|
||||
),
|
||||
];
|
||||
_selectedVendor = _vendors.first;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -707,7 +743,10 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
hours = endH - startH;
|
||||
if (hours < 0) hours += 24;
|
||||
} catch (_) {}
|
||||
total += hours * widget.order.hourlyRate * (pos['count'] as int);
|
||||
|
||||
final double rate =
|
||||
_selectedVendor?.rates[pos['role']] ?? widget.order.hourlyRate;
|
||||
total += hours * rate * (pos['count'] as int);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
@@ -741,6 +780,45 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
|
||||
_buildSectionHeader('VENDOR'),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space3,
|
||||
),
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.white,
|
||||
borderRadius: UiConstants.radiusMd,
|
||||
border: Border.all(color: UiColors.border),
|
||||
),
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton<Vendor>(
|
||||
isExpanded: true,
|
||||
value: _selectedVendor,
|
||||
icon: const Icon(
|
||||
UiIcons.chevronDown,
|
||||
size: 18,
|
||||
color: UiColors.iconSecondary,
|
||||
),
|
||||
onChanged: (Vendor? vendor) {
|
||||
if (vendor != null) {
|
||||
setState(() => _selectedVendor = vendor);
|
||||
}
|
||||
},
|
||||
items: _vendors.map((Vendor vendor) {
|
||||
return DropdownMenuItem<Vendor>(
|
||||
value: vendor,
|
||||
child: Text(
|
||||
vendor.name,
|
||||
style: UiTypography.body2m.textPrimary,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
|
||||
_buildSectionHeader('DATE'),
|
||||
UiTextField(
|
||||
controller: _dateController,
|
||||
@@ -902,17 +980,8 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
hint: 'Select role',
|
||||
value: pos['role'],
|
||||
items: <String>[
|
||||
'Server',
|
||||
'Bartender',
|
||||
'Cook',
|
||||
'Busser',
|
||||
'Host',
|
||||
'Barista',
|
||||
'Dishwasher',
|
||||
'Event Staff',
|
||||
if (pos['role'] != null &&
|
||||
pos['role'].toString().isNotEmpty &&
|
||||
!<String>[
|
||||
...(_selectedVendor?.rates.keys.toList() ??
|
||||
<String>[
|
||||
'Server',
|
||||
'Bartender',
|
||||
'Cook',
|
||||
@@ -921,9 +990,17 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
'Barista',
|
||||
'Dishwasher',
|
||||
'Event Staff',
|
||||
].contains(pos['role']))
|
||||
]),
|
||||
if (pos['role'] != null &&
|
||||
pos['role'].toString().isNotEmpty &&
|
||||
!(_selectedVendor?.rates.keys.contains(pos['role']) ?? false))
|
||||
pos['role'].toString(),
|
||||
],
|
||||
itemBuilder: (dynamic role) {
|
||||
final double? rate = _selectedVendor?.rates[role];
|
||||
if (rate == null) return role.toString();
|
||||
return '$role - \$${rate.toStringAsFixed(0)}/hr';
|
||||
},
|
||||
onChanged: (dynamic val) => _updatePosition(index, 'role', val),
|
||||
),
|
||||
|
||||
@@ -1358,6 +1435,9 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
}
|
||||
|
||||
Widget _buildReviewPositionCard(Map<String, dynamic> pos) {
|
||||
final double rate =
|
||||
_selectedVendor?.rates[pos['role']] ?? widget.order.hourlyRate;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
padding: const EdgeInsets.all(16),
|
||||
@@ -1387,7 +1467,7 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
],
|
||||
),
|
||||
Text(
|
||||
'\$${widget.order.hourlyRate.round()}/hr',
|
||||
'\$${rate.round()}/hr',
|
||||
style: UiTypography.body2b.copyWith(color: UiColors.primary),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user