import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../models/project.dart'; class ProjectDialog extends StatefulWidget { final Project? project; final Function(Project) onSave; const ProjectDialog({ super.key, this.project, required this.onSave, }); @override State createState() => _ProjectDialogState(); } class _ProjectDialogState extends State { late TextEditingController _nameController; late TextEditingController _ipController; late TextEditingController _portController; final _formKey = GlobalKey(); @override void initState() { super.initState(); _nameController = TextEditingController(text: widget.project?.name ?? ''); _ipController = TextEditingController( text: widget.project?.ipAddress ?? '127.0.0.1', ); _portController = TextEditingController( text: widget.project?.port.toString() ?? '8888', ); } @override void dispose() { _nameController.dispose(); _ipController.dispose(); _portController.dispose(); super.dispose(); } void _save() { if (_formKey.currentState!.validate()) { final project = Project( id: widget.project?.id ?? DateTime.now().millisecondsSinceEpoch.toString(), name: _nameController.text.trim(), ipAddress: _ipController.text.trim(), port: int.parse(_portController.text), packages: widget.project?.packages ?? [], ); widget.onSave(project); Navigator.pop(context); } } @override Widget build(BuildContext context) { return AlertDialog( title: Text(widget.project == null ? 'New Project' : 'Edit Project'), content: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, children: [ TextFormField( controller: _nameController, decoration: const InputDecoration( labelText: 'Project Name', prefixIcon: Icon(Icons.folder), ), validator: (value) { if (value == null || value.trim().isEmpty) { return 'Please enter a project name'; } return null; }, autofocus: true, ), const SizedBox(height: 16), TextFormField( controller: _ipController, decoration: const InputDecoration( labelText: 'IP Address', prefixIcon: Icon(Icons.computer), hintText: '127.0.0.1', ), validator: (value) { if (value == null || value.trim().isEmpty) { return 'Please enter an IP address'; } // Basic IP address validation final ipPattern = RegExp( r'^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$', ); if (!ipPattern.hasMatch(value.trim())) { return 'Please enter a valid IP address'; } final parts = value.trim().split('.'); for (final part in parts) { final num = int.tryParse(part); if (num == null || num < 0 || num > 255) { return 'Each octet must be between 0 and 255'; } } return null; }, ), const SizedBox(height: 16), TextFormField( controller: _portController, decoration: const InputDecoration( labelText: 'UDP Port', prefixIcon: Icon(Icons.router), ), keyboardType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], validator: (value) { if (value == null || value.isEmpty) { return 'Please enter a port number'; } final port = int.tryParse(value); if (port == null || port < 1 || port > 65535) { return 'Port must be between 1 and 65535'; } return null; }, ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), FilledButton( onPressed: _save, child: const Text('Save'), ), ], ); } }