diff --git a/.DS_Store b/.DS_Store index bb29be06..1cf57d2e 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index d6c36b79..548552a8 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ Mzansi_Mail/ .venv google-chrome-stable_current_amd64.deb .env -Frontend/android/app/.cxx/ \ No newline at end of file +Frontend/android/app/.cxx/ +.DS_Store \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index e21f3f8c..4a7c8618 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,6 +11,17 @@ "type": "dart", "program": "lib/main_dev.dart" }, + { + "name": "Debug (web)", + "cwd": "mih_ui", + "request": "launch", + "type": "dart", + "program": "lib/main_dev.dart", + "args": [ + "--web-port", + "1995" + ] + }, { "name": "Profile", "cwd": "mih_ui", diff --git a/docker-compose.yml b/docker-compose.yml index 5bc17734..3f209328 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,37 +4,6 @@ networks: driver: bridge #============== MIH Containers ==================================================================== services: - #============== Nginx Proxy Server Old ==================================================================== - # nginx: - # container_name: nginx - # restart: unless-stopped - # image: nginx - # ports: - # - 80:80 - # - 443:443 - # volumes: - # - ./nginx/nginx.conf:/etc/nginx/nginx.conf - # - certbotConf:/etc/letsencrypt - # - certbotChall:/var/www/certbot - # depends_on: - # - mih-ux - # networks: - # - mih-network - # profiles: [ 'prod' ] - #============== Cert Bot Old ==================================================================== - # certbot: - # image: certbot/certbot - # container_name: certbot - # volumes: - # - certbotConf:/etc/letsencrypt - # - certbotChall:/var/www/certbot - # #command: certonly --test-cert --webroot -w /var/www/certbot --force-renewal --email yasienmeth@gmail.com -d mzansi-innovation-hub.co.za -d www.mzansi-innovation-hub.co.za --agree-tos - # command: certonly --webroot -w /var/www/certbot --force-renewal --email ${CERTBOT_EMAIL} -d ${CERTBOT_APP_DOMAIN} -d ${CERTBOT_API_DOMAIN} -d ${CERTBOT_STORAGE_DOMAIN} -d ${CERTBOT_MONITOR_DOMAIN} -d ${CERTBOT_AI_DOMAIN} --agree-tos - # networks: - # - mih-network - # depends_on: - # - nginx - # profiles: [ 'withCert' ] #============== Nginx Proxy Manager ==================================================================== mih-nginx: container_name: mih-nginx @@ -43,7 +12,7 @@ services: ports: - '80:80' # Public HTTP - '443:443' # Public HTTPS - - '81:81' # Admin Web Port + - '127.0.0.1:81:81' # Admin Web Port volumes: - ./mih_nginx/data:/data - ./mih_nginx/letsencrypt:/etc/letsencrypt @@ -69,8 +38,8 @@ services: - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro ports: - - "3000:3000" - - "222:22" + - "127.0.0.1:3000:3000" + - "127.0.0.1:222:22" depends_on: mih-gitea-db: condition: service_healthy @@ -99,7 +68,7 @@ services: depends_on: - mih-db ports: - - 3567:3567 + - '127.0.0.1:3567:3567' environment: REFRESH_TOKEN_VALIDITY: '604800' ACCESS_TOKEN_VALIDITY: '86400' @@ -125,7 +94,7 @@ services: image: wordpress restart: always ports: - - 8081:80 + - '127.0.0.1:8081:80' environment: WORDPRESS_DB_HOST: mih-wp-db WORDPRESS_DB_USER: ${WP_SQL_USER} @@ -154,7 +123,7 @@ services: build: context: ./mih_ui ports: - - "83:83" + - "127.0.0.1:83:83" networks: - mih-network depends_on: @@ -166,7 +135,7 @@ services: target: builder container_name: mih-api-hub ports: - - 8080:80 + - "127.0.0.1:8080:80" volumes: - ./mih_api_hub:/app networks: @@ -187,7 +156,7 @@ services: networks: - mih-network ports: - - '3306:3306' + - '127.0.0.1:3306:3306' volumes: - ./mih_db:/var/lib/mysql #============== PHP My Admin ==================================================================== @@ -213,8 +182,8 @@ services: hostname: mih-minio image: minio/minio ports: - - '9000:9000' - - '9001:9001' + - '127.0.0.1:9000:9000' + - '127.0.0.1:9001:9001' volumes: - './mih_minio:/data' environment: @@ -228,7 +197,7 @@ services: container_name: mih-monitor image: portainer/portainer-ce:2.20.3 ports: - - 9444:9443 + - '127.0.0.1:9444:9443' volumes: - ./mih_monitor/data:/data - /var/run/docker.sock:/var/run/docker.sock @@ -240,7 +209,7 @@ services: container_name: mih-ai image: ollama/ollama:latest ports: - - 11434:11434 + - '127.0.0.1:11434:11434' volumes: - ./mih_ai/ollama/ollama:/root/.ollama pull_policy: always diff --git a/mih_api_hub/.gitignore b/mih_api_hub/.gitignore index 156d46ab..fb1147e5 100644 --- a/mih_api_hub/.gitignore +++ b/mih_api_hub/.gitignore @@ -1,3 +1,4 @@ .env __pycache__/ -temp*.pdf \ No newline at end of file +temp*.pdf +.DS_Store \ No newline at end of file diff --git a/mih_api_hub/__init__.py b/mih_api_hub/__init__.py index 51d9c310..e69de29b 100644 --- a/mih_api_hub/__init__.py +++ b/mih_api_hub/__init__.py @@ -1,28 +0,0 @@ -# from supertokens_python import init, InputAppInfo, SupertokensConfig -# from supertokens_python.recipe import emailpassword, session, dashboard - -# init( -# app_info=InputAppInfo( -# app_name="MIH_API_HUB", -# api_domain="http://localhost:8080/", -# website_domain="http://mzansi-innovation-hub.co.za", -# api_base_path="/auth", -# website_base_path="/auth" -# ), -# supertokens_config=SupertokensConfig( -# # https://try.supertokens.com is for demo purposes. Replace this with the address of your core instance (sign up on supertokens.com), or self host a core. -# connection_uri="supertokens:3567/", -# api_key="leatucczyixqwkqqdrhayiwzeofkltds" -# ), -# framework='fastapi', -# recipe_list=[ -# # SuperTokens.init(), -# session.init(), # initializes session features -# emailpassword.init(), -# dashboard.init(admins=[ -# "yasienmeth@gmail.com", -# ], -# ) -# ], -# mode='wsgi' # use wsgi instead of asgi if you are running using gunicorn -# ) \ No newline at end of file diff --git a/mih_api_hub/main.py b/mih_api_hub/main.py index bb107d42..3d1151f1 100644 --- a/mih_api_hub/main.py +++ b/mih_api_hub/main.py @@ -34,14 +34,20 @@ from supertokens_python.recipe.session.framework.fastapi import verify_session from supertokens_python.recipe.emailverification import EmailVerificationClaim from supertokens_python.recipe.session import SessionContainer +import os +from dotenv import load_dotenv + +load_dotenv() +st_api_key = os.getenv("SUPERTOKENS_API_KEY") origins = [ "http://localhost", "http://localhost:80", + "http://localhost:1995", "http://localhost:8080", "http://MIH-API-Hub:80", "http://MIH-API-Hub", "http://api.mzansi-innovation-hub.co.za", - "*", + "http://app.mzansi-innovation-hub.co.za", ] init( @@ -55,7 +61,7 @@ init( supertokens_config=SupertokensConfig( # https://try.supertokens.com is for demo purposes. Replace this with the address of your core instance (sign up on supertokens.com), or self host a core. connection_uri="http://mih-supertokens:3567/", - api_key="leatucczyixqwkqqdrhayiwzeofkltds" + api_key=st_api_key ), framework='fastapi', recipe_list=[ diff --git a/mih_api_hub/requirements.txt b/mih_api_hub/requirements.txt index 1cca0eba..f3fd0d07 100644 --- a/mih_api_hub/requirements.txt +++ b/mih_api_hub/requirements.txt @@ -9,5 +9,5 @@ watchfiles python-multipart python-dotenv xlrd -supertokens-python +supertokens-python==0.24.0 sniffio \ No newline at end of file diff --git a/mih_ui/.gitignore b/mih_ui/.gitignore index 9fc6e4ac..5af64235 100644 --- a/mih_ui/.gitignore +++ b/mih_ui/.gitignore @@ -1,3 +1,5 @@ +.env + # Miscellaneous *.class *.log diff --git a/mih_ui/Dockerfile b/mih_ui/Dockerfile index 1664c3c1..fe4435fc 100644 --- a/mih_ui/Dockerfile +++ b/mih_ui/Dockerfile @@ -1,48 +1,36 @@ -# Install Operating system and dependencies -#FROM ubuntu:22.04 +# --- STAGE 1: The Builder --- FROM debian:latest AS build-env -#ENV DEBIAN_FRONTEND=noninteractive +# Install necessary dependencies for Flutter +RUN apt-get update && apt-get install -y \ + curl git wget unzip libglu1-mesa fonts-droid-fallback python3 \ + && rm -rf /var/lib/apt/lists/* -RUN apt-get update --fix-missing - -RUN apt-get install -y curl git wget unzip gdb libstdc++6 libglu1-mesa fonts-droid-fallback -# RUN apt-get install -y curl git wget unzip libgconf-2-4 gdb libstdc++6 libglu1-mesa fonts-droid-fallback -RUN apt-get install python3 -y - -# download Flutter SDK from Flutter Github repo +# Clone Flutter SDK RUN git clone -b flutter-3.32-candidate.0 https://github.com/flutter/flutter.git /usr/local/flutter -# RUN git clone -b stable https://github.com/flutter/flutter.git /usr/local/flutter - -# Set flutter environment path ENV PATH="/usr/local/flutter/bin:/usr/local/flutter/bin/cache/dart-sdk/bin:${PATH}" -#ENV PATH "$PATH:/home/developer/flutter/bin" - -RUN flutter doctor -v - -# Enable flutter web -RUN flutter channel flutter-3.32-candidate.0 -# RUN flutter channel stable -RUN flutter config --enable-web - -# Copy files to container and build -RUN mkdir /app/ -COPY . /app/ +# Build the Flutter web app WORKDIR /app -# RUN flutter upgrade +COPY . . +RUN flutter config --enable-web RUN flutter build web --release -t ./lib/main_prod.dart -# RUN flutter build web --release --web-renderer canvaskit -t ./lib/main_prod.dart +# --- STAGE 2: The Final Production Image --- +FROM nginx:alpine -# RUN cd .. +# Copy built files from the first stage +COPY --from=build-env /app/build/web /usr/share/nginx/html -# WORKDIR /app/build/web/ +# Create the Nginx config inside the Dockerfile to handle SPA routing +RUN echo 'server { \ + listen 83; \ + location / { \ + root /usr/share/nginx/html; \ + index index.html; \ + try_files $uri $uri/ /index.html; \ + } \ + }' > /etc/nginx/conf.d/default.conf EXPOSE 83 - -RUN ["chmod", "+x", "/app/server/server.sh"] - -ENTRYPOINT [ "/app/server/server.sh"] - -# RUN ["python3", "-u", "/app/server/MIH_web_server.py"] \ No newline at end of file +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/mih_ui/android/app/src/main/res/drawable/mzansi_directory.xml b/mih_ui/android/app/src/main/res/drawable/mzansi_directory.xml index 5cd49d32..1ee03963 100644 --- a/mih_ui/android/app/src/main/res/drawable/mzansi_directory.xml +++ b/mih_ui/android/app/src/main/res/drawable/mzansi_directory.xml @@ -5,7 +5,6 @@ android:height="23.53125dp" android:width="24dp"> + android:fillColor="@color/mih_icon_foreground"/> diff --git a/mih_ui/android/app/src/main/res/values/arrays.xml b/mih_ui/android/app/src/main/res/values/arrays.xml index fb766383..669016d1 100644 --- a/mih_ui/android/app/src/main/res/values/arrays.xml +++ b/mih_ui/android/app/src/main/res/values/arrays.xml @@ -5,5 +5,6 @@ @drawable/mzansi_wallet_sc @drawable/mzansi_ai_sc @drawable/mih_calculator_sc + @drawable/mzansi_directory_sc \ No newline at end of file diff --git a/mih_ui/lib/main_dev.dart b/mih_ui/lib/main_dev.dart index 437c62b9..e1c394dd 100644 --- a/mih_ui/lib/main_dev.dart +++ b/mih_ui/lib/main_dev.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:flutter_web_plugins/url_strategy.dart' if (dart.library.html) 'package:flutter_web_plugins/url_strategy.dart'; @@ -42,6 +43,7 @@ void main() async { debugPrint('APP INSTALLED!'); }); final GoRouter appRouter = MihGoRouter().mihRouter; + await dotenv.load(fileName: ".env"); FlutterNativeSplash.remove(); runApp(MzansiInnovationHub( router: appRouter, diff --git a/mih_ui/lib/main_prod.dart b/mih_ui/lib/main_prod.dart index c8ce2935..b7026b50 100644 --- a/mih_ui/lib/main_prod.dart +++ b/mih_ui/lib/main_prod.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:flutter_web_plugins/url_strategy.dart' if (dart.library.html) 'package:flutter_web_plugins/url_strategy.dart'; @@ -33,6 +34,7 @@ void main() async { debugPrint('APP INSTALLED!'); }); final GoRouter appRouter = MihGoRouter().mihRouter; + await dotenv.load(fileName: ".env"); FlutterNativeSplash.remove(); runApp(MzansiInnovationHub( router: appRouter, diff --git a/mih_ui/lib/mih_config/mih_env.dart b/mih_ui/lib/mih_config/mih_env.dart index e9dcdd2f..9af57766 100644 --- a/mih_ui/lib/mih_config/mih_env.dart +++ b/mih_ui/lib/mih_config/mih_env.dart @@ -20,13 +20,13 @@ abstract class AppEnviroment { switch (env) { case Enviroment.dev: { - if (kIsWeb) { + if (kIsWeb || Platform.isIOS || Platform.isLinux) { //================= Web Dev Urls ================= baseAppUrl = "http://localhost:80"; baseApiUrl = "http://localhost:8080"; baseFileUrl = "http://localhost:9000"; baseAiUrl = "http://localhost:11434"; - bannerAdUnitId = 'ca-app-pub-3940256099942544/2435281174'; + bannerAdUnitId = 'ca-app-pub-3940256099942544/2435281174'; // IOS ID break; } else if (Platform.isAndroid) { //================= Android Dev Urls ================= @@ -35,14 +35,6 @@ abstract class AppEnviroment { baseFileUrl = "http://10.0.2.2:9000"; baseAiUrl = "http://10.0.2.2:11434"; bannerAdUnitId = 'ca-app-pub-3940256099942544/9214589741'; - } else { - //================= Web & iOS Dev Urls ================= - baseAppUrl = "http://localhost:80"; - baseApiUrl = "http://localhost:8080"; - baseFileUrl = "http://localhost:9000"; - baseAiUrl = "http://localhost:11434"; - bannerAdUnitId = 'ca-app-pub-3940256099942544/2435281174'; - break; } } case Enviroment.prod: diff --git a/mih_ui/lib/mih_config/mih_theme.dart b/mih_ui/lib/mih_config/mih_theme.dart index dd7ea4c1..1e4870cc 100644 --- a/mih_ui/lib/mih_config/mih_theme.dart +++ b/mih_ui/lib/mih_config/mih_theme.dart @@ -9,7 +9,7 @@ class MihTheme { late String loadingAssetText; late TargetPlatform platform; bool kIsWeb = const bool.fromEnvironment('dart.library.js_util'); - String latestVersion = "1.2.5"; + String latestVersion = "1.2.6"; MihTheme() { mode = "Dark"; } @@ -98,6 +98,12 @@ class MihTheme { return "Android"; } else if (platform == TargetPlatform.iOS) { return "iOS"; + } else if (platform == TargetPlatform.linux) { + return "Linux"; + } else if (platform == TargetPlatform.macOS) { + return "macOS"; + } else if (platform == TargetPlatform.windows) { + return "Windows"; } } return "Other"; diff --git a/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_one.dart b/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_one.dart index 3406a341..95de0348 100644 --- a/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_one.dart +++ b/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_one.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:country_code_picker/country_code_picker.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; @@ -161,6 +163,7 @@ class _PackageToolOneState extends State { return Stack( children: [ MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: MzansiInnovationHub.of(context)!.theme.screenType == "desktop" @@ -450,7 +453,7 @@ class _PackageToolOneState extends State { ), ], ), - MihBannerAd(), + if (Platform.isAndroid || Platform.isIOS) MihBannerAd(), const SizedBox(height: 10), Divider( color: MihColors.getSecondaryColor( @@ -773,6 +776,7 @@ class _PackageToolOneState extends State { MihCircleAvatar( imageFile: imagePreview, width: 50, + expandable: true, editable: false, fileNameController: _fileNameController, userSelectedfile: file, diff --git a/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_three.dart b/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_three.dart index 1682426a..c91f3299 100644 --- a/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_three.dart +++ b/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_three.dart @@ -111,6 +111,7 @@ class _PackageToolThreeState extends State { return Stack( children: [ MihSingleChildScroll( + scrollbarOn: true, child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ diff --git a/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_two.dart b/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_two.dart index f6713a51..181f34b5 100644 --- a/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_two.dart +++ b/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_two.dart @@ -24,6 +24,7 @@ class _PackageToolTwoState extends State { Widget getBody() { return MihSingleChildScroll( + scrollbarOn: true, child: Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, diff --git a/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_zero.dart b/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_zero.dart index 5b9cf66a..7bf24130 100644 --- a/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_zero.dart +++ b/mih_ui/lib/mih_package_components/Example/package_tools/package_tool_zero.dart @@ -25,6 +25,7 @@ class _PackageToolZeroState extends State { Widget getBody() { return MihSingleChildScroll( + scrollbarOn: true, child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ diff --git a/mih_ui/lib/mih_package_components/mih_business_info_card_v2.dart b/mih_ui/lib/mih_package_components/mih_business_info_card_v2.dart new file mode 100644 index 00000000..2c9684d7 --- /dev/null +++ b/mih_ui/lib/mih_package_components/mih_business_info_card_v2.dart @@ -0,0 +1,659 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_objects/bookmarked_business.dart'; +import 'package:mzansi_innovation_hub/mih_objects/business.dart'; +import 'package:mzansi_innovation_hub/mih_objects/business_review.dart'; +import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart'; +import 'package:mzansi_innovation_hub/mih_package_components/mih_icons.dart'; +import 'package:mzansi_innovation_hub/mih_package_components/mih_package_window.dart'; +import 'package:mzansi_innovation_hub/mih_providers/mzansi_directory_provider.dart'; +import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/components/mih_add_bookmark_alert.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/components/mih_delete_bookmark_alert.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart'; +import 'package:mzansi_innovation_hub/mih_providers/mzansi_profile_provider.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_alert_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_business_details_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_file_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_mzansi_directory_services.dart'; +import 'package:provider/provider.dart'; +import 'package:redacted/redacted.dart'; +import 'package:supertokens_flutter/supertokens.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class MihBusinessCardV2 extends StatefulWidget { + final Business business; + final double width; + const MihBusinessCardV2({ + super.key, + required this.business, + required this.width, + }); + + @override + State createState() => _MihBusinessCardV2State(); +} + +class _MihBusinessCardV2State extends State { + Future? _businessReviewFuture; + Future? _bookmarkedBusinessFuture; + bool _isUserSignedIn = false; + + Future _checkUserSession() async { + final doesSessionExist = await SuperTokens.doesSessionExist(); + setState(() { + _isUserSignedIn = doesSessionExist; + }); + } + + RedactedConfiguration getRedactedConfiguration() { + return RedactedConfiguration( + // redactedColor: Colors.pink, + redactedColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ); + } + + Future _makePhoneCall(String phoneNumber) async { + String formattedNumber = phoneNumber.replaceAll("-", ""); + final Uri url = Uri(scheme: 'tel', path: formattedNumber); + if (await canLaunchUrl(url)) { + await launchUrl(url); + } else { + MihAlertServices().errorBasicAlert( + "Error Making Call", + "We couldn't open your phone app to call $formattedNumber. To fix this, make sure you have a phone application installed and it's set as your default dialer.", + context, + ); + } + } + + String? _encodeQueryParameters(Map params) { + return params.entries + .map((MapEntry e) => + '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}') + .join('&'); + } + + Future _launchEmail( + String recipient, String subject, String body) async { + final Uri emailLaunchUri = Uri( + scheme: 'mailto', + path: recipient, + query: _encodeQueryParameters({ + 'subject': subject, + 'body': body, + }), + ); + + if (await canLaunchUrl(emailLaunchUri)) { + await launchUrl(emailLaunchUri); + } else { + MihAlertServices().errorBasicAlert( + "Error Creating Email", + "We couldn't launch your email app to send a message to $recipient. To fix this, please confirm that you have an email application installed and that it's set as your default.", + context, + ); + } + } + + Future _launchGoogleMapsWithUrl({ + required double latitude, + required double longitude, + String? label, + }) async { + final Uri googleMapsUrl = Uri.parse( + 'https://www.google.com/maps/search/?api=1&query=$latitude,$longitude${label != null ? '&query_place_id=' : ''}', + ); + try { + if (await canLaunchUrl(googleMapsUrl)) { + await launchUrl(googleMapsUrl); + } else { + MihAlertServices().errorBasicAlert( + "Error Opening Maps", + "There was an issue opening maps for ${widget.business.Name}. This usually happens if you don't have a maps app installed or it's not set as your default. Please install one to proceed.", + context, + ); + } + } catch (e) { + MihAlertServices().errorBasicAlert( + "Error Opening Maps", + "There was an issue opening maps for ${widget.business.Name}. This usually happens if you don't have a maps app installed or it's not set as your default. Please install one to proceed.", + context, + ); + } + } + + Future _launchWebsite(String urlString) async { + String newUrl = urlString; + if (!newUrl.startsWith("https://")) { + newUrl = "https://$urlString"; + } + final Uri url = Uri.parse(newUrl); + try { + if (await canLaunchUrl(url)) { + await launchUrl(url); + } else { + MihAlertServices().errorBasicAlert( + "Error Opening Website", + "We couldn't open the link to $newUrl. To view this website, please ensure you have a web browser installed and set as your default.", + context, + ); + } + } catch (e) { + MihAlertServices().errorBasicAlert( + "Error Opening Website", + "We couldn't open the link to $newUrl. To view this website, please ensure you have a web browser installed and set as your default.", + context, + ); + } + } + + Future getUserReview() async { + String user_id = await SuperTokens.getUserId(); + return await MihMzansiDirectoryServices().getUserReviewOfBusiness( + user_id, + widget.business.business_id, + ); + } + + Future getUserBookmark() async { + String user_id = await SuperTokens.getUserId(); + return await MihMzansiDirectoryServices().getUserBookmarkOfBusiness( + user_id, + widget.business.business_id, + ); + } + + bool isValidGps(String coordinateString) { + final RegExp gpsRegex = RegExp( + r"^-?([1-8]?\d(\.\d+)?|90(\.0+)?),\s*-?(1[0-7]\d(\.\d+)?|180(\.0+)?|\d{1,2}(\.\d+)?)$"); + return gpsRegex.hasMatch(coordinateString); + } + + @override + void initState() { + super.initState(); + _checkUserSession(); + _businessReviewFuture = getUserReview(); + _bookmarkedBusinessFuture = getUserBookmark(); + } + + @override + Widget build(BuildContext context) { + // double screenWidth = MediaQuery.of(context).size.width; + return Consumer2( + builder: (BuildContext context, MzansiProfileProvider profileProvider, + MzansiDirectoryProvider directoryProvider, Widget? child) { + double iconSize = 50.0; + return Wrap( + alignment: WrapAlignment.center, + runSpacing: 10, + spacing: 10, + children: [ + Column( + children: [ + MihButton( + width: 80, + height: 80, + onPressed: () { + _makePhoneCall(widget.business.contact_no); + }, + buttonColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + child: Icon( + Icons.phone, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + size: iconSize, + ), + ), + const SizedBox(height: 2), + FittedBox( + child: Text( + "Call", + style: TextStyle( + fontWeight: FontWeight.bold, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 20, + ), + ), + ), + ], + ), + Column( + children: [ + MihButton( + width: 80, + height: 80, + onPressed: () { + _launchEmail( + widget.business.bus_email, + "Inquiery about ${widget.business.Name}", + "Dear ${widget.business.Name},\n\nI would like to inquire about your services.\n\nBest regards,\n", + ); + }, + buttonColor: MihColors.getPinkColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + child: Icon( + Icons.email, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + size: iconSize, + ), + ), + const SizedBox(height: 2), + FittedBox( + child: Text( + "Email", + style: TextStyle( + fontWeight: FontWeight.bold, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 20, + ), + ), + ), + ], + ), + if (isValidGps(widget.business.gps_location)) + Column( + children: [ + MihButton( + width: 80, + height: 80, + onPressed: () { + final latitude = double.parse( + widget.business.gps_location.split(',')[0]); + final longitude = double.parse( + widget.business.gps_location.split(',')[1]); + _launchGoogleMapsWithUrl( + latitude: latitude, + longitude: longitude, + ); + }, + buttonColor: MihColors.getOrangeColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + child: Icon( + Icons.location_on, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + size: iconSize, + ), + ), + const SizedBox(height: 2), + FittedBox( + child: Text( + "Maps", + style: TextStyle( + fontWeight: FontWeight.bold, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 20, + ), + ), + ), + ], + ), + if (widget.business.website.isNotEmpty && + widget.business.website != "") + Column( + children: [ + MihButton( + width: 80, + height: 80, + onPressed: () { + _launchWebsite(widget.business.website); + }, + buttonColor: MihColors.getRedColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + child: Icon( + Icons.language, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + size: iconSize, + ), + ), + const SizedBox(height: 2), + FittedBox( + child: Text( + "Website", + style: TextStyle( + fontWeight: FontWeight.bold, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 20, + ), + ), + ), + ], + ), + FutureBuilder( + future: _businessReviewFuture, + builder: (context, asyncSnapshot) { + if (asyncSnapshot.connectionState == ConnectionState.waiting) { + return Column( + children: [ + MihButton( + width: 80, + height: 80, + onPressed: () {}, + buttonColor: MihColors.getGreyColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + child: Icon( + Icons.star_rate_rounded, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + size: iconSize, + ), + ).redacted(context: context, redact: true), + const SizedBox(height: 2), + FittedBox( + child: Text( + "Rate Us", + style: TextStyle( + fontWeight: FontWeight.bold, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 20, + ), + ).redacted(context: context, redact: true), + ), + ], + ); + } else { + BusinessReview? businessReview = asyncSnapshot.data; + String ratingTitle = ""; + if (businessReview == null) { + ratingTitle = "Rate Us"; + } else { + ratingTitle = "Edit"; + } + return Column( + children: [ + MihButton( + width: 80, + height: 80, + onPressed: () { + businessReviewRatingWindow(directoryProvider, + businessReview, true, widget.width); + }, + buttonColor: MihColors.getYellowColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + child: Icon( + Icons.star_rate_rounded, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + size: iconSize, + ), + ), + const SizedBox(height: 2), + FittedBox( + child: Text( + ratingTitle, + style: TextStyle( + fontWeight: FontWeight.bold, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 20, + ), + ), + ), + ], + ); + } + }, + ), + FutureBuilder( + future: _bookmarkedBusinessFuture, + builder: (context, asyncSnapshot) { + if (asyncSnapshot.connectionState == ConnectionState.waiting) { + return Column( + children: [ + MihButton( + width: 80, + height: 80, + onPressed: () {}, + buttonColor: MihColors.getGreyColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + child: Icon( + Icons.bookmark_add_rounded, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + size: iconSize, + ), + ).redacted(context: context, redact: true), + const SizedBox(height: 2), + FittedBox( + child: Text( + "bookmark", + style: TextStyle( + fontWeight: FontWeight.bold, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 20, + ), + ).redacted(context: context, redact: true), + ), + ], + ); + } else { + BookmarkedBusiness? bookmarkBusiness = asyncSnapshot.data; + String bookmarkDisplayTitle = ""; + if (bookmarkBusiness == null) { + bookmarkDisplayTitle = "Bookmark"; + } else { + bookmarkDisplayTitle = "Remove"; + } + return Column( + children: [ + MihButton( + width: 80, + height: 80, + onPressed: () { + if (bookmarkBusiness == null) { + showAddBookmarkAlert(); + } else { + showDeleteBookmarkAlert(bookmarkBusiness); + } + }, + buttonColor: MihColors.getBluishPurpleColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + child: Icon( + bookmarkBusiness == null + ? Icons.bookmark_add_rounded + : Icons.bookmark_remove_rounded, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + size: iconSize, + ), + ), + const SizedBox(height: 2), + FittedBox( + child: Text( + bookmarkDisplayTitle, + style: TextStyle( + fontWeight: FontWeight.bold, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 20, + ), + ), + ), + ], + ); + } + }, + ), + ], + ); + }, + ); + } + + Future businessReviewRatingWindow( + MzansiDirectoryProvider directoryProvider, + BusinessReview? myReview, + bool previouslyRated, + double width) async { + if (_isUserSignedIn) { + showDialog( + barrierDismissible: false, + context: context, + builder: (context) => MihReviewBusinessWindow( + business: widget.business, + businessReview: myReview, + screenWidth: width, + readOnly: false, + onSuccessDismissPressed: () async { + List? businessSearchResults = []; + businessSearchResults = await MihBusinessDetailsServices() + .searchBusinesses(directoryProvider.searchTerm, + directoryProvider.businessTypeFilter, context); + Map> busImagesUrl = {}; + Future businessLogoUrl; + for (var bus in businessSearchResults) { + businessLogoUrl = MihFileApi.getMinioFileUrl(bus.logo_path); + busImagesUrl[bus.business_id] = businessLogoUrl; + } + directoryProvider.setSearchedBusinesses( + searchedBusinesses: businessSearchResults, + businessesImagesUrl: busImagesUrl, + ); + setState(() { + _businessReviewFuture = getUserReview(); + }); + }, + ), + ); + } else { + showSignInRequiredAlert(); + } + } + + void showAddBookmarkAlert() { + if (_isUserSignedIn) { + showDialog( + barrierDismissible: false, + context: context, + builder: (context) => MihAddBookmarkAlert( + business: widget.business, + onSuccessDismissPressed: () async { + _bookmarkedBusinessFuture = getUserBookmark(); + }, + ), + ); + } else { + showSignInRequiredAlert(); + } + } + + void showDeleteBookmarkAlert(BookmarkedBusiness? bookmarkBusiness) { + if (_isUserSignedIn) { + showDialog( + barrierDismissible: false, + context: context, + builder: (context) => MihDeleteBookmarkAlert( + business: widget.business, + bookmarkBusiness: bookmarkBusiness, + onSuccessDismissPressed: () { + _bookmarkedBusinessFuture = getUserBookmark(); + }, + // startUpSearch: widget.startUpSearch, + )); + } else { + showSignInRequiredAlert(); + } + } + + void showSignInRequiredAlert() { + showDialog( + barrierDismissible: false, + context: context, + builder: (context) { + return MihPackageWindow( + fullscreen: false, + windowTitle: null, + onWindowTapClose: () { + context.pop(); + }, + windowBody: Column( + children: [ + Icon( + MihIcons.mihLogo, + size: 125, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + const SizedBox(height: 10), + Text( + "Let's Get Started", + textAlign: TextAlign.center, + style: TextStyle( + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + fontSize: 25, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 15), + Text( + "Ready to dive in to the world of MIH?\nSign in or create a free MIH account to unlock all the powerful features of the MIH app. It's quick and easy!", + style: TextStyle( + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + fontSize: 15, + ), + ), + const SizedBox(height: 25), + Center( + child: MihButton( + onPressed: () { + context.goNamed( + 'mihHome', + extra: true, + ); + }, + buttonColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + elevation: 10, + width: 300, + child: Text( + "Sign In/ Create Account", + style: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ); + }, + ); + } +} diff --git a/mih_ui/lib/mih_package_components/mih_business_profile_preview.dart b/mih_ui/lib/mih_package_components/mih_business_profile_preview.dart index 81cdcf6b..0380688e 100644 --- a/mih_ui/lib/mih_package_components/mih_business_profile_preview.dart +++ b/mih_ui/lib/mih_package_components/mih_business_profile_preview.dart @@ -68,6 +68,7 @@ class _MihBusinessProfilePreviewState extends State { : MihCircleAvatar( imageFile: widget.imageFile, width: profilePictureWidth, + expandable: false, editable: false, fileNameController: TextEditingController(), userSelectedfile: null, diff --git a/mih_ui/lib/mih_package_components/mih_circle_avatar.dart b/mih_ui/lib/mih_package_components/mih_circle_avatar.dart index c44c72a3..c3d4db1f 100644 --- a/mih_ui/lib/mih_package_components/mih_circle_avatar.dart +++ b/mih_ui/lib/mih_package_components/mih_circle_avatar.dart @@ -2,13 +2,17 @@ import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:ken_logger/ken_logger.dart'; import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_icons.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; +import 'package:mzansi_innovation_hub/mih_package_components/mih_package_window.dart'; class MihCircleAvatar extends StatefulWidget { final ImageProvider? imageFile; final double width; + final bool expandable; final bool editable; final TextEditingController? fileNameController; final onChange; @@ -19,6 +23,7 @@ class MihCircleAvatar extends StatefulWidget { super.key, required this.imageFile, required this.width, + required this.expandable, required this.editable, required this.fileNameController, required this.userSelectedfile, @@ -35,23 +40,33 @@ class _MihCircleAvatarState extends State { late ImageProvider? imagePreview; ImageProvider? getAvatar() { - // Color dark = const Color(0XFF3A4454); if (widget.imageFile == null) { return null; - // if (widget.backgroundColor == dark) { - // print("here in light icon"); - // return const AssetImage( - // 'lib/mih_package_components/assets/images/i-dont-know-light.png'); - // } else { - // print("here in dark icon"); - // return const AssetImage( - // 'lib/mih_package_components/assets/images/i-dont-know-dark.png'); - // } } else { return widget.imageFile; } } + void expandAvatar() { + showDialog( + context: context, + builder: (context) { + return MihPackageWindow( + fullscreen: true, + windowTitle: "", + scrollbarOn: false, + onWindowTapClose: () { + context.pop(); + }, + windowBody: SizedBox.expand( + child: InteractiveViewer( + child: Image(image: imagePreview!), + ), + ), + ); + }); + } + @override void initState() { super.initState(); @@ -62,111 +77,121 @@ class _MihCircleAvatarState extends State { @override Widget build(BuildContext context) { - return Container( - // color: Colors.white, - alignment: Alignment.center, - width: widget.width, - height: widget.width, - child: Stack( + return GestureDetector( + onTap: widget.expandable + ? () { + KenLogger.success("Avatar tapped"); + expandAvatar(); + } + : null, + child: Container( alignment: Alignment.center, - children: [ - Visibility( - visible: imagePreview != null, - child: Positioned( - right: widget.width * 0.03, - child: CircleAvatar( - radius: widget.width / 2.2, - backgroundColor: widget.backgroundColor, - backgroundImage: imagePreview, + width: widget.width, + height: widget.width, + child: Stack( + alignment: Alignment.center, + children: [ + Visibility( + visible: imagePreview != null, + child: Positioned( + right: widget.width * 0.03, + child: CircleAvatar( + radius: widget.width / 2.2, + backgroundColor: widget.backgroundColor, + backgroundImage: imagePreview, + ), ), ), - ), - Visibility( - visible: imagePreview != null, - child: Icon( - size: widget.width, - MihIcons.mihRing, - color: widget.frameColor, + Visibility( + visible: imagePreview != null, + child: Icon( + size: widget.width, + MihIcons.mihRing, + color: widget.frameColor, + ), ), - ), - Visibility( - visible: imagePreview == null, - child: Icon( - MihIcons.iDontKnow, - size: widget.width, - color: widget.frameColor, + Visibility( + visible: imagePreview == null, + child: Icon( + MihIcons.iDontKnow, + size: widget.width, + color: widget.frameColor, + ), ), - ), - Visibility( - visible: widget.editable, - child: Positioned( - bottom: 0, - right: 0, - child: IconButton.filled( - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - MihColors.getGreenColor( - MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + Visibility( + visible: widget.editable, + child: Positioned( + bottom: 0, + right: 0, + child: IconButton.filled( + style: ButtonStyle( + backgroundColor: WidgetStateProperty.all( + MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + ), ), - ), - onPressed: () async { - try { - FilePickerResult? result = - await FilePicker.platform.pickFiles( - type: FileType.image, - ); - // print("Here 1"); - if (MzansiInnovationHub.of(context)!.theme.getPlatform() == - "Web") { - // print("Here 2"); - if (result == null) return; - // print("Here 3"); - PlatformFile? selectedFile = result.files.first; - setState(() { - // print("Here 4"); - widget.onChange(selectedFile); - // print("Here 5"); - imagePreview = MemoryImage(selectedFile.bytes!); - }); - - setState(() { - widget.fileNameController!.text = selectedFile.name; - }); - } else { - if (result != null) { - File file = File(result.files.single.path!); - PlatformFile? androidFile = PlatformFile( - path: file.path, - name: file.path.split('/').last, - size: file.lengthSync(), - bytes: await file.readAsBytes(), // Read file bytes - //extension: fileExtension, - ); + onPressed: () async { + try { + FilePickerResult? result = + await FilePicker.platform.pickFiles( + type: FileType.image, + ); + // print("Here 1"); + if (MzansiInnovationHub.of(context)! + .theme + .getPlatform() == + "Web") { + // print("Here 2"); + if (result == null) return; + // print("Here 3"); + PlatformFile? selectedFile = result.files.first; setState(() { - widget.onChange(androidFile); - imagePreview = FileImage(file); + // print("Here 4"); + widget.onChange(selectedFile); + // print("Here 5"); + imagePreview = MemoryImage(selectedFile.bytes!); }); setState(() { - widget.fileNameController!.text = - file.path.split('/').last; + widget.fileNameController!.text = selectedFile.name; }); } else { - print("here in else"); - // User canceled the picker + if (result != null) { + File file = File(result.files.single.path!); + PlatformFile? androidFile = PlatformFile( + path: file.path, + name: file.path.split('/').last, + size: file.lengthSync(), + bytes: await file.readAsBytes(), // Read file bytes + //extension: fileExtension, + ); + setState(() { + widget.onChange(androidFile); + imagePreview = FileImage(file); + }); + + setState(() { + widget.fileNameController!.text = + file.path.split('/').last; + }); + } else { + print("here in else"); + // User canceled the picker + } } + } catch (e) { + print("Here Error: $e"); } - } catch (e) { - print("Here Error: $e"); - } - }, - icon: Icon( - Icons.camera_alt, + }, + icon: Icon( + Icons.camera_alt, + ), ), ), ), - ), - ], + ], + ), ), ); } diff --git a/mih_ui/lib/mih_package_components/mih_dropdwn_field.dart b/mih_ui/lib/mih_package_components/mih_dropdwn_field.dart index dd8d46ee..3ec6861f 100644 --- a/mih_ui/lib/mih_package_components/mih_dropdwn_field.dart +++ b/mih_ui/lib/mih_package_components/mih_dropdwn_field.dart @@ -103,6 +103,18 @@ class _MihDropdownFieldState extends State { Expanded( child: Theme( data: Theme.of(context).copyWith( + scrollbarTheme: ScrollbarThemeData( + thumbColor: WidgetStatePropertyAll( + MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark")), + thickness: const WidgetStatePropertyAll(6), + radius: const Radius.circular(10), + thumbVisibility: const WidgetStatePropertyAll( + true), // Always show when scrolling + ), textSelectionTheme: TextSelectionThemeData( cursorColor: MihColors.getPrimaryColor( MzansiInnovationHub.of(context)!.theme.mode == diff --git a/mih_ui/lib/mih_package_components/mih_loading_circle.dart b/mih_ui/lib/mih_package_components/mih_loading_circle.dart index 5e9f76c2..576a425a 100644 --- a/mih_ui/lib/mih_package_components/mih_loading_circle.dart +++ b/mih_ui/lib/mih_package_components/mih_loading_circle.dart @@ -19,9 +19,6 @@ class _MihloadingcircleState extends State late AnimationController _controller; late Animation _animation; - late double width; - late double height; - @override void initState() { super.initState(); @@ -82,16 +79,15 @@ class _MihloadingcircleState extends State }, ), ), - widget.message != null - ? Text( - widget.message!, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ) - : SizedBox(), + if (widget.message != null) + Text( + widget.message!, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), ], )), ), diff --git a/mih_ui/lib/mih_package_components/mih_package.dart b/mih_ui/lib/mih_package_components/mih_package.dart index e402d564..f387f5c7 100644 --- a/mih_ui/lib/mih_package_components/mih_package.dart +++ b/mih_ui/lib/mih_package_components/mih_package.dart @@ -1,7 +1,8 @@ +import 'dart:io'; + import 'package:flutter/services.dart'; import 'package:go_router/go_router.dart'; import 'package:ken_logger/ken_logger.dart'; -import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_scack_bar.dart'; import 'package:mzansi_innovation_hub/mih_packages/mih_home/components/mih_app_drawer.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tools.dart'; @@ -100,7 +101,7 @@ class _MihPackageState extends State // _peakAnimation(); // }); // } - if (!MzansiInnovationHub.of(context)!.theme.kIsWeb) { + if (Platform.isAndroid || Platform.isIOS) { // Trigger the peak animation only AFTER the route transition is complete WidgetsBinding.instance.addPostFrameCallback((_) { final ModalRoute? currentRoute = ModalRoute.of(context); diff --git a/mih_ui/lib/mih_package_components/mih_package_tile.dart b/mih_ui/lib/mih_package_components/mih_package_tile.dart index 5496841d..e5d25f35 100644 --- a/mih_ui/lib/mih_package_components/mih_package_tile.dart +++ b/mih_ui/lib/mih_package_components/mih_package_tile.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:app_settings/app_settings.dart'; import 'package:flutter/foundation.dart'; import 'package:local_auth/local_auth.dart'; @@ -155,7 +157,8 @@ class _MihPackageTileState extends State { Future authenticateUser() async { if (widget.authenticateUser != null && widget.authenticateUser! && - !kIsWeb) { + !kIsWeb && + !Platform.isLinux) { if (await isUserAuthenticated()) { widget.onTap(); } diff --git a/mih_ui/lib/mih_package_components/mih_package_window.dart b/mih_ui/lib/mih_package_components/mih_package_window.dart index e2dc8720..77f6a855 100644 --- a/mih_ui/lib/mih_package_components/mih_package_window.dart +++ b/mih_ui/lib/mih_package_components/mih_package_window.dart @@ -15,6 +15,7 @@ class MihPackageWindow extends StatefulWidget { final Color? foregroundColor; final bool? borderOn; final bool fullscreen; + final bool? scrollbarOn; const MihPackageWindow({ super.key, required this.fullscreen, @@ -23,6 +24,7 @@ class MihPackageWindow extends StatefulWidget { required this.onWindowTapClose, required this.windowBody, this.borderOn, + this.scrollbarOn, this.backgroundColor, this.foregroundColor, }); @@ -177,7 +179,13 @@ class _MihPackageWindowState extends State { getHeader(), const SizedBox(height: 5), Expanded( - child: SingleChildScrollView(child: widget.windowBody)), + child: widget.scrollbarOn != null || !widget.scrollbarOn! + ? widget.windowBody + : MihSingleChildScroll( + scrollbarOn: true, + child: widget.windowBody, + ), + ), ], ) : Column( @@ -197,7 +205,10 @@ class _MihPackageWindowState extends State { maxHeight: windowHeight * 0.85, maxWidth: windowWidth * 0.85, ), - child: MihSingleChildScroll(child: widget.windowBody), + child: MihSingleChildScroll( + scrollbarOn: true, + child: widget.windowBody, + ), ), ), ), diff --git a/mih_ui/lib/mih_package_components/mih_personal_profile_preview.dart b/mih_ui/lib/mih_package_components/mih_personal_profile_preview.dart index 1b562f81..daf313af 100644 --- a/mih_ui/lib/mih_package_components/mih_personal_profile_preview.dart +++ b/mih_ui/lib/mih_package_components/mih_personal_profile_preview.dart @@ -49,6 +49,7 @@ class _MihPersonalProfilePreviewState extends State { : MihCircleAvatar( imageFile: widget.imageFile, width: profilePictureWidth, + expandable: false, editable: false, fileNameController: TextEditingController(), userSelectedfile: null, diff --git a/mih_ui/lib/mih_package_components/mih_profile_links.dart b/mih_ui/lib/mih_package_components/mih_profile_links.dart index 29e7acc0..756edfe1 100644 --- a/mih_ui/lib/mih_package_components/mih_profile_links.dart +++ b/mih_ui/lib/mih_package_components/mih_profile_links.dart @@ -3,19 +3,17 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; import 'package:mzansi_innovation_hub/mih_objects/profile_link.dart'; -import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tile.dart'; +import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart'; import 'package:mzansi_innovation_hub/mih_providers/mzansi_profile_provider.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; class MihProfileLinks extends StatefulWidget { final List links; - final double? buttonSize; final bool? paddingOn; const MihProfileLinks({ super.key, required this.links, - this.buttonSize, this.paddingOn, }); @@ -26,72 +24,89 @@ class MihProfileLinks extends StatefulWidget { class _MihProfileLinksState extends State { Widget displayLinkButton(ProfileLink link) { IconData iconData; - Color iconColor; + Color btnColor; + Color iconColor = Colors.white; switch (link.destination.toLowerCase()) { case "youtube": iconData = FontAwesomeIcons.youtube; - iconColor = const Color(0xFFFF0000); + btnColor = const Color(0xFFFF0000); break; case "tiktok": iconData = FontAwesomeIcons.tiktok; - iconColor = const Color(0xFF000000); + btnColor = const Color(0xFF000000); break; case "twitch": iconData = FontAwesomeIcons.twitch; - iconColor = const Color(0xFF6441a5); + btnColor = const Color(0xFF6441a5); break; case "threads": iconData = FontAwesomeIcons.threads; - iconColor = const Color(0xFF000000); + btnColor = const Color(0xFF000000); break; case "whatsapp": iconData = FontAwesomeIcons.whatsapp; - iconColor = const Color(0xFF25D366); + btnColor = const Color(0xFF25D366); break; case "instagram": iconData = FontAwesomeIcons.instagram; - iconColor = const Color(0xFFF56040); + btnColor = const Color(0xFFF56040); break; case "x": iconData = FontAwesomeIcons.xTwitter; - iconColor = const Color(0xFF000000); + btnColor = const Color(0xFF000000); break; case "linkedin": iconData = FontAwesomeIcons.linkedin; - iconColor = const Color(0xFF0a66c2); + btnColor = const Color(0xFF0a66c2); break; case "facebook": iconData = FontAwesomeIcons.facebook; - iconColor = const Color(0xFF4267B2); + btnColor = const Color(0xFF4267B2); break; case "reddit": iconData = FontAwesomeIcons.reddit; - iconColor = const Color(0xFFFF4500); + btnColor = const Color(0xFFFF4500); break; case "discord": iconData = FontAwesomeIcons.discord; - iconColor = const Color(0xFF5865F2); + btnColor = const Color(0xFF5865F2); + break; + case "git": + iconData = FontAwesomeIcons.git; + btnColor = const Color(0xFF73A952); break; default: iconData = FontAwesomeIcons.link; - iconColor = MihColors.getPrimaryColor( + btnColor = MihColors.getPrimaryColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"); } - - return MihPackageTile( - onTap: () { + return MihButton( + width: 80, + height: 80, + onPressed: () { launchSocialUrl(Uri.parse(link.web_link)); }, - appName: link.destination, - appIcon: Icon( + buttonColor: btnColor, + child: FaIcon( iconData, color: iconColor, + size: 40, ), - iconSize: 200, - textColor: Colors.black, - // MihColors.getPrimaryColor( - // MzansiInnovationHub.of(context)!.theme.mode == "Dark"), ); + // return MihPackageTile( + // onTap: () { + // launchSocialUrl(Uri.parse(link.web_link)); + // }, + // appName: link.destination, + // appIcon: Icon( + // iconData, + // color: btnColor, + // ), + // iconSize: 200, + // textColor: Colors.black, + // // MihColors.getPrimaryColor( + // // MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + // ); } Future launchSocialUrl(Uri linkUrl) async { @@ -110,54 +125,33 @@ class _MihProfileLinksState extends State { padding: widget.paddingOn == null || widget.paddingOn! ? MzansiInnovationHub.of(context)!.theme.screenType == "desktop" ? EdgeInsets.symmetric(horizontal: width * 0.2) - : EdgeInsets.symmetric(horizontal: width * 0.075) + : EdgeInsets.symmetric(horizontal: width * 0) : EdgeInsetsGeometry.all(0), - child: Material( - color: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == "Dark") - .withValues(alpha: 0.6), - borderRadius: BorderRadius.circular(25), - elevation: 10, - shadowColor: Colors.black, - child: Container( - width: 500, - padding: EdgeInsets.all(10), - decoration: BoxDecoration( - color: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - borderRadius: BorderRadius.circular(10), - ), - child: widget.links.isEmpty - ? SizedBox( - height: 35, - child: Text( - "No Profile Links", - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 25, - fontWeight: FontWeight.bold, - color: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - ), - ), - ) - : Wrap( - alignment: WrapAlignment.center, - runSpacing: 15, - spacing: 15, - children: widget.links.map( - (link) { - return SizedBox( - width: widget.buttonSize ?? 80, - height: widget.buttonSize ?? 80, - child: displayLinkButton(link), - ); - }, - ).toList(), + child: widget.links.isEmpty + ? SizedBox( + height: 35, + child: Text( + "No Profile Links", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), ), - ), - ), + ), + ) + : Wrap( + alignment: WrapAlignment.center, + runSpacing: 10, + spacing: 10, + children: widget.links.map( + (link) { + return displayLinkButton(link); + }, + ).toList(), + ), ); }, ); diff --git a/mih_ui/lib/mih_package_components/mih_single_child_scroll.dart b/mih_ui/lib/mih_package_components/mih_single_child_scroll.dart index f8f6e096..74c846b1 100644 --- a/mih_ui/lib/mih_package_components/mih_single_child_scroll.dart +++ b/mih_ui/lib/mih_package_components/mih_single_child_scroll.dart @@ -2,9 +2,11 @@ import 'package:flutter/material.dart'; class MihSingleChildScroll extends StatefulWidget { final Widget child; + final bool? scrollbarOn; const MihSingleChildScroll({ super.key, required this.child, + this.scrollbarOn, }); @override @@ -18,7 +20,8 @@ class _MihSingleChildScrollState extends State { bottom: false, minimum: EdgeInsets.only(bottom: 5), child: ScrollConfiguration( - behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false), + behavior: ScrollConfiguration.of(context) + .copyWith(scrollbars: widget.scrollbarOn ?? false), child: SingleChildScrollView( child: widget.child, ), diff --git a/mih_ui/lib/mih_packages/about_mih/package_tools/mih_ attributes.dart b/mih_ui/lib/mih_packages/about_mih/package_tools/mih_ attributes.dart index abdbff56..d1918fbb 100644 --- a/mih_ui/lib/mih_packages/about_mih/package_tools/mih_ attributes.dart +++ b/mih_ui/lib/mih_packages/about_mih/package_tools/mih_ attributes.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_single_child_scroll.dart'; -import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tool_body.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_icons.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; @@ -21,72 +20,30 @@ class _MihAttributesState extends State { } } - TableRow displayIcon(IconData icon, String creator, String link) { - return TableRow( - children: [ - TableCell( - verticalAlignment: TableCellVerticalAlignment.middle, - child: SizedBox( - height: 150, - child: Padding( - padding: const EdgeInsets.only(bottom: 15.0), - child: FittedBox( - child: Center( - child: Icon( - icon, - // size: 125, - color: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - ), - ), - ), - ), + Widget displayAttribution(IconData resource, String creator, String link) { + return GestureDetector( + onTap: () { + launchUrlLink( + Uri.parse( + link, ), - ), - TableCell( - verticalAlignment: TableCellVerticalAlignment.middle, - child: Padding( - padding: const EdgeInsets.all(15.0), - child: Center( - child: Text( - creator, - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), - ), + ); + }, + child: Column( + children: [ + Icon( + resource, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + size: 100, ), - ), - TableCell( - verticalAlignment: TableCellVerticalAlignment.middle, - child: Padding( - padding: const EdgeInsets.only(bottom: 15.0), - child: MihButton( - onPressed: () { - launchUrlLink( - Uri.parse( - link, - ), - ); - }, - buttonColor: MihColors.getGreenColor( - MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - width: 100, - child: Text( - "Visit", - style: TextStyle( - color: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), - ), + const SizedBox(height: 5), + Text( + creator, + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), - ), - ], + ], + ), ); } @@ -94,7 +51,6 @@ class _MihAttributesState extends State { Widget build(BuildContext context) { return MihPackageToolBody( borderOn: false, - innerHorizontalPadding: 10, bodyItem: getBody(), ); } @@ -108,179 +64,77 @@ class _MihAttributesState extends State { "As per the terms for free use for these third party providers, the following assets require attribution"; return MihSingleChildScroll( - child: Column( - children: [ - Icon( - MihIcons.mihLogo, - color: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - size: 165, - ), - const SizedBox( - height: 10, - ), - SelectableText( - message, - style: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.bold, + scrollbarOn: true, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: Column( + children: [ + Icon( + MihIcons.mihLogo, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + size: 165, ), - ), - const SizedBox( - height: 10, - ), - SizedBox( - width: 700, - child: Table( - defaultVerticalAlignment: TableCellVerticalAlignment.middle, - columnWidths: const { - 0: FlexColumnWidth(1), - 1: FlexColumnWidth(1), - 2: FlexColumnWidth(1), - }, - children: [ - const TableRow( - children: [ - TableCell( - verticalAlignment: TableCellVerticalAlignment.middle, - child: Padding( - padding: const EdgeInsets.only(bottom: 15.0), - child: Center( - child: Text( - "Resources", - style: TextStyle( - fontSize: 25, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ), - TableCell( - verticalAlignment: TableCellVerticalAlignment.middle, - child: Padding( - padding: const EdgeInsets.only(bottom: 15.0), - child: Center( - child: Text( - "Creator", - style: TextStyle( - fontSize: 25, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ), - TableCell( - child: Padding( - padding: const EdgeInsets.only(bottom: 15.0), - child: Center( - child: Text( - "Link", - style: TextStyle( - fontSize: 25, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ), - ], - ), - displayIcon(MihIcons.mihRing, "Tarah Meth", - "https://www.linkedin.com/in/tarah-meth-3b6309254/"), - displayIcon(MihIcons.mihLogo, "Tarah Meth", - "https://www.linkedin.com/in/tarah-meth-3b6309254/"), - displayIcon(MihIcons.mzansiAi, "Ollama", "https://ollama.com/"), - displayIcon(MihIcons.mzansiWallet, "Freepik", - "https://www.flaticon.com/free-icon/wallet-passes-app_3884407?term=wallet&page=1&position=21&origin=search&related_id=3884407"), - displayIcon(MihIcons.patientProfile, "RaftelDesign", - "https://www.flaticon.com/free-icon/patient_2376100?term=medication&page=1&position=6&origin=search&related_id=2376100"), - displayIcon(MihIcons.patientProfile, "Srip", - "https://www.flaticon.com/free-icon/hospital_1233930?term=medical+snake&page=1&position=7&origin=search&related_id=1233930"), - displayIcon(MihIcons.calendar, "Freepik", - "https://www.flaticon.com/free-icon/calendar_2278049?term=calendar&page=1&position=5&origin=search&related_id=2278049"), - displayIcon(MihIcons.calculator, "Freepik", - "https://www.flaticon.com/free-icon/calculator_2374409?term=calculator&page=1&position=20&origin=search&related_id=2374409"), - displayIcon(MihIcons.aboutMih, "Chanut", - "https://www.flaticon.com/free-icon/info_151776?term=about&page=1&position=8&origin=search&related_id=151776"), - displayIcon(MihIcons.personalProfile, "Freepik", - "https://www.flaticon.com/free-icon/user_1077063?term=profile&page=1&position=6&origin=search&related_id=1077063"), - displayIcon(MihIcons.businessProfile, "Gravisio", - "https://www.flaticon.com/free-icon/contractor_11813336?term=company+profile&page=1&position=2&origin=search&related_id=11813336"), - displayIcon(MihIcons.patientManager, "Vector Tank", - "https://www.flaticon.com/free-icon/doctor_10215061?term=doctor&page=1&position=73&origin=search&related_id=10215061"), - displayIcon(MihIcons.profileSetup, "Freepik", - "https://www.flaticon.com/free-icon/add-user_748137?term=profile+add&page=1&position=1&origin=search&related_id=748137"), - displayIcon(MihIcons.businessSetup, "kerismaker", - "https://www.flaticon.com/free-icon/business_13569850?term=company+add&page=1&position=25&origin=search&related_id=13569850"), - displayIcon(MihIcons.calculator, "fawazahmed0", - "https://github.com/fawazahmed0/exchange-api"), - displayIcon(MihIcons.iDontKnow, "Freepik", - "https://www.flaticon.com/free-icon/i-dont-know_5359909?term=i+dont+know&page=1&position=7&origin=search&related_id=5359909"), - ], + const SizedBox( + height: 10, ), - ), - // SizedBox( - // width: 500, - // child: Column( - // children: [ - // const SizedBox( - // width: double.infinity, - // child: Row( - // mainAxisAlignment: MainAxisAlignment.spaceEvenly, - // mainAxisSize: MainAxisSize.max, - // children: [ - // Flexible( - // child: Text( - // "Icon", - // style: TextStyle( - // fontSize: 25, - // fontWeight: FontWeight.bold, - // ), - // ), - // ), - // Flexible( - // child: Text( - // "Creator", - // style: TextStyle( - // fontSize: 25, - // fontWeight: FontWeight.bold, - // ), - // ), - // ), - // Flexible( - // child: Text( - // "Link", - // style: TextStyle( - // fontSize: 25, - // fontWeight: FontWeight.bold, - // ), - // ), - // ), - // ], - // ), - // ), - // const Padding( - // padding: EdgeInsets.symmetric(vertical: 10.0), - // child: Divider(), - // ), - // displayIcon(MihIcons.mihLogo, "Tarah Meth", - // "https://app.mzansi-innovation-hub.co.za/"), - // const SizedBox(height: 10), - // displayIcon(MihIcons.mihLogo, "Test", - // "https://www.flaticon.com/free-icons/mih"), - // const SizedBox(height: 10), - // displayIcon(MihIcons.mihLogo, "Test", - // "https://www.flaticon.com/free-icons/mih"), - // const SizedBox(height: 10), - // displayIcon(MihIcons.mihLogo, "Test", - // "https://www.flaticon.com/free-icons/mih"), - // const SizedBox(height: 10), - // ], - // ), - // ) - ], + SelectableText( + message, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox( + height: 10, + ), + SizedBox( + width: 900, + child: Wrap( + alignment: WrapAlignment.center, + runSpacing: 10, + spacing: 10, + children: [ + displayAttribution(MihIcons.mihRing, "Tarah Meth", + "https://www.linkedin.com/in/tarah-meth-3b6309254/"), + displayAttribution(MihIcons.mihLogo, "Tarah Meth", + "https://www.linkedin.com/in/tarah-meth-3b6309254/"), + displayAttribution( + MihIcons.mzansiAi, "Ollama", "https://ollama.com/"), + displayAttribution(MihIcons.mzansiWallet, "Freepik", + "https://www.flaticon.com/free-icon/wallet-passes-app_3884407?term=wallet&page=1&position=21&origin=search&related_id=3884407"), + displayAttribution(MihIcons.patientProfile, "RaftelDesign", + "https://www.flaticon.com/free-icon/patient_2376100?term=medication&page=1&position=6&origin=search&related_id=2376100"), + displayAttribution(MihIcons.patientProfile, "Srip", + "https://www.flaticon.com/free-icon/hospital_1233930?term=medical+snake&page=1&position=7&origin=search&related_id=1233930"), + displayAttribution(MihIcons.calendar, "Freepik", + "https://www.flaticon.com/free-icon/calendar_2278049?term=calendar&page=1&position=5&origin=search&related_id=2278049"), + displayAttribution(MihIcons.calculator, "Freepik", + "https://www.flaticon.com/free-icon/calculator_2374409?term=calculator&page=1&position=20&origin=search&related_id=2374409"), + displayAttribution(MihIcons.aboutMih, "Chanut", + "https://www.flaticon.com/free-icon/info_151776?term=about&page=1&position=8&origin=search&related_id=151776"), + displayAttribution(MihIcons.personalProfile, "Freepik", + "https://www.flaticon.com/free-icon/user_1077063?term=profile&page=1&position=6&origin=search&related_id=1077063"), + displayAttribution(MihIcons.businessProfile, "Gravisio", + "https://www.flaticon.com/free-icon/contractor_11813336?term=company+profile&page=1&position=2&origin=search&related_id=11813336"), + displayAttribution(MihIcons.patientManager, "Vector Tank", + "https://www.flaticon.com/free-icon/doctor_10215061?term=doctor&page=1&position=73&origin=search&related_id=10215061"), + displayAttribution(MihIcons.profileSetup, "Freepik", + "https://www.flaticon.com/free-icon/add-user_748137?term=profile+add&page=1&position=1&origin=search&related_id=748137"), + displayAttribution(MihIcons.businessSetup, "kerismaker", + "https://www.flaticon.com/free-icon/business_13569850?term=company+add&page=1&position=25&origin=search&related_id=13569850"), + displayAttribution(MihIcons.calculator, "fawazahmed0", + "https://github.com/fawazahmed0/exchange-api"), + displayAttribution(MihIcons.iDontKnow, "Freepik", + "https://www.flaticon.com/free-icon/i-dont-know_5359909?term=i+dont+know&page=1&position=7&origin=search&related_id=5359909"), + ], + ), + ), + const SizedBox( + height: 30, + ), + ], + ), ), ); } diff --git a/mih_ui/lib/mih_packages/about_mih/package_tools/mih_info.dart b/mih_ui/lib/mih_packages/about_mih/package_tools/mih_info.dart index 3260449a..125a3762 100644 --- a/mih_ui/lib/mih_packages/about_mih/package_tools/mih_info.dart +++ b/mih_ui/lib/mih_packages/about_mih/package_tools/mih_info.dart @@ -1,7 +1,9 @@ import 'package:flutter_speed_dial/flutter_speed_dial.dart'; +import 'package:go_router/go_router.dart'; import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_objects/profile_link.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; +import 'package:mzansi_innovation_hub/mih_package_components/mih_package_window.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_profile_links.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_business_details_services.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_install_services.dart'; @@ -432,7 +434,119 @@ class _MihInfoState extends State { children: [ MihButton( onPressed: () { - MihInstallServices().installMihTrigger(context); + if (MzansiInnovationHub.of(context)!.theme.getPlatform() == + "Android") { + showDialog( + context: context, + builder: (context) { + return MihPackageWindow( + fullscreen: false, + windowTitle: "Select Option", + onWindowTapClose: () { + context.pop(); + }, + windowBody: Column( + children: [ + Text( + "Please select the platform you want to install/ Update MIH from", + style: TextStyle( + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 25), + MihButton( + onPressed: () { + launchSocialUrl( + Uri.parse( + "https://play.google.com/store/apps/details?id=za.co.mzansiinnovationhub.mih", + ), + ); + }, + buttonColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FaIcon( + FontAwesomeIcons.googlePlay, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + ), + const SizedBox(width: 10), + Text( + "Play Store", + style: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + const SizedBox( + height: 10, + ), + MihButton( + onPressed: () { + launchSocialUrl( + Uri.parse( + "https://appgallery.huawei.com/app/C113315335?pkgName=za.co.mzansiinnovationhub.mih", + ), + ); + }, + buttonColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FaIcon( + Icons.store, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + ), + const SizedBox(width: 10), + Text( + "App Gallery", + style: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ], + ), + ); + }, + ); + } else { + MihInstallServices().installMihTrigger(context); + } }, buttonColor: MihColors.getGreenColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), @@ -736,6 +850,14 @@ class _MihInfoState extends State { destination: "Reddit", web_link: "https://www.reddit.com/r/Mzani_Innovation_Hub/", ), + ProfileLink( + idprofile_links: 1, + app_id: "1234", + business_id: "", + destination: "Git", + web_link: + "https://git.mzansi-innovation-hub.co.za/yaso_meth/mih-project", + ), ]; return Column( children: [ @@ -752,7 +874,7 @@ class _MihInfoState extends State { ), MihProfileLinks(links: links), const SizedBox( - height: 25, + height: 75, ), ], ); @@ -777,6 +899,7 @@ class _MihInfoState extends State { return Stack( children: [ MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ aboutHeadings(), diff --git a/mih_ui/lib/mih_packages/about_mih/package_tools/mih_privacy_policy.dart b/mih_ui/lib/mih_packages/about_mih/package_tools/mih_privacy_policy.dart index 0ac7d838..92e4f575 100644 --- a/mih_ui/lib/mih_packages/about_mih/package_tools/mih_privacy_policy.dart +++ b/mih_ui/lib/mih_packages/about_mih/package_tools/mih_privacy_policy.dart @@ -19,7 +19,6 @@ class _MihPrivacyPolicyState extends State { Widget build(BuildContext context) { return MihPackageToolBody( borderOn: false, - innerHorizontalPadding: 10, bodyItem: getBody(context), ); } @@ -55,9 +54,13 @@ class _MihPrivacyPolicyState extends State { children .addAll(PolicyAndTermsText().getPrivacyPolicyText(context, englishOn)); return MihSingleChildScroll( - child: Column( - mainAxisSize: MainAxisSize.max, - children: children, + scrollbarOn: true, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: Column( + mainAxisSize: MainAxisSize.max, + children: children, + ), ), ); } diff --git a/mih_ui/lib/mih_packages/about_mih/package_tools/mih_terms_of_service.dart b/mih_ui/lib/mih_packages/about_mih/package_tools/mih_terms_of_service.dart index 4f73ea0e..435b71ea 100644 --- a/mih_ui/lib/mih_packages/about_mih/package_tools/mih_terms_of_service.dart +++ b/mih_ui/lib/mih_packages/about_mih/package_tools/mih_terms_of_service.dart @@ -19,7 +19,6 @@ class _MIHTermsOfServiceState extends State { Widget build(BuildContext context) { return MihPackageToolBody( borderOn: false, - innerHorizontalPadding: 10, bodyItem: getBody(context), ); } @@ -55,8 +54,12 @@ class _MIHTermsOfServiceState extends State { children .addAll(PolicyAndTermsText().getTermsOfServiceText(context, englishOn)); return MihSingleChildScroll( - child: Column( - children: children, + scrollbarOn: true, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: Column( + children: children, + ), ), ); } diff --git a/mih_ui/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart b/mih_ui/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart index ba7ea0a5..794765ff 100644 --- a/mih_ui/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart +++ b/mih_ui/lib/mih_packages/calculator/package_tools/currency_exchange_rate.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:mzansi_innovation_hub/main.dart'; @@ -157,7 +159,11 @@ class _CurrencyExchangeRateState extends State { ), SizedBox(height: 10), Consumer(builder: (context, bannerAdDisplay, child) { - return MihBannerAd(); + if (Platform.isAndroid || Platform.isIOS) { + return MihBannerAd(); + } else { + return const SizedBox(height: 0); + } }), ], ), @@ -290,6 +296,7 @@ class _CurrencyExchangeRateState extends State { return Consumer( builder: (context, calculatorProvider, child) { return MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: MzansiInnovationHub.of(context)!.theme.screenType == "desktop" diff --git a/mih_ui/lib/mih_packages/calculator/package_tools/simple_calc.dart b/mih_ui/lib/mih_packages/calculator/package_tools/simple_calc.dart index 4a5e05fd..1d2c148d 100644 --- a/mih_ui/lib/mih_packages/calculator/package_tools/simple_calc.dart +++ b/mih_ui/lib/mih_packages/calculator/package_tools/simple_calc.dart @@ -94,6 +94,7 @@ class _SimpleCalcState extends State { } } return MihSingleChildScroll( + scrollbarOn: true, child: Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, diff --git a/mih_ui/lib/mih_packages/calculator/package_tools/tip_calc.dart b/mih_ui/lib/mih_packages/calculator/package_tools/tip_calc.dart index 3553a4c0..72ed50a6 100644 --- a/mih_ui/lib/mih_packages/calculator/package_tools/tip_calc.dart +++ b/mih_ui/lib/mih_packages/calculator/package_tools/tip_calc.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_banner_ad.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; @@ -229,7 +231,11 @@ class _TipCalcState extends State { ), SizedBox(height: 10), Consumer(builder: (context, bannerAdDisplay, child) { - return MihBannerAd(); + if (Platform.isAndroid || Platform.isIOS) { + return MihBannerAd(); + } else { + return const SizedBox(height: 0); + } }), // if (splitBillController.text == "Yes") const Divider(), ], @@ -259,6 +265,7 @@ class _TipCalcState extends State { Widget getBody(double width) { return MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: MzansiInnovationHub.of(context)!.theme.screenType == "desktop" ? EdgeInsets.symmetric(horizontal: width * 0.2) diff --git a/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_forgot_password.dart b/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_forgot_password.dart index a19f11f5..b4406da8 100644 --- a/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_forgot_password.dart +++ b/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_forgot_password.dart @@ -5,6 +5,7 @@ import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_form.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tool_body.dart'; +import 'package:mzansi_innovation_hub/mih_package_components/mih_single_child_scroll.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_text_form_field.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_loading_circle.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; @@ -139,89 +140,85 @@ class _MihForgotPasswordState extends State { validateInput(); } }, - child: SafeArea( - child: SingleChildScrollView( - physics: const BouncingScrollPhysics(), - child: Padding( - padding: MzansiInnovationHub.of(context)!.theme.screenType == - "desktop" - ? EdgeInsets.symmetric(vertical: 25, horizontal: width * 0.2) - : EdgeInsets.symmetric(vertical: 25, horizontal: width * 0.075), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - children: [ - //logo - Icon( - Icons.lock, - size: 100, + child: MihSingleChildScroll( + scrollbarOn: true, + child: Padding( + padding: MzansiInnovationHub.of(context)!.theme.screenType == + "desktop" + ? EdgeInsets.symmetric(vertical: 25, horizontal: width * 0.2) + : EdgeInsets.symmetric(vertical: 25, horizontal: width * 0.075), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + //logo + Icon( + Icons.lock, + size: 100, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + //spacer + const SizedBox(height: 10), + //Heading + Text( + 'Forgot Password', + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, color: MihColors.getSecondaryColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), ), - //spacer - const SizedBox(height: 10), - //Heading - Text( - 'Forgot Password', - style: TextStyle( - fontSize: 25, - fontWeight: FontWeight.bold, - color: MihColors.getSecondaryColor( + ), + const SizedBox(height: 25), + MihForm( + formKey: _formKey, + formFields: [ + MihTextFormField( + fillColor: MihColors.getSecondaryColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + controller: emailController, + multiLineInput: false, + requiredText: true, + hintText: "Email", + validator: (value) { + return MihValidationServices().validateEmail(value); + }, ), - ), - const SizedBox(height: 25), - MihForm( - formKey: _formKey, - formFields: [ - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: emailController, - multiLineInput: false, - requiredText: true, - hintText: "Email", - validator: (value) { - return MihValidationServices().validateEmail(value); + //spacer + const SizedBox(height: 20), + Align( + alignment: Alignment.center, + child: MihButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + prePassResteWarning(); + } else { + MihAlertServices().inputErrorAlert(context); + } }, - ), - //spacer - const SizedBox(height: 20), - Align( - alignment: Alignment.center, - child: MihButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - prePassResteWarning(); - } else { - MihAlertServices().inputErrorAlert(context); - } - }, - buttonColor: MihColors.getGreenColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - width: 300, - child: Text( - "Reset Password", - style: TextStyle( - color: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - fontSize: 20, - fontWeight: FontWeight.bold, - ), + buttonColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + width: 300, + child: Text( + "Reset Password", + style: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 20, + fontWeight: FontWeight.bold, ), ), ), - ], - ), - ], - ), + ), + ], + ), + ], ), ), ), diff --git a/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_register.dart b/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_register.dart index ba9ce004..f6555b88 100644 --- a/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_register.dart +++ b/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_register.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_form.dart'; @@ -107,7 +108,7 @@ class _MihRegisterState extends State { headers: { 'Content-type': 'application/json', 'Accept': 'application/json', - "Authorization": "leatucczyixqwkqqdrhayiwzeofkltds" + "Authorization": dotenv.env['SUPERTOKENS_API_KEY'] ?? "", }, ); //print("response 2: ${response2.statusCode}"); @@ -204,6 +205,7 @@ class _MihRegisterState extends State { } }, child: MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: MzansiInnovationHub.of(context)!.theme.screenType == "desktop" diff --git a/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_reset_password.dart b/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_reset_password.dart index eb776b02..72fe56e2 100644 --- a/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_reset_password.dart +++ b/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_reset_password.dart @@ -5,6 +5,7 @@ import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_form.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tool_body.dart'; +import 'package:mzansi_innovation_hub/mih_package_components/mih_single_child_scroll.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_text_form_field.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_loading_circle.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; @@ -104,110 +105,104 @@ class _MihResetPasswordState extends State { } } }, - child: SafeArea( - child: SingleChildScrollView( - physics: const BouncingScrollPhysics(), - child: Padding( - padding: - MzansiInnovationHub.of(context)!.theme.screenType == "desktop" - ? EdgeInsets.symmetric(horizontal: width * 0.2) - : EdgeInsets.symmetric(horizontal: width * 0.075), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - // Text("Token: ${widget.token}"), // For testing purposes only - //logo - Icon( - Icons.lock, - size: 100, + child: MihSingleChildScroll( + scrollbarOn: true, + child: Padding( + padding: + MzansiInnovationHub.of(context)!.theme.screenType == "desktop" + ? EdgeInsets.symmetric(horizontal: width * 0.2) + : EdgeInsets.symmetric(horizontal: width * 0.075), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + // Text("Token: ${widget.token}"), // For testing purposes only + //logo + Icon( + Icons.lock, + size: 100, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + //spacer + const SizedBox(height: 10), + //Heading + Text( + 'Reset Password', + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, color: MihColors.getSecondaryColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), ), - //spacer - const SizedBox(height: 10), - //Heading - Text( - 'Reset Password', - style: TextStyle( - fontSize: 25, - fontWeight: FontWeight.bold, - color: MihColors.getSecondaryColor( + ), + //spacer + const SizedBox(height: 25), + MihForm( + formKey: _formKey, + formFields: [ + MihTextFormField( + fillColor: MihColors.getSecondaryColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + controller: passwordController, + multiLineInput: false, + requiredText: true, + hintText: "Password", + passwordMode: true, + autofillHints: const [AutofillHints.password], + validator: (value) { + return MihValidationServices().validatePassword(value); + }, ), - ), - //spacer - const SizedBox(height: 25), - MihForm( - formKey: _formKey, - formFields: [ - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: passwordController, - multiLineInput: false, - requiredText: true, - hintText: "Password", - passwordMode: true, - autofillHints: const [AutofillHints.password], - validator: (value) { - return MihValidationServices().validatePassword(value); + //spacer + const SizedBox(height: 10), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + controller: confirmPasswordController, + multiLineInput: false, + requiredText: true, + hintText: "Confirm Password", + passwordMode: true, + autofillHints: const [AutofillHints.password], + validator: (value) { + return MihValidationServices().validatePassword(value); + }, + ), + //spacer + const SizedBox(height: 25), + // sign in button + Center( + child: MihButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + submitFormInput(); + } else { + MihAlertServices().inputErrorAlert(context); + } }, - ), - //spacer - const SizedBox(height: 10), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( + buttonColor: MihColors.getGreenColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: confirmPasswordController, - multiLineInput: false, - requiredText: true, - hintText: "Confirm Password", - passwordMode: true, - autofillHints: const [AutofillHints.password], - validator: (value) { - return MihValidationServices().validatePassword(value); - }, - ), - //spacer - const SizedBox(height: 25), - // sign in button - Center( - child: MihButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - submitFormInput(); - } else { - MihAlertServices().inputErrorAlert(context); - } - }, - buttonColor: MihColors.getGreenColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - width: 300, - child: Text( - "Reset Password", - style: TextStyle( - color: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - fontSize: 20, - fontWeight: FontWeight.bold, - ), + width: 300, + child: Text( + "Reset Password", + style: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 20, + fontWeight: FontWeight.bold, ), ), ), - ], - ), - ], - ), + ), + ], + ), + ], ), ), ), diff --git a/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_sign_in.dart b/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_sign_in.dart index 69c327f0..1e64837d 100644 --- a/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_sign_in.dart +++ b/mih_ui/lib/mih_packages/mih_authentication/package_tools/mih_sign_in.dart @@ -202,6 +202,7 @@ class _MihSignInState extends State { } }, child: MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: MzansiInnovationHub.of(context)!.theme.screenType == "desktop" diff --git a/mih_ui/lib/mih_packages/mih_home/components/mih_app_drawer.dart b/mih_ui/lib/mih_packages/mih_home/components/mih_app_drawer.dart index 45a0e89f..71ec53f7 100644 --- a/mih_ui/lib/mih_packages/mih_home/components/mih_app_drawer.dart +++ b/mih_ui/lib/mih_packages/mih_home/components/mih_app_drawer.dart @@ -78,6 +78,7 @@ class _MIHAppDrawerState extends State { ? mzansiProfileProvider.userProfilePicture : mzansiProfileProvider.businessProfilePicture, width: 60, + expandable: false, editable: false, fileNameController: proPicController, onChange: (_) {}, diff --git a/mih_ui/lib/mih_packages/mih_home/mih_home.dart b/mih_ui/lib/mih_packages/mih_home/mih_home.dart index f3f49482..aaa59ed2 100644 --- a/mih_ui/lib/mih_packages/mih_home/mih_home.dart +++ b/mih_ui/lib/mih_packages/mih_home/mih_home.dart @@ -353,6 +353,7 @@ class _MihHomeState extends State { key: Key(imageKey), imageFile: currentImage, width: 50, + expandable: false, editable: false, fileNameController: null, userSelectedfile: null, diff --git a/mih_ui/lib/mih_packages/mine_sweeper/builders/build_minesweeper_leaderboard_list.dart b/mih_ui/lib/mih_packages/mine_sweeper/builders/build_minesweeper_leaderboard_list.dart index 7f870c46..2b9ed288 100644 --- a/mih_ui/lib/mih_packages/mine_sweeper/builders/build_minesweeper_leaderboard_list.dart +++ b/mih_ui/lib/mih_packages/mine_sweeper/builders/build_minesweeper_leaderboard_list.dart @@ -104,6 +104,7 @@ class _BuildMinesweeperLeaderboardListState key: UniqueKey(), imageFile: imageFile, width: 80, + expandable: true, editable: false, fileNameController: null, userSelectedfile: null, diff --git a/mih_ui/lib/mih_packages/mine_sweeper/components/leaderboard_user_ranking.dart b/mih_ui/lib/mih_packages/mine_sweeper/components/leaderboard_user_ranking.dart index 4bd48355..338189b7 100644 --- a/mih_ui/lib/mih_packages/mine_sweeper/components/leaderboard_user_ranking.dart +++ b/mih_ui/lib/mih_packages/mine_sweeper/components/leaderboard_user_ranking.dart @@ -50,6 +50,7 @@ class LeaderboardUserRanking extends StatelessWidget { .toString()), // Use ValueKey for stable identity imageFile: asyncSnapshot.data, width: 60, + expandable: true, editable: false, fileNameController: null, userSelectedfile: null, diff --git a/mih_ui/lib/mih_packages/mine_sweeper/package_tools/mine_sweeper_game.dart b/mih_ui/lib/mih_packages/mine_sweeper/package_tools/mine_sweeper_game.dart index adc07fda..f400c98b 100644 --- a/mih_ui/lib/mih_packages/mine_sweeper/package_tools/mine_sweeper_game.dart +++ b/mih_ui/lib/mih_packages/mine_sweeper/package_tools/mine_sweeper_game.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.dart'; @@ -626,6 +627,7 @@ class _MineSweeperGameState extends State { alignment: Alignment.topCenter, children: [ MihSingleChildScroll( + scrollbarOn: true, child: board.isEmpty && squaresLeft < 0 // Start Up Message before setting up game ? Padding( @@ -850,7 +852,9 @@ class _MineSweeperGameState extends State { ], ), ), - _timer != null ? MihBannerAd() : SizedBox(), + _timer != null && (Platform.isAndroid || Platform.isIOS) + ? MihBannerAd() + : SizedBox(), SizedBox(height: 15), ], ); diff --git a/mih_ui/lib/mih_packages/mine_sweeper/package_tools/mine_sweeper_quick_start_guide.dart b/mih_ui/lib/mih_packages/mine_sweeper/package_tools/mine_sweeper_quick_start_guide.dart index 939b7055..5dcd790b 100644 --- a/mih_ui/lib/mih_packages/mine_sweeper/package_tools/mine_sweeper_quick_start_guide.dart +++ b/mih_ui/lib/mih_packages/mine_sweeper/package_tools/mine_sweeper_quick_start_guide.dart @@ -845,6 +845,7 @@ class _MineSweeperQuickStartGuideState Widget getBody(double width) { return MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: EdgeInsets.symmetric(horizontal: width / 20), child: Column( diff --git a/mih_ui/lib/mih_packages/mine_sweeper/package_tools/my_score_board.dart b/mih_ui/lib/mih_packages/mine_sweeper/package_tools/my_score_board.dart index 31e18824..25433358 100644 --- a/mih_ui/lib/mih_packages/mine_sweeper/package_tools/my_score_board.dart +++ b/mih_ui/lib/mih_packages/mine_sweeper/package_tools/my_score_board.dart @@ -87,6 +87,7 @@ class _MihMineSweeperLeaderBoardState extends State { child: MihCircleAvatar( imageFile: profileProvider.userProfilePicture, width: 150, + expandable: true, editable: false, fileNameController: null, userSelectedfile: null, diff --git a/mih_ui/lib/mih_packages/mzansi_ai/package_tools/mih_ai_chat.dart b/mih_ui/lib/mih_packages/mzansi_ai/package_tools/mih_ai_chat.dart index f4da1b6c..2ec0671e 100644 --- a/mih_ui/lib/mih_packages/mzansi_ai/package_tools/mih_ai_chat.dart +++ b/mih_ui/lib/mih_packages/mzansi_ai/package_tools/mih_ai_chat.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -112,15 +113,35 @@ class _MihAiChatState extends State with WidgetsBindingObserver { void speakLastMessage(MzansiAiProvider aiProvider) { final history = aiProvider.ollamaProvider.history; - if (history.isNotEmpty) { - final historyList = history.toList(); - for (int i = historyList.length - 1; i >= 0; i--) { - if (historyList[i].origin == MessageOrigin.llm && - historyList[i].text != null && - historyList[i].text!.isNotEmpty) { - _flutterTts.speak(historyList[i].text!); - return; - } + if (history.isEmpty) return; + + final historyList = history.toList(); + String? textToSpeak; + + // Find the last LLM message + for (int i = historyList.length - 1; i >= 0; i--) { + if (historyList[i].origin == MessageOrigin.llm && + historyList[i].text != null && + historyList[i].text!.isNotEmpty) { + textToSpeak = historyList[i].text!; + break; + } + } + + if (textToSpeak != null) { + if (Platform.isLinux) { + // Linux Workaround: Use Speech Dispatcher (standard on most distros) + // '-t female1' is optional for voice variety + Process.run('spd-say', [textToSpeak]); + + // Since spd-say doesn't have an easy "completion handler" via CLI, + // we manually toggle the UI state or just leave it off. + aiProvider.setTTSstate(true); + Future.delayed( + Duration(seconds: 5), () => aiProvider.setTTSstate(false)); + } else { + // Your existing mobile/web logic + _flutterTts.speak(textToSpeak); } } } @@ -183,11 +204,16 @@ class _MihAiChatState extends State with WidgetsBindingObserver { // } void stopTTS(MzansiAiProvider aiProvider) { - _flutterTts.stop(); + if (Platform.isLinux) { + Process.run('spd-say', ['-S']); // The -S flag stops current speech + } else { + _flutterTts.stop(); + } aiProvider.setTTSstate(false); } Future initTts(MzansiAiProvider aiProvider) async { + if (Platform.isLinux) return; try { await _flutterTts.setSpeechRate(!kIsWeb ? 0.55 : 1); // await _flutterTts.setLanguage("en-US"); @@ -258,7 +284,9 @@ class _MihAiChatState extends State with WidgetsBindingObserver { @override void dispose() { - _flutterTts.stop(); + if (!Platform.isLinux) { + _flutterTts.stop(); + } WidgetsBinding.instance.removeObserver(this); super.dispose(); } diff --git a/mih_ui/lib/mih_packages/mzansi_directory/package_tools/mih_contacts.dart b/mih_ui/lib/mih_packages/mzansi_directory/package_tools/mih_contacts.dart index cc7c8f98..4814d109 100644 --- a/mih_ui/lib/mih_packages/mzansi_directory/package_tools/mih_contacts.dart +++ b/mih_ui/lib/mih_packages/mzansi_directory/package_tools/mih_contacts.dart @@ -28,6 +28,7 @@ class _MihContactsState extends State { Widget getBody(double width) { return MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Padding( diff --git a/mih_ui/lib/mih_packages/mzansi_directory/package_tools/mih_search_mzansi.dart b/mih_ui/lib/mih_packages/mzansi_directory/package_tools/mih_search_mzansi.dart index fa2fc911..2fa17e2b 100644 --- a/mih_ui/lib/mih_packages/mzansi_directory/package_tools/mih_search_mzansi.dart +++ b/mih_ui/lib/mih_packages/mzansi_directory/package_tools/mih_search_mzansi.dart @@ -330,6 +330,7 @@ class _MihSearchMzansiState extends State { } else if (directoryProvider.searchedBusinesses.isEmpty && directoryProvider.searchTerm.isNotEmpty) { return MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ const SizedBox(height: 50), @@ -357,6 +358,7 @@ class _MihSearchMzansiState extends State { } else if (directoryProvider.searchedBusinesses.isEmpty && directoryProvider.searchTerm.isEmpty) { return MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 10.0), child: Column( @@ -472,6 +474,7 @@ class _MihSearchMzansiState extends State { } else if (directoryProvider.searchedUsers.isEmpty && directoryProvider.searchTerm.isEmpty) { return MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 10.0), child: Column( @@ -533,6 +536,7 @@ class _MihSearchMzansiState extends State { } else if (directoryProvider.searchedUsers.isEmpty && directoryProvider.searchTerm.isNotEmpty) { return MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ const SizedBox(height: 50), diff --git a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart index 539c4f52..4b1d22cd 100644 --- a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart +++ b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart @@ -316,6 +316,7 @@ class _MihReviewBusinessWindowState extends State { ] : null, windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: MzansiInnovationHub.of(context)!.theme.screenType == "desktop" diff --git a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/components/mih_update_business_details_window.dart b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/components/mih_update_business_details_window.dart index 67b1c937..80f294ec 100644 --- a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/components/mih_update_business_details_window.dart +++ b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/components/mih_update_business_details_window.dart @@ -251,278 +251,45 @@ class _MihUpdateBusinessDetailsWindowState context.pop(); }, windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: MzansiInnovationHub.of(context)!.theme.screenType == "desktop" ? EdgeInsets.symmetric(horizontal: widget.width * 0.05) : EdgeInsets.symmetric(horizontal: widget.width * 0), - child: Column( + child: Stack( children: [ - MihForm( - formKey: _formKey, - formFields: [ - Center( - child: MihCircleAvatar( - imageFile: newSelectedLogoPic != null - ? MemoryImage(newSelectedLogoPic!.bytes!) - : mzansiProfileProvider.businessProfilePicture, - width: 150, - editable: true, - fileNameController: fileNameController, - userSelectedfile: newSelectedLogoPic, - frameColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - backgroundColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - onChange: (selectedfile) { - setState(() { - newSelectedLogoPic = selectedfile; - }); - }, - ), - ), - Visibility( - visible: false, - child: MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: fileNameController, - multiLineInput: false, - requiredText: true, - readOnly: true, - hintText: "Selected File Name", - ), - ), - const SizedBox(height: 20), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: nameController, - multiLineInput: false, - requiredText: true, - hintText: "Business Name", - validator: (value) { - return MihValidationServices().isEmpty(value); - }, - ), - const SizedBox(height: 10), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: typeController, - multiLineInput: false, - requiredText: true, - hintText: "Business Type", - validator: (value) { - return MihValidationServices() - .validateNoSpecialChars(value); - }, - ), - const SizedBox(height: 10), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: emailController, - multiLineInput: false, - requiredText: true, - hintText: "Business Email", - validator: (value) { - return MihValidationServices().validateEmail(value); - }, - ), - const SizedBox(height: 10), - Container( - width: 300, - alignment: Alignment.topLeft, - child: const Text( - "Contact Number:", - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - ), - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - CountryCodePicker( - padding: EdgeInsetsGeometry.all(0), - onChanged: (selectedCode) { - setState(() { - countryCodeController.text = - selectedCode.toString(); - }); - debugPrint( - "Selected Country Code: ${countryCodeController.text}"); - }, - initialSelection: countryCodeController.text, - showDropDownButton: false, - pickerStyle: PickerStyle.bottomSheet, - dialogBackgroundColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - barrierColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - ), - Expanded( - child: MihTextFormField( - fillColor: MihColors.getSecondaryColor( + Column( + children: [ + MihForm( + formKey: _formKey, + formFields: [ + Center( + child: MihCircleAvatar( + imageFile: newSelectedLogoPic != null + ? MemoryImage(newSelectedLogoPic!.bytes!) + : mzansiProfileProvider + .businessProfilePicture, + width: 150, + expandable: false, + editable: true, + fileNameController: fileNameController, + userSelectedfile: newSelectedLogoPic, + frameColor: MihColors.getSecondaryColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - inputColor: MihColors.getPrimaryColor( + backgroundColor: MihColors.getPrimaryColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - controller: contactController, - numberMode: true, - multiLineInput: false, - requiredText: true, - hintText: null, - validator: (value) { - return MihValidationServices().isEmpty(value); + onChange: (selectedfile) { + setState(() { + newSelectedLogoPic = selectedfile; + }); }, ), ), - ], - ), - const SizedBox(height: 10), - MihTextFormField( - height: 250, - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: missionVisionController, - multiLineInput: true, - requiredText: true, - hintText: "Business Mission & Vision", - validator: (value) { - return MihValidationServices().validateLength( - missionVisionController.text, 256); - }, - ), - SizedBox( - height: 15, - child: ValueListenableBuilder( - valueListenable: _counter, - builder: - (BuildContext context, int value, Widget? child) { - return Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Text( - "$value", - style: TextStyle( - color: getMissionVisionLimitColor(256), - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(width: 5), - Text( - "/256", - style: TextStyle( - color: getMissionVisionLimitColor(256), - fontWeight: FontWeight.bold, - ), - ), - ], - ); - }, - ), - ), - const SizedBox(height: 10.0), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: websiteController, - multiLineInput: false, - requiredText: false, - hintText: "Business Website", - validator: (value) { - return MihValidationServices() - .validateWebsite(value, false); - }, - ), - const SizedBox(height: 10), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: regController, - multiLineInput: false, - requiredText: false, - hintText: "Registration No.", - validator: (value) { - // return MihValidationServices().isEmpty(value); - return null; - }, - ), - const SizedBox(height: 10), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: practiceNoController, - multiLineInput: false, - requiredText: false, - hintText: "Practice Number", - validator: (validateValue) { - return null; - }, - ), - const SizedBox(height: 10), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: vatNoController, - multiLineInput: false, - requiredText: false, - hintText: "VAT Number", - validator: (value) { - // return MihValidationServices().isEmpty(value); - return null; - }, - ), - const SizedBox(height: 10), - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Flexible( + Visibility( + visible: false, child: MihTextFormField( fillColor: MihColors.getSecondaryColor( MzansiInnovationHub.of(context)!.theme.mode == @@ -530,84 +297,374 @@ class _MihUpdateBusinessDetailsWindowState inputColor: MihColors.getPrimaryColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - controller: locationController, + controller: fileNameController, multiLineInput: false, requiredText: true, readOnly: true, - hintText: "GPS Location", + hintText: "Selected File Name", ), ), - const SizedBox(width: 10.0), - MihButton( - onPressed: () { - showDialog( - context: context, - builder: (context) { - return const Mihloadingcircle( - message: "Getting your location", - ); - }, - ); - MIHLocationAPI() - .getGPSPosition(context) - .then((position) { - if (position != null) { - setState(() { - locationController.text = - "${position.latitude}, ${position.longitude}"; - }); - } - //Dismiss loading indicator - context.pop(); - }); - }, - buttonColor: MihColors.getSecondaryColor( + const SizedBox(height: 20), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - width: 100, - child: Text( - "Set", + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: nameController, + multiLineInput: false, + requiredText: true, + hintText: "Business Name", + validator: (value) { + return MihValidationServices().isEmpty(value); + }, + ), + const SizedBox(height: 10), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: typeController, + multiLineInput: false, + requiredText: true, + hintText: "Business Type", + validator: (value) { + return MihValidationServices() + .validateNoSpecialChars(value); + }, + ), + const SizedBox(height: 10), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: emailController, + multiLineInput: false, + requiredText: true, + hintText: "Business Email", + validator: (value) { + return MihValidationServices() + .validateEmail(value); + }, + ), + const SizedBox(height: 10), + Container( + width: 300, + alignment: Alignment.topLeft, + child: const Text( + "Contact Number:", style: TextStyle( - color: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)! - .theme - .mode == - "Dark"), - fontSize: 20, + fontSize: 18, fontWeight: FontWeight.bold, ), ), ), - ], - ), - const SizedBox(height: 25), - Center( - child: MihButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - submitForm(mzansiProfileProvider); - } else { - MihAlertServices().inputErrorAlert(context); - } - }, - buttonColor: MihColors.getGreenColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - width: 300, - child: Text( - "Update", - style: TextStyle( - color: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - fontSize: 20, - fontWeight: FontWeight.bold, + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + CountryCodePicker( + padding: EdgeInsetsGeometry.all(0), + onChanged: (selectedCode) { + setState(() { + countryCodeController.text = + selectedCode.toString(); + }); + debugPrint( + "Selected Country Code: ${countryCodeController.text}"); + }, + initialSelection: countryCodeController.text, + showDropDownButton: false, + pickerStyle: PickerStyle.bottomSheet, + dialogBackgroundColor: + MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + barrierColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + ), + Expanded( + child: MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + controller: contactController, + numberMode: true, + multiLineInput: false, + requiredText: true, + hintText: null, + validator: (value) { + return MihValidationServices() + .isEmpty(value); + }, + ), + ), + ], + ), + const SizedBox(height: 10), + MihTextFormField( + height: 250, + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: missionVisionController, + multiLineInput: true, + requiredText: true, + hintText: "Business Mission & Vision", + validator: (value) { + return MihValidationServices().validateLength( + missionVisionController.text, 256); + }, + ), + SizedBox( + height: 15, + child: ValueListenableBuilder( + valueListenable: _counter, + builder: (BuildContext context, int value, + Widget? child) { + return Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + "$value", + style: TextStyle( + color: getMissionVisionLimitColor(256), + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(width: 5), + Text( + "/256", + style: TextStyle( + color: getMissionVisionLimitColor(256), + fontWeight: FontWeight.bold, + ), + ), + ], + ); + }, ), ), + const SizedBox(height: 10.0), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: websiteController, + multiLineInput: false, + requiredText: false, + hintText: "Business Website", + validator: (value) { + return MihValidationServices() + .validateWebsite(value, false); + }, + ), + const SizedBox(height: 10), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: regController, + multiLineInput: false, + requiredText: false, + hintText: "Registration No.", + validator: (value) { + // return MihValidationServices().isEmpty(value); + return null; + }, + ), + const SizedBox(height: 10), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: practiceNoController, + multiLineInput: false, + requiredText: false, + hintText: "Practice Number", + validator: (validateValue) { + return null; + }, + ), + const SizedBox(height: 10), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: vatNoController, + multiLineInput: false, + requiredText: false, + hintText: "VAT Number", + validator: (value) { + // return MihValidationServices().isEmpty(value); + return null; + }, + ), + const SizedBox(height: 10), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Flexible( + child: MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + controller: locationController, + multiLineInput: false, + requiredText: true, + readOnly: true, + hintText: "GPS Location", + ), + ), + const SizedBox(width: 10.0), + MihButton( + onPressed: () { + showDialog( + context: context, + builder: (context) { + return const Mihloadingcircle( + message: "Getting your location", + ); + }, + ); + MIHLocationAPI() + .getGPSPosition(context) + .then((position) { + if (position != null) { + setState(() { + locationController.text = + "${position.latitude}, ${position.longitude}"; + }); + } + //Dismiss loading indicator + context.pop(); + }); + }, + buttonColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + width: 100, + child: Text( + "Set", + style: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + const SizedBox(height: 25), + Center( + child: MihButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + submitForm(mzansiProfileProvider); + } else { + MihAlertServices().inputErrorAlert(context); + } + }, + buttonColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + width: 300, + child: Text( + "Update", + style: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + const SizedBox(height: 20), + ], + ), + ], + ), + Positioned( + right: 0, + top: 0, + child: MihButton( + onPressed: () { + //Add validation here + if (_formKey.currentState!.validate()) { + submitForm(mzansiProfileProvider); + } else { + MihAlertServices().inputErrorAlert(context); + } + }, + buttonColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + width: 100, + height: 25, + child: Text( + mzansiProfileProvider.user!.username.isEmpty + ? "Setup Profile" + : "Update", + style: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 15, + fontWeight: FontWeight.bold, ), ), - const SizedBox(height: 20), - ], + ), ), ], ), diff --git a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/components/mih_update_my_business_user_details.dart b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/components/mih_update_my_business_user_details.dart index 89082b31..7217dbc7 100644 --- a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/components/mih_update_my_business_user_details.dart +++ b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/components/mih_update_my_business_user_details.dart @@ -5,7 +5,6 @@ import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_env.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart'; -import 'package:mzansi_innovation_hub/mih_package_components/mih_circle_avatar.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_form.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_image_display.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_package_window.dart'; @@ -127,190 +126,224 @@ class _MihUpdateMyBusinessUserDetailsState builder: (BuildContext context, MzansiProfileProvider mzansiProfileProvider, Widget? child) { return MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: MzansiInnovationHub.of(context)!.theme.screenType == "desktop" ? EdgeInsets.symmetric(horizontal: width * 0.2) : EdgeInsets.symmetric(horizontal: width * 0.075), - child: Column( + child: Stack( children: [ - MihForm( - formKey: _formKey, - formFields: [ - Center( - child: MihCircleAvatar( - imageFile: mzansiProfileProvider.userProfilePicture, - width: 150, - editable: false, - fileNameController: fileNameController, - userSelectedfile: userPicFile, - frameColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - backgroundColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - onChange: (_) {}, - ), - ), - Visibility( - visible: false, - child: MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: fileNameController, - multiLineInput: false, - requiredText: true, - readOnly: true, - hintText: "Selected File Name", - ), - ), - const SizedBox(height: 10), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: titleTextController, - multiLineInput: false, - requiredText: true, - readOnly: false, - hintText: "Title", - validator: (value) { - return MihValidationServices().isEmpty(value); - }, - ), - const SizedBox(height: 10), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: fnameController, - multiLineInput: false, - requiredText: true, - readOnly: true, - hintText: "First Name", - validator: (value) { - return MihValidationServices().isEmpty(value); - }, - ), - const SizedBox(height: 10), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: lnameController, - multiLineInput: false, - requiredText: true, - readOnly: true, - hintText: "Surname", - validator: (value) { - return MihValidationServices().isEmpty(value); - }, - ), - const SizedBox(height: 10), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: accessController, - multiLineInput: false, - requiredText: true, - hintText: "Access Level", - readOnly: true, - validator: (value) { - return MihValidationServices().isEmpty(value); - }, - ), - const SizedBox(height: 10), - Container( - width: 300, - alignment: Alignment.topLeft, - child: const Text( - "Signature:", - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - ), - Center( - child: MihImageDisplay( - imageFile: newSelectedSignaturePic != null - ? MemoryImage(newSelectedSignaturePic!.bytes!) - : mzansiProfileProvider.businessUserSignature, - width: 300, - height: 200, - editable: true, - fileNameController: signtureController, - userSelectedfile: newSelectedSignaturePic, - onChange: (selectedFile) { - setState(() { - newSelectedSignaturePic = selectedFile; - }); - }, - ), - ), - const SizedBox(height: 10), - Visibility( - visible: false, - child: MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: fileNameController, - multiLineInput: false, - requiredText: true, - readOnly: true, - hintText: "Selected Signature File", - ), - ), - const SizedBox(height: 15), - Center( - child: MihButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - submitForm(mzansiProfileProvider); - } else { - MihAlertServices().inputErrorAlert(context); - } - }, - buttonColor: MihColors.getGreenColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - width: 300, - child: Text( - "Update", - style: TextStyle( - color: MihColors.getPrimaryColor( + Column( + children: [ + MihForm( + formKey: _formKey, + formFields: [ + // Center( + // child: MihCircleAvatar( + // imageFile: mzansiProfileProvider.userProfilePicture, + // width: 150, + // editable: false, + // fileNameController: fileNameController, + // userSelectedfile: userPicFile, + // frameColor: MihColors.getSecondaryColor( + // MzansiInnovationHub.of(context)!.theme.mode == + // "Dark"), + // backgroundColor: MihColors.getPrimaryColor( + // MzansiInnovationHub.of(context)!.theme.mode == + // "Dark"), + // onChange: (_) {}, + // ), + // ), + Visibility( + visible: false, + child: MihTextFormField( + fillColor: MihColors.getSecondaryColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - fontSize: 20, - fontWeight: FontWeight.bold, + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: fileNameController, + multiLineInput: false, + requiredText: true, + readOnly: true, + hintText: "Selected File Name", ), ), + const SizedBox(height: 10), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: titleTextController, + multiLineInput: false, + requiredText: true, + readOnly: false, + hintText: "Title", + validator: (value) { + return MihValidationServices().isEmpty(value); + }, + ), + const SizedBox(height: 10), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: fnameController, + multiLineInput: false, + requiredText: true, + readOnly: true, + hintText: "First Name", + validator: (value) { + return MihValidationServices().isEmpty(value); + }, + ), + const SizedBox(height: 10), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: lnameController, + multiLineInput: false, + requiredText: true, + readOnly: true, + hintText: "Surname", + validator: (value) { + return MihValidationServices().isEmpty(value); + }, + ), + const SizedBox(height: 10), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: accessController, + multiLineInput: false, + requiredText: true, + hintText: "Access Level", + readOnly: true, + validator: (value) { + return MihValidationServices().isEmpty(value); + }, + ), + const SizedBox(height: 10), + Container( + width: 300, + alignment: Alignment.topLeft, + child: const Text( + "Signature:", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ), + Center( + child: MihImageDisplay( + imageFile: newSelectedSignaturePic != null + ? MemoryImage(newSelectedSignaturePic!.bytes!) + : mzansiProfileProvider.businessUserSignature, + width: 300, + height: 200, + editable: true, + fileNameController: signtureController, + userSelectedfile: newSelectedSignaturePic, + onChange: (selectedFile) { + setState(() { + newSelectedSignaturePic = selectedFile; + }); + }, + ), + ), + const SizedBox(height: 10), + Visibility( + visible: false, + child: MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: fileNameController, + multiLineInput: false, + requiredText: true, + readOnly: true, + hintText: "Selected Signature File", + ), + ), + const SizedBox(height: 15), + Center( + child: MihButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + submitForm(mzansiProfileProvider); + } else { + MihAlertServices().inputErrorAlert(context); + } + }, + buttonColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + width: 300, + child: Text( + "Update", + style: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + const SizedBox(height: 20), + ], + ), + ], + ), + Positioned( + top: 0, + right: 0, + child: MihButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + submitForm(mzansiProfileProvider); + } else { + MihAlertServices().inputErrorAlert(context); + } + }, + buttonColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + width: 100, + height: 25, + child: Text( + "Update", + style: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 15, + fontWeight: FontWeight.bold, ), ), - const SizedBox(height: 20), - ], + ), ), ], ), diff --git a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart index 29e12043..9b5df943 100644 --- a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart +++ b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart @@ -1,9 +1,10 @@ +import 'package:custom_rating_bar/custom_rating_bar.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_package_components/mih_business_info_card_v2.dart'; import 'package:mzansi_innovation_hub/mih_providers/mzansi_profile_provider.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; -import 'package:mzansi_innovation_hub/mih_package_components/mih_business_info_card.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/components/mih_update_business_details_window.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_single_child_scroll.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart'; @@ -60,32 +61,63 @@ class _MihBusinessDetailsState extends State { return Stack( children: [ MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: MzansiInnovationHub.of(context)!.theme.screenType == "desktop" ? EdgeInsets.symmetric(horizontal: width * 0.2) - : EdgeInsets.symmetric(horizontal: width * 0.075), + : EdgeInsets.symmetric(horizontal: width * 0), child: Column( children: [ Center( - child: MihCircleAvatar( - key: UniqueKey(), - imageFile: mzansiProfileProvider.businessProfilePicture, - width: 150, - editable: false, - fileNameController: fileNameController, - userSelectedfile: newSelectedLogoPic, - frameColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - backgroundColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - onChange: (selectedfile) { - setState(() { - newSelectedLogoPic = selectedfile; - }); - }, + child: Stack( + children: [ + MihCircleAvatar( + key: UniqueKey(), + imageFile: + mzansiProfileProvider.businessProfilePicture, + width: 150, + expandable: true, + editable: false, + fileNameController: fileNameController, + userSelectedfile: newSelectedLogoPic, + frameColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + backgroundColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + onChange: (selectedfile) { + setState(() { + newSelectedLogoPic = selectedfile; + }); + }, + ), + Positioned( + bottom: 5, + right: 5, + child: MihButton( + onPressed: () { + // editProfileWindow(width); + editBizProfileWindow( + mzansiProfileProvider, width); + }, + buttonColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + width: 35, + height: 35, + child: Icon( + Icons.edit, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + ), + ), + ), + ], ), ), FittedBox( @@ -112,6 +144,30 @@ class _MihBusinessDetailsState extends State { ), ), ), + RatingBar.readOnly( + size: 50, + alignment: Alignment.center, + filledIcon: Icons.star, + emptyIcon: Icons.star_border, + halfFilledIcon: Icons.star_half, + filledColor: MihColors.getYellowColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + // MihColors.getPrimaryColor(MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + emptyColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + halfFilledColor: MihColors.getYellowColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + // MihColors.getPrimaryColor(MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + isHalfAllowed: true, + initialRating: mzansiProfileProvider + .business!.rating.isNotEmpty + ? double.parse(mzansiProfileProvider.business!.rating) + : 0, + maxRating: 5, + ), const SizedBox(height: 5), Center( child: SizedBox( @@ -133,68 +189,16 @@ class _MihBusinessDetailsState extends State { ), ), const SizedBox(height: 20), - SizedBox( - width: 700, - child: MihBusinessCard( - business: mzansiProfileProvider.business!, - // startUpSearch: null, - width: width, - ), + MihBusinessCardV2( + business: mzansiProfileProvider.business!, + // startUpSearch: null, + width: width, ), const SizedBox(height: 30.0), - Center( - child: MihButton( - onPressed: () { - // Connect with the user - editBizProfileWindow(mzansiProfileProvider, width); - }, - buttonColor: MihColors.getGreenColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - width: 300, - child: Text( - "Edit Profile", - style: TextStyle( - color: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), - ), - ), ], ), ), ), - // Positioned( - // right: 5, - // bottom: 10, - // child: MihFloatingMenu( - // animatedIcon: AnimatedIcons.menu_close, - // children: [ - // SpeedDialChild( - // child: Icon( - // Icons.edit, - // color: MihColors.getPrimaryColor(MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - // ), - // label: "Edit Profile", - // labelBackgroundColor: - // MihColors.getGreenColor(MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - // labelStyle: TextStyle( - // color: MihColors.getPrimaryColor(MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - // fontWeight: FontWeight.bold, - // ), - // backgroundColor: - // MihColors.getGreenColor(MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - // onTap: () { - // editBizProfileWindow(width); - // }, - // ) - // ], - // ), - // ), ], ); }, diff --git a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_set_up.dart b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_set_up.dart index a02ef2e7..9564774e 100644 --- a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_set_up.dart +++ b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_set_up.dart @@ -330,6 +330,7 @@ class _MihBusinessDetailsSetUpState extends State { ? MemoryImage(newSelectedLogoPic!.bytes!) : mzansiProfileProvider.businessProfilePicture, width: 150, + expandable: false, editable: true, fileNameController: logoFileNameController, userSelectedfile: newSelectedLogoPic, @@ -765,7 +766,7 @@ class _MihBusinessDetailsSetUpState extends State { "Dark"), width: 300, child: Text( - "Add", + "Set Up Buasiness", style: TextStyle( color: MihColors.getPrimaryColor( MzansiInnovationHub.of(context)!.theme.mode == diff --git a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart index ea6a1edf..5663c9d7 100644 --- a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart +++ b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart @@ -3,10 +3,10 @@ import 'package:custom_rating_bar/custom_rating_bar.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_package_components/mih_business_info_card_v2.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_icons.dart'; import 'package:mzansi_innovation_hub/mih_providers/mzansi_directory_provider.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; -import 'package:mzansi_innovation_hub/mih_package_components/mih_business_info_card.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_file_services.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_single_child_scroll.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tool_body.dart'; @@ -58,11 +58,12 @@ class _MihBusinessDetailsViewState extends State { return Stack( children: [ MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: MzansiInnovationHub.of(context)!.theme.screenType == "desktop" ? EdgeInsets.symmetric(horizontal: width * 0.2) - : EdgeInsets.symmetric(horizontal: width * 0.075), + : EdgeInsets.symmetric(horizontal: width * 0), child: Column( children: [ FutureBuilder( @@ -76,6 +77,7 @@ class _MihBusinessDetailsViewState extends State { imageFile: CachedNetworkImageProvider( asyncSnapshot.requireData), width: profilePictureWidth, + expandable: true, editable: false, fileNameController: TextEditingController(), userSelectedfile: file, @@ -154,40 +156,6 @@ class _MihBusinessDetailsViewState extends State { ), ), ), - const SizedBox(height: 5), - // FittedBox( - // child: Text( - // "Mission & Vision", - // style: TextStyle( - // fontSize: 15, - // fontWeight: FontWeight.bold, - // color: MzansiInnovationHub.of(context)! - // .theme - // .secondaryColor(), - // ), - // ), - // ), - Center( - child: SizedBox( - width: 700, - child: Text( - directoryProvider - .selectedBusiness!.mission_vision.isNotEmpty - ? directoryProvider - .selectedBusiness!.mission_vision - : "No Mission & Vision added yet", - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.bold, - color: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - ), - ), - ), - ), - const SizedBox(height: 10), RatingBar.readOnly( size: 50, alignment: Alignment.center, @@ -213,14 +181,32 @@ class _MihBusinessDetailsViewState extends State { : 0, maxRating: 5, ), - const SizedBox(height: 20), - SizedBox( - width: 700, - child: MihBusinessCard( - business: directoryProvider.selectedBusiness!, - width: width, + const SizedBox(height: 5), + Center( + child: SizedBox( + width: 700, + child: Text( + directoryProvider + .selectedBusiness!.mission_vision.isNotEmpty + ? directoryProvider + .selectedBusiness!.mission_vision + : "No Mission & Vision added yet", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + ), + ), ), ), + const SizedBox(height: 20), + MihBusinessCardV2( + business: directoryProvider.selectedBusiness!, + width: width, + ), ], ), ), diff --git a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_qr_code.dart b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_qr_code.dart index f1d695f8..ab1142fa 100644 --- a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_qr_code.dart +++ b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_qr_code.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:cached_network_image/cached_network_image.dart'; import 'package:file_picker/file_picker.dart'; import 'package:file_saver/file_saver.dart'; @@ -82,6 +84,19 @@ class _MihBusinessQrCodeState extends State { fileExtension: "png", mimeType: MimeType.png, ); + } else if (defaultTargetPlatform == TargetPlatform.linux || + defaultTargetPlatform == TargetPlatform.windows) { + // Use File Picker to get a save path on Desktop + String? outputFile = await FilePicker.platform.saveFile( + dialogTitle: 'Please select where to save your QR Code:', + fileName: filename, + ); + + if (outputFile != null) { + final file = File(outputFile); + await file.writeAsBytes(imageBytes); + KenLogger.success("Saved to $outputFile"); + } } else { await FileSaver.instance.saveAs( name: filename, @@ -95,14 +110,14 @@ class _MihBusinessQrCodeState extends State { Future downloadQrCode() async { if (_isUserSignedIn) { await screenshotController.capture().then((image) { - KenLogger.success("Image Captured: $image"); + // KenLogger.success("Image Captured: $image"); setState(() { businessQRImageFile = image; }); }).catchError((onError) { KenLogger.error(onError); }); - KenLogger.success("QR Code Image Captured : $businessQRImageFile"); + // KenLogger.success("QR Code Image Captured : $businessQRImageFile"); saveImage(businessQRImageFile!); } else { showSignInRequiredAlert(); @@ -211,6 +226,7 @@ class _MihBusinessQrCodeState extends State { imageFile: CachedNetworkImageProvider( asyncSnapshot.requireData), width: profilePictureWidth, + expandable: true, editable: false, fileNameController: TextEditingController(), userSelectedfile: file, @@ -298,7 +314,9 @@ class _MihBusinessQrCodeState extends State { height: 300, child: CachedNetworkImage( imageUrl: getQrCodeData(qrSize.toInt()), - placeholder: (context, url) => const Mihloadingcircle(), + placeholder: (context, url) => FittedBox( + child: const Mihloadingcircle(), + ), errorWidget: (context, url, error) => const Icon(Icons.error), ), @@ -367,6 +385,7 @@ class _MihBusinessQrCodeState extends State { alignment: Alignment.topCenter, children: [ MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 15.0), child: Padding( diff --git a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_my_business_user.dart b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_my_business_user.dart index f78114f1..db4e2235 100644 --- a/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_my_business_user.dart +++ b/mih_ui/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_my_business_user.dart @@ -175,6 +175,7 @@ class _MihMyBusinessUserState extends State { return Stack( children: [ MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: MzansiInnovationHub.of(context)!.theme.screenType == "desktop" @@ -183,19 +184,47 @@ class _MihMyBusinessUserState extends State { child: Column( children: [ Center( - child: MihCircleAvatar( - imageFile: mzansiProfileProvider.userProfilePicture, - width: 150, - editable: false, - fileNameController: fileNameController, - userSelectedfile: userPicFile, - frameColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - backgroundColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - onChange: (_) {}, + child: Stack( + children: [ + MihCircleAvatar( + imageFile: mzansiProfileProvider.userProfilePicture, + width: 150, + expandable: true, + editable: false, + fileNameController: fileNameController, + userSelectedfile: userPicFile, + frameColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + backgroundColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + onChange: (_) {}, + ), + Positioned( + bottom: 5, + right: 5, + child: MihButton( + onPressed: () { + editBizUserProfileWindow( + mzansiProfileProvider, width); + }, + buttonColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + width: 35, + height: 35, + child: Icon( + Icons.edit, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + ), + ), + ), + ], ), ), const SizedBox(height: 20), @@ -245,28 +274,6 @@ class _MihMyBusinessUserState extends State { ), ), const SizedBox(height: 20), - Center( - child: MihButton( - onPressed: () { - editBizUserProfileWindow( - mzansiProfileProvider, width); - }, - buttonColor: MihColors.getGreenColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - width: 300, - child: Text( - "Edit Profile", - style: TextStyle( - color: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), - ), - ), ], ), ), diff --git a/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/components/mih_edit_personal_profile_window.dart b/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/components/mih_edit_personal_profile_window.dart index c0ad5736..5fb60204 100644 --- a/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/components/mih_edit_personal_profile_window.dart +++ b/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/components/mih_edit_personal_profile_window.dart @@ -329,195 +329,233 @@ class _MihEditPersonalProfileWindowState MzansiInnovationHub.of(context)!.theme.screenType == "desktop" ? EdgeInsets.symmetric(horizontal: screenWidth * 0.05) : EdgeInsets.symmetric(horizontal: screenWidth * 0), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, + child: Stack( children: [ - MihForm( - formKey: _formKey, - formFields: [ - Center( - child: MihCircleAvatar( - imageFile: newSelectedProPic != null - ? MemoryImage(newSelectedProPic!.bytes!) - : mzansiProfileProvider.userProfilePicture, - width: 150, - editable: true, - fileNameController: proPicController, - userSelectedfile: newSelectedProPic, - frameColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - backgroundColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - onChange: (selectedImage) { - setState(() { - newSelectedProPic = selectedImage; - }); - }, - ), - ), - // const SizedBox(height: 25.0), - Visibility( - visible: false, - child: MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: proPicController, - multiLineInput: false, - requiredText: true, - readOnly: true, - hintText: "Selected File Name", - ), - ), - const SizedBox(height: 10.0), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: usernameController, - multiLineInput: false, - requiredText: true, - hintText: "Username", - validator: (value) { - return MihValidationServices().validateUsername(value); - }, - ), - const SizedBox(height: 10.0), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: fnameController, - multiLineInput: false, - requiredText: true, - hintText: "First Name", - validator: (value) { - return MihValidationServices().isEmpty(value); - }, - ), - const SizedBox(height: 10.0), - MihTextFormField( - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: lnameController, - multiLineInput: false, - requiredText: true, - hintText: "Last Name", - validator: (value) { - return MihValidationServices().isEmpty(value); - }, - ), - const SizedBox(height: 10.0), - MihTextFormField( - height: 250, - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - inputColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - controller: purposeController, - multiLineInput: true, - requiredText: true, - hintText: "Your Personal Mission", - validator: (value) { - return MihValidationServices() - .validateLength(purposeController.text, 256); - }, - ), - SizedBox( - height: 15, - child: ValueListenableBuilder( - valueListenable: _counter, - builder: - (BuildContext context, int value, Widget? child) { - return Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Text( - "$value", - style: TextStyle( - color: getPurposeLimitColor(256), - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(width: 5), - Text( - "/256", - style: TextStyle( - color: getPurposeLimitColor(256), - fontWeight: FontWeight.bold, - ), - ), - ], - ); - }, - ), - ), - const SizedBox(height: 10.0), - MihToggle( - hintText: "Activate Business Account", - initialPostion: businessUser, - fillColor: MihColors.getSecondaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - secondaryFillColor: MihColors.getPrimaryColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - onChange: (value) { - setState(() { - businessUser = value; - }); - KenLogger.success("Business User: $businessUser"); - }, - ), - const SizedBox(height: 30.0), - Center( - child: MihButton( - onPressed: () { - //Add validation here - if (_formKey.currentState!.validate()) { - submitForm(mzansiProfileProvider); - } else { - MihAlertServices().inputErrorAlert(context); - } - }, - buttonColor: MihColors.getGreenColor( - MzansiInnovationHub.of(context)!.theme.mode == - "Dark"), - width: 300, - child: Text( - mzansiProfileProvider.user!.username.isEmpty - ? "Setup Profile" - : "Update", - style: TextStyle( - color: MihColors.getPrimaryColor( + Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + MihForm( + formKey: _formKey, + formFields: [ + Center( + child: MihCircleAvatar( + imageFile: newSelectedProPic != null + ? MemoryImage(newSelectedProPic!.bytes!) + : mzansiProfileProvider.userProfilePicture, + width: 150, + expandable: false, + editable: true, + fileNameController: proPicController, + userSelectedfile: newSelectedProPic, + frameColor: MihColors.getSecondaryColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), - fontSize: 20, - fontWeight: FontWeight.bold, + backgroundColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + onChange: (selectedImage) { + setState(() { + newSelectedProPic = selectedImage; + }); + }, ), ), - ), + // const SizedBox(height: 25.0), + Visibility( + visible: false, + child: MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: proPicController, + multiLineInput: false, + requiredText: true, + readOnly: true, + hintText: "Selected File Name", + ), + ), + const SizedBox(height: 10.0), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: usernameController, + multiLineInput: false, + requiredText: true, + hintText: "Username", + validator: (value) { + return MihValidationServices() + .validateUsername(value); + }, + ), + const SizedBox(height: 10.0), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: fnameController, + multiLineInput: false, + requiredText: true, + hintText: "First Name", + validator: (value) { + return MihValidationServices().isEmpty(value); + }, + ), + const SizedBox(height: 10.0), + MihTextFormField( + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: lnameController, + multiLineInput: false, + requiredText: true, + hintText: "Last Name", + validator: (value) { + return MihValidationServices().isEmpty(value); + }, + ), + const SizedBox(height: 10.0), + MihTextFormField( + height: 250, + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + inputColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + controller: purposeController, + multiLineInput: true, + requiredText: true, + hintText: "Your Personal Mission", + validator: (value) { + return MihValidationServices() + .validateLength(purposeController.text, 256); + }, + ), + SizedBox( + height: 15, + child: ValueListenableBuilder( + valueListenable: _counter, + builder: (BuildContext context, int value, + Widget? child) { + return Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + "$value", + style: TextStyle( + color: getPurposeLimitColor(256), + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(width: 5), + Text( + "/256", + style: TextStyle( + color: getPurposeLimitColor(256), + fontWeight: FontWeight.bold, + ), + ), + ], + ); + }, + ), + ), + const SizedBox(height: 10.0), + MihToggle( + hintText: "Activate Business Account", + initialPostion: businessUser, + fillColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + secondaryFillColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + onChange: (value) { + setState(() { + businessUser = value; + }); + KenLogger.success("Business User: $businessUser"); + }, + ), + const SizedBox(height: 30.0), + Center( + child: MihButton( + onPressed: () { + //Add validation here + if (_formKey.currentState!.validate()) { + submitForm(mzansiProfileProvider); + } else { + MihAlertServices().inputErrorAlert(context); + } + }, + buttonColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + width: 300, + child: Text( + mzansiProfileProvider.user!.username.isEmpty + ? "Setup Profile" + : "Update", + style: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)! + .theme + .mode == + "Dark"), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], ), ], ), + Positioned( + right: 0, + top: 0, + child: MihButton( + onPressed: () { + //Add validation here + if (_formKey.currentState!.validate()) { + submitForm(mzansiProfileProvider); + } else { + MihAlertServices().inputErrorAlert(context); + } + }, + buttonColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + width: 100, + height: 25, + child: Text( + mzansiProfileProvider.user!.username.isEmpty + ? "Setup Profile" + : "Update", + style: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + ), + ), ], ), ), diff --git a/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile.dart b/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile.dart index 2c36bea8..622a3adc 100644 --- a/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile.dart +++ b/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile.dart @@ -146,6 +146,7 @@ class _MihPersonalProfileState extends State { ); } else { return MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: MzansiInnovationHub.of(context)!.theme.screenType == "desktop" @@ -159,6 +160,7 @@ class _MihPersonalProfileState extends State { MihCircleAvatar( imageFile: mzansiProfileProvider.userProfilePicture, width: 150, + expandable: true, editable: false, fileNameController: proPicController, userSelectedfile: newSelectedProPic, diff --git a/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile_view.dart b/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile_view.dart index d95e9ad0..1b39d6fa 100644 --- a/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile_view.dart +++ b/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile_view.dart @@ -54,6 +54,7 @@ class _MihPersonalProfileViewState extends State { builder: (BuildContext context, MzansiDirectoryProvider directoryProvider, Widget? child) { return MihSingleChildScroll( + scrollbarOn: true, child: Padding( padding: MzansiInnovationHub.of(context)!.theme.screenType == "desktop" @@ -73,6 +74,7 @@ class _MihPersonalProfileViewState extends State { imageFile: CachedNetworkImageProvider( asyncSnapshot.requireData), width: profilePictureWidth, + expandable: true, editable: false, fileNameController: TextEditingController(), userSelectedfile: file, diff --git a/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_settings.dart b/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_settings.dart index a12f410b..aff6f1b3 100644 --- a/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_settings.dart +++ b/mih_ui/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_settings.dart @@ -81,6 +81,7 @@ class _MihPersonalSettingsState extends State { Widget getBody(MzansiProfileProvider mzansiProfileProvider) { return MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Center( diff --git a/mih_ui/lib/mih_packages/mzansi_wallet/builder/build_loyalty_card_list.dart b/mih_ui/lib/mih_packages/mzansi_wallet/builder/build_loyalty_card_list.dart index 9a7e84bf..be1cde25 100644 --- a/mih_ui/lib/mih_packages/mzansi_wallet/builder/build_loyalty_card_list.dart +++ b/mih_ui/lib/mih_packages/mzansi_wallet/builder/build_loyalty_card_list.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.dart'; import 'package:go_router/go_router.dart'; @@ -545,7 +547,7 @@ class _BuildLoyaltyCardListState extends State { ), ), SizedBox(height: 10), - MihBannerAd() + if (Platform.isAndroid || Platform.isIOS) MihBannerAd() // MihBannerAd(), ], ), @@ -572,7 +574,7 @@ class _BuildLoyaltyCardListState extends State { } Future setScreenBrightness(double newBrightness) async { - if (!kIsWeb) { + if (!kIsWeb && !Platform.isLinux) { bool canChange = await ScreenBrightness.instance.canChangeSystemBrightness; diff --git a/mih_ui/lib/mih_packages/patient_manager/pat_profile/package_tools/patient_info.dart b/mih_ui/lib/mih_packages/patient_manager/pat_profile/package_tools/patient_info.dart index 1579db92..01a80abd 100644 --- a/mih_ui/lib/mih_packages/patient_manager/pat_profile/package_tools/patient_info.dart +++ b/mih_ui/lib/mih_packages/patient_manager/pat_profile/package_tools/patient_info.dart @@ -306,6 +306,7 @@ class _PatientInfoState extends State { return Stack( children: [ MihSingleChildScroll( + scrollbarOn: true, child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ @@ -313,6 +314,7 @@ class _PatientInfoState extends State { imageFile: patientManagerProvider.selectedPatientProfilePicture, width: 160, + expandable: true, editable: false, fileNameController: null, userSelectedfile: null, diff --git a/mih_ui/lib/mih_providers/mih_banner_ad_provider.dart b/mih_ui/lib/mih_providers/mih_banner_ad_provider.dart index 394bcbb4..e37594db 100644 --- a/mih_ui/lib/mih_providers/mih_banner_ad_provider.dart +++ b/mih_ui/lib/mih_providers/mih_banner_ad_provider.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_env.dart'; @@ -28,35 +30,37 @@ class MihBannerAdProvider extends ChangeNotifier { } void loadBannerAd() { - if (bannerAd != null) { - bannerAd!.dispose(); - bannerAd = null; - isBannerAdLoaded = false; + if (Platform.isAndroid || Platform.isIOS) { + if (bannerAd != null) { + bannerAd!.dispose(); + bannerAd = null; + isBannerAdLoaded = false; + } + bannerAd = BannerAd( + adUnitId: adUnitId, + request: const AdRequest(), + size: AdSize.banner, + listener: BannerAdListener( + onAdLoaded: (ad) { + debugPrint('$ad loaded.'); + isBannerAdLoaded = true; + notifyListeners(); + }, + onAdFailedToLoad: (ad, err) { + debugPrint('BannerAd failed to load: $err'); + errorMessage = + 'Failed to load ad- Message: ${err.message} Code :${err.code}'; + ad.dispose(); // Dispose the ad to free resources + isBannerAdLoaded = false; // ⬅️ Explicitly set to false + bannerAd = null; // ⬅️ Explicitly set to null + notifyListeners(); + }, + onAdOpened: (Ad ad) => debugPrint('$ad opened.'), + onAdClosed: (Ad ad) => debugPrint('$ad closed.'), + onAdImpression: (Ad ad) => debugPrint('$ad impression.'), + ), + ); + bannerAd!.load(); } - bannerAd = BannerAd( - adUnitId: adUnitId, - request: const AdRequest(), - size: AdSize.banner, - listener: BannerAdListener( - onAdLoaded: (ad) { - debugPrint('$ad loaded.'); - isBannerAdLoaded = true; - notifyListeners(); - }, - onAdFailedToLoad: (ad, err) { - debugPrint('BannerAd failed to load: $err'); - errorMessage = - 'Failed to load ad- Message: ${err.message} Code :${err.code}'; - ad.dispose(); // Dispose the ad to free resources - isBannerAdLoaded = false; // ⬅️ Explicitly set to false - bannerAd = null; // ⬅️ Explicitly set to null - notifyListeners(); - }, - onAdOpened: (Ad ad) => debugPrint('$ad opened.'), - onAdClosed: (Ad ad) => debugPrint('$ad closed.'), - onAdImpression: (Ad ad) => debugPrint('$ad impression.'), - ), - ); - bannerAd!.load(); } } diff --git a/mih_ui/lib/mih_providers/mzansi_ai_provider.dart b/mih_ui/lib/mih_providers/mzansi_ai_provider.dart index be9c7f2b..19d00ae7 100644 --- a/mih_ui/lib/mih_providers/mzansi_ai_provider.dart +++ b/mih_ui/lib/mih_providers/mzansi_ai_provider.dart @@ -20,7 +20,7 @@ class MzansiAiProvider extends ChangeNotifier { ollamaProvider = OllamaProvider( baseUrl: "${AppEnviroment.baseAiUrl}/api", model: AppEnviroment.getEnv() == "Prod" - ? 'qwen3-vl:8b' + ? 'qwen3-vl:8b-instruct' : "qwen3-vl:2b-instruct", think: false, systemPrompt: "---INSTRUCTION START---\n" diff --git a/mih_ui/lib/mih_services/mih_alert_services.dart b/mih_ui/lib/mih_services/mih_alert_services.dart index df8fbefd..c2e4e721 100644 --- a/mih_ui/lib/mih_services/mih_alert_services.dart +++ b/mih_ui/lib/mih_services/mih_alert_services.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:mzansi_innovation_hub/main.dart'; @@ -32,6 +34,7 @@ class MihAlertServices { backgroundColor: MihColors.getRedColor( MzansiInnovationHub.of(context)!.theme.mode != "Dark"), windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Icon( @@ -109,6 +112,7 @@ class MihAlertServices { backgroundColor: MihColors.getRedColor( MzansiInnovationHub.of(context)!.theme.mode != "Dark"), windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Icon( @@ -132,7 +136,9 @@ class MihAlertServices { ), const SizedBox(height: 15), Text( - "To get the most out of MIH, we need your location. Please go to the site settings of the app and enable location services. Once you do that, we can start showing you relevant information based on your location.", + Platform.isLinux + ? "To get the most out of MIH, we need your location. Please go to your System Settings and enable location services. Once you do that, we can start showing you relevant information based on your location." + : "To get the most out of MIH, we need your location. Please go to the site settings of the app and enable location services. Once you do that, we can start showing you relevant information based on your location.", style: TextStyle( color: MihColors.getSecondaryColor( MzansiInnovationHub.of(context)!.theme.mode == @@ -186,6 +192,7 @@ class MihAlertServices { backgroundColor: MihColors.getRedColor( MzansiInnovationHub.of(context)!.theme.mode != "Dark"), windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Icon( @@ -298,6 +305,7 @@ class MihAlertServices { backgroundColor: MihColors.getRedColor( MzansiInnovationHub.of(context)!.theme.mode != "Dark"), windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Icon( @@ -416,6 +424,7 @@ class MihAlertServices { backgroundColor: MihColors.getRedColor( MzansiInnovationHub.of(context)!.theme.mode != "Dark"), windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Icon( @@ -493,6 +502,7 @@ class MihAlertServices { backgroundColor: MihColors.getRedColor( MzansiInnovationHub.of(context)!.theme.mode != "Dark"), windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Icon( @@ -594,6 +604,7 @@ class MihAlertServices { backgroundColor: MihColors.getRedColor( MzansiInnovationHub.of(context)!.theme.mode != "Dark"), windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Icon( @@ -671,6 +682,7 @@ class MihAlertServices { backgroundColor: MihColors.getRedColor( MzansiInnovationHub.of(context)!.theme.mode != "Dark"), windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Icon( @@ -748,6 +760,7 @@ class MihAlertServices { backgroundColor: MihColors.getSecondaryColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Icon( @@ -830,6 +843,7 @@ class MihAlertServices { backgroundColor: MihColors.getSecondaryColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Icon( @@ -900,6 +914,7 @@ class MihAlertServices { backgroundColor: MihColors.getRedColor( MzansiInnovationHub.of(context)!.theme.mode != "Dark"), windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Icon( @@ -1007,6 +1022,7 @@ class MihAlertServices { backgroundColor: MihColors.getGreenColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Icon( @@ -1089,6 +1105,7 @@ class MihAlertServices { backgroundColor: MihColors.getGreenColor( MzansiInnovationHub.of(context)!.theme.mode == "Dark"), windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Icon( @@ -1159,6 +1176,7 @@ class MihAlertServices { backgroundColor: MihColors.getRedColor( MzansiInnovationHub.of(context)!.theme.mode != "Dark"), windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Icon( @@ -1244,6 +1262,7 @@ class MihAlertServices { backgroundColor: MihColors.getRedColor( MzansiInnovationHub.of(context)!.theme.mode != "Dark"), windowBody: MihSingleChildScroll( + scrollbarOn: true, child: Column( children: [ Icon( diff --git a/mih_ui/lib/mih_services/mih_authentication_services.dart b/mih_ui/lib/mih_services/mih_authentication_services.dart index 3b1f6cd7..08b59175 100644 --- a/mih_ui/lib/mih_services/mih_authentication_services.dart +++ b/mih_ui/lib/mih_services/mih_authentication_services.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:go_router/go_router.dart'; import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart'; @@ -32,7 +33,7 @@ class MihAuthenticationServices { headers: { 'Content-type': 'application/json', 'Accept': 'application/json', - "Authorization": "leatucczyixqwkqqdrhayiwzeofkltds" + "Authorization": dotenv.env['SUPERTOKENS_API_KEY'] ?? "", }, ); if (response.statusCode == 200) { diff --git a/mih_ui/lib/mih_services/mih_file_services.dart b/mih_ui/lib/mih_services/mih_file_services.dart index c9106c2c..9dd0c8a8 100644 --- a/mih_ui/lib/mih_services/mih_file_services.dart +++ b/mih_ui/lib/mih_services/mih_file_services.dart @@ -1,7 +1,10 @@ import 'dart:convert'; +import 'dart:io'; import 'package:file_picker/file_picker.dart'; +import 'package:flutter/foundation.dart'; import 'package:go_router/go_router.dart'; +import 'package:ken_logger/ken_logger.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_loading_circle.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_env.dart'; import 'package:flutter/material.dart'; @@ -51,6 +54,15 @@ class MihFileApi { } finally { // Navigator.of(context).pop(); // Always pop loading dialog } + KenLogger.success("File URL: $fileUrl"); + if (AppEnviroment.getEnv() == "Dev" && kIsWeb) { + fileUrl = fileUrl.replaceAll("10.0.2.2", "127.0.0.1"); + } else if (AppEnviroment.getEnv() == "Dev" && Platform.isIOS) { + fileUrl = fileUrl.replaceAll("10.0.2.2", "127.0.0.1"); + } else if (AppEnviroment.getEnv() == "Dev" && Platform.isLinux) { + fileUrl = fileUrl.replaceAll("10.0.2.2", "127.0.0.1"); + } + KenLogger.success("File URL: $fileUrl"); return fileUrl; } diff --git a/mih_ui/lib/mih_services/mih_location_services.dart b/mih_ui/lib/mih_services/mih_location_services.dart index a27c6e61..a18d745c 100644 --- a/mih_ui/lib/mih_services/mih_location_services.dart +++ b/mih_ui/lib/mih_services/mih_location_services.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_alert_services.dart'; @@ -13,6 +15,12 @@ class MIHLocationAPI { ///if user has blocked permission (denied or denied forver), user will get error pop up. ///if user has granted permission (while in use), function will return Position object. Future getGPSPosition(BuildContext context) async { + bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); + if (!serviceEnabled && Platform.isLinux) { + // Direct the user to their System Settings + MihAlertServices().locationPermissionAlert(context); + return null; + } print("Before checkPermission"); // Debug LocationPermission permission = await Geolocator.checkPermission(); print("After checkPermission: $permission"); // Debug diff --git a/mih_ui/linux/my_application.cc b/mih_ui/linux/my_application.cc index 8758cffa..8f518d2d 100644 --- a/mih_ui/linux/my_application.cc +++ b/mih_ui/linux/my_application.cc @@ -40,11 +40,11 @@ static void my_application_activate(GApplication* application) { if (use_header_bar) { GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "patient_manager"); + gtk_header_bar_set_title(header_bar, "MIH App - Mzansi Innovation Hub"); gtk_header_bar_set_show_close_button(header_bar, TRUE); gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); } else { - gtk_window_set_title(window, "patient_manager"); + gtk_window_set_title(window, "MIH App - Mzansi Innovation Hub"); } gtk_window_set_default_size(window, 1280, 720); diff --git a/mih_ui/pubspec.lock b/mih_ui/pubspec.lock index 41003e1b..a253f2b4 100644 --- a/mih_ui/pubspec.lock +++ b/mih_ui/pubspec.lock @@ -369,6 +369,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.0" + dbus: + dependency: transitive + description: + name: dbus + sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270 + url: "https://pub.dev" + source: hosted + version: "0.7.12" device_info_plus: dependency: transitive description: @@ -670,6 +678,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.0" + flutter_dotenv: + dependency: "direct main" + description: + name: flutter_dotenv + sha256: d4130c4a43e0b13fefc593bc3961f2cb46e30cb79e253d4a526b1b5d24ae1ce4 + url: "https://pub.dev" + source: hosted + version: "6.0.0" flutter_launcher_icons: dependency: "direct main" description: @@ -800,6 +816,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" + geoclue: + dependency: transitive + description: + name: geoclue + sha256: c2a998c77474fc57aa00c6baa2928e58f4b267649057a1c76738656e9dbd2a7f + url: "https://pub.dev" + source: hosted + version: "0.1.1" geolocator: dependency: "direct main" description: @@ -824,6 +848,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.13" + geolocator_linux: + dependency: "direct main" + description: + name: geolocator_linux + sha256: d64112a205931926f4363bb6bd48f14cb38e7326833041d170615586cd143797 + url: "https://pub.dev" + source: hosted + version: "0.2.4" geolocator_platform_interface: dependency: transitive description: @@ -896,6 +928,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + gsettings: + dependency: transitive + description: + name: gsettings + sha256: "1b0ce661f5436d2db1e51f3c4295a49849f03d304003a7ba177d01e3a858249c" + url: "https://pub.dev" + source: hosted + version: "0.2.8" html: dependency: transitive description: diff --git a/mih_ui/pubspec.yaml b/mih_ui/pubspec.yaml index cd20d55a..2614956c 100644 --- a/mih_ui/pubspec.yaml +++ b/mih_ui/pubspec.yaml @@ -1,7 +1,7 @@ name: mzansi_innovation_hub description: "" publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 1.2.5+126 +version: 1.2.6+130 # version: 1.1.1+97 #--- Updated version for upgrader package testing environment: @@ -12,6 +12,7 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter + flutter_dotenv: ^6.0.0 cupertino_icons: ^1.0.8 font_awesome_flutter: ^10.7.0 @@ -28,6 +29,7 @@ dependencies: flutter_native_splash: ^2.4.6 printing: ^5.13.3 geolocator: ^14.0.1 + geolocator_linux: ^0.2.4 table_calendar: ^3.1.2 youtube_player_iframe: ^5.2.0 mobile_scanner: ^7.0.1 @@ -76,6 +78,7 @@ dev_dependencies: flutter: uses-material-design: true assets: + - .env - lib/mih_package_components/assets/images/ - lib/mih_package_components/assets/fonts/ - lib/mih_package_components/assets/images/loyalty_cards/ diff --git a/mih_ui/server/MIH_web_server.py b/mih_ui/server/MIH_web_server.py deleted file mode 100644 index c4bd7eb0..00000000 --- a/mih_ui/server/MIH_web_server.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python - -# Inspired by https://gist.github.com/jtangelder/e445e9a7f5e31c220be6 -# Python3 http.server for Single Page Application - -import urllib.parse -import http.server -import socketserver -import re -from pathlib import Path -port = 83 -HOST = ('', port) -pattern = re.compile('.png|.jpg|.jpeg|.js|.css|.ico|.gif|.svg|.ico', re.IGNORECASE) - - -class Handler(http.server.SimpleHTTPRequestHandler): - def do_GET(self): - url_parts = urllib.parse.urlparse(self.path) - request_file_path = Path(url_parts.path.strip("/")) - - ext = request_file_path.suffix - if not request_file_path.is_file() and not pattern.match(ext): - self.path = 'index.html' - - return http.server.SimpleHTTPRequestHandler.do_GET(self) - - -httpd = socketserver.TCPServer(HOST, Handler) -print(f"Starting Web App Server on pot: {port}") -httpd.serve_forever() diff --git a/mih_ui/server/server.sh b/mih_ui/server/server.sh deleted file mode 100644 index 2fb5d8b7..00000000 --- a/mih_ui/server/server.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# Define the port -PORT=83 - -# Check if the port is in use and release it if necessary. -# echo "Checking if port $PORT is in use..." -# if [ "$(lsof -t -i :$PORT)" ]; then -# echo "Port $PORT is in use. Stopping the process on that port..." -# fuser -k -n tcp $PORT -# fi - -# Switch to the web construction directory -cd /app/build/web/ - -# Start the web server on the specified port -#python3 -m http.server 83 -python3 -u ../../server/MIH_web_server.py \ No newline at end of file