xp_nix/xp_dashboard/lib/src/widgets/config_card.dart

220 lines
6.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../theme/app_theme.dart';
class ConfigCard extends StatefulWidget {
final Map<String, dynamic> config;
final Future<void> Function(Map<String, dynamic>) onConfigUpdate;
const ConfigCard({
super.key,
required this.config,
required this.onConfigUpdate,
});
@override
State<ConfigCard> createState() => _ConfigCardState();
}
class _ConfigCardState extends State<ConfigCard> {
final _formKey = GlobalKey<FormState>();
late TextEditingController _codingXpController;
late TextEditingController _researchXpController;
late TextEditingController _meetingXpController;
late TextEditingController _focusBonusController;
bool _isLoading = false;
@override
void initState() {
super.initState();
_initializeControllers();
}
void _initializeControllers() {
final xpRewards = widget.config['xp_rewards'] as Map<String, dynamic>? ?? {};
final baseMultipliers = xpRewards['base_multipliers'] as Map<String, dynamic>? ?? {};
final focusBonuses = xpRewards['focus_session_bonuses'] as Map<String, dynamic>? ?? {};
_codingXpController = TextEditingController(
text: (baseMultipliers['coding'] ?? 10).toString(),
);
_researchXpController = TextEditingController(
text: (baseMultipliers['research'] ?? 8).toString(),
);
_meetingXpController = TextEditingController(
text: (baseMultipliers['meeting'] ?? 3).toString(),
);
_focusBonusController = TextEditingController(
text: (focusBonuses['base_xp_per_minute'] ?? 5).toString(),
);
}
@override
void didUpdateWidget(ConfigCard oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.config != oldWidget.config) {
_initializeControllers();
}
}
@override
void dispose() {
_codingXpController.dispose();
_researchXpController.dispose();
_meetingXpController.dispose();
_focusBonusController.dispose();
super.dispose();
}
Future<void> _saveConfig() async {
if (!_formKey.currentState!.validate()) return;
setState(() {
_isLoading = true;
});
try {
final updates = {
'xp_rewards.base_multipliers.coding': int.parse(_codingXpController.text),
'xp_rewards.base_multipliers.research': int.parse(_researchXpController.text),
'xp_rewards.base_multipliers.meeting': int.parse(_meetingXpController.text),
'xp_rewards.focus_session_bonuses.base_xp_per_minute': int.parse(_focusBonusController.text),
};
await widget.onConfigUpdate(updates);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Configuration saved successfully!'),
backgroundColor: AppTheme.successColor,
),
);
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to save configuration: $e'),
backgroundColor: AppTheme.errorColor,
),
);
}
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.settings, color: AppTheme.primaryColor),
const SizedBox(width: 8),
Text(
'XP Configuration',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 16),
_ConfigField(
label: 'Coding XP Multiplier',
controller: _codingXpController,
icon: Icons.code,
),
const SizedBox(height: 12),
_ConfigField(
label: 'Research XP Multiplier',
controller: _researchXpController,
icon: Icons.search,
),
const SizedBox(height: 12),
_ConfigField(
label: 'Meeting XP Multiplier',
controller: _meetingXpController,
icon: Icons.groups,
),
const SizedBox(height: 12),
_ConfigField(
label: 'Focus Bonus (XP/min)',
controller: _focusBonusController,
icon: Icons.psychology,
),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _isLoading ? null : _saveConfig,
child: _isLoading
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: const Text('Save Configuration'),
),
),
],
),
),
),
);
}
}
class _ConfigField extends StatelessWidget {
final String label;
final TextEditingController controller;
final IconData icon;
const _ConfigField({
required this.label,
required this.controller,
required this.icon,
});
@override
Widget build(BuildContext context) {
return TextFormField(
controller: controller,
decoration: InputDecoration(
labelText: label,
prefixIcon: Icon(icon, color: AppTheme.primaryColor),
border: const OutlineInputBorder(),
),
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a value';
}
final intValue = int.tryParse(value);
if (intValue == null || intValue < 0) {
return 'Please enter a valid positive number';
}
return null;
},
);
}
}