diff --git a/assets/images/svg/book_appo_bottom.svg b/assets/images/svg/book_appo_bottom.svg new file mode 100644 index 0000000..d09107e --- /dev/null +++ b/assets/images/svg/book_appo_bottom.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/doctor_calendar_icon.svg b/assets/images/svg/doctor_calendar_icon.svg new file mode 100644 index 0000000..f76c49f --- /dev/null +++ b/assets/images/svg/doctor_calendar_icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/images/svg/eye_results_icon.svg b/assets/images/svg/eye_results_icon.svg new file mode 100644 index 0000000..fd9c754 --- /dev/null +++ b/assets/images/svg/eye_results_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/forward_arrow_icon.svg b/assets/images/svg/forward_arrow_icon.svg new file mode 100644 index 0000000..d8a3d51 --- /dev/null +++ b/assets/images/svg/forward_arrow_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/home_bottom.svg b/assets/images/svg/home_bottom.svg new file mode 100644 index 0000000..1bf82f9 --- /dev/null +++ b/assets/images/svg/home_bottom.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/my_files_bottom.svg b/assets/images/svg/my_files_bottom.svg new file mode 100644 index 0000000..813805e --- /dev/null +++ b/assets/images/svg/my_files_bottom.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/prescription_refill_icon.svg b/assets/images/svg/prescription_refill_icon.svg new file mode 100644 index 0000000..7a9706b --- /dev/null +++ b/assets/images/svg/prescription_refill_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/prescription_remarks_icon.svg b/assets/images/svg/prescription_remarks_icon.svg new file mode 100644 index 0000000..a8d7adb --- /dev/null +++ b/assets/images/svg/prescription_remarks_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/rating_icon.svg b/assets/images/svg/rating_icon.svg new file mode 100644 index 0000000..dee25d4 --- /dev/null +++ b/assets/images/svg/rating_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/search_icon.svg b/assets/images/svg/search_icon.svg new file mode 100644 index 0000000..1a64ce5 --- /dev/null +++ b/assets/images/svg/search_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/services_bottom.svg b/assets/images/svg/services_bottom.svg new file mode 100644 index 0000000..baa3d6b --- /dev/null +++ b/assets/images/svg/services_bottom.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/svg/todo_bottom.svg b/assets/images/svg/todo_bottom.svg new file mode 100644 index 0000000..ab49757 --- /dev/null +++ b/assets/images/svg/todo_bottom.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/view_report_icon.svg b/assets/images/svg/view_report_icon.svg new file mode 100644 index 0000000..0f7d866 --- /dev/null +++ b/assets/images/svg/view_report_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/json/countriesList.json b/assets/json/countriesList.json new file mode 100644 index 0000000..25a65b3 --- /dev/null +++ b/assets/json/countriesList.json @@ -0,0 +1,1212 @@ +[ + { + "ID": "AFG", + "Name": "Afghan", + "NameN": "أفغاني" + }, + { + "ID": "CAF", + "Name": "African", + "NameN": "أفريقي" + }, + { + "ID": "ALB", + "Name": "Albanian", + "NameN": "ألباني" + }, + { + "ID": "DZA", + "Name": "Algerian", + "NameN": "جزائري" + }, + { + "ID": "USA", + "Name": "American", + "NameN": "أمريكي" + }, + { + "ID": "UMI", + "Name": "American (Minor Outlying Islands)", + "NameN": "أمريكي (الجزر الصغرى النائية)" + }, + { + "ID": "ASM", + "Name": "American Samoa", + "NameN": "ساموا الأمريكية" + }, + { + "ID": "AND", + "Name": "Andorrian", + "NameN": "أندوري" + }, + { + "ID": "AGO", + "Name": "Angolan", + "NameN": "أنغولي" + }, + { + "ID": "AIA", + "Name": "Anguillan", + "NameN": "أنغيلي" + }, + { + "ID": "ATA", + "Name": "Antarctica", + "NameN": "أنتاركتيكا" + }, + { + "ID": "ATG", + "Name": "Antiguans, Barbudans", + "NameN": "أنتيغوي، باربودي" + }, + { + "ID": "ARG", + "Name": "Argentine", + "NameN": "أرجنتيني" + }, + { + "ID": "ARM", + "Name": "Armenian", + "NameN": "أرمني" + }, + { + "ID": "ABW", + "Name": "Arubian", + "NameN": "أروبي" + }, + { + "ID": "AUS", + "Name": "Australian", + "NameN": "أسترالي" + }, + { + "ID": "AUT", + "Name": "Austrian", + "NameN": "نمساوي" + }, + { + "ID": "AZE", + "Name": "Azeri", + "NameN": "أذربيجاني" + }, + { + "ID": "BHS", + "Name": "Bahamian", + "NameN": "باهامي" + }, + { + "ID": "BHR", + "Name": "Bahraini", + "NameN": "بحريني" + }, + { + "ID": "BRB", + "Name": "Barbadian or Bajuns", + "NameN": "باربادوسي" + }, + { + "ID": "BLR", + "Name": "Belarusian", + "NameN": "بيلاروسي" + }, + { + "ID": "BEL", + "Name": "Belgian", + "NameN": "بلجيكي" + }, + { + "ID": "BLZ", + "Name": "Belizean", + "NameN": "بليزي" + }, + { + "ID": "BGD", + "Name": "Bengali", + "NameN": "بنغلاديشي" + }, + { + "ID": "BEN", + "Name": "Beninese", + "NameN": "بنيني" + }, + { + "ID": "BMU", + "Name": "Bermudian", + "NameN": "برمودي" + }, + { + "ID": "BTN", + "Name": "Bhutanese", + "NameN": "بوتاني" + }, + { + "ID": "BOL", + "Name": "Bolivian", + "NameN": "بوليفي" + }, + { + "ID": "BIH", + "Name": "Bosnian And Herzegovinian", + "NameN": "بوسني وهرسكي" + }, + { + "ID": "BWA", + "Name": "Botswanian", + "NameN": "بوتسواني" + }, + { + "ID": "BVT", + "Name": "Bouvet Island (Bouvetoya)", + "NameN": "جزيرة بوفيه" + }, + { + "ID": "BRA", + "Name": "Brazilian", + "NameN": "برازيلي" + }, + { + "ID": "GBR", + "Name": "British", + "NameN": "بريطاني" + }, + { + "ID": "IOT", + "Name": "British (Chagos Archipelago)", + "NameN": "بريطاني (أرخبيل شاغوس)" + }, + { + "ID": "VGB", + "Name": "British (Virgin Islands)", + "NameN": "بريطاني (الجزر العذراء)" + }, + { + "ID": "BRN", + "Name": "Bruneian", + "NameN": "بروني" + }, + { + "ID": "BGR", + "Name": "Bulgarian", + "NameN": "بلغاري" + }, + { + "ID": "MMR", + "Name": "Burmese or Myanmarese", + "NameN": "بورمي أو ميانماري" + }, + { + "ID": "BFA", + "Name": "Burnikabe", + "NameN": "بوركيني" + }, + { + "ID": "BDI", + "Name": "Burundi", + "NameN": "بوروندي" + }, + { + "ID": "KHM", + "Name": "Cambodian", + "NameN": "كمبودي" + }, + { + "ID": "CMR", + "Name": "Cameroonian", + "NameN": "كاميروني" + }, + { + "ID": "CAN", + "Name": "Canadian", + "NameN": "كندي" + }, + { + "ID": "CPV", + "Name": "Cape Verdian or Cape Verdean", + "NameN": "رأس أخضري" + }, + { + "ID": "CYM", + "Name": "Cayman Islander", + "NameN": "جزر كايماني" + }, + { + "ID": "TCD", + "Name": "Chadian", + "NameN": "تشادي" + }, + { + "ID": "CHL", + "Name": "Chilean", + "NameN": "تشيلي" + }, + { + "ID": "HKG", + "Name": "Chinese (Hong Kong)", + "NameN": "صيني (هونغ كونغ)" + }, + { + "ID": "MAC", + "Name": "Chinese (Macao)", + "NameN": "صيني (ماكاو)" + }, + { + "ID": "CHN", + "Name": "Chinese", + "NameN": "صيني" + }, + { + "ID": "CXR", + "Name": "Christmas Island", + "NameN": "جزيرة الكريسماس" + }, + { + "ID": "CCK", + "Name": "Cocos (Keeling) Islands", + "NameN": "جزر كوكوس (كيلنغ)" + }, + { + "ID": "COL", + "Name": "Colombian", + "NameN": "كولومبي" + }, + { + "ID": "COM", + "Name": "Comoran", + "NameN": "قمري" + }, + { + "ID": "COG", + "Name": "Congolese", + "NameN": "كونغولي" + }, + { + "ID": "COD", + "Name": "Congolese (Zaire)", + "NameN": "كونغولي (زائير)" + }, + { + "ID": "COK", + "Name": "Cook Islander", + "NameN": "جزر كوك" + }, + { + "ID": "CRI", + "Name": "Costa Rican", + "NameN": "كوستاريكي" + }, + { + "ID": "HRV", + "Name": "Croat", + "NameN": "كرواتي" + }, + { + "ID": "CUB", + "Name": "Cuban", + "NameN": "كوبي" + }, + { + "ID": "CYP", + "Name": "Cypriot, Greek", + "NameN": "قبرصي، يوناني" + }, + { + "ID": "CZE", + "Name": "Czech", + "NameN": "تشيكي" + }, + { + "ID": "DNK", + "Name": "Danish", + "NameN": "دنماركي" + }, + { + "ID": "DJI", + "Name": "Djibouti", + "NameN": "جيبوتي" + }, + { + "ID": "DOM", + "Name": "Dominican", + "NameN": "دومينيكاني" + }, + { + "ID": "DMA", + "Name": "Dominican (Commonwealth)", + "NameN": "دومينيكي (الكومنولث)" + }, + { + "ID": "ANT", + "Name": "Dutch (Antilles)", + "NameN": "هولندي (الأنتيل)" + }, + { + "ID": "NLD", + "Name": "Dutch (Netherlands)", + "NameN": "هولندي (هولندا)" + }, + { + "ID": "ECU", + "Name": "Ecuadorean", + "NameN": "إكوادوري" + }, + { + "ID": "EGY", + "Name": "Egyptians", + "NameN": "مصري" + }, + { + "ID": "ARE", + "Name": "Emirati", + "NameN": "إماراتي" + }, + { + "ID": "GNQ", + "Name": "Equatorial Guinean or Equatoguinean", + "NameN": "غيني استوائي" + }, + { + "ID": "ERI", + "Name": "Eritrean", + "NameN": "إريتري" + }, + { + "ID": "EST", + "Name": "Estonian", + "NameN": "إستوني" + }, + { + "ID": "ETH", + "Name": "Ethiopian", + "NameN": "إثيوبي" + }, + { + "ID": "FRO", + "Name": "Faeroe Islander", + "NameN": "جزر فارو" + }, + { + "ID": "FLK", + "Name": "Falkland Islander", + "NameN": "جزر فوكلاند" + }, + { + "ID": "FJI", + "Name": "Fijian", + "NameN": "فيجي" + }, + { + "ID": "PHL", + "Name": "Filipino", + "NameN": "فلبيني" + }, + { + "ID": "FIN", + "Name": "Finnish", + "NameN": "فنلندي" + }, + { + "ID": "FRA", + "Name": "French", + "NameN": "فرنسي" + }, + { + "ID": "GUF", + "Name": "French Guianian", + "NameN": "غويانا الفرنسية" + }, + { + "ID": "PYF", + "Name": "French Polynesian", + "NameN": "بولينيزيا الفرنسية" + }, + { + "ID": "ATF", + "Name": "French Southern Territories", + "NameN": "الأقاليم الجنوبية الفرنسية" + }, + { + "ID": "GAB", + "Name": "Gabonese", + "NameN": "غابوني" + }, + { + "ID": "GMB", + "Name": "Gambian", + "NameN": "غامبي" + }, + { + "ID": "GEO", + "Name": "Georgian", + "NameN": "جورجي" + }, + { + "ID": "SGS", + "Name": "Georgian (South)", + "NameN": "جورجي (جنوبي)" + }, + { + "ID": "DEU", + "Name": "German", + "NameN": "ألماني" + }, + { + "ID": "GHA", + "Name": "Ghanaian", + "NameN": "غاني" + }, + { + "ID": "GIB", + "Name": "Gibraltarian", + "NameN": "جبل طارقي" + }, + { + "ID": "GRC", + "Name": "Greek", + "NameN": "يوناني" + }, + { + "ID": "GRL", + "Name": "Greenlander", + "NameN": "غرينلاندي" + }, + { + "ID": "GRD", + "Name": "Grenadian or Grenadan", + "NameN": "غرينادي" + }, + { + "ID": "GLP", + "Name": "Guadeloupian", + "NameN": "غوادلوبي" + }, + { + "ID": "GUM", + "Name": "Guamanian", + "NameN": "غوامي" + }, + { + "ID": "GTM", + "Name": "Guatemalan", + "NameN": "غواتيمالي" + }, + { + "ID": "GNB", + "Name": "Guinea-Bissauan", + "NameN": "غيني بيساوي" + }, + { + "ID": "GIN", + "Name": "Guinean", + "NameN": "غيني" + }, + { + "ID": "GUY", + "Name": "Guyanese", + "NameN": "غيانيزي" + }, + { + "ID": "HTI", + "Name": "Haitian", + "NameN": "هايتيني" + }, + { + "ID": "HMD", + "Name": "Heard and McDonald Islands", + "NameN": "جزر هيرد وماكدونالد" + }, + { + "ID": "HND", + "Name": "Honduran", + "NameN": "هندوراسي" + }, + { + "ID": "HUN", + "Name": "Hungarian", + "NameN": "مجري" + }, + { + "ID": "ISL", + "Name": "Icelander", + "NameN": "آيسلندي" + }, + { + "ID": "KIR", + "Name": "I-Kiribati", + "NameN": "كيريباتي" + }, + { + "ID": "IND", + "Name": "Indian", + "NameN": "هندي" + }, + { + "ID": "IDN", + "Name": "Indonesian", + "NameN": "إندونيسي" + }, + { + "ID": "IRN", + "Name": "Iranian", + "NameN": "إيراني" + }, + { + "ID": "IRQ", + "Name": "Iraqi", + "NameN": "عراقي" + }, + { + "ID": "IRL", + "Name": "Irish", + "NameN": "إيرلندي" + }, + { + "ID": "ITA", + "Name": "Italian", + "NameN": "إيطالي" + }, + { + "ID": "CIV", + "Name": "Ivoirian", + "NameN": "إيفواري" + }, + { + "ID": "JAM", + "Name": "Jamaican", + "NameN": "جامايكي" + }, + { + "ID": "JPN", + "Name": "Japanese", + "NameN": "ياباني" + }, + { + "ID": "JOR", + "Name": "Jordanian", + "NameN": "أردني" + }, + { + "ID": "KAZ", + "Name": "Kazakhstani", + "NameN": "كازاخستاني" + }, + { + "ID": "KEN", + "Name": "Kenyan", + "NameN": "كيني" + }, + { + "ID": "KGZ", + "Name": "Kirghiz", + "NameN": "قيرغيزي" + }, + { + "ID": "KOR", + "Name": "Korean", + "NameN": "كوري" + }, + { + "ID": "PRK", + "Name": "Korean", + "NameN": "كوري" + }, + { + "ID": "KWT", + "Name": "Kuwaiti", + "NameN": "كويتي" + }, + { + "ID": "LAO", + "Name": "Laotian", + "NameN": "لاوسي" + }, + { + "ID": "LVA", + "Name": "Latvian", + "NameN": "لاتفي" + }, + { + "ID": "LBN", + "Name": "Lebanese", + "NameN": "لبناني" + }, + { + "ID": "LBR", + "Name": "Liberian", + "NameN": "ليبيري" + }, + { + "ID": "LBY", + "Name": "Libyan", + "NameN": "ليبي" + }, + { + "ID": "LIE", + "Name": "Liechtensteiner", + "NameN": "ليختنشتايني" + }, + { + "ID": "LTU", + "Name": "Lithuanian", + "NameN": "ليتواني" + }, + { + "ID": "LUX", + "Name": "Luxembourger", + "NameN": "لوكسمبورغي" + }, + { + "ID": "MKD", + "Name": "Macedonia, the former Yugoslav Republic of", + "NameN": "مقدوني، جمهورية يوغوسلافيا السابقة" + }, + { + "ID": "MYT", + "Name": "Mahorais", + "NameN": "مايوتي" + }, + { + "ID": "MDG", + "Name": "Malagasy", + "NameN": "مدغشقري" + }, + { + "ID": "MWI", + "Name": "Malawian", + "NameN": "مالاوي" + }, + { + "ID": "MYS", + "Name": "Malaysian", + "NameN": "ماليزي" + }, + { + "ID": "MDV", + "Name": "Maldivan", + "NameN": "مالديفي" + }, + { + "ID": "MLI", + "Name": "Malian", + "NameN": "مالي" + }, + { + "ID": "MLT", + "Name": "Maltese", + "NameN": "مالطي" + }, + { + "ID": "MHL", + "Name": "Marshallese", + "NameN": "مارشالي" + }, + { + "ID": "MTQ", + "Name": "Martiniquais", + "NameN": "مارتينيكي" + }, + { + "ID": "MRT", + "Name": "Mauritanian", + "NameN": "موريتاني" + }, + { + "ID": "MUS", + "Name": "Mauritian", + "NameN": "موريشيوسي" + }, + { + "ID": "MEX", + "Name": "Mexican", + "NameN": "مكسيكي" + }, + { + "ID": "FSM", + "Name": "Micronesian", + "NameN": "ميكرونيزي" + }, + { + "ID": "MDA", + "Name": "Moldovian", + "NameN": "مولدوفي" + }, + { + "ID": "MCO", + "Name": "Monegasque or Monacan", + "NameN": "موناكي" + }, + { + "ID": "MNG", + "Name": "Mongolian", + "NameN": "منغولي" + }, + { + "ID": "MNE", + "Name": "Montenegrin", + "NameN": "جبل أسودي" + }, + { + "ID": "MSR", + "Name": "Montserratian", + "NameN": "مونتسيراتي" + }, + { + "ID": "MAR", + "Name": "Moroccan", + "NameN": "مغربي" + }, + { + "ID": "LSO", + "Name": "Mosotho", + "NameN": "ليسوتي" + }, + { + "ID": "MOZ", + "Name": "Mozambican", + "NameN": "موزمبيقي" + }, + { + "ID": "NAM", + "Name": "Namibian", + "NameN": "ناميبي" + }, + { + "ID": "NRU", + "Name": "Nauruan", + "NameN": "ناوروي" + }, + { + "ID": "NPL", + "Name": "Nepalese", + "NameN": "نيبالي" + }, + { + "ID": "NCL", + "Name": "New Caledonian", + "NameN": "كاليدوني جديد" + }, + { + "ID": "NZL", + "Name": "New Zealand", + "NameN": "نيوزيلندي" + }, + { + "ID": "VUT", + "Name": "Ni- Vanuatu", + "NameN": "فانواتي" + }, + { + "ID": "NIC", + "Name": "Nicaraguan", + "NameN": "نيكاراغوي" + }, + { + "ID": "NGA", + "Name": "Nigerian", + "NameN": "نيجيري" + }, + { + "ID": "NER", + "Name": "Nigerien", + "NameN": "نيجري" + }, + { + "ID": "NIU", + "Name": "Niuean", + "NameN": "نيوي" + }, + { + "ID": "VAT", + "Name": "None (Vatican City)", + "NameN": "لا شيء (مدينة الفاتيكان)" + }, + { + "ID": "NFK", + "Name": "Norfolk Islander", + "NameN": "جزيرة نورفولك" + }, + { + "ID": "MNP", + "Name": "Northern Mariana Islander", + "NameN": "جزر ماريانا الشمالية" + }, + { + "ID": "NOR", + "Name": "Norwegean", + "NameN": "نرويجي" + }, + { + "ID": "OMN", + "Name": "Omani", + "NameN": "عماني" + }, + { + "ID": "OTH", + "Name": "OTHERS", + "NameN": "آخرون" + }, + { + "ID": "PAK", + "Name": "Pakistani", + "NameN": "باكستاني" + }, + { + "ID": "PLW", + "Name": "Palauan", + "NameN": "بالاوي" + }, + { + "ID": "PSE", + "Name": "Palestinian", + "NameN": "فلسطيني" + }, + { + "ID": "PAN", + "Name": "Panamanian", + "NameN": "بنمي" + }, + { + "ID": "PNG", + "Name": "Papua New Guinean", + "NameN": "بابوا غيني جديد" + }, + { + "ID": "PRY", + "Name": "Paraguayan", + "NameN": "باراغوايي" + }, + { + "ID": "PER", + "Name": "Peruvian", + "NameN": "بيروفي" + }, + { + "ID": "PCN", + "Name": "Pitcairn Islander", + "NameN": "جزر بيتكايرن" + }, + { + "ID": "POL", + "Name": "Polish", + "NameN": "بولندي" + }, + { + "ID": "PRT", + "Name": "Portuguese", + "NameN": "برتغالي" + }, + { + "ID": "PRI", + "Name": "Puerto Rican", + "NameN": "بورتوريكي" + }, + { + "ID": "QAT", + "Name": "Qatari", + "NameN": "قطري" + }, + { + "ID": "RKS", + "Name": "Republic of Kosovo", + "NameN": "جمهورية كوسوفو" + }, + { + "ID": "REU", + "Name": "Reunionese", + "NameN": "ريونيوني" + }, + { + "ID": "ROU", + "Name": "Romanian", + "NameN": "روماني" + }, + { + "ID": "RUS", + "Name": "Russian", + "NameN": "روسي" + }, + { + "ID": "RWA", + "Name": "Rwandan", + "NameN": "رواندي" + }, + { + "ID": "ESH", + "Name": "Sahrawi", + "NameN": "صحراوي" + }, + { + "ID": "SLV", + "Name": "Salvadoran", + "NameN": "سلفادوري" + }, + { + "ID": "SMR", + "Name": "Sammarinese", + "NameN": "سان مارينو" + }, + { + "ID": "WSM", + "Name": "Samoan", + "NameN": "ساموي" + }, + { + "ID": "STP", + "Name": "Sao Tomean", + "NameN": "ساو تومي" + }, + { + "ID": "SAU", + "Name": "Saudi", + "NameN": "سعودي" + }, + { + "ID": "SCT", + "Name": "Scottish", + "NameN": "اسكتلندي" + }, + { + "ID": "SEN", + "Name": "Senegalese", + "NameN": "سنغالي" + }, + { + "ID": "SRB", + "Name": "Serbian", + "NameN": "صربي" + }, + { + "ID": "SYC", + "Name": "Seychellois", + "NameN": "سيشيلي" + }, + { + "ID": "SLE", + "Name": "Sierra Leonean", + "NameN": "سيراليوني" + }, + { + "ID": "SGP", + "Name": "Singaporian", + "NameN": "سنغافوري" + }, + { + "ID": "SVK", + "Name": "Slovak", + "NameN": "سلوفاكي" + }, + { + "ID": "SVN", + "Name": "Slovene", + "NameN": "سلوفيني" + }, + { + "ID": "SLB", + "Name": "Solomon Islander", + "NameN": "جزر سليمان" + }, + { + "ID": "SOM", + "Name": "Somalian", + "NameN": "صومالي" + }, + { + "ID": "ZAF", + "Name": "South African", + "NameN": "جنوب أفريقي" + }, + { + "ID": "ESP", + "Name": "Spanish", + "NameN": "إسباني" + }, + { + "ID": "LKA", + "Name": "Sri Lankan", + "NameN": "سريلانكي" + }, + { + "ID": "SHN", + "Name": "St. Helena", + "NameN": "سانت هيلينا" + }, + { + "ID": "KNA", + "Name": "St. Kitts and Nevis", + "NameN": "سانت كيتس ونيفيس" + }, + { + "ID": "LCA", + "Name": "St. Lucia", + "NameN": "سانت لوسيا" + }, + { + "ID": "SPM", + "Name": "St. Pierre and Miquelon", + "NameN": "سانت بيير وميكيلون" + }, + { + "ID": "VCT", + "Name": "St. Vincent and the Grenadines", + "NameN": "سانت فنسنت والغرينادين" + }, + { + "ID": "SDN", + "Name": "Sudanese", + "NameN": "سوداني" + }, + { + "ID": "SUR", + "Name": "Surinamer", + "NameN": "سورينامي" + }, + { + "ID": "SJM", + "Name": "Svalbard and Jan Mayen islander", + "NameN": "سفالبارد وجان ماين" + }, + { + "ID": "SWZ", + "Name": "Swazi", + "NameN": "سوازي" + }, + { + "ID": "SWE", + "Name": "Swedish", + "NameN": "سويدي" + }, + { + "ID": "CHE", + "Name": "Swiss", + "NameN": "سويسري" + }, + { + "ID": "SYR", + "Name": "Syrian", + "NameN": "سوري" + }, + { + "ID": "TWN", + "Name": "Taiwan, Province of China", + "NameN": "تايوان، مقاطعة الصين" + }, + { + "ID": "TJK", + "Name": "Tajik", + "NameN": "طاجيكي" + }, + { + "ID": "TZA", + "Name": "Tanzania, United Republic o", + "NameN": "تنزاني، الجمهورية المتحدة" + }, + { + "ID": "THA", + "Name": "Thai", + "NameN": "تايلندي" + }, + { + "ID": "TLS", + "Name": "Timorese", + "NameN": "تيموري" + }, + { + "ID": "TGO", + "Name": "Togolese", + "NameN": "توغولي" + }, + { + "ID": "TKL", + "Name": "Tokelauan", + "NameN": "توكيلاوي" + }, + { + "ID": "TON", + "Name": "Tongan", + "NameN": "تونغي" + }, + { + "ID": "TTO", + "Name": "Trinidadian", + "NameN": "ترينيدادي" + }, + { + "ID": "TUN", + "Name": "Tunisian", + "NameN": "تونسي" + }, + { + "ID": "TCA", + "Name": "Turk", + "NameN": "تركي" + }, + { + "ID": "TUR", + "Name": "Turkish", + "NameN": "تركي" + }, + { + "ID": "TKM", + "Name": "Turkmen", + "NameN": "تركماني" + }, + { + "ID": "TUV", + "Name": "Tuvaluan", + "NameN": "توفالوي" + }, + { + "ID": "UGA", + "Name": "Ugandan", + "NameN": "أوغندي" + }, + { + "ID": "UKR", + "Name": "Ukrainian", + "NameN": "أوكراني" + }, + { + "ID": "URY", + "Name": "Uruguayan", + "NameN": "أوروغوايي" + }, + { + "ID": "UZB", + "Name": "Uzbek", + "NameN": "أوزبكي" + }, + { + "ID": "VEN", + "Name": "Venenzuelan", + "NameN": "فنزويلي" + }, + { + "ID": "VNM", + "Name": "Vietnamese", + "NameN": "فيتنامي" + }, + { + "ID": "VIR", + "Name": "Virgin Islander", + "NameN": "جزر عذراء" + }, + { + "ID": "WLF", + "Name": "Wallis and Futuna Islander", + "NameN": "واليس وفوتونا" + }, + { + "ID": "YEM", + "Name": "Yemeni", + "NameN": "يمني" + }, + { + "ID": "ZMB", + "Name": "Zambian", + "NameN": "زامبي" + }, + { + "ID": "ZWE", + "Name": "Zimbabwean", + "NameN": "زيمبابوي" + } +] \ No newline at end of file diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 64477a3..2c7ac9b 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -776,8 +776,13 @@ "validPassportNumber": "يرجى إدخال رقم جواز سفر صالح", "continuePlan": "متابعة خطة العلاج؟", "aboutApp": "حول التطبيق", - "loginOrRegister": "تسجيل الدخول أو التسجيل", "dontHaveAccount": "ليس لديك حساب؟", + "loginOrRegister": "تسجيل الدخول أو التسجيل", + "myFiles" : "ملفاتي", + "resultsPending": "النتائج معلقة", + "resultsAvailable": "النتائج متاحة", + "viewReport": "عرض التقرير", + "prescriptionDeliveryError": "هذه العيادة لا تدعم إعادة التعبئة والتسليم.", "receiveOtpToast": "أين تود تلقي رمز التحقق OTP؟", "enterPhoneNumber": "أدخل رقم الهاتف", "enterEmailDesc": "أدخل عنوان بريدك الإلكتروني لإكمال عملية إنشاء ملف طبي", @@ -790,5 +795,13 @@ "notice": "إشعار", "oR": "أو", "sendOTPWHATSAPP": "أرسل لي OTP عبر واتساب", - "sendOTPSMS": "أرسل لي OTP عبر الرسائل القصيرة" + "sendOTPSMS": "أرسل لي OTP عبر الرسائل القصيرة", + "fullName": "الاسم الكامل", + "married": "متزوج", + "uae": "الإمارات العربية المتحدة", + "malE": "ذكر", + "loginBy": "تسجيل الدخول بواسطة", + "loginByOTP": "تسجيل الدخول بواسطة OTP", + "guest": "زائر", + "switchAccount": "تبديل الحساب" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index cd15a5a..d01d9f6 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -665,7 +665,7 @@ "healthWeatherIndicators": "Health Weather Indicators", "healthTipsBasedOnCurrentWeather": "Health Tips Based On Current Weather", "moreDetails": "More details", - "resendOrder": "Refill & Delivery", + "resendOrder": "Refill and Delivery", "ports": "Ports", "way": "Way", "dailyDoses": "Daily Doses", @@ -779,6 +779,11 @@ "pleaseChooseOption": "Please select from the below options to receive OTP", "dontHaveAccount": "Don't have an account?", "loginOrRegister": "Login or Register", + "myFiles": "My Files", + "resultsPending": "Results Pending", + "resultsAvailable": "Results Available", + "viewReport": "View Report", + "prescriptionDeliveryError": "This clinic doesn't support refill", "prepareToElevate": "Prepared to elevate your health and well-being?", "iAcceptTermsConditions": "I Accept the Terms and Conditions", "alreadyHaveAccount": "Already have an account?", @@ -786,5 +791,13 @@ "notice": "Notice", "oR": "OR", "sendOTPWHATSAPP": "Send me OTP on Whatsapp", - "sendOTPSMS": "Send me OTP on SMS" + "sendOTPSMS": "Send me OTP on SMS", + "fullName": "Full Name", + "married": "Married", + "uae" : "United Arab Emirates", + "malE": "Male", + "loginBy": "Login By", + "loginByOTP": "Login By OTP", + "guest": "Guest", + "switchAccount": "Switch Account" } \ No newline at end of file diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index a7835f4..6e64543 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -177,7 +177,7 @@ class ApiClientImp implements ApiClient { // TODO : we will use all these from appState body['LanguageID'] = body['LanguageID'] ?? "2"; - body['VersionID'] = body['VersionID'] ?? "18.7"; + body['VersionID'] = body['VersionID'] ?? "50.0"; body['Channel'] = body['Channel'] ?? "3"; body['IPAdress'] = body['IPAdress'] ?? "10.20.10.20"; body['generalid'] = body['generalid'] ?? "Cs2020@2016\$2958"; @@ -189,6 +189,7 @@ class ApiClientImp implements ApiClient { : await Utils.isGoogleServicesAvailable() ? "2" : "3"); + body['TokenID'] = "@dm!n"; body.removeWhere((key, value) => value == null); log("body: ${json.encode(body)}"); log("uri: ${Uri.parse(url.trim())}"); diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 1522174..ef7656c 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -17,8 +17,8 @@ var PACKAGES_ORDERS = '/api/orders'; var PACKAGES_ORDER_HISTORY = '/api/orders/items'; var PACKAGES_TAMARA_OPT = '/api/orders/paymentoptions/tamara'; // var BASE_URL = 'http://10.50.100.198:2018/'; -var BASE_URL = 'https://uat.hmgwebservices.com/'; -// var BASE_URL = 'https://hmgwebservices.com/'; +// var BASE_URL = 'https://uat.hmgwebservices.com/'; +var BASE_URL = 'https://hmgwebservices.com/'; // var BASE_URL = 'http://10.201.204.103/'; // var BASE_URL = 'https://orash.cloudsolutions.com.sa/'; // var BASE_URL = 'https://vidauat.cloudsolutions.com.sa/'; diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index 0e7d944..bd3e541 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -59,6 +59,22 @@ class AppAssets { static const String habib_background_icon = '$svgBasePath/habib_logo_background.svg'; static const String show_icon = '$svgBasePath/show_icon.svg'; static const String recharge_icon = '$svgBasePath/recharge_icon.svg'; + static const String eye_result_icon = '$svgBasePath/eye_results_icon.svg'; + static const String search_icon = '$svgBasePath/search_icon.svg'; + static const String view_report_icon = '$svgBasePath/view_report_icon.svg'; + static const String forward_arrow_icon = '$svgBasePath/forward_arrow_icon.svg'; + static const String prescription_refill_icon = '$svgBasePath/prescription_refill_icon.svg'; + static const String rating_icon = '$svgBasePath/rating_icon.svg'; + static const String doctor_calendar_icon = '$svgBasePath/doctor_calendar_icon.svg'; + static const String prescription_remarks_icon = '$svgBasePath/prescription_remarks_icon.svg'; + + //bottom navigation// + + static const String homeBottom = '$svgBasePath/home_bottom.svg'; + static const String bookAppoBottom = '$svgBasePath/book_appo_bottom.svg'; + static const String myFilesBottom = '$svgBasePath/my_files_bottom.svg'; + static const String toDoBottom = '$svgBasePath/todo_bottom.svg'; + static const String servicesBottom = '$svgBasePath/services_bottom.svg'; // PNGS // static const String hmg_logo = '$pngBasePath/hmg_logo.png'; diff --git a/lib/core/common_models/nationality_country_model.dart b/lib/core/common_models/nationality_country_model.dart new file mode 100644 index 0000000..fb3961b --- /dev/null +++ b/lib/core/common_models/nationality_country_model.dart @@ -0,0 +1,29 @@ +import 'dart:convert'; + +class NationalityCountries { + String? id; + String? name; + String? nameN; + + NationalityCountries({ + this.id, + this.name, + this.nameN, + }); + + factory NationalityCountries.fromRawJson(String str) => NationalityCountries.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory NationalityCountries.fromJson(Map json) => NationalityCountries( + id: json["ID"], + name: json["Name"], + nameN: json["NameN"], + ); + + Map toJson() => { + "ID": id, + "Name": name, + "NameN": nameN, + }; +} diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index ee20a60..721d01d 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -7,7 +7,13 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_repo. import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart'; import 'package:hmg_patient_app_new/features/common/common_repo.dart'; +import 'package:hmg_patient_app_new/features/lab/lab_repo.dart'; +import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart'; +import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart'; +import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; +import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart'; +import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart'; import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart'; import 'package:hmg_patient_app_new/services/cache_service.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart'; @@ -64,9 +70,34 @@ class AppDependencies { getIt.registerLazySingleton(() => AuthenticationRepoImp(loggerService: getIt(), apiClient: getIt())); getIt.registerLazySingleton(() => BookAppointmentsRepoImp(loggerService: getIt(), apiClient: getIt())); getIt.registerLazySingleton(() => MyAppointmentsRepoImp(loggerService: getIt(), apiClient: getIt())); + getIt.registerLazySingleton(() => LabRepoImp(loggerService: getIt(), apiClient: getIt())); + getIt.registerLazySingleton(() => RadiologyRepoImp(loggerService: getIt(), apiClient: getIt())); + getIt.registerLazySingleton(() => PrescriptionsRepoImp(loggerService: getIt(), apiClient: getIt())); // ViewModels // Global/shared VMs → LazySingleton + + getIt.registerLazySingleton( + () => LabViewModel( + labRepo: getIt(), + errorHandlerService: getIt(), + ), + ); + + getIt.registerLazySingleton( + () => RadiologyViewModel( + radiologyRepo: getIt(), + errorHandlerService: getIt(), + ), + ); + + getIt.registerLazySingleton( + () => PrescriptionsViewModel( + prescriptionsRepo: getIt(), + errorHandlerService: getIt(), + ), + ); + getIt.registerLazySingleton( () => AuthenticationViewModel( authenticationRepo: getIt(), diff --git a/lib/core/enums.dart b/lib/core/enums.dart index 77a17bc..874dff4 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -41,6 +41,51 @@ enum ChipTypeEnum { success, error, alert, info, warning } enum OTPTypeEnum { sms, whatsapp } +enum LoginTypeEnum { sms, whatsapp, face, fingerprint } + +extension LoginTypeExtension on LoginTypeEnum { + int get toInt { + switch (this) { + case LoginTypeEnum.sms: + return 1; + case LoginTypeEnum.whatsapp: + return 4; + case LoginTypeEnum.face: + return 3; + case LoginTypeEnum.fingerprint: + return 2; + } + } + + String get displayName { + switch (this) { + case LoginTypeEnum.sms: + return 'SMS'; + case LoginTypeEnum.whatsapp: + return 'WhatsApp'; + case LoginTypeEnum.face: + return 'Face Recognition'; + case LoginTypeEnum.fingerprint: + return 'Fingerprint'; + } + } + + static LoginTypeEnum? fromValue(int value) { + switch (value) { + case 1: + return LoginTypeEnum.sms; + case 2: + return LoginTypeEnum.fingerprint; + case 3: + return LoginTypeEnum.face; + case 4: + return LoginTypeEnum.whatsapp; + default: + return null; + } + } +} + extension OTPTypeEnumExtension on OTPTypeEnum { /// Convert enum to int int toInt() { diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index cbfac11..4d813f0 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -206,19 +206,25 @@ extension EmailValidator on String { Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) => Text( this, maxLines: maxlines, - style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 21.fSize, letterSpacing: -0.4, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)), + style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 21.fSize, letterSpacing: -0.4, fontWeight: weight ?? (isBold ? FontWeight.w600 : FontWeight.normal)), ); Widget toText22({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - style: TextStyle(height: 1, color: color ?? AppColors.blackColor, fontSize: 22.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + style: TextStyle(height: 1, color: color ?? AppColors.blackColor, fontSize: 22.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), ); Widget toText24({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - style: TextStyle(height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + style: TextStyle(height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), + ); + + Widget toText26({Color? color, bool isBold = false, bool isCenter = false, double height = 23/26}) => Text( + this, + textAlign: isCenter ? TextAlign.center : null, + style: TextStyle(height: height, color: color ?? AppColors.blackColor, fontSize: 26.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), ); Widget toText28({Color? color, bool isBold = false, bool isCenter = false, TextScaler? textScaler}) => Text( @@ -231,20 +237,18 @@ extension EmailValidator on String { Widget toText32({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), ); Widget toText36({Color? color, bool isBold = false, bool isCenter = false}) => Text( - this, - textAlign: isCenter ? TextAlign.center : null, - style: TextStyle(height: 47 / 36, color: color ?? AppColors.blackColor, fontSize: 36.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), - ); - - + this, + textAlign: isCenter ? TextAlign.center : null, + style: TextStyle(height: 47 / 36, color: color ?? AppColors.blackColor, fontSize: 36.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), + ); Widget toText44({Color? color, bool isBold = false}) => Text( this, - style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 44.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 44.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), ); Widget toSectionHeading({String upperHeading = "", String lowerHeading = ""}) { @@ -383,7 +387,6 @@ class FontUtils { } } - extension CountryExtension on CountryEnum { String get displayName { switch (this) { @@ -435,3 +438,103 @@ extension CountryExtension on CountryEnum { } } +extension GenderTypeExtension on GenderTypeEnum { + String get value => this == GenderTypeEnum.male ? "M" : "F"; + + String get type => this == GenderTypeEnum.male ? "Male" : "Female"; + + String get typeAr => this == GenderTypeEnum.male ? "ذكر" : "أنثى"; + + static GenderTypeEnum? fromValue(String? value) { + switch (value) { + case "M": + return GenderTypeEnum.male; + case "F": + return GenderTypeEnum.female; + default: + return null; + } + } + + static GenderTypeEnum? fromType(String? type) { + switch (type) { + case "Male": + return GenderTypeEnum.male; + case "Female": + return GenderTypeEnum.female; + default: + return null; + } + } +} + +extension MaritalStatusTypeExtension on MaritalStatusTypeEnum { + String get value { + switch (this) { + case MaritalStatusTypeEnum.single: + return "U"; + case MaritalStatusTypeEnum.married: + return "M"; + case MaritalStatusTypeEnum.divorced: + return "D"; + case MaritalStatusTypeEnum.widowed: + return "W"; + } + } + + String get type { + switch (this) { + case MaritalStatusTypeEnum.single: + return "Single"; + case MaritalStatusTypeEnum.married: + return "Married"; + case MaritalStatusTypeEnum.divorced: + return "Divorced"; + case MaritalStatusTypeEnum.widowed: + return "Widowed"; + } + } + + String get typeAr { + switch (this) { + case MaritalStatusTypeEnum.single: + return "أعزب"; + case MaritalStatusTypeEnum.married: + return "متزوج"; + case MaritalStatusTypeEnum.divorced: + return "مطلق"; + case MaritalStatusTypeEnum.widowed: + return "أرمل"; + } + } + + static MaritalStatusTypeEnum? fromValue(String? value) { + switch (value) { + case "U": + return MaritalStatusTypeEnum.single; + case "M": + return MaritalStatusTypeEnum.married; + case "D": + return MaritalStatusTypeEnum.divorced; + case "W": + return MaritalStatusTypeEnum.widowed; + default: + return null; + } + } + + static MaritalStatusTypeEnum? fromType(String? type) { + switch (type) { + case "Single": + return MaritalStatusTypeEnum.single; + case "Married": + return MaritalStatusTypeEnum.married; + case "Divorced": + return MaritalStatusTypeEnum.divorced; + case "Widowed": + return MaritalStatusTypeEnum.widowed; + default: + return null; + } + } +} diff --git a/lib/extensions/widget_extensions.dart b/lib/extensions/widget_extensions.dart index e6913d2..d1a3db4 100644 --- a/lib/extensions/widget_extensions.dart +++ b/lib/extensions/widget_extensions.dart @@ -78,7 +78,14 @@ extension WidgetExtensions on Widget { } Widget objectContainerBorderView( - {String title = "", String note = "", bool disablePadding = false, double radius = 20, Color? color, Color borderColor = AppColors.buttonColor, bool disableWidth = false, bool isAlignment = false}) { + {String title = "", + String note = "", + bool disablePadding = false, + double radius = 20, + Color? color, + Color borderColor = AppColors.buttonColor, + bool disableWidth = false, + bool isAlignment = false}) { return Container( padding: disablePadding ? EdgeInsets.zero : const EdgeInsets.only(top: 15, bottom: 15, left: 14, right: 14), decoration: BoxDecoration( @@ -127,6 +134,7 @@ extension SmoothContainerExtension on ShapeBorder { bool isDisabled = false, Color? backgroundColor, BorderSide? side, + bool hasShadow = false, }) { final bgColor = backgroundColor ?? color; return ShapeDecoration( @@ -136,15 +144,19 @@ extension SmoothContainerExtension on ShapeBorder { smoothness: 1, side: side ?? BorderSide.none, ), + shadows: hasShadow + ? [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 32, + offset: const Offset(0, 0), + ) + ] + : [], ); } } - - - - - //Height Spacers in percentages Widget heightSpacer02per() => SizedBox(height: 0.2.h); @@ -191,8 +203,6 @@ Widget widthSpacer4per() => SizedBox(height: 4.w); Widget widthSpacer5per() => SizedBox(height: 5.w); - - extension ChipTypeEnumExtension on ChipTypeEnum { Color get color { switch (this) { @@ -223,4 +233,4 @@ extension ChipTypeEnumExtension on ChipTypeEnum { return AppColors.warningLightColor; // Replace with your actual color } } -} \ No newline at end of file +} diff --git a/lib/features/authentication/widgets/otp_verification_screen.dart b/lib/features/authentication/widgets/otp_verification_screen.dart index 5ff4fdf..9ea1b9e 100644 --- a/lib/features/authentication/widgets/otp_verification_screen.dart +++ b/lib/features/authentication/widgets/otp_verification_screen.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/presentation/authentication/register_step2.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; @@ -220,6 +221,8 @@ class _OTPVerificationScreenState extends State { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Verifying OTP: $otp')), ); + + Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => RegisterNewStep2(null, {"nationalID": "12345678654321"}))); } /// Auto fill OTP into text fields diff --git a/lib/features/lab/lab_repo.dart b/lib/features/lab/lab_repo.dart new file mode 100644 index 0000000..326379c --- /dev/null +++ b/lib/features/lab/lab_repo.dart @@ -0,0 +1,78 @@ +import 'package:hmg_patient_app_new/core/api/api_client.dart'; +import 'package:hmg_patient_app_new/core/api_consts.dart'; +import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; +import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; +import 'package:dartz/dartz.dart'; +import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; + +abstract class LabRepo { + Future>>> getPatientLabOrders({required String patientId}); +} + +class LabRepoImp implements LabRepo { + final ApiClient apiClient; + final LoggerService loggerService; + + LabRepoImp({required this.loggerService, required this.apiClient}); + + @override + Future>>> getPatientLabOrders({required String patientId}) async { + final mapDevice = { + "isDentalAllowedBackend": false, + "VersionID": 50.0, + "Channel": 3, + "LanguageID": 2, + "IPAdress": "10.20.10.20", + "generalid": "Cs2020@2016\$2958", + "Latitude": 0.0, + "Longitude": 0.0, + "DeviceTypeID": 1, + "PatientType": 1, + "PatientTypeID": 1, + "TokenID": "@dm!n", + "PatientID": "1018977", + "PatientOutSA": "0", + "SessionID": "03478TYC02N80874CTYN04883475!?" + }; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_Patient_LAB_ORDERS, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus}) { + try { + final list = response['ListPLO']; + if (list == null || list.isEmpty) { + throw Exception("lab list is empty"); + } + + final labOrders = list + .map((item) => PatientLabOrdersResponseModel.fromJson(item as Map)) + .toList() + .cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: labOrders, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } +} diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart new file mode 100644 index 0000000..acd87eb --- /dev/null +++ b/lib/features/lab/lab_view_model.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/features/lab/lab_repo.dart'; +import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; +import 'package:hmg_patient_app_new/services/error_handler_service.dart'; + +class LabViewModel extends ChangeNotifier { + bool isLabOrdersLoading = false; + bool isLabResultsLoading = false; + + LabRepo labRepo; + ErrorHandlerService errorHandlerService; + + List patientLabOrders = []; + + LabViewModel({required this.labRepo, required this.errorHandlerService}); + + initLabProvider() { + patientLabOrders.clear(); + isLabOrdersLoading = true; + isLabResultsLoading = true; + getPatientLabOrders(); + notifyListeners(); + } + + Future getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await labRepo.getPatientLabOrders(patientId: "1231755"); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + patientLabOrders = apiResponse.data!; + isLabOrdersLoading = false; + isLabResultsLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } +} diff --git a/lib/features/lab/models/resp_models/patient_lab_orders_response_model.dart b/lib/features/lab/models/resp_models/patient_lab_orders_response_model.dart new file mode 100644 index 0000000..11bf573 --- /dev/null +++ b/lib/features/lab/models/resp_models/patient_lab_orders_response_model.dart @@ -0,0 +1,245 @@ +class PatientLabOrdersResponseModel { + int? actualDoctorRate; + dynamic? admissionDate; + dynamic? admissionNumber; + dynamic? appointmentDate; + dynamic? appointmentNo; + dynamic? appointmentTime; + String? clinicDescription; + String? clinicDescriptionEnglish; + dynamic? clinicDescriptionN; + int? clinicID; + String? createdOn; + num? decimalDoctorRate; + int? doctorID; + String? doctorImageURL; + String? doctorName; + String? doctorNameEnglish; + dynamic? doctorNameN; + int? doctorRate; + num? doctorStarsRate; + String? doctorTitle; + int? gender; + String? genderDescription; + String? invoiceNo; + dynamic? invoiceNoVP; + String? invoiceType; + bool? isActiveDoctorProfile; + bool? isDoctorAllowVedioCall; + bool? isDrReviewReq; + bool? isExecludeDoctor; + bool? isInOutPatient; + String? isInOutPatientDescription; + String? isInOutPatientDescriptionN; + bool? isLiveCareAppointment; + bool? isRead; + bool? isSendEmail; + String? nationalityFlagURL; + int? noOfPatientsRate; + String? orderDate; + String? orderNo; + dynamic? orderProjectID; + String? patientID; + String? projectID; + String? projectName; + dynamic? projectNameN; + String? qR; + String? setupID; + // List? speciality; + int? status; + String? statusDesc; + String? strOrderDate; + List? testDetails; + + PatientLabOrdersResponseModel( + {this.actualDoctorRate, + this.admissionDate, + this.admissionNumber, + this.appointmentDate, + this.appointmentNo, + this.appointmentTime, + this.clinicDescription, + this.clinicDescriptionEnglish, + this.clinicDescriptionN, + this.clinicID, + this.createdOn, + this.decimalDoctorRate, + this.doctorID, + this.doctorImageURL, + this.doctorName, + this.doctorNameEnglish, + this.doctorNameN, + this.doctorRate, + this.doctorStarsRate, + this.doctorTitle, + this.gender, + this.genderDescription, + this.invoiceNo, + this.invoiceNoVP, + this.invoiceType, + this.isActiveDoctorProfile, + this.isDoctorAllowVedioCall, + this.isDrReviewReq, + this.isExecludeDoctor, + this.isInOutPatient, + this.isInOutPatientDescription, + this.isInOutPatientDescriptionN, + this.isLiveCareAppointment, + this.isRead, + this.isSendEmail, + this.nationalityFlagURL, + this.noOfPatientsRate, + this.orderDate, + this.orderNo, + this.orderProjectID, + this.patientID, + this.projectID, + this.projectName, + this.projectNameN, + this.qR, + this.setupID, + // this.speciality, + this.status, + this.statusDesc, + this.strOrderDate, + this.testDetails}); + + PatientLabOrdersResponseModel.fromJson(Map json) { + actualDoctorRate = json['ActualDoctorRate']; + admissionDate = json['AdmissionDate']; + admissionNumber = json['AdmissionNumber']; + appointmentDate = json['AppointmentDate']; + appointmentNo = json['AppointmentNo']; + appointmentTime = json['AppointmentTime']; + clinicDescription = json['ClinicDescription']; + clinicDescriptionEnglish = json['ClinicDescriptionEnglish']; + clinicDescriptionN = json['ClinicDescriptionN']; + clinicID = json['ClinicID']; + createdOn = json['CreatedOn']; + decimalDoctorRate = json['DecimalDoctorRate']; + doctorID = json['DoctorID']; + doctorImageURL = json['DoctorImageURL']; + doctorName = json['DoctorName']; + doctorNameEnglish = json['DoctorNameEnglish']; + doctorNameN = json['DoctorNameN']; + doctorRate = json['DoctorRate']; + doctorStarsRate = json['DoctorStarsRate']; + doctorTitle = json['DoctorTitle']; + gender = json['Gender']; + genderDescription = json['GenderDescription']; + invoiceNo = json['InvoiceNo']; + invoiceNoVP = json['InvoiceNo_VP']; + invoiceType = json['InvoiceType']; + isActiveDoctorProfile = json['IsActiveDoctorProfile']; + isDoctorAllowVedioCall = json['IsDoctorAllowVedioCall']; + isDrReviewReq = json['IsDrReviewReq']; + isExecludeDoctor = json['IsExecludeDoctor']; + isInOutPatient = json['IsInOutPatient']; + isInOutPatientDescription = json['IsInOutPatientDescription']; + isInOutPatientDescriptionN = json['IsInOutPatientDescriptionN']; + isLiveCareAppointment = json['IsLiveCareAppointment']; + isRead = json['IsRead']; + isSendEmail = json['IsSendEmail']; + nationalityFlagURL = json['NationalityFlagURL']; + noOfPatientsRate = json['NoOfPatientsRate']; + orderDate = json['OrderDate']; + orderNo = json['OrderNo']; + orderProjectID = json['OrderProjectID']; + patientID = json['PatientID']; + projectID = json['ProjectID']; + projectName = json['ProjectName']; + projectNameN = json['ProjectNameN']; + qR = json['QR']; + setupID = json['SetupID']; + // speciality = json['Speciality'].cast(); + status = json['Status']; + statusDesc = json['StatusDesc']; + strOrderDate = json['StrOrderDate']; + if (json['TestDetails'] != dynamic) { + testDetails = []; + json['TestDetails'].forEach((v) { + testDetails!.add(new TestDetails.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + data['ActualDoctorRate'] = this.actualDoctorRate; + data['AdmissionDate'] = this.admissionDate; + data['AdmissionNumber'] = this.admissionNumber; + data['AppointmentDate'] = this.appointmentDate; + data['AppointmentNo'] = this.appointmentNo; + data['AppointmentTime'] = this.appointmentTime; + data['ClinicDescription'] = this.clinicDescription; + data['ClinicDescriptionEnglish'] = this.clinicDescriptionEnglish; + data['ClinicDescriptionN'] = this.clinicDescriptionN; + data['ClinicID'] = this.clinicID; + data['CreatedOn'] = this.createdOn; + data['DecimalDoctorRate'] = this.decimalDoctorRate; + data['DoctorID'] = this.doctorID; + data['DoctorImageURL'] = this.doctorImageURL; + data['DoctorName'] = this.doctorName; + data['DoctorNameEnglish'] = this.doctorNameEnglish; + data['DoctorNameN'] = this.doctorNameN; + data['DoctorRate'] = this.doctorRate; + data['DoctorStarsRate'] = this.doctorStarsRate; + data['DoctorTitle'] = this.doctorTitle; + data['Gender'] = this.gender; + data['GenderDescription'] = this.genderDescription; + data['InvoiceNo'] = this.invoiceNo; + data['InvoiceNo_VP'] = this.invoiceNoVP; + data['InvoiceType'] = this.invoiceType; + data['IsActiveDoctorProfile'] = this.isActiveDoctorProfile; + data['IsDoctorAllowVedioCall'] = this.isDoctorAllowVedioCall; + data['IsDrReviewReq'] = this.isDrReviewReq; + data['IsExecludeDoctor'] = this.isExecludeDoctor; + data['IsInOutPatient'] = this.isInOutPatient; + data['IsInOutPatientDescription'] = this.isInOutPatientDescription; + data['IsInOutPatientDescriptionN'] = this.isInOutPatientDescriptionN; + data['IsLiveCareAppointment'] = this.isLiveCareAppointment; + data['IsRead'] = this.isRead; + data['IsSendEmail'] = this.isSendEmail; + data['NationalityFlagURL'] = this.nationalityFlagURL; + data['NoOfPatientsRate'] = this.noOfPatientsRate; + data['OrderDate'] = this.orderDate; + data['OrderNo'] = this.orderNo; + data['OrderProjectID'] = this.orderProjectID; + data['PatientID'] = this.patientID; + data['ProjectID'] = this.projectID; + data['ProjectName'] = this.projectName; + data['ProjectNameN'] = this.projectNameN; + data['QR'] = this.qR; + data['SetupID'] = this.setupID; + // data['Speciality'] = this.speciality; + data['Status'] = this.status; + data['StatusDesc'] = this.statusDesc; + data['StrOrderDate'] = this.strOrderDate; + if (this.testDetails != dynamic) { + data['TestDetails'] = this.testDetails!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class TestDetails { + String? description; + String? testCode; + String? testID; + + TestDetails({this.description, this.testCode, this.testID}); + + TestDetails.fromJson(Map json) { + description = json['Description']; + testCode = json['TestCode']; + testID = json['TestID']; + } + + Map toJson() { + final Map data = new Map(); + data['Description'] = this.description; + data['TestCode'] = this.testCode; + data['TestID'] = this.testID; + return data; + } +} diff --git a/lib/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart b/lib/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart new file mode 100644 index 0000000..7b1d879 --- /dev/null +++ b/lib/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart @@ -0,0 +1,174 @@ +class PatientPrescriptionsResponseModel { + String? setupID; + int? projectID; + int? patientID; + int? appointmentNo; + String? appointmentDate; + String? doctorName; + String? clinicDescription; + String? name; + int? episodeID; + num? actualDoctorRate; + int? admission; + int? clinicID; + String? companyName; + num? decimalDoctorRate; + String? despensedStatus; + String? dischargeDate; + int? dischargeNo; + int? doctorID; + String? doctorImageURL; + num? doctorRate; + num? doctorStarsRate; + String? doctorTitle; + int? gender; + String? genderDescription; + bool? isActiveDoctorProfile; + bool? isDoctorAllowVedioCall; + bool? isExecludeDoctor; + bool? isHomeMedicineDeliverySupported; + bool? isInOutPatient; + String? isInOutPatientDescription; + String? isInOutPatientDescriptionN; + bool? isInsurancePatient; + bool? isLiveCareAppointment; + String? nationalityFlagURL; + int? noOfPatientsRate; + String? qR; + + // List? speciality; + String? strAppointmentDate; + + PatientPrescriptionsResponseModel( + {this.setupID, + this.projectID, + this.patientID, + this.appointmentNo, + this.appointmentDate, + this.doctorName, + this.clinicDescription, + this.name, + this.episodeID, + this.actualDoctorRate, + this.admission, + this.clinicID, + this.companyName, + this.decimalDoctorRate, + this.despensedStatus, + this.dischargeDate, + this.dischargeNo, + this.doctorID, + this.doctorImageURL, + this.doctorRate, + this.doctorStarsRate, + this.doctorTitle, + this.gender, + this.genderDescription, + this.isActiveDoctorProfile, + this.isDoctorAllowVedioCall, + this.isExecludeDoctor, + this.isHomeMedicineDeliverySupported, + this.isInOutPatient, + this.isInOutPatientDescription, + this.isInOutPatientDescriptionN, + this.isInsurancePatient, + this.isLiveCareAppointment, + this.nationalityFlagURL, + this.noOfPatientsRate, + this.qR, + // this.speciality, + this.strAppointmentDate}); + + PatientPrescriptionsResponseModel.fromJson(Map json) { + setupID = json['SetupID']; + projectID = json['ProjectID']; + patientID = json['PatientID']; + appointmentNo = json['AppointmentNo']; + appointmentDate = json['AppointmentDate']; + doctorName = json['DoctorName']; + clinicDescription = json['ClinicDescription']; + name = json['Name']; + episodeID = json['EpisodeID']; + actualDoctorRate = json['ActualDoctorRate']; + admission = json['Admission']; + clinicID = json['ClinicID']; + companyName = json['CompanyName']; + decimalDoctorRate = json['DecimalDoctorRate']; + despensedStatus = json['Despensed_Status']; + dischargeDate = json['DischargeDate']; + dischargeNo = json['DischargeNo']; + doctorID = json['DoctorID']; + doctorImageURL = json['DoctorImageURL']; + doctorRate = json['DoctorRate']; + doctorStarsRate = json['DoctorStarsRate']; + doctorTitle = json['DoctorTitle']; + gender = json['Gender']; + genderDescription = json['GenderDescription']; + isActiveDoctorProfile = json['IsActiveDoctorProfile']; + isDoctorAllowVedioCall = json['IsDoctorAllowVedioCall']; + isExecludeDoctor = json['IsExecludeDoctor']; + isHomeMedicineDeliverySupported = json['IsHomeMedicineDeliverySupported']; + isInOutPatient = json['IsInOutPatient']; + isInOutPatientDescription = json['IsInOutPatientDescription']; + isInOutPatientDescriptionN = json['IsInOutPatientDescriptionN']; + isInsurancePatient = json['IsInsurancePatient']; + isLiveCareAppointment = json['IsLiveCareAppointment']; + nationalityFlagURL = json['NationalityFlagURL']; + noOfPatientsRate = json['NoOfPatientsRate']; + qR = json['QR']; + // speciality = json['Speciality'].cast(); + strAppointmentDate = json['StrAppointmentDate']; + } + + Map toJson() { + final Map data = new Map(); + data['SetupID'] = this.setupID; + data['ProjectID'] = this.projectID; + data['PatientID'] = this.patientID; + data['AppointmentNo'] = this.appointmentNo; + data['AppointmentDate'] = this.appointmentDate; + data['DoctorName'] = this.doctorName; + data['ClinicDescription'] = this.clinicDescription; + data['Name'] = this.name; + data['EpisodeID'] = this.episodeID; + data['ActualDoctorRate'] = this.actualDoctorRate; + data['Admission'] = this.admission; + data['ClinicID'] = this.clinicID; + data['CompanyName'] = this.companyName; + data['DecimalDoctorRate'] = this.decimalDoctorRate; + data['Despensed_Status'] = this.despensedStatus; + data['DischargeDate'] = this.dischargeDate; + data['DischargeNo'] = this.dischargeNo; + data['DoctorID'] = this.doctorID; + data['DoctorImageURL'] = this.doctorImageURL; + data['DoctorRate'] = this.doctorRate; + data['DoctorStarsRate'] = this.doctorStarsRate; + data['DoctorTitle'] = this.doctorTitle; + data['Gender'] = this.gender; + data['GenderDescription'] = this.genderDescription; + data['IsActiveDoctorProfile'] = this.isActiveDoctorProfile; + data['IsDoctorAllowVedioCall'] = this.isDoctorAllowVedioCall; + data['IsExecludeDoctor'] = this.isExecludeDoctor; + data['IsHomeMedicineDeliverySupported'] = this.isHomeMedicineDeliverySupported; + data['IsInOutPatient'] = this.isInOutPatient; + data['IsInOutPatientDescription'] = this.isInOutPatientDescription; + data['IsInOutPatientDescriptionN'] = this.isInOutPatientDescriptionN; + data['IsInsurancePatient'] = this.isInsurancePatient; + data['IsLiveCareAppointment'] = this.isLiveCareAppointment; + data['NationalityFlagURL'] = this.nationalityFlagURL; + data['NoOfPatientsRate'] = this.noOfPatientsRate; + data['QR'] = this.qR; + // data['Speciality'] = this.speciality; + data['StrAppointmentDate'] = this.strAppointmentDate; + return data; + } +} + +class PrescriptionsList { + String? filterName = ""; + List? prescriptionsList = []; + + PrescriptionsList({this.filterName, PatientPrescriptionsResponseModel? prescriptions}) { + prescriptionsList!.add(prescriptions!); + } +} diff --git a/lib/features/prescriptions/models/resp_models/prescription_detail_response_model.dart b/lib/features/prescriptions/models/resp_models/prescription_detail_response_model.dart new file mode 100644 index 0000000..3ca8396 --- /dev/null +++ b/lib/features/prescriptions/models/resp_models/prescription_detail_response_model.dart @@ -0,0 +1,144 @@ +class PrescriptionDetailResponseModel { + String? address; + num? appointmentNo; + String? clinic; + dynamic companyName; + num? days; + String? doctorName; + num? doseDailyQuantity; + String? frequency; + num? frequencyNumber; + dynamic image; + dynamic imageExtension; + String? imageSRCUrl; + dynamic imageString; + String? imageThumbUrl; + String? isCovered; + String? itemDescription; + num? itemID; + String? orderDate; + num? patientID; + String? patientName; + String? phoneOffice1; + dynamic prescriptionQR; + num? prescriptionTimes; + dynamic productImage; + dynamic productImageBase64; + String? productImageString; + num? projectID; + String? projectName; + String? remarks; + String? route; + String? sKU; + num? scaleOffset; + String? startDate; + + PrescriptionDetailResponseModel( + {this.address, + this.appointmentNo, + this.clinic, + this.companyName, + this.days, + this.doctorName, + this.doseDailyQuantity, + this.frequency, + this.frequencyNumber, + this.image, + this.imageExtension, + this.imageSRCUrl, + this.imageString, + this.imageThumbUrl, + this.isCovered, + this.itemDescription, + this.itemID, + this.orderDate, + this.patientID, + this.patientName, + this.phoneOffice1, + this.prescriptionQR, + this.prescriptionTimes, + this.productImage, + this.productImageBase64, + this.productImageString, + this.projectID, + this.projectName, + this.remarks, + this.route, + this.sKU, + this.scaleOffset, + this.startDate}); + + PrescriptionDetailResponseModel.fromJson(Map json) { + address = json['Address']; + appointmentNo = json['AppointmentNo']; + clinic = json['Clinic']; + companyName = json['CompanyName']; + days = json['Days']; + doctorName = json['DoctorName']; + doseDailyQuantity = json['DoseDailyQuantity']; + frequency = json['Frequency']; + frequencyNumber = json['FrequencyNumber']; + image = json['Image']; + imageExtension = json['ImageExtension']; + imageSRCUrl = json['ImageSRCUrl']; + imageString = json['ImageString']; + imageThumbUrl = json['ImageThumbUrl']; + isCovered = json['IsCovered']; + itemDescription = json['ItemDescription']; + itemID = json['ItemID']; + orderDate = json['OrderDate']; + patientID = json['PatientID']; + patientName = json['PatientName']; + phoneOffice1 = json['PhoneOffice1']; + prescriptionQR = json['PrescriptionQR']; + prescriptionTimes = json['PrescriptionTimes']; + productImage = json['ProductImage']; + productImageBase64 = json['ProductImageBase64']; + productImageString = json['ProductImageString']; + projectID = json['ProjectID']; + projectName = json['ProjectName']; + remarks = json['Remarks']; + route = json['Route']; + sKU = json['SKU']; + scaleOffset = json['ScaleOffset']; + startDate = json['StartDate']; + } + + Map toJson() { + final Map data = new Map(); + data['Address'] = address; + data['AppointmentNo'] = appointmentNo; + data['Clinic'] = clinic; + data['CompanyName'] = companyName; + data['Days'] = days; + data['DoctorName'] = doctorName; + data['DoseDailyQuantity'] = doseDailyQuantity; + data['Frequency'] = frequency; + data['FrequencyNumber'] = frequencyNumber; + data['Image'] = image; + data['ImageExtension'] = imageExtension; + data['ImageSRCUrl'] = imageSRCUrl; + data['ImageString'] = imageString; + data['ImageThumbUrl'] = imageThumbUrl; + data['IsCovered'] = isCovered; + data['ItemDescription'] = itemDescription; + data['ItemID'] = itemID; + data['OrderDate'] = orderDate; + data['PatientID'] = patientID; + data['PatientName'] = patientName; + data['PhoneOffice1'] = phoneOffice1; + data['PrescriptionQR'] = prescriptionQR; + data['PrescriptionTimes'] = prescriptionTimes; + data['ProductImage'] = productImage; + data['ProductImageBase64'] = productImageBase64; + data['ProductImageString'] = productImageString; + data['ProjectID'] = projectID; + data['ProjectName'] = projectName; + data['Remarks'] = remarks; + data['Route'] = route; + data['SKU'] = sKU; + data['ScaleOffset'] = scaleOffset; + data['StartDate'] = startDate; + return data; + } +} diff --git a/lib/features/prescriptions/prescriptions_repo.dart b/lib/features/prescriptions/prescriptions_repo.dart new file mode 100644 index 0000000..de7bc4b --- /dev/null +++ b/lib/features/prescriptions/prescriptions_repo.dart @@ -0,0 +1,141 @@ +import 'package:hmg_patient_app_new/core/api/api_client.dart'; +import 'package:hmg_patient_app_new/core/api_consts.dart'; +import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; +import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; +import 'package:dartz/dartz.dart'; +import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart'; +import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/prescription_detail_response_model.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; + +abstract class PrescriptionsRepo { + Future>>> getPatientPrescriptionOrders({required String patientId}); + + Future>>> getPatientPrescriptionDetails({required PatientPrescriptionsResponseModel prescriptionsResponseModel}); +} + +class PrescriptionsRepoImp implements PrescriptionsRepo { + final ApiClient apiClient; + final LoggerService loggerService; + + PrescriptionsRepoImp({required this.loggerService, required this.apiClient}); + + @override + Future>>> getPatientPrescriptionOrders({required String patientId}) async { + final mapDevice = { + "isDentalAllowedBackend": false, + "VersionID": 50.0, + "Channel": 3, + "LanguageID": 2, + "IPAdress": "10.20.10.20", + "generalid": "Cs2020@2016\$2958", + "Latitude": 0.0, + "Longitude": 0.0, + "DeviceTypeID": 1, + "PatientType": 1, + "PatientTypeID": 1, + "TokenID": "@dm!n", + "PatientID": "1018977", + "PatientOutSA": "0", + "SessionID": "03478TYC02N80874CTYN04883475!?" + }; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + PRESCRIPTIONS, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus}) { + try { + final list = response['PatientPrescriptionList']; + if (list == null || list.isEmpty) { + throw Exception("lab list is empty"); + } + + final prescriptionOrders = list.map((item) => PatientPrescriptionsResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: prescriptionOrders, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future>>> getPatientPrescriptionDetails({required PatientPrescriptionsResponseModel prescriptionsResponseModel}) async { + final mapDevice = { + "AppointmentNo": prescriptionsResponseModel.appointmentNo.toString(), + "SetupID": prescriptionsResponseModel.setupID, + "EpisodeID": prescriptionsResponseModel.episodeID.toString(), + "ClinicID": prescriptionsResponseModel.clinicID.toString(), + "ProjectID": prescriptionsResponseModel.projectID.toString(), + "DischargeNo": prescriptionsResponseModel.dischargeNo.toString(), + "isDentalAllowedBackend": false, + "VersionID": 50.0, + "Channel": 3, + "LanguageID": 2, + "IPAdress": "10.20.10.20", + "generalid": "Cs2020@2016\$2958", + "Latitude": 0.0, + "Longitude": 0.0, + "DeviceTypeID": 1, + "PatientType": 1, + "PatientTypeID": 1, + "TokenID": "@dm!n", + "PatientID": "1018977", + "PatientOutSA": "0", + "SessionID": "03478TYC02N80874CTYN04883475!?" + }; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + prescriptionsResponseModel.isInOutPatient! ? GET_PRESCRIPTION_REPORT_ENH : GET_PRESCRIPTION_REPORT, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus}) { + try { + final list = prescriptionsResponseModel.isInOutPatient! ? response['ListPRM'] : response['INP_GetPrescriptionReport_List']; + if (list == null || list.isEmpty) { + throw Exception("prescription list is empty"); + } + + final prescriptionOrders = list.map((item) => PrescriptionDetailResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: prescriptionOrders, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } +} diff --git a/lib/features/prescriptions/prescriptions_view_model.dart b/lib/features/prescriptions/prescriptions_view_model.dart new file mode 100644 index 0000000..23a818e --- /dev/null +++ b/lib/features/prescriptions/prescriptions_view_model.dart @@ -0,0 +1,114 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart'; +import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/prescription_detail_response_model.dart'; +import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart'; +import 'package:hmg_patient_app_new/services/error_handler_service.dart'; + +class PrescriptionsViewModel extends ChangeNotifier { + bool isPrescriptionsOrdersLoading = false; + bool isPrescriptionsDetailsLoading = false; + + PrescriptionsRepo prescriptionsRepo; + ErrorHandlerService errorHandlerService; + + // Prescription Orders Lists + List patientPrescriptionOrders = []; + + List patientPrescriptionOrdersByClinic = []; + List patientPrescriptionOrdersByHospital = []; + List patientPrescriptionOrdersViewList = []; + + // Prescription Details List + List prescriptionDetailsList = []; + + bool isSortByClinic = true; + + PrescriptionsViewModel({required this.prescriptionsRepo, required this.errorHandlerService}); + + initPrescriptionsViewModel() { + patientPrescriptionOrders.clear(); + patientPrescriptionOrdersByClinic.clear(); + patientPrescriptionOrdersByHospital.clear(); + patientPrescriptionOrdersViewList.clear(); + isPrescriptionsOrdersLoading = true; + isPrescriptionsDetailsLoading = true; + isSortByClinic = true; + getPatientPrescriptionOrders(); + notifyListeners(); + } + + setPrescriptionsDetailsLoading() { + isPrescriptionsDetailsLoading = true; + prescriptionDetailsList.clear(); + notifyListeners(); + } + + setIsSortByClinic(bool value) { + isSortByClinic = value; + if (isSortByClinic) { + patientPrescriptionOrdersViewList = patientPrescriptionOrdersByClinic; + } else { + patientPrescriptionOrdersViewList = patientPrescriptionOrdersByHospital; + } + notifyListeners(); + } + + Future getPatientPrescriptionOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await prescriptionsRepo.getPatientPrescriptionOrders(patientId: "1231755"); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + patientPrescriptionOrders = apiResponse.data!; + isPrescriptionsOrdersLoading = false; + + for (var element in patientPrescriptionOrders) { + List prescriptionsByClinic = patientPrescriptionOrdersByClinic.where((elementClinic) => elementClinic.filterName == element.clinicDescription).toList(); + + if (prescriptionsByClinic.isNotEmpty) { + patientPrescriptionOrdersByClinic[patientPrescriptionOrdersByClinic.indexOf(prescriptionsByClinic[0])].prescriptionsList!.add(element); + } else { + patientPrescriptionOrdersByClinic.add(PrescriptionsList(filterName: element.clinicDescription, prescriptions: element)); + } + + List prescriptionsByHospital = patientPrescriptionOrdersByHospital.where((elementClinic) => elementClinic.filterName == element.name).toList(); + + if (prescriptionsByHospital.isNotEmpty) { + patientPrescriptionOrdersByHospital[patientPrescriptionOrdersByHospital.indexOf(prescriptionsByHospital[0])].prescriptionsList!.add(element); + } else { + patientPrescriptionOrdersByHospital.add(PrescriptionsList(filterName: element.name, prescriptions: element)); + } + } + patientPrescriptionOrdersViewList = patientPrescriptionOrdersByClinic; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + Future getPrescriptionDetails(PatientPrescriptionsResponseModel prescriptionsResponseModel, {Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await prescriptionsRepo.getPatientPrescriptionDetails(prescriptionsResponseModel: prescriptionsResponseModel); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + prescriptionDetailsList = apiResponse.data!; + isPrescriptionsDetailsLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } +} diff --git a/lib/features/radiology/models/resp_models/patient_radiology_response_model.dart b/lib/features/radiology/models/resp_models/patient_radiology_response_model.dart new file mode 100644 index 0000000..b5568bf --- /dev/null +++ b/lib/features/radiology/models/resp_models/patient_radiology_response_model.dart @@ -0,0 +1,232 @@ +class PatientRadiologyResponseModel { + String? setupID; + int? projectID; + dynamic patientID; + int? invoiceLineItemNo; + int? invoiceNo; + int? doctorID; + int? clinicID; + String? orderDate; + String? reportData; + String? imageURL; + String? procedureID; + int? appointmentNo; + dynamic dIAPacsURL; + bool? isRead; + String? readOn; + dynamic admissionNo; + bool? isInOutPatient; + int? actualDoctorRate; + dynamic admissionDate; + dynamic admissionNumber; + dynamic appointmentDate; + dynamic appointmentNumber; + dynamic appointmentTime; + String? clinicDescription; + String? dIAPACSURL; + dynamic decimalDoctorRate; + String? description; + String? doctorImageURL; + String? doctorName; + num? doctorRate; + num? doctorStarsRate; + String? doctorTitle; + String? examId; + int? gender; + dynamic genderDescription; + int? invoiceNoVP; + String? invoiceType; + bool? isActiveDoctorProfile; + bool? isExecludeDoctor; + String? isInOutPatientDescription; + String? isInOutPatientDescriptionN; + bool? isLiveCareAppointment; + dynamic nationalityFlagURL; + int? noOfPatientsRate; + int? orderNo; + dynamic procedureName; + String? projectName; + String? qR; + String? reportDataHTML; + String? reportDataTextString; + dynamic strAppointmentDate; + dynamic strOrderDate; + bool? isCVI; + bool? isRadMedicalReport; + dynamic vida3Id; + + PatientRadiologyResponseModel( + {this.setupID, + this.projectID, + this.patientID, + this.invoiceLineItemNo, + this.invoiceNo, + this.doctorID, + this.clinicID, + this.orderDate, + this.reportData, + this.imageURL, + this.procedureID, + this.appointmentNo, + this.dIAPacsURL, + this.isRead, + this.readOn, + this.admissionNo, + this.isInOutPatient, + this.actualDoctorRate, + this.admissionDate, + this.admissionNumber, + this.appointmentDate, + this.appointmentNumber, + this.appointmentTime, + this.clinicDescription, + this.dIAPACSURL, + this.decimalDoctorRate, + this.description, + this.doctorImageURL, + this.doctorName, + this.doctorRate, + this.doctorStarsRate, + this.doctorTitle, + this.examId, + this.gender, + this.genderDescription, + this.invoiceNoVP, + this.invoiceType, + this.isActiveDoctorProfile, + this.isExecludeDoctor, + this.isInOutPatientDescription, + this.isInOutPatientDescriptionN, + this.isLiveCareAppointment, + this.nationalityFlagURL, + this.noOfPatientsRate, + this.orderNo, + this.procedureName, + this.projectName, + this.qR, + this.reportDataHTML, + this.reportDataTextString, + this.strAppointmentDate, + this.strOrderDate, + this.isCVI, + this.isRadMedicalReport, + this.vida3Id}); + + PatientRadiologyResponseModel.fromJson(Map json) { + setupID = json['SetupID']; + projectID = json['ProjectID']; + patientID = json['PatientID']; + invoiceLineItemNo = json['InvoiceLineItemNo']; + invoiceNo = json['InvoiceNo']; + doctorID = json['DoctorID']; + clinicID = json['ClinicID']; + orderDate = json['OrderDate']; + reportData = json['ReportData']; + imageURL = json['ImageURL']; + procedureID = json['ProcedureID']; + appointmentNo = json['AppointmentNo']; + dIAPacsURL = json['DIAPacsURL']; + isRead = json['IsRead']; + readOn = json['ReadOn']; + admissionNo = json['AdmissionNo']; + isInOutPatient = json['IsInOutPatient']; + actualDoctorRate = json['ActualDoctorRate']; + admissionDate = json['AdmissionDate']; + admissionNumber = json['AdmissionNumber']; + appointmentDate = json['AppointmentDate']; + appointmentNumber = json['AppointmentNumber']; + appointmentTime = json['AppointmentTime']; + clinicDescription = json['ClinicDescription']; + dIAPACSURL = json['DIA_PACS_URL']; + decimalDoctorRate = json['DecimalDoctorRate']; + description = json['Description']; + doctorImageURL = json['DoctorImageURL']; + doctorName = json['DoctorName']; + doctorRate = json['DoctorRate']; + doctorStarsRate = json['DoctorStarsRate']; + doctorTitle = json['DoctorTitle']; + examId = json['Exam_id']; + gender = json['Gender']; + genderDescription = json['GenderDescription']; + invoiceNoVP = json['InvoiceNo_VP']; + invoiceType = json['InvoiceType']; + isActiveDoctorProfile = json['IsActiveDoctorProfile']; + isExecludeDoctor = json['IsExecludeDoctor']; + isInOutPatientDescription = json['IsInOutPatientDescription']; + isInOutPatientDescriptionN = json['IsInOutPatientDescriptionN']; + isLiveCareAppointment = json['IsLiveCareAppointment']; + nationalityFlagURL = json['NationalityFlagURL']; + noOfPatientsRate = json['NoOfPatientsRate']; + orderNo = json['OrderNo']; + procedureName = json['ProcedureName']; + projectName = json['ProjectName']; + qR = json['QR']; + reportDataHTML = json['ReportDataHTML']; + reportDataTextString = json['ReportDataTextString']; + strAppointmentDate = json['StrAppointmentDate']; + strOrderDate = json['StrOrderDate']; + isCVI = json['isCVI']; + isRadMedicalReport = json['isRadMedicalReport']; + vida3Id = json['vida3Id']; + } + + Map toJson() { + final Map data = new Map(); + data['SetupID'] = this.setupID; + data['ProjectID'] = this.projectID; + data['PatientID'] = this.patientID; + data['InvoiceLineItemNo'] = this.invoiceLineItemNo; + data['InvoiceNo'] = this.invoiceNo; + data['DoctorID'] = this.doctorID; + data['ClinicID'] = this.clinicID; + data['OrderDate'] = this.orderDate; + data['ReportData'] = this.reportData; + data['ImageURL'] = this.imageURL; + data['ProcedureID'] = this.procedureID; + data['AppointmentNo'] = this.appointmentNo; + data['DIAPacsURL'] = this.dIAPacsURL; + data['IsRead'] = this.isRead; + data['ReadOn'] = this.readOn; + data['AdmissionNo'] = this.admissionNo; + data['IsInOutPatient'] = this.isInOutPatient; + data['ActualDoctorRate'] = this.actualDoctorRate; + data['AdmissionDate'] = this.admissionDate; + data['AdmissionNumber'] = this.admissionNumber; + data['AppointmentDate'] = this.appointmentDate; + data['AppointmentNumber'] = this.appointmentNumber; + data['AppointmentTime'] = this.appointmentTime; + data['ClinicDescription'] = this.clinicDescription; + data['DIA_PACS_URL'] = this.dIAPACSURL; + data['DecimalDoctorRate'] = this.decimalDoctorRate; + data['Description'] = this.description; + data['DoctorImageURL'] = this.doctorImageURL; + data['DoctorName'] = this.doctorName; + data['DoctorRate'] = this.doctorRate; + data['DoctorStarsRate'] = this.doctorStarsRate; + data['DoctorTitle'] = this.doctorTitle; + data['Exam_id'] = this.examId; + data['Gender'] = this.gender; + data['GenderDescription'] = this.genderDescription; + data['InvoiceNo_VP'] = this.invoiceNoVP; + data['InvoiceType'] = this.invoiceType; + data['IsActiveDoctorProfile'] = this.isActiveDoctorProfile; + data['IsExecludeDoctor'] = this.isExecludeDoctor; + data['IsInOutPatientDescription'] = this.isInOutPatientDescription; + data['IsInOutPatientDescriptionN'] = this.isInOutPatientDescriptionN; + data['IsLiveCareAppointment'] = this.isLiveCareAppointment; + data['NationalityFlagURL'] = this.nationalityFlagURL; + data['NoOfPatientsRate'] = this.noOfPatientsRate; + data['OrderNo'] = this.orderNo; + data['ProcedureName'] = this.procedureName; + data['ProjectName'] = this.projectName; + data['QR'] = this.qR; + data['ReportDataHTML'] = this.reportDataHTML; + data['ReportDataTextString'] = this.reportDataTextString; + data['StrAppointmentDate'] = this.strAppointmentDate; + data['StrOrderDate'] = this.strOrderDate; + data['isCVI'] = this.isCVI; + data['isRadMedicalReport'] = this.isRadMedicalReport; + data['vida3Id'] = this.vida3Id; + return data; + } +} diff --git a/lib/features/radiology/radiology_repo.dart b/lib/features/radiology/radiology_repo.dart new file mode 100644 index 0000000..d3e4f3d --- /dev/null +++ b/lib/features/radiology/radiology_repo.dart @@ -0,0 +1,77 @@ +import 'package:hmg_patient_app_new/core/api/api_client.dart'; +import 'package:hmg_patient_app_new/core/api_consts.dart'; +import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; +import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; +import 'package:dartz/dartz.dart'; +import 'package:hmg_patient_app_new/features/radiology/models/resp_models/patient_radiology_response_model.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; + +abstract class RadiologyRepo { + Future>>> getPatientRadiologyOrders({required String patientId}); +} + +class RadiologyRepoImp implements RadiologyRepo { + final ApiClient apiClient; + final LoggerService loggerService; + + RadiologyRepoImp({required this.loggerService, required this.apiClient}); + + @override + Future>>> getPatientRadiologyOrders({required String patientId}) async { + final mapDevice = { + "isDentalAllowedBackend": false, + "VersionID": 50.0, + "Channel": 3, + "LanguageID": 2, + "IPAdress": "10.20.10.20", + "generalid": "Cs2020@2016\$2958", + "Latitude": 0.0, + "Longitude": 0.0, + "DeviceTypeID": 1, + "PatientType": 1, + "PatientTypeID": 1, + "TokenID": "@dm!n", + "PatientID": "1018977", + "PatientOutSA": "0", + "SessionID": "03478TYC02N80874CTYN04883475!?" + }; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_PATIENT_ORDERS, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus}) { + final radOrders; + try { + if (response['FinalRadiologyList'] != null && response['FinalRadiologyList'].length != 0) { + final list = response['FinalRadiologyList']; + radOrders = list.map((item) => PatientRadiologyResponseModel.fromJson(item as Map)).toList().cast(); + } else { + final list = response['FinalRadiologyListAPI']; + radOrders = list.map((item) => PatientRadiologyResponseModel.fromJson(item as Map)).toList().cast(); + } + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: radOrders, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } +} diff --git a/lib/features/radiology/radiology_view_model.dart b/lib/features/radiology/radiology_view_model.dart new file mode 100644 index 0000000..1bdba04 --- /dev/null +++ b/lib/features/radiology/radiology_view_model.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart'; +import 'package:hmg_patient_app_new/services/error_handler_service.dart'; + +import 'models/resp_models/patient_radiology_response_model.dart'; + +class RadiologyViewModel extends ChangeNotifier { + bool isRadiologyOrdersLoading = false; + + RadiologyRepo radiologyRepo; + ErrorHandlerService errorHandlerService; + + List patientRadiologyOrders = []; + + RadiologyViewModel({required this.radiologyRepo, required this.errorHandlerService}); + + initRadiologyProvider() { + patientRadiologyOrders.clear(); + isRadiologyOrdersLoading = true; + getPatientRadiologyOrders(); + notifyListeners(); + } + + Future getPatientRadiologyOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await radiologyRepo.getPatientRadiologyOrders(patientId: "1231755"); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + patientRadiologyOrders = apiResponse.data!; + isRadiologyOrdersLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } +} diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index c5f2592..0210e4c 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -774,8 +774,13 @@ abstract class LocaleKeys { static const validPassportNumber = 'validPassportNumber'; static const continuePlan = 'continuePlan'; static const aboutApp = 'aboutApp'; - static const loginOrRegister = 'loginOrRegister'; static const dontHaveAccount = 'dontHaveAccount'; + static const loginOrRegister = 'loginOrRegister'; + static const myFiles = 'myFiles'; + static const resultsPending = 'resultsPending'; + static const resultsAvailable = 'resultsAvailable'; + static const viewReport = 'viewReport'; + static const prescriptionDeliveryError = 'prescriptionDeliveryError'; static const receiveOtpToast = 'receiveOtpToast'; static const enterPhoneNumber = 'enterPhoneNumber'; static const enterEmailDesc = 'enterEmailDesc'; @@ -789,5 +794,13 @@ abstract class LocaleKeys { static const oR = 'oR'; static const sendOTPWHATSAPP = 'sendOTPWHATSAPP'; static const sendOTPSMS = 'sendOTPSMS'; + static const fullName = 'fullName'; + static const married = 'married'; + static const uae = 'uae'; + static const malE = 'malE'; + static const loginBy = 'loginBy'; + static const loginByOTP = 'loginByOTP'; + static const guest = 'guest'; + static const switchAccount = 'switchAccount'; } diff --git a/lib/main.dart b/lib/main.dart index f528886..e57d960 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,7 +7,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; -import 'package:hmg_patient_app_new/providers/bottom_navigation_provider.dart'; +import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; +import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; +import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart'; import 'package:hmg_patient_app_new/routes/app_routes.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; @@ -51,8 +53,23 @@ void main() async { path: 'assets/langs', fallbackLocale: Locale('en', 'US'), child: MultiProvider(providers: [ - ChangeNotifierProvider( - create: (_) => BottomNavigationProvider(), + ChangeNotifierProvider( + create: (_) => LabViewModel( + labRepo: getIt(), + errorHandlerService: getIt(), + ), + ), + ChangeNotifierProvider( + create: (_) => RadiologyViewModel( + radiologyRepo: getIt(), + errorHandlerService: getIt(), + ), + ), + ChangeNotifierProvider( + create: (_) => PrescriptionsViewModel( + prescriptionsRepo: getIt(), + errorHandlerService: getIt(), + ), ), ChangeNotifierProvider( create: (_) => AuthenticationViewModel( diff --git a/lib/presentation/authentication/register_step2.dart b/lib/presentation/authentication/register_step2.dart new file mode 100644 index 0000000..295dd8a --- /dev/null +++ b/lib/presentation/authentication/register_step2.dart @@ -0,0 +1,313 @@ +import 'dart:convert'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/context_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; +import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart'; +import 'package:hmg_patient_app_new/widgets/input_widget.dart'; + +class RegisterNewStep2 extends StatefulWidget { + var nHICData; + var payload; + + RegisterNewStep2(this.nHICData, this.payload, {Key? key}) : super(key: key); + + @override + _RegisterNew createState() => _RegisterNew(); +} + +class _RegisterNew extends State { + bool isFromDubai = true; + List countriesList = []; + AppState? appState; + GenderTypeEnum? selectedGenderType; + MaritalStatusTypeEnum? selectedMaritalStatusType; + CountryEnum? selectedCountry; + + @override + void initState() { + super.initState(); + // isFromDubai = widget.payload.zipCode!.contains("971") || widget.payload.zipCode!.contains("+971"); + loadCountriesList(); + } + + loadCountriesList() async { + appState = getIt.get(); + final String response = await DefaultAssetBundle.of(context).loadString('assets/json/countriesList.json'); + final List data = json.decode(response); + countriesList = data.map((e) => NationalityCountries.fromJson(e)).toList(); + } + + Widget build(BuildContext context) { + return Scaffold( + + appBar: CustomAppBar(onBackPressed: () {}, onLanguageChanged: (lang) {}, hideLogoAndLang: true,), + body: SingleChildScrollView( + reverse: false, + padding: EdgeInsets.only(left: 24.h, right: 24.h, top: 24.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Directionality( + textDirection: Directionality.of(context), + child: Container( + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(24)), + padding: EdgeInsets.only(left: 16.h, right: 16.h), + child: Column( + children: [ + TextInputWidget( + labelText: isFromDubai ? LocaleKeys.fullName.tr() : LocaleKeys.name.tr(), + hintText: isFromDubai ? "name" ?? "" : (widget.nHICData!.firstNameEn!.toUpperCase() + " " + widget.nHICData!.lastNameEn!.toUpperCase()), + controller: null, + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + keyboardType: TextInputType.text, + isAllowLeadingIcon: true, + isReadOnly: isFromDubai ? false : true, + leadingIcon: AppAssets.user_circle).paddingSymmetrical(0.h,16.h), + Divider(height: 1, color: AppColors.greyColor,), + TextInputWidget( + labelText: LocaleKeys.nationalIdNumber.tr(), + hintText: isFromDubai ? "widget.payload.nationalID!" : (widget.nHICData!.idNumber ?? ""), + controller: null, + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + leadingIcon: AppAssets.student_card).paddingSymmetrical(0.h,16.h), + Divider(height: 1, color: AppColors.greyColor,), + isFromDubai + ? DropdownWidget( + labelText: LocaleKeys.gender.tr(), + hintText: LocaleKeys.malE.tr(), + isEnable: true, + dropdownItems: GenderTypeEnum.values.map((e) => appState!.isArabic() ? e.typeAr : e.type).toList(), + selectedValue: appState!.isArabic() ? selectedGenderType!.typeAr : selectedGenderType?.type, + // selectionType: SelectionType.dropdown, + onChange: (val) { + if (val != null) {} + }, + isBorderAllowed: false, + hasSelectionCustomIcon: true, + isAllowRadius: false, + padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.user_full, + ).withVerticalPadding(8) + : TextInputWidget( + labelText: LocaleKeys.gender.tr(), + hintText: (widget.nHICData!.gender ?? ""), + controller: null, + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: isFromDubai ? false : true, + leadingIcon: AppAssets.user_full, + + onChange: (value) {}).paddingSymmetrical(0.h,16.h), + Divider(height: 1, color: AppColors.greyColor,), + isFromDubai + ? DropdownWidget( + labelText: LocaleKeys.maritalStatus.tr(), + hintText: LocaleKeys.married.tr(), + isEnable: true, + dropdownItems: MaritalStatusTypeEnum.values.map((e) => appState!.isArabic() ? e.typeAr : e.type).toList(), + selectedValue: appState!.isArabic() ? selectedMaritalStatusType!.typeAr : selectedMaritalStatusType?.type, + onChange: (val) {}, + isBorderAllowed: false, + hasSelectionCustomIcon: true, + isAllowRadius: false, + padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.smart_phone, + ).withVerticalPadding(8) + : TextInputWidget( + labelText: LocaleKeys.maritalStatus.tr(), + hintText: appState!.isArabic() + ? (MaritalStatusTypeExtension.fromValue(widget.nHICData!.maritalStatusCode)!.typeAr) + : (MaritalStatusTypeExtension.fromValue(widget.nHICData!.maritalStatusCode)!.type), + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + leadingIcon: AppAssets.smart_phone, + onChange: (value) {}).paddingSymmetrical(0.h,16.h), + Divider(height: 1, color: AppColors.greyColor,), + isFromDubai + ? DropdownWidget( + labelText: LocaleKeys.country.tr(), + hintText: LocaleKeys.uae.tr(), + isEnable: true, + dropdownItems: countriesList.map((e) => appState!.isArabic() ? e.nameN ?? "" : e.name ?? "").toList(), + selectedValue: appState!.isArabic() ? selectedCountry!.nameArabic ?? "" : selectedCountry?.name ?? "", + onChange: (val) {}, + isBorderAllowed: false, + hasSelectionCustomIcon: true, + isAllowRadius: false, + padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.globe, + ).withVerticalPadding(8) + : TextInputWidget( + labelText: LocaleKeys.nationality.tr(), + hintText: appState!.isArabic() + ? (countriesList.firstWhere((e) => e.id == (widget.nHICData!.nationalityCode ?? ""), orElse: () => NationalityCountries()).nameN ?? "") + : (countriesList.firstWhere((e) => e.id == (widget.nHICData!.nationalityCode ?? ""), orElse: () => NationalityCountries()).name ?? ""), + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + leadingIcon: AppAssets.globe, + onChange: (value) {}).paddingSymmetrical(0.h,16.h), + Divider(height: 1, color: AppColors.greyColor,), + TextInputWidget( + labelText: LocaleKeys.mobileNumber.tr(), + hintText: ("widget.payload.mobileNo" ?? ""), + controller: null, + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + leadingIcon: AppAssets.call, + onChange: (value) {}).paddingSymmetrical(0.h,16.h), + Divider(height: 1, color: AppColors.greyColor,), + TextInputWidget( + labelText: LocaleKeys.dob.tr(), + hintText: isFromDubai ? "widget.payload.dob!" : (widget.nHICData!.dateOfBirth ?? ""), + controller: null, + isEnable: true, + prefix: null, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + // : SelectionType.calendar, + // selectedValue: widget.payload.dob != null ? Utils.formatDateToDisplay(widget.payload.dob.toString()) : null, + // selectionCustomIcon: AppAssets.calendar, + leadingIcon: AppAssets.birthday_cake, + onChange: (value) {}).paddingSymmetrical(0.h,16.h), + ], + ), + ), + ), + SizedBox(height: 50.h), + Row( + children: [ + Expanded( + child: CustomButton( + text: LocaleKeys.cancel, + icon: AppAssets.cancel, + onPressed: () { + Navigator.of(context).pop(); + }, + // fontFamily: context.fontFamily, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + iconColor: AppColors.primaryRedColor, + ), + ), + SizedBox( + width: 16, + ), + Expanded( + child: CustomButton( + backgroundColor: AppColors.lightGreenColor, + borderColor: AppColors.lightGreenColor, + textColor: AppColors.textGreenColor, + text: LocaleKeys.confirm.tr(), + icon: AppAssets.confirm, + iconColor: AppColors.textGreenColor, + onPressed: () { + // if (isFromDubai) { + // if (name == null) { + // AppToast.showErrorToast(message: LocaleKeys.enterFullName); + // return; + // } + // if (!name!.contains(" ")) if (selectedGenderType == null) { + // AppToast.showErrorToast(message: TranslationBase.of(context).enterFullName); + // return; + // } + // if (selectedMaritalStatusType == null) { + // AppToast.showErrorToast(message: TranslationBase.of(context).chooseMaritalStatus); + // return; + // } + // if (selectedCountry == null) { + // AppToast.showErrorToast(message: TranslationBase.of(context).chooseCountry); + // return; + // } + // } + + showModel(context: context); + }, + ), + ) + ], + ), + ], + ), + ), + ); + } + + void showModel({required BuildContext context}) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + isDismissible: false, + backgroundColor: Colors.transparent, + builder: (bottomSheetContext) => Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom), + child: SingleChildScrollView( + child: GenericBottomSheet( + textController: TextEditingController(), + isForEmail: true, + buttons: [ + Padding( + padding: const EdgeInsets.only(bottom: 10), + child: CustomButton( + text: LocaleKeys.submit, + onPressed: () { + // if (emailAddress.text.isEmpty) { + // Utils.showErrorToast(TranslationBase.of(context).enterEmailAddress); + // return; + // } else { + // Navigator.of(context).pop(); + // registerNow(); + // } + }, + backgroundColor: AppColors.bgGreenColor, + borderColor: AppColors.bgGreenColor, + textColor: AppColors.whiteColor), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/presentation/authentication/saved_login_screen.dart b/lib/presentation/authentication/saved_login_screen.dart new file mode 100644 index 0000000..da04c40 --- /dev/null +++ b/lib/presentation/authentication/saved_login_screen.dart @@ -0,0 +1,289 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/authentication/login.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; +import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; + +class SavedLogin extends StatefulWidget { + // final SelectDeviceIMEIRES savedLoginData; + + // const SavedLogin(this.savedLoginData, {Key? key}) : super(key: key); + const SavedLogin(); + + @override + _SavedLogin createState() => _SavedLogin(); +} + +class _SavedLogin extends State { + LoginTypeEnum loginType = LoginTypeEnum.sms; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + onBackPressed: () {}, + onLanguageChanged: (lang) {}, + ), + body: SafeArea( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 24.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Spacer(flex: 2), + // Welcome back text + LocaleKeys.welcomeBack.tr().toText16(color: AppColors.inputLabelTextColor), + SizedBox(height: 16.h), + ("widget.savedLoginData.name!.toLowerCase().capitalizeFirstofEach").toText26(isBold: true, height: 26 / 36, color: AppColors.textColor), + SizedBox(height: 24.h), + Container( + padding: EdgeInsets.all(16.h), + decoration: BoxDecoration( + color: AppColors.whiteColor, + border: Border.all(color: AppColors.whiteColor), + borderRadius: BorderRadius.circular(24.h), + boxShadow: [ + BoxShadow(color: Color(0x0D000000), blurRadius: 16, offset: Offset(0, 0), spreadRadius: 5), + ], + ), + child: Column( + children: [ + // Last login info + ('LocaleKeys.lastloginBy.tr()' + ' {getType(widget.savedLoginData.logInType!, context)}').toText14(isBold: true, color: AppColors.greyTextColor), + ('widget.savedLoginData.createdOn != null ? DateUtil.getFormattedDate(DateUtil.convertStringToDate(widget.savedLoginData.createdOn!), "d MMMM, y at HH:mm") : --') + .toText16(isBold: true, color: AppColors.textColor), + + Container(margin: EdgeInsets.all(16.h), child: Utils.buildSvgWithAssets(icon: getTypeIcons(loginType.toInt), iconColor: loginType.toInt == 4 ? null : AppColors.primaryRedColor)), + // Face ID login button + Container( + height: 45, + child: CustomButton( + text: "${LocaleKeys.loginBy.tr()} ${loginType.displayName}", + onPressed: () { + if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) { + // loginWithFingerPrintFace(loginType.toInt, widget.savedLoginData.iMEI!); + } else { + int? val = loginType.toInt; + //checkUserAuthentication(val); + } + }, + backgroundColor: Color(0xffED1C2B), + borderColor: Color(0xffFEE9EA), + textColor: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(0, 10, 0, 10), + icon: AppAssets.apple_finder, + ), + ), + ], + ), + ), + const SizedBox(height: 24), + Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + LocaleKeys.oR.tr(), + style: context.dynamicTextStyle(fontSize: 16, fontWeight: FontWeight.w500), + ), + ), + const SizedBox(height: 24), + // OTP login button + loginType != null && loginType.toInt != 1 + ? Column( + children: [ + loginType.toInt != 1 + ? CustomButton( + text: LocaleKeys.loginByOTP.tr(), + onPressed: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + isDismissible: false, + useSafeArea: true, + backgroundColor: Colors.transparent, + enableDrag: false, + // Prevent dragging to avoid focus conflicts + builder: (bottomSheetContext) => StatefulBuilder(builder: (BuildContext context, StateSetter setModalState) { + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom), + child: SingleChildScrollView( + child: GenericBottomSheet( + countryCode: "966", + initialPhoneNumber: "", + textController: TextEditingController(), + isFromSavedLogin: true, + isEnableCountryDropdown: true, + onCountryChange: (value) {}, + onChange: (String? value) {}, + buttons: [ + Padding( + padding: EdgeInsets.only(bottom: 10.h), + child: CustomButton( + text: LocaleKeys.sendOTPSMS.tr(), + onPressed: () { + Navigator.of(context).pop(); + loginType = LoginTypeEnum.sms; + int? val = loginType.toInt; + // checkUserAuthentication(val); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedBorderColor, + textColor: AppColors.whiteColor, + icon: AppAssets.message, + ), + ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding(padding: EdgeInsets.symmetric(horizontal: 8.h), child: (LocaleKeys.oR.tr()).toText16(color: AppColors.textColor)), + ], + ), + Padding( + padding: const EdgeInsets.only(bottom: 10, top: 10), + child: CustomButton( + text: LocaleKeys.sendOTPWHATSAPP.tr(), + onPressed: () { + Navigator.of(context).pop(); + loginType = LoginTypeEnum.whatsapp; + int? val = loginType.toInt; + // checkUserAuthentication(val); + }, + backgroundColor: AppColors.transparent, + borderColor: AppColors.textColor, + textColor: AppColors.textColor, + icon: AppAssets.whatsapp, + ), + ), + ], + ), + ), + ); + }), + ); + }, + backgroundColor: AppColors.whiteColor, + borderColor: AppColors.borderOnlyColor, + textColor: AppColors.textColor, + borderWidth: 2, + padding: EdgeInsets.fromLTRB(0, 14, 0, 14), + icon: AppAssets.password_validation, + ) + : Container(), + SizedBox( + height: 20, + ), + ], + ) + : CustomButton( + text: "${LocaleKeys.loginBy.tr()} ${LoginTypeEnum.whatsapp.displayName}", + onPressed: () { + if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) { + // loginWithFingerPrintFace(loginType.toInt, "iMEI"); + } else { + loginType = LoginTypeEnum.whatsapp; + int? val = loginType.toInt; + // checkUserAuthentication(val); + } + }, + backgroundColor: AppColors.whiteColor, + borderColor: Color(0xFF2E3039), + textColor: Color(0xFF2E3039), + borderWidth: 2, + padding: EdgeInsets.fromLTRB(0, 14, 0, 14), + icon: AppAssets.whatsapp, + ), + const Spacer(flex: 2), + // OR divider + + const SizedBox(height: 24), + // Guest and Switch account + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Container( + height: 56, + child: CustomButton( + text: LocaleKeys.guest.tr(), + onPressed: () { + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (BuildContext context) => LoginScreen()), + ); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 16, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(0, 10, 0, 10), + // icon: "assets/images/svg/apple-finder.svg", + ), + ), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.05, + ), + Expanded( + child: Container( + height: 56, + child: CustomButton( + text: LocaleKeys.switchAccount.tr(), + onPressed: () { + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (BuildContext context) => LoginScreen()), + ); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 15, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(0, 10, 0, 10), + // icon: "assets/images/svg/apple-finder.svg", + ), + ), + ), + ], + ), + const SizedBox(height: 20), + ], + ), + ), + ), + ); + } + + String getTypeIcons(int type) { + final types = { + 1: AppAssets.sms, + 2: AppAssets.fingerprint, + 3: AppAssets.fingerprint, + 4: AppAssets.whatsapp, + }; + + if (types.containsKey(type)) { + return types[type]!; + } else { + throw Exception('Invalid login type: $type'); + } + } +} diff --git a/lib/presentation/home/data/landing_page_data.dart b/lib/presentation/home/data/landing_page_data.dart index b34d9bc..ad524e3 100644 --- a/lib/presentation/home/data/landing_page_data.dart +++ b/lib/presentation/home/data/landing_page_data.dart @@ -6,6 +6,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; class LandingPageData { static List getNotLoggedInServiceCardsList = [ ServiceCardData( + serviceName: "emergency", icon: AppAssets.emergency_services_icon, title: "Emergency", subtitle: "Services", @@ -15,6 +16,7 @@ class LandingPageData { isBold: true, ), ServiceCardData( + serviceName: "indoor_navigation", icon: AppAssets.indoor_nav_icon, title: "Indoor", subtitle: "Navigation", @@ -24,6 +26,7 @@ class LandingPageData { isBold: false, ), ServiceCardData( + serviceName: "search_doctor", icon: AppAssets.search_doctor_icon, title: "Search", subtitle: "Doctor", @@ -33,6 +36,7 @@ class LandingPageData { isBold: false, ), ServiceCardData( + serviceName: "health_calculators", icon: AppAssets.health_calculators_icon, title: "Health", subtitle: "Calculators", @@ -42,6 +46,7 @@ class LandingPageData { isBold: false, ), ServiceCardData( + serviceName: "health_converters", icon: AppAssets.health_calculators_icon, title: "Health", subtitle: "Converters", @@ -51,6 +56,7 @@ class LandingPageData { isBold: false, ), ServiceCardData( + serviceName: "parking_guide", icon: AppAssets.health_calculators_icon, title: "Parking", subtitle: "Guide", @@ -63,6 +69,7 @@ class LandingPageData { static List getLoggedInServiceCardsList = [ ServiceCardData( + serviceName: "emergency", icon: AppAssets.emergency_services_icon, title: "Emergency", subtitle: "Services", @@ -72,6 +79,7 @@ class LandingPageData { isBold: true, ), ServiceCardData( + serviceName: "lab_results", icon: AppAssets.lab_result_icon, title: "My Lab", subtitle: "Results", @@ -81,6 +89,17 @@ class LandingPageData { isBold: false, ), ServiceCardData( + serviceName: "radiology_results", + icon: AppAssets.lab_result_icon, + title: "My Radiology", + subtitle: "Results", + backgroundColor: AppColors.whiteColor, + iconColor: AppColors.blackColor, + textColor: AppColors.blackColor, + isBold: false, + ), + ServiceCardData( + serviceName: "prescriptions", icon: AppAssets.my_prescription_icon, title: "My", subtitle: "Prescriptions", @@ -90,6 +109,7 @@ class LandingPageData { isBold: false, ), ServiceCardData( + serviceName: "insurance_update", icon: AppAssets.insurance_update_icon, title: "Insurance", subtitle: "Update", @@ -99,6 +119,7 @@ class LandingPageData { isBold: false, ), ServiceCardData( + serviceName: "my_doctors", icon: AppAssets.insurance_update_icon, title: "My", subtitle: "Doctors", @@ -108,6 +129,7 @@ class LandingPageData { isBold: false, ), ServiceCardData( + serviceName: "sick_leaves", icon: AppAssets.insurance_update_icon, title: "My Sick", subtitle: "Leaves", diff --git a/lib/presentation/home/data/service_card_data.dart b/lib/presentation/home/data/service_card_data.dart index 856a15e..49e7e3d 100644 --- a/lib/presentation/home/data/service_card_data.dart +++ b/lib/presentation/home/data/service_card_data.dart @@ -3,6 +3,7 @@ import 'dart:ui'; import 'package:hmg_patient_app_new/theme/colors.dart'; class ServiceCardData { + final String serviceName; final String icon; final String title; final String subtitle; @@ -13,6 +14,7 @@ class ServiceCardData { final String largeCardIcon; ServiceCardData({ + this.serviceName = "", this.icon = "", this.title = "", this.subtitle = "", diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 0710d7c..3592acd 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page. import 'package:hmg_patient_app_new/providers/bottom_navigation_provider.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/bottom_navigation/bottom_navigation.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; diff --git a/lib/presentation/home/widgets/small_service_card.dart b/lib/presentation/home/widgets/small_service_card.dart index a138db6..297ed2b 100644 --- a/lib/presentation/home/widgets/small_service_card.dart +++ b/lib/presentation/home/widgets/small_service_card.dart @@ -2,11 +2,16 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart'; +import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart'; +import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import '../../../core/utils/utils.dart'; import '../../../theme/colors.dart'; +import '../../radiology/radiology_orders_page.dart' show RadiologyOrdersPage; class SmallServiceCard extends StatelessWidget { + final String serviceName; final String icon; final String title; final String subtitle; @@ -18,6 +23,7 @@ class SmallServiceCard extends StatelessWidget { SmallServiceCard({ super.key, this.icon = "", + this.serviceName = "", this.title = "", this.subtitle = "", this.backgroundColor = AppColors.whiteColor, @@ -48,6 +54,35 @@ class SmallServiceCard extends StatelessWidget { ), ), ), - ); + ).onPress(() { + switch (serviceName) { + case "lab_results": + Navigator.of(context).push( + FadePage( + page: LabOrdersPage(), + ), + ); + break; + case "radiology_results": + Navigator.of(context).push( + FadePage( + page: RadiologyOrdersPage(), + ), + ); + break; + case "prescriptions": + Navigator.of(context).push( + FadePage( + page: PrescriptionsListPage(), + ), + ); + break; + case "insurance_update": + break; + default: + // Handle unknown service + break; + } + }); } } diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart new file mode 100644 index 0000000..8a0ce26 --- /dev/null +++ b/lib/presentation/lab/lab_orders_page.dart @@ -0,0 +1,265 @@ +import 'dart:async'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/date_util.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; +import 'package:provider/provider.dart'; + +class LabOrdersPage extends StatefulWidget { + const LabOrdersPage({super.key}); + + @override + State createState() => _LabOrdersPageState(); +} + +class _LabOrdersPageState extends State { + late LabViewModel labProvider; + + int? expandedIndex; + + @override + void initState() { + scheduleMicrotask(() { + labProvider.initLabProvider(); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + labProvider = Provider.of(context); + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + appBar: AppBar( + title: LocaleKeys.labResults.tr(context: context).toText18(), + backgroundColor: AppColors.bgScaffoldColor, + ), + body: Padding( + padding: EdgeInsets.all(24.h), + child: SingleChildScrollView( + child: Consumer( + builder: (context, model, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.labResults.tr(context: context).toText24(isBold: true), + Utils.buildSvgWithAssets(icon: AppAssets.search_icon), + ], + ), + SizedBox(height: 16.h), + // Build Tab Bar + SizedBox(height: 16.h), + // Expandable list + ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: model.isLabOrdersLoading ? 5 : model.patientLabOrders.length, + itemBuilder: (context, index) { + final isExpanded = expandedIndex == index; + return model.isLabOrdersLoading + ? const MoviesShimmerWidget() + : AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 500), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: AnimatedContainer( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + margin: EdgeInsets.symmetric(vertical: 8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), + child: InkWell( + onTap: () { + setState(() { + expandedIndex = isExpanded ? null : index; + }); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + CustomButton( + text: getLabOrderStatusText(model.patientLabOrders[index].status!), + onPressed: () {}, + backgroundColor: getLabOrderStatusColor(model.patientLabOrders[index].status!).withOpacity(0.15), + borderColor: getLabOrderStatusColor(model.patientLabOrders[index].status!).withOpacity(0.01), + textColor: getLabOrderStatusColor(model.patientLabOrders[index].status!), + fontSize: 10, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + Icon(isExpanded ? Icons.expand_less : Icons.expand_more), + ], + ), + SizedBox(height: 8.h), + Row( + children: [ + Image.network( + model.patientLabOrders[index].doctorImageURL!, + width: 24.h, + height: 24.h, + fit: BoxFit.fill, + ).circle(100), + SizedBox(width: 4.h), + model.patientLabOrders[index].doctorName!.toText16(isBold: true) + ], + ), + SizedBox(height: 8.h), + Row( + children: [ + CustomButton( + text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(model.patientLabOrders[index].createdOn), false), + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 24.h, + ), + SizedBox(width: 8.h), + CustomButton( + text: model.patientLabOrders[index].clinicDescription!, + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 24.h, + ), + ], + ), + ], + ), + ), + AnimatedSwitcher( + duration: Duration(milliseconds: 300), + switchInCurve: Curves.easeIn, + switchOutCurve: Curves.easeOut, + transitionBuilder: (Widget child, Animation animation) { + return FadeTransition( + opacity: animation, + child: SizeTransition( + sizeFactor: animation, + axisAlignment: 0.0, + child: child, + ), + ); + }, + child: isExpanded + ? Container( + key: ValueKey(index), + padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ...model.patientLabOrders[index].testDetails!.map((detail) { + return Padding( + padding: EdgeInsets.only(bottom: 8.h), + child: '● ${detail.description}'.toText14(weight: FontWeight.w500), + ); + }).toList(), + SizedBox(height: 16.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox(), + CustomButton( + icon: AppAssets.view_report_icon, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, + text: LocaleKeys.viewReport.tr(context: context), + onPressed: () {}, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14, + fontWeight: FontWeight.bold, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), + ], + ), + ], + ), + ) + : SizedBox.shrink(key: ValueKey(-index)), + ), + ], + ), + ), + ), + ), + ), + ); + }, + ), + ], + ); + }, + ), + ), + ), + ); + } + + Color getLabOrderStatusColor(num status) { + switch (status) { + case 44: + return AppColors.warningColorYellow; + case 45: + return AppColors.warningColorYellow; + case 16: + return AppColors.successColor; + case 17: + return AppColors.successColor; + default: + return AppColors.greyColor; + } + } + + String getLabOrderStatusText(num status) { + switch (status) { + case 44: + return LocaleKeys.resultsPending.tr(context: context); + case 45: + return LocaleKeys.resultsPending.tr(context: context); + case 16: + return LocaleKeys.resultsAvailable.tr(context: context); + case 17: + return LocaleKeys.resultsAvailable.tr(context: context); + default: + return ""; + } + } +} diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 1e5143d..9b2265c 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -5,6 +5,7 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; @@ -85,9 +86,6 @@ class MedicalFilePage extends StatelessWidget { ), SizedBox(width: 4.h), CustomButton( - icon: AppAssets.checkmark_icon, - iconColor: AppColors.successColor, - iconSize: 13.h, text: LocaleKeys.verified.tr(context: context), onPressed: () {}, backgroundColor: AppColors.greyColor, @@ -164,7 +162,7 @@ class MedicalFilePage extends StatelessWidget { SizedBox(height: 16.h), //Insurance Tab Data Container( - height: 150.h, + // height: 150.h, width: double.infinity, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, @@ -173,6 +171,7 @@ class MedicalFilePage extends StatelessWidget { child: Padding( padding: EdgeInsets.all(16.h), child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -201,10 +200,75 @@ class MedicalFilePage extends StatelessWidget { ), ], ), + SizedBox(height: 12.h), + "NCCI".toText12(isBold: true), + "NC_Dr Sulaiman Al Habib Medical Group".toText12(isBold: true), + SizedBox(height: 8.h), + Row( + children: [ + CustomButton( + icon: AppAssets.cross_circle, + iconColor: AppColors.primaryRedColor, + iconSize: 13.h, + text: "Expiry: 18 Mar, 2025", + onPressed: () {}, + backgroundColor: AppColors.primaryRedColor.withOpacity(0.1), + borderColor: AppColors.primaryRedColor.withOpacity(0.0), + textColor: AppColors.primaryRedColor, + fontSize: 10, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + SizedBox(width: 5.h), + CustomButton( + text: "Patient Card ID: 3628599", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 10, + fontWeight: FontWeight.normal, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + SizedBox(height: 10.h), + CustomButton( + icon: AppAssets.cross_circle, + iconColor: AppColors.primaryRedColor, + iconSize: 13.h, + text: "${LocaleKeys.updateInsurance.tr(context: context)} ${LocaleKeys.updateInsuranceSubtitle.tr(context: context)}", + onPressed: () {}, + backgroundColor: AppColors.bgGreenColor.withOpacity(0.20), + borderColor: AppColors.bgGreenColor.withOpacity(0.0), + textColor: AppColors.bgGreenColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), ], ), ), ), + SizedBox(height: 10.h), + GridView( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, crossAxisSpacing: 13, mainAxisSpacing: 13), + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsets.only(top: 12), + shrinkWrap: true, + children: [ + MedicalFileCard(label: "Update Insurance", textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon), + MedicalFileCard(label: "Insurance Approvals", textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon), + MedicalFileCard(label: "My Invoices List", textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon), + MedicalFileCard(label: "Ancillary Orders List", textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon), + ], + ), ], ), ), diff --git a/lib/presentation/medical_file/widgets/medical_file_card.dart b/lib/presentation/medical_file/widgets/medical_file_card.dart new file mode 100644 index 0000000..92bb4ee --- /dev/null +++ b/lib/presentation/medical_file/widgets/medical_file_card.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; + +class MedicalFileCard extends StatelessWidget { + final String label; + + // final Color svgColor; + final Color textColor; + final Color backgroundColor; + final String svgIcon; + + MedicalFileCard({ + required this.label, + // required this.svgColor, + required this.textColor, + required this.backgroundColor, + this.svgIcon = "", + }); + + @override + Widget build(BuildContext context) { + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: backgroundColor, + borderRadius: 20, + ), + child: Padding( + padding: EdgeInsets.all(8.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Utils.buildSvgWithAssets(icon: svgIcon, width: 30.h, height: 30.h, fit: BoxFit.contain), + SizedBox(height: 12.h), + label.toText11(color: textColor, isBold: true), + ], + ), + ), + ); + } +} diff --git a/lib/presentation/prescriptions/prescription_detail_page.dart b/lib/presentation/prescriptions/prescription_detail_page.dart new file mode 100644 index 0000000..0231c67 --- /dev/null +++ b/lib/presentation/prescriptions/prescription_detail_page.dart @@ -0,0 +1,313 @@ +import 'dart:async'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/date_util.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart'; +import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; +import 'package:provider/provider.dart'; + +class PrescriptionDetailPage extends StatefulWidget { + PrescriptionDetailPage({super.key, required this.prescriptionsResponseModel}); + + PatientPrescriptionsResponseModel prescriptionsResponseModel; + + @override + State createState() => _PrescriptionDetailPageState(); +} + +class _PrescriptionDetailPageState extends State { + late PrescriptionsViewModel prescriptionsViewModel; + + @override + void initState() { + scheduleMicrotask(() { + prescriptionsViewModel.getPrescriptionDetails(widget.prescriptionsResponseModel); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + prescriptionsViewModel = Provider.of(context, listen: false); + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + appBar: AppBar( + title: LocaleKeys.prescriptions.tr(context: context).toText18(), + backgroundColor: AppColors.bgScaffoldColor, + ), + body: SingleChildScrollView( + child: Consumer(builder: (context, prescriptionVM, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.prescriptions.tr(context: context).toText24(isBold: true).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 24.h), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.network( + widget.prescriptionsResponseModel.doctorImageURL!, + width: 24.h, + height: 24.h, + fit: BoxFit.fill, + ).circle(100), + SizedBox(width: 8.h), + Expanded(child: widget.prescriptionsResponseModel.doctorName!.toText16(isBold: true)), + ], + ), + SizedBox(height: 16.h), + Wrap( + direction: Axis.horizontal, + spacing: 6.h, + runSpacing: 6.h, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + icon: AppAssets.doctor_calendar_icon, + iconColor: AppColors.textColor, + iconSize: 13.h, + text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.prescriptionsResponseModel.appointmentDate), false), + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + text: widget.prescriptionsResponseModel.clinicDescription!, + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + icon: AppAssets.rating_icon, + iconColor: AppColors.ratingColorYellow, + iconSize: 13.h, + text: "Rating: ${widget.prescriptionsResponseModel.decimalDoctorRate}", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + text: widget.prescriptionsResponseModel.name!, + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + ], + ), + ], + ), + ), + ).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 16.h), + ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: prescriptionVM.isPrescriptionsDetailsLoading ? 5 : prescriptionVM.prescriptionDetailsList.length, + itemBuilder: (context, index) { + return prescriptionVM.isPrescriptionsDetailsLoading + ? const MoviesShimmerWidget() + : AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 500), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: AnimatedContainer( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + margin: EdgeInsets.symmetric(vertical: 8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.all(16.h), + child: Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Image.network( + prescriptionVM.prescriptionDetailsList[index].imageSRCUrl!, + width: 60.h, + height: 60.h, + fit: BoxFit.fill, + ).circle(100), + SizedBox(width: 8.h), + Expanded( + child: prescriptionVM.prescriptionDetailsList[index].itemDescription!.toText16(isBold: true, maxlines: 2), + ), + ], + ), + SizedBox(height: 16.h), + Wrap( + direction: Axis.horizontal, + spacing: 6.h, + runSpacing: 6.h, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + text: "${LocaleKeys.route.tr(context: context)}: ${prescriptionVM.prescriptionDetailsList[index].route}", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + text: "${LocaleKeys.frequency.tr(context: context)}: ${prescriptionVM.prescriptionDetailsList[index].frequency}", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + text: "${LocaleKeys.dailyDoses.tr(context: context)}: ${prescriptionVM.prescriptionDetailsList[index].doseDailyQuantity}", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + CustomButton( + text: "${LocaleKeys.days.tr(context: context)}: ${prescriptionVM.prescriptionDetailsList[index].days}", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + ], + ), + SizedBox(height: 8.h), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.prescription_remarks_icon, width: 18.h, height: 18.h), + SizedBox(width: 9.h), + Expanded(child: "${LocaleKeys.remarks.tr(context: context)}: ${prescriptionVM.prescriptionDetailsList[index].remarks!}".toText10(isBold: true)), + ], + ) + ], + ), + ), + ) + ], + ), + ), + ), + ), + ); + }, + ).paddingSymmetrical(24.h, 0.h), + ], + ); + }), + ), + ); + } +} diff --git a/lib/presentation/prescriptions/prescriptions_list_page.dart b/lib/presentation/prescriptions/prescriptions_list_page.dart new file mode 100644 index 0000000..4aa1a1b --- /dev/null +++ b/lib/presentation/prescriptions/prescriptions_list_page.dart @@ -0,0 +1,299 @@ +import 'dart:async'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/date_util.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; +import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; +import 'package:provider/provider.dart'; + +class PrescriptionsListPage extends StatefulWidget { + const PrescriptionsListPage({super.key}); + + @override + State createState() => _PrescriptionsListPageState(); +} + +class _PrescriptionsListPageState extends State { + int? expandedIndex; + + late PrescriptionsViewModel prescriptionsViewModel; + + @override + void initState() { + scheduleMicrotask(() { + prescriptionsViewModel.initPrescriptionsViewModel(); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + prescriptionsViewModel = Provider.of(context, listen: false); + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + appBar: AppBar( + title: LocaleKeys.prescriptions.tr(context: context).toText18(), + backgroundColor: AppColors.bgScaffoldColor, + ), + body: SingleChildScrollView( + child: Consumer(builder: (context, model, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.prescriptions.tr(context: context).toText24(isBold: true).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 16.h), + // Build Tab Bar + SizedBox(height: 16.h), + // Clinic & Hospital Sort + Row( + children: [ + CustomButton( + text: LocaleKeys.byClinic.tr(context: context), + onPressed: () { + model.setIsSortByClinic(true); + }, + backgroundColor: model.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor, + borderColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), + textColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 10, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), + SizedBox(width: 8.h), + CustomButton( + text: LocaleKeys.byHospital.tr(context: context), + onPressed: () { + model.setIsSortByClinic(false); + }, + backgroundColor: model.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor, + borderColor: model.isSortByClinic ? AppColors.textColor.withOpacity(0.2) : AppColors.primaryRedColor, + textColor: model.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 10, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), + ], + ).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 20.h), + // Expandable list + ListView.builder( + itemCount: model.isPrescriptionsOrdersLoading ? 4 : model.patientPrescriptionOrdersViewList.length, + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + padding: const EdgeInsets.only(left: 0, right: 8), + itemBuilder: (context, index) { + final isExpanded = expandedIndex == index; + return model.isPrescriptionsOrdersLoading + ? const MoviesShimmerWidget() + : AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 500), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: AnimatedContainer( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + margin: EdgeInsets.symmetric(vertical: 8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), + child: InkWell( + onTap: () { + setState(() { + expandedIndex = isExpanded ? null : index; + }); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + CustomButton( + text: "${model.patientPrescriptionOrdersViewList[index].prescriptionsList!.length} Prescriptions Available", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 10, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + Icon(isExpanded ? Icons.expand_less : Icons.expand_more), + ], + ), + SizedBox(height: 8.h), + model.patientPrescriptionOrdersViewList[index].filterName!.toText16(isBold: true) + ], + ), + ), + AnimatedSwitcher( + duration: Duration(milliseconds: 500), + switchInCurve: Curves.easeIn, + switchOutCurve: Curves.easeOut, + transitionBuilder: (Widget child, Animation animation) { + return FadeTransition( + opacity: animation, + child: SizeTransition( + sizeFactor: animation, + axisAlignment: 0.0, + child: child, + ), + ); + }, + child: isExpanded + ? Container( + key: ValueKey(index), + padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ...model.patientPrescriptionOrdersViewList[index].prescriptionsList!.map((prescription) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.network( + prescription.doctorImageURL!, + width: 24.h, + height: 24.h, + fit: BoxFit.fill, + ).circle(100), + SizedBox(width: 8.h), + Expanded(child: prescription.doctorName!.toText14(weight: FontWeight.w500)), + ], + ), + SizedBox(height: 8.h), + Row( + children: [ + CustomButton( + text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(prescription.appointmentDate), false), + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 24.h, + ), + SizedBox(width: 8.h), + CustomButton( + text: model.isSortByClinic ? prescription.name! : prescription.clinicDescription!, + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 24.h, + ), + ], + ), + SizedBox(height: 8.h), + Row( + children: [ + Expanded( + flex: 6, + child: CustomButton( + text: prescription.isHomeMedicineDeliverySupported! + ? LocaleKeys.resendOrder.tr(context: context) + : LocaleKeys.prescriptionDeliveryError.tr(context: context), + onPressed: () {}, + backgroundColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor.withOpacity(0.15) : AppColors.greyF7Color, + borderColor: AppColors.successColor.withOpacity(0.01), + textColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35), + fontSize: prescription.isHomeMedicineDeliverySupported! ? 14 : 12, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + icon: AppAssets.prescription_refill_icon, + iconColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35), + iconSize: 14.h, + ), + ), + SizedBox(width: 8.h), + Expanded( + flex: 1, + child: Container( + height: 40.h, + width: 40.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.textColor, + borderRadius: 10.h, + ), + child: Padding( + padding: EdgeInsets.all(8.h), + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, + width: 10.h, + height: 10.h, + fit: BoxFit.contain, + ), + ), + ).onPress(() { + model.setPrescriptionsDetailsLoading(); + Navigator.of(context).push( + FadePage( + page: PrescriptionDetailPage(prescriptionsResponseModel: prescription), + ), + ); + }), + ), + ], + ), + SizedBox(height: 12.h), + Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h), + SizedBox(height: 12.h), + ], + ); + }).toList(), + ], + ), + ) + : SizedBox.shrink(), + ), + ], + ), + ), + ), + ), + ), + ); + }, + ).paddingSymmetrical(24.h, 0.h), + ], + ); + }), + ), + ); + } +} diff --git a/lib/presentation/radiology/radiology_orders_page.dart b/lib/presentation/radiology/radiology_orders_page.dart new file mode 100644 index 0000000..d1b3830 --- /dev/null +++ b/lib/presentation/radiology/radiology_orders_page.dart @@ -0,0 +1,251 @@ +import 'dart:async'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/date_util.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; +import 'package:provider/provider.dart'; + +import '../../features/radiology/radiology_view_model.dart'; + +class RadiologyOrdersPage extends StatefulWidget { + const RadiologyOrdersPage({super.key}); + + @override + State createState() => _RadiologyOrdersPageState(); +} + +class _RadiologyOrdersPageState extends State { + late RadiologyViewModel radiologyViewModel; + + int? expandedIndex; + + @override + void initState() { + scheduleMicrotask(() { + radiologyViewModel.initRadiologyProvider(); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + radiologyViewModel = Provider.of(context); + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + appBar: AppBar( + title: LocaleKeys.radiology.tr(context: context).toText18(), + backgroundColor: AppColors.bgScaffoldColor, + ), + body: Padding( + padding: EdgeInsets.all(24.h), + child: SingleChildScrollView( + child: Consumer( + builder: (context, model, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.radiology.tr(context: context).toText24(isBold: true), + Utils.buildSvgWithAssets(icon: AppAssets.search_icon), + ], + ), + SizedBox(height: 16.h), + // Build Tab Bar + SizedBox(height: 16.h), + // Expandable list + ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: model.isRadiologyOrdersLoading ? 5 : model.patientRadiologyOrders.length, + itemBuilder: (context, index) { + final isExpanded = expandedIndex == index; + return model.isRadiologyOrdersLoading + ? const MoviesShimmerWidget() + : AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 500), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: AnimatedContainer( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + margin: EdgeInsets.symmetric(vertical: 8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), + child: InkWell( + onTap: () { + setState(() { + expandedIndex = isExpanded ? null : index; + }); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + CustomButton( + text: LocaleKeys.resultsAvailable.tr(context: context), + onPressed: () {}, + backgroundColor: AppColors.successColor.withOpacity(0.15), + borderColor: AppColors.successColor.withOpacity(0.01), + textColor: AppColors.successColor, + fontSize: 10, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + Icon(isExpanded ? Icons.expand_less : Icons.expand_more), + ], + ), + SizedBox(height: 8.h), + Row( + children: [ + Image.network( + model.patientRadiologyOrders[index].doctorImageURL!, + width: 24.h, + height: 24.h, + fit: BoxFit.fill, + ).circle(100), + SizedBox(width: 4.h), + model.patientRadiologyOrders[index].doctorName!.toText16(isBold: true) + ], + ), + SizedBox(height: 8.h), + Row( + children: [ + CustomButton( + text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(model.patientRadiologyOrders[index].orderDate), false), + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 24.h, + ), + SizedBox(width: 8.h), + CustomButton( + text: model.patientRadiologyOrders[index].clinicDescription!, + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 24.h, + ), + ], + ), + ], + ), + ), + AnimatedCrossFade( + firstChild: SizedBox.shrink(), + secondChild: Padding( + padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only(bottom: 8.h), + child: '● ${model.patientRadiologyOrders[index].description}'.toText14(weight: FontWeight.w500), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox(), + CustomButton( + icon: AppAssets.view_report_icon, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, + text: LocaleKeys.viewReport.tr(context: context), + onPressed: () {}, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14, + fontWeight: FontWeight.bold, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), + ], + ), + ], + ), + ), + crossFadeState: isExpanded ? CrossFadeState.showSecond : CrossFadeState.showFirst, + duration: Duration(milliseconds: 300), + ), + ], + ), + ), + ), + ), + ), + ); + }, + ), + ], + ); + }, + ), + ), + ), + ); + } + + Color getLabOrderStatusColor(num status) { + switch (status) { + case 44: + return AppColors.warningColorYellow; + case 45: + return AppColors.warningColorYellow; + case 16: + return AppColors.successColor; + case 17: + return AppColors.successColor; + default: + return AppColors.greyColor; + } + } + + String getLabOrderStatusText(num status) { + switch (status) { + case 44: + return LocaleKeys.resultsPending.tr(context: context); + case 45: + return LocaleKeys.resultsPending.tr(context: context); + case 16: + return LocaleKeys.resultsAvailable.tr(context: context); + case 17: + return LocaleKeys.resultsAvailable.tr(context: context); + default: + return ""; + } + } +} diff --git a/lib/providers/bottom_navigation_provider.dart b/lib/providers/bottom_navigation_provider.dart deleted file mode 100644 index 58a0d4e..0000000 --- a/lib/providers/bottom_navigation_provider.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:flutter/material.dart'; - -class BottomNavigationProvider extends ChangeNotifier { - int _currentIndex = 0; - - int get currentIndex => _currentIndex; - - void setIndex(int index) { - _currentIndex = index; - notifyListeners(); - } -} \ No newline at end of file diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index 3aefebf..166783d 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -30,9 +30,16 @@ class AppColors { static const Color textColor = Color(0xFF2E3039); static const Color borderOnlyColor = Color(0xFF2E3039); static const Color dividerColor = Color(0xFFD2D2D2); + static const Color warningColorYellow = Color(0xFFF4A308); static const Color blackBgColor = Color(0xFF2E3039); static const blackColor = textColor; static const inputLabelTextColor = Color(0xff898A8D); + static const greyTextColor = Color(0xFF8F9AA3); + + + static const lightGreenColor = Color(0xFF0ccedde); + static const textGreenColor = Color(0xFF18C273); + static const Color ratingColorYellow = Color(0xFFFFAF15); //Chips static const Color successColor = Color(0xff18C273); @@ -42,10 +49,12 @@ class AppColors { static const Color warningColor = Color(0xFFFFCC00); static const Color greyColor = Color(0xFFEFEFF0); - static const Color successLightColor = Color(0xFF18C27326); - static const Color errorLightColor = Color(0xFFED1C2B1A); - static const Color alertLightColor = Color(0xFFD48D0526); - static const Color infoLightColor = Color(0xFF0B85F726); - static const Color warningLightColor = Color(0xFFFFCC0026); - static const Color greyLightColor = Color(0xFFEFEFF026); +static const Color successLightColor = Color(0xFF18C273); +static const Color errorLightColor = Color(0xFFED1C2B); +static const Color alertLightColor = Color(0xFFD48D05); +static const Color infoLightColor = Color(0xFF0B85F7); +static const Color warningLightColor = Color(0xFFFFCC00); +static const Color greyLightColor = Color(0xFFEFEFF0); + +static const Color bottomNAVBorder = Color(0xFFEEEEEE); } diff --git a/lib/widgets/bottom_navigation/bottom_navigation.dart b/lib/widgets/bottom_navigation/bottom_navigation.dart new file mode 100644 index 0000000..a052bc6 --- /dev/null +++ b/lib/widgets/bottom_navigation/bottom_navigation.dart @@ -0,0 +1,59 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; + +class BottomNavigation extends StatelessWidget { + const BottomNavigation({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 12), + decoration: const BoxDecoration( + color: Colors.white, + border: Border( + top: BorderSide(color: AppColors.bottomNAVBorder, width: 0.5), + ), + + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + _buildNavItem(AppAssets.homeBottom, LocaleKeys.home.tr()), + _buildNavItem(AppAssets.myFilesBottom, LocaleKeys.myFiles.tr()), + _buildNavItem(AppAssets.bookAppoBottom, LocaleKeys.appointment.tr(), iconSize: 32), + _buildNavItem(AppAssets.toDoBottom, LocaleKeys.todoList.tr()), + _buildNavItem(AppAssets.servicesBottom, LocaleKeys.services2.tr()), + ], + ), + ); + } + + Widget _buildNavItem(String iconName, String label,{ double iconSize = 24}) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.all(10), + child: Utils.buildSvgWithAssets( + icon: iconName, + height: iconSize, + width: iconSize + ), + ), + // const SizedBox(height: 4), + Text( + label, + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + color: Colors.black87, + ), + ), + ], + ); + } +} diff --git a/lib/widgets/dropdown/country_dropdown_widget.dart b/lib/widgets/dropdown/country_dropdown_widget.dart index 5e4bddf..3d28e23 100644 --- a/lib/widgets/dropdown/country_dropdown_widget.dart +++ b/lib/widgets/dropdown/country_dropdown_widget.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; @@ -5,17 +6,26 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; class CustomCountryDropdown extends StatefulWidget { final List countryList; final Function(CountryEnum)? onCountryChange; + final Function(String)? onPhoneNumberChanged; final bool isRtl; + final bool isFromBottomSheet; + final bool isEnableTextField; + Widget? textField; - const CustomCountryDropdown({ + CustomCountryDropdown({ Key? key, required this.countryList, this.onCountryChange, + this.onPhoneNumberChanged, required this.isRtl, + this.isFromBottomSheet = false, + this.isEnableTextField = false, + this.textField, }) : super(key: key); @override @@ -26,48 +36,103 @@ class _CustomCountryDropdownState extends State { CountryEnum? selectedCountry; late OverlayEntry _overlayEntry; bool _isDropdownOpen = false; + FocusNode textFocusNode = new FocusNode(); @override void initState() { super.initState(); selectedCountry = CountryEnum.saudiArabia; + + if (widget.isEnableTextField && widget.isFromBottomSheet) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted && textFocusNode.canRequestFocus) { + FocusScope.of(context).requestFocus(textFocusNode); + } + }); + } + } + + @override + void dispose() { + textFocusNode.dispose(); + super.dispose(); } @override Widget build(BuildContext context) { - return GestureDetector( - onTap: () { - if (_isDropdownOpen) { - _closeDropdown(); - } else { - _openDropdown(); - } - }, - child: Container( - height: 40.h, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 10.h), - child: Row( - children: [ - Utils.buildSvgWithAssets(icon: selectedCountry != null ? selectedCountry!.iconPath : AppAssets.ksa, width: 40.h, height: 40.h), - SizedBox(width: 8.h), - Utils.buildSvgWithAssets(icon: _isDropdownOpen ? AppAssets.dropdow_icon : AppAssets.dropdow_icon), - SizedBox(width: 4.h), + return Container( + height: 40.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 10.h), + child: Row( + children: [ + GestureDetector( + onTap: () { + if (_isDropdownOpen) { + _closeDropdown(); + } else { + _openDropdown(); + } + }, + child: Utils.buildSvgWithAssets(icon: selectedCountry != null ? selectedCountry!.iconPath : AppAssets.ksa, width: 40.h, height: 40.h)), + SizedBox(width: 8.h), + Utils.buildSvgWithAssets(icon: _isDropdownOpen ? AppAssets.dropdow_icon : AppAssets.dropdow_icon), + SizedBox(width: 4.h), + if (widget.isFromBottomSheet) + GestureDetector( + onTap: () { + if (widget.isEnableTextField && textFocusNode.canRequestFocus) { + FocusScope.of(context).requestFocus(textFocusNode); + } + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + children: [ + Text( + LocaleKeys.phoneNumber.tr(), + style: TextStyle(fontSize: 12.fSize, height: 21 / 12, fontWeight: FontWeight.w500, letterSpacing: -1), + ), + ], + ), + Row( + children: [ + Text( + selectedCountry!.countryCode, + style: TextStyle(fontSize: 12.fSize, height: 21 / 18, fontWeight: FontWeight.w600, letterSpacing: -0.2), + ), + SizedBox(width: 4.h), + if (widget.isEnableTextField) + SizedBox( + height: 20, + width: 200, + child: TextField( + focusNode: textFocusNode, + decoration: InputDecoration(hintText: "", isDense: true, border: InputBorder.none), + keyboardType: TextInputType.phone, + onChanged: widget.onPhoneNumberChanged), + ), + ], + ) + ], + ), + ), + if (!widget.isFromBottomSheet) Text( selectedCountry != null ? selectedCountry!.displayName : "Select Country", - style: TextStyle( - fontSize: 14.fSize, - height: 21 / 14, - fontWeight: FontWeight.w500, - letterSpacing: -0.2, - ), + style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, letterSpacing: -0.2), ), - ], - ), + ], ), ); } void _openDropdown() { + if (textFocusNode.hasFocus) { + textFocusNode.unfocus(); + } + RenderBox renderBox = context.findRenderObject() as RenderBox; Offset offset = renderBox.localToGlobal(Offset.zero); @@ -84,27 +149,11 @@ class _CustomCountryDropdownState extends State { ), Positioned( top: offset.dy + renderBox.size.height, - left: widget.isRtl ? offset.dx + 15.h : offset.dx - 15.h, + left: widget.isRtl ? offset.dx + 6.h : offset.dx - 6.h, width: renderBox.size.width, child: Material( child: Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: Colors.white, - borderRadius: 12, - ), - // decoration: BoxDecoration( - // borderRadius: BorderRadius.circular(12), - // boxShadow: [ - // BoxShadow( - // color: Color(0xFFF8F8FA), - // blurRadius: 8.h, - // offset: Offset( - // 0, - // 2, - // ), - // ), - // ], - // ), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.white, borderRadius: 12), child: Column( children: widget.countryList .map( @@ -117,25 +166,13 @@ class _CustomCountryDropdownState extends State { _closeDropdown(); }, child: Container( - padding: EdgeInsets.symmetric(vertical: 12.h, horizontal: 16.h), - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - borderRadius: 16.h, - ), + padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 16.h), child: Row( children: [ - Utils.buildSvgWithAssets( - icon: country.iconPath, - width: 38.h, - height: 38.h, - ), - SizedBox(width: 12.h), - Text(country.displayName, - style: TextStyle( - fontSize: 14.fSize, - height: 21 / 14, - fontWeight: FontWeight.w500, - letterSpacing: -0.2, - )), + Utils.buildSvgWithAssets(icon: country.iconPath, width: 38.h, height: 38.h), + if (!widget.isFromBottomSheet) SizedBox(width: 12.h), + if (!widget.isFromBottomSheet) Text(country.displayName, style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, letterSpacing: -0.2)), ], ), ), @@ -161,5 +198,13 @@ class _CustomCountryDropdownState extends State { setState(() { _isDropdownOpen = false; }); + + if (widget.isEnableTextField && widget.isFromBottomSheet) { + Future.delayed(Duration(milliseconds: 100), () { + if (mounted && textFocusNode.canRequestFocus) { + FocusScope.of(context).requestFocus(textFocusNode); + } + }); + } } } diff --git a/lib/widgets/dropdown/dropdown_widget.dart b/lib/widgets/dropdown/dropdown_widget.dart index 268e44a..2a0cad3 100644 --- a/lib/widgets/dropdown/dropdown_widget.dart +++ b/lib/widgets/dropdown/dropdown_widget.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart' show Icons, PopupMenuItem, showMenu, Colo import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; class DropdownWidget extends StatelessWidget { final String labelText; @@ -16,6 +17,7 @@ class DropdownWidget extends StatelessWidget { final EdgeInsetsGeometry? padding; final bool hasSelectionCustomIcon; final String? selectionCustomIcon; + final String? leadingIcon; const DropdownWidget({ Key? key, @@ -30,29 +32,49 @@ class DropdownWidget extends StatelessWidget { this.padding, this.hasSelectionCustomIcon = false, this.selectionCustomIcon, + this.leadingIcon, }) : super(key: key); @override Widget build(BuildContext context) { + Widget content = Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [_buildLabelText(), _buildDropdown(context)], + ); + return Container( padding: padding, - alignment: Alignment.center, + alignment: Alignment.center, // This might need adjustment based on layout decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: Colors.white, borderRadius: isAllowRadius ? 15.h : null, side: isBorderAllowed ? BorderSide(color: const Color(0xffefefef), width: 1) : null, ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, + child: Row( + // Wrap with a Row + crossAxisAlignment: CrossAxisAlignment.center, // Align items vertically in the center children: [ - _buildLabelText(), - _buildDropdown(context), + if (leadingIcon != null) ...[ + _buildLeadingIcon(), + SizedBox(width: 3.h), + ], + Expanded(child: content), ], ), ); } + Widget _buildLeadingIcon() { + return Container( + height: 40.h, + width: 40.h, + margin: EdgeInsets.only(right: 10.h), + padding: EdgeInsets.all(8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 10.h, color: AppColors.greyColor), + child: Utils.buildSvgWithAssets(icon: leadingIcon!)); + } + Widget _buildLabelText() { return Text( labelText, diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart index ca72fe8..b8d36e7 100644 --- a/lib/widgets/input_widget.dart +++ b/lib/widgets/input_widget.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dart'; // TODO: Import AppColors if bgRedColor is defined there // import 'package:hmg_patient_app_new/core/ui_utils/app_colors.dart'; @@ -72,17 +74,32 @@ class TextInputWidget extends StatelessWidget { child: Row( textDirection: Directionality.of(context), children: [ - if (isAllowLeadingIcon && leadingIcon != null) _buildLeadingIcon(context), - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildLabelText(), - _buildTextField(context), - ], - ), - ), + if (isAllowLeadingIcon && leadingIcon != null && !isCountryDropDown) + _buildLeadingIcon(context), + isCountryDropDown + ? CustomCountryDropdown( + countryList: CountryEnum.values, + onCountryChange: (CountryEnum? value) { + print(value); + }, + isRtl: Directionality.of(context) == TextDirection.rtl, + isFromBottomSheet: isCountryDropDown, + isEnableTextField: true, + onPhoneNumberChanged: (value) { + print(value); + }, + textField: _buildTextField(context), + ) + : Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildLabelText(), + _buildTextField(context), + ], + ), + ), ], ), ), diff --git a/pubspec.yaml b/pubspec.yaml index 9c78b48..a2637f5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -82,6 +82,7 @@ flutter: - assets/ - assets/fonts/ - assets/langs/ + - assets/json/ - assets/images/ - assets/images/svg/ - assets/images/png/ @@ -99,8 +100,8 @@ flutter: # weight: 700 - asset: assets/fonts/poppins/Poppins-SemiBold.ttf weight: 600 -# - asset: assets/fonts/poppins/Poppins-Medium.ttf -# weight: 500 + - asset: assets/fonts/poppins/Poppins-Medium.ttf + weight: 500 - asset: assets/fonts/poppins/Poppins-Regular.ttf weight: 400 # - asset: assets/fonts/poppins/Poppins-Light.ttf @@ -115,8 +116,8 @@ flutter: fonts: - asset: assets/fonts/gess_two/GE_SS_Two_Bold.otf weight: 600 -# - asset: assets/fonts/gess_two/GE_SS_Two_Medium.otf -# weight: 500 + - asset: assets/fonts/gess_two/GE_SS_Two_Medium.otf + weight: 500 - asset: assets/fonts/gess_two/GE_SS_Two_Light.otf weight: 400