203 lines
6.8 KiB
Dart
203 lines
6.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:uto_manager/domain/entities/doctrinal_branch.dart';
|
|
import 'package:uto_manager/domain/entities/echelon.dart';
|
|
import 'package:uto_manager/domain/entities/unit_node.dart';
|
|
import 'package:uuid/uuid.dart';
|
|
|
|
/// Dialog for creating or editing a [UnitNode].
|
|
///
|
|
/// Pass [existing] to edit; leave null to create a new unit.
|
|
/// Pass [parentUnit] to pre-select the parent and constrain the echelon.
|
|
class AddEditUnitDialog extends StatefulWidget {
|
|
const AddEditUnitDialog({
|
|
super.key,
|
|
this.existing,
|
|
this.parentUnit,
|
|
this.availableParents = const [],
|
|
});
|
|
|
|
final UnitNode? existing;
|
|
final UnitNode? parentUnit;
|
|
final List<UnitNode> availableParents;
|
|
|
|
@override
|
|
State<AddEditUnitDialog> createState() => _AddEditUnitDialogState();
|
|
}
|
|
|
|
class _AddEditUnitDialogState extends State<AddEditUnitDialog> {
|
|
final _formKey = GlobalKey<FormState>();
|
|
late final TextEditingController _nameCtrl;
|
|
|
|
late Echelon _echelon;
|
|
late DoctrinalBranch _branch;
|
|
String? _parentUnitId;
|
|
late bool _isKeyLeader;
|
|
|
|
bool get _isEditing => widget.existing != null;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
final e = widget.existing;
|
|
_nameCtrl = TextEditingController(text: e?.name ?? '');
|
|
_echelon = e?.echelon ?? _defaultChildEchelon();
|
|
_branch = e?.branch ?? DoctrinalBranch.infantry;
|
|
_parentUnitId = e?.parentUnitId ?? widget.parentUnit?.unitId;
|
|
_isKeyLeader = e?.isKeyLeader ?? false;
|
|
}
|
|
|
|
Echelon _defaultChildEchelon() {
|
|
final parent = widget.parentUnit;
|
|
if (parent == null) return Echelon.brigade;
|
|
// Default to one level below parent
|
|
final childLevel = parent.echelon.level + 1;
|
|
return Echelon.values.firstWhere(
|
|
(e) => e.level == childLevel,
|
|
orElse: () => Echelon.team,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_nameCtrl.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
void _submit() {
|
|
if (!_formKey.currentState!.validate()) return;
|
|
|
|
// If no name provided, generate a default from echelon + branch abbreviation
|
|
final rawName = _nameCtrl.text.trim();
|
|
final name = rawName.isEmpty ? '${_echelon.displayName} (${_branch.abbreviation})' : rawName;
|
|
|
|
final unit = UnitNode(
|
|
unitId: widget.existing?.unitId ?? const Uuid().v4(),
|
|
echelon: _echelon,
|
|
branch: _branch,
|
|
name: name,
|
|
isKeyLeader: _isKeyLeader,
|
|
parentUnitId: _parentUnitId,
|
|
);
|
|
|
|
Navigator.of(context).pop(unit);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AlertDialog(
|
|
title: Row(
|
|
children: [
|
|
Icon(_isEditing ? Icons.edit : Icons.add_box_outlined, size: 20),
|
|
const SizedBox(width: 8),
|
|
Text(_isEditing ? 'Edit Unit' : 'Add Unit'),
|
|
],
|
|
),
|
|
content: SizedBox(
|
|
width: 420,
|
|
child: Form(
|
|
key: _formKey,
|
|
child: SingleChildScrollView(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Unit Name
|
|
TextFormField(
|
|
controller: _nameCtrl,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Unit Name / Designation (optional)',
|
|
hintText: 'e.g. 1-68 AR, Alpha Company, 3rd Platoon',
|
|
helperText: 'Leave blank to auto-generate from echelon & branch',
|
|
prefixIcon: Icon(Icons.account_tree_outlined),
|
|
),
|
|
textCapitalization: TextCapitalization.words,
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
// Echelon
|
|
DropdownButtonFormField<Echelon>(
|
|
initialValue: _echelon,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Echelon',
|
|
prefixIcon: Icon(Icons.layers_outlined),
|
|
),
|
|
items: Echelon.values.map((e) {
|
|
return DropdownMenuItem(
|
|
value: e,
|
|
child: Text(e.displayName),
|
|
);
|
|
}).toList(),
|
|
onChanged: (v) => setState(() => _echelon = v!),
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
// Branch
|
|
DropdownButtonFormField<DoctrinalBranch>(
|
|
initialValue: _branch,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Branch / Functional Area',
|
|
prefixIcon: Icon(Icons.category_outlined),
|
|
),
|
|
items: DoctrinalBranch.values.map((b) {
|
|
return DropdownMenuItem(
|
|
value: b,
|
|
child: Text('${b.abbreviation} — ${b.displayName}'),
|
|
);
|
|
}).toList(),
|
|
onChanged: (v) => setState(() => _branch = v!),
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
// Parent Unit (optional)
|
|
if (widget.availableParents.isNotEmpty) ...[
|
|
DropdownButtonFormField<String?>(
|
|
initialValue: _parentUnitId,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Parent Unit (optional)',
|
|
prefixIcon: Icon(Icons.device_hub_outlined),
|
|
),
|
|
items: [
|
|
const DropdownMenuItem<String?>(
|
|
child: Text('— None (root unit) —'),
|
|
),
|
|
...widget.availableParents.map((u) {
|
|
return DropdownMenuItem<String?>(
|
|
value: u.unitId,
|
|
child: Text('${u.echelon.displayName}: ${u.name}'),
|
|
);
|
|
}),
|
|
],
|
|
onChanged: (v) => setState(() => _parentUnitId = v),
|
|
),
|
|
const SizedBox(height: 12),
|
|
],
|
|
|
|
// Key Leader toggle
|
|
SwitchListTile(
|
|
title: const Text('Key Leader Unit'),
|
|
subtitle: const Text('Marks this as a command/key leader node'),
|
|
value: _isKeyLeader,
|
|
onChanged: (v) => setState(() => _isKeyLeader = v),
|
|
contentPadding: EdgeInsets.zero,
|
|
dense: true,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: const Text('Cancel'),
|
|
),
|
|
FilledButton.icon(
|
|
icon: Icon(_isEditing ? Icons.save : Icons.add, size: 16),
|
|
label: Text(_isEditing ? 'Save Changes' : 'Add Unit'),
|
|
onPressed: _submit,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|