Initial commit - app-padrao-1.0
This commit is contained in:
170
lib/features/services/presentation/pages/add_service_page.dart
Normal file
170
lib/features/services/presentation/pages/add_service_page.dart
Normal file
@@ -0,0 +1,170 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:barber_app/core/theme/app_theme.dart';
|
||||
import 'package:barber_app/features/services/data/models/service_model.dart';
|
||||
import 'package:barber_app/features/services/data/repositories/service_repository.dart';
|
||||
import 'package:barber_app/shared/widgets/custom_text_field.dart';
|
||||
import 'package:barber_app/shared/widgets/loading_button.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class AddServicePage extends StatefulWidget {
|
||||
final ServiceModel? service;
|
||||
|
||||
const AddServicePage({super.key, this.service});
|
||||
|
||||
@override
|
||||
State<AddServicePage> createState() => _AddServicePageState();
|
||||
}
|
||||
|
||||
class _AddServicePageState extends State<AddServicePage> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _nameController = TextEditingController();
|
||||
final _priceController = TextEditingController(); // Valor numérico raw
|
||||
final _displayPriceController = TextEditingController(); // Valor formatado R$
|
||||
final _durationController = TextEditingController(text: '30');
|
||||
|
||||
final _repository = ServiceRepository();
|
||||
bool _isLoading = false;
|
||||
|
||||
final _currencyFormat = NumberFormat.currency(locale: 'pt_BR', symbol: 'R\$');
|
||||
|
||||
bool get _isEditing => widget.service != null;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (_isEditing) {
|
||||
_nameController.text = widget.service!.name;
|
||||
_priceController.text = widget.service!.price.toStringAsFixed(2);
|
||||
_displayPriceController.text = _currencyFormat.format(widget.service!.price);
|
||||
_durationController.text = widget.service!.durationMinutes.toString();
|
||||
}
|
||||
}
|
||||
|
||||
void _formatPrice(String value) {
|
||||
if (value.isEmpty) return;
|
||||
|
||||
// Remove tudo que não é número
|
||||
String numbers = value.replaceAll(RegExp(r'[^0-9]'), '');
|
||||
if (numbers.isEmpty) return;
|
||||
|
||||
double val = double.parse(numbers) / 100;
|
||||
_priceController.text = val.toStringAsFixed(2);
|
||||
_displayPriceController.text = _currencyFormat.format(val);
|
||||
|
||||
// Mantém cursor no final
|
||||
_displayPriceController.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: _displayPriceController.text.length),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _saveService() async {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
if (_priceController.text.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Por favor, informe o preço')),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => _isLoading = true);
|
||||
|
||||
try {
|
||||
final price = double.parse(_priceController.text);
|
||||
final duration = int.tryParse(_durationController.text) ?? 30;
|
||||
|
||||
if (_isEditing) {
|
||||
final updatedService = widget.service!.copyWith(
|
||||
name: _nameController.text.trim(),
|
||||
price: price,
|
||||
durationMinutes: duration,
|
||||
);
|
||||
await _repository.updateService(updatedService);
|
||||
} else {
|
||||
await _repository.createService(
|
||||
name: _nameController.text.trim(),
|
||||
price: price,
|
||||
durationMinutes: duration,
|
||||
);
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(_isEditing
|
||||
? 'Serviço atualizado com sucesso!'
|
||||
: 'Serviço criado com sucesso!'
|
||||
),
|
||||
backgroundColor: AppColors.success,
|
||||
),
|
||||
);
|
||||
Navigator.pop(context, true);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Erro ao salvar: $e'),
|
||||
backgroundColor: AppColors.error,
|
||||
),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(_isEditing ? 'Editar Serviço' : 'Novo Serviço'),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
CustomTextField(
|
||||
label: 'Nome do Serviço',
|
||||
controller: _nameController,
|
||||
hint: 'Ex: Corte Degradê, Barba Terapia...',
|
||||
validator: (v) => v?.isEmpty == true ? 'Informe o nome' : null,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
CustomTextField(
|
||||
label: 'Preço',
|
||||
controller: _displayPriceController,
|
||||
hint: 'R\$ 0,00',
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: _formatPrice,
|
||||
validator: (v) => v?.isEmpty == true ? 'Informe o preço' : null,
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
CustomTextField(
|
||||
label: 'Duração Média (minutos)',
|
||||
controller: _durationController,
|
||||
keyboardType: TextInputType.number,
|
||||
hint: '30',
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
),
|
||||
|
||||
const SizedBox(height: 32),
|
||||
|
||||
LoadingButton(
|
||||
text: 'Salvar Serviço',
|
||||
onPressed: _saveService,
|
||||
isLoading: _isLoading,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user