diff --git a/Frontend/lib/mih_packages/about_mih/package_tools/mih_info.dart b/Frontend/lib/mih_packages/about_mih/package_tools/mih_info.dart index 0fdf31ca..b8c6cef7 100644 --- a/Frontend/lib/mih_packages/about_mih/package_tools/mih_info.dart +++ b/Frontend/lib/mih_packages/about_mih/package_tools/mih_info.dart @@ -1,6 +1,7 @@ import 'package:flutter_speed_dial/flutter_speed_dial.dart'; import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_business_details_services.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_install_services.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_layout/mih_tile.dart'; @@ -11,7 +12,9 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_floating_menu.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_icons.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_user_services.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'package:redacted/redacted.dart'; import 'package:share_plus/share_plus.dart'; class MihInfo extends StatefulWidget { @@ -22,6 +25,8 @@ class MihInfo extends StatefulWidget { } class _MihInfoState extends State { + late Future _futureUserCount; + late Future _futureBusinessCount; final Uri _tiktokUrl = Uri.parse('https://www.tiktok.com/@mzansi.innovation.hub'); final Uri _whatsappUrl = @@ -144,7 +149,7 @@ class _MihInfoState extends State { textAlign: TextAlign.center, style: const TextStyle( //fontWeight: FontWeight.bold, - fontSize: 15, + fontSize: 17, ), ), ], @@ -178,7 +183,7 @@ class _MihInfoState extends State { textAlign: TextAlign.center, style: const TextStyle( //fontWeight: FontWeight.bold, - fontSize: 15, + fontSize: 17, ), ), ], @@ -506,6 +511,121 @@ class _MihInfoState extends State { ); } + Widget displayBusinessCount() { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + FutureBuilder( + future: _futureBusinessCount, + builder: (context, snapshot) { + bool isLoading = true; + String userCount = "⚠️"; + if (snapshot.connectionState == ConnectionState.waiting) { + isLoading = true; + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.hasError) { + isLoading = false; + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData) { + isLoading = false; + userCount = snapshot.data.toString(); + } else { + isLoading = true; + } + return SizedBox( + child: Text( + userCount, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 23, + ), + ), + ).redacted( + context: context, + redact: isLoading, + configuration: RedactedConfiguration( + defaultBorderRadius: BorderRadius.circular(5), + redactedColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark", + ), + ), + ); + }, + ), + Text( + "Businesses", + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.normal, + fontSize: 20, + ), + ), + ], + ); + } + + Widget displayUserCount() { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + FutureBuilder( + future: _futureUserCount, + builder: (context, snapshot) { + bool isLoading = true; + String userCount = "⚠️"; + if (snapshot.connectionState == ConnectionState.waiting) { + isLoading = true; + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.hasError) { + isLoading = false; + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData) { + isLoading = false; + userCount = snapshot.data.toString(); + } else { + isLoading = true; + } + return SizedBox( + child: Text( + userCount, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 23, + ), + ), + ).redacted( + context: context, + redact: isLoading, + configuration: RedactedConfiguration( + defaultBorderRadius: BorderRadius.circular(5), + redactedColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark", + ), + ), + ); + }, + ), + Text( + "Users", + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.normal, + fontSize: 20, + ), + ), + ], + ); + } + + @override + void initState() { + super.initState(); + _futureUserCount = MihUserServices().fetchUserCount(); + _futureBusinessCount = MihBusinessDetailsServices().fetchBusinessCount(); + } + @override Widget build(BuildContext context) { return MihPackageToolBody( @@ -555,6 +675,30 @@ class _MihInfoState extends State { // const SizedBox( // height: 10, // ), + Text( + "The MIH Family", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 25, + ), + ), + Wrap( + alignment: WrapAlignment.spaceEvenly, + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 25, + runSpacing: 10, + children: [ + displayUserCount(), + displayBusinessCount(), + ], + ), + const Padding( + padding: EdgeInsets.symmetric(vertical: 10.0), + child: Divider(), + ), + // const SizedBox( + // height: 10, + // ), Wrap( alignment: WrapAlignment.start, crossAxisAlignment: WrapCrossAlignment.start, diff --git a/Frontend/lib/mih_services/mih_business_details_services.dart b/Frontend/lib/mih_services/mih_business_details_services.dart index 252c707d..b59840c1 100644 --- a/Frontend/lib/mih_services/mih_business_details_services.dart +++ b/Frontend/lib/mih_services/mih_business_details_services.dart @@ -9,6 +9,21 @@ import '../mih_components/mih_pop_up_messages/mih_error_message.dart'; import 'package:supertokens_flutter/http.dart' as http; class MihBusinessDetailsServices { + Future fetchBusinessCount() async { + var response = await http.get( + Uri.parse("${AppEnviroment.baseApiUrl}/business/count/"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + ); + if (response.statusCode == 200) { + var jsonBody = jsonDecode(response.body); + return jsonBody['count']; + } else { + return 0; + } + } + Future> fetchAllBusinessTypes() async { var response = await http.get( Uri.parse("${AppEnviroment.baseApiUrl}/business/types/"), diff --git a/Frontend/lib/mih_services/mih_user_services.dart b/Frontend/lib/mih_services/mih_user_services.dart index 4b267345..44f83c87 100644 --- a/Frontend/lib/mih_services/mih_user_services.dart +++ b/Frontend/lib/mih_services/mih_user_services.dart @@ -33,6 +33,21 @@ class MihUserServices { } } + Future fetchUserCount() async { + var response = await http.get( + Uri.parse("${AppEnviroment.baseApiUrl}/users/count/"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + ); + if (response.statusCode == 200) { + var jsonBody = jsonDecode(response.body); + return jsonBody['count']; + } else { + return 0; + } + } + Future createUser( String email, String app_id, diff --git a/backend/routers/business.py b/backend/routers/business.py index e1384d29..560e8b81 100644 --- a/backend/routers/business.py +++ b/backend/routers/business.py @@ -6,6 +6,7 @@ import mih_database.mihDbConnections from mih_database.mihDbObjects import User, Business, BusinessRating, BookmarkedBusiness from sqlalchemy import desc, or_ from sqlalchemy.orm import Session +from sqlalchemy.sql import func #SuperToken Auth from front end from supertokens_python.recipe.session.framework.fastapi import verify_session from supertokens_python.recipe.session import SessionContainer @@ -62,6 +63,24 @@ class businessUpdateRequestV2(BaseModel): rating: str mission_vision: str +@router.get("/business/count/", tags=["MIH Business"]) +async def read_business_by_business_id(): #, session: SessionContainer = Depends(verify_session()) + dbEngine = mih_database.mihDbConnections.dbAllConnect() + dbSession = Session(dbEngine) + try: + queryResults = dbSession.query(func.count(Business.business_id)).scalar() + response_data = {"count": queryResults} + return response_data + except Exception as e: + print(f"An error occurred during the ORM query: {e}") + if dbSession.is_active: + dbSession.rollback() + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to retrieve records due to an internal server error." + ) + finally: + dbSession.close() # Get List of all files @router.get("/business/types/", tags=["MIH Business"]) diff --git a/backend/routers/users.py b/backend/routers/users.py index 60f0d1f2..5aede146 100644 --- a/backend/routers/users.py +++ b/backend/routers/users.py @@ -1,7 +1,12 @@ -from fastapi import APIRouter, HTTPException +from fastapi import APIRouter, HTTPException, status from pydantic import BaseModel #from ..mih_database import dbConnection import mih_database +import mih_database.mihDbConnections +from mih_database.mihDbObjects import User, Business, BusinessRating, BookmarkedBusiness +from sqlalchemy import desc, or_ +from sqlalchemy.orm import Session +from sqlalchemy.sql import func #SuperToken Auth from front end from supertokens_python.recipe.session.framework.fastapi import verify_session from supertokens_python.recipe.session import SessionContainer @@ -67,6 +72,26 @@ class userDeleteRequest(BaseModel): # return items[0] + +@router.get("/users/count/", tags=["MIH Users"]) +async def read_users_by_app_id(): #, session: SessionContainer = Depends(verify_session()) + dbEngine = mih_database.mihDbConnections.dbAllConnect() + dbSession = Session(dbEngine) + try: + queryResults = dbSession.query(func.count(User.app_id)).scalar() + response_data = {"count": queryResults} + return response_data + except Exception as e: + print(f"An error occurred during the ORM query: {e}") + if dbSession.is_active: + dbSession.rollback() + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to retrieve records due to an internal server error." + ) + finally: + dbSession.close() + # Get List of all files @router.get("/users/search/{search}", tags=["MIH Users"]) async def read_all_users(search: str, session: SessionContainer = Depends(verify_session())): #, session: SessionContainer = Depends(verify_session())