171 lines
5.4 KiB
Dart
171 lines
5.4 KiB
Dart
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,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|