Error tile
This commit is contained in:
parent
6e8100d4d7
commit
6016776d98
5 changed files with 135 additions and 28 deletions
5
TODO.md
5
TODO.md
|
|
@ -57,7 +57,6 @@
|
||||||
- A constructor without UUID will generate a new random UUID. A constructor with UUID will use the provided UUID.
|
- A constructor without UUID will generate a new random UUID. A constructor with UUID will use the provided UUID.
|
||||||
- The field should be used in database and everywhere instead of the name. The name is not unique anymore, but the UUID is.
|
- The field should be used in database and everywhere instead of the name. The name is not unique anymore, but the UUID is.
|
||||||
- This will be used to identify formulas, instead of the name. This way, we can have formulas with the same name but different UUIDs. The name is not unique anymore. Corpus will be a list of UUIDs, instead of a list of formulas. The corpus.getFormula() method will return the first formula with that name.
|
- This will be used to identify formulas, instead of the name. This way, we can have formulas with the same name but different UUIDs. The name is not unique anymore. Corpus will be a list of UUIDs, instead of a list of formulas. The corpus.getFormula() method will return the first formula with that name.
|
||||||
- [ ] When _FormulaScreenState._evaluateFormula() detect an error, instead of show an SnackBar, show a ExpansionTile with "⚠️ There were an error. Show details..." with the details of the exception. The ExpansionTile will be invisible if there is no error.
|
- [R] When _FormulaScreenState._evaluateFormula() detect an error, instead of show an SnackBar, show a ExpansionTile with "⚠️ There were an error. Show details..." with the details of the exception. The ExpansionTile will be invisible if there is no error.
|
||||||
- [R] When FormulaEditor._save formula, ensure formula is updated in the initial FormulaList
|
- [R] When FormulaEditor._save formula, ensure formula is updated in the initial FormulaList
|
||||||
- [ ] Refresh FormulaList each time it gets focus, so formulas are updated from corpus
|
- [R] In FormulaEditor, add a button to "Save as copy", additional to the existing button "Save". It doesnt matter if the copy has the same name as the original formula, since they are identified by a internal UUID.
|
||||||
- [ ] Investigate https://pub.dev/packages/quantity
|
|
||||||
|
|
|
||||||
|
|
@ -195,21 +195,21 @@ class _FormulaEditorState extends State<FormulaEditor> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final database = getDatabase();
|
final database = getDatabase();
|
||||||
|
|
||||||
// Update corpus in memory
|
// Update corpus in memory
|
||||||
widget.corpus.updateFormula(formula);
|
widget.corpus.updateFormula(formula);
|
||||||
|
|
||||||
// Update database
|
// Update database
|
||||||
final updated = await database.updateFormula(formula);
|
final updated = await database.updateFormula(formula);
|
||||||
|
|
||||||
if (!updated) {
|
if (!updated) {
|
||||||
// If formula wasn't found (e.g., name changed), add it as new
|
// If formula wasn't found (e.g., name changed), add it as new
|
||||||
await database.addFormula(formula);
|
await database.addFormula(formula);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the onSave callback if provided
|
// Call the onSave callback if provided
|
||||||
widget.onSave?.call(formula);
|
widget.onSave?.call(formula);
|
||||||
|
|
||||||
// Show success message
|
// Show success message
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
|
|
@ -223,6 +223,52 @@ class _FormulaEditorState extends State<FormulaEditor> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _saveFormulaAsCopy() async {
|
||||||
|
if (!_validateFormula()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final formula = _buildFormula();
|
||||||
|
if (formula == null) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final database = getDatabase();
|
||||||
|
|
||||||
|
// Create a copy with a new UUID
|
||||||
|
final formulaCopy = Formula(
|
||||||
|
name: '${formula.name} (Copy)',
|
||||||
|
description: formula.description,
|
||||||
|
input: formula.input,
|
||||||
|
output: formula.output,
|
||||||
|
d4rtCode: formula.d4rtCode,
|
||||||
|
tags: formula.tags,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add to corpus
|
||||||
|
widget.corpus.addFormula(formulaCopy);
|
||||||
|
|
||||||
|
// Add to database
|
||||||
|
await database.addFormula(formulaCopy);
|
||||||
|
|
||||||
|
// Call the onSave callback if provided
|
||||||
|
widget.onSave?.call(formulaCopy);
|
||||||
|
|
||||||
|
// Show success message
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text('Formula "${formulaCopy.name}" saved successfully!'),
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Navigate back to the formula list with the new formula
|
||||||
|
Navigator.pop(context, formulaCopy);
|
||||||
|
} catch (e, stack) {
|
||||||
|
print('Error saving formula copy: $e\n$stack');
|
||||||
|
_showErrorDialog('Error saving formula copy: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _showErrorDialog(String message) {
|
void _showErrorDialog(String message) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -254,6 +300,11 @@ class _FormulaEditorState extends State<FormulaEditor> {
|
||||||
onPressed: _testFormula,
|
onPressed: _testFormula,
|
||||||
tooltip: 'Test Formula',
|
tooltip: 'Test Formula',
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.copy),
|
||||||
|
onPressed: _saveFormulaAsCopy,
|
||||||
|
tooltip: 'Save as copy',
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.save),
|
icon: const Icon(Icons.save),
|
||||||
onPressed: _saveFormula,
|
onPressed: _saveFormula,
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,8 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
||||||
String? _result;
|
String? _result;
|
||||||
String? _selectedOutputUnit;
|
String? _selectedOutputUnit;
|
||||||
bool _isDescriptionExpanded = false; // Track description expansion state
|
bool _isDescriptionExpanded = false; // Track description expansion state
|
||||||
|
String? _errorMessage; // Track error message for expansion tile
|
||||||
|
bool _isErrorExpanded = false; // Track error expansion state
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -187,15 +189,16 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
||||||
_result = result?.toString();
|
_result = result?.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {});
|
setState(() {
|
||||||
|
_errorMessage = null; // Clear error on successful evaluation
|
||||||
|
});
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
errorHandler.notify(e, stack);
|
errorHandler.notify(e, stack);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
setState(() {
|
||||||
SnackBar(
|
_errorMessage = e.toString();
|
||||||
content: Text('Error: ${e.toString()}'),
|
_isErrorExpanded = true; // Auto-expand on error
|
||||||
backgroundColor: Theme.of(context).colorScheme.error,
|
_result = null;
|
||||||
),
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -236,6 +239,7 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
_buildDescriptionSection(),
|
_buildDescriptionSection(),
|
||||||
|
_buildErrorSection(),
|
||||||
_buildInputSection(),
|
_buildInputSection(),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
_buildOutputSection(),
|
_buildOutputSection(),
|
||||||
|
|
@ -294,6 +298,51 @@ class _FormulaScreenState extends State<FormulaScreen> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildErrorSection() {
|
||||||
|
if (_errorMessage == null) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.only(bottom: 16),
|
||||||
|
color: Theme.of(context).colorScheme.errorContainer,
|
||||||
|
child: ExpansionTile(
|
||||||
|
title: Text(
|
||||||
|
'⚠️ There were an error. Show details...',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
initiallyExpanded: _isErrorExpanded,
|
||||||
|
onExpansionChanged: (bool expanded) {
|
||||||
|
setState(() {
|
||||||
|
_isErrorExpanded = expanded;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: SelectableText(
|
||||||
|
_errorMessage!,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildInputSection() {
|
Widget _buildInputSection() {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
|
||||||
|
|
@ -76,25 +76,33 @@ class Corpus{
|
||||||
|
|
||||||
/// Updates a formula in the corpus
|
/// Updates a formula in the corpus
|
||||||
void updateFormula(Formula formula) {
|
void updateFormula(Formula formula) {
|
||||||
if (!_allFormulas.containsKey(formula.name)) {
|
if (!_allFormulas.containsKey(formula.uuid)) {
|
||||||
throw ArgumentError("Formula not found: ${formula.name}");
|
throw ArgumentError("Formula not found: ${formula.uuid}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove old tags
|
// Remove old tags
|
||||||
final oldFormula = _allFormulas[formula.name]!;
|
final oldFormula = _allFormulas[formula.uuid]!;
|
||||||
for (final tag in oldFormula.tags) {
|
for (final tag in oldFormula.tags) {
|
||||||
_tags[tag]?.removeWhere((f) => f.name == formula.name);
|
_tags[tag]?.removeWhere((f) => f.uuid == formula.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the formula
|
// Update the formula
|
||||||
_allFormulas[formula.name] = formula;
|
_allFormulas[formula.uuid] = formula;
|
||||||
|
|
||||||
// Add new tags
|
// Add new tags
|
||||||
for (final tag in formula.tags) {
|
for (final tag in formula.tags) {
|
||||||
_tags[tag]?.add(formula);
|
_tags[tag]?.add(formula);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a new formula to the corpus
|
||||||
|
void addFormula(Formula formula) {
|
||||||
|
_allFormulas[formula.uuid] = formula;
|
||||||
|
for (final tag in formula.tags) {
|
||||||
|
_tags[tag]?.add(formula);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final Multimap<String, String> _baseToUnits = Multimap.create();
|
final Multimap<String, String> _baseToUnits = Multimap.create();
|
||||||
final Map<String, UnitSpec> _allUnits = {};
|
final Map<String, UnitSpec> _allUnits = {};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,19 +37,19 @@ extension CorpusDatabaseExtension on FormulasDatabase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to update a formula in the database by name
|
// Method to update a formula in the database by UUID
|
||||||
Future<bool> updateFormula(models.Formula formula) async {
|
Future<bool> updateFormula(models.Formula formula) async {
|
||||||
final elements = await getAllFormulaElements();
|
final elements = await getAllFormulaElements();
|
||||||
|
|
||||||
for (final element in elements) {
|
for (final element in elements) {
|
||||||
try {
|
try {
|
||||||
final parsed = SetUtils.parseCorpusElements('[${element.elementText}]');
|
final parsed = SetUtils.parseCorpusElements('[${element.elementText}]');
|
||||||
if (parsed.isNotEmpty && parsed.first is models.Formula) {
|
if (parsed.isNotEmpty && parsed.first is models.Formula) {
|
||||||
final existingFormula = parsed.first as models.Formula;
|
final existingFormula = parsed.first as models.Formula;
|
||||||
if (existingFormula.name == formula.name) {
|
if (existingFormula.uuid == formula.uuid) {
|
||||||
// Update this element
|
// Update this element
|
||||||
await updateFormulaElement(
|
await updateFormulaElement(
|
||||||
element.id,
|
element.id,
|
||||||
formula.toStringLiteral()
|
formula.toStringLiteral()
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -60,7 +60,7 @@ extension CorpusDatabaseExtension on FormulasDatabase {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false; // Formula not found
|
return false; // Formula not found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue