import 'package:flutter/material.dart'; import '../../portfolio/labels.dart' show assetClassLabel; import '../data/pending_api.dart'; /// The form behind `action: "create"`. Fields are prefilled from what the report literally /// said about this line (ISIN, ticker, name, currency) — that is transcription, not a guess /// about which instrument it is; the user still confirms every field before submitting. class CreateInstrumentDialog extends StatefulWidget { const CreateInstrumentDialog({required this.pending, super.key}); final PendingInstrument pending; @override State createState() => _CreateInstrumentDialogState(); } class _CreateInstrumentDialogState extends State { final _formKey = GlobalKey(); late final _isin = TextEditingController(text: widget.pending.isin ?? ''); late final _ticker = TextEditingController(text: widget.pending.ticker ?? ''); late final _board = TextEditingController(text: widget.pending.board ?? ''); late final _name = TextEditingController(text: widget.pending.name ?? ''); late final _currency = TextEditingController( text: widget.pending.currency ?? 'RUB', ); late final _lot = TextEditingController(text: '1'); /// `asset_class_hint` is what the report's own section said (e.g. the «Фонды» table), so /// it is offered as the initial value of a control the user must still look at. late String _assetClass = assetClassKeys.contains(widget.pending.assetClassHint) ? widget.pending.assetClassHint! : 'share'; @override void dispose() { _isin.dispose(); _ticker.dispose(); _board.dispose(); _name.dispose(); _currency.dispose(); _lot.dispose(); super.dispose(); } void _submit() { if (!_formKey.currentState!.validate()) return; Navigator.of(context).pop( NewInstrument( assetClass: _assetClass, name: _name.text.trim(), currency: _currency.text.trim().toUpperCase(), isin: _isin.text.trim(), ticker: _ticker.text.trim(), board: _board.text.trim(), lot: int.tryParse(_lot.text.trim()), ), ); } @override Widget build(BuildContext context) { return AlertDialog( title: const Text('Создать инструмент'), content: SizedBox( width: 480, child: Form( key: _formKey, child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ DropdownButtonFormField( initialValue: _assetClass, isExpanded: true, decoration: const InputDecoration(labelText: 'Класс актива'), items: [ for (final key in assetClassKeys) DropdownMenuItem( value: key, child: Text('${assetClassLabel(key)} ($key)'), ), ], onChanged: (v) => setState(() => _assetClass = v ?? _assetClass), ), _field(_name, 'Название', required: true), _field(_isin, 'ISIN'), _field(_ticker, 'Тикер'), _field(_board, 'Доска (TQBR, TQTF…)'), _field(_currency, 'Валюта', required: true), _field( _lot, 'Лот', keyboard: TextInputType.number, validator: (v) { if (v == null || v.trim().isEmpty) return null; return int.tryParse(v.trim()) == null ? 'Целое число' : null; }, ), ], ), ), ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Отмена'), ), FilledButton( onPressed: _submit, child: const Text('Создать и привязать'), ), ], ); } Widget _field( TextEditingController controller, String label, { bool required = false, TextInputType? keyboard, String? Function(String?)? validator, }) { return Padding( padding: const EdgeInsets.only(top: 12), child: TextFormField( controller: controller, keyboardType: keyboard, decoration: InputDecoration(labelText: label, isDense: true), validator: validator ?? (required ? (v) => (v == null || v.trim().isEmpty) ? 'Обязательное поле' : null : null), ), ); } }