Files
lis/mylis/lib/screens/expertise/expertise_form.dart
T
2026-07-21 19:08:27 +07:00

193 lines
5.8 KiB
Dart

part of '../../app/app.dart';
class ExpertiseForm extends StatelessWidget {
const ExpertiseForm({
super.key,
required this.dlp,
required this.fields,
required this.textControllers,
required this.selectValues,
required this.multiValues,
required this.onChanged,
});
final String dlp;
final List<dynamic> fields;
final Map<String, TextEditingController> textControllers;
final Map<String, String> selectValues;
final Map<String, Set<String>> multiValues;
final VoidCallback onChanged;
@override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
child: Padding(
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_templateTitle(dlp),
style: Theme.of(
context,
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w800),
),
const SizedBox(height: 12),
...fields.map((rawField) => _buildField(context, asMap(rawField))),
],
),
),
);
}
Widget _buildField(BuildContext context, Map<String, dynamic> field) {
final name = field['name']?.toString() ?? '';
final label = field['label']?.toString() ?? name;
final type = field['type']?.toString() ?? 'text';
final options = asList(
field['options'],
).map((item) => item.toString()).toList();
if (type == 'select') {
final currentValue = options.contains(selectValues[name])
? selectValues[name]
: null;
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: DropdownButtonFormField<String>(
initialValue: currentValue,
isExpanded: true,
decoration: InputDecoration(
labelText: label,
border: const OutlineInputBorder(),
),
items: options
.map(
(option) =>
DropdownMenuItem(value: option, child: Text(option)),
)
.toList(),
onChanged: (value) {
selectValues[name] = value ?? '';
onChanged();
},
),
);
}
if (type == 'multiselect') {
final selected = multiValues[name] ?? <String>{};
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: InputDecorator(
decoration: InputDecoration(
labelText: label,
border: const OutlineInputBorder(),
),
child: Wrap(
spacing: 8,
runSpacing: 4,
children: options
.map(
(option) => FilterChip(
label: Text(option),
selected: selected.contains(option),
onSelected: (checked) {
if (checked) {
selected.add(option);
} else {
selected.remove(option);
}
multiValues[name] = selected;
onChanged();
},
),
)
.toList(),
),
),
);
}
final controller = textControllers[name] ??= TextEditingController();
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: TextField(
controller: controller,
minLines: type == 'textarea' ? 4 : 1,
maxLines: type == 'textarea' ? 8 : 1,
decoration: InputDecoration(
labelText: label,
border: const OutlineInputBorder(),
),
),
);
}
}
class ExpertiseActions extends StatelessWidget {
const ExpertiseActions({
super.key,
required this.isSupervisor,
required this.saving,
required this.onSave,
});
final bool isSupervisor;
final bool saving;
final ValueChanged<String> onSave;
@override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
child: Padding(
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
FilledButton.icon(
onPressed: saving ? null : () => onSave('Draft'),
icon: saving
? const SizedBox.square(
dimension: 18,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.save_outlined),
label: const Text('Save as Draft'),
),
if (isSupervisor) ...[
OutlinedButton.icon(
onPressed: saving ? null : () => onSave('preliminary'),
icon: const Icon(Icons.done_all_outlined),
label: const Text('Save Preliminary result'),
),
OutlinedButton.icon(
onPressed: saving ? null : () => onSave('verifikasi'),
icon: const Icon(Icons.verified_outlined),
label: const Text('Save Final Result'),
),
] else ...[
OutlinedButton.icon(
onPressed: saving
? null
: () => onSave('Permohonan Verifikasi'),
icon: const Icon(Icons.send_outlined),
label: const Text('Save and Send To SPV'),
),
OutlinedButton.icon(
onPressed: saving
? null
: () => onSave('Permohonan Verifikasi Preliminary'),
icon: const Icon(Icons.outgoing_mail),
label: const Text('Kirim SPV Preliminary'),
),
],
],
),
),
);
}
}