186 Commits

Author SHA1 Message Date
yasien_gmail 0ea3482e9a revert Dockerfile 2026-06-10 09:40:01 +02:00
yasien_gmail 0e05ce0b89 WIP:update flatpak configs 2026-06-10 09:37:57 +02:00
yasien_gmail 166328df89 updated .git to ignore flatpak build files 2026-06-10 09:35:38 +02:00
yasien_gmail 87b0ebfa27 lock mih-ai to ollama 0.30.6 2026-06-08 08:30:58 +02:00
yasien_gmail c32972ea6d add screenshots for app stores 2026-06-05 10:50:20 +02:00
yasien_gmail 76456fb530 include network to mih-legal 2026-06-05 10:05:05 +02:00
yasien_gmail 1241540bbe create seperate privacy policy server 2026-06-05 09:56:02 +02:00
yasien_gmail 21ae7d92a8 update version numer to 1.3.0 and add remaining files 2026-06-04 10:56:43 +02:00
yasien_gmail ef1f8b18cd fix deep link to privacy policy and update docker file with lastest container for ollama, portainer and api-hub 2026-06-04 10:47:27 +02:00
yasien_gmail 4b1a70f709 update mzansi ai to qwen3.5:9b 2026-06-03 14:26:47 +02:00
yasien_gmail ea3d796013 enable gpu for mzansi ai 2026-06-02 12:35:20 +02:00
yasien_gmail 5f7daadf85 fix deep link for unsigned in user 2026-06-02 10:48:11 +02:00
yasien_gmail 482dde9a72 service worker v4 2026-06-02 10:36:42 +02:00
yasien_gmail b518b9e536 service worker stuff 2026-06-02 10:06:17 +02:00
yasien_gmail a85def8156 fix service worker logic for new mih updates 2026-06-02 09:40:14 +02:00
yasien_gmail 86556e7543 fix docker file flutter 2026-06-01 15:43:32 +02:00
yasien_gmail 95511fdc99 fix font awesome icons use 2026-06-01 15:37:27 +02:00
yasien_gmail 0dc8ac49be fix font awesome icons 2026-06-01 14:44:46 +02:00
yasien_gmail 93942ff060 switch from query paramater to path paramater for profile views 2026-06-01 14:14:56 +02:00
yasien_gmail efe225b9f8 Add business profile links 2026-06-01 12:58:17 +02:00
yasien_gmail 49c7ecce1f add provider indexing for business & user views 2026-05-29 08:26:27 +02:00
yasien_gmail 39bf88356f fix I dont know logo and profile link build issues 2026-05-27 16:32:28 +02:00
yasien_gmail 052f937027 MIH Profile Links 2026-05-27 15:36:56 +02:00
yasien_gmail 33d07b1617 fix alert colors 2026-05-26 14:59:56 +02:00
yasien_gmail 806c25d7b0 fix profile link api 2026-05-26 14:59:41 +02:00
yasien_gmail aee6497ccb Fix go router to work with web and ios nav 2026-05-26 13:19:34 +02:00
yasien_gmail e85bf2d577 fix clicks cardf colors 2026-05-22 10:40:23 +02:00
yasien_gmail 2a5056e7ff profile links display enhancement 2026-04-29 14:44:00 +02:00
yasien_gmail e0a381d00e fix ios icon & fix ios navigation bug pt2 2026-04-28 15:33:21 +02:00
yasien_gmail 2be2f69f30 fix ios icon & fix ios navigation bug 2026-04-28 15:17:16 +02:00
yasien_gmail 17f7f3287d add localhost port 83 for api call 2026-04-24 13:08:04 +02:00
yasien_gmail c2353fef20 update mih ui with stable flutter and optimised docker file caching 2026-04-24 13:07:20 +02:00
yasien_gmail 3e3170b103 fix supertokens to work with lastest version of flutter 2026-04-24 13:02:12 +02:00
yasien_gmail d71f337d37 Mzansi Wallet new list display pt2 2026-04-22 11:52:54 +02:00
yasien_gmail 62c5634cf6 Mzansi Wallet new list display pt1 2026-04-22 10:45:49 +02:00
yasien_gmail 26d3638d80 remove image url print 2026-04-16 10:00:53 +02:00
yasien_gmail 6c591172df update android skd to 36 2026-04-16 10:00:21 +02:00
yasien_gmail 0a5c4a3d20 Get all user and business data on any ackage load 2026-04-15 12:47:01 +02:00
yasien_gmail c0077e532c Mzansi Wallet 2.0 update 2026-04-15 12:39:48 +02:00
yasien_gmail 379633d7f5 remove usage of user type 2026-04-15 08:40:55 +02:00
yasien_gmail c855503edd Mzansi AI model update to qwen3.5 2026-04-14 10:43:04 +02:00
yasien_gmail 0f6c6e51ab New Business Setup Flow 2026-04-08 15:47:33 +02:00
yasien_gmail e5ce03e396 migrate to mih_package_tooklit 2026-03-20 12:04:18 +02:00
yasien_gmail c67529dbac Migration to mih_package_toolkit 2026-03-18 16:42:12 +02:00
yasien_gmail 84cb6b2e83 linux icon image fix 2026-02-26 15:15:49 +02:00
yasien_gmail 6e07a55885 Flatpak linux build 2026-02-26 13:54:38 +02:00
yasien_gmail 8fb31695a8 linux flatpak config pt11 2026-02-26 12:34:01 +02:00
yasien_gmail 4fafa35888 linux flatpak config pt10 2026-02-26 12:17:14 +02:00
yasien_gmail 0cf9634c5d linux flatpak config pt9 2026-02-26 12:15:59 +02:00
yasien_gmail 787a8057b2 linux flatpak config pt8 2026-02-26 12:15:04 +02:00
yasien_gmail 5f911d51f9 linux flatpak config pt7 2026-02-26 09:31:20 +02:00
yasien_gmail 8da29792b4 linux flatpak config pt6 2026-02-26 09:28:18 +02:00
yasien_gmail 5e003a4d71 linux flatpak config pt5 2026-02-25 15:50:02 +02:00
yasien_gmail fcf1bbbb15 linux flatpak config pt4 2026-02-25 15:44:10 +02:00
yasien_gmail ff7f363983 linux flatpak config pt3 2026-02-25 15:35:45 +02:00
yasien_gmail 843997e58c linux flatpak config pt2 2026-02-25 15:09:23 +02:00
yasien_gmail 3778ebb261 linux flatpak config 2026-02-25 14:51:15 +02:00
yasien_gmail b1487839a7 linux name change 2026-02-25 13:58:42 +02:00
yasien_gmail 221030eff3 fix platform specific code not working on web pt2 2026-02-25 12:16:53 +02:00
yasien_gmail 5135629b33 fix platform specific code not working on web 2026-02-25 12:05:07 +02:00
yasien_gmail 281ea863e8 api cros fix 2026-02-25 11:54:49 +02:00
yasien_gmail 1c0dd6d328 update build number to 130 2026-02-25 10:25:28 +02:00
yasien_gmail 07d4ba4afa Support linux version of MIHpt2 2026-02-24 16:30:06 +02:00
yasien_gmail 6ad6b6ccbd Support linux version of MIH 2026-02-24 15:41:55 +02:00
yasien_gmail ce2575035f make profile picture the full height of the window 2026-02-24 12:43:34 +02:00
yasien_gmail baea2c9fdb fix file display on Dev Web & iOS 2026-02-24 12:37:31 +02:00
yasien_gmail 27639cb964 add scroll bar to dropdown fields 2026-02-24 11:05:24 +02:00
yasien_gmail 1143d11054 fix supertoken versioning error 2026-02-24 11:02:30 +02:00
yasien_gmail 213f3d418d update build number to 129 2026-02-18 15:41:37 +02:00
yasien_gmail e33a62b909 update look & feel of attribution list 2026-02-18 14:03:04 +02:00
yasien_gmail ebab9bae52 update build no to 128 2026-02-18 11:52:30 +02:00
yasien_gmail 82c25c5406 make profile picture expandable 2026-02-18 11:51:44 +02:00
yasien_gmail 3f0fc08a5c update business user edit workflow 2026-02-18 10:56:34 +02:00
yasien_gmail f137ea41ac fix loading indicator alignment for business QR code 2026-02-18 10:41:49 +02:00
yasien_gmail a7effa3576 Update profile link and business card Icons alignment 2026-02-18 10:26:41 +02:00
yasien_gmail 74341a9cc6 Enhance Mzansi Profile Look & Feel and workflow 2026-02-18 10:18:03 +02:00
yasien_gmail c5267c0540 change follow us link alignments 2026-02-16 16:27:39 +02:00
yasien_gmail d4ba3aaa03 fix short cut icon issue 2026-02-16 15:34:27 +02:00
yasien_gmail 103ccdc022 Add scroll bar to mih 2026-02-16 15:23:16 +02:00
yasien_gmail f8a722eb50 change design on profile links and add git option 2026-02-16 14:43:34 +02:00
yasien_gmail fdb28080e3 fix web cros issues with fixed dev port pt2 2026-02-16 14:42:33 +02:00
yasien_gmail 8a384921c5 fix web cros issues with fixed dev port 2026-02-16 14:41:51 +02:00
yasien_gmail 4b47bf5288 enhacne install/ update mih button to cater for huawei 2026-02-16 14:08:00 +02:00
yasien_gmail 141611b84d fix mzansi ai modal 2026-02-16 13:43:15 +02:00
yasien_gmail a29d0afeb8 update version number of mih 2026-02-16 13:25:46 +02:00
yasien_gmail eb93714022 add ds_store file to ingore file 2026-02-16 13:09:33 +02:00
yasien_gmail 071612a521 BUG: API Wildcard CROS issue 2026-02-16 13:09:33 +02:00
yasien_gmail 726a60ad25 BUG: Fix exposed ports on mih server 2026-02-16 13:09:33 +02:00
yasien_gmail b897986c1f BUG: Fix hardcoded supertoken api key 2026-02-16 13:09:33 +02:00
yasien_gmail 7d4d7fc713 BUG: Fix Security Flutter Web server 2026-02-16 13:09:33 +02:00
yasien_gmail 91075255f4 add scrolling to about package 2026-02-10 14:07:05 +02:00
yasien_gmail 5c2f19dcc4 enable linux platform & rename widnow 2026-02-10 11:58:35 +02:00
yaso_meth 58aebbeabe Merge pull request 'ruleset test' (#1) from tester-branch into main
Reviewed-on: #1
2026-02-04 11:18:21 +00:00
yasien_gmail 670480910b ruleset test 2026-02-04 13:16:15 +02:00
yasien_gmail 45c1b247d3 testing 2026-02-04 13:13:21 +02:00
yaso-meth ecd24670e9 Simplify MIH Server start/stop instructions 2026-02-03 16:43:46 +02:00
yaso-meth ab99518ff2 Fix directory name in README for navigation 2026-02-03 16:41:39 +02:00
yaso-meth 6ac6aff59e Update directory name in README for navigation 2026-02-03 16:40:59 +02:00
yasien_gmail 33ca7b613e remove old nginx 2026-02-03 16:31:10 +02:00
yaso-meth e83b0b50ae Merge pull request #265 from yaso-meth/yaso-meth-patch-1
Update README with Gitea and WordPress SQL config
2026-02-03 16:19:38 +02:00
yaso-meth bcd19de256 Update README with Gitea and WordPress SQL config
Add database configuration variables for Gitea and WordPress.
2026-02-03 16:18:48 +02:00
yaso-meth ae36879ec5 Revise title and add introduction in README
Updated project title and added introductory text.
2026-02-03 16:15:41 +02:00
yaso-meth eda609cf7e Update license information for the MIH Project 2026-02-03 15:46:42 +02:00
yaso-meth e1744b09e3 Merge pull request #264 from yaso-meth/V.1.2.5
V.1.2.5
2026-02-03 14:01:14 +02:00
yasien_gmail a3845df8bd Merge branch 'main' into V.1.2.5 2026-02-03 13:57:15 +02:00
yasien_gmail d7eebe152b test file 2026-02-03 13:52:47 +02:00
yasien_gmail d8a807b43e update build to 1.2.5+1.2.6 2026-02-03 13:29:20 +02:00
yasien_gmail 5172017a54 Update MZansi AI prod Model 2026-02-03 13:26:37 +02:00
yasien_gmail 535924691e NEW: Add Mzansi Directory Shortcut 2026-02-03 13:26:17 +02:00
yasien_gmail ca8e0f56ac Remove women4change info and revert colors 2026-02-03 10:17:56 +02:00
yasien_gmail 63071325cb favourite business load fix 2026-02-03 10:15:07 +02:00
yasien_gmail 800cd635c2 Remove profile link from personal profile for now 2026-02-03 10:05:00 +02:00
yasien_gmail f46ce36861 linux app config 2026-02-03 09:57:38 +02:00
yasien_gmail b69fd92b19 Update docker compose file for new architechure 2026-01-29 13:02:24 +02:00
yasien_gmail 9e19dc0fa4 Update API files for new architechure 2026-01-29 13:02:05 +02:00
yasien_gmail 38c40e2dfe Update launch file for new architechure 2026-01-29 13:01:41 +02:00
yasien_gmail 02bbc32d23 Update ignore file for new architechure 2026-01-29 13:01:28 +02:00
yasien_gmail 5b052a1fa9 rename container folders 2026-01-29 11:11:45 +02:00
yasien_gmail d5349d981c update architecture 2026-01-29 11:11:25 +02:00
yasien_gmail d6c28b631a ignore new mih_minio 2026-01-29 11:07:50 +02:00
yasien_gmail 74c5276c94 remove old database ignore 2026-01-29 11:06:01 +02:00
yasien_gmail d5e349d218 ignore new db folder 2026-01-29 11:04:53 +02:00
yasien_gmail 44527c8f10 Add self hosted GIT plaform 2026-01-29 10:08:44 +02:00
yasien_gmail fbb9d8573c pat man loading 2026-01-06 16:49:32 +02:00
yasien_gmail 010fc0bc74 BUG: file viewer opening bug 2025-12-17 20:12:51 +02:00
yasien_gmail 45ac3f03e6 BUG: Supertokens api fix 2025-12-14 23:36:11 +02:00
yasien_gmail eea3248525 NEW: MIH Profile Links pt1 2025-12-11 12:57:12 +02:00
yasien_gmail b945a34ad4 BUG: Profile set up bug 2025-12-10 19:42:55 +02:00
yasien_gmail e3ac1be71c BUG: Business Profile Vew pt2 2025-12-10 12:52:31 +02:00
yasien_gmail 3a955e67ef BUG: Business Profile Vew 2025-12-10 12:52:13 +02:00
yasien_gmail 777043e2ca NEW: Config firebase Cloud Messaging for Notifications 2025-12-10 10:18:57 +02:00
yasien_gmail 6a8b9c6902 QOL: Package Tile Update 2025-12-09 19:14:50 +02:00
yasien_gmail 07360dd308 QOL: only get user data if user is null 2025-12-09 11:46:16 +02:00
yasien_gmail 7c59e2a5c8 NEW: New python package added to requirements 2025-12-09 10:54:33 +02:00
yasien_gmail 0b57e10532 QOL: Button alignment 2025-12-08 19:46:21 +02:00
yasien_gmail 5681c6d73b BUG: Patient Manager Data fix pt2 2025-12-04 09:19:30 +02:00
yasien_gmail f44ff6443c BUG: Patient Manager Data fix 2025-12-03 22:01:49 +02:00
yasien_gmail fe5b61f6bc QOL: Switch from Network Image to CachedNetworkImage 2025-12-03 21:42:42 +02:00
yasien_gmail e7729a8ce8 QOL: About MIH Share button update 2025-12-03 21:35:41 +02:00
yasien_gmail 227a2f7ae7 update buiold to 125 2025-12-03 21:09:22 +02:00
yasien_gmail f26c1eb01a QOL: Mzansi Ai LLM detection 2025-12-03 12:31:21 +02:00
yasien_gmail ca0f13a6df build number update to 124 2025-12-03 11:40:29 +02:00
yasien_gmail 021a25f50c QOL: Data display load minesweeper pt1 2025-12-03 11:26:47 +02:00
yasien_gmail 0a9f0c000e QOL: Data display load Mzansi Direct pt4 2025-12-03 11:16:24 +02:00
yasien_gmail 3ff670886c QOL: Data display load Mzansi Direct pt3 2025-12-03 11:08:22 +02:00
yasien_gmail a6d5e4ad35 QOL: Data display load Mzansi Direct pt2 2025-12-03 10:51:18 +02:00
yasien_gmail 456dff6402 QOL: Data display load Mzansi Direct pt1 2025-12-03 10:32:03 +02:00
yasien_gmail d3fdc83373 BUG: incorrect code display style 2025-12-03 09:21:15 +02:00
yasien_gmail 8704d4dd64 BUG: Patient Infor Scrolling 2025-12-02 19:20:24 +02:00
yasien_gmail 37920466ac Update build to 123 2025-12-01 10:51:01 +02:00
yasien_gmail 56d54a3711 QOL: Calanedar message alingment 2025-12-01 10:49:14 +02:00
yasien_gmail aef501cd25 QOL: Cache leaderboard images 2025-12-01 10:40:29 +02:00
yasien_gmail 4f5761271c QOL: Mzansi AI Suggestion update 2025-12-01 10:37:57 +02:00
yasien_gmail 252e120b99 BUG: Business Team list view 2025-12-01 10:36:25 +02:00
yasien_gmail b5c26c3e43 BUG: Mzansi Directory person search on person press 2025-12-01 10:32:26 +02:00
yasien_gmail 74be2fc559 BUG: File list icons 2025-12-01 10:29:02 +02:00
yasien_gmail b519b99a91 QOL: Mzansi Home Scroll 2025-12-01 10:26:17 +02:00
yasien_gmail 72261af7b9 build number update to 122 2025-11-28 13:49:02 +02:00
yasien_gmail ad96725478 QOL: Mzansi Profile Package performance improvements pt2 2025-11-28 13:48:25 +02:00
yasien_gmail ef4c3102a9 QOL: MIH Authentication Package performance improvements 2025-11-28 13:43:51 +02:00
yasien_gmail 9a75bcc810 QOL: MIH Access Controls Package performance improvements pt2 2025-11-28 13:38:14 +02:00
yasien_gmail 586e67b369 QOL: MIH Mine Sweeper Package performance improvements pt2 2025-11-28 13:30:12 +02:00
yasien_gmail e8cae1a894 QOL: MIH Calendar Package performance improvements pt2 2025-11-28 13:26:32 +02:00
yasien_gmail cd8115c597 QOL: Patient Manager Package performance improvements pt2 2025-11-28 13:22:54 +02:00
yasien_gmail 730c5d2bdf QOL: MIH Package performance improvements pt2 2025-11-28 13:13:27 +02:00
yasien_gmail cea8ccab5a QOL: Mzansi Wallet Package performance improvements pt2 2025-11-28 13:02:34 +02:00
yasien_gmail 4f168c5b0e QOL: MIH About Package performance improvements 2025-11-28 12:57:19 +02:00
yasien_gmail 99f8b1a3f9 QOL: MIH Access Controls Package performance improvements 2025-11-28 12:54:31 +02:00
yasien_gmail 30c06261c8 QOL: MIH Mine Sweeper Package performance improvements 2025-11-28 12:52:33 +02:00
yasien_gmail c843c0a55d QOL: MIH Calculator Package performance improvements 2025-11-28 12:47:44 +02:00
yasien_gmail 94ac83db9e QOL: Mzansi AI Package performance improvements 2025-11-28 12:44:51 +02:00
yasien_gmail a3b8da5357 QOL: MIH Calendar Package performance improvements 2025-11-28 12:43:07 +02:00
yasien_gmail 47bc23c029 QOL: Patient Manager Package performance improvements 2025-11-28 12:41:30 +02:00
yasien_gmail c16d8b6e91 QOL: Mzansi Wallet Package performance improvements 2025-11-28 12:31:39 +02:00
yasien_gmail a6fe4499d0 QOL: Mzansi Profile Package performance improvements 2025-11-28 12:29:09 +02:00
yasien_gmail 004c2397c5 QOL: MIH Home Package performance improvements 2025-11-28 12:15:12 +02:00
yasien_gmail d64193d1f8 QOL: MIH Package performance improvements 2025-11-28 12:14:20 +02:00
yasien_gmail e330875c6f build update to 121 2025-11-28 11:43:38 +02:00
yasien_gmail 3d2addf1d6 BUG: fav bus view fix performace pt 2 2025-11-28 11:33:26 +02:00
yasien_gmail 3b6e1d22ec build update to 120 2025-11-28 10:30:33 +02:00
yasien_gmail ff913c0c54 BUG: fav bus view fix 2025-11-28 10:26:36 +02:00
yasien_gmail c45c933277 Update version and build 2025-11-27 13:16:30 +02:00
yasien_gmail 89d6999abf BUG: patient profile picture in patient manager 2025-11-27 13:14:40 +02:00
yasien_gmail 969ecf8fdc BUG: User search piture load 2025-11-27 12:57:15 +02:00
yasien_gmail ee7d3881e6 BUG: Image loading of businesses 2025-11-27 12:40:34 +02:00
yasien_gmail cc3f18f7e2 QOL: Mzansi AI Enhancement pt1 2025-11-27 09:48:42 +02:00
849 changed files with 16495 additions and 16271 deletions
Vendored
BIN
View File
Binary file not shown.
+8 -10
View File
@@ -1,16 +1,14 @@
# *database/auto.cnf
# *database/binlog.index
# *database/mysql.sock
File_Storage
database/
mih_minio/
mih_db/
mih_git/
mih_nginx/
mih_monitor/
mih_wp/
certbot/
Firebase-emulator/
Mzansi_Mail/
# database/ibdata1
# database/mysql.ibd
# database/undo*
# database/#innodb_redo/#ib_redo*
.venv
google-chrome-stable_current_amd64.deb
.env
Frontend/android/app/.cxx/
Frontend/android/app/.cxx/
.DS_Store
+14 -3
View File
@@ -6,14 +6,25 @@
"configurations": [
{
"name": "Debug",
"cwd": "Frontend",
"cwd": "mih_ui",
"request": "launch",
"type": "dart",
"program": "lib/main_dev.dart"
},
{
"name": "Debug (web)",
"cwd": "mih_ui",
"request": "launch",
"type": "dart",
"program": "lib/main_dev.dart",
"args": [
"--web-port",
"1995"
]
},
{
"name": "Profile",
"cwd": "Frontend",
"cwd": "mih_ui",
"request": "launch",
"type": "dart",
"flutterMode": "profile",
@@ -21,7 +32,7 @@
},
{
"name": "Release",
"cwd": "Frontend",
"cwd": "mih_ui",
"request": "launch",
"type": "dart",
"flutterMode": "release",
-45
View File
@@ -1,45 +0,0 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "8defaa71a77c16e8547abdbfad2053ce3a6e2d5b"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b
base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b
- platform: android
create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b
base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b
- platform: ios
create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b
base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b
- platform: linux
create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b
base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b
- platform: macos
create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b
base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b
- platform: web
create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b
base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b
- platform: windows
create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b
base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
-48
View File
@@ -1,48 +0,0 @@
# Install Operating system and dependencies
#FROM ubuntu:22.04
FROM debian:latest AS build-env
#ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update --fix-missing
RUN apt-get install -y curl git wget unzip gdb libstdc++6 libglu1-mesa fonts-droid-fallback
# RUN apt-get install -y curl git wget unzip libgconf-2-4 gdb libstdc++6 libglu1-mesa fonts-droid-fallback
RUN apt-get install python3 -y
# download Flutter SDK from Flutter Github repo
RUN git clone -b flutter-3.32-candidate.0 https://github.com/flutter/flutter.git /usr/local/flutter
# RUN git clone -b stable https://github.com/flutter/flutter.git /usr/local/flutter
# Set flutter environment path
ENV PATH="/usr/local/flutter/bin:/usr/local/flutter/bin/cache/dart-sdk/bin:${PATH}"
#ENV PATH "$PATH:/home/developer/flutter/bin"
RUN flutter doctor -v
# Enable flutter web
RUN flutter channel flutter-3.32-candidate.0
# RUN flutter channel stable
RUN flutter config --enable-web
# Copy files to container and build
RUN mkdir /app/
COPY . /app/
WORKDIR /app
# RUN flutter upgrade
RUN flutter build web --release -t ./lib/main_prod.dart
# RUN flutter build web --release --web-renderer canvaskit -t ./lib/main_prod.dart
# RUN cd ..
# WORKDIR /app/build/web/
EXPOSE 83
RUN ["chmod", "+x", "/app/server/server.sh"]
ENTRYPOINT [ "/app/server/server.sh"]
# RUN ["python3", "-u", "/app/server/MIH_web_server.py"]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#6641b2</color>
<!-- Women For Change -->
<color name="mih_icon_background">#6641b2</color>
<color name="mih_icon_foreground">#E0D1FF</color>
<!-- Original -->
<!-- <color name="mih_icon_background">#3A4454</color>
<color name="mih_icon_foreground">#bedcfe</color> -->
</resources>
-46
View File
@@ -1,46 +0,0 @@
# flutter pub run flutter_launcher_icons
flutter_launcher_icons:
# Original
# image_path: "lib/mih_package_components/assets/images/app_icon/mih_logo_app.png"
# Women For Change
image_path: "lib/mih_package_components/assets/images/app_icon/mih_logo_app_w4c.png"
android: "launcher_icon"
min_sdk_android: 21 # android min sdk min:16, default 21
# Original
# adaptive_icon_background: "#3A4454"
# adaptive_icon_foreground: "lib/mih_package_components/assets/images/app_icon/mih_logo_app.png"
# Women For Change
adaptive_icon_background: "#6641b2"
adaptive_icon_foreground: "lib/mih_package_components/assets/images/app_icon/mih_logo_app_w4c.png"
ios: true
# Original
# image_path_ios: "lib/mih_package_components/assets/images/app_icon/mih_logo_app.png"
# Women For Change
image_path_ios: "lib/mih_package_components/assets/images/app_icon/mih_logo_app_w4c.png"
remove_alpha_channel_ios: true
web:
generate: true
# Original
# image_path: "lib/mih_package_components/assets/images/app_icon/mih_logo_web.png"
# background_color: "#3A4454"
# theme_color: "#3A4454"
# Women For Change
image_path: "lib/mih_package_components/assets/images/app_icon/mih_logo_web_w4c.png"
background_color: "#6641b2"
theme_color: "#6641b2"
windows:
generate: true
image_path: "lib/mih_package_components/assets/images/app_icon/mih_logo_web.png"
icon_size: 48 # min:48, max:256, default: 48
macos:
generate: true
image_path: "lib/mih_package_components/assets/images/app_icon/mih_logo_web.png"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

@@ -1 +0,0 @@
{"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

-102
View File
@@ -1,102 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to your microphone to enable voice input for the chat.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>This app uses speech recognition to convert your voice messages into text.</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to your microphone to enable voice input for the chat.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>This app uses speech recognition to convert your voice messages into text.</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>SKAdNetworkItems</key>
<array>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>v9wttpbfk9.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>n38lu8286q.skadnetwork</string>
</dict>
</array>
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-4781880856775334~6935644635</string>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>MIH</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>mzansi_innovation_hub</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>sms</string>
<string>tel</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan QR codes</string>
<key>NSFaceIDUsageDescription</key>
<string>Why is my app authenticating using face id?</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to location when open.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs access to your location at all times to provide [Explain your specific, complete reason here, e.g., real-time tracking, background updates, etc.].</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs to access your photo library to select images.</string>
<key>NSDownloadsFolderUsageDescription</key>
<string>This app needs to access your downloads folder to select files from there.</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
</array>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIStatusBarHidden</key>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
-140
View File
@@ -1,140 +0,0 @@
import 'package:flutter/material.dart';
class MihColors {
bool women4Change = true;
static Color getPrimaryColor(bool darkMode) {
if (darkMode == true) {
// return const Color(0XFF3A4454); // Original
return const Color(0XFF6641b2); // Women4change
} else {
// return const Color(0XFFbedcfe); // Original
return const Color(0xFFE0D1FF); // Women4change
}
}
static Color getSecondaryColor(bool darkMode) {
if (darkMode == true) {
// return const Color(0XFFbedcfe); // Original
return const Color(0xFFE0D1FF); // Women4change
} else {
// return const Color(0XFF3A4454); // Original
return const Color(0XFF6641b2); // Women4change
}
}
static Color getSecondaryInvertedColor(bool darkMode) {
if (darkMode == true) {
// return const Color(0XFF412301); // Original
return const Color(0XFF1f2e00); // Women4change
} else {
// return const Color(0XFFc5bbab); // Original
return const Color(0XFF99be4d); // Women4change
}
}
static Color getHighlightColor(bool darkMode) {
if (darkMode == true) {
// return const Color(0XFF9bc7fa);
return const Color(0xFFC8AFFB); // Women4change
} else {
// return const Color(0XFF354866);
return const Color(0XFF6641b2); // Women4change
}
}
static Color getGreyColor(bool darkMode) {
if (darkMode == true) {
return const Color(0XFFc8c8c8);
} else {
return const Color(0XFF747474);
}
}
static Color getGreenColor(bool darkMode) {
if (darkMode == true) {
return const Color(0xff8ae290);
} else {
return const Color(0xFF41B349);
}
}
static Color getRedColor(bool darkMode) {
if (darkMode == true) {
return const Color(0xffD87E8B);
} else {
return const Color(0xffbb3d4f);
}
}
static Color getPinkColor(bool darkMode) {
if (darkMode == true) {
return const Color(0xffdaa2e9);
} else {
// Add a different shade of pink for light mode
return const Color(0xffdaa2e9);
}
}
static Color getOrangeColor(bool darkMode) {
if (darkMode == true) {
return const Color(0xffd69d7d);
} else {
// Add a different shade of pink for light mode
return const Color(0xFFBD7145);
}
}
static Color getYellowColor(bool darkMode) {
if (darkMode == true) {
return const Color(0xfff4e467);
} else {
// Add a different shade of pink for light mode
return const Color(0xffd4af37);
}
}
static Color getBluishPurpleColor(bool darkMode) {
if (darkMode == true) {
return const Color(0xff6e7dcc);
} else {
// Add a different shade of pink for light mode
return const Color(0xFF5567C0);
}
}
static Color getPurpleColor(bool darkMode) {
if (darkMode == true) {
return const Color(0xffb682e7);
} else {
// Add a different shade of pink for light mode
return const Color(0xFF9857D4);
}
}
static Color getGoldColor(bool darkMode) {
if (darkMode == true) {
return const Color(0xFFD4AF37);
} else {
// Add a different shade of pink for light mode
return const Color(0xffFFD700);
}
}
static Color getSilverColor(bool darkMode) {
if (darkMode == true) {
return const Color(0xffC0C0C0);
} else {
// Add a different shade of pink for light mode
return const Color(0xFFA6A6A6);
}
}
static Color getBronze(bool darkMode) {
if (darkMode == true) {
return const Color(0xffB1560F);
} else {
// Add a different shade of pink for light mode
return const Color(0xFFCD7F32);
}
}
}
-374
View File
@@ -1,374 +0,0 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:mzansi_innovation_hub/mih_packages/mih_file_viewer/components/mih_print_prevew.dart';
import 'package:mzansi_innovation_hub/mih_objects/arguments.dart';
import 'package:mzansi_innovation_hub/mih_package_components/Example/package_test.dart';
import 'package:mzansi_innovation_hub/mih_providers/mzansi_directory_provider.dart';
import 'package:mzansi_innovation_hub/mih_packages/about_mih/about_mih.dart';
import 'package:mzansi_innovation_hub/mih_packages/access_review/mih_access.dart';
import 'package:mzansi_innovation_hub/mih_packages/calculator/mih_calculator.dart';
import 'package:mzansi_innovation_hub/mih_packages/calendar/mzansi_calendar.dart';
import 'package:mzansi_innovation_hub/mih_packages/mih_authentication/mih_auth_forgot_password.dart';
import 'package:mzansi_innovation_hub/mih_packages/mih_authentication/mih_auth_password_reset.dart';
import 'package:mzansi_innovation_hub/mih_packages/mih_authentication/mih_authentication.dart';
import 'package:mzansi_innovation_hub/mih_packages/mih_file_viewer/mih_fle_viewer.dart';
import 'package:mzansi_innovation_hub/mih_packages/mih_home/mih_home.dart';
import 'package:mzansi_innovation_hub/mih_packages/mih_home/mih_route_error.dart';
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/mih_mine_sweeper.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_ai/mzansi_ai.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_directory/mzansi_directory.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/busines_profile.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/mzansi_business_profile_view.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/mzansi_set_up_business_profile.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/mzansi_profile.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/mzansi_profile_view.dart';
import 'package:ken_logger/ken_logger.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_wallet/components/mih_barcode_scanner.dart';
import 'package:mzansi_innovation_hub/mih_packages/mzansi_wallet/mih_wallet.dart';
import 'package:mzansi_innovation_hub/mih_packages/patient_manager/pat_manager/pat_manager.dart';
import 'package:mzansi_innovation_hub/mih_packages/patient_manager/pat_profile/patient_profile.dart';
import 'package:mzansi_innovation_hub/mih_packages/patient_manager/pat_profile/patient_set_up.dart';
import 'package:provider/provider.dart';
import 'package:supertokens_flutter/supertokens.dart';
class MihGoRouterPaths {
// External
static const String resetPassword = '/auth/reset-password';
static const String privacyPolicyExternal = '/privacy-policy';
static const String termsOfServiceExternal = '/terms-of-service';
// Internal
// static const String authCheck = '/';
static const String mihAuthentication = '/mih-authentication';
static const String mihHome = '/';
static const String notifications = '/notifications';
static const String forgotPassword = '/mih-authentication/forgot-password';
static const String aboutMih = '/about';
static const String mzansiProfileManage = '/mzansi-profile';
static const String mzansiProfileView = '/mzansi-profile/view';
static const String businessProfileSetup = '/business-profile/set-up';
static const String businessProfileManage = '/business-profile/manage';
static const String businessProfileView = '/business-profile/view';
static const String patientProfile = '/patient-profile';
static const String patientProfileSetup = '/patient-profile/set-up';
static const String mzansiWallet = '/mzansi-wallet';
static const String mzansiDirectory = '/mzansi-directory';
static const String mihAccess = '/mih-access';
static const String calendar = '/calendar';
static const String appointments = '/appointments';
static const String patientManager = '/patient-manager';
static const String patientManagerPatient = '/patient-manager/patient';
static const String fileViewer = '/file-veiwer';
static const String printPreview = '/file-veiwer/print-preview';
static const String barcodeScanner = '/scanner';
static const String calculator = '/calculator';
static const String mzansiAi = '/mzansi-ai';
static const String mihMineSweeper = '/mih-minesweeper';
static const String packageDevTest = '/package-dev';
}
class MihGoRouter {
final GoRouter mihRouter = GoRouter(
initialLocation: MihGoRouterPaths.mihHome,
redirect: (BuildContext context, GoRouterState state) async {
final bool isUserSignedIn = await SuperTokens.doesSessionExist();
final unauthenticatedPaths = [
MihGoRouterPaths.mihAuthentication,
MihGoRouterPaths.forgotPassword,
MihGoRouterPaths.resetPassword,
MihGoRouterPaths.aboutMih,
MihGoRouterPaths.businessProfileView,
];
KenLogger.success(
"Redirect Check: ${state.fullPath}, isUserSignedIn: $isUserSignedIn");
if (!isUserSignedIn && !unauthenticatedPaths.contains(state.fullPath)) {
return MihGoRouterPaths.mihAuthentication;
}
if (isUserSignedIn &&
unauthenticatedPaths.contains(state.fullPath) &&
state.fullPath != MihGoRouterPaths.aboutMih &&
state.fullPath != MihGoRouterPaths.businessProfileView) {
return MihGoRouterPaths.mihHome;
}
return null; // Stay on current route
},
routes: [
// ========================== MIH Auth ==================================
GoRoute(
name: "mihAuthentication",
path: MihGoRouterPaths.mihAuthentication,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: mihAuthentication");
return MihAuthentication();
},
),
GoRoute(
name: "forgotPassword",
path: MihGoRouterPaths.forgotPassword,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: forgotPassword");
return const MihAuthForgotPassword();
},
),
GoRoute(
name: "resetPassword",
path: MihGoRouterPaths.resetPassword,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: resetPassword");
String? token = state.uri.queryParameters['token'];
KenLogger.success("token: $token");
if (token == null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
context.go(MihGoRouterPaths.mihHome);
});
return const SizedBox.shrink();
}
return MihAuthPasswordReset(token: token);
},
),
// ========================== MIH Home ==================================
GoRoute(
name: "mihHome",
path: MihGoRouterPaths.mihHome,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: mihHome");
return MihHome(
key: UniqueKey(),
);
},
),
// ========================== About MIH ==================================
GoRoute(
name: "aboutMih",
path: MihGoRouterPaths.aboutMih,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: aboutMih");
return AboutMih();
},
),
// ========================== Mzansi Profile Personal ==================================
GoRoute(
name: "mzansiProfileManage",
path: MihGoRouterPaths.mzansiProfileManage,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: mzansiProfileManage");
return MzansiProfile();
},
),
GoRoute(
name: "mzansiProfileView",
path: MihGoRouterPaths.mzansiProfileView,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: mzansiProfileView");
MzansiDirectoryProvider directoryProvider =
context.read<MzansiDirectoryProvider>();
if (directoryProvider.selectedUser == null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
context.go(MihGoRouterPaths.mihHome);
});
return const SizedBox.shrink();
}
return MzansiProfileView();
},
),
// ========================== Mzansi Profile Business ==================================
GoRoute(
name: "businessProfileManage",
path: MihGoRouterPaths.businessProfileManage,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: businessProfileManage");
return BusinesProfile();
},
),
GoRoute(
name: "businessProfileView",
path: MihGoRouterPaths.businessProfileView,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: businessProfileView");
String? businessId = state.uri.queryParameters['business_id'];
KenLogger.success("businessId: $businessId");
MzansiDirectoryProvider directoryProvider =
context.read<MzansiDirectoryProvider>();
if (directoryProvider.selectedBusiness == null &&
businessId == null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
context.go(MihGoRouterPaths.mihHome);
});
return const SizedBox.shrink();
}
return MzansiBusinessProfileView(
businessId: businessId,
);
},
),
GoRoute(
name: "businessProfileSetup",
path: MihGoRouterPaths.businessProfileSetup,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: businessProfileSetup");
return MzansiSetUpBusinessProfile();
},
),
// ========================== MIH Calculator ==================================
GoRoute(
name: "mihCalculator",
path: MihGoRouterPaths.calculator,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: mihCalculator");
return MIHCalculator();
},
),
// ========================== MIH Calculator ==================================
GoRoute(
name: "mihCalendar",
path: MihGoRouterPaths.calendar,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: mihCalendar");
return MzansiCalendar();
},
),
// ========================== Mzansi AI ==================================
GoRoute(
name: "mzansiAi",
path: MihGoRouterPaths.mzansiAi,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: mzansiAi");
return MzansiAi();
},
),
// ========================== Mzansi Wallet ==================================
GoRoute(
name: "mzansiWallet",
path: MihGoRouterPaths.mzansiWallet,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: mzansiWallet");
return MihWallet();
},
),
GoRoute(
name: "barcodeScanner",
path: MihGoRouterPaths.barcodeScanner,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: barcodeScanner");
final TextEditingController? args =
state.extra as TextEditingController?;
if (args == null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
context.go(MihGoRouterPaths.mihHome);
});
return const SizedBox.shrink();
}
return MihBarcodeScanner(cardNumberController: args);
},
),
// ========================== Test Package ==================================
GoRoute(
name: "testPackage",
path: MihGoRouterPaths.packageDevTest,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: testPackage");
return PackageTest();
},
),
// ========================== MIH Access Controls ==================================
GoRoute(
name: "mihAccess",
path: MihGoRouterPaths.mihAccess,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: mihAccess");
return MihAccess();
},
),
// ========================== Patient Profile ==================================
GoRoute(
name: "patientProfile",
path: MihGoRouterPaths.patientProfile,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: patientProfile");
return PatientProfile();
},
),
GoRoute(
name: "patientProfileSetup",
path: MihGoRouterPaths.patientProfileSetup,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: patientProfileSetup");
return PatientSetUp();
},
),
GoRoute(
name: "patientManager",
path: MihGoRouterPaths.patientManager,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: patientManager");
return PatManager();
},
),
GoRoute(
name: "patientManagerPatient",
path: MihGoRouterPaths.patientManagerPatient,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: patientManagerPatient");
return PatientProfile();
},
),
// ========================== Mzansi Directory ==================================
GoRoute(
name: "mzansiDirectory",
path: MihGoRouterPaths.mzansiDirectory,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: mzansiDirectory");
return MzansiDirectory();
},
),
// ========================== End ==================================
GoRoute(
name: "fileViewer",
path: MihGoRouterPaths.fileViewer,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: fileViewer");
return MihFleViewer();
},
),
GoRoute(
name: "printPreview",
path: MihGoRouterPaths.printPreview,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: printPreview");
final PrintPreviewArguments? args =
state.extra as PrintPreviewArguments?;
if (args == null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
context.go(MihGoRouterPaths.mihHome);
});
return const SizedBox.shrink();
}
return MIHPrintPreview(arguments: args);
},
),
// ========================== MIH Minesweeper ==================================
GoRoute(
name: "mihMinesweeper",
path: MihGoRouterPaths.mihMineSweeper,
builder: (BuildContext context, GoRouterState state) {
KenLogger.success("MihGoRouter: mihMineSweeper");
return MihMineSweeper();
},
),
// ========================== End ==================================
// GoRoute(
// name: "notifications",
// path: MihGoRouterPaths.notifications,
// builder: (BuildContext context, GoRouterState state) {
// final NotificationArguments? args = state.extra as NotificationArguments?;
// return MIHNotificationMessage(arguments: args!);
// },
// ),
],
// 3. Error handling with `errorBuilder` and `redirect`
errorBuilder: (BuildContext context, GoRouterState state) {
KenLogger.error('Invalid Route');
return const MihRouteError();
},
);
}
-321
View File
@@ -1,321 +0,0 @@
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import "package:universal_html/html.dart" as html;
class MihTheme {
// late int _mainColor;
// late int _secondColor;
//late int _errColor;
//late int _succColor;
// late int _mesColor;
late String mode;
late String screenType;
late AssetImage loading;
late String loadingAssetText;
late TargetPlatform platform;
bool kIsWeb = const bool.fromEnvironment('dart.library.js_util');
String latestVersion = "1.2.4";
// Options:-
// f3f9d2 = Cream
// f0f0c9 = cream2
// caffd0 = light green
// B0F2B4 = light grean 2 *
// 85bda6 = light green 3
// 70f8ba = green
// F7F3EA = white
// a63446 = red
//747474
MihTheme() {
mode = "Dark";
//_errColor = 0xffD87E8B;
//_succColor = 0xffB0F2B4;
//_mesColor = 0xffc8c8c8d9;
}
ThemeData getData(bool bool) {
return ThemeData(
fontFamily: 'Segoe UI',
scaffoldBackgroundColor: MihColors.getPrimaryColor(mode == "Dark"),
// pageTransitionsTheme: PageTransitionsTheme(
// builders: Map<TargetPlatform, PageTransitionsBuilder>.fromIterable(
// TargetPlatform.values,
// value: (dynamic _) => const FadeUpwardsPageTransitionsBuilder(),
// ),
// ),
colorScheme: ColorScheme(
brightness: getBritness(),
primary: MihColors.getSecondaryColor(mode == "Dark"),
onPrimary: MihColors.getPrimaryColor(mode == "Dark"),
secondary: MihColors.getPrimaryColor(mode == "Dark"),
onSecondary: MihColors.getSecondaryColor(mode == "Dark"),
error: MihColors.getRedColor(mode == "Dark"),
onError: MihColors.getPrimaryColor(mode == "Dark"),
surface: MihColors.getPrimaryColor(mode == "Dark"),
onSurface: MihColors.getSecondaryColor(mode == "Dark"),
),
datePickerTheme: DatePickerThemeData(
backgroundColor: MihColors.getPrimaryColor(mode == "Dark"),
headerBackgroundColor: MihColors.getSecondaryColor(mode == "Dark"),
headerForegroundColor: MihColors.getPrimaryColor(mode == "Dark"),
),
appBarTheme: AppBarTheme(
color: MihColors.getSecondaryColor(mode == "Dark"),
foregroundColor: MihColors.getPrimaryColor(mode == "Dark"),
titleTextStyle: TextStyle(
color: MihColors.getPrimaryColor(mode == "Dark"),
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: MihColors.getSecondaryColor(mode == "Dark"),
foregroundColor: MihColors.getPrimaryColor(mode == "Dark"),
extendedTextStyle:
TextStyle(color: MihColors.getPrimaryColor(mode == "Dark")),
),
drawerTheme: DrawerThemeData(
backgroundColor: MihColors.getPrimaryColor(mode == "Dark"),
),
// Text selection / cursor color
textSelectionTheme: TextSelectionThemeData(
cursorColor: MihColors.getPrimaryColor(mode == "Dark"),
selectionColor:
MihColors.getPrimaryColor(mode == "Dark").withOpacity(0.25),
selectionHandleColor: MihColors.getPrimaryColor(mode == "Dark"),
),
tooltipTheme: TooltipThemeData(
decoration: BoxDecoration(
color: MihColors.getSecondaryColor(mode == "Dark"),
borderRadius: BorderRadius.circular(6),
border: Border.all(
width: 1.0,
color: MihColors.getPrimaryColor(mode == "Dark"),
),
boxShadow: [
BoxShadow(
color:
MihColors.getPrimaryColor(mode == "Dark").withOpacity(0.18),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
),
textStyle: TextStyle(
color: MihColors.getPrimaryColor(mode == "Dark"),
fontSize: 13,
height: 1.2,
),
waitDuration: const Duration(milliseconds: 500),
showDuration: const Duration(seconds: 3),
preferBelow: true,
verticalOffset: 24,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
triggerMode: TooltipTriggerMode.longPress,
),
// // Input decoration (text fields) theme
// inputDecorationTheme: InputDecorationTheme(
// filled: true,
// fillColor: mode == "Dark"
// ? MihColors.getPrimaryColor(true).withOpacity(0.06)
// : MihColors.getPrimaryColor(false).withOpacity(0.03),
// contentPadding:
// const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
// border: OutlineInputBorder(
// borderRadius: BorderRadius.circular(8),
// borderSide:
// BorderSide(color: MihColors.getSecondaryColor(mode == "Dark")),
// ),
// enabledBorder: OutlineInputBorder(
// borderRadius: BorderRadius.circular(8),
// borderSide: BorderSide(
// color:
// MihColors.getSecondaryColor(mode == "Dark").withOpacity(0.6)),
// ),
// focusedBorder: OutlineInputBorder(
// borderRadius: BorderRadius.circular(8),
// borderSide: BorderSide(
// color: MihColors.getSecondaryColor(mode == "Dark"), width: 2),
// ),
// hintStyle: TextStyle(
// color:
// MihColors.getSecondaryColor(mode == "Dark").withOpacity(0.7)),
// labelStyle:
// TextStyle(color: MihColors.getSecondaryColor(mode == "Dark")),
// errorStyle: TextStyle(color: MihColors.getRedColor(mode == "Dark")),
// ),
);
}
String getPlatform() {
// if (isPwa()) {
// if (platform == TargetPlatform.android) {
// return "Android";
// } else if (platform == TargetPlatform.iOS) {
// return "iOS";
// }
// } else
if (kIsWeb) {
return "Web";
} else if (!kIsWeb) {
if (platform == TargetPlatform.android) {
return "Android";
} else if (platform == TargetPlatform.iOS) {
return "iOS";
}
}
return "Other";
}
bool isPwa() {
return html.window.matchMedia('(display-mode: standalone)').matches;
}
void setMode(String m) {
mode;
}
String getLatestVersion() {
return latestVersion;
}
ThemeData getThemeData() {
return getData(mode == "Dark");
}
ThemeData darkMode() {
return getData(mode == "Dark");
}
ThemeData lightMode() {
return getData(mode == "Dark");
}
Brightness getBritness() {
if (mode == "Dark") {
return Brightness.dark;
} else {
return Brightness.light;
}
}
// Color messageTextColor() {
// if (mode == "Dark") {
// _mesColor = 0XFFc8c8c8;
// } else {
// _mesColor = 0XFF747474;
// }
// return Color(_mesColor);
// }
// Color errorColor() {
// if (mode == "Dark") {
// return const Color(0xffD87E8B);
// } else {
// return const Color(0xffbb3d4f);
// }
// //return Color(_errColor);
// }
// Color highlightColor() {
// if (mode == "Dark") {
// return const Color(0XFF9bc7fa);
// } else {
// return const Color(0XFF354866);
// }
// }
// Color successColor() {
// if (mode == "Dark") {
// return const Color(0xffB0F2B4);
// } else {
// return const Color(0xff56a95b);
// }
// }
// AssetImage loadingImage() {
// if (mode == "Dark") {
// loading = const AssetImage(
// 'lib/mih_package_components/assets/images/loading_light.gif',
// );
// } else {
// loading = const AssetImage(
// 'lib/mih_package_components/assets/images/loading_dark.gif',
// );
// }
// return loading;
// }
// AssetImage altLoadingImage() {
// if (mode == "Dark") {
// loading = const AssetImage(
// 'lib/mih_package_components/assets/images/loading_dark.gif',
// );
// } else {
// loading = const AssetImage(
// 'lib/mih_package_components/assets/images/loading_light.gif',
// );
// }
// return loading;
// }
// String loadingImageLocation() {
// if (mode == "Dark") {
// loadingAssetText =
// 'lib/mih_package_components/assets/images/loading_light.gif';
// } else {
// loadingAssetText =
// 'lib/mih_package_components/assets/images/loading_dark.gif';
// }
// return loadingAssetText;
// }
// String altLoadingImageLocation() {
// if (mode == "Dark") {
// loadingAssetText =
// 'lib/mih_package_components/assets/images/loading_dark.gif';
// } else {
// loadingAssetText =
// 'lib/mih_package_components/assets/images/loading_light.gif';
// }
// return loadingAssetText;
// }
// AssetImage aiLogoImage() {
// if (mode == "Dark") {
// return const AssetImage(
// 'lib/mih_package_components/assets/images/mzansi_ai-dark.png',
// );
// } else {
// return const AssetImage(
// 'lib/mih_package_components/assets/images/mzansi_ai-light.png',
// );
// }
// }
void setScreenType(double width) {
if (width <= 800) {
screenType = "mobile";
} else {
screenType = "desktop";
}
}
// Color MihColors.getPrimaryColor(mode == "Dark") {
// if (mode == "Dark") {
// _mainColor = 0XFF3A4454;
// } else {
// _mainColor = 0XFFbedcfe;
// }
// return Color(_mainColor);
// }
// Color MihColors.getSecondaryColor(mode == "Dark") {
// if (mode == "Dark") {
// _secondColor = 0XFFbedcfe;
// } else {
// _secondColor = 0XFF3A4454;
// }
// return Color(_secondColor);
// }
}
@@ -1,58 +0,0 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_objects/app_user.dart';
import 'package:mzansi_innovation_hub/mih_objects/arguments.dart';
import 'package:mzansi_innovation_hub/mih_objects/business.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tile.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
class TestPackageTile extends StatefulWidget {
final AppUser signedInUser;
final Business? business;
final double packageSize;
const TestPackageTile({
super.key,
required this.signedInUser,
required this.business,
required this.packageSize,
});
@override
State<TestPackageTile> createState() => _TestPackageTileState();
}
class _TestPackageTileState extends State<TestPackageTile> {
@override
Widget build(BuildContext context) {
return MihPackageTile(
onTap: () {
context.goNamed(
'testPackage',
extra: TestArguments(
widget.signedInUser,
widget.business,
),
);
// Navigator.of(context).pushNamed(
// '/package-dev',
// arguments: TestArguments(
// widget.signedInUser,
// widget.business,
// ),
// );
},
appName: "Test",
appIcon: Icon(
Icons.warning_amber_rounded,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
iconSize: widget.packageSize,
primaryColor: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
secondaryColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
);
}
}
@@ -1,68 +0,0 @@
import 'package:flutter/material.dart';
class MihButton extends StatelessWidget {
final void Function()? onPressed;
final void Function()? onLongPressed;
final Color buttonColor;
final double? width;
final double? height;
final double? borderRadius;
final double? elevation; // 0 = flat, higher = more shadow
final Widget child;
const MihButton({
super.key,
required this.onPressed,
this.onLongPressed,
required this.buttonColor,
this.width,
this.height,
this.borderRadius,
this.elevation,
required this.child,
});
Color _darkerColor(Color color, [double amount = .1]) {
final hsl = HSLColor.fromColor(color);
final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0));
return hslDark.toColor();
}
@override
Widget build(BuildContext context) {
final Color effectiveButtonColor = onPressed == null
? buttonColor.withValues(alpha: 0.6) // Example disabled color
: buttonColor;
final Color rippleColor = _darkerColor(effectiveButtonColor, 0.1);
final double radius = borderRadius ?? 25.0;
final double effectiveElevation =
onPressed == null ? 0.0 : (elevation ?? 4.0);
return MouseRegion(
cursor: onPressed == null
? SystemMouseCursors.basic
: SystemMouseCursors.click,
child: Material(
color: effectiveButtonColor,
borderRadius: BorderRadius.circular(radius),
elevation: effectiveElevation,
shadowColor: Colors.black,
child: InkWell(
borderRadius: BorderRadius.circular(radius),
splashColor: rippleColor,
highlightColor: rippleColor.withValues(alpha: 0.2),
hoverColor: rippleColor.withValues(alpha: 0.3),
onTap: onPressed,
onLongPress: onLongPressed,
child: Container(
width: width,
height: height,
padding: (width == null || height == null)
? const EdgeInsets.symmetric(horizontal: 24, vertical: 12)
: null,
alignment: Alignment.center,
child: child,
),
),
),
);
}
}
@@ -1,173 +0,0 @@
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_icons.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
class MihCircleAvatar extends StatefulWidget {
final ImageProvider<Object>? imageFile;
final double width;
final bool editable;
final TextEditingController? fileNameController;
final onChange;
final PlatformFile? userSelectedfile;
final Color frameColor;
final Color? backgroundColor;
const MihCircleAvatar({
super.key,
required this.imageFile,
required this.width,
required this.editable,
required this.fileNameController,
required this.userSelectedfile,
required this.frameColor,
required this.backgroundColor,
required this.onChange,
});
@override
State<MihCircleAvatar> createState() => _MihCircleAvatarState();
}
class _MihCircleAvatarState extends State<MihCircleAvatar> {
late ImageProvider<Object>? imagePreview;
ImageProvider<Object>? getAvatar() {
// Color dark = const Color(0XFF3A4454);
if (widget.imageFile == null) {
return null;
// if (widget.backgroundColor == dark) {
// print("here in light icon");
// return const AssetImage(
// 'lib/mih_package_components/assets/images/i-dont-know-light.png');
// } else {
// print("here in dark icon");
// return const AssetImage(
// 'lib/mih_package_components/assets/images/i-dont-know-dark.png');
// }
} else {
return widget.imageFile;
}
}
@override
void initState() {
super.initState();
setState(() {
imagePreview = getAvatar();
});
}
@override
Widget build(BuildContext context) {
return Container(
// color: Colors.white,
alignment: Alignment.center,
width: widget.width,
height: widget.width,
child: Stack(
alignment: Alignment.center,
children: [
Visibility(
visible: imagePreview != null,
child: Positioned(
right: widget.width * 0.03,
child: CircleAvatar(
radius: widget.width / 2.2,
backgroundColor: widget.backgroundColor,
backgroundImage: imagePreview,
),
),
),
Visibility(
visible: imagePreview != null,
child: Icon(
size: widget.width,
MihIcons.mihRing,
color: widget.frameColor,
),
),
Visibility(
visible: imagePreview == null,
child: Icon(
MihIcons.iDontKnow,
size: widget.width,
color: widget.frameColor,
),
),
Visibility(
visible: widget.editable,
child: Positioned(
bottom: 0,
right: 0,
child: IconButton.filled(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
),
onPressed: () async {
try {
FilePickerResult? result =
await FilePicker.platform.pickFiles(
type: FileType.image,
);
// print("Here 1");
if (MzansiInnovationHub.of(context)!.theme.getPlatform() ==
"Web") {
// print("Here 2");
if (result == null) return;
// print("Here 3");
PlatformFile? selectedFile = result.files.first;
setState(() {
// print("Here 4");
widget.onChange(selectedFile);
// print("Here 5");
imagePreview = MemoryImage(selectedFile.bytes!);
});
setState(() {
widget.fileNameController!.text = selectedFile.name;
});
} else {
if (result != null) {
File file = File(result.files.single.path!);
PlatformFile? androidFile = PlatformFile(
path: file.path,
name: file.path.split('/').last,
size: file.lengthSync(),
bytes: await file.readAsBytes(), // Read file bytes
//extension: fileExtension,
);
setState(() {
widget.onChange(androidFile);
imagePreview = FileImage(file);
});
setState(() {
widget.fileNameController!.text =
file.path.split('/').last;
});
} else {
print("here in else");
// User canceled the picker
}
}
} catch (e) {
print("Here Error: $e");
}
},
icon: Icon(
Icons.edit,
),
),
),
),
],
),
);
}
}
@@ -1,207 +0,0 @@
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
class MihDateField extends StatefulWidget {
final TextEditingController controller;
final String labelText;
final bool required;
final double? width;
final double? height;
final double? borderRadius;
final double? elevation;
final FormFieldValidator<String>? validator;
const MihDateField({
super.key,
required this.controller,
required this.labelText,
required this.required,
this.width,
this.height,
this.borderRadius,
this.elevation,
this.validator,
});
@override
State<MihDateField> createState() => _MihDateFieldState();
}
class _MihDateFieldState extends State<MihDateField> {
FormFieldState<String>? _formFieldState;
Future<void> _selectDate(BuildContext context) async {
DateTime? picked = await showDatePicker(
context: context,
initialDate: widget.controller.text.isNotEmpty
? DateTime.tryParse(widget.controller.text) ?? DateTime.now()
: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2100),
);
if (picked != null) {
widget.controller.text = picked.toString().split(" ")[0];
_formFieldState?.didChange(widget.controller.text);
setState(() {});
}
}
@override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
width: widget.width,
height: widget.height,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.labelText,
style: TextStyle(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
if (!widget.required)
Text(
"(Optional)",
style: TextStyle(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 4),
FormField<String>(
initialValue: widget.controller.text,
validator: widget.validator,
autovalidateMode: AutovalidateMode.onUserInteraction,
builder: (field) {
_formFieldState = field;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Material(
elevation: widget.elevation ?? 4.0,
borderRadius:
BorderRadius.circular(widget.borderRadius ?? 8.0),
child: TextFormField(
controller: widget.controller,
readOnly: true,
onTap: () => _selectDate(context),
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.w500,
),
decoration: InputDecoration(
suffixIcon: Icon(
Icons.calendar_today,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
errorStyle: const TextStyle(height: 0, fontSize: 0),
contentPadding: const EdgeInsets.symmetric(
horizontal: 10.0, vertical: 8.0),
filled: true,
fillColor: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(
widget.borderRadius ?? 8.0),
borderSide: field.hasError
? BorderSide(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
width: 2.0,
)
: BorderSide.none,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
widget.borderRadius ?? 8.0),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
widget.borderRadius ?? 8.0),
borderSide: BorderSide(
color: field.hasError
? MihColors.getRedColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark")
: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
width: 3.0,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
widget.borderRadius ?? 8.0),
borderSide: BorderSide(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
width: 3.0,
),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
widget.borderRadius ?? 8.0),
borderSide: BorderSide(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
width: 3.0,
),
),
),
onChanged: (value) {
field.didChange(value);
},
),
),
if (field.hasError)
Padding(
padding: const EdgeInsets.only(left: 8.0, top: 4.0),
child: Text(
field.errorText ?? '',
style: TextStyle(
fontSize: 12,
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
),
),
),
],
);
},
),
],
),
),
);
}
}
@@ -1,248 +0,0 @@
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
class MihDropdownField extends StatefulWidget {
final TextEditingController controller;
final String hintText;
final bool requiredText;
final List<String> dropdownOptions;
final bool editable;
final bool enableSearch;
final FormFieldValidator<String>? validator;
final Function(String?)? onSelected;
const MihDropdownField({
super.key,
required this.controller,
required this.hintText,
required this.dropdownOptions,
required this.requiredText,
required this.editable,
required this.enableSearch,
this.validator,
this.onSelected,
});
@override
State<MihDropdownField> createState() => _MihDropdownFieldState();
}
class _MihDropdownFieldState extends State<MihDropdownField> {
late List<DropdownMenuEntry<String>> menu;
List<DropdownMenuEntry<String>> buildMenuOptions(List<String> options) {
List<DropdownMenuEntry<String>> menuList = [];
for (final i in options) {
menuList.add(DropdownMenuEntry(
value: i,
label: i,
style: ButtonStyle(
foregroundColor: WidgetStatePropertyAll(MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark")),
),
));
}
return menuList;
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
menu = buildMenuOptions(widget.dropdownOptions);
}
@override
void initState() {
super.initState();
menu = widget.dropdownOptions
.map((e) => DropdownMenuEntry(value: e, label: e))
.toList();
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.hintText,
style: TextStyle(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
if (!widget.requiredText)
Text(
"(Optional)",
style: TextStyle(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 4),
FormField<String>(
validator: widget.validator,
autovalidateMode: AutovalidateMode.onUserInteraction,
initialValue: widget.controller.text,
builder: (field) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Theme(
data: Theme.of(context).copyWith(
textSelectionTheme: TextSelectionThemeData(
cursorColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
selectionColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark")
.withValues(alpha: 0.3),
selectionHandleColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
),
child: DropdownMenu(
controller: widget.controller,
dropdownMenuEntries: menu,
enableSearch: widget.enableSearch,
enableFilter: widget.enableSearch,
enabled: widget.editable,
textInputAction: widget.enableSearch
? TextInputAction.search
: TextInputAction.none,
requestFocusOnTap: widget.enableSearch,
menuHeight: 400,
expandedInsets: EdgeInsets.zero,
textStyle: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.w500,
),
trailingIcon: Icon(
Icons.arrow_drop_down,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
selectedTrailingIcon: Icon(
Icons.arrow_drop_up,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
leadingIcon: IconButton(
onPressed: () {
widget.controller.clear();
field.didChange('');
},
icon: Icon(
Icons.delete_outline_rounded,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
),
onSelected: (String? selectedValue) {
field.didChange(selectedValue);
widget.onSelected?.call(selectedValue);
},
menuStyle: MenuStyle(
backgroundColor: WidgetStatePropertyAll(
MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark")),
side: WidgetStatePropertyAll(
BorderSide(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
width: 1.0),
),
shape: WidgetStatePropertyAll(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
10), // Increase for more roundness
),
),
),
inputDecorationTheme: InputDecorationTheme(
errorStyle: const TextStyle(height: 0, fontSize: 0),
contentPadding: const EdgeInsets.symmetric(
horizontal: 10.0, vertical: 8.0),
filled: true,
fillColor: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color: field.hasError
? MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark")
: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
width: 3.0,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
width: 3.0,
),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
width: 3.0,
),
),
),
),
),
if (field.hasError)
Padding(
padding: const EdgeInsets.only(left: 8.0, top: 4.0),
child: Text(
field.errorText ?? '',
style: TextStyle(
fontSize: 12,
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
),
),
),
],
);
},
),
],
);
}
}
@@ -1,47 +0,0 @@
import 'package:flutter/material.dart';
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';
class MihFloatingMenu extends StatefulWidget {
final IconData? icon;
final double? iconSize;
final AnimatedIconData? animatedIcon;
final SpeedDialDirection? direction;
final List<SpeedDialChild> children;
const MihFloatingMenu({
super.key,
this.icon,
this.iconSize,
this.animatedIcon,
this.direction,
required this.children,
});
@override
State<MihFloatingMenu> createState() => _MihFloatingMenuState();
}
class _MihFloatingMenuState extends State<MihFloatingMenu> {
@override
Widget build(BuildContext context) {
return SpeedDial(
icon: widget.icon,
buttonSize: Size(widget.iconSize ?? 56.0, widget.iconSize ?? 56.0),
animatedIcon: widget.animatedIcon,
direction: widget.direction ?? SpeedDialDirection.up,
activeIcon: Icons.close,
backgroundColor: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
activeBackgroundColor: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
foregroundColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
overlayColor: Colors.black,
overlayOpacity: 0.5,
children: widget.children,
onOpen: () => debugPrint('OPENING DIAL'),
onClose: () => debugPrint('DIAL CLOSED'),
);
}
}
@@ -1,27 +0,0 @@
import 'package:flutter/material.dart';
class MihForm extends StatefulWidget {
final GlobalKey<FormState> formKey;
final List<Widget> formFields;
const MihForm({
super.key,
required this.formKey,
required this.formFields,
});
@override
State<MihForm> createState() => _MihFormState();
}
class _MihFormState extends State<MihForm> {
@override
Widget build(BuildContext context) {
return Form(
key: widget.formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: widget.formFields,
),
);
}
}
@@ -1,67 +0,0 @@
import 'package:flutter/widgets.dart'; // You need this import for IconData
class MihIcons {
MihIcons._(); // This makes the class non-instantiable (good practice for utility classes)
// This MUST match the 'family' name you specify in pubspec.yaml
static const _mihFontFam = 'MihIcons';
// Set to your package name ONLY if this font is part of a separate package you created
static const String? _mihFontPkg = null;
// IconData constants based on your style.css file
// Note: We convert the hex code from CSS (\eXXX) to an integer (0xeXXX)
static const IconData mineSweeper =
IconData(0xe900, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData mzansiDirectory =
IconData(0xe901, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData personalProfile =
IconData(0xe902, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData aboutMih =
IconData(0xe903, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData accessControl =
IconData(0xe904, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData businessProfile =
IconData(0xe905, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData businessSetup =
IconData(0xe906, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData calculator =
IconData(0xe907, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData calendar =
IconData(0xe908, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData iDontKnow =
IconData(0xe909, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData mihLogo =
IconData(0xe90a, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData mihRing =
IconData(0xe90b, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData mzansiAi =
IconData(0xe90c, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData mzansiWallet =
IconData(0xe90d, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData notifications =
IconData(0xe90e, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData patientManager =
IconData(0xe90f, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData patientProfile =
IconData(0xe910, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
static const IconData profileSetup =
IconData(0xe911, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
}
@@ -1,101 +0,0 @@
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_icons.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import '../main.dart';
class Mihloadingcircle extends StatefulWidget {
final String? message;
const Mihloadingcircle({
super.key,
this.message,
});
@override
State<Mihloadingcircle> createState() => _MihloadingcircleState();
}
class _MihloadingcircleState extends State<Mihloadingcircle>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
late double width;
late double height;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(
milliseconds: 500), // Duration for one pulse (grow and shrink)
vsync: this,
);
_animation = Tween<double>(
begin: 200,
end: 200 * 0.5, // Pulse to 50% of the initial size
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut, // Smooth start and end of the pulse
));
_controller.repeat(reverse: true);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Dialog(
child: IntrinsicWidth(
child: IntrinsicHeight(
child: Container(
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
borderRadius: BorderRadius.circular(25.0),
border: Border.all(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
width: 5.0),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: 200,
height: 200,
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Icon(
MihIcons.mihLogo,
size: _animation
.value, // The size changes based on the animation value
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
);
},
),
),
widget.message != null
? Text(
widget.message!,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
)
: SizedBox(),
],
)),
),
),
);
}
}
@@ -1,238 +0,0 @@
import 'package:flutter/material.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_validation_services.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_text_form_field.dart';
class MihNumericStepper extends StatefulWidget {
final TextEditingController controller;
final Color fillColor;
final Color inputColor;
final String hintText;
final bool requiredText;
final double? width;
final int? minValue;
final int? maxValue;
final bool validationOn;
const MihNumericStepper({
super.key,
required this.controller,
required this.fillColor,
required this.inputColor,
required this.hintText,
required this.requiredText,
this.width,
this.minValue,
this.maxValue,
required this.validationOn,
});
@override
State<MihNumericStepper> createState() => _MihNumericStepperState();
}
class _MihNumericStepperState extends State<MihNumericStepper> {
late int _currentValue;
late bool error;
@override
void dispose() {
widget.controller.removeListener(_syncCurrentValue);
super.dispose();
}
@override
void initState() {
super.initState();
_currentValue =
int.tryParse(widget.controller.text) ?? widget.minValue ?? 0;
widget.controller.text = _currentValue.toString();
int.tryParse(widget.controller.text) ?? widget.minValue ?? 0;
widget.controller.addListener(_syncCurrentValue);
// print("Current Value: $_currentValue");
}
void _syncCurrentValue() {
final newValue =
int.tryParse(widget.controller.text) ?? widget.minValue ?? 0;
if (newValue != _currentValue) {
setState(() {
_currentValue = newValue;
});
}
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: [
Text(
widget.hintText,
style: TextStyle(
fontWeight: FontWeight.bold,
color: widget.fillColor,
fontSize: 18,
),
),
],
),
const SizedBox(height: 4),
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Column(
children: [
Container(
// color: Colors.white,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
25), // Optional: rounds the corners
boxShadow: const [
BoxShadow(
color: Color.fromARGB(60, 0, 0,
0), // 0.2 opacity = 51 in alpha (255 * 0.2)
spreadRadius: -2,
blurRadius: 10,
offset: Offset(0, 5),
),
],
),
child: Padding(
padding: const EdgeInsets.only(
top: 2.0,
left: 5.0,
),
child: SizedBox(
width: 40,
child: IconButton.filled(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark")),
),
color: widget.inputColor,
iconSize: 20,
onPressed: () {
print("Current Value: $_currentValue");
if (_currentValue >= (widget.minValue ?? 0)) {
setState(() {
widget.controller.text =
(_currentValue - 1).toString();
_currentValue =
int.tryParse(widget.controller.text)!;
});
}
print("New Current Value: $_currentValue");
},
icon: const Icon(
Icons.remove,
),
),
),
),
),
Visibility(
visible: _currentValue < (widget.minValue ?? 0) ||
(widget.maxValue != null &&
_currentValue > widget.maxValue!),
child: const SizedBox(
height: 21,
),
),
],
),
const SizedBox(width: 15),
Expanded(
child: MihTextFormField(
width: widget.width,
fillColor: widget.fillColor,
inputColor: widget.inputColor,
controller: widget.controller,
hintText: null,
requiredText: widget.requiredText,
readOnly: true,
numberMode: true,
textIputAlignment: TextAlign.center,
validator: (value) {
if (widget.validationOn) {
return MihValidationServices().validateNumber(
value, widget.minValue, widget.maxValue);
}
return null;
},
),
),
const SizedBox(width: 10),
Column(
children: [
Container(
// color: Colors.white,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
25), // Optional: rounds the corners
boxShadow: const [
BoxShadow(
color: Color.fromARGB(60, 0, 0,
0), // 0.2 opacity = 51 in alpha (255 * 0.2)
spreadRadius: -2,
blurRadius: 10,
offset: Offset(0, 5),
),
],
),
child: Padding(
padding: const EdgeInsets.only(
top: 2.0,
left: 5.0,
),
child: SizedBox(
width: 40,
child: IconButton.filled(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark")),
),
color: widget.inputColor,
iconSize: 20,
onPressed: () {
print("Current Value: $_currentValue");
if (widget.maxValue == null ||
_currentValue <= widget.maxValue!) {
setState(() {
widget.controller.text =
(_currentValue + 1).toString();
_currentValue =
int.tryParse(widget.controller.text)!;
});
}
print("New Current Value: $_currentValue");
},
icon: const Icon(
Icons.add,
),
),
),
),
),
Visibility(
visible: _currentValue < (widget.minValue ?? 0) ||
(widget.maxValue != null &&
_currentValue > widget.maxValue!),
child: const SizedBox(
height: 21,
),
),
],
),
],
),
],
);
}
}
@@ -1,214 +0,0 @@
import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
import 'package:ken_logger/ken_logger.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_scack_bar.dart';
import 'package:mzansi_innovation_hub/mih_packages/mih_home/components/mih_app_drawer.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tools.dart';
import 'package:flutter/material.dart';
// ignore: must_be_immutable
class MihPackage extends StatefulWidget {
final Widget appActionButton;
final MihPackageTools appTools;
final List<Widget> appBody;
final List<String> appToolTitles;
final MIHAppDrawer? actionDrawer;
int selectedbodyIndex;
final Function(int) onIndexChange;
MihPackage({
super.key,
required this.appActionButton,
required this.appTools,
required this.appBody,
required this.appToolTitles,
this.actionDrawer,
required this.selectedbodyIndex,
required this.onIndexChange,
});
@override
State<MihPackage> createState() => _MihPackageState();
}
class _MihPackageState extends State<MihPackage>
with SingleTickerProviderStateMixin {
late PageController _pageController;
late AnimationController _animationController;
DateTime? lastPressedAt;
void unfocusAll() {
FocusScope.of(context).unfocus();
}
Future<void> _peakAnimation() async {
int currentPage = widget.selectedbodyIndex;
double peakOffset = _pageController.position.viewportDimension * 0.075;
double currentOffset =
_pageController.page! * _pageController.position.viewportDimension;
int nextPage =
currentPage + 1 < widget.appBody.length ? currentPage + 1 : currentPage;
if (nextPage != currentPage) {
await Future.delayed(const Duration(milliseconds: 100));
await _pageController.animateTo(
currentOffset + peakOffset,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
);
// await Future.delayed(const Duration(milliseconds: 100));
await _pageController.animateTo(
currentPage * _pageController.position.viewportDimension,
duration: const Duration(milliseconds: 300),
curve: Curves.easeIn,
);
}
}
@override
void dispose() {
super.dispose();
_pageController.dispose();
_animationController.dispose();
}
@override
void didUpdateWidget(covariant MihPackage oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.selectedbodyIndex != widget.selectedbodyIndex) {
_pageController.animateToPage(
widget.selectedbodyIndex,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
}
@override
void initState() {
super.initState();
_pageController = PageController(initialPage: widget.selectedbodyIndex);
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 400),
);
// if (!MzansiInnovationHub.of(context)!.theme.kIsWeb) {
// // Trigger the peak animation on start (or call this elsewhere)
// WidgetsBinding.instance.addPostFrameCallback((_) {
// _peakAnimation();
// });
// }
if (!MzansiInnovationHub.of(context)!.theme.kIsWeb) {
// Trigger the peak animation only AFTER the route transition is complete
WidgetsBinding.instance.addPostFrameCallback((_) {
final ModalRoute? currentRoute = ModalRoute.of(context);
if (currentRoute != null) {
currentRoute.animation?.addStatusListener((status) {
if (status == AnimationStatus.completed && mounted) {
// Ensure the widget is still mounted and the animation is completed
_peakAnimation();
}
});
}
});
}
}
@override
Widget build(BuildContext context) {
Size screenSize = MediaQuery.of(context).size;
return GestureDetector(
onTap: unfocusAll,
child: PopScope(
canPop: false,
onPopInvokedWithResult: (bool didPop, Object? result) {
if (GoRouterState.of(context).name == 'mihHome' ||
GoRouterState.of(context).name == 'mihAuthentication') {
if (lastPressedAt == null ||
DateTime.now().difference(lastPressedAt!) >
const Duration(seconds: 2)) {
// First press: show a message and update the timestamp.
lastPressedAt = DateTime.now();
ScaffoldMessenger.of(context).showSnackBar(
MihSnackBar(
child: Text("Press back again to exit"),
),
);
} else {
// Second press within 2 seconds: exit the app.
KenLogger.warning('Exiting app...'); // Your custom logger
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
}
} else {
context.goNamed(
'mihHome',
extra: true,
);
}
},
child: Scaffold(
drawer: widget.actionDrawer,
body: SafeArea(
bottom: false,
minimum: EdgeInsets.only(bottom: 0),
child: Container(
width: screenSize.width,
height: screenSize.height,
//color: Colors.black,
padding: const EdgeInsets.only(top: 5),
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
widget.appActionButton,
const SizedBox(
width: 10,
),
Expanded(
child: Container(
// alignment: Alignment.center,
// alignment: Alignment.centerRight,
alignment: Alignment.centerLeft,
// color: Colors.black,
child: FittedBox(
child: Text(
widget.appToolTitles[widget.selectedbodyIndex],
style: const TextStyle(
fontSize: 23,
fontWeight: FontWeight.w600,
),
),
),
),
),
const SizedBox(width: 5),
widget.appTools,
const SizedBox(width: 5),
],
),
const SizedBox(height: 5),
Expanded(
child: PageView.builder(
controller: _pageController,
itemCount: widget.appBody.length,
itemBuilder: (context, index) {
return widget.appBody[index];
},
onPageChanged: (index) {
setState(() {
widget.selectedbodyIndex = index;
widget.onIndexChange(widget.selectedbodyIndex);
});
},
),
),
],
),
),
),
),
),
);
}
}
@@ -1,38 +0,0 @@
import 'package:flutter/material.dart';
class MihPackageAction extends StatefulWidget {
final void Function()? onTap;
final double iconSize;
final Widget icon;
const MihPackageAction({
super.key,
required this.icon,
required this.iconSize,
required this.onTap,
});
@override
State<MihPackageAction> createState() => _MihPackageActionState();
}
class _MihPackageActionState extends State<MihPackageAction> {
@override
void dispose() {
super.dispose();
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return IconButton(
iconSize: widget.iconSize,
padding: const EdgeInsets.all(0),
onPressed: widget.onTap,
icon: widget.icon,
);
}
}
@@ -1,220 +0,0 @@
import 'package:app_settings/app_settings.dart';
import 'package:flutter/foundation.dart';
import 'package:local_auth/local_auth.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_window.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_yt_video_player.dart';
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_alert_services.dart';
class MihPackageTile extends StatefulWidget {
final String appName;
final String? ytVideoID;
final Widget appIcon;
final void Function() onTap;
final double iconSize;
final Color primaryColor;
final Color secondaryColor;
final bool? authenticateUser;
const MihPackageTile({
super.key,
required this.onTap,
required this.appName,
this.ytVideoID,
required this.appIcon,
required this.iconSize,
required this.primaryColor,
required this.secondaryColor,
this.authenticateUser,
});
@override
State<MihPackageTile> createState() => _MihPackageTileState();
}
class _MihPackageTileState extends State<MihPackageTile> {
final LocalAuthentication _auth = LocalAuthentication();
void displayHint() {
if (widget.ytVideoID != null) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return MihPackageWindow(
fullscreen: false,
windowTitle: widget.appName,
// windowTools: const [],
onWindowTapClose: () {
Navigator.pop(context);
},
windowBody: MIHYTVideoPlayer(
videoYTLink: widget.ytVideoID!,
),
);
},
);
}
}
Future<bool> isUserAuthenticated() async {
final bool canAuthWithBio = await _auth.canCheckBiometrics;
final bool canAuthenticate =
canAuthWithBio || await _auth.isDeviceSupported();
print("Auth Available: $canAuthenticate");
if (canAuthenticate) {
try {
final bool didBioAuth = await _auth.authenticate(
localizedReason: "Authenticate to access ${widget.appName}",
options: const AuthenticationOptions(
biometricOnly: false,
),
);
if (didBioAuth) {
return true;
} else {
authErrorPopUp();
}
// print("Authenticated: $didBioAuth");
} catch (error) {
print("Auth Error: $error");
authErrorPopUp();
}
} else {
print("Auth Error: No Biometrics Available");
authErrorPopUp();
}
return false;
}
void authErrorPopUp() {
MihAlertServices().errorAdvancedAlert(
"Biometric Authentication Required",
"Hi there! To jump into the ${widget.appName} Package, you'll need to authenticate yourself with your devices biometrics, please set up biometric authentication (like fingerprint, face ID, pattern or pin) on your device first.\n\nIf you have already set up biometric authentication, press \"Authenticate now\" to try again or press \"Set Up Authentication\" to go to your device settings.",
[
MihButton(
onPressed: () {
Navigator.of(context).pop();
},
buttonColor: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
width: 300,
child: Text(
"Dismiss",
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
MihButton(
onPressed: () {
AppSettings.openAppSettings(
type: AppSettingsType.security,
);
Navigator.of(context).pop();
},
buttonColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
width: 300,
child: Text(
"Set Up Authentication",
style: TextStyle(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
MihButton(
onPressed: () {
Navigator.of(context).pop();
authenticateUser();
},
buttonColor: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
width: 300,
child: Text(
"Authenticate Now",
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
],
context,
);
}
Future<void> authenticateUser() async {
if (widget.authenticateUser != null &&
widget.authenticateUser! &&
!kIsWeb) {
if (await isUserAuthenticated()) {
widget.onTap();
}
} else {
widget.onTap();
}
}
@override
Widget build(BuildContext context) {
return Container(
// alignment: Alignment.topCenter,
// color: Colors.black,
// width: widget.iconSize,
// height: widget.iconSize + widget.iconSize / 3,
child: GestureDetector(
onTap: () async {
authenticateUser();
},
onLongPress: null, // Do this later
child: Column(
children: [
Flexible(
flex: 3,
child: LayoutBuilder(
builder: (context, constraints) {
double iconHeight = constraints.maxWidth;
return Container(
width: iconHeight,
height: iconHeight,
child:
FittedBox(fit: BoxFit.fitHeight, child: widget.appIcon),
);
},
),
),
const SizedBox(height: 10),
Flexible(
flex: 1,
child: FittedBox(
child: Text(
widget.appName,
textAlign: TextAlign.center,
// softWrap: true,
// overflow: TextOverflow.visible,
style: TextStyle(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
)
],
),
),
);
}
}
@@ -1,95 +0,0 @@
import 'package:mzansi_innovation_hub/main.dart';
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
class MihPackageToolBody extends StatefulWidget {
final bool borderOn;
final Widget bodyItem;
final double? innerHorizontalPadding;
const MihPackageToolBody({
super.key,
required this.borderOn,
required this.bodyItem,
this.innerHorizontalPadding,
});
@override
State<MihPackageToolBody> createState() => _MihPackageToolBodyState();
}
class _MihPackageToolBodyState extends State<MihPackageToolBody> {
late double _innerBodyPadding;
double getHorizontalPaddingSize(Size screenSize) {
if (MzansiInnovationHub.of(context)!.theme.screenType == "desktop") {
if (widget.borderOn) {
return widget.innerHorizontalPadding ?? 10;
} else {
return widget.innerHorizontalPadding ?? 0;
}
} else {
// mobile
if (widget.borderOn) {
return widget.innerHorizontalPadding ?? 10;
} else {
return widget.innerHorizontalPadding ?? 0;
}
}
}
double getVerticalPaddingSize(Size screenSize) {
// mobile
if (widget.borderOn) {
return 10;
} else {
return 0;
}
}
Decoration? getBoader() {
if (widget.borderOn) {
_innerBodyPadding = 10.0;
return BoxDecoration(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
borderRadius: BorderRadius.circular(25.0),
border: Border.all(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
width: 3.0),
);
} else {
_innerBodyPadding = 0.0;
return BoxDecoration(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
borderRadius: BorderRadius.circular(25.0),
border: Border.all(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
width: 3.0),
);
}
}
@override
Widget build(BuildContext context) {
Size screenSize = MediaQuery.sizeOf(context);
return Padding(
padding: EdgeInsets.only(
left: getHorizontalPaddingSize(screenSize),
right: getHorizontalPaddingSize(screenSize),
bottom: getVerticalPaddingSize(screenSize),
top: 0,
),
child: Container(
height: screenSize.height,
decoration: getBoader(),
child: Padding(
padding: EdgeInsets.all(_innerBodyPadding),
child: widget.bodyItem,
),
),
);
}
}
@@ -1,52 +0,0 @@
import 'package:flutter/material.dart';
// ignore: must_be_immutable
class MihPackageTools extends StatefulWidget {
final Map<Widget, void Function()?> tools;
int selcetedIndex;
MihPackageTools({
super.key,
required this.tools,
required this.selcetedIndex,
});
@override
State<MihPackageTools> createState() => _MihPackageToolsState();
}
class _MihPackageToolsState extends State<MihPackageTools> {
List<Widget> getTools() {
List<Widget> temp = [];
int index = 0;
widget.tools.forEach((icon, onTap) {
temp.add(
Visibility(
visible: widget.selcetedIndex != index,
child: IconButton(
onPressed: onTap,
icon: icon,
),
),
);
temp.add(
Visibility(
visible: widget.selcetedIndex == index,
child: IconButton.filled(
onPressed: onTap,
icon: icon,
),
),
);
index += 1;
});
return temp;
}
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: getTools(),
);
}
}
@@ -1,210 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_single_child_scroll.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_floating_menu.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
class MihPackageWindow extends StatefulWidget {
final String? windowTitle;
final Widget windowBody;
final List<SpeedDialChild>? menuOptions;
final void Function()? onWindowTapClose;
final Color? backgroundColor;
final Color? foregroundColor;
final bool? borderOn;
final bool fullscreen;
const MihPackageWindow({
super.key,
required this.fullscreen,
required this.windowTitle,
this.menuOptions,
required this.onWindowTapClose,
required this.windowBody,
this.borderOn,
this.backgroundColor,
this.foregroundColor,
});
@override
State<MihPackageWindow> createState() => _MihPackageWindowState();
}
class _MihPackageWindowState extends State<MihPackageWindow> {
late double windowTitleSize;
late double horizontralWindowPadding;
late double vertticalWindowPadding;
late double windowWidth;
late double windowHeight;
late double width;
late double height;
void checkScreenSize() {
// print("screen width: $width");
// print("screen height: $height");
if (MzansiInnovationHub.of(context)!.theme.screenType == "desktop") {
setState(() {
windowTitleSize = 25;
horizontralWindowPadding = width / 7;
vertticalWindowPadding = 10;
windowWidth = width;
windowHeight = height;
});
} else {
setState(() {
windowTitleSize = 20;
horizontralWindowPadding = 10;
vertticalWindowPadding = 10;
windowWidth = width;
windowHeight = height;
});
}
}
Widget getHeader() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (widget.onWindowTapClose != null)
Padding(
padding: const EdgeInsets.only(
top: 5.0,
left: 5.0,
),
child: MihButton(
width: 40,
height: 40,
elevation: 10,
onPressed: widget.onWindowTapClose,
buttonColor: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
child: Icon(
Icons.close,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
),
),
if (widget.windowTitle != null)
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Text(
widget.windowTitle!,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: windowTitleSize,
fontWeight: FontWeight.bold,
color: widget.foregroundColor ??
MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
),
),
),
if (widget.menuOptions != null)
Padding(
padding: const EdgeInsets.only(
top: 5.0,
right: 5.0,
),
child: SizedBox(
width: 40,
child: MihFloatingMenu(
iconSize: 40,
animatedIcon: AnimatedIcons.menu_close,
direction: SpeedDialDirection.down,
children: widget.menuOptions != null ? widget.menuOptions! : [],
),
),
),
],
);
}
@override
void dispose() {
super.dispose();
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
setState(() {
width = size.width;
height = size.height;
});
checkScreenSize();
return Dialog(
insetPadding: EdgeInsets.symmetric(
horizontal: horizontralWindowPadding,
vertical: vertticalWindowPadding,
),
insetAnimationCurve: Easing.emphasizedDecelerate,
insetAnimationDuration: Durations.short1,
child: Material(
elevation: 10,
shadowColor: Colors.black,
color: widget.backgroundColor ??
MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
borderRadius: BorderRadius.circular(25.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25.0),
border: widget.borderOn == null || !widget.borderOn!
? null
: Border.all(
color: widget.foregroundColor ??
MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
width: 5.0),
),
child: widget.fullscreen
? Column(
mainAxisSize: MainAxisSize.max,
children: [
getHeader(),
const SizedBox(height: 5),
Expanded(
child: SingleChildScrollView(child: widget.windowBody)),
],
)
: Column(
mainAxisSize: MainAxisSize.min,
children: [
getHeader(),
const SizedBox(height: 5),
Flexible(
child: Padding(
padding: EdgeInsets.only(
left: 25,
right: 25,
bottom: vertticalWindowPadding,
),
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: windowHeight * 0.85,
maxWidth: windowWidth * 0.85,
),
child: MihSingleChildScroll(child: widget.windowBody),
),
),
),
],
),
),
),
);
}
}
@@ -1,108 +0,0 @@
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_objects/app_user.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_circle_avatar.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_icons.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_file_services.dart';
class MihPersonalProfilePreview extends StatefulWidget {
final AppUser user;
const MihPersonalProfilePreview({
super.key,
required this.user,
});
@override
State<MihPersonalProfilePreview> createState() =>
_MihPersonalProfilePreviewState();
}
class _MihPersonalProfilePreviewState extends State<MihPersonalProfilePreview> {
late Future<String> futureImageUrl;
// String imageUrl = "";
PlatformFile? file;
@override
void initState() {
super.initState();
futureImageUrl = MihFileApi.getMinioFileUrl(widget.user.pro_pic_path);
}
@override
Widget build(BuildContext context) {
double profilePictureWidth = 60;
return Row(
children: [
FutureBuilder(
future: futureImageUrl,
builder: (context, asyncSnapshot) {
if (asyncSnapshot.connectionState == ConnectionState.done &&
asyncSnapshot.hasData) {
if (asyncSnapshot.requireData != "") {
return MihCircleAvatar(
imageFile: NetworkImage(asyncSnapshot.requireData),
width: profilePictureWidth,
editable: false,
fileNameController: TextEditingController(),
userSelectedfile: file,
frameColor: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
backgroundColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
onChange: () {},
);
} else {
return Icon(
MihIcons.iDontKnow,
size: profilePictureWidth,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
);
}
} else {
return Icon(
MihIcons.mihRing,
size: profilePictureWidth,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
);
}
},
),
const SizedBox(width: 15),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.user.username.isNotEmpty
? widget.user.username
: "Username",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
Text(
widget.user.fname.isNotEmpty
? "${widget.user.fname} ${widget.user.lname}"
: "Name Surname",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
Text(
widget.user.type.toUpperCase(),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 10,
),
),
],
)
],
);
}
}
@@ -1,140 +0,0 @@
import 'package:flutter/material.dart';
class MihRadioOptions extends StatefulWidget {
final TextEditingController controller;
final String hintText;
final Color fillColor;
final Color secondaryFillColor;
final bool requiredText;
final List<String> radioOptions;
const MihRadioOptions({
super.key,
required this.controller,
required this.hintText,
required this.fillColor,
required this.secondaryFillColor,
required this.requiredText,
required this.radioOptions,
});
@override
State<MihRadioOptions> createState() => _MihRadioOptionsState();
}
class _MihRadioOptionsState extends State<MihRadioOptions> {
// late String _currentSelection;
@override
void initState() {
super.initState();
if (widget.controller.text.isEmpty && widget.radioOptions.isNotEmpty) {
widget.controller.text = widget.radioOptions[0];
}
// else{
// int index = widget.radioOptions
// .indexWhere((element) => element == option);
// _currentSelection = widget.radioOptions[index];
// widget.controller.text = option;
// }
// _currentSelection = widget.radioOptions[0];
}
// The method to handle a change in selection.
void _onChanged(String? value) {
if (value != null) {
widget.controller.text = value;
}
}
Widget displayRadioOptions(String selection) {
return Material(
elevation: 4.0,
borderRadius: BorderRadius.circular(8.0),
child: Container(
decoration: BoxDecoration(
color: widget.fillColor,
borderRadius: BorderRadius.circular(8.0),
),
child: Column(
children: widget.radioOptions.map((option) {
return GestureDetector(
onTap: () {
_onChanged(option);
},
child: Row(
children: [
const SizedBox(width: 10),
Expanded(
child: Text(
option,
style: TextStyle(
color: widget.secondaryFillColor,
fontWeight: FontWeight.w500,
fontSize: 15,
),
),
),
Radio<String>(
value: option,
groupValue: selection,
onChanged: _onChanged,
activeColor: widget.secondaryFillColor,
fillColor: WidgetStateProperty.resolveWith<Color?>(
(Set<WidgetState> states) {
if (states.contains(WidgetState.selected)) {
return widget.secondaryFillColor; // Color when selected
}
return widget.secondaryFillColor;
}),
),
],
),
);
}).toList(),
),
),
);
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: widget.controller,
builder: (context, child) {
final currentSelection = widget.controller.text;
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.hintText,
textAlign: TextAlign.left,
style: TextStyle(
color: widget.fillColor,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Visibility(
visible: !widget.requiredText,
child: Text(
"(Optional)",
textAlign: TextAlign.right,
style: TextStyle(
color: widget.fillColor,
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 4),
displayRadioOptions(currentSelection),
],
);
});
}
}
@@ -1,18 +0,0 @@
import 'package:flutter/material.dart';
SnackBar MihSnackBar({
required Widget child,
}) {
return SnackBar(
content: child,
shape: StadiumBorder(),
behavior: SnackBarBehavior.floating,
duration: Duration(seconds: 2),
width: null,
action: SnackBarAction(
label: "Dismiss",
onPressed: () {},
),
// elevation: 30,
);
}
@@ -1,185 +0,0 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class MihSearchBar extends StatefulWidget {
final TextEditingController controller;
final String hintText;
final IconData prefixIcon;
final IconData? prefixAltIcon;
final List<Widget>? suffixTools;
final double? width;
final double? height;
final Color fillColor;
final Color hintColor;
final void Function()? onPrefixIconTap;
final void Function()? onClearIconTap;
final double? elevation;
final FocusNode searchFocusNode;
const MihSearchBar({
Key? key,
required this.controller,
required this.hintText,
required this.prefixIcon,
this.prefixAltIcon,
this.suffixTools,
this.width,
this.height,
required this.fillColor,
required this.hintColor,
required this.onPrefixIconTap,
this.onClearIconTap,
this.elevation,
required this.searchFocusNode,
}) : super(key: key);
@override
State<MihSearchBar> createState() => _MihSearchBarState();
}
class _MihSearchBarState extends State<MihSearchBar> {
bool _showClearIcon = false;
Widget getPrefixIcon() {
if (_showClearIcon) {
// If the clear icon is shown and an alternative prefix icon is provided, use it
return widget.prefixAltIcon != null
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Icon(
widget.prefixAltIcon,
color: widget.hintColor,
size: 35,
),
)
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Icon(
Icons.search,
color: widget.hintColor,
size: 35,
),
); // Default to search icon if no alt icon
} else {
// Return the primary prefix icon or the alternative if provided
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Icon(
Icons.search,
color: widget.hintColor,
size: 35,
),
);
}
}
@override
void initState() {
super.initState();
// 1. Add the listener to the controller
widget.controller.addListener(_updateClearIconVisibility);
// 2. Initialize the clear icon visibility based on the current text
_updateClearIconVisibility();
}
@override
void dispose() {
widget.controller.removeListener(_updateClearIconVisibility);
super.dispose();
}
void _updateClearIconVisibility() {
if (!mounted) {
return;
}
final bool shouldShow = widget.controller.text.isNotEmpty;
// Only call setState if the visibility state actually changes
if (_showClearIcon != shouldShow) {
setState(() {
_showClearIcon = shouldShow;
});
}
}
@override
Widget build(BuildContext context) {
return Material(
elevation: widget.elevation ?? 4.0, // Use provided elevation or default
borderRadius: BorderRadius.circular(30.0),
color: widget.fillColor,
child: AnimatedContainer(
// Keep AnimatedContainer for width/height transitions
alignment: Alignment.centerLeft,
width: widget.width,
height: widget.height ?? 50,
duration: const Duration(milliseconds: 300),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
),
child: Theme(
data: Theme.of(context).copyWith(
textSelectionTheme: TextSelectionThemeData(
selectionColor: widget.hintColor.withValues(alpha: 0.3),
selectionHandleColor: widget.hintColor,
),
),
child: TextField(
textAlignVertical: TextAlignVertical.center,
controller: widget.controller, // Assign the controller
focusNode: widget.searchFocusNode,
autocorrect: true,
spellCheckConfiguration: kIsWeb ? null : SpellCheckConfiguration(),
onSubmitted: (value) {
widget.onPrefixIconTap
?.call(); // Call the prefix icon tap handler
},
style: TextStyle(
color: widget.hintColor,
fontWeight: FontWeight.w600,
fontSize: 16,
),
cursorColor: widget.hintColor,
decoration: InputDecoration(
isDense: true,
hintText: widget.hintText,
hintStyle: TextStyle(
color: widget.hintColor,
fontWeight: FontWeight.w600,
fontSize: 16,
),
border: InputBorder.none,
contentPadding:
const EdgeInsets.symmetric(horizontal: 10.0, vertical: 15.0),
prefixIcon: GestureDetector(
onTap: widget.onPrefixIconTap,
child: getPrefixIcon(),
),
suffixIcon: Row(
// Use a Row for multiple suffix icons
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Optional suffix tools
if (widget.suffixTools != null) ...widget.suffixTools!,
// Clear Icon (conditionally visible)
if (_showClearIcon) // Only show if input is not empty
IconButton(
iconSize: 35,
icon: Icon(Icons.clear,
color: widget.hintColor), // Clear icon
onPressed: widget.onClearIconTap ??
() {
widget.controller.clear();
// No need for setState here, _updateClearIconVisibility will handle it
},
),
],
),
),
),
),
),
);
}
}
@@ -1,28 +0,0 @@
import 'package:flutter/material.dart';
class MihSingleChildScroll extends StatefulWidget {
final Widget child;
const MihSingleChildScroll({
super.key,
required this.child,
});
@override
State<MihSingleChildScroll> createState() => _MihSingleChildScrollState();
}
class _MihSingleChildScrollState extends State<MihSingleChildScroll> {
@override
Widget build(BuildContext context) {
return SafeArea(
bottom: false,
minimum: EdgeInsets.only(bottom: 5),
child: ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
child: SingleChildScrollView(
child: widget.child,
),
),
);
}
}
@@ -1,312 +0,0 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
class MihTextFormField extends StatefulWidget {
final double? width;
final double? height;
final Color fillColor;
final Color inputColor;
final TextEditingController controller;
final bool? hasError;
final String? hintText;
final double? borderRadius;
final bool? multiLineInput;
final bool? readOnly;
final bool? passwordMode;
final bool? numberMode;
final bool requiredText;
final FormFieldValidator<String>? validator;
final List<String>? autofillHints;
final double? elevation;
final TextAlign? textIputAlignment;
const MihTextFormField({
Key? key,
this.width,
this.height,
required this.fillColor,
required this.inputColor,
required this.controller,
this.hasError,
required this.hintText,
required this.requiredText,
this.borderRadius,
this.multiLineInput,
this.readOnly,
this.passwordMode,
this.numberMode,
this.validator,
this.autofillHints,
this.elevation,
this.textIputAlignment,
}) : super(key: key);
@override
State<MihTextFormField> createState() => _MihTextFormFieldState();
}
class _MihTextFormFieldState extends State<MihTextFormField> {
late bool _obscureText;
FormFieldState<String>? _formFieldState;
@override
void initState() {
super.initState();
_obscureText = widget.passwordMode ?? false;
widget.controller.addListener(_onControllerTextChanged);
}
@override
void didUpdateWidget(covariant MihTextFormField oldWidget) {
super.didUpdateWidget(oldWidget);
// If the controller itself changes, remove listener from old and add to new
if (widget.controller != oldWidget.controller) {
oldWidget.controller.removeListener(_onControllerTextChanged);
widget.controller.addListener(_onControllerTextChanged);
// Immediately update form field state if controller changed and has value
_formFieldState?.didChange(widget.controller.text);
}
}
void _onControllerTextChanged() {
// Only update the FormField's value if it's not already the same
// and if the formFieldState is available.
if (_formFieldState != null &&
_formFieldState!.value != widget.controller.text) {
_formFieldState!.didChange(widget.controller.text);
}
}
@override
void dispose() {
widget.controller.removeListener(_onControllerTextChanged);
super.dispose();
}
@override
Widget build(BuildContext context) {
final isMultiline = widget.multiLineInput == true;
return Center(
child: SizedBox(
width: widget.width,
// height: widget.height,
height: isMultiline ? null : widget.height,
child: Theme(
data: Theme.of(context).copyWith(
textSelectionTheme: TextSelectionThemeData(
selectionColor: widget.inputColor.withValues(alpha: 0.3),
selectionHandleColor: widget.inputColor,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Visibility(
visible: widget.hintText != null,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.hintText ?? "",
textAlign: TextAlign.left,
style: TextStyle(
color: widget.fillColor,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Visibility(
visible: !widget.requiredText,
child: Text(
"(Optional)",
textAlign: TextAlign.right,
style: TextStyle(
color: widget.fillColor,
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
const SizedBox(height: 4),
FormField<String>(
initialValue: widget.controller.text,
validator: widget.validator,
autovalidateMode: AutovalidateMode.onUserInteraction,
builder: (field) {
_formFieldState = field;
return Column(
crossAxisAlignment:
CrossAxisAlignment.start, // <-- Add this line
children: [
Material(
elevation: widget.elevation ?? 4.0,
borderRadius:
BorderRadius.circular(widget.borderRadius ?? 8.0),
child: SizedBox(
height: widget.height != null
? widget.height! - 30
: null,
child: TextFormField(
controller: widget.controller,
cursorColor: widget.inputColor,
autofillHints: widget.autofillHints,
autocorrect: true,
spellCheckConfiguration: (kIsWeb ||
widget.passwordMode == true ||
widget.numberMode == true)
? null
: SpellCheckConfiguration(),
textAlign:
widget.textIputAlignment ?? TextAlign.start,
textAlignVertical: widget.multiLineInput == true
? TextAlignVertical.top
: TextAlignVertical.center,
obscureText: widget.passwordMode == true
? _obscureText
: false,
expands: widget.passwordMode == true
? false
: (widget.multiLineInput ?? false),
maxLines: widget.passwordMode == true ? 1 : null,
readOnly: widget.readOnly ?? false,
keyboardType: widget.numberMode == true
? const TextInputType.numberWithOptions(
decimal: true)
: null,
inputFormatters: widget.numberMode == true
? [
FilteringTextInputFormatter.allow(
RegExp(r'^\d*\.?\d*'))
]
: null,
style: TextStyle(
color: widget.inputColor,
fontWeight: FontWeight.w500,
),
decoration: InputDecoration(
suffixIcon: widget.passwordMode == true
? FocusScope(
canRequestFocus: false,
child: IconButton(
icon: Icon(
_obscureText
? Icons.visibility_off
: Icons.visibility,
color: widget.inputColor,
),
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
),
)
: null,
errorStyle: const TextStyle(
height: 0, fontSize: 0), // <-- Add this line
contentPadding: const EdgeInsets.symmetric(
horizontal: 10.0, vertical: 8.0),
filled: true,
fillColor: widget.fillColor,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(
widget.borderRadius ?? 8.0),
borderSide: field.hasError
? BorderSide(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
width: 2.0,
)
: BorderSide.none,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
widget.borderRadius ?? 8.0),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
widget.borderRadius ?? 8.0),
borderSide: BorderSide(
color: field.hasError
? MihColors.getRedColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark")
: widget.inputColor,
width: 3.0,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
widget.borderRadius ?? 8.0),
borderSide: BorderSide(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
width: 3.0,
),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
widget.borderRadius ?? 8.0),
borderSide: BorderSide(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
width: 3.0,
),
),
),
onChanged: (value) {
field.didChange(value);
},
),
),
),
if (field.hasError)
Row(
children: [
Padding(
padding:
const EdgeInsets.only(left: 8.0, top: 4.0),
child: Text(
field.errorText ?? '',
style: TextStyle(
fontSize: 12,
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
fontWeight: FontWeight.bold,
),
),
),
],
),
],
);
},
),
],
),
),
),
);
}
}
@@ -1,217 +0,0 @@
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
class MihTimeField extends StatefulWidget {
final TextEditingController controller;
final String labelText;
final bool required;
final double? width;
final double? height;
final double? borderRadius;
final double? elevation;
final FormFieldValidator<String>? validator;
const MihTimeField({
super.key,
required this.controller,
required this.labelText,
required this.required,
this.width,
this.height,
this.borderRadius,
this.elevation,
this.validator,
});
@override
State<MihTimeField> createState() => _MihTimeFieldState();
}
class _MihTimeFieldState extends State<MihTimeField> {
FormFieldState<String>? _formFieldState;
Future<void> _selectTime(BuildContext context) async {
TimeOfDay? picked = await showTimePicker(
context: context,
initialTime: widget.controller.text.isNotEmpty
? TimeOfDay(
hour: int.tryParse(widget.controller.text.split(":")[0]) ?? 0,
minute: int.tryParse(widget.controller.text.split(":")[1]) ?? 0,
)
: TimeOfDay.now(),
builder: (context, child) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true),
child: child as Widget,
);
},
);
if (picked != null) {
final hours = picked.hour.toString().padLeft(2, '0');
final minutes = picked.minute.toString().padLeft(2, '0');
widget.controller.text = "$hours:$minutes";
_formFieldState?.didChange(widget.controller.text);
setState(() {});
}
}
@override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
width: widget.width,
height: widget.height,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.labelText,
style: TextStyle(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
if (!widget.required)
Text(
"(Optional)",
style: TextStyle(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 4),
FormField<String>(
initialValue: widget.controller.text,
validator: widget.validator,
autovalidateMode: AutovalidateMode.onUserInteraction,
builder: (field) {
_formFieldState = field;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Material(
elevation: widget.elevation ?? 4.0,
borderRadius:
BorderRadius.circular(widget.borderRadius ?? 8.0),
child: TextFormField(
controller: widget.controller,
readOnly: true,
onTap: () => _selectTime(context),
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.w500,
),
decoration: InputDecoration(
suffixIcon: Icon(
Icons.access_time,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
errorStyle: const TextStyle(height: 0, fontSize: 0),
contentPadding: const EdgeInsets.symmetric(
horizontal: 10.0, vertical: 8.0),
filled: true,
fillColor: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(
widget.borderRadius ?? 8.0),
borderSide: field.hasError
? BorderSide(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
width: 2.0,
)
: BorderSide.none,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
widget.borderRadius ?? 8.0),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
widget.borderRadius ?? 8.0),
borderSide: BorderSide(
color: field.hasError
? MihColors.getRedColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark")
: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
width: 3.0,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
widget.borderRadius ?? 8.0),
borderSide: BorderSide(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
width: 3.0,
),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(
widget.borderRadius ?? 8.0),
borderSide: BorderSide(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
width: 3.0,
),
),
),
onChanged: (value) {
field.didChange(value);
},
),
),
if (field.hasError)
Padding(
padding: const EdgeInsets.only(left: 8.0, top: 4.0),
child: Text(
field.errorText ?? '',
style: TextStyle(
fontSize: 12,
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
),
),
),
],
);
},
),
],
),
),
);
}
}

Some files were not shown because too many files have changed in this diff Show More