MIH Profile Links

This commit is contained in:
2026-05-27 15:36:56 +02:00
parent 33d07b1617
commit 052f937027
30 changed files with 2240 additions and 499 deletions
+15 -2
View File
@@ -155,10 +155,23 @@ services:
MYSQL_DATABASE: ${SUPERTOKENS_DB} MYSQL_DATABASE: ${SUPERTOKENS_DB}
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
#============== Adminer ====================================================================
mih-adminer:
image: adminer:latest
container_name: mih-adminer
restart: always
environment:
ADMINER_DEFAULT_SERVER: mih-db
ports:
- "127.0.0.1:8082:8080"
networks:
- mih-network
depends_on:
- mih-db
#============== PHP My Admin ==================================================================== #============== PHP My Admin ====================================================================
# phpmyadmin: # phpmyadmin:
# platform: linux/amd64 # platform: linux/amd64
+128
View File
@@ -0,0 +1,128 @@
from fastapi import APIRouter, HTTPException, status
from pydantic import BaseModel
from typing import List
#from ..mih_database import dbConnection
import mih_database
import mih_database.mihDbConnections
from mih_database.mihDbObjects import ProfileLink
from sqlalchemy import select, insert, delete, CursorResult
from sqlalchemy.orm import Session
#SuperToken Auth from front end
from supertokens_python.recipe.session.framework.fastapi import verify_session
from supertokens_python.recipe.session import SessionContainer
from fastapi import Depends
router = APIRouter()
class ProfileLinkResponse(BaseModel):
idprofile_links: int
app_id: str
business_id: str
site_name: str
custom_name: str
destination: str
order: int
class Config:
from_attributes = True
class profileLinkInsertRequest(BaseModel):
app_id: str
business_id: str
site_name: str
custom_name: str
destination: str
order:int
class profileLinkDeletRequest(BaseModel):
idprofile_links: int
class profileLinkUpdateRequest(BaseModel):
idprofile_links: int
site_name: str
custom_name: str
destination: str
order:int
def get_db():
dbEngine = mih_database.mihDbConnections.dbAllConnect()
with Session(dbEngine) as session:
yield session
@router.get("/profile-links/user/{app_id}", response_model=List[ProfileLinkResponse], tags=["Profile Links"])
async def getUserProfileLinks(
app_id: str,
dbSession: Session = Depends(get_db),
# session: SessionContainer = Depends(verify_session())
):
queryStatement = select(ProfileLink).where(ProfileLink.app_id == app_id).order_by(ProfileLink.order)
queryResults = dbSession.execute(queryStatement).scalars().all()
return queryResults
@router.get("/profile-links/business/{business_id}", response_model=List[ProfileLinkResponse], tags=["Profile Links"])
async def getBusinessProfileLinks(
business_id: str,
dbSession: Session = Depends(get_db),
# session: SessionContainer = Depends(verify_session())
):
queryStatement = select(ProfileLink).where(ProfileLink.business_id == business_id).order_by(ProfileLink.order)
queryResults = dbSession.execute(queryStatement).scalars().all()
return queryResults
@router.post("/profile-links/insert/", status_code=201, tags = ["Profile Links"])
async def addNewProfileLink(
insertItem: profileLinkInsertRequest,
dbSession: Session = Depends(get_db),
session: SessionContainer = Depends(verify_session())
):
queryStatement = insert(ProfileLink).values(
app_id = insertItem.app_id,
business_id = insertItem.business_id,
site_name = insertItem.site_name,
custom_name = insertItem.custom_name,
destination = insertItem.destination,
order = insertItem.order
)
dbSession.execute(queryStatement)
dbSession.commit()
return {"message": "Successfully Created Record"}
@router.delete("/profile-links/delete/", tags=["Profile Links"])
async def deleteProfileLink(
deleteItem: profileLinkDeletRequest,
dbSession: Session = Depends(get_db),
session: SessionContainer = Depends(verify_session())
):
queryStatement = select(ProfileLink).where(ProfileLink.idprofile_links == deleteItem.idprofile_links)
profileLink = dbSession.execute(queryStatement).scalar_one_or_none()
if not profileLink:
raise HTTPException(status_code=404, detail="Record not found")
dbSession.delete(profileLink)
dbSession.execute(queryStatement)
dbSession.commit()
return {"message": "Successfully Deleted Record"}
@router.put("/profile-links/update/", tags=["Profile Links"])
async def updateProfileLink(
updateItem: profileLinkUpdateRequest,
dbSession: Session = Depends(get_db),
session: SessionContainer = Depends(verify_session())
):
queryStatement = select(ProfileLink).where(ProfileLink.idprofile_links == updateItem.idprofile_links)
profileLink = dbSession.execute(queryStatement).scalar_one_or_none()
if not profileLink:
raise HTTPException(status_code=404, detail="Link not found")
profileLink.site_name = updateItem.site_name
profileLink.custom_name = updateItem.custom_name
profileLink.destination = updateItem.destination
profileLink.order = updateItem.order
dbSession.commit()
return {"message": "Successfully Updated Record"}
+28
View File
@@ -136,6 +136,34 @@ async def read_all_users(username: str, session: SessionContainer = Depends(veri
db.close() db.close()
return {"available": available} return {"available": available}
# Get List of all files
@router.get("/user/username/{username}", tags=["MIH Users"])
async def read_users_by_username(username: str,
# session: SessionContainer = Depends(verify_session()),
):
db = mih_database.dbConnection.dbAppDataConnect()
cursor = db.cursor()
# query = "SELECT * FROM users where username = %s"
query = "SELECT * FROM users WHERE LOWER(username) = LOWER(%s)"
cursor.execute(query, (username,))
items = [
{
"idUser": item[0],
"email": item[1],
"fname": item[2],
"lname": item[3],
"type": item[4],
"app_id": item[5],
"username": item[6],
"pro_pic_path": item[7],
"purpose": item[8],
}
for item in cursor.fetchall()
]
cursor.close()
db.close()
return items[0]
# Get List of all files # Get List of all files
@router.get("/user/{app_id}", tags=["MIH Users"]) @router.get("/user/{app_id}", tags=["MIH Users"])
async def read_users_by_app_id(app_id: str, session: SessionContainer = Depends(verify_session())): async def read_users_by_app_id(app_id: str, session: SessionContainer = Depends(verify_session())):
+14 -7
View File
@@ -77,7 +77,8 @@ class MihGoRouter {
"${MihGoRouterPaths.mihAuthentication}/${MihGoRouterPaths.forgotPassword}", "${MihGoRouterPaths.mihAuthentication}/${MihGoRouterPaths.forgotPassword}",
MihGoRouterPaths.resetPassword, MihGoRouterPaths.resetPassword,
"/${MihGoRouterPaths.aboutMih}", "/${MihGoRouterPaths.aboutMih}",
MihGoRouterPaths.businessProfileView, "/${MihGoRouterPaths.businessProfileView}",
"/${MihGoRouterPaths.mzansiProfileView}",
]; ];
KenLogger.success( KenLogger.success(
"Redirect Check: ${state.fullPath}, isUserSignedIn: $isUserSignedIn"); "Redirect Check: ${state.fullPath}, isUserSignedIn: $isUserSignedIn");
@@ -86,8 +87,9 @@ class MihGoRouter {
} }
if (isUserSignedIn && if (isUserSignedIn &&
unauthenticatedPaths.contains(state.fullPath) && unauthenticatedPaths.contains(state.fullPath) &&
state.fullPath != MihGoRouterPaths.aboutMih && state.fullPath != "/${MihGoRouterPaths.aboutMih}" &&
state.fullPath != MihGoRouterPaths.businessProfileView) { state.fullPath != "/${MihGoRouterPaths.mzansiProfileView}" &&
state.fullPath != "/${MihGoRouterPaths.businessProfileView}") {
return MihGoRouterPaths.mihHome; return MihGoRouterPaths.mihHome;
} }
return null; // Stay on current route return null; // Stay on current route
@@ -161,16 +163,21 @@ class MihGoRouter {
name: "mzansiProfileView", name: "mzansiProfileView",
path: MihGoRouterPaths.mzansiProfileView, path: MihGoRouterPaths.mzansiProfileView,
builder: (BuildContext context, GoRouterState state) { builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: mzansiProfileView");
String? username = state.uri.queryParameters['username'];
KenLogger.success("username: username");
KenLogger.success("MihGoRouter: mzansiProfileView"); KenLogger.success("MihGoRouter: mzansiProfileView");
MzansiDirectoryProvider directoryProvider = MzansiDirectoryProvider directoryProvider =
context.read<MzansiDirectoryProvider>(); context.read<MzansiDirectoryProvider>();
if (directoryProvider.selectedUser == null) { if (directoryProvider.selectedUser == null && username == null) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
context.go(MihGoRouterPaths.mihHome); context.go(MihGoRouterPaths.mihHome);
}); });
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
return MzansiProfileView(); return MzansiProfileView(
username: username,
);
}, },
), ),
// ========================== Mzansi Profile Business ================================== // ========================== Mzansi Profile Business ==================================
@@ -186,9 +193,9 @@ class MihGoRouter {
name: "businessProfileView", name: "businessProfileView",
path: MihGoRouterPaths.businessProfileView, path: MihGoRouterPaths.businessProfileView,
builder: (BuildContext context, GoRouterState state) { builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: businessProfileView"); // KenLogger.success("MihGoRouter: businessProfileView");
String? businessId = state.uri.queryParameters['business_id']; String? businessId = state.uri.queryParameters['business_id'];
KenLogger.success("businessId: $businessId"); // KenLogger.success("businessId: $businessId");
MzansiDirectoryProvider directoryProvider = MzansiDirectoryProvider directoryProvider =
context.read<MzansiDirectoryProvider>(); context.read<MzansiDirectoryProvider>();
if (directoryProvider.selectedBusiness == null && if (directoryProvider.selectedBusiness == null &&
+12 -4
View File
@@ -2,15 +2,19 @@ class ProfileLink {
final int idprofile_links; final int idprofile_links;
final String app_id; final String app_id;
final String business_id; final String business_id;
final String site_name;
final String custom_name;
final String destination; final String destination;
final String web_link; final int order;
const ProfileLink({ const ProfileLink({
required this.idprofile_links, required this.idprofile_links,
required this.app_id, required this.app_id,
required this.business_id, required this.business_id,
required this.site_name,
required this.custom_name,
required this.destination, required this.destination,
required this.web_link, required this.order,
}); });
factory ProfileLink.fromJson(Map<String, dynamic> json) { factory ProfileLink.fromJson(Map<String, dynamic> json) {
@@ -18,8 +22,10 @@ class ProfileLink {
idprofile_links: json['idprofile_links'], idprofile_links: json['idprofile_links'],
app_id: json['app_id'], app_id: json['app_id'],
business_id: json['business_id'], business_id: json['business_id'],
site_name: json['site_name'],
custom_name: json['custom_name'],
destination: json['destination'], destination: json['destination'],
web_link: json['web_link'], order: json['order'],
); );
} }
@@ -28,8 +34,10 @@ class ProfileLink {
'idprofile_links': idprofile_links, 'idprofile_links': idprofile_links,
'app_id': app_id, 'app_id': app_id,
'business_id': business_id, 'business_id': business_id,
'site_name': site_name,
'custom_name': custom_name,
'destination': destination, 'destination': destination,
'web_link': web_link, 'order': order,
}; };
} }
} }
@@ -18,7 +18,7 @@ class _TestPackageTileState extends State<TestPackageTile> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MihPackageTile( return MihPackageTile(
onTap: () { onTap: () {
context.pushNamed( context.goNamed(
'testPackage', 'testPackage',
); );
// Navigator.of(context).pushNamed( // Navigator.of(context).pushNamed(
@@ -11,6 +11,227 @@ class PackageToolThree extends StatefulWidget {
} }
class _PackageToolThreeState extends State<PackageToolThree> { class _PackageToolThreeState extends State<PackageToolThree> {
List<ProfileLink> links = [
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "YouTube",
custom_name: "",
destination: "https://www.youtube.com/@MzansiInnovationHub",
order: 1,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Threads",
custom_name: "",
destination: "https://www.threads.com/@mzansi.innovation.hub",
order: 2,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "TikTok",
custom_name: "",
destination: "https://www.tiktok.com/@mzansiinnovationhub",
order: 3,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "WhatsApp",
custom_name: "",
destination: "https://whatsapp.com/channel/0029Vax3INCIyPtMn8KgeM2F",
order: 4,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Twitch",
custom_name: "",
destination: "https://www.twitch.tv/mzansiinnovationhub",
order: 5,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Instagram",
custom_name: "",
destination: "https://www.instagram.com/mzansi.innovation.hub/",
order: 6,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "X",
custom_name: "",
destination: "https://x.com/mzansi_inno_hub",
order: 7,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "LinkedIn",
custom_name: "",
destination: "https://www.linkedin.com/in/yasien-meth-172352108/",
order: 8,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Facebook",
custom_name: "",
destination: "https://www.facebook.com/profile.php?id=61565345762136",
order: 9,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Reddit",
custom_name: "",
destination: "https://www.reddit.com/r/Mzani_Innovation_Hub/",
order: 10,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Git",
custom_name: "",
destination:
"https://git.mzansi-innovation-hub.co.za/yaso_meth/mzansi_vim",
order: 11,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Telegram",
custom_name: "",
destination: "https://t.me/unisagroupschannel",
order: 11,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Pinterest",
custom_name: "",
destination: "https://za.pinterest.com/food/tomato-based-recipes-ideas/",
order: 11,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Snapchat",
custom_name: "",
destination: "https://www.snapchat.com/",
order: 11,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Messenger",
custom_name: "",
destination: "https://www.messenger.com/",
order: 11,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Medium",
custom_name: "",
destination:
"https://medium.com/flutter-community/the-ultimate-guide-flutter-architecture-template-ii-f86f9aa222e6",
order: 11,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Substack",
custom_name: "",
destination: "https://substack.com/@flutterbytes",
order: 11,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Spotify",
custom_name: "",
destination: "https://open.spotify.com/album/2oss3QgSxdNikts0shvMMo",
order: 11,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "YouTube Music",
custom_name: "",
destination:
"https://music.youtube.com/playlist?list=OLAK5uy_m9x66mE1zyhom3o_NPxmjf60HU1BjTXEE",
order: 11,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Apple Music",
custom_name: "",
destination: "https://music.apple.com/us/album/bastholile/1812031316",
order: 11,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Patreon",
custom_name: "",
destination: "https://www.patreon.com/c/MzansiInnovationHub",
order: 11,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Loolio",
custom_name: "",
destination: "https://www.loolio.com/user/mzansiinnovationhub",
order: 11,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "WeChat",
custom_name: "",
destination: "https://www.wechat.com/en",
order: 11,
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
site_name: "Other",
custom_name: "My App",
destination: "https://app.mzansi-innovation-hub.co.za/about",
order: 12,
),
];
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MihPackageToolBody( return MihPackageToolBody(
@@ -21,108 +242,47 @@ class _PackageToolThreeState extends State<PackageToolThree> {
} }
Widget getBody() { Widget getBody() {
List<ProfileLink> links = [ return
ProfileLink( // Column(
idprofile_links: 1, // mainAxisAlignment: MainAxisAlignment.center,
app_id: "1234", // crossAxisAlignment: CrossAxisAlignment.center,
business_id: "", // children: [
destination: "Youtube", // MihProfileLinks(
web_link: "https://www.youtube.com/@MzansiInnovationHub", // links: links,
), // // links: [],
ProfileLink( // ),
idprofile_links: 1, // const SizedBox(
app_id: "1234", // height: 20,
business_id: "", // ),
destination: "Threads", Column(
web_link: "https://www.threads.com/@mzansi.innovation.hub",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "TikTok",
web_link: "https://www.tiktok.com/@mzansiinnovationhub",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "WhatsApp",
web_link: "https://whatsapp.com/channel/0029Vax3INCIyPtMn8KgeM2F",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "Twitch",
web_link: "https://www.twitch.tv/mzansiinnovationhub",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "Instagram",
web_link: "https://www.instagram.com/mzansi.innovation.hub/",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "X",
web_link: "https://x.com/mzansi_inno_hub",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "LinkedIn",
web_link: "https://www.linkedin.com/in/yasien-meth-172352108/",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "Facebook",
web_link: "https://www.facebook.com/profile.php?id=61565345762136",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "Reddit",
web_link: "https://www.reddit.com/r/Mzani_Innovation_Hub/",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "Discord",
web_link: "https://discord.gg/ZtTZYd5d",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "My App",
web_link: "https://app.mzansi-innovation-hub.co.za/about",
),
];
return Stack(
children: [
MihSingleChildScroll(
scrollbarOn: true,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
MihProfileLinks( MihProfileLinks(
links: links, links: links,
// links: [], // links: [],
), ),
], Expanded(
child: ReorderableListView.builder(
itemBuilder: (context, index) {
return ListTile(
key: ValueKey("$index"),
title: Text("Link SIte: ${links[index].site_name}"),
);
},
itemCount: links.length,
onReorder: (oldIndex, newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
var link = links.removeAt(oldIndex);
links.insert(newIndex, link);
});
},
), ),
), ),
], ],
); );
// ],
// );
} }
} }
@@ -24,72 +24,142 @@ class _MihProfileLinksState extends State<MihProfileLinks> {
IconData iconData; IconData iconData;
Color btnColor; Color btnColor;
Color iconColor = Colors.white; Color iconColor = Colors.white;
switch (link.destination.toLowerCase()) { switch (link.site_name.toLowerCase()) {
case "youtube": case "youtube":
iconData = FontAwesomeIcons.youtube; // iconData = FontAwesomeIcons.youtube;
iconData = MihIcons.youtube;
btnColor = const Color(0xFFFF0000); btnColor = const Color(0xFFFF0000);
break; break;
case "tiktok": case "tiktok":
iconData = FontAwesomeIcons.tiktok; iconData = MihIcons.tiktok;
btnColor = const Color(0xFF000000); btnColor = const Color(0xFF000000);
break; break;
case "twitch": case "twitch":
iconData = FontAwesomeIcons.twitch; iconData = MihIcons.twitch;
btnColor = const Color(0xFF6441a5); btnColor = const Color(0xFF6441a5);
break; break;
case "threads": case "threads":
iconData = FontAwesomeIcons.threads; iconData = MihIcons.threads;
btnColor = const Color(0xFF000000); btnColor = const Color(0xFF000000);
break; break;
case "whatsapp": case "whatsapp":
iconData = FontAwesomeIcons.whatsapp; iconData = MihIcons.whatsapp;
btnColor = const Color(0xFF25D366); btnColor = const Color(0xFF25D366);
break; break;
case "instagram": case "instagram":
iconData = FontAwesomeIcons.instagram; iconData = MihIcons.instagram;
btnColor = const Color(0xFFF56040); btnColor = const Color(0xFFF56040);
break; break;
case "x": case "x":
iconData = FontAwesomeIcons.xTwitter; iconData = MihIcons.x;
btnColor = const Color(0xFF000000); btnColor = const Color(0xFF000000);
break; break;
case "linkedin": case "linkedin":
iconData = FontAwesomeIcons.linkedin; iconData = MihIcons.linkedin;
btnColor = const Color(0xFF0a66c2); btnColor = const Color(0xFF0a66c2);
break; break;
case "facebook": case "facebook":
iconData = FontAwesomeIcons.facebook; iconData = MihIcons.facebook;
btnColor = const Color(0xFF4267B2); btnColor = const Color(0xFF4267B2);
break; break;
case "reddit": case "reddit":
iconData = FontAwesomeIcons.reddit; iconData = MihIcons.reddit;
btnColor = const Color(0xFFFF4500); btnColor = const Color(0xFFFF4500);
break; break;
case "discord": case "discord":
iconData = FontAwesomeIcons.discord; iconData = MihIcons.discord;
btnColor = const Color(0xFF5865F2); btnColor = const Color(0xFF5865F2);
break; break;
case "git": case "git":
iconData = FontAwesomeIcons.git; iconData = MihIcons.git;
btnColor = const Color(0xFF73A952); btnColor = const Color(0xFFf14e32);
break;
case "telegram":
iconData = MihIcons.telegram;
btnColor = const Color(0xFF0088cc);
break;
case "pinterest":
iconData = MihIcons.pinterest;
btnColor = const Color(0xFFe60023);
break;
case "snapchat":
iconData = MihIcons.snapchat;
btnColor = const Color(0xFFfffc00);
iconColor = Colors.black;
break;
case "messenger":
iconData = MihIcons.messenger;
btnColor = const Color(0xFF0084ff);
break;
case "medium":
iconData = MihIcons.medium;
btnColor = const Color(0xFF000000);
break;
case "substack":
iconData = MihIcons.substack;
btnColor = const Color(0xFFFF7731);
break;
case "spotify":
iconData = MihIcons.spotify;
btnColor = const Color(0xFF1db954);
iconColor = Colors.black;
break;
case "yt music":
iconData = MihIcons.youtubeMusic;
btnColor = const Color(0xFFFF0000);
iconColor = Colors.white;
break;
case "apple music":
iconData = MihIcons.appleMusic;
btnColor = const Color(0xFFff4e6b);
break;
case "patreon":
iconData = MihIcons.patreon;
btnColor = const Color(0xFF000000);
break;
case "loolio":
iconData = MihIcons.loolio;
btnColor = const Color(0xFF24244a);
iconColor = const Color(0xFF5fc343);
break;
case "wechat":
iconData = MihIcons.wechat;
btnColor = const Color(0xFFff4e6b);
break; break;
default: default:
iconData = FontAwesomeIcons.link; // iconData = FontAwesomeIcons.link;
iconData = MihIcons.link;
btnColor = MihColors.secondary(); btnColor = MihColors.secondary();
iconColor = Colors.black; iconColor = MihColors.primary();
} }
return MihButton( return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MihButton(
width: widget.buttonSize ?? 70, width: widget.buttonSize ?? 70,
height: widget.buttonSize ?? 70, height: widget.buttonSize ?? 70,
onPressed: () { onPressed: () {
launchSocialUrl(Uri.parse(link.web_link)); launchSocialUrl(Uri.parse(link.destination));
}, },
buttonColor: btnColor, buttonColor: btnColor,
child: FaIcon( child: Icon(
iconData, iconData,
color: iconColor, color: iconColor,
size: 40, size: 50,
), ),
),
const SizedBox(height: 2),
if (link.custom_name.isNotEmpty)
Text(
link.custom_name,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
// link.custom_name.isNotEmpty
// ? Text(link.custom_name)
// : Text(link.site_name),
],
); );
// return MihPackageTile( // return MihPackageTile(
// onTap: () { // onTap: () {
@@ -137,6 +207,7 @@ class _MihProfileLinksState extends State<MihProfileLinks> {
children: [ children: [
Wrap( Wrap(
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
runAlignment: WrapAlignment.center,
runSpacing: 10, runSpacing: 10,
spacing: 10, spacing: 10,
children: widget.links.map( children: widget.links.map(
@@ -725,79 +725,101 @@ class _MihInfoState extends State<MihInfo> {
idprofile_links: 1, idprofile_links: 1,
app_id: "1234", app_id: "1234",
business_id: "", business_id: "",
destination: "Youtube", site_name: "Youtube",
web_link: "https://www.youtube.com/@MzansiInnovationHub", custom_name: "",
destination: "https://www.youtube.com/@MzansiInnovationHub",
order: 1,
), ),
ProfileLink( ProfileLink(
idprofile_links: 1, idprofile_links: 1,
app_id: "1234", app_id: "1234",
business_id: "", business_id: "",
destination: "TikTok", site_name: "TikTok",
web_link: "https://www.tiktok.com/@mzansiinnovationhub", custom_name: "",
destination: "https://www.tiktok.com/@mzansiinnovationhub",
order: 2,
), ),
ProfileLink( ProfileLink(
idprofile_links: 1, idprofile_links: 1,
app_id: "1234", app_id: "1234",
business_id: "", business_id: "",
destination: "Twitch", site_name: "Twitch",
web_link: "https://www.twitch.tv/mzansiinnovationhub", custom_name: "",
destination: "https://www.twitch.tv/mzansiinnovationhub",
order: 3,
), ),
ProfileLink( ProfileLink(
idprofile_links: 1, idprofile_links: 1,
app_id: "1234", app_id: "1234",
business_id: "", business_id: "",
destination: "Threads", site_name: "Threads",
web_link: "https://www.threads.com/@mzansi.innovation.hub", custom_name: "",
destination: "https://www.threads.com/@mzansi.innovation.hub",
order: 4,
), ),
ProfileLink( ProfileLink(
idprofile_links: 1, idprofile_links: 1,
app_id: "1234", app_id: "1234",
business_id: "", business_id: "",
destination: "WhatsApp", site_name: "WhatsApp",
web_link: "https://whatsapp.com/channel/0029Vax3INCIyPtMn8KgeM2F", custom_name: "",
destination: "https://whatsapp.com/channel/0029Vax3INCIyPtMn8KgeM2F",
order: 5,
), ),
ProfileLink( ProfileLink(
idprofile_links: 1, idprofile_links: 1,
app_id: "1234", app_id: "1234",
business_id: "", business_id: "",
destination: "Instagram", site_name: "Instagram",
web_link: "https://www.instagram.com/mzansi.innovation.hub/", custom_name: "",
destination: "https://www.instagram.com/mzansi.innovation.hub/",
order: 6,
), ),
ProfileLink( ProfileLink(
idprofile_links: 1, idprofile_links: 1,
app_id: "1234", app_id: "1234",
business_id: "", business_id: "",
destination: "X", site_name: "X",
web_link: "https://x.com/mzansi_inno_hub", custom_name: "",
destination: "https://x.com/mzansi_inno_hub",
order: 7,
), ),
ProfileLink( ProfileLink(
idprofile_links: 1, idprofile_links: 1,
app_id: "1234", app_id: "1234",
business_id: "", business_id: "",
destination: "LinkedIn", site_name: "LinkedIn",
web_link: "https://www.linkedin.com/company/mzansi-innovation-hub/", custom_name: "",
destination: "https://www.linkedin.com/company/mzansi-innovation-hub/",
order: 8,
), ),
ProfileLink( ProfileLink(
idprofile_links: 1, idprofile_links: 1,
app_id: "1234", app_id: "1234",
business_id: "", business_id: "",
destination: "Facebook", site_name: "Facebook",
web_link: "https://www.facebook.com/profile.php?id=61565345762136", custom_name: "",
destination: "https://www.facebook.com/profile.php?id=61565345762136",
order: 9,
), ),
ProfileLink( ProfileLink(
idprofile_links: 1, idprofile_links: 1,
app_id: "1234", app_id: "1234",
business_id: "", business_id: "",
destination: "Reddit", site_name: "Reddit",
web_link: "https://www.reddit.com/r/Mzani_Innovation_Hub/", custom_name: "",
destination: "https://www.reddit.com/r/Mzani_Innovation_Hub/",
order: 10,
), ),
ProfileLink( ProfileLink(
idprofile_links: 1, idprofile_links: 1,
app_id: "1234", app_id: "1234",
business_id: "", business_id: "",
destination: "Git", site_name: "Git",
web_link: custom_name: "",
destination:
"https://git.mzansi-innovation-hub.co.za/yaso_meth/mih-project", "https://git.mzansi-innovation-hub.co.za/yaso_meth/mih-project",
order: 11,
), ),
]; ];
return Column( return Column(
@@ -130,7 +130,7 @@ class _MihMineSweeperLeaderBoardState extends State<MihMineSweeperLeaderBoard> {
children: [ children: [
const SizedBox(height: 50), const SizedBox(height: 50),
Icon( Icon(
MihIcons.mineSweeper, MihIcons.mihMinesweeper,
size: 165, size: 165,
color: MihColors.secondary(), color: MihColors.secondary(),
), ),
@@ -596,7 +596,7 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
children: [ children: [
const SizedBox(height: 50), const SizedBox(height: 50),
Icon( Icon(
MihIcons.mineSweeper, MihIcons.mihMinesweeper,
size: 165, size: 165,
color: MihColors.secondary(), color: MihColors.secondary(),
), ),
@@ -780,7 +780,7 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
_timer != null && !kIsWeb && (Platform.isAndroid || Platform.isIOS) _timer != null && !kIsWeb && (Platform.isAndroid || Platform.isIOS)
? MihBannerAd() ? MihBannerAd()
: SizedBox(), : SizedBox(),
SizedBox(height: 15), SizedBox(height: 10),
], ],
); );
}, },
@@ -134,7 +134,7 @@ class _MihMineSweeperLeaderBoardState extends State<MyScoreBoard> {
children: [ children: [
const SizedBox(height: 50), const SizedBox(height: 50),
Icon( Icon(
MihIcons.mineSweeper, MihIcons.mihMinesweeper,
size: 165, size: 165,
color: MihColors.secondary(), color: MihColors.secondary(),
), ),
@@ -40,7 +40,7 @@ class _MzansiBusinessProfileViewState extends State<MzansiBusinessProfileView> {
extra: true, extra: true,
); );
} else { } else {
KenLogger.success("Business found: ${biz.Name}"); // KenLogger.success("Business found: ${biz.Name}");
directoryProvider.setSelectedBusiness(business: biz); directoryProvider.setSelectedBusiness(business: biz);
} }
} }
@@ -1,9 +1,13 @@
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:custom_rating_bar/custom_rating_bar.dart'; import 'package:custom_rating_bar/custom_rating_bar.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.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_business_info_card_v2.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_business_info_card_v2.dart';
import 'package:mzansi_innovation_hub/mih_providers/mzansi_directory_provider.dart'; import 'package:mzansi_innovation_hub/mih_providers/mzansi_directory_provider.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_file_services.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_file_services.dart';
@@ -53,9 +57,10 @@ class _MihBusinessDetailsViewState extends State<MihBusinessDetailsView> {
return Consumer<MzansiDirectoryProvider>( return Consumer<MzansiDirectoryProvider>(
builder: (BuildContext context, MzansiDirectoryProvider directoryProvider, builder: (BuildContext context, MzansiDirectoryProvider directoryProvider,
Widget? child) { Widget? child) {
return Stack( return Column(
children: [ children: [
MihSingleChildScroll( Expanded(
child: MihSingleChildScroll(
scrollbarOn: true, scrollbarOn: true,
child: Padding( child: Padding(
padding: MzansiInnovationHub.of(context)!.theme.screenType == padding: MzansiInnovationHub.of(context)!.theme.screenType ==
@@ -148,8 +153,8 @@ class _MihBusinessDetailsViewState extends State<MihBusinessDetailsView> {
halfFilledColor: MihColors.yellow(), halfFilledColor: MihColors.yellow(),
// MihColors.primary(), // MihColors.primary(),
isHalfAllowed: true, isHalfAllowed: true,
initialRating: initialRating: directoryProvider
directoryProvider.selectedBusiness!.rating.isNotEmpty .selectedBusiness!.rating.isNotEmpty
? double.parse( ? double.parse(
directoryProvider.selectedBusiness!.rating) directoryProvider.selectedBusiness!.rating)
: 0, : 0,
@@ -183,6 +188,11 @@ class _MihBusinessDetailsViewState extends State<MihBusinessDetailsView> {
), ),
), ),
), ),
),
!kIsWeb && (Platform.isAndroid || Platform.isIOS)
? MihBannerAd()
: SizedBox(),
SizedBox(height: 10),
], ],
); );
}, },
@@ -63,7 +63,7 @@ class _MihBusinessQrCodeState extends State<MihBusinessQrCode> {
Future<void> saveImage(Uint8List imageBytes) async { Future<void> saveImage(Uint8List imageBytes) async {
final String filename = final String filename =
"${business.Name}_QR_Code_${DateTime.now().millisecondsSinceEpoch}.png"; "${business.Name}_QR_Code_${DateTime.now().millisecondsSinceEpoch}";
if (kIsWeb) { if (kIsWeb) {
await FileSaver.instance.saveFile( await FileSaver.instance.saveFile(
name: filename, name: filename,
@@ -0,0 +1,185 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:ken_logger/ken_logger.dart';
import 'package:mih_package_toolkit/mih_package_toolkit.dart';
import 'package:mzansi_innovation_hub/main.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_profile_links_service.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.dart';
import 'package:provider/provider.dart';
class MihAddUserProfileLinksWindow extends StatefulWidget {
const MihAddUserProfileLinksWindow({super.key});
@override
State<MihAddUserProfileLinksWindow> createState() =>
_MihAddUserProfileLinksWindowState();
}
class _MihAddUserProfileLinksWindowState
extends State<MihAddUserProfileLinksWindow> {
final _formKey = GlobalKey<FormState>();
List<String> _dropdowOptions = [
"YouTube",
"TikTok",
"Twitch",
"Threads",
"WhatsApp",
"Instagram",
"X",
"LinkedIn",
"Facebook",
"Reddit",
"Discord",
"Git",
"Telegram",
"Pinterest",
"Snapchat",
"Messenger",
"Medium",
"Substack",
"Spotify",
"YT Music",
"Apple Music",
"Patreon",
"Loolio",
"WeChat",
"Other"
];
TextEditingController _dropdownLinkNameController = TextEditingController();
TextEditingController _linkNameController = TextEditingController();
TextEditingController _destinationController = TextEditingController();
void successPopUp(String title, String message, int packageIndex) {
MihAlertServices().successBasicAlert(
title,
message,
context,
);
}
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
_dropdowOptions.sort();
bool isOtherSelected = _dropdownLinkNameController.text == "Other";
return Consumer<MzansiProfileProvider>(
builder: (
BuildContext context,
MzansiProfileProvider profileProvider,
Widget? child,
) {
return MihPackageWindow(
fullscreen: false,
windowTitle: "Add Link",
onWindowTapClose: () {
_dropdownLinkNameController.clear();
_destinationController.clear();
_linkNameController.clear();
Navigator.pop(context);
},
windowBody: Padding(
padding:
MzansiInnovationHub.of(context)!.theme.screenType == "desktop"
? EdgeInsets.symmetric(horizontal: screenWidth * 0.05)
: EdgeInsets.symmetric(horizontal: screenWidth * 0),
child: Column(
children: [
MihForm(
formKey: _formKey,
formFields: [
MihDropdownField(
controller: _dropdownLinkNameController,
hintText: 'Site Name',
dropdownOptions: _dropdowOptions,
requiredText: true,
editable: true,
enableSearch: true,
validator: (value) {
return MihValidationServices().isEmpty(value);
},
onSelected: (value) {
setState(() {});
},
),
const SizedBox(height: 10),
MihTextFormField(
fillColor: MihColors.secondary(),
inputColor: MihColors.primary(),
controller: _linkNameController,
hintText: "Custom Name",
requiredText: isOtherSelected,
validator: (value) {
if (isOtherSelected) {
return MihValidationServices().isEmpty(value);
}
return null;
},
),
const SizedBox(height: 10),
MihTextFormField(
fillColor: MihColors.secondary(),
inputColor: MihColors.primary(),
controller: _destinationController,
hintText: "Link",
requiredText: true,
validator: (value) {
return MihValidationServices().isEmpty(value);
},
),
const SizedBox(height: 20),
MihButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
MihProfileLinksServices.loadingPopUp(context);
int statusCode =
await MihProfileLinksServices.addProfileLink(
profileProvider,
profileProvider.user!.app_id,
"",
_dropdownLinkNameController.text,
_linkNameController.text,
_destinationController.text,
profileProvider.personalLinks.length + 1,
);
KenLogger.success("Status COde: $statusCode");
context.pop();
if (statusCode == 201) {
await MihProfileLinksServices.getUserProfileLinks(
profileProvider,
profileProvider.user!.app_id,
);
context.pop();
successPopUp(
"Profile Link Added",
"You have successfully added a new link to your profile",
0);
} else {
MihAlertServices().internetConnectionAlert(context);
}
} else {
MihAlertServices().inputErrorAlert(context);
}
},
buttonColor: MihColors.green(),
width: 300,
child: Text(
"Add",
style: TextStyle(
color: MihColors.primary(),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
),
);
},
);
}
}
@@ -0,0 +1,195 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:ken_logger/ken_logger.dart';
import 'package:mih_package_toolkit/mih_package_toolkit.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_objects/profile_link.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_profile_links_service.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.dart';
import 'package:provider/provider.dart';
class MihEditUserProfileLinksWindow extends StatefulWidget {
final ProfileLink link;
const MihEditUserProfileLinksWindow({
super.key,
required this.link,
});
@override
State<MihEditUserProfileLinksWindow> createState() =>
_MihEditUserProfileLinksWindowState();
}
class _MihEditUserProfileLinksWindowState
extends State<MihEditUserProfileLinksWindow> {
final _formKey = GlobalKey<FormState>();
List<String> _dropdowOptions = [
"YouTube",
"TikTok",
"Twitch",
"Threads",
"WhatsApp",
"Instagram",
"X",
"LinkedIn",
"Facebook",
"Reddit",
"Discord",
"Git",
"Telegram",
"Pinterest",
"Snapchat",
"Messenger",
"Medium",
"Substack",
"Spotify",
"YT Music",
"Apple Music",
"Patreon",
"Loolio",
"WeChat",
"Other"
];
TextEditingController _dropdownLinkNameController = TextEditingController();
TextEditingController _linkNameController = TextEditingController();
TextEditingController _destinationController = TextEditingController();
void successPopUp(String title, String message, int packageIndex) {
MihAlertServices().successBasicAlert(
title,
message,
context,
);
}
@override
void initState() {
super.initState();
_dropdownLinkNameController.text = widget.link.site_name;
_linkNameController.text = widget.link.custom_name;
_destinationController.text = widget.link.destination;
}
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
_dropdowOptions.sort();
bool isOtherSelected = _dropdownLinkNameController.text == "Other";
return Consumer<MzansiProfileProvider>(
builder: (
BuildContext context,
MzansiProfileProvider profileProvider,
Widget? child,
) {
return MihPackageWindow(
fullscreen: false,
windowTitle: "Update Link",
onWindowTapClose: () {
_dropdownLinkNameController.clear();
_destinationController.clear();
_linkNameController.clear();
Navigator.pop(context);
},
windowBody: Padding(
padding:
MzansiInnovationHub.of(context)!.theme.screenType == "desktop"
? EdgeInsets.symmetric(horizontal: screenWidth * 0.05)
: EdgeInsets.symmetric(horizontal: screenWidth * 0),
child: Column(
children: [
MihForm(
formKey: _formKey,
formFields: [
MihDropdownField(
controller: _dropdownLinkNameController,
hintText: 'Site Name',
dropdownOptions: _dropdowOptions,
requiredText: true,
editable: true,
enableSearch: true,
validator: (value) {
return MihValidationServices().isEmpty(value);
},
onSelected: (value) {
setState(() {});
},
),
const SizedBox(height: 10),
MihTextFormField(
fillColor: MihColors.secondary(),
inputColor: MihColors.primary(),
controller: _linkNameController,
hintText: "Custom Name",
requiredText: isOtherSelected,
validator: (value) {
if (isOtherSelected) {
return MihValidationServices().isEmpty(value);
}
return null;
},
),
const SizedBox(height: 10),
MihTextFormField(
fillColor: MihColors.secondary(),
inputColor: MihColors.primary(),
controller: _destinationController,
hintText: "Link",
requiredText: true,
validator: (value) {
return MihValidationServices().isEmpty(value);
},
),
const SizedBox(height: 20),
MihButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
MihProfileLinksServices.loadingPopUp(context);
int statusCode =
await MihProfileLinksServices.updateProfileLink(
profileProvider,
widget.link.idprofile_links,
profileProvider.user!.app_id,
"",
_dropdownLinkNameController.text,
_linkNameController.text,
_destinationController.text,
widget.link.order,
context,
);
context.pop();
if (statusCode == 200) {
context.pop();
successPopUp(
"Profile Link Updated",
"You have successfully update a link in your profile",
0);
} else {
MihAlertServices().internetConnectionAlert(context);
}
} else {
MihAlertServices().inputErrorAlert(context);
}
},
buttonColor: MihColors.green(),
width: 300,
child: Text(
"Update",
style: TextStyle(
color: MihColors.primary(),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
),
);
},
);
}
}
@@ -0,0 +1,226 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:mih_package_toolkit/mih_package_toolkit.dart';
import 'package:mzansi_innovation_hub/mih_objects/profile_link.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/components/mih_edit_user_profile_links_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_profile_links_service.dart';
import 'package:provider/provider.dart';
class MihManageUserProfileLinksWindow extends StatefulWidget {
const MihManageUserProfileLinksWindow({super.key});
@override
State<MihManageUserProfileLinksWindow> createState() =>
_MihManageUserProfileLinksWindowState();
}
class _MihManageUserProfileLinksWindowState
extends State<MihManageUserProfileLinksWindow> {
void successPopUp(String title, String message, int packageIndex) {
MihAlertServices().successBasicAlert(
title,
message,
context,
);
}
void removeLinkWarning(
MzansiProfileProvider profileProvider, int idprofile_links) {
MihAlertServices().warningAdvancedAlert(
"Remove Link?",
"Are you sure you want to remove this link from your profile?",
[
MihButton(
onPressed: () async {
MihProfileLinksServices.loadingPopUp(context);
int statusCode = await MihProfileLinksServices.deleteProfileLink(
profileProvider,
idprofile_links,
);
context.pop();
context.pop();
if (statusCode == 200) {
successPopUp("profile Link Deleted",
"you have successfully deleted a link to your profile", 0);
} else {
MihAlertServices().internetConnectionAlert(context);
}
},
buttonColor: MihColors.red(),
width: 300,
child: Text(
"Remove",
style: TextStyle(
color: MihColors.primary(),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
MihButton(
onPressed: () async {
context.pop();
},
buttonColor: MihColors.green(),
width: 300,
child: Text(
"Cancel",
style: TextStyle(
color: MihColors.primary(),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
],
context,
);
}
void editLinkWindow(ProfileLink link) {
showDialog(
context: context,
builder: (context) => MihEditUserProfileLinksWindow(link: link));
}
Widget linkActions(MzansiProfileProvider profileProvider, ProfileLink link) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
color: MihColors.green(),
onPressed: () {
editLinkWindow(link);
},
icon: Icon(
Icons.edit,
),
),
const SizedBox(width: 2),
IconButton(
color: MihColors.red(),
onPressed: () {
removeLinkWarning(profileProvider, link.idprofile_links);
},
icon: Icon(
Icons.delete,
),
),
],
);
}
@override
Widget build(BuildContext context) {
// double screenWidth = MediaQuery.of(context).size.width;
return Consumer<MzansiProfileProvider>(
builder: (
BuildContext context,
MzansiProfileProvider profileProvider,
Widget? child,
) {
// return Placeholder();
return MihPackageWindow(
fullscreen: true,
windowTitle: "Manage Links",
onWindowTapClose: () {
Navigator.pop(context);
},
windowBody: Column(
children: [
Expanded(
child: Theme(
data: Theme.of(context).copyWith(
iconTheme: IconThemeData(
color: MihColors.grey(),
),
),
child: ReorderableListView.builder(
itemBuilder: (context, index) {
ProfileLink link = profileProvider.personalLinks[index];
String display = link.site_name;
if (link.custom_name.isNotEmpty) {
display += " (${link.custom_name})";
}
return ListTile(
key: ValueKey("$index"),
title: Text(
"$display",
style: TextStyle(
// fontWeight: FontWeight.bold,
fontSize: 18,
),
),
leading: linkActions(
profileProvider,
link,
),
);
},
itemCount: profileProvider.personalLinks.length,
onReorder: (oldIndex, newIndex) {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final ProfileLink link =
profileProvider.personalLinks.removeAt(oldIndex);
profileProvider.personalLinks.insert(newIndex, link);
}),
),
),
MihButton(
onPressed: () async {
MihProfileLinksServices.loadingPopUp(context);
int newIndex = 1;
for (var link in profileProvider.personalLinks) {
int statusCode =
await MihProfileLinksServices.updateProfileLink(
profileProvider,
link.idprofile_links,
link.app_id,
link.business_id,
link.site_name,
link.custom_name,
link.destination,
newIndex,
context);
if (statusCode != 200) {
await MihProfileLinksServices.updateProfileLink(
profileProvider,
link.idprofile_links,
link.app_id,
link.business_id,
link.site_name,
link.custom_name,
link.destination,
newIndex,
context);
}
newIndex++;
}
context.pop();
context.pop();
successPopUp("profile Link Reordered",
"you have successfully reordered your profile links", 0);
},
buttonColor: MihColors.green(),
width: 300,
child: Text(
"Update Order",
style: TextStyle(
color: MihColors.primary(),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 10),
],
),
);
},
);
}
}
@@ -1,10 +1,12 @@
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:mih_package_toolkit/mih_package_toolkit.dart'; import 'package:mih_package_toolkit/mih_package_toolkit.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_qr_code.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_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_settings.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_settings.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_data_helper_services.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_data_helper_services.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_profile_links_service.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class MzansiProfile extends StatefulWidget { class MzansiProfile extends StatefulWidget {
@@ -19,6 +21,7 @@ class MzansiProfile extends StatefulWidget {
class _MzansiProfileState extends State<MzansiProfile> { class _MzansiProfileState extends State<MzansiProfile> {
bool _isLoadingInitialData = true; bool _isLoadingInitialData = true;
late final MihPersonalProfile _personalProfile; late final MihPersonalProfile _personalProfile;
late final MihPersonalQrCode _personalQrCode;
late final MihPersonalSettings _personalSettings; late final MihPersonalSettings _personalSettings;
Future<void> _loadInitialData() async { Future<void> _loadInitialData() async {
@@ -32,6 +35,10 @@ class _MzansiProfileState extends State<MzansiProfile> {
mzansiProfileProvider, mzansiProfileProvider,
); );
} }
await MihProfileLinksServices.getUserProfileLinks(
mzansiProfileProvider,
mzansiProfileProvider.user!.app_id,
);
setState(() { setState(() {
_isLoadingInitialData = false; _isLoadingInitialData = false;
}); });
@@ -41,6 +48,7 @@ class _MzansiProfileState extends State<MzansiProfile> {
void initState() { void initState() {
super.initState(); super.initState();
_personalProfile = const MihPersonalProfile(); _personalProfile = const MihPersonalProfile();
_personalQrCode = const MihPersonalQrCode(user: null);
_personalSettings = const MihPersonalSettings(); _personalSettings = const MihPersonalSettings();
_loadInitialData(); _loadInitialData();
} }
@@ -91,12 +99,12 @@ class _MzansiProfileState extends State<MzansiProfile> {
temp[const Icon(Icons.person)] = () { temp[const Icon(Icons.person)] = () {
context.read<MzansiProfileProvider>().setPersonalIndex(0); context.read<MzansiProfileProvider>().setPersonalIndex(0);
}; };
// temp[const Icon(Icons.person)] = () { temp[const Icon(Icons.qr_code_rounded)] = () {
// context.read<MzansiProfileProvider>().setPersonalIndex(1);
// };
temp[const Icon(Icons.settings)] = () {
context.read<MzansiProfileProvider>().setPersonalIndex(1); context.read<MzansiProfileProvider>().setPersonalIndex(1);
}; };
temp[const Icon(Icons.settings)] = () {
context.read<MzansiProfileProvider>().setPersonalIndex(2);
};
return MihPackageTools( return MihPackageTools(
tools: temp, tools: temp,
selectedIndex: context.watch<MzansiProfileProvider>().personalIndex, selectedIndex: context.watch<MzansiProfileProvider>().personalIndex,
@@ -106,6 +114,7 @@ class _MzansiProfileState extends State<MzansiProfile> {
List<Widget> getToolBody() { List<Widget> getToolBody() {
return [ return [
_personalProfile, _personalProfile,
_personalQrCode,
_personalSettings, _personalSettings,
]; ];
} }
@@ -113,6 +122,7 @@ class _MzansiProfileState extends State<MzansiProfile> {
List<String> getToolTitle() { List<String> getToolTitle() {
List<String> toolTitles = [ List<String> toolTitles = [
"Profile", "Profile",
"Share",
"Settings", "Settings",
]; ];
return toolTitles; return toolTitles;
@@ -1,11 +1,18 @@
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
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/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile_view.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile_view.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_qr_code.dart';
import 'package:mzansi_innovation_hub/mih_providers/mzansi_directory_provider.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_user_services.dart';
import 'package:provider/provider.dart';
class MzansiProfileView extends StatefulWidget { class MzansiProfileView extends StatefulWidget {
final String? username;
const MzansiProfileView({ const MzansiProfileView({
super.key, super.key,
required this.username,
}); });
@override @override
@@ -15,15 +22,47 @@ class MzansiProfileView extends StatefulWidget {
class _MzansiProfileViewState extends State<MzansiProfileView> { class _MzansiProfileViewState extends State<MzansiProfileView> {
int _selectedIndex = 0; int _selectedIndex = 0;
late final MihPersonalProfileView _personalProfileView; late final MihPersonalProfileView _personalProfileView;
late final MihPersonalQrCode _personalQrCode;
void _loadUserData() async {
MzansiDirectoryProvider directoryProvider =
context.read<MzansiDirectoryProvider>();
if (widget.username != null) {
final user = await MihUserServices()
.getMIHUserDetailsByUsername(widget.username!, context);
if (user == null) {
context.goNamed(
'mihHome',
extra: true,
);
} else {
KenLogger.success("User found: ${user.username}");
directoryProvider.setSelectedUser(user: user);
}
}
_personalProfileView = MihPersonalProfileView();
_personalQrCode = MihPersonalQrCode(user: directoryProvider.selectedUser);
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_personalProfileView = MihPersonalProfileView(); _loadUserData();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Consumer<MzansiDirectoryProvider>(
builder: (BuildContext context, MzansiDirectoryProvider directoryProvider,
Widget? child) {
if (directoryProvider.selectedUser == null) {
KenLogger.warning("User is null, showing loading indicator");
return Scaffold(
body: const Center(
child: Mihloadingcircle(),
),
);
} else {
return MihPackage( return MihPackage(
packageActionButton: getAction(), packageActionButton: getAction(),
packageTools: getTools(), packageTools: getTools(),
@@ -37,6 +76,9 @@ class _MzansiProfileViewState extends State<MzansiProfileView> {
}, },
); );
} }
},
);
}
MihPackageAction getAction() { MihPackageAction getAction() {
return MihPackageAction( return MihPackageAction(
@@ -57,6 +99,11 @@ class _MzansiProfileViewState extends State<MzansiProfileView> {
_selectedIndex = 0; _selectedIndex = 0;
}); });
}; };
temp[const Icon(Icons.qr_code_rounded)] = () {
setState(() {
_selectedIndex = 1;
});
};
return MihPackageTools( return MihPackageTools(
tools: temp, tools: temp,
selectedIndex: _selectedIndex, selectedIndex: _selectedIndex,
@@ -66,12 +113,14 @@ class _MzansiProfileViewState extends State<MzansiProfileView> {
List<Widget> getToolBody() { List<Widget> getToolBody() {
return [ return [
_personalProfileView, _personalProfileView,
_personalQrCode,
]; ];
} }
List<String> getToolTitle() { List<String> getToolTitle() {
List<String> toolTitles = [ List<String> toolTitles = [
"Profile", "Profile",
"Share",
]; ];
return toolTitles; return toolTitles;
} }
@@ -2,9 +2,10 @@ import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.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_objects/profile_link.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_circle_avatar.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_circle_avatar.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_profile_links.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_profile_links.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/components/mih_add_user_profile_links_window.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/components/mih_manage_user_profile_links_window.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_packages/mzansi_profile/personal_profile/components/mih_edit_personal_profile_window.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/components/mih_edit_personal_profile_window.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -20,107 +21,29 @@ class _MihPersonalProfileState extends State<MihPersonalProfile> {
TextEditingController proPicController = TextEditingController(); TextEditingController proPicController = TextEditingController();
PlatformFile? newSelectedProPic; PlatformFile? newSelectedProPic;
void editProfileWindow(double width) { void editProfileWindow() {
showDialog( showDialog(
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
builder: (context) => Consumer<MzansiProfileProvider>( builder: (context) => MihEditPersonalProfileWindow(),
builder: (BuildContext context,
MzansiProfileProvider mzansiProfileProvider, Widget? child) {
return MihEditPersonalProfileWindow();
},
),
); );
} }
List<ProfileLink> getTempLinks() { void addProfileLinksWindow() {
// return []; showDialog(
return [ context: context,
ProfileLink( barrierDismissible: false,
idprofile_links: 1, builder: (context) => MihAddUserProfileLinksWindow(),
app_id: "1234", );
business_id: "", }
destination: "Youtube",
web_link: "https://www.youtube.com/@MzansiInnovationHub", void editProfileLinksWindow() {
), showDialog(
ProfileLink( context: context,
idprofile_links: 1, // barrierDismissible: false,
app_id: "1234", // builder: (context) => Placeholder(),
business_id: "", builder: (context) => MihManageUserProfileLinksWindow(),
destination: "Threads", );
web_link: "https://www.threads.com/@mzansi.innovation.hub",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "TikTok",
web_link: "https://www.tiktok.com/@mzansiinnovationhub",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "WhatsApp",
web_link: "https://whatsapp.com/channel/0029Vax3INCIyPtMn8KgeM2F",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "Twitch",
web_link: "https://www.twitch.tv/mzansiinnovationhub",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "Instagram",
web_link: "https://www.instagram.com/mzansi.innovation.hub/",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "X",
web_link: "https://x.com/mzansi_inno_hub",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "LinkedIn",
web_link: "https://www.linkedin.com/in/yasien-meth-172352108/",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "Facebook",
web_link: "https://www.facebook.com/profile.php?id=61565345762136",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "Reddit",
web_link: "https://www.reddit.com/r/Mzani_Innovation_Hub/",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "Discord",
web_link: "https://discord.gg/ZtTZYd5d",
),
ProfileLink(
idprofile_links: 1,
app_id: "1234",
business_id: "",
destination: "My App",
web_link: "https://app.mzansi-innovation-hub.co.za/about",
),
];
} }
@override @override
@@ -176,7 +99,7 @@ class _MihPersonalProfileState extends State<MihPersonalProfile> {
right: 5, right: 5,
child: MihButton( child: MihButton(
onPressed: () { onPressed: () {
editProfileWindow(width); editProfileWindow();
}, },
buttonColor: MihColors.green(), buttonColor: MihColors.green(),
width: 35, width: 35,
@@ -245,20 +168,20 @@ class _MihPersonalProfileState extends State<MihPersonalProfile> {
), ),
const SizedBox(height: 15.0), const SizedBox(height: 15.0),
MihProfileLinks( MihProfileLinks(
// links: mzansiProfileProvider.personalLinks, links: mzansiProfileProvider.personalLinks,
links: getTempLinks(),
), ),
const SizedBox(height: 5.0), const SizedBox(height: 8.0),
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
MihButton( MihButton(
onPressed: () { onPressed: () {
editProfileWindow(width); addProfileLinksWindow();
}, },
buttonColor: MihColors.green(), buttonColor: MihColors.green(),
// width: mzansiProfileProvider.personalLinks.isNotEmpty ? 50 : null, width: mzansiProfileProvider.personalLinks.isNotEmpty
width: getTempLinks().isNotEmpty ? 50 : null, ? 50
: null,
height: 50, height: 50,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@@ -268,8 +191,7 @@ class _MihPersonalProfileState extends State<MihPersonalProfile> {
Icons.add, Icons.add,
color: MihColors.primary(), color: MihColors.primary(),
), ),
// if (mzansiProfileProvider.personalLinks.isEmpty) if (mzansiProfileProvider.personalLinks.isEmpty)
if (getTempLinks().isEmpty)
Text( Text(
"Add Links", "Add Links",
style: TextStyle( style: TextStyle(
@@ -281,6 +203,19 @@ class _MihPersonalProfileState extends State<MihPersonalProfile> {
], ],
), ),
), ),
const SizedBox(width: 8.0),
MihButton(
onPressed: () {
editProfileLinksWindow();
},
buttonColor: MihColors.green(),
width: 50,
height: 50,
child: Icon(
Icons.edit,
color: MihColors.primary(),
),
),
], ],
), ),
const SizedBox(height: 20.0), const SizedBox(height: 20.0),
@@ -1,12 +1,20 @@
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/foundation.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_objects/profile_link.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_banner_ad.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_profile_links.dart';
import 'package:mzansi_innovation_hub/mih_providers/mzansi_directory_provider.dart'; import 'package:mzansi_innovation_hub/mih_providers/mzansi_directory_provider.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_file_services.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_file_services.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_circle_avatar.dart'; import 'package:mzansi_innovation_hub/mih_package_components/mih_circle_avatar.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_profile_links_service.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:redacted/redacted.dart';
class MihPersonalProfileView extends StatefulWidget { class MihPersonalProfileView extends StatefulWidget {
const MihPersonalProfileView({ const MihPersonalProfileView({
@@ -19,6 +27,7 @@ class MihPersonalProfileView extends StatefulWidget {
class _MihPersonalProfileViewState extends State<MihPersonalProfileView> { class _MihPersonalProfileViewState extends State<MihPersonalProfileView> {
late Future<String> futureImageUrl; late Future<String> futureImageUrl;
late Future<List<ProfileLink>> futureLinks;
PlatformFile? file; PlatformFile? file;
@override @override
@@ -33,6 +42,8 @@ class _MihPersonalProfileViewState extends State<MihPersonalProfileView> {
context.read<MzansiDirectoryProvider>(); context.read<MzansiDirectoryProvider>();
futureImageUrl = MihFileApi.getMinioFileUrl( futureImageUrl = MihFileApi.getMinioFileUrl(
directoryProvider.selectedUser!.pro_pic_path); directoryProvider.selectedUser!.pro_pic_path);
futureLinks = MihProfileLinksServices.getUserProfileLinksMD(
directoryProvider, directoryProvider.selectedUser!.app_id);
} }
@override @override
@@ -51,11 +62,14 @@ class _MihPersonalProfileViewState extends State<MihPersonalProfileView> {
return Consumer<MzansiDirectoryProvider>( return Consumer<MzansiDirectoryProvider>(
builder: (BuildContext context, MzansiDirectoryProvider directoryProvider, builder: (BuildContext context, MzansiDirectoryProvider directoryProvider,
Widget? child) { Widget? child) {
return MihSingleChildScroll( return Column(
children: [
Expanded(
child: MihSingleChildScroll(
scrollbarOn: true, scrollbarOn: true,
child: Padding( child: Padding(
padding: padding: MzansiInnovationHub.of(context)!.theme.screenType ==
MzansiInnovationHub.of(context)!.theme.screenType == "desktop" "desktop"
? EdgeInsets.symmetric(horizontal: width * 0.2) ? EdgeInsets.symmetric(horizontal: width * 0.2)
: EdgeInsets.symmetric(horizontal: width * 0.075), : EdgeInsets.symmetric(horizontal: width * 0.075),
child: Column( child: Column(
@@ -146,10 +160,55 @@ class _MihPersonalProfileViewState extends State<MihPersonalProfileView> {
), ),
), ),
), ),
const SizedBox(height: 30.0), const SizedBox(height: 15.0),
FutureBuilder(
future: futureLinks,
builder: (context, asyncSnapshot) {
if (asyncSnapshot.connectionState ==
ConnectionState.done &&
asyncSnapshot.hasData) {
directoryProvider.setPersonalLinks(
personalLinks: asyncSnapshot.data!);
return MihProfileLinks(
links: directoryProvider.personalLinks,
);
} else {
return Wrap(
alignment: WrapAlignment.center,
runAlignment: WrapAlignment.center,
runSpacing: 10,
spacing: 10,
children: [
Container(width: 70, height: 70).redacted(
context: context,
redact: true,
),
Container(width: 70, height: 70).redacted(
context: context,
redact: true,
),
Container(width: 70, height: 70).redacted(
context: context,
redact: true,
),
Container(width: 70, height: 70).redacted(
context: context,
redact: true,
),
],
);
}
}),
], ],
), ),
), ),
),
),
!kIsWeb && (Platform.isAndroid || Platform.isIOS)
? MihBannerAd()
: SizedBox(),
SizedBox(height: 10),
],
); );
}, },
); );
@@ -0,0 +1,410 @@
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';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:go_router/go_router.dart';
import 'package:ken_logger/ken_logger.dart';
import 'package:mih_package_toolkit/mih_package_toolkit.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_objects/app_user.dart';
import 'package:mzansi_innovation_hub/mih_providers/mzansi_directory_provider.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_env.dart';
import 'package:mzansi_innovation_hub/mih_providers/mzansi_profile_provider.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_file_services.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_circle_avatar.dart';
import 'package:provider/provider.dart';
import 'package:screenshot/screenshot.dart';
import 'package:share_plus/share_plus.dart';
import 'package:supertokens_flutter/supertokens.dart';
class MihPersonalQrCode extends StatefulWidget {
final AppUser? user;
const MihPersonalQrCode({
super.key,
required this.user,
});
@override
State<MihPersonalQrCode> createState() => _MihPersonalQrCodeState();
}
class _MihPersonalQrCodeState extends State<MihPersonalQrCode> {
late AppUser user;
late Future<String> futureImageUrl;
PlatformFile? file;
int qrSize = 500;
ScreenshotController screenshotController = ScreenshotController();
Uint8List? personalQRImageFile;
bool _isUserSignedIn = false;
final String _qrCodedata =
"${AppEnviroment.baseAppUrl}/mzansi-profile/view?username=";
Future<void> _checkUserSession() async {
final doesSessionExist = await SuperTokens.doesSessionExist();
setState(() {
_isUserSignedIn = doesSessionExist;
});
}
String getQrCodeData(int qrSize) {
String color =
MihColors.primary().toARGB32().toRadixString(16).substring(2, 8);
// KenLogger.warning(color);
String bgColor =
MihColors.secondary().toARGB32().toRadixString(16).substring(2, 8);
// KenLogger.warning(bgColor);
String encodedData = Uri.encodeComponent("$_qrCodedata${user.username}");
return "https://api.qrserver.com/v1/create-qr-code/?data=$encodedData&size=${qrSize}x$qrSize&bgcolor=$bgColor&color=$color";
}
Future<void> saveImage(Uint8List imageBytes) async {
final String filename =
"${user.username}_QR_Code_${DateTime.now().millisecondsSinceEpoch}";
// "${user.username}_QR_Code_${DateTime.now().millisecondsSinceEpoch}.png";
if (kIsWeb) {
await FileSaver.instance.saveFile(
name: filename,
bytes: imageBytes,
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,
bytes: imageBytes,
fileExtension: "png",
mimeType: MimeType.png,
);
}
}
Future<void> downloadQrCode() async {
if (_isUserSignedIn) {
await screenshotController.capture().then((image) {
// KenLogger.success("Image Captured: $image");
setState(() {
personalQRImageFile = image;
});
}).catchError((onError) {
KenLogger.error(onError);
});
// KenLogger.success("QR Code Image Captured : $businessQRImageFile");
saveImage(personalQRImageFile!);
} else {
showSignInRequiredAlert();
}
}
void showSignInRequiredAlert() {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return MihPackageWindow(
fullscreen: false,
windowTitle: null,
onWindowTapClose: null,
windowBody: Column(
children: [
Icon(
MihIcons.mihLogo,
size: 100,
color: MihColors.secondary(),
),
Text(
"Let's Get Started",
textAlign: TextAlign.center,
style: TextStyle(
color: MihColors.primary(),
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.secondary(),
fontSize: 15,
),
),
const SizedBox(height: 25),
Center(
child: MihButton(
onPressed: () {
context.goNamed(
'mihHome',
extra: true,
);
},
buttonColor: MihColors.green(),
elevation: 10,
width: 300,
child: Text(
"Sign In/ Create Account",
style: TextStyle(
color: MihColors.primary(),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
)
],
),
);
},
);
}
Widget displayPersonalQRCode(double profilePictureWidth) {
return Screenshot(
controller: screenshotController,
child: Material(
color: MihColors.secondary().withValues(alpha: 0.6),
borderRadius: BorderRadius.circular(25),
elevation: 10,
shadowColor: Colors.black,
child: Container(
decoration: BoxDecoration(
color: MihColors.secondary(),
borderRadius: BorderRadius.circular(20),
),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
FutureBuilder(
future: futureImageUrl,
builder: (context, asyncSnapshot) {
if (asyncSnapshot.connectionState ==
ConnectionState.done &&
asyncSnapshot.hasData) {
if (asyncSnapshot.requireData != "" ||
asyncSnapshot.requireData.isNotEmpty) {
return MihCircleAvatar(
imageFile: CachedNetworkImageProvider(
asyncSnapshot.requireData),
width: profilePictureWidth,
expandable: true,
editable: false,
fileNameController: TextEditingController(),
userSelectedfile: file,
frameColor: MihColors.primary(),
backgroundColor: MihColors.secondary(),
onChange: () {},
);
} else {
return Icon(
MihIcons.iDontKnow,
size: profilePictureWidth,
color: MihColors.primary(),
);
}
} else {
return Icon(
MihIcons.mihRing,
size: profilePictureWidth,
color: MihColors.primary(),
);
}
},
),
FittedBox(
child: Text(
user.username,
style: TextStyle(
fontSize: 35,
fontWeight: FontWeight.bold,
color: MihColors.primary(),
),
),
),
FittedBox(
child: Text(
"${user.fname} ${user.lname}",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: MihColors.primary(),
),
),
),
const SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FittedBox(
child: Text(
"Powered by MIH",
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: MihColors.primary(),
),
),
),
const SizedBox(width: 5),
Icon(
MihIcons.mihLogo,
size: 20,
color: MihColors.primary(),
),
],
),
const SizedBox(height: 10),
SizedBox(
width: 300,
height: 300,
child: CachedNetworkImage(
imageUrl: getQrCodeData(qrSize.toInt()),
placeholder: (context, url) => FittedBox(
child: const Mihloadingcircle(),
),
errorWidget: (context, url, error) =>
const Icon(Icons.error),
),
),
const SizedBox(height: 10),
FittedBox(
child: Text(
"Scan & Connect",
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
color: MihColors.primary(),
),
),
),
],
)),
),
),
);
}
void shareMIHLink(BuildContext context, String message, String link) {
String shareText = "$message: $link";
SharePlus.instance.share(
ShareParams(text: shareText),
);
}
@override
void initState() {
super.initState();
MzansiProfileProvider profileProvider =
context.read<MzansiProfileProvider>();
if (widget.user != null) {
user = widget.user!;
} else {
user = profileProvider.user!;
}
_checkUserSession();
futureImageUrl = MihFileApi.getMinioFileUrl(user.pro_pic_path);
}
@override
Widget build(BuildContext context) {
Size screenSize = MediaQuery.of(context).size;
return Consumer(builder: (
BuildContext context,
MzansiDirectoryProvider directoryProvider,
Widget? child,
) {
return MihPackageToolBody(
backgroundColor: MihColors.primary(),
borderOn: false,
innerHorizontalPadding: 10,
bodyItem: getBody(screenSize, context),
);
});
}
Widget getBody(Size screenSize, BuildContext context) {
double profilePictureWidth = 150;
return Stack(
alignment: Alignment.topCenter,
children: [
MihSingleChildScroll(
scrollbarOn: true,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
child: Padding(
padding:
MzansiInnovationHub.of(context)!.theme.screenType == "desktop"
? EdgeInsets.symmetric(horizontal: screenSize.width * 0.2)
: EdgeInsets.symmetric(
horizontal: screenSize.width * 0), //.075),
child: Padding(
padding: const EdgeInsets.only(top: 10.0),
child: displayPersonalQRCode(profilePictureWidth),
),
),
),
),
Positioned(
right: 10,
bottom: 10,
child: MihFloatingMenu(
animatedIcon: AnimatedIcons.menu_close,
children: [
SpeedDialChild(
child: Icon(
Icons.download_rounded,
color: MihColors.primary(),
),
label: "Download QR Code",
labelBackgroundColor: MihColors.green(),
labelStyle: TextStyle(
color: MihColors.primary(),
fontWeight: FontWeight.bold,
),
backgroundColor: MihColors.green(),
onTap: () {
downloadQrCode();
},
),
SpeedDialChild(
child: Icon(
Icons.share_rounded,
color: MihColors.primary(),
),
label: "Share Profile",
labelBackgroundColor: MihColors.green(),
labelStyle: TextStyle(
color: MihColors.primary(),
fontWeight: FontWeight.bold,
),
backgroundColor: MihColors.green(),
onTap: () {
shareMIHLink(
context,
"Check out ${user.username} on the MIH app's Mzansi Directory",
"$_qrCodedata${user.username}",
);
},
),
]),
)
],
);
}
}
@@ -17,7 +17,8 @@ class MzansiAiProvider extends ChangeNotifier {
}) { }) {
ollamaProvider = OllamaProvider( ollamaProvider = OllamaProvider(
baseUrl: "${AppEnviroment.baseAiUrl}/api", baseUrl: "${AppEnviroment.baseAiUrl}/api",
model: AppEnviroment.getEnv() == "Prod" ? "qwen3.5:9b" : "qwen3.5:0.8b", model:
AppEnviroment.getEnv() == "Prod" ? "mzansiai:latest" : "qwen3.5:0.8b",
think: false, think: false,
// systemPrompt: "---INSTRUCTION START---\n" // systemPrompt: "---INSTRUCTION START---\n"
// // "Respond concisely. Do not include any <think> tags or internal monologues./n" // // "Respond concisely. Do not include any <think> tags or internal monologues./n"
@@ -3,6 +3,7 @@ import 'package:geolocator/geolocator.dart';
import 'package:mzansi_innovation_hub/mih_objects/app_user.dart'; import 'package:mzansi_innovation_hub/mih_objects/app_user.dart';
import 'package:mzansi_innovation_hub/mih_objects/bookmarked_business.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.dart';
import 'package:mzansi_innovation_hub/mih_objects/profile_link.dart';
class MzansiDirectoryProvider extends ChangeNotifier { class MzansiDirectoryProvider extends ChangeNotifier {
int toolIndex; int toolIndex;
@@ -20,6 +21,8 @@ class MzansiDirectoryProvider extends ChangeNotifier {
AppUser? selectedUser; AppUser? selectedUser;
String searchTerm; String searchTerm;
String businessTypeFilter; String businessTypeFilter;
List<ProfileLink> personalLinks = [];
List<ProfileLink> businessLinks = [];
MzansiDirectoryProvider({ MzansiDirectoryProvider({
this.toolIndex = 0, this.toolIndex = 0,
@@ -116,4 +119,14 @@ class MzansiDirectoryProvider extends ChangeNotifier {
this.businessTypeFilter = businessTypeFilter; this.businessTypeFilter = businessTypeFilter;
notifyListeners(); notifyListeners();
} }
void setPersonalLinks({required List<ProfileLink> personalLinks}) {
this.personalLinks = personalLinks;
notifyListeners();
}
void setBusinessLinks({required List<ProfileLink> businessLinks}) {
this.businessLinks = businessLinks;
notifyListeners();
}
} }
@@ -25,6 +25,7 @@ class MzansiProfileProvider extends ChangeNotifier {
List<AppUser> userSearchResults = []; List<AppUser> userSearchResults = [];
bool hideBusinessUserDetails; bool hideBusinessUserDetails;
List<ProfileLink> personalLinks = []; List<ProfileLink> personalLinks = [];
List<ProfileLink> businessLinks = [];
MzansiProfileProvider({ MzansiProfileProvider({
this.personalHome = true, this.personalHome = true,
@@ -156,4 +157,30 @@ class MzansiProfileProvider extends ChangeNotifier {
this.personalLinks = personalLinks; this.personalLinks = personalLinks;
notifyListeners(); notifyListeners();
} }
void setBusinessLinks({required List<ProfileLink> businessLinks}) {
this.businessLinks = businessLinks;
notifyListeners();
}
void deleteProfileLink({required int linkId}) {
personalLinks.removeWhere((link) => link.idprofile_links == linkId);
businessLinks.removeWhere((link) => link.idprofile_links == linkId);
notifyListeners();
}
void editProfileLink({required ProfileLink updatedLink}) {
int personalIndex = personalLinks.indexWhere(
(link) => link.idprofile_links == updatedLink.idprofile_links);
int businessIndex = businessLinks.indexWhere(
(link) => link.idprofile_links == updatedLink.idprofile_links);
if (personalIndex != -1) {
personalLinks[personalIndex] = updatedLink;
}
if (businessIndex != -1) {
businessLinks[personalIndex] = updatedLink;
}
notifyListeners();
}
} }
@@ -0,0 +1,165 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:ken_logger/ken_logger.dart';
import 'package:mih_package_toolkit/mih_package_toolkit.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_env.dart';
import 'package:mzansi_innovation_hub/mih_objects/profile_link.dart';
import 'package:mzansi_innovation_hub/mih_providers/mzansi_directory_provider.dart';
import 'package:mzansi_innovation_hub/mih_providers/mzansi_profile_provider.dart';
import 'package:supertokens_flutter/http.dart' as http;
class MihProfileLinksServices {
final baseAPI = AppEnviroment.baseApiUrl;
static Future<List<ProfileLink>> getUserProfileLinksMD(
MzansiDirectoryProvider directoryProvider,
String app_id,
) async {
final response = await http.get(
Uri.parse("${AppEnviroment.baseApiUrl}/profile-links/user/$app_id"));
if (response.statusCode == 200) {
Iterable l = jsonDecode(response.body);
List<ProfileLink> myLinks =
List<ProfileLink>.from(l.map((model) => ProfileLink.fromJson(model)));
return myLinks;
} else {
throw Exception('failed to fecth user profile links');
}
}
static Future<void> getUserProfileLinks(
MzansiProfileProvider profileProvider,
String app_id,
) async {
final response = await http.get(
Uri.parse("${AppEnviroment.baseApiUrl}/profile-links/user/$app_id"));
if (response.statusCode == 200) {
Iterable l = jsonDecode(response.body);
List<ProfileLink> myLinks =
List<ProfileLink>.from(l.map((model) => ProfileLink.fromJson(model)));
profileProvider.setPersonalLinks(personalLinks: myLinks);
} else {
throw Exception('failed to fecth user profile links');
}
}
static Future<void> getBusinessProfileLinks(
MzansiProfileProvider profileProvider,
String business_id,
) async {
final response = await http.get(Uri.parse(
"${AppEnviroment.baseApiUrl}/profile-links/business/$business_id"));
if (response.statusCode == 200) {
Iterable l = jsonDecode(response.body);
List<ProfileLink> myLinks =
List<ProfileLink>.from(l.map((model) => ProfileLink.fromJson(model)));
profileProvider.setBusinessLinks(businessLinks: myLinks);
} else {
throw Exception('failed to fecth user profile links');
}
}
static Future<int> deleteProfileLink(
MzansiProfileProvider profileProvider,
int idprofile_links,
) async {
final response = await http.delete(
Uri.parse("${AppEnviroment.baseApiUrl}/profile-links/delete/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(
<String, dynamic>{
"idprofile_links": idprofile_links,
},
),
);
if (response.statusCode == 200) {
profileProvider.deleteProfileLink(linkId: idprofile_links);
}
return response.statusCode;
}
static Future<int> addProfileLink(
MzansiProfileProvider profileProvider,
String app_id,
String business_id,
String site_name,
String custom_name,
String destination,
int order,
) async {
var response = await http.post(
Uri.parse("${AppEnviroment.baseApiUrl}/profile-links/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"app_id": app_id,
"business_id": business_id,
"site_name": site_name,
"custom_name": custom_name,
"destination": destination,
"order": order,
}),
);
KenLogger.success("Response: $response.statusCode");
// if (response.statusCode == 201) {
// if (app_id == "") {
// await getUserProfileLinks(profileProvider, app_id);
// } else {
// await getBusinessProfileLinks(profileProvider, business_id);
// }
// }
return response.statusCode;
}
static Future<int> updateProfileLink(
MzansiProfileProvider profileProvider,
int idprofile_links,
String app_id,
String business_id,
String site_name,
String custom_name,
String destination,
int order,
BuildContext context,
) async {
final response = await http.put(
Uri.parse("${AppEnviroment.baseApiUrl}/profile-links/update/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"idprofile_links": idprofile_links,
"site_name": site_name,
"custom_name": custom_name,
"destination": destination,
"order": order,
}),
);
if (response.statusCode == 200) {
profileProvider.editProfileLink(
updatedLink: ProfileLink(
idprofile_links: idprofile_links,
app_id: app_id,
business_id: business_id,
site_name: site_name,
custom_name: custom_name,
destination: destination,
order: order,
),
);
}
return response.statusCode;
}
static void loadingPopUp(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
}
}
@@ -113,6 +113,25 @@ class MihUserServices {
} }
} }
Future<AppUser?> getMIHUserDetailsByUsername(
String username,
BuildContext context,
) async {
var response = await http.get(
Uri.parse("${AppEnviroment.baseApiUrl}/user/username/$username"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
);
if (response.statusCode == 200) {
String body = response.body;
var jsonBody = jsonDecode(body);
return AppUser.fromJson(jsonBody);
} else {
return null;
}
}
Future<AppUser?> getMyUserDetails( Future<AppUser?> getMyUserDetails(
MzansiProfileProvider profileProvider, MzansiProfileProvider profileProvider,
) async { ) async {
+2 -2
View File
@@ -1220,10 +1220,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: mih_package_toolkit name: mih_package_toolkit
sha256: "63e9ee80b0a3ca8b15a7172f924152e0528149cbe29c7751287a4d30b2d0671f" sha256: "3d0dec0ae9471a37b8c6822419633643721b6b463f2b9b862233feefcec6e8a6"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.0.2" version: "0.0.6"
mime: mime:
dependency: transitive dependency: transitive
description: description:
+1 -1
View File
@@ -14,7 +14,7 @@ dependencies:
sdk: flutter sdk: flutter
flutter_dotenv: ^6.0.0 flutter_dotenv: ^6.0.0
mih_package_toolkit: ^0.0.2 mih_package_toolkit: ^0.0.6
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
font_awesome_flutter: ^10.7.0 font_awesome_flutter: ^10.7.0
# firebase_core: ^4.4.0 # firebase_core: ^4.4.0