diff --git a/lib/main.dart b/lib/main.dart index 91af6fd..d2cebb4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -90,16 +90,9 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp.router( debugShowCheckedModeBanner: false, - // initialRoute: '/', - // routes: { - // '/': (context) => const IndexPage(), - // '/login': (context) => const LoginPage(), - // }, - // debugShowCheckedModeBanner: false, - // theme: ThemeData( - // colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - // useMaterial3: true, - // ) + theme: ThemeData( + useMaterial3: true, + ), routerConfig: _router, ); } diff --git a/lib/pages/list_stocks.dart b/lib/pages/list_stocks.dart index 3c586ee..349c8a5 100644 --- a/lib/pages/list_stocks.dart +++ b/lib/pages/list_stocks.dart @@ -1,13 +1,11 @@ -import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; +import 'package:intl/intl.dart'; import 'package:pharmacy_mobile/tables/stocks.dart'; import 'package:pharmacy_mobile/widgets/datatable_widget.dart'; import 'package:pharmacy_mobile/widgets/page_background_widget.dart'; import 'package:pharmacy_mobile/widgets/text_widget.dart'; import 'package:pharmacy_mobile/widgets/title_widget.dart'; -import 'package:intl/intl.dart'; class ListStocksPage extends StatefulWidget { const ListStocksPage({super.key}); @@ -20,18 +18,36 @@ class _ListStocksPageState extends State { final _stocks = Stocks(); late List _stockList = []; + bool _isLoading = false; List _createRows() { + final today = DateTime.now().toUtc(); + return _stockList.map((item) { final dateString = item['expiration_date']; - final date = DateTime.parse(dateString); + final date = DateTime.parse(dateString).toUtc(); final formattedDate = DateFormat('MMMM d, yyyy').format(date); - return DataRow(cells: [ - DataCell(Text(item['medicine_name'])), - DataCell(Text(item['quantity'].toString())), - DataCell(Text(formattedDate)), - ]); + if (date.isBefore(today)) { + return DataRow( + cells: [ + DataCell(Text(item['medicine_name'], style: const TextStyle(color: Color.fromRGBO(188, 59, 50, 1)))), + DataCell(Text(item['quantity'].toString(), style: const TextStyle(color: Color.fromRGBO(188, 59, 50, 1)))), + DataCell( + Text(formattedDate, + style: const TextStyle(color: Color.fromRGBO(188, 59, 50, 1))), // Highlight expired items + ), + ], + ); + } else { + return DataRow( + cells: [ + DataCell(Text(item['medicine_name'])), + DataCell(Text(item['quantity'].toString())), + DataCell(Text(formattedDate)), + ], + ); + } }).toList(); } @@ -44,8 +60,13 @@ class _ListStocksPageState extends State { } void autoRun() async { + setState(() { + _isLoading = true; + }); _stockList = await _stocks.getList(); - setState(() {}); + setState(() { + _isLoading = false; + }); } @override @@ -63,14 +84,27 @@ class _ListStocksPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - body: PageBackgroundWidget( - child: Column(children: [ - const Gap(104), - const TitleWidget(firstTextSize: 20, secondTextSize: 32), - const Gap(32), - const TextWidget(text: 'List of Stocks'), - const Gap(16), - DataTableWidget(column: _createColumns(), row: _createRows()), - ]))); + body: PageBackgroundWidget( + child: Column( + children: [ + const Gap(104), + const TitleWidget(firstTextSize: 20, secondTextSize: 32), + const Gap(32), + const TextWidget(text: 'List of Stocks'), + const Gap(16), + if (_isLoading) + Center( + child: CircularProgressIndicator( + color: Colors.white, + )) + else + DataTableWidget( + column: _createColumns(), + row: _createRows(), + ), + ], + ), + ), + ); } } diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart index dcc9f68..8b3f6ad 100644 --- a/lib/pages/login_page.dart +++ b/lib/pages/login_page.dart @@ -6,6 +6,7 @@ import 'package:pharmacy_mobile/auth/auth_service.dart'; import 'package:pharmacy_mobile/widgets/button_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:internet_connection_checker/internet_connection_checker.dart'; @@ -37,13 +38,7 @@ class _LoginPageState extends State { if (await InternetConnectionChecker.instance.hasConnection) { await _authService.signIn(email, password); if (mounted) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text('Login Successful'), - backgroundColor: const Color.fromRGBO(37, 106, 32, 1), - elevation: 4.0, - behavior: SnackBarBehavior.floating, - duration: Duration(seconds: 5), - )); + showNotification(context, 'Login Successful', true); WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { @@ -53,24 +48,12 @@ class _LoginPageState extends State { } } else { if (mounted) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text('No Internet Connection'), - backgroundColor: const Color.fromRGBO(188, 59, 50, 1), - elevation: 4.0, - behavior: SnackBarBehavior.floating, - duration: Duration(seconds: 5), - )); + showNotification(context, 'Error: No Internet Connection', false); } } } catch (e) { if (mounted) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text('Error: $e'), - backgroundColor: const Color.fromRGBO(188, 59, 50, 1), - elevation: 4.0, - behavior: SnackBarBehavior.floating, - duration: Duration(seconds: 5), - )); + showNotification(context, 'Error: $e', false); } } finally { setState(() => _isLoading = false); @@ -110,7 +93,8 @@ class _LoginPageState extends State { child: Container( padding: EdgeInsets.fromLTRB(32, 32, 32, 40), decoration: BoxDecoration( - color: const Color.fromRGBO(38, 17, 46, 0.859), + // color: const Color.fromRGBO(38, 17, 46, 0.859), + color: const Color.fromRGBO(57, 38, 62, 0.9), borderRadius: BorderRadius.all(Radius.circular(16)), boxShadow: [ BoxShadow( diff --git a/lib/pages/register_page.dart b/lib/pages/register_page.dart index 17f1cdb..f558ab1 100644 --- a/lib/pages/register_page.dart +++ b/lib/pages/register_page.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; import 'package:gap/gap.dart'; import 'package:pharmacy_mobile/auth/auth_service.dart'; -import 'package:pharmacy_mobile/main.dart'; +import 'package:pharmacy_mobile/widgets/button_widget.dart'; +import 'package:pharmacy_mobile/widgets/input_widget.dart'; import 'package:pharmacy_mobile/widgets/page_background_widget.dart'; +import 'package:pharmacy_mobile/widgets/text_widget.dart'; +import 'package:pharmacy_mobile/widgets/title_widget.dart'; class RegisterPage extends StatefulWidget { const RegisterPage({super.key}); @@ -17,6 +19,9 @@ class _RegisterPageState extends State { final _emailController = TextEditingController(); final _passwordController = TextEditingController(); final _confirmPasswordController = TextEditingController(); + final FocusNode _focusNode = FocusNode(); + + bool _isLoading = false; Future _signUp() async { final email = _emailController.text; @@ -47,6 +52,8 @@ class _RegisterPageState extends State { _emailController.dispose(); _passwordController.dispose(); _confirmPasswordController.dispose(); + _focusNode.dispose(); + _isLoading = false; super.dispose(); } @@ -54,8 +61,58 @@ class _RegisterPageState extends State { Widget build(BuildContext context) { return Scaffold( body: PageBackgroundWidget( - child: Column( - children: [], + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const Gap(104), + const TitleWidget(firstTextSize: 20, secondTextSize: 32), + const Gap(32), + const TextWidget(text: 'Register'), + const Gap(16), + Padding( + padding: const EdgeInsets.only(left: 32, right: 32), + child: Container( + padding: EdgeInsets.fromLTRB(32, 32, 32, 40), + decoration: BoxDecoration( + color: const Color.fromRGBO(57, 38, 62, 0.9), + borderRadius: BorderRadius.all(Radius.circular(16)), + boxShadow: [ + BoxShadow( + color: const Color.fromRGBO(0, 0, 0, 0.4), // Subtle shadow to give depth + spreadRadius: 0, + blurRadius: 4, + offset: Offset(0, 2), + ) + ]), + child: Form( + child: Column( + children: [ + InputWidget(label: 'Email', controller: _emailController), + const Gap(16), + InputWidget( + label: 'Password', + controller: _passwordController, + password: true, + ), + const Gap(16), + InputWidget( + label: 'Confirm Password', + controller: _confirmPasswordController, + password: true, + ), + const Gap(40), + // TextButton(onPressed: () => {_signIn()}, child: const Text('Login')) + if (_isLoading) + Center(child: CircularProgressIndicator(color: Colors.white)) + else + ButtonWidget(text: 'Create Account', onPressed: _signUp) + ], + )), + ), + ), + ], + ), )), ); } diff --git a/lib/widgets/button_widget.dart b/lib/widgets/button_widget.dart index bce40d3..7d680c0 100644 --- a/lib/widgets/button_widget.dart +++ b/lib/widgets/button_widget.dart @@ -13,29 +13,38 @@ class ButtonWidget extends StatelessWidget { return ElevatedButton( style: outline == true ? OutlinedButton.styleFrom( - foregroundColor: Color.fromRGBO(0, 0, 0, 1), // text color - side: const BorderSide(color: Color.fromRGBO(79, 51, 94, 1)), // border color + foregroundColor: const Color.fromRGBO(0, 0, 0, 1), + backgroundColor: Colors.transparent, + side: const BorderSide(color: Color.fromRGBO(198, 133, 232, 1)), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), // rounded corners + borderRadius: BorderRadius.circular(12), ), minimumSize: Size(MediaQuery.of(context).size.width - 96, 44), // minimum size - padding: EdgeInsets.symmetric(vertical: 10, horizontal: 16), + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16), ) : ElevatedButton.styleFrom( - foregroundColor: Color.fromRGBO(0, 0, 0, 1), // text color + foregroundColor: const Color.fromRGBO(0, 0, 0, 1), // text color backgroundColor: const Color.fromRGBO(198, 133, 232, 1), // background color side: const BorderSide(color: Color.fromRGBO(79, 51, 94, 1)), // border color shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), // rounded corners ), minimumSize: Size(MediaQuery.of(context).size.width - 96, 44), // minimum size - padding: EdgeInsets.symmetric(vertical: 10, horizontal: 16), + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16), ), onPressed: onPressed, child: Text( text, - style: GoogleFonts.outfit(textStyle: const TextStyle(fontSize: 14)), + style: _textStyle(outline), ), ); } + + TextStyle _textStyle(bool? outline) { + if (outline == true) { + return GoogleFonts.outfit(textStyle: const TextStyle(fontSize: 14, color: Color.fromRGBO(198, 133, 232, 1))); + } else { + return GoogleFonts.outfit(textStyle: const TextStyle(fontSize: 14, color: Color.fromRGBO(0, 0, 0, 1))); + } + } } diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart index ff6bcda..bc63d98 100644 --- a/lib/widgets/input_widget.dart +++ b/lib/widgets/input_widget.dart @@ -16,7 +16,8 @@ class InputWidget extends StatelessWidget { children: [ Text('$label:', style: GoogleFonts.outfit( - textStyle: const TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w500), + textStyle: + const TextStyle(color: Color.fromRGBO(255, 255, 255, 1), fontSize: 12, fontWeight: FontWeight.w500), )), const Gap(8), TextField( diff --git a/lib/widgets/snackbar_widget.dart b/lib/widgets/snackbar_widget.dart new file mode 100644 index 0000000..4a6b726 --- /dev/null +++ b/lib/widgets/snackbar_widget.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +void showNotification(BuildContext context, String text, bool isPositive) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(text), + 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), + ), + ); +} diff --git a/pubspec.lock b/pubspec.lock index 539b222..9d2d78b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -57,14 +57,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" - bottom_picker: - dependency: "direct main" - description: - name: bottom_picker - sha256: "8ede549685825df389135bebc7984416ed95f24d4f19631d21ad25c9c23db482" - url: "https://pub.dev" - source: hosted - version: "2.10.1" characters: dependency: transitive description: @@ -113,14 +105,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.6" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.dev" - source: hosted - version: "1.0.8" dbus: dependency: transitive description: @@ -774,5 +758,5 @@ packages: source: hosted version: "2.0.3" sdks: - dart: ">=3.6.0 <4.0.0" + dart: ">=3.6.1 <4.0.0" flutter: ">=3.27.0" diff --git a/pubspec.yaml b/pubspec.yaml index 934538a..c4d9452 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,20 +5,18 @@ publish_to: "none" version: 1.0.0+1 environment: - sdk: ^3.6.0 + sdk: ^3.6.1 dependencies: flutter: sdk: flutter - cupertino_icons: ^1.0.8 google_fonts: ^6.2.1 gap: ^3.0.1 go_router: ^14.6.3 supabase_flutter: ^2.8.3 font_awesome_flutter: ^10.8.0 uuid: ^4.5.1 - bottom_picker: ^2.10.1 intl: ^0.20.2 visibility_detector: ^0.4.0+2 internet_connection_checker: ^3.0.1