5 Commits

9 changed files with 498 additions and 353 deletions

View File

@@ -7,18 +7,18 @@ services:
#============== Nginx Proxy Manager ==================================================================== #============== Nginx Proxy Manager ====================================================================
mih-nginx: mih-nginx:
container_name: mih-nginx container_name: mih-nginx
image: 'jc21/nginx-proxy-manager:latest' image: "jc21/nginx-proxy-manager:latest"
restart: unless-stopped restart: unless-stopped
ports: ports:
- '80:80' # Public HTTP - "80:80" # Public HTTP
- '443:443' # Public HTTPS - "443:443" # Public HTTPS
- '127.0.0.1:81:81' # Admin Web Port - "127.0.0.1:81:81" # Admin Web Port
volumes: volumes:
- ./mih_nginx/data:/data - ./mih_nginx/data:/data
- ./mih_nginx/letsencrypt:/etc/letsencrypt - ./mih_nginx/letsencrypt:/etc/letsencrypt
networks: networks:
- mih-network - mih-network
#============== GITEA ==================================================================== #============== GITEA ====================================================================
mih-gitea: mih-gitea:
image: gitea/gitea:latest image: gitea/gitea:latest
container_name: mih-gitea container_name: mih-gitea
@@ -68,11 +68,11 @@ services:
depends_on: depends_on:
- mih-db - mih-db
ports: ports:
- '127.0.0.1:3567:3567' - "127.0.0.1:3567:3567"
environment: environment:
REFRESH_TOKEN_VALIDITY: '604800' REFRESH_TOKEN_VALIDITY: "604800"
ACCESS_TOKEN_VALIDITY: '86400' ACCESS_TOKEN_VALIDITY: "86400"
PASSWORD_RESET_TOKEN_LIFETIME: '7200000' PASSWORD_RESET_TOKEN_LIFETIME: "7200000"
MYSQL_USER: ${SQL_USER} MYSQL_USER: ${SQL_USER}
MYSQL_PASSWORD: ${SQL_USER_PW} MYSQL_PASSWORD: ${SQL_USER_PW}
MYSQL_HOST: mih-db MYSQL_HOST: mih-db
@@ -94,7 +94,7 @@ services:
image: wordpress image: wordpress
restart: always restart: always
ports: ports:
- '127.0.0.1:8081:80' - "127.0.0.1:8081:80"
environment: environment:
WORDPRESS_DB_HOST: mih-wp-db WORDPRESS_DB_HOST: mih-wp-db
WORDPRESS_DB_USER: ${WP_SQL_USER} WORDPRESS_DB_USER: ${WP_SQL_USER}
@@ -112,7 +112,7 @@ services:
MARIADB_DATABASE: ${WP_SQL_DB} MARIADB_DATABASE: ${WP_SQL_DB}
MARIADB_USER: ${WP_SQL_USER} MARIADB_USER: ${WP_SQL_USER}
MARIADB_PASSWORD: ${WP_SQL_USER_PW} MARIADB_PASSWORD: ${WP_SQL_USER_PW}
MARIADB_RANDOM_ROOT_PASSWORD: '1' MARIADB_RANDOM_ROOT_PASSWORD: "1"
volumes: volumes:
- ./mih_wp/database:/var/lib/mysql - ./mih_wp/database:/var/lib/mysql
networks: networks:
@@ -156,7 +156,7 @@ services:
networks: networks:
- mih-network - mih-network
ports: ports:
- '127.0.0.1:3306:3306' - "127.0.0.1:3306:3306"
volumes: volumes:
- ./mih_db:/var/lib/mysql - ./mih_db:/var/lib/mysql
#============== PHP My Admin ==================================================================== #============== PHP My Admin ====================================================================
@@ -182,10 +182,10 @@ services:
hostname: mih-minio hostname: mih-minio
image: minio/minio image: minio/minio
ports: ports:
- '127.0.0.1:9000:9000' - "127.0.0.1:9000:9000"
- '127.0.0.1:9001:9001' - "127.0.0.1:9001:9001"
volumes: volumes:
- './mih_minio:/data' - "./mih_minio:/data"
environment: environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER} MINIO_ROOT_USER: ${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PW} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PW}
@@ -197,7 +197,7 @@ services:
container_name: mih-monitor container_name: mih-monitor
image: portainer/portainer-ce:2.20.3 image: portainer/portainer-ce:2.20.3
ports: ports:
- '127.0.0.1:9444:9443' - "127.0.0.1:9444:9443"
volumes: volumes:
- ./mih_monitor/data:/data - ./mih_monitor/data:/data
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
@@ -207,9 +207,9 @@ services:
#============== MIH-AI Ollama ==================================================================== #============== MIH-AI Ollama ====================================================================
mih-ai: mih-ai:
container_name: mih-ai container_name: mih-ai
image: ollama/ollama:latest image: ollama/ollama:0.21.2
ports: ports:
- '127.0.0.1:11434:11434' - "127.0.0.1:11434:11434"
volumes: volumes:
- ./mih_ai/ollama/ollama:/root/.ollama - ./mih_ai/ollama/ollama:/root/.ollama
pull_policy: always pull_policy: always
@@ -222,14 +222,14 @@ services:
networks: networks:
- mih-network - mih-network
# === Added section for NVIDIA GPU acceleration === # === Added section for NVIDIA GPU acceleration ===
# runtime: nvidia # runtime: nvidia
# deploy: # deploy:
# resources: # resources:
# reservations: # reservations:
# devices: # devices:
# - driver: nvidia # - driver: nvidia
# count: all # or specify a number of GPUs # count: all # or specify a number of GPUs
# capabilities: [ gpu ] # capabilities: [ gpu ]
#============== Firebaase ==================================================================== #============== Firebaase ====================================================================
# firebase: # firebase:
# container_name: MIH-firebase-emulator # container_name: MIH-firebase-emulator

View File

@@ -42,6 +42,7 @@ st_api_key = os.getenv("SUPERTOKENS_API_KEY")
origins = [ origins = [
"http://localhost", "http://localhost",
"http://localhost:80", "http://localhost:80",
"http://localhost:83",
"http://localhost:1995", "http://localhost:1995",
"http://localhost:8080", "http://localhost:8080",
"http://MIH-API-Hub:80", "http://MIH-API-Hub:80",

View File

@@ -3,17 +3,19 @@ FROM debian:latest AS build-env
# Install necessary dependencies for Flutter # Install necessary dependencies for Flutter
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \
curl git wget unzip libglu1-mesa fonts-droid-fallback python3 \ curl git wget unzip libglu1-mesa fonts-droid-fallback python3 \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Clone Flutter SDK # 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
ENV PATH="/usr/local/flutter/bin:/usr/local/flutter/bin/cache/dart-sdk/bin:${PATH}" ENV PATH="/usr/local/flutter/bin:/usr/local/flutter/bin/cache/dart-sdk/bin:${PATH}"
# Build the Flutter web app # Build the Flutter web app
RUN flutter config --enable-web && flutter precache --web
WORKDIR /app WORKDIR /app
COPY pubspec.yaml pubspec.lock ./
RUN flutter pub get
COPY . . COPY . .
RUN flutter config --enable-web
RUN flutter build web --release -t ./lib/main_prod.dart RUN flutter build web --release -t ./lib/main_prod.dart
# --- STAGE 2: The Final Production Image --- # --- STAGE 2: The Final Production Image ---
@@ -24,13 +26,13 @@ COPY --from=build-env /app/build/web /usr/share/nginx/html
# Create the Nginx config inside the Dockerfile to handle SPA routing # Create the Nginx config inside the Dockerfile to handle SPA routing
RUN echo 'server { \ RUN echo 'server { \
listen 83; \ listen 83; \
location / { \ location / { \
root /usr/share/nginx/html; \ root /usr/share/nginx/html; \
index index.html; \ index index.html; \
try_files $uri $uri/ /index.html; \ try_files $uri $uri/ /index.html; \
} \ } \
}' > /etc/nginx/conf.d/default.conf }' > /etc/nginx/conf.d/default.conf
EXPOSE 83 EXPOSE 83
CMD ["nginx", "-g", "daemon off;"] CMD ["nginx", "-g", "daemon off;"]

View File

@@ -7,6 +7,7 @@ import 'package:ken_logger/ken_logger.dart';
import 'package:mih_package_toolkit/mih_package_toolkit.dart'; import 'package:mih_package_toolkit/mih_package_toolkit.dart';
import 'package:mzansi_innovation_hub/main.dart'; 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_package_components/mih_banner_ad.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_wallet/components/mih_card_display_slanted.dart';
import 'package:mzansi_innovation_hub/mih_providers/mzansi_profile_provider.dart'; import 'package:mzansi_innovation_hub/mih_providers/mzansi_profile_provider.dart';
import 'package:mzansi_innovation_hub/mih_providers/mzansi_wallet_provider.dart'; import 'package:mzansi_innovation_hub/mih_providers/mzansi_wallet_provider.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_alert_services.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_alert_services.dart';
@@ -727,14 +728,18 @@ class _BuildLoyaltyCardListState extends State<BuildLoyaltyCardList> {
), ),
itemCount: widget.cardList.length, itemCount: widget.cardList.length,
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
mainAxisSpacing: 5, mainAxisSpacing: 0,
// mainAxisSpacing: 15,
// crossAxisSpacing: 15,
crossAxisSpacing: 5, crossAxisSpacing: 5,
maxCrossAxisExtent: 200, maxCrossAxisExtent: 200,
// childAspectRatio: 0.80, // childAspectRatio: 0.80,
), ),
itemBuilder: (context, index) { itemBuilder: (context, index) {
return GestureDetector( return GestureDetector(
child: MihCardDisplay( child: MihCardDisplaySlanted(
// child: MihCardDisplay(
height: 100,
shopName: widget.cardList[index].shop_name, shopName: widget.cardList[index].shop_name,
nickname: widget.cardList[index].nickname, nickname: widget.cardList[index].nickname,
), ),

View File

@@ -0,0 +1,185 @@
import 'package:flutter/material.dart';
class MihCardDisplaySlanted extends StatefulWidget {
final String shopName;
final String nickname;
final double height;
const MihCardDisplaySlanted({
super.key,
required this.shopName,
required this.height,
required this.nickname,
});
@override
State<MihCardDisplaySlanted> createState() => _MihCardDisplaySlantedState();
}
class _MihCardDisplaySlantedState extends State<MihCardDisplaySlanted> {
Widget? displayLoyaltyCard() {
switch (widget.shopName.toLowerCase()) {
case "apple tree":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/apple_tree-min.png');
case "best before":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/best_before-min.png');
case "checkers":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/checkers-min.png');
case "clicks":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/clicks-min.png');
case "cotton:on":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/cotton_on-min.png');
case "dis-chem":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/dischem-min.png');
case "pick n pay":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/pick_n_pay-min.png');
case "shoprite":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/shoprite-min.png');
case "spar":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/spar-min.png');
case "woolworths":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/woolworths-min.png');
case "makro":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/makro-min.png');
case "fresh stop":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/fresh_stop-min.png');
case "panarottis":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/panarottis-min.png');
case "shell":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/shell-min.png');
case "edgars":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/edgars-min.png');
case "jet":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/jet-min.png');
case "spur":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/spur-min.png');
case "infinity":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/infinity-min.png');
case "eskom":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/eskom-min.png');
case "+more":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/plus_more-min.png');
case "bp":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/bp-min.png');
case "builders warehouse":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/builders-min.png');
case "exclusive books":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/exclusive_books-min.png');
case "pna":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/pna-min.png');
case "pq clothing":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/pq-min.png');
case "rage":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/rage-min.png');
case "sasol":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/sasol-min.png');
case "tfg group":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/tfg-min.png');
case "toys r us":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/toysrus-min.png');
case "leroy merlin":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/leroy_merlin-min.png');
case "signature cosmetics & fragrances":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/signature_cosmetics-min.png');
case "ok foods":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/ok_food-min.png');
case "choppies":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/choppies-min.png');
case "boxer":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/boxer-min.png');
case "carrefour":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/carrefour-min.png');
case "sefalana":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/sefalana-min.png');
case "big save":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/big_save-min.png');
case "justrite":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/justrite-min.png');
case "naivas":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/naivas-min.png');
case "kero":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/kero-min.png');
case "auchan":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/auchan-min.png');
case "woermann brock":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/woermann_brock-min.png');
case "continente":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/continente-min.png');
case "fresmart":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/fresmart-min.png');
case "total energies":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/total_energies-min.png');
case "engen":
return Image.asset(
'lib/mih_package_components/assets/images/loyalty_cards/mini/engen-min.png');
default:
return null;
}
}
@override
Widget build(BuildContext context) {
return Visibility(
visible: displayLoyaltyCard() != null,
child: Column(
children: [
displayLoyaltyCard() != null ? displayLoyaltyCard()! : SizedBox(),
FittedBox(
child: Text(
widget.nickname,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
)
],
),
);
}
}

View File

@@ -133,10 +133,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: build_modules name: build_modules
sha256: b1fc29a603669b25a5d95cc9610ed649e9f00e6075e5b6b721aa1a095cff13de sha256: "51422a5753a74fda433d4345b11ce6ad40c2033880a26b2c6b7a8fa7e10e8f2f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.13" version: "5.1.11"
build_resolvers: build_resolvers:
dependency: transitive dependency: transitive
description: description:
@@ -165,10 +165,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: build_web_compilers name: build_web_compilers
sha256: f9b8e84dbfa7688221c2376e6f68ffd796597785a0a5b1e8cd2516a92fdc0a3c sha256: "311e0b9c797f40eecc8450f0836200b0ad9ea5227f86428a7ed5691f35e347c0"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.1.5" version: "4.4.18"
built_collection: built_collection:
dependency: transitive dependency: transitive
description: description:
@@ -253,10 +253,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: characters name: characters
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.4.1"
charcode: charcode:
dependency: transitive dependency: transitive
description: description:
@@ -1084,26 +1084,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.9" version: "11.0.2"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_flutter_testing name: leak_tracker_flutter_testing
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.9" version: "3.0.10"
leak_tracker_testing: leak_tracker_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_testing name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" version: "3.0.2"
linkify: linkify:
dependency: transitive dependency: transitive
description: description:
@@ -1180,18 +1180,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.17" version: "0.12.19"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.11.1" version: "0.13.0"
math_expressions: math_expressions:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1212,10 +1212,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.16.0" version: "1.17.0"
mih_package_toolkit: mih_package_toolkit:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1604,10 +1604,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: scratch_space name: scratch_space
sha256: "8510fbff458d733a58fc427057d1ac86303b376d609d6e1bc43f240aad9aa445" sha256: "3417e014d20b12cebc5bfb1c0b1f63806054177158596cc31cc4d9aaca767a60"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.2" version: "1.2.0"
screen_brightness: screen_brightness:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1977,10 +1977,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.4" version: "0.7.10"
timing: timing:
dependency: transitive dependency: transitive
description: description:
@@ -2129,10 +2129,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.2.0"
version: version:
dependency: transitive dependency: transitive
description: description:
@@ -2294,5 +2294,5 @@ packages:
source: hosted source: hosted
version: "3.1.1" version: "3.1.1"
sdks: sdks:
dart: ">=3.8.1 <3.9.0" dart: ">=3.9.0-0 <3.13.0-z"
flutter: ">=3.29.0" flutter: ">=3.29.0"

View File

@@ -1,11 +1,11 @@
name: mzansi_innovation_hub name: mzansi_innovation_hub
description: "" description: ""
publish_to: 'none' # Remove this line if you wish to publish to pub.dev publish_to: "none" # Remove this line if you wish to publish to pub.dev
version: 1.2.6+130 version: 1.2.6+130
# version: 1.1.1+97 #--- Updated version for upgrader package testing # version: 1.1.1+97 #--- Updated version for upgrader package testing
environment: environment:
sdk: '>=3.5.3 <4.0.0' sdk: ">=3.5.3 <4.0.0"
# flutter: ">=1.17.0" # flutter: ">=1.17.0"
dependencies: dependencies:
flutter: flutter:
@@ -67,6 +67,9 @@ dependencies:
cross_file: ^0.3.5+1 cross_file: ^0.3.5+1
quick_actions: ^1.1.0 quick_actions: ^1.1.0
dependency_overrides:
supertokens_flutter: any
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter

View File

@@ -0,0 +1,5 @@
{{flutter_js}}
{{flutter_build_config}}
// This is the default loader that Flutter 3.24 expects
_flutter.loader.load();

View File

@@ -1,74 +1,78 @@
<!DOCTYPE html><html><head> <!DOCTYPE html>
<base href="/"> <html>
<!-- APP Description --> <head>
<meta charset="UTF-8"> <base href="/">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="MIH, the first Mzansi super app by Mzansi Innovation Hub. Streamline your life with Mzansi Profile, Wallet, Patient Manager, AI, Directory, Calendar, Calculator, &amp; secure MIH Access. Unify social, business &amp; personal tools.">
<!-- iOS meta tags & icons --> <!-- APP Description -->
<meta name="mobile-web-app-capable" content="yes"> <meta charset="UTF-8">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="apple-mobile-web-app-title" content="MIH"> <meta name="description"
content="MIH, the first Mzansi super app by Mzansi Innovation Hub. Streamline your life with Mzansi Profile, Wallet, Patient Manager, AI, Directory, Calendar, Calculator, &amp; secure MIH Access. Unify social, business &amp; personal tools.">
<link rel="apple-touch-icon" href="icons/apple-touch-icon.png"> <!-- iOS meta tags & icons -->
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="MIH">
<!-- Favicon --> <link rel="apple-touch-icon" href="icons/apple-touch-icon.png">
<link rel="icon" href="favicon.ico">
<title>MIH App: Mzansi Innovation Hub - All-in-One Super App for Business &amp; Personal Life</title> <!-- Favicon -->
<link rel="manifest" href="manifest.json"> <link rel="icon" href="favicon.ico">
<!-- test stuff for speed --> <title>MIH App: Mzansi Innovation Hub - All-in-One Super App for Business &amp; Personal Life</title>
<link rel="preconnect" href="https://cdnjs.cloudflare.com"> <link rel="manifest" href="manifest.json">
<!-- <link rel="preload" href="flutter_bootstrap.js" as="script"> -->
<link rel="preload" href="main.dart.js" as="script">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <!-- test stuff for speed -->
<link rel="preconnect" href="https://cdnjs.cloudflare.com">
<!-- <link rel="preload" href="flutter_bootstrap.js" as="script"> -->
<link rel="preload" href="main.dart.js" as="script">
<!-- Splash screen --> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<!-- Splash screen -->
<!-- <script src="install_pwa.js" defer=""></script> --> <!-- <script src="install_pwa.js" defer=""></script> -->
<!-- Capture PWA install prompt event --> <!-- Capture PWA install prompt event -->
<script> <script>
let deferredPrompt; let deferredPrompt;
window.addEventListener('beforeinstallprompt', (e) => { window.addEventListener('beforeinstallprompt', (e) => {
deferredPrompt = e; deferredPrompt = e;
}); });
function promptInstall() { function promptInstall() {
deferredPrompt.prompt(); deferredPrompt.prompt();
} }
// Listen for app install event // Listen for app install event
window.addEventListener('appinstalled', () => { window.addEventListener('appinstalled', () => {
deferredPrompt = null; deferredPrompt = null;
appInstalled(); appInstalled();
}); });
// Track how PWA was launched (either from browser or as PWA) // Track how PWA was launched (either from browser or as PWA)
function getLaunchMode() { function getLaunchMode() {
const isStandalone = window.matchMedia('(display-mode: standalone)').matches; const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
if (deferredPrompt) hasPrompt(); if (deferredPrompt) hasPrompt();
if (document.referrer.startsWith('android-app://')) { if (document.referrer.startsWith('android-app://')) {
appLaunchedAsTWA(); appLaunchedAsTWA();
} else if (navigator.standalone || isStandalone) { } else if (navigator.standalone || isStandalone) {
appLaunchedAsPWA(); appLaunchedAsPWA();
} else { } else {
window.appLaunchedInBrowser(); window.appLaunchedInBrowser();
} }
} }
</script> </script>
<!-- Splash screen --> <!-- Splash screen -->
<!-- <script> <!-- <script>
var dartPdfJsBaseUrl = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.2.146/"; var dartPdfJsBaseUrl = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.2.146/";
</script> --> </script> -->
<!---------------------> <!--------------------->
<script type="application/ld+json"> <script type="application/ld+json">
{ {
"@context": "http://schema.org", "@context": "http://schema.org",
"@type": "SoftwareApplication", "@type": "SoftwareApplication",
@@ -105,249 +109,189 @@
} }
</script> </script>
<style id="splash-screen-style"> <style id="splash-screen-style">
html { html {
height: 100% height: 100%
} }
body { body {
margin: 0; margin: 0;
min-height: 100%; min-height: 100%;
background-color: #3A4454; background-color: #3A4454;
background-size: 100% 100%; background-size: 100% 100%;
} }
.center { .center {
margin: 0; margin: 0;
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
-ms-transform: translate(-50%, -50%); -ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
} }
.contain { .contain {
display:block; display: block;
width:100%; height:100%; width: 100%;
object-fit: contain; height: 100%;
} object-fit: contain;
}
.stretch { .stretch {
display:block; display: block;
width:100%; height:100%; width: 100%;
} height: 100%;
}
.cover { .cover {
display:block; display: block;
width:100%; height:100%; width: 100%;
object-fit: cover; height: 100%;
} object-fit: cover;
}
.bottom { .bottom {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 50%; left: 50%;
-ms-transform: translate(-50%, 0); -ms-transform: translate(-50%, 0);
transform: translate(-50%, 0); transform: translate(-50%, 0);
} }
.bottomLeft { .bottomLeft {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
} }
.bottomRight { .bottomRight {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
right: 0; right: 0;
} }
</style> </style>
<script id="splash-screen-script"> <script id="splash-screen-script">
function removeSplashFromWeb() { function removeSplashFromWeb() {
document.getElementById("splash")?.remove(); document.getElementById("splash")?.remove();
document.getElementById("splash-branding")?.remove(); document.getElementById("splash-branding")?.remove();
document.body.style.background = "transparent"; document.body.style.background = "transparent";
} }
</script> </script>
</head> </head>
<body> <body>
<picture id="splash-branding"> <picture id="splash-branding">
<source srcset="splash/img/branding-1x.gif 1x, splash/img/branding-2x.gif 2x, splash/img/branding-3x.gif 3x, splash/img/branding-4x.gif 4x" media="(prefers-color-scheme: light)"> <source
<source srcset="splash/img/branding-dark-1x.gif 1x, splash/img/branding-dark-2x.gif 2x, splash/img/branding-dark-3x.gif 3x, splash/img/branding-dark-4x.gif 4x" media="(prefers-color-scheme: dark)"> srcset="splash/img/branding-1x.gif 1x, splash/img/branding-2x.gif 2x, splash/img/branding-3x.gif 3x, splash/img/branding-4x.gif 4x"
<img class="bottom" aria-hidden="true" src="splash/img/branding-1x.gif" alt=""> media="(prefers-color-scheme: light)">
</picture> <source
<picture id="splash"> srcset="splash/img/branding-dark-1x.gif 1x, splash/img/branding-dark-2x.gif 2x, splash/img/branding-dark-3x.gif 3x, splash/img/branding-dark-4x.gif 4x"
<source srcset="splash/img/light-1x.gif 1x, splash/img/light-2x.gif 2x, splash/img/light-3x.gif 3x, splash/img/light-4x.gif 4x" media="(prefers-color-scheme: light)"> media="(prefers-color-scheme: dark)">
<source srcset="splash/img/dark-1x.gif 1x, splash/img/dark-2x.gif 2x, splash/img/dark-3x.gif 3x, splash/img/dark-4x.gif 4x" media="(prefers-color-scheme: dark)"> <img class="bottom" aria-hidden="true" src="splash/img/branding-1x.gif" alt="">
<img class="center" aria-hidden="true" src="splash/img/light-1x.gif" alt=""> </picture>
</picture> <picture id="splash">
<source
srcset="splash/img/light-1x.gif 1x, splash/img/light-2x.gif 2x, splash/img/light-3x.gif 3x, splash/img/light-4x.gif 4x"
media="(prefers-color-scheme: light)">
<source
srcset="splash/img/dark-1x.gif 1x, splash/img/dark-2x.gif 2x, splash/img/dark-3x.gif 3x, splash/img/dark-4x.gif 4x"
media="(prefers-color-scheme: dark)">
<img class="center" aria-hidden="true" src="splash/img/light-1x.gif" alt="">
</picture>
<div id="seo-content" style="display:none;"> <div id="seo-content" style="display:none;">
<h1>Mzansi Innovation Hub (MIH) - Your All-in-One Super App for South Africa</h1> <h1>Mzansi Innovation Hub (MIH) - Your All-in-One Super App for South Africa</h1>
<p>Tired of juggling multiple apps? Mzansi Innovation Hub (MIH) presents the first super app designed specifically for the people and businesses of Mzansi. MIH is your unified online information technology system to streamline both your personal and professional life, bridging the socio-economic divide during the digital revolution.</p> <p>Tired of juggling multiple apps? Mzansi Innovation Hub (MIH) presents the first super app designed
specifically for the people and businesses of Mzansi. MIH is your unified online information technology
system to streamline both your personal and professional life, bridging the socio-economic divide during the
digital revolution.</p>
<h2>Key Features of the MIH Super App:</h2> <h2>Key Features of the MIH Super App:</h2>
<ul> <ul>
<li><strong>Mzansi Profile:</strong> Effortlessly manage your professional profile, personal information, and valuable business team details. Amplify your online presence.</li> <li><strong>Mzansi Profile:</strong> Effortlessly manage your professional profile, personal information,
<li><strong>Mzansi Wallet:</strong> Go digital! Securely store all your loyalty cards in one convenient place, reducing clutter.</li> and valuable business team details. Amplify your online presence.</li>
<li><strong>Patient Manager:</strong> Revolutionize medical practices with seamless patient appointment scheduling and efficient data management.</li> <li><strong>Mzansi Wallet:</strong> Go digital! Securely store all your loyalty cards in one convenient
<li><strong>Mzansi AI:</strong> Get instant support and quick answers with our friendly AI assistant, available 24/7.</li> place, reducing clutter.</li>
<li></li><li><strong>Mzansi Directory:</strong> Discover and connect with people and businesses across Mzansi.</li> <li><strong>Patient Manager:</strong> Revolutionize medical practices with seamless patient appointment
<li><strong>Calendar:</strong> Stay perfectly organized with an integrated calendar for all your personal and business appointments.</li> scheduling and efficient data management.</li>
<li><strong>Calculator:</strong> Perform quick calculations, including tips and forex, right within the app.</li> <li><strong>Mzansi AI:</strong> Get instant support and quick answers with our friendly AI assistant,
<li><strong>MIH Access:</strong> Take control of your security. Easily manage and view who has access to your profile and data.</li> available 24/7.</li>
</ul> <li></li>
<li><strong>Mzansi Directory:</strong> Discover and connect with people and businesses across Mzansi.</li>
<li><strong>Calendar:</strong> Stay perfectly organized with an integrated calendar for all your personal
and business appointments.</li>
<li><strong>Calculator:</strong> Perform quick calculations, including tips and forex, right within the app.
</li>
<li><strong>MIH Access:</strong> Take control of your security. Easily manage and view who has access to
your profile and data.</li>
</ul>
<p>MIH simplifies daily tasks, reduces costs, and helps you manage your professional profile, team, and appointments all from a single, user-friendly platform. It's the essential mobile app for business owners and individuals looking to streamline their online presence in South Africa.</p> <p>MIH simplifies daily tasks, reduces costs, and helps you manage your professional profile, team, and
appointments all from a single, user-friendly platform. It's the essential mobile app for business owners
and individuals looking to streamline their online presence in South Africa.</p>
<h3>Download the MIH Super App Today!</h3> <h3>Download the MIH Super App Today!</h3>
<p>Available for Android and iOS. Search "Mzansi Innovation Hub" or "MIH App" on your app store.</p> <p>Available for Android and iOS. Search "Mzansi Innovation Hub" or "MIH App" on your app store.</p>
</div> </div>
<script> <script>
// Hide the SEO content once the Flutter app starts to load // Hide the SEO content once the Flutter app starts to load
window.addEventListener('load', function() { window.addEventListener('load', function () {
const seoContent = document.getElementById('seo-content'); const seoContent = document.getElementById('seo-content');
if (seoContent) { if (seoContent) {
seoContent.style.display = 'none'; // Or 'hidden' seoContent.style.display = 'none'; // Or 'hidden'
} }
}); });
// You might need to adjust this script to ensure it hides *after* the Flutter app's visual elements are fully rendered. // You might need to adjust this script to ensure it hides *after* the Flutter app's visual elements are fully rendered.
// A more robust solution might involve listening for a Flutter-specific event when the app is ready. // A more robust solution might involve listening for a Flutter-specific event when the app is ready.
</script> </script>
<script> <script>
window.addEventListener("load", function (e) { window.addEventListener("load", function (e) {
document.addEventListener("focusin", function (event) { document.addEventListener("focusin", function (event) {
if (event.target.tagName === "INPUT" || event.target.tagName === "TEXTAREA") { if (event.target.tagName === "INPUT" || event.target.tagName === "TEXTAREA") {
event.target.setAttribute("spellcheck", "true"); event.target.setAttribute("spellcheck", "true");
} }
}); });
}); });
</script> </script>
<script defer=""> <script src="flutter_bootstrap.js" async></script>
    var serviceWorkerVersion = '{{flutter_service_worker_version}}'; <script>
var scriptLoaded = false; var serviceWorkerVersion = '{{flutter_service_worker_version}}';
var swFallbackTimeout; var scriptLoaded = false;
function loadMainDartJs() { // We keep your logic to monitor the service worker for immediate updates
console.log('Loading app...'); if ('serviceWorker' in navigator) {
if (scriptLoaded) { window.addEventListener('load', function () {
return; var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion.replace(/"/g, '');
}
scriptLoaded = true;
// Cancel Timeut
if (swFallbackTimeout) {
clearTimeout(swFallbackTimeout);
console.log('Service worker loaded successfully - fallback timeout cancelled.');
}
{{flutter_js}}
{{flutter_build_config}}
_flutter.loader.load({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function (engineInitializer) {
engineInitializer.initializeEngine().then(function (appRunner) {
appRunner.runApp();
});
}
});
var finishLoad = new Date();
var loadTime = (finishLoad.getTime() - startLoad.getTime());
console.log("Load Time: " + loadTime);
}
// Helper function to strip quotes from the version placeholder navigator.serviceWorker.register(serviceWorkerUrl).then((reg) => {
function getCleanVersion(version) { reg.addEventListener('updatefound', () => {
return version.replace(/"/g, ''); const newWorker = reg.installing;
} newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'activated') {
if ('serviceWorker' in navigator) { // This fulfills your requirement for immediate update
var startLoad = new Date(); if (confirm('A new version of MIH is available. Refresh now to update?')) {
// Service workers are supported. Use them. window.location.reload();
window.addEventListener('load', function () { }
var cleanServiceWorkerVersion = getCleanVersion(serviceWorkerVersion); }
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + cleanServiceWorkerVersion; });
});
navigator.serviceWorker.register(serviceWorkerUrl) });
.then((reg) => { });
}
function waitForActivation(serviceWorker, isFirstTimeInstall) { </script>
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
// Only prompt if this is NOT the first time installing (i.e., returning user)
if (!isFirstTimeInstall && navigator.serviceWorker.controller) {
console.log('New app version activated. Prompting for reload.');
// You can replace this confirm with a better UI notification.
if (confirm('A new version of the app is available. Refresh now to update?')) {
window.location.reload();
}
} else if (isFirstTimeInstall) {
console.log('First time install - skipping reload prompt.');
}
}
});
}
const currentSWVersion = reg.active ? reg.active.scriptURL.split("=")[1].replaceAll("%22", "") : null;
console.log('Active Service Worker URL: ' + (reg.active ? reg.active.scriptURL : 'None'));
console.log('Latest Service Worker Version: ' + cleanServiceWorkerVersion);
console.log('Active Service Worker Version: ' + (currentSWVersion || 'None'));
const isMatch = currentSWVersion === cleanServiceWorkerVersion;
console.log('Latest Service Worker Installed: ' + isMatch);
if (!reg.active && (reg.installing || reg.waiting)) {
// First time load: wait for activation but don't prompt.
console.log('No Service Worker Available - Installing New Service Worker.');
waitForActivation(reg.installing || reg.waiting, true); // true = first time install
} else if (!isMatch) {
// New version available: force update (returning user).
console.log('New service worker available. Updating and waiting for activation.');
reg.update();
waitForActivation(reg.installing, false); // false = not first time
} else {
// Existing service worker is still good.
console.log('Service Worker up-to-date, Loading app.');
loadMainDartJs();
}
})
.catch((error) => {
console.error('Service Worker registration failed:', error);
loadMainDartJs(); // Fallback if registration fails completely
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plain <script> tag.
swFallbackTimeout = setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from a service worker. Falling back to plain <script> tag.',
);
loadMainDartJs();
}
}, 1500);
});
}
else {
// Service workers not supported. Just drop the <script> tag.
console.log('Service Workers Not Supported. Loading app directly.');
loadMainDartJs();
}
  </script>
<script id="pdfjs-lib" src="//cdnjs.cloudflare.com/ajax/libs/pdf.js/3.2.146/pdf.min.js" defer=""></script> <script id="pdfjs-lib" src="//cdnjs.cloudflare.com/ajax/libs/pdf.js/3.2.146/pdf.min.js" defer=""></script>
<script id="pdfjs-worker" type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/pdf.js/3.2.146/pdf.worker.min.js" defer=""></script> <script id="pdfjs-worker" type="text/javascript"
<!---------------------> src="//cdnjs.cloudflare.com/ajax/libs/pdf.js/3.2.146/pdf.worker.min.js" defer=""></script>
<!--------------------->
</body><!-- File Picker & PDF viewer --></html> </body><!-- File Picker & PDF viewer -->
</html>