From d02877b2879cfd8ce4f7785723dfe5bffb17b30d Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 11 Jun 2025 16:46:23 +0200 Subject: [PATCH 1/9] make dropdown searchable --- .../mih_package_components/mih_dropdwn_field.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Frontend/lib/mih_components/mih_package_components/mih_dropdwn_field.dart b/Frontend/lib/mih_components/mih_package_components/mih_dropdwn_field.dart index 2c22327f..d22f469e 100644 --- a/Frontend/lib/mih_components/mih_package_components/mih_dropdwn_field.dart +++ b/Frontend/lib/mih_components/mih_package_components/mih_dropdwn_field.dart @@ -98,6 +98,7 @@ class _MihDropdownFieldState extends State { Theme( data: Theme.of(context).copyWith( textSelectionTheme: TextSelectionThemeData( + cursorColor: theme.primaryColor(), selectionColor: theme.primaryColor().withValues(alpha: 0.3), selectionHandleColor: theme.primaryColor(), @@ -109,6 +110,8 @@ class _MihDropdownFieldState extends State { enableSearch: widget.enableSearch, enableFilter: widget.enableSearch, enabled: widget.editable, + textInputAction: TextInputAction.search, + requestFocusOnTap: true, menuHeight: 400, expandedInsets: EdgeInsets.zero, textStyle: TextStyle( From 4581480f2e67fee49585ad434111511cec5ea83a Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 11 Jun 2025 16:46:33 +0200 Subject: [PATCH 2/9] add currency object --- .../mih_components/mih_objects/currency.dart | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Frontend/lib/mih_components/mih_objects/currency.dart diff --git a/Frontend/lib/mih_components/mih_objects/currency.dart b/Frontend/lib/mih_components/mih_objects/currency.dart new file mode 100644 index 00000000..d87eacf7 --- /dev/null +++ b/Frontend/lib/mih_components/mih_objects/currency.dart @@ -0,0 +1,23 @@ +class Currency { + final String code; + final String name; + + const Currency({ + required this.code, + required this.name, + }); + + factory Currency.fromJson(Map json) { + return switch (json) { + { + "code": String code, + 'name': String name, + } => + Currency( + code: code, + name: name, + ), + _ => throw const FormatException('Failed to load Currency object.'), + }; + } +} From 37aa9a0ddc7829696cae113f879f6a5b9dea4058 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 11 Jun 2025 18:20:31 +0200 Subject: [PATCH 3/9] add tool to calc package --- Frontend/lib/mih_packages/calculator/mih_calculator.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Frontend/lib/mih_packages/calculator/mih_calculator.dart b/Frontend/lib/mih_packages/calculator/mih_calculator.dart index e64d05f5..37a1a105 100644 --- a/Frontend/lib/mih_packages/calculator/mih_calculator.dart +++ b/Frontend/lib/mih_packages/calculator/mih_calculator.dart @@ -1,6 +1,7 @@ import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_action.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tools.dart'; +import 'package:mzansi_innovation_hub/mih_packages/calculator/package_tools/currency_exchange_rate.dart'; import 'package:mzansi_innovation_hub/mih_packages/calculator/package_tools/simple_calc.dart'; import 'package:mzansi_innovation_hub/mih_packages/calculator/package_tools/tip_calc.dart'; import 'package:flutter/material.dart'; @@ -59,6 +60,11 @@ class _MIHCalculatorState extends State { _selectedIndex = 1; }); }; + temp[const Icon(Icons.currency_exchange)] = () { + setState(() { + _selectedIndex = 2; + }); + }; return MihPackageTools( tools: temp, selcetedIndex: _selectedIndex, @@ -69,6 +75,7 @@ class _MIHCalculatorState extends State { List toolBodies = [ const SimpleCalc(), const TipCalc(), + const CurrencyExchangeRate(), ]; return toolBodies; } @@ -77,6 +84,7 @@ class _MIHCalculatorState extends State { List toolTitles = [ "Simple Calculator", "Tip Calculator", + "Currency Exchange Rate", ]; return toolTitles; } From 91f223da686be4c923f9c058203a87e86d4b7134 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 11 Jun 2025 18:20:42 +0200 Subject: [PATCH 4/9] pt 2 --- Frontend/lib/mih_packages/calculator/mih_calculator.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Frontend/lib/mih_packages/calculator/mih_calculator.dart b/Frontend/lib/mih_packages/calculator/mih_calculator.dart index 37a1a105..60ecbb87 100644 --- a/Frontend/lib/mih_packages/calculator/mih_calculator.dart +++ b/Frontend/lib/mih_packages/calculator/mih_calculator.dart @@ -84,7 +84,7 @@ class _MIHCalculatorState extends State { List toolTitles = [ "Simple Calculator", "Tip Calculator", - "Currency Exchange Rate", + "Forex Calculator", ]; return toolTitles; } From a4f2f693d2b53046faea1412fe5e5bd211bb5fed Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 11 Jun 2025 18:21:00 +0200 Subject: [PATCH 5/9] add currency services file --- .../mih_currency_exchange_rate_services.dart | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Frontend/lib/mih_services/mih_currency_exchange_rate_services.dart diff --git a/Frontend/lib/mih_services/mih_currency_exchange_rate_services.dart b/Frontend/lib/mih_services/mih_currency_exchange_rate_services.dart new file mode 100644 index 00000000..091a2554 --- /dev/null +++ b/Frontend/lib/mih_services/mih_currency_exchange_rate_services.dart @@ -0,0 +1,73 @@ +import 'dart:convert'; + +import 'package:mzansi_innovation_hub/mih_components/mih_objects/currency.dart'; +import 'package:supertokens_flutter/http.dart' as http; + +class MihCurrencyExchangeRateServices { + static Future> getCurrencyObjectList() async { + final response = await http.get(Uri.parse( + "https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies.min.json")); + if (response.statusCode == 200) { + final Map jsonMap = json.decode(response.body); + List currencies = []; + jsonMap.forEach((code, dynamic nameValue) { + final String name = nameValue is String ? nameValue : 'Unknown Name'; + currencies.add(Currency(code: code, name: name)); + }); + currencies.sort((a, b) => a.name.compareTo(b.name)); + return currencies; + } else { + throw Exception('failed to fatch currencies'); + } + } + + static Future> getCurrencyCodeList() async { + final response = await http.get(Uri.parse( + "https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies.min.json")); + if (response.statusCode == 200) { + final Map jsonMap = json.decode(response.body); + List currencies = []; + jsonMap.forEach((code, dynamic nameValue) { + final String name = nameValue is String ? nameValue : 'Unknown Name'; + currencies.add("$code - $name"); + }); + currencies.sort(); + return currencies; + } else { + throw Exception('failed to fatch currencies'); + } + } + + static Future getCurrencyExchangeValue( + String fromCurrencyCode, + String toCurrencyCode, + ) async { + final response = await http.get(Uri.parse( + "https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/$fromCurrencyCode.min.json")); + if (response.statusCode == 200) { + final Map jsonResponse = json.decode(response.body); + final Map? baseCurrencyData = + jsonResponse[fromCurrencyCode]; + + if (baseCurrencyData != null) { + // Access the value for the target currency (e.g., "usd") + final dynamic rateValue = baseCurrencyData[toCurrencyCode]; + + if (rateValue is num) { + // Check if it's a number (int or double) + return rateValue.toDouble(); + } else { + // Handle cases where the rate might not be a number or is missing + print( + 'Warning: Rate for $toCurrencyCode in $fromCurrencyCode is not a number or missing.'); + return 0; + } + } else { + throw Exception( + 'Base currency "$fromCurrencyCode" data not found in response.'); + } + } else { + throw Exception('failed to fatch currencies'); + } + } +} From 73ff46faa7138c2fa372139f18493953c99b7d1b Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 11 Jun 2025 18:29:02 +0200 Subject: [PATCH 6/9] create exchange rate cacl tool --- .../package_tools/currency_exchange_rate.dart | 301 ++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 Frontend/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart diff --git a/Frontend/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart b/Frontend/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart new file mode 100644 index 00000000..0b077a11 --- /dev/null +++ b/Frontend/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart @@ -0,0 +1,301 @@ +import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_button.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_dropdwn_field.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_form.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tool_body.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_window.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_text_form_field.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_alert_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_currency_exchange_rate_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.dart'; + +class CurrencyExchangeRate extends StatefulWidget { + const CurrencyExchangeRate({super.key}); + + @override + State createState() => _CurrencyExchangeRateState(); +} + +class _CurrencyExchangeRateState extends State { + final _formKey = GlobalKey(); + final TextEditingController _fromCurrencyController = TextEditingController(); + final TextEditingController _toCurrencyController = TextEditingController(); + final TextEditingController _fromAmountController = TextEditingController(); + final TextEditingController _toAmountController = TextEditingController(); + late Future> availableCurrencies; + + Future submitForm() async { + String fromCurrencyCode = _fromCurrencyController.text.split(" - ")[0]; + String toCurrencyCode = _toCurrencyController.text.split(" - ")[0]; + double exchangeRate = 0; + await MihCurrencyExchangeRateServices.getCurrencyExchangeValue( + fromCurrencyCode, toCurrencyCode) + .then((amount) { + exchangeRate = amount; + }); + double exchangeValue = + double.parse(_fromAmountController.text) * exchangeRate; + + print( + "${_fromAmountController.text} | $fromCurrencyCode\n$exchangeValue | $toCurrencyCode"); + displayResult(_fromAmountController.text, fromCurrencyCode, exchangeValue, + toCurrencyCode); + } + + void clearInput() { + _fromCurrencyController.clear(); + _fromAmountController.clear(); + _toCurrencyController.clear(); + _toAmountController.clear(); + } + + void displayResult(String amount, String fromCurrencyCode, + double exchangeValue, String toCurrencyCode) { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => MihPackageWindow( + fullscreen: false, + windowTitle: "Calculation Results", + onWindowTapClose: () { + Navigator.pop(context); + }, + windowBody: Column( + children: [ + Icon( + Icons.currency_exchange, + size: 150, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + child: Text( + amount, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: Text( + fromCurrencyCode.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + ), + ], + ), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + child: Text( + exchangeValue.toStringAsFixed(2), + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: Text( + toCurrencyCode.toUpperCase(), + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + ), + ], + ), + ], + ), + ), + ); + } + + @override + void dispose() { + super.dispose(); + _fromCurrencyController.dispose(); + _fromAmountController.dispose(); + _toCurrencyController.dispose(); + _toAmountController.dispose(); + } + + @override + void initState() { + super.initState(); + availableCurrencies = MihCurrencyExchangeRateServices.getCurrencyCodeList(); + } + + @override + Widget build(BuildContext context) { + double screenWidth = MediaQuery.of(context).size.width; + return MihPackageToolBody( + borderOn: false, + innerHorizontalPadding: 10, + bodyItem: getBody(screenWidth), + ); + } + + Widget getBody(double width) { + return FutureBuilder( + future: availableCurrencies, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Mihloadingcircle(); + } else if (snapshot.connectionState == ConnectionState.done) { + return MihSingleChildScroll( + child: Padding( + padding: MzanziInnovationHub.of(context)!.theme.screenType == + "desktop" + ? EdgeInsets.symmetric(horizontal: width * 0.2) + : EdgeInsets.symmetric(horizontal: width * 0.075), + child: Column( + children: [ + MihForm( + formKey: _formKey, + formFields: [ + MihTextFormField( + fillColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + controller: _fromAmountController, + multiLineInput: false, + requiredText: true, + hintText: "Currency Amount", + numberMode: true, + validator: (value) { + return MihValidationServices().isEmpty(value); + }, + ), + const SizedBox(height: 10), + MihDropdownField( + controller: _fromCurrencyController, + hintText: "From", + dropdownOptions: snapshot.data!, + editable: true, + enableSearch: true, + validator: (value) { + return MihValidationServices().isEmpty(value); + }, + requiredText: true, + ), + const SizedBox(height: 10), + MihDropdownField( + controller: _toCurrencyController, + hintText: "To", + dropdownOptions: snapshot.data!, + editable: true, + enableSearch: true, + validator: (value) { + return MihValidationServices().isEmpty(value); + }, + requiredText: true, + ), + const SizedBox(height: 25), + Center( + child: Wrap( + spacing: 10, + runSpacing: 10, + children: [ + MihButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + submitForm(); + FocusScope.of(context) + .requestFocus(FocusNode()); + } else { + MihAlertServices() + .formNotFilledCompletely(context); + } + }, + buttonColor: MzanziInnovationHub.of(context)! + .theme + .successColor(), + width: 300, + child: Text( + "Calculate", + style: TextStyle( + color: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + MihButton( + onPressed: () { + clearInput(); + }, + buttonColor: MzanziInnovationHub.of(context)! + .theme + .errorColor(), + width: 300, + child: Text( + "Clear", + style: TextStyle( + color: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ), + ], + ), + ], + ), + ), + ); + } else { + return Center( + child: Text( + "Error pulling Currency Exchange Data.", + style: TextStyle( + fontSize: 25, + color: MzanziInnovationHub.of(context)!.theme.errorColor()), + textAlign: TextAlign.center, + ), + ); + } + }); + } +} From 55fd214e03e085be6c2c689a6004b41d36dc027c Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Thu, 12 Jun 2025 10:17:24 +0200 Subject: [PATCH 7/9] fix decimal in number only text field --- .../mih_package_components/mih_text_form_field.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Frontend/lib/mih_components/mih_package_components/mih_text_form_field.dart b/Frontend/lib/mih_components/mih_package_components/mih_text_form_field.dart index 7675561b..b63af3a4 100644 --- a/Frontend/lib/mih_components/mih_package_components/mih_text_form_field.dart +++ b/Frontend/lib/mih_components/mih_package_components/mih_text_form_field.dart @@ -166,10 +166,14 @@ class _MihTextFormFieldState extends State { maxLines: widget.passwordMode == true ? 1 : null, readOnly: widget.readOnly ?? false, keyboardType: widget.numberMode == true - ? TextInputType.number + ? const TextInputType.numberWithOptions( + decimal: true) : null, inputFormatters: widget.numberMode == true - ? [FilteringTextInputFormatter.digitsOnly] + ? [ + FilteringTextInputFormatter.allow( + RegExp(r'^\d*\.?\d*')) + ] : null, style: TextStyle( color: widget.inputColor, From 00b2fba27104a3dec98f0a451f334fe91a9b7976 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Thu, 12 Jun 2025 11:16:36 +0200 Subject: [PATCH 8/9] Add date of values --- .../package_tools/currency_exchange_rate.dart | 25 +++++++++++++++---- .../mih_currency_exchange_rate_services.dart | 14 +++++------ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/Frontend/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart b/Frontend/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart index 0b077a11..762dbc5f 100644 --- a/Frontend/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart +++ b/Frontend/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart @@ -30,19 +30,21 @@ class _CurrencyExchangeRateState extends State { Future submitForm() async { String fromCurrencyCode = _fromCurrencyController.text.split(" - ")[0]; String toCurrencyCode = _toCurrencyController.text.split(" - ")[0]; + List dateValue = []; double exchangeRate = 0; await MihCurrencyExchangeRateServices.getCurrencyExchangeValue( fromCurrencyCode, toCurrencyCode) .then((amount) { - exchangeRate = amount; + dateValue = amount; }); + exchangeRate = double.parse(dateValue[1]); double exchangeValue = double.parse(_fromAmountController.text) * exchangeRate; print( - "${_fromAmountController.text} | $fromCurrencyCode\n$exchangeValue | $toCurrencyCode"); - displayResult(_fromAmountController.text, fromCurrencyCode, exchangeValue, - toCurrencyCode); + "Date: ${dateValue[0]}\n${_fromAmountController.text} | $fromCurrencyCode\n$exchangeValue | $toCurrencyCode"); + displayResult(dateValue[0], _fromAmountController.text, fromCurrencyCode, + exchangeValue, toCurrencyCode); } void clearInput() { @@ -52,7 +54,7 @@ class _CurrencyExchangeRateState extends State { _toAmountController.clear(); } - void displayResult(String amount, String fromCurrencyCode, + void displayResult(String date, String amount, String fromCurrencyCode, double exchangeValue, String toCurrencyCode) { showDialog( context: context, @@ -71,6 +73,19 @@ class _CurrencyExchangeRateState extends State { color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), ), const SizedBox(height: 20), + FittedBox( + child: Text( + "Values as at $date", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ), + const SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ diff --git a/Frontend/lib/mih_services/mih_currency_exchange_rate_services.dart b/Frontend/lib/mih_services/mih_currency_exchange_rate_services.dart index 091a2554..2be8748e 100644 --- a/Frontend/lib/mih_services/mih_currency_exchange_rate_services.dart +++ b/Frontend/lib/mih_services/mih_currency_exchange_rate_services.dart @@ -38,7 +38,7 @@ class MihCurrencyExchangeRateServices { } } - static Future getCurrencyExchangeValue( + static Future> getCurrencyExchangeValue( String fromCurrencyCode, String toCurrencyCode, ) async { @@ -48,19 +48,19 @@ class MihCurrencyExchangeRateServices { final Map jsonResponse = json.decode(response.body); final Map? baseCurrencyData = jsonResponse[fromCurrencyCode]; - + final List dateValue = []; if (baseCurrencyData != null) { - // Access the value for the target currency (e.g., "usd") final dynamic rateValue = baseCurrencyData[toCurrencyCode]; + final String date = jsonResponse["date"]; if (rateValue is num) { - // Check if it's a number (int or double) - return rateValue.toDouble(); + dateValue.add(date); + dateValue.add(rateValue.toString()); + return dateValue; } else { - // Handle cases where the rate might not be a number or is missing print( 'Warning: Rate for $toCurrencyCode in $fromCurrencyCode is not a number or missing.'); - return 0; + return ["Error", "0"]; } } else { throw Exception( From 4ec73bf30a8bf4a85499b9f948b51b8e9cb2526c Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Thu, 12 Jun 2025 11:45:51 +0200 Subject: [PATCH 9/9] disclaimer notice --- .../package_tools/currency_exchange_rate.dart | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/Frontend/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart b/Frontend/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart index 762dbc5f..7982cc89 100644 --- a/Frontend/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart +++ b/Frontend/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart @@ -1,3 +1,4 @@ +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_button.dart'; @@ -157,6 +158,97 @@ class _CurrencyExchangeRateState extends State { ); } + void displayDisclaimer() { + final String companyName = 'Mzansi Innovation Hub'; + + showDialog( + context: context, + builder: (context) => MihPackageWindow( + fullscreen: false, + windowTitle: "Disclaimer Notice", + onWindowTapClose: () { + Navigator.pop(context); + }, + windowBody: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Main Title + + Text( + 'Disclaimer of Warranty and Limitation of Liability for Forex Calculator', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 25, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + fontWeight: FontWeight.bold, + ), + ), + + const SizedBox(height: 24.0), + + // First Paragraph - using RichText to bold "the Tool" + _buildRichText( + 'The Forex Calculator feature ("', + 'the Tool', + '") is provided on an "as is" and "as available" basis. It is an experimental feature and is intended solely for informational and illustrative purposes.', + ), + const SizedBox(height: 16.0), + + // Second Paragraph + Text( + '$companyName makes no representations or warranties of any kind, express or implied, as to the accuracy, completeness, reliability, or suitability of the information and calculations generated by the Tool. All exchange rates and results are estimates and are subject to change without notice.', + style: TextStyle( + fontSize: 15, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + fontWeight: FontWeight.normal, + ), + ), + const SizedBox(height: 16.0), + + // Third Paragraph + Text( + 'The information provided by the Tool should not be construed as financial, investment, trading, or any other form of advice. You should not make any financial decisions based solely on the output of this Tool. We expressly recommend that you seek independent professional advice and verify all data with a qualified financial advisor and/or through alternative, reliable market data sources before executing any foreign exchange transactions.', + style: TextStyle( + fontSize: 15, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + fontWeight: FontWeight.normal, + ), + ), + const SizedBox(height: 16.0), + + // Fourth Paragraph + Text( + 'By using the Tool, you agree that $companyName, its affiliates, directors, and employees shall not be held liable for any direct, indirect, incidental, special, consequential, or exemplary damages, including but not limited to, damages for loss of profits, goodwill, use, data, or other intangible losses, resulting from: (i) the use or the inability to use the Tool; (ii) any inaccuracies, errors, or omissions in the Tool\'s calculations or data; or (iii) any reliance placed by you on the information provided by the Tool.', + style: TextStyle( + fontSize: 15, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + fontWeight: FontWeight.normal, + ), + ), + ], + ), + ), + ); + } + + Widget _buildRichText(String start, String bold, String end) { + return RichText( + text: TextSpan( + style: TextStyle( + fontSize: 15, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + fontWeight: FontWeight.normal, + ), + children: [ + TextSpan(text: start), + TextSpan( + text: bold, style: const TextStyle(fontWeight: FontWeight.bold)), + TextSpan(text: end), + ], + ), + ); + } + @override void dispose() { super.dispose(); @@ -240,6 +332,38 @@ class _CurrencyExchangeRateState extends State { }, requiredText: true, ), + const SizedBox(height: 15), + RichText( + textAlign: TextAlign.left, + text: TextSpan( + style: TextStyle( + fontSize: 15, + color: MzanziInnovationHub.of(context)! + .theme + .errorColor(), + ), + children: [ + const TextSpan( + text: + "* Experimental Feature: Please review "), + TextSpan( + text: "Diclaimer", + style: TextStyle( + decoration: TextDecoration.underline, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + fontWeight: FontWeight.bold, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + displayDisclaimer(); + }, + ), + const TextSpan(text: " before use."), + ], + ), + ), const SizedBox(height: 25), Center( child: Wrap(