From 17a1430ef024458603ecda4540fff74238cce454 Mon Sep 17 00:00:00 2001 From: Patrick Alvin Alcala Date: Wed, 5 Feb 2025 17:06:13 +0800 Subject: [PATCH] update --- lib/main.dart | 8 ++- lib/pages/add_category.dart | 91 ++++++++++++++++++++++++++++++++ lib/pages/add_generics.dart | 43 +++++++++++++-- lib/pages/add_medicine.dart | 52 +++++++++++++----- lib/pages/add_type.dart | 37 ++++++++++++- lib/pages/list_stocks.dart | 18 +++++++ lib/pages/login_page.dart | 27 +++++++++- lib/pages/main_page.dart | 6 +++ lib/pages/register_page.dart | 2 +- lib/tables/ref_categories.dart | 6 +++ lib/widgets/snackbar_widget.dart | 6 ++- 11 files changed, 271 insertions(+), 25 deletions(-) create mode 100644 lib/pages/add_category.dart diff --git a/lib/main.dart b/lib/main.dart index d2cebb4..6c2ace2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:pharmacy_mobile/pages/add_category.dart'; import 'package:pharmacy_mobile/pages/add_generics.dart'; import 'package:pharmacy_mobile/pages/add_medicine.dart'; import 'package:pharmacy_mobile/pages/add_stock.dart'; @@ -35,7 +36,7 @@ final _router = GoRouter( initialLocation: '/', routes: [ GoRoute( - name: 'index', // Optional, add name to your routes. Allows you navigate by name instead of path + name: 'index', path: '/', builder: (context, state) => IndexPage(), ), @@ -69,6 +70,11 @@ final _router = GoRouter( path: '/addtype', builder: (context, state) => AddTypePage(), ), + GoRoute( + name: 'addcategory', + path: '/addcategory', + builder: (context, state) => AddCategoryPage(), + ), GoRoute( name: 'addstock', path: '/addstock', diff --git a/lib/pages/add_category.dart b/lib/pages/add_category.dart new file mode 100644 index 0000000..5e5effd --- /dev/null +++ b/lib/pages/add_category.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:internet_connection_checker/internet_connection_checker.dart'; +import 'package:pharmacy_mobile/tables/ref_categories.dart'; +import 'package:pharmacy_mobile/widgets/button_widget.dart'; +import 'package:pharmacy_mobile/widgets/form_border_widget.dart'; +import 'package:pharmacy_mobile/widgets/input_widget.dart'; +import 'package:pharmacy_mobile/widgets/page_background_widget.dart'; +import 'package:pharmacy_mobile/widgets/snackbar_widget.dart'; +import 'package:pharmacy_mobile/widgets/text_widget.dart'; +import 'package:pharmacy_mobile/widgets/title_widget.dart'; +import 'package:go_router/go_router.dart'; + +class AddCategoryPage extends StatefulWidget { + const AddCategoryPage({super.key}); + + @override + State createState() => _AddCategoryPageState(); +} + +class _AddCategoryPageState extends State { + final _formKey = GlobalKey(); + final _categoryController = TextEditingController(); + final _refCategories = RefCategories(); + bool _isLoading = false; + + void _saveCategory() async { + setState(() => _isLoading = true); + + try { + if (await InternetConnectionChecker.instance.hasConnection) { + await _refCategories.postCategory(_categoryController.text.toUpperCase()); + + if (mounted) { + showNotification(context, 'Category Saved', true); + + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + context.push('/main'); + } + }); + } + } else { + if (mounted) { + showNotification(context, 'Error: No Internet Connection', false); + } + } + } catch (e) { + if (mounted) { + showNotification(context, 'Error: $e', false); + } + } finally { + setState(() => _isLoading = false); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: PageBackgroundWidget( + child: Center( + child: Column( + children: [ + const Gap(96), + const TitleWidget(firstTextSize: 20, secondTextSize: 32), + const Gap(32), + const TextWidget(text: 'Add Category'), + const Gap(16), + FormBorderWidget( + color: 'blue', + child: Form( + key: _formKey, + child: Center( + child: Column( + children: [ + InputWidget(label: 'Category Name', controller: _categoryController), + const Gap(32), + if (_isLoading) + Center(child: CircularProgressIndicator(color: Colors.white)) + else + ButtonWidget(text: 'Save Category', onPressed: _saveCategory) + ], + ), + )), + ) + ], + ), + )), + ); + } +} diff --git a/lib/pages/add_generics.dart b/lib/pages/add_generics.dart index fa95884..b5f84ed 100644 --- a/lib/pages/add_generics.dart +++ b/lib/pages/add_generics.dart @@ -1,6 +1,6 @@ import 'package:gap/gap.dart'; import 'package:flutter/material.dart'; -// import 'package:pharmacy_mobile/auth/auth_service.dart'; +import 'package:internet_connection_checker/internet_connection_checker.dart'; import 'package:pharmacy_mobile/tables/ref_categories.dart'; import 'package:pharmacy_mobile/tables/ref_generic_names.dart'; import 'package:pharmacy_mobile/widgets/button_widget.dart'; @@ -8,9 +8,11 @@ import 'package:pharmacy_mobile/widgets/dropdown_widget.dart'; import 'package:pharmacy_mobile/widgets/form_border_widget.dart'; import 'package:pharmacy_mobile/widgets/input_widget.dart'; import 'package:pharmacy_mobile/widgets/page_background_widget.dart'; +import 'package:pharmacy_mobile/widgets/snackbar_widget.dart'; import 'package:pharmacy_mobile/widgets/text_widget.dart'; import 'package:pharmacy_mobile/widgets/title_widget.dart'; import 'package:visibility_detector/visibility_detector.dart'; +import 'package:go_router/go_router.dart'; class AddGenericsPage extends StatefulWidget { const AddGenericsPage({super.key}); @@ -26,6 +28,7 @@ class _AddGenericsPageState extends State { final _nameController = TextEditingController(); final _formKey = GlobalKey(); bool _isVisible = false; + bool _isLoading = false; late List _categoryList = []; late String _selectedCategory = ''; @@ -40,9 +43,35 @@ class _AddGenericsPageState extends State { } void saveGeneric() async { - _categoryUUID = await _refCategories.getUUID(_selectedCategory); + setState(() => _isLoading = true); - await _refGenericNames.postGeneric(_nameController.text, _categoryUUID); + try { + if (await InternetConnectionChecker.instance.hasConnection) { + _categoryUUID = await _refCategories.getUUID(_selectedCategory); + + await _refGenericNames.postGeneric(_nameController.text, _categoryUUID); + + if (mounted) { + showNotification(context, 'Generic Name Saved', true); + + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + context.push('/main'); + } + }); + } + } else { + if (mounted) { + showNotification(context, 'Error: No Internet Connection', false); + } + } + } catch (e) { + if (mounted) { + showNotification(context, 'Error: $e', false); + } + } finally { + setState(() => _isLoading = false); + } } @override @@ -58,6 +87,7 @@ class _AddGenericsPageState extends State { _selectedCategory = ''; _categoryUUID = ''; _isVisible = false; + _isLoading = false; super.dispose(); } @@ -96,8 +126,11 @@ class _AddGenericsPageState extends State { list: _categoryList, listTitle: 'category_name', onChanged: _updateCategory), - const Gap(16), - ButtonWidget(text: 'Add', onPressed: saveGeneric) + const Gap(32), + if (_isLoading) + Center(child: CircularProgressIndicator(color: Colors.white)) + else + ButtonWidget(text: 'Add', onPressed: saveGeneric) ], )), ) diff --git a/lib/pages/add_medicine.dart b/lib/pages/add_medicine.dart index ff9b42d..aeaeeec 100644 --- a/lib/pages/add_medicine.dart +++ b/lib/pages/add_medicine.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; +import 'package:internet_connection_checker/internet_connection_checker.dart'; import 'package:pharmacy_mobile/tables/ref_categories.dart'; import 'package:pharmacy_mobile/tables/ref_generic_names.dart'; import 'package:pharmacy_mobile/tables/ref_manufactorers.dart'; @@ -10,6 +11,7 @@ import 'package:pharmacy_mobile/widgets/dropdown_widget.dart'; import 'package:pharmacy_mobile/widgets/form_border_widget.dart'; import 'package:pharmacy_mobile/widgets/input_widget.dart'; import 'package:pharmacy_mobile/widgets/page_background_widget.dart'; +import 'package:pharmacy_mobile/widgets/snackbar_widget.dart'; import 'package:pharmacy_mobile/widgets/text_widget.dart'; import 'package:pharmacy_mobile/widgets/title_widget.dart'; import 'package:visibility_detector/visibility_detector.dart'; @@ -32,6 +34,7 @@ class _AddMedicinePageState extends State { final _nameController = TextEditingController(); final FocusNode _focusNode = FocusNode(); bool _isVisible = false; + bool _isLoading = false; late List _genericNameList = []; late String _selectedGeneric = ''; @@ -42,9 +45,15 @@ class _AddMedicinePageState extends State { late String _selectedManufactorer = ''; void autoRun() async { - _genericNameList = await _refGenericNames.getList(); - _typeList = await _refTypes.getList(); - _manufactorerList = await _refManufactorer.getList(); + if (await InternetConnectionChecker.instance.hasConnection) { + _genericNameList = await _refGenericNames.getList(); + _typeList = await _refTypes.getList(); + _manufactorerList = await _refManufactorer.getList(); + } else { + if (mounted) { + showNotification(context, 'Error: No Internet Connection', false); + } + } } void _updateGeneric(dynamic generic) async { @@ -52,9 +61,7 @@ class _AddMedicinePageState extends State { final catuuid = await _refGenericNames.getCategoryUUID(_selectedGeneric); final catname = await _refCategories.getName(catuuid); - setState(() { - _selectedCategory = catname; - }); + setState(() => _selectedCategory = catname); } void _updateType(dynamic type) { @@ -66,12 +73,28 @@ class _AddMedicinePageState extends State { } void _saveMedicine() async { - final medName = _nameController.text; - final medGenericUUID = await _refGenericNames.getUUID(_selectedGeneric); - final medTypeUUID = await _refTypes.getUUID(_selectedType); - final medManufactorerUUID = await _refManufactorer.getUUID(_selectedManufactorer); + setState(() => _isLoading = true); - await _refMedicines.postMedicine(medName, medGenericUUID, medManufactorerUUID, medTypeUUID); + try { + if (await InternetConnectionChecker.instance.hasConnection) { + final medName = _nameController.text; + final medGenericUUID = await _refGenericNames.getUUID(_selectedGeneric); + final medTypeUUID = await _refTypes.getUUID(_selectedType); + final medManufactorerUUID = await _refManufactorer.getUUID(_selectedManufactorer); + + await _refMedicines.postMedicine(medName, medGenericUUID, medManufactorerUUID, medTypeUUID); + } else { + if (mounted) { + showNotification(context, 'Error: No Internet Connection', false); + } + } + } catch (e) { + if (mounted) { + showNotification(context, 'Error: $e', false); + } + } finally { + setState(() => _isLoading = false); + } } @override @@ -147,8 +170,11 @@ class _AddMedicinePageState extends State { list: _manufactorerList, listTitle: 'manufactorer_name', onChanged: _updateManufactorer), - const Gap(16), - ButtonWidget(text: 'Save Medicine', onPressed: _saveMedicine) + const Gap(32), + if (_isLoading) + Center(child: CircularProgressIndicator(color: Colors.white)) + else + ButtonWidget(text: 'Save Medicine', onPressed: _saveMedicine) ], ), )), diff --git a/lib/pages/add_type.dart b/lib/pages/add_type.dart index 50c8b26..369137f 100644 --- a/lib/pages/add_type.dart +++ b/lib/pages/add_type.dart @@ -1,12 +1,15 @@ import 'package:gap/gap.dart'; import 'package:flutter/material.dart'; +import 'package:internet_connection_checker/internet_connection_checker.dart'; import 'package:pharmacy_mobile/tables/ref_types.dart'; import 'package:pharmacy_mobile/widgets/button_widget.dart'; import 'package:pharmacy_mobile/widgets/form_border_widget.dart'; import 'package:pharmacy_mobile/widgets/input_widget.dart'; import 'package:pharmacy_mobile/widgets/page_background_widget.dart'; +import 'package:pharmacy_mobile/widgets/snackbar_widget.dart'; import 'package:pharmacy_mobile/widgets/text_widget.dart'; import 'package:pharmacy_mobile/widgets/title_widget.dart'; +import 'package:go_router/go_router.dart'; class AddTypePage extends StatefulWidget { const AddTypePage({super.key}); @@ -20,8 +23,35 @@ class _AddTypePageState extends State { final _typeController = TextEditingController(); final _refTypes = RefTypes(); + bool _isLoading = false; + void saveType() async { - await _refTypes.postType(_typeController.text); + setState(() => _isLoading = true); + try { + if (await InternetConnectionChecker.instance.hasConnection) { + await _refTypes.postType(_typeController.text); + + if (mounted) { + showNotification(context, 'Medicine Type Saved', true); + + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + context.push('/main'); + } + }); + } + } else { + if (mounted) { + showNotification(context, 'Error: No Internet Connection', false); + } + } + } catch (e) { + if (mounted) { + showNotification(context, 'Error: $e', false); + } + } finally { + setState(() => _isLoading = false); + } } @override @@ -52,7 +82,10 @@ class _AddTypePageState extends State { children: [ InputWidget(label: 'Type Name', controller: _typeController), const Gap(16), - ButtonWidget(text: 'Save Type', onPressed: saveType) + if (_isLoading) + Center(child: CircularProgressIndicator(color: Colors.white)) + else + ButtonWidget(text: 'Save Type', onPressed: saveType) ], ), )), diff --git a/lib/pages/list_stocks.dart b/lib/pages/list_stocks.dart index 64e683d..d0f7e22 100644 --- a/lib/pages/list_stocks.dart +++ b/lib/pages/list_stocks.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:pharmacy_mobile/tables/stocks.dart'; import 'package:pharmacy_mobile/widgets/datatable_widget.dart'; @@ -97,6 +98,23 @@ class _ListStocksPageState extends State { child: CircularProgressIndicator( color: Colors.white, )) + else if (_stockList.isEmpty) + // TextWidget(text: 'No Stock Listed') + Container( + decoration: BoxDecoration( + border: Border.all(color: const Color.fromRGBO(205, 59, 208, 0.702), width: 2), + borderRadius: BorderRadius.circular(12), + ), + padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16.0), + child: Text( + 'No Stock Listed', + style: GoogleFonts.outfit( + color: Color.fromRGBO(255, 255, 255, 1), + fontSize: 14, + fontWeight: FontWeight.normal, + ), + ), + ) else DataTableWidget( column: _createColumns(), diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart index 716be22..42378c9 100644 --- a/lib/pages/login_page.dart +++ b/lib/pages/login_page.dart @@ -10,6 +10,7 @@ import 'package:pharmacy_mobile/widgets/snackbar_widget.dart'; import 'package:pharmacy_mobile/widgets/text_widget.dart'; import 'package:pharmacy_mobile/widgets/title_widget.dart'; import 'package:internet_connection_checker/internet_connection_checker.dart'; +import 'package:supabase_flutter/supabase_flutter.dart'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @@ -32,11 +33,26 @@ class _LoginPageState extends State { final email = _emailController.text; final password = _passwordController.text; + if (email.isEmpty) { + if (mounted) { + showNotification(context, 'Error: Please enter a valid email', false); + } + return; + } + + if (password.isEmpty) { + if (mounted) { + showNotification(context, 'Error: Please enter a password', false); + } + return; + } + setState(() => _isLoading = true); try { if (await InternetConnectionChecker.instance.hasConnection) { await _authService.signIn(email, password); + if (mounted) { showNotification(context, 'Login Successful', true); @@ -52,8 +68,15 @@ class _LoginPageState extends State { } } } catch (e) { - if (mounted) { - showNotification(context, 'Error: $e', false); + if (e is AuthException) { + final errorMessage = e.message; + + if (mounted) { + // showNotification(context, 'Error: $errorMessage', false); + if (errorMessage == 'Invalid login credentials') { + showNotification(context, 'Error: Invalid Email or Password', false); + } + } } } finally { setState(() => _isLoading = false); diff --git a/lib/pages/main_page.dart b/lib/pages/main_page.dart index 2dea59a..07f048d 100644 --- a/lib/pages/main_page.dart +++ b/lib/pages/main_page.dart @@ -47,6 +47,12 @@ class MainPage extends StatelessWidget { onPressed: () => {context.push('/addtype')}, color: 'blue'), const Gap(16), + MenuWidget( + icon: FontAwesomeIcons.squarePlus, + text: 'Add Category', + onPressed: () => {context.push('/addcategory')}, + color: 'blue'), + const Gap(16), MenuWidget( icon: FontAwesomeIcons.squarePlus, text: 'Add Generics', onPressed: gotoAddGenerics, color: 'blue'), const Gap(32), diff --git a/lib/pages/register_page.dart b/lib/pages/register_page.dart index ba21097..09dd801 100644 --- a/lib/pages/register_page.dart +++ b/lib/pages/register_page.dart @@ -11,7 +11,7 @@ class RegisterPage extends StatefulWidget { const RegisterPage({super.key}); @override - _RegisterPageState createState() => _RegisterPageState(); + State createState() => _RegisterPageState(); } class _RegisterPageState extends State { diff --git a/lib/tables/ref_categories.dart b/lib/tables/ref_categories.dart index 1f19af6..8dfab27 100644 --- a/lib/tables/ref_categories.dart +++ b/lib/tables/ref_categories.dart @@ -1,4 +1,5 @@ import 'package:supabase_flutter/supabase_flutter.dart'; +import 'package:uuid/uuid.dart'; class RefCategories { final SupabaseClient _supabase = Supabase.instance.client; @@ -17,4 +18,9 @@ class RefCategories { final data = await _supabase.from('ref_categories').select('category_name').eq('ref_categories_uuid', uuid); return data.first['category_name']; } + + Future postCategory(String name) async { + final categoryUUID = Uuid().v4(); + await _supabase.from('ref_categories').insert({'ref_categories_uuid': categoryUUID, 'category_name': name}); + } } diff --git a/lib/widgets/snackbar_widget.dart b/lib/widgets/snackbar_widget.dart index 4a6b726..e21563c 100644 --- a/lib/widgets/snackbar_widget.dart +++ b/lib/widgets/snackbar_widget.dart @@ -1,13 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; void showNotification(BuildContext context, String text, bool isPositive) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text(text), + content: Text(text, style: GoogleFonts.inter(fontSize: 14, fontWeight: FontWeight.w600)), backgroundColor: isPositive ? const Color.fromRGBO(37, 106, 32, 1) : const Color.fromRGBO(188, 59, 50, 1), elevation: 4.0, behavior: SnackBarBehavior.floating, duration: const Duration(seconds: 3), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), // Adjust the radius value as needed + ), ), ); }