From 83ad10ffb4f42653554fc666d47fdbd07d556265 Mon Sep 17 00:00:00 2001 From: Vaibhav Surve Date: Thu, 3 Jul 2025 13:22:04 +0530 Subject: [PATCH] feat: update UI components for improved consistency and add tab indicator styling --- lib/helpers/utils/launcher_utils.dart | 41 +- .../Attendence/attendance_screen.dart | 80 +-- lib/view/directory/contact_detail_screen.dart | 569 +++++++++--------- lib/view/directory/directory_main_screen.dart | 90 +-- lib/view/employees/employees_screen.dart | 83 +-- lib/view/taskPlaning/daily_progress.dart | 80 +-- lib/view/taskPlaning/daily_task_planing.dart | 80 +-- pubspec.yaml | 1 + 8 files changed, 551 insertions(+), 473 deletions(-) diff --git a/lib/helpers/utils/launcher_utils.dart b/lib/helpers/utils/launcher_utils.dart index daaf082..8ac99d8 100644 --- a/lib/helpers/utils/launcher_utils.dart +++ b/lib/helpers/utils/launcher_utils.dart @@ -4,6 +4,7 @@ import 'package:marco/helpers/widgets/my_snackbar.dart'; import 'package:marco/helpers/services/app_logger.dart'; class LauncherUtils { + /// Launches the phone dialer with the provided phone number static Future launchPhone(String phoneNumber) async { logSafe('Attempting to launch phone: $phoneNumber', sensitive: true); @@ -11,6 +12,7 @@ class LauncherUtils { await _tryLaunch(url, 'Could not launch phone'); } + /// Launches the email app with the provided email address static Future launchEmail(String email) async { logSafe('Attempting to launch email: $email', sensitive: true); @@ -18,9 +20,9 @@ class LauncherUtils { await _tryLaunch(url, 'Could not launch email'); } + /// Launches WhatsApp with the provided phone number static Future launchWhatsApp(String phoneNumber) async { - logSafe('Attempting to launch WhatsApp with: $phoneNumber', - sensitive: true); + logSafe('Attempting to launch WhatsApp with: $phoneNumber', sensitive: true); String normalized = phoneNumber.replaceAll(RegExp(r'\D'), ''); if (!normalized.startsWith('91')) { @@ -43,8 +45,8 @@ class LauncherUtils { await _tryLaunch(url, 'Could not open WhatsApp'); } - static Future copyToClipboard(String text, - {required String typeLabel}) async { + /// Copies text to clipboard with feedback + static Future copyToClipboard(String text, {required String typeLabel}) async { try { logSafe('Copying "$typeLabel" to clipboard'); @@ -56,9 +58,12 @@ class LauncherUtils { type: SnackbarType.success, ); } catch (e, st) { - logSafe('Failed to copy $typeLabel to clipboard: $e', - stackTrace: st, level: LogLevel.error, sensitive: true); - + logSafe( + 'Failed to copy $typeLabel to clipboard: $e', + stackTrace: st, + level: LogLevel.error, + sensitive: true, + ); showAppSnackbar( title: 'Error', message: 'Failed to copy $typeLabel', @@ -67,17 +72,23 @@ class LauncherUtils { } } + /// Internal function to launch a URL and show error if failed static Future _tryLaunch(Uri url, String errorMsg) async { try { logSafe('Trying to launch URL: ${url.toString()}'); - if (await canLaunchUrl(url)) { - await launchUrl(url, mode: LaunchMode.externalApplication); + final bool launched = await launchUrl( + url, + mode: LaunchMode.externalApplication, + ); + + if (launched) { logSafe('URL launched successfully: ${url.toString()}'); } else { logSafe( - 'Launch failed - canLaunchUrl returned false: ${url.toString()}', - level: LogLevel.warning); + 'launchUrl returned false: ${url.toString()}', + level: LogLevel.warning, + ); showAppSnackbar( title: 'Error', message: errorMsg, @@ -85,9 +96,11 @@ class LauncherUtils { ); } } catch (e, st) { - logSafe('Exception during launch of ${url.toString()}: $e', - stackTrace: st, level: LogLevel.error); - + logSafe( + 'Exception during launch of ${url.toString()}: $e', + stackTrace: st, + level: LogLevel.error, + ); showAppSnackbar( title: 'Error', message: '$errorMsg: $e', diff --git a/lib/view/dashboard/Attendence/attendance_screen.dart b/lib/view/dashboard/Attendence/attendance_screen.dart index 3974484..12d8e34 100644 --- a/lib/view/dashboard/Attendence/attendance_screen.dart +++ b/lib/view/dashboard/Attendence/attendance_screen.dart @@ -71,48 +71,58 @@ class _AttendanceScreenState extends State with UIMixin { Widget build(BuildContext context) { return Scaffold( appBar: PreferredSize( - preferredSize: const Size.fromHeight(80), + preferredSize: const Size.fromHeight(72), child: AppBar( backgroundColor: const Color(0xFFF5F5F5), elevation: 0.5, - foregroundColor: Colors.black, + automaticallyImplyLeading: false, titleSpacing: 0, - centerTitle: false, - leading: Padding( - padding: const EdgeInsets.only(top: 15.0), - child: IconButton( - icon: const Icon(Icons.arrow_back_ios_new, - color: Colors.black, size: 20), - onPressed: () { - Get.offNamed('/dashboard'); - }, - ), - ), title: Padding( - padding: const EdgeInsets.only(top: 15.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, + padding: MySpacing.xy(16, 0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - MyText.titleLarge( - 'Attendance', - fontWeight: 700, - color: Colors.black, + IconButton( + icon: const Icon(Icons.arrow_back_ios_new, + color: Colors.black, size: 20), + onPressed: () => Get.offNamed('/dashboard'), ), - const SizedBox(height: 2), - GetBuilder( - builder: (projectController) { - final projectName = - projectController.selectedProject?.name ?? - 'Select Project'; - return MyText.bodySmall( - projectName, - fontWeight: 600, - maxLines: 1, - overflow: TextOverflow.ellipsis, - color: Colors.grey[700], - ); - }, + MySpacing.width(8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + MyText.titleLarge( + 'Attendance', + fontWeight: 700, + color: Colors.black, + ), + MySpacing.height(2), + GetBuilder( + builder: (projectController) { + final projectName = + projectController.selectedProject?.name ?? + 'Select Project'; + return Row( + children: [ + const Icon(Icons.work_outline, + size: 14, color: Colors.grey), + MySpacing.width(4), + Expanded( + child: MyText.bodySmall( + projectName, + fontWeight: 600, + overflow: TextOverflow.ellipsis, + color: Colors.grey[700], + ), + ), + ], + ); + }, + ), + ], + ), ), ], ), diff --git a/lib/view/directory/contact_detail_screen.dart b/lib/view/directory/contact_detail_screen.dart index 7ffa1eb..3ded565 100644 --- a/lib/view/directory/contact_detail_screen.dart +++ b/lib/view/directory/contact_detail_screen.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; +import 'package:flutter_html/flutter_html.dart'; import 'package:marco/controller/project_controller.dart'; import 'package:marco/controller/directory/directory_controller.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; @@ -8,34 +9,179 @@ import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/model/directory/contact_model.dart'; import 'package:marco/helpers/widgets/avatar.dart'; import 'package:marco/helpers/utils/launcher_utils.dart'; -import 'package:flutter_html/flutter_html.dart'; +import 'package:tab_indicator_styler/tab_indicator_styler.dart'; + class ContactDetailScreen extends StatelessWidget { final ContactModel contact; + const ContactDetailScreen({super.key, required this.contact}); + @override Widget build(BuildContext context) { final directoryController = Get.find(); final projectController = Get.find(); + Future.microtask(() { if (!directoryController.contactCommentsMap.containsKey(contact.id)) { directoryController.fetchCommentsForContact(contact.id); } }); + + return DefaultTabController( + length: 2, + child: Scaffold( + backgroundColor: const Color(0xFFF5F5F5), + appBar: _buildMainAppBar(projectController), + body: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildSubHeader(), + Expanded( + child: TabBarView( + children: [ + _buildDetailsTab(directoryController, projectController), + _buildCommentsTab(directoryController), + ], + ), + ), + ], + ), + ), + ), + ); + } + + PreferredSizeWidget _buildMainAppBar(ProjectController projectController) { + return AppBar( + backgroundColor: const Color(0xFFF5F5F5), + elevation: 0.2, + automaticallyImplyLeading: false, + titleSpacing: 0, + title: Padding( + padding: MySpacing.xy(16, 0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + IconButton( + icon: const Icon(Icons.arrow_back_ios_new, + color: Colors.black, size: 20), + onPressed: () => Get.back(), + ), + MySpacing.width(8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + MyText.titleLarge( + 'Contact Profile', + fontWeight: 700, + color: Colors.black, + ), + MySpacing.height(2), + GetBuilder( + builder: (projectController) { + final projectName = + projectController.selectedProject?.name ?? + 'Select Project'; + return Row( + children: [ + const Icon(Icons.work_outline, + size: 14, color: Colors.grey), + MySpacing.width(4), + Expanded( + child: MyText.bodySmall( + projectName, + fontWeight: 600, + overflow: TextOverflow.ellipsis, + color: Colors.grey[700], + ), + ), + ], + ); + }, + ), + ], + ), + ), + ], + ), + ), + ); + } + + Widget _buildSubHeader() { + return Padding( + padding: MySpacing.xy(16, 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Avatar( + firstName: contact.name.split(" ").first, + lastName: contact.name.split(" ").length > 1 + ? contact.name.split(" ").last + : "", + size: 35, + backgroundColor: Colors.indigo, + ), + MySpacing.width(12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.titleSmall(contact.name, + fontWeight: 600, color: Colors.black), + MySpacing.height(2), + MyText.bodySmall(contact.organization, + fontWeight: 500, color: Colors.grey[700]), + ], + ), + ], + ), + TabBar( + labelColor: Colors.indigo, + unselectedLabelColor: Colors.grey, + indicator: MaterialIndicator( + color: Colors.indigo, + height: 4, + topLeftRadius: 8, + topRightRadius: 8, + bottomLeftRadius: 8, + bottomRightRadius: 8, + ), + tabs: const [ + Tab(text: "Details"), + Tab(text: "Comments"), + ], + ), + ], + ), + ); + } + + Widget _buildDetailsTab(DirectoryController directoryController, + ProjectController projectController) { final email = contact.contactEmails.isNotEmpty ? contact.contactEmails.first.emailAddress : "-"; + final phone = contact.contactPhones.isNotEmpty ? contact.contactPhones.first.phoneNumber : "-"; + final createdDate = DateTime.now(); final formattedDate = DateFormat('MMMM dd, yyyy').format(createdDate); final tags = contact.tags.map((e) => e.name).join(", "); + final bucketNames = contact.bucketIds .map((id) => directoryController.contactBuckets .firstWhereOrNull((b) => b.id == id) ?.name) .whereType() .join(", "); + final projectNames = contact.projectIds ?.map((id) => projectController.projects .firstWhereOrNull((p) => p.id == id) @@ -43,292 +189,159 @@ class ContactDetailScreen extends StatelessWidget { .whereType() .join(", ") ?? "-"; + final category = contact.contactCategory?.name ?? "-"; - return DefaultTabController( - length: 2, - child: Scaffold( - backgroundColor: const Color(0xFFF5F5F5), - appBar: PreferredSize( - preferredSize: const Size.fromHeight(170), - child: AppBar( - backgroundColor: const Color(0xFFF5F5F5), - elevation: 0.5, - automaticallyImplyLeading: false, - flexibleSpace: SafeArea( - child: Padding( - padding: MySpacing.xy(10, 12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + return SingleChildScrollView( + padding: MySpacing.xy(8, 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _infoCard("Basic Info", [ + _iconInfoRow(Icons.email, "Email", email, + onTap: () => LauncherUtils.launchEmail(email), + onLongPress: () => + LauncherUtils.copyToClipboard(email, typeLabel: "Email")), + _iconInfoRow(Icons.phone, "Phone", phone, + onTap: () => LauncherUtils.launchPhone(phone), + onLongPress: () => + LauncherUtils.copyToClipboard(phone, typeLabel: "Phone")), + _iconInfoRow(Icons.calendar_today, "Created", formattedDate), + _iconInfoRow(Icons.location_on, "Address", contact.address), + ]), + _infoCard("Organization", [ + _iconInfoRow(Icons.business, "Organization", contact.organization), + _iconInfoRow(Icons.category, "Category", category), + ]), + _infoCard("Meta Info", [ + _iconInfoRow(Icons.label, "Tags", tags.isNotEmpty ? tags : "-"), + _iconInfoRow(Icons.folder_shared, "Contact Buckets", + bucketNames.isNotEmpty ? bucketNames : "-"), + _iconInfoRow(Icons.work_outline, "Projects", projectNames), + ]), + _infoCard("Description", [ + MySpacing.height(6), + Align( + alignment: Alignment.topLeft, + child: MyText.bodyMedium( + contact.description, + color: Colors.grey[800], + maxLines: 10, + textAlign: TextAlign.left, + ), + ), + ]) + ], + ), + ); + } + + Widget _buildCommentsTab(DirectoryController directoryController) { + return Obx(() { + final comments = directoryController.contactCommentsMap[contact.id]; + + if (comments == null) { + return const Center(child: CircularProgressIndicator()); + } + + if (comments.isEmpty) { + return Center( + child: MyText.bodyLarge("No comments yet.", color: Colors.grey)); + } + + return ListView.separated( + padding: MySpacing.xy(8, 8), + itemCount: comments.length, + separatorBuilder: (_, __) => MySpacing.height(12), + itemBuilder: (_, index) { + final comment = comments[index]; + final initials = comment.createdBy.firstName.isNotEmpty + ? comment.createdBy.firstName[0].toUpperCase() + : "?"; + + return Container( + padding: MySpacing.xy(14, 12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: const [ + BoxShadow( + color: Colors.black12, + blurRadius: 4, + offset: Offset(0, 2), + ) + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( children: [ - // Back button and title - Row( - children: [ - IconButton( - icon: const Icon(Icons.arrow_back_ios_new, - color: Colors.black, size: 20), - onPressed: () => Get.back(), - ), - const SizedBox(width: 4), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MyText.titleLarge('Contact Profile', - fontWeight: 700, color: Colors.black), - const SizedBox(height: 2), - GetBuilder( - builder: (_) { - final projectName = - projectController.selectedProject?.name ?? - 'Select Project'; - return MyText.bodySmall( - projectName, - fontWeight: 600, - color: Colors.grey[700], - ); - }, - ), - ], - ), - ], + Avatar(firstName: initials, lastName: '', size: 31), + MySpacing.width(8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.bodySmall("By: ${comment.createdBy.firstName}", + fontWeight: 600, color: Colors.indigo[700]), + MySpacing.height(2), + MyText.bodySmall( + DateFormat('dd MMM yyyy, hh:mm a') + .format(comment.createdAt), + fontWeight: 500, + color: Colors.grey[600], + ), + ], + ), ), - const SizedBox(height: 12), - // Avatar + name + org - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Avatar( - firstName: contact.name.split(" ").first, - lastName: contact.name.split(" ").length > 1 - ? contact.name.split(" ").last - : "", - size: 35, - backgroundColor: Colors.indigo, - ), - const SizedBox(width: 12), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MyText.titleSmall( - contact.name, - fontWeight: 600, - color: Colors.black, - ), - const SizedBox(height: 2), - MyText.titleSmall( - contact.organization, - fontWeight: 500, - color: Colors.grey[700], - ), - ], - ), - ], - ), - - const SizedBox(height: 6), - - // Tab Bar - const TabBar( - indicatorColor: Colors.indigo, - labelColor: Colors.indigo, - unselectedLabelColor: Colors.grey, - tabs: [ - Tab(text: "Details"), - Tab(text: "Comments"), - ], + IconButton( + icon: const Icon(Icons.more_vert, + size: 20, color: Colors.grey), + onPressed: () {}, + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), ), ], ), - ), + MySpacing.height(10), + Html( + data: comment.note, + style: { + "body": Style( + margin: Margins.all(0), + padding: HtmlPaddings.all(0), + fontSize: FontSize.medium, + color: Colors.black87, + ), + "pre": Style( + padding: HtmlPaddings.all(8), + fontSize: FontSize.small, + fontFamily: 'monospace', + backgroundColor: const Color(0xFFF1F1F1), + border: Border.all(color: Colors.grey.shade300), + ), + "h3": Style( + fontSize: FontSize.large, + fontWeight: FontWeight.bold, + color: Colors.indigo[700], + ), + "strong": Style(fontWeight: FontWeight.w700), + "p": Style(margin: Margins.only(bottom: 8)), + }, + ), + ], ), - ), - ), - body: TabBarView( - children: [ - // Details Tab - SingleChildScrollView( - padding: MySpacing.xy(9, 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _infoCard("Basic Info", [ - _iconInfoRow( - Icons.email, - "Email", - email, - onTap: () => LauncherUtils.launchEmail(email), - onLongPress: () => LauncherUtils.copyToClipboard(email, - typeLabel: "Email"), - ), - _iconInfoRow( - Icons.phone, - "Phone", - phone, - onTap: () => LauncherUtils.launchPhone(phone), - onLongPress: () => LauncherUtils.copyToClipboard(phone, - typeLabel: "Phone"), - ), - _iconInfoRow( - Icons.calendar_today, "Created", formattedDate), - _iconInfoRow(Icons.location_on, "Address", contact.address), - ]), - _infoCard("Organization", [ - _iconInfoRow( - Icons.business, "Organization", contact.organization), - _iconInfoRow(Icons.category, "Category", category), - ]), - _infoCard("Meta Info", [ - _iconInfoRow( - Icons.label, "Tags", tags.isNotEmpty ? tags : "-"), - _iconInfoRow(Icons.folder_shared, "Contat Buckets", - bucketNames.isNotEmpty ? bucketNames : "-"), - _iconInfoRow(Icons.work_outline, "Projects", projectNames), - ]), - _infoCard("Description", [ - const SizedBox(height: 6), - SizedBox( - width: double.infinity, - child: MyText.bodyMedium( - contact.description, - color: Colors.grey[800], - maxLines: 10, - ), - ), - ]), - ], - ), - ), - - // Comments Tab - // Improved Comments Tab - Obx(() { - final comments = - directoryController.contactCommentsMap[contact.id]; - - if (comments == null) { - return const Center(child: CircularProgressIndicator()); - } - - if (comments.isEmpty) { - return Center( - child: - MyText.bodyLarge("No comments yet.", color: Colors.grey), - ); - } - - return ListView.separated( - padding: MySpacing.xy(12, 16), - itemCount: comments.length, - separatorBuilder: (_, __) => const SizedBox(height: 12), - itemBuilder: (_, index) { - final comment = comments[index]; - final initials = comment.createdBy.firstName.isNotEmpty - ? comment.createdBy.firstName[0].toUpperCase() - : "?"; - - return Container( - padding: MySpacing.xy(14, 12), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(12), - boxShadow: const [ - BoxShadow( - color: Colors.black12, - blurRadius: 4, - offset: Offset(0, 2), - ) - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Avatar + By + Date Row at top - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Avatar( - firstName: initials, - lastName: '', - size: 31, - ), - const SizedBox(width: 8), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MyText.bodySmall( - "By: ${comment.createdBy.firstName}", - fontWeight: 600, - color: Colors.indigo[700], - ), - const SizedBox(height: 2), - MyText.bodySmall( - DateFormat('dd MMM yyyy, hh:mm a') - .format(comment.createdAt), - fontWeight: 500, - color: Colors.grey[600], - ), - ], - ), - ), - IconButton( - icon: const Icon(Icons.more_vert, - size: 20, color: Colors.grey), - onPressed: () {}, - padding: EdgeInsets.zero, - constraints: const BoxConstraints(), - ), - ], - ), - - const SizedBox(height: 10), - - // Comment content - Html( - data: comment.note, - style: { - "body": Style( - margin: Margins.all(0), - padding: HtmlPaddings.all(0), - fontSize: FontSize.medium, - color: Colors.black87, - ), - "pre": Style( - padding: HtmlPaddings.all(8), - fontSize: FontSize.small, - fontFamily: 'monospace', - backgroundColor: const Color(0xFFF1F1F1), - border: Border.all(color: Colors.grey.shade300), - ), - "h3": Style( - fontSize: FontSize.large, - fontWeight: FontWeight.bold, - color: Colors.indigo[700], - ), - "strong": Style( - fontWeight: FontWeight.w700, - ), - "p": Style( - margin: Margins.only(bottom: 8), - ), - }, - ), - ], - ), - ); - }, - ); - }) - ], - ), - ), - ); + ); + }, + ); + }); } Widget _iconInfoRow(IconData icon, String label, String value, {VoidCallback? onTap, VoidCallback? onLongPress}) { return Padding( - padding: const EdgeInsets.symmetric(vertical: 8), + padding: MySpacing.y(8), child: GestureDetector( onTap: onTap, onLongPress: onLongPress, @@ -336,14 +349,14 @@ class ContactDetailScreen extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 22, color: Colors.indigo), - const SizedBox(width: 12), + MySpacing.width(12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.bodySmall(label, fontWeight: 600, color: Colors.black87), - const SizedBox(height: 2), + MySpacing.height(2), MyText.bodyMedium(value, color: Colors.grey[800]), ], ), @@ -358,7 +371,7 @@ class ContactDetailScreen extends StatelessWidget { return Card( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), elevation: 2, - margin: const EdgeInsets.only(bottom: 10), + margin: MySpacing.bottom(12), child: Padding( padding: MySpacing.xy(16, 16), child: Column( @@ -366,7 +379,7 @@ class ContactDetailScreen extends StatelessWidget { children: [ MyText.titleSmall(title, fontWeight: 700, color: Colors.indigo[700]), - const SizedBox(height: 8), + MySpacing.height(8), ...children, ], ), diff --git a/lib/view/directory/directory_main_screen.dart b/lib/view/directory/directory_main_screen.dart index 3ab2d8e..aa268f4 100644 --- a/lib/view/directory/directory_main_screen.dart +++ b/lib/view/directory/directory_main_screen.dart @@ -33,48 +33,58 @@ class DirectoryMainScreen extends StatelessWidget { return Scaffold( backgroundColor: const Color(0xFFF5F5F5), appBar: PreferredSize( - preferredSize: const Size.fromHeight(80), + preferredSize: const Size.fromHeight(72), child: AppBar( backgroundColor: const Color(0xFFF5F5F5), elevation: 0.5, - foregroundColor: Colors.black, + automaticallyImplyLeading: false, titleSpacing: 0, - centerTitle: false, - leading: Padding( - padding: const EdgeInsets.only(top: 15.0), - child: IconButton( - icon: const Icon(Icons.arrow_back_ios_new, - color: Colors.black, size: 20), - onPressed: () { - Get.offNamed('/dashboard'); - }, - ), - ), title: Padding( - padding: const EdgeInsets.only(top: 15.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, + padding: MySpacing.xy(16, 0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - MyText.titleLarge( - 'Directory', - fontWeight: 700, - color: Colors.black, + IconButton( + icon: const Icon(Icons.arrow_back_ios_new, + color: Colors.black, size: 20), + onPressed: () => Get.offNamed('/dashboard'), ), - const SizedBox(height: 2), - GetBuilder( - builder: (projectController) { - final projectName = - projectController.selectedProject?.name ?? - 'Select Project'; - return MyText.bodySmall( - projectName, - fontWeight: 600, - maxLines: 1, - overflow: TextOverflow.ellipsis, - color: Colors.grey[700], - ); - }, + MySpacing.width(8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + MyText.titleLarge( + 'Directory', + fontWeight: 700, + color: Colors.black, + ), + MySpacing.height(2), + GetBuilder( + builder: (projectController) { + final projectName = + projectController.selectedProject?.name ?? + 'Select Project'; + return Row( + children: [ + const Icon(Icons.work_outline, + size: 14, color: Colors.grey), + MySpacing.width(4), + Expanded( + child: MyText.bodySmall( + projectName, + fontWeight: 600, + overflow: TextOverflow.ellipsis, + color: Colors.grey[700], + ), + ), + ], + ); + }, + ), + ], + ), ), ], ), @@ -366,7 +376,7 @@ class DirectoryMainScreen extends StatelessWidget { padding: EdgeInsets.only(right: 8.0), child: Icon(Icons.email_outlined, - color: Colors.blue, size: 20), + color: Colors.blue, size: 25), ), ), Expanded( @@ -378,7 +388,7 @@ class DirectoryMainScreen extends StatelessWidget { LauncherUtils.copyToClipboard( email, typeLabel: 'Email'), - child: MyText.bodySmall( + child: MyText.bodyMedium( email, maxLines: 1, overflow: TextOverflow.ellipsis, @@ -408,7 +418,7 @@ class DirectoryMainScreen extends StatelessWidget { child: const Icon( Icons.phone_outlined, color: Colors.blue, - size: 20), + size: 25), ), ), @@ -421,7 +431,7 @@ class DirectoryMainScreen extends StatelessWidget { LauncherUtils.copyToClipboard( phone, typeLabel: 'Phone number'), - child: MyText.bodySmall( + child: MyText.bodyMedium( phone, maxLines: 1, overflow: TextOverflow.ellipsis, @@ -445,7 +455,7 @@ class DirectoryMainScreen extends StatelessWidget { child: const FaIcon( FontAwesomeIcons.whatsapp, color: Colors.green, - size: 18), + size: 25), ), ), ], diff --git a/lib/view/employees/employees_screen.dart b/lib/view/employees/employees_screen.dart index 37bbc28..3e3dbef 100644 --- a/lib/view/employees/employees_screen.dart +++ b/lib/view/employees/employees_screen.dart @@ -15,6 +15,7 @@ import 'package:marco/helpers/widgets/avatar.dart'; import 'package:marco/model/employees/employee_detail_bottom_sheet.dart'; import 'package:marco/controller/project_controller.dart'; import 'package:marco/helpers/widgets/my_custom_skeleton.dart'; + class EmployeesScreen extends StatefulWidget { const EmployeesScreen({super.key}); @@ -73,49 +74,59 @@ class _EmployeesScreenState extends State with UIMixin { Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF5F5F5), - appBar: PreferredSize( - preferredSize: const Size.fromHeight(80), + appBar: PreferredSize( + preferredSize: const Size.fromHeight(72), child: AppBar( backgroundColor: const Color(0xFFF5F5F5), elevation: 0.5, - foregroundColor: Colors.black, + automaticallyImplyLeading: false, titleSpacing: 0, - centerTitle: false, - leading: Padding( - padding: const EdgeInsets.only(top: 15.0), // Aligns with title - child: IconButton( - icon: const Icon(Icons.arrow_back_ios_new, - color: Colors.black, size: 20), - onPressed: () { - Get.offNamed('/dashboard'); - }, - ), - ), title: Padding( - padding: const EdgeInsets.only(top: 15.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, + padding: MySpacing.xy(16, 0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - MyText.titleLarge( - 'Employees', - fontWeight: 700, - color: Colors.black, + IconButton( + icon: const Icon(Icons.arrow_back_ios_new, + color: Colors.black, size: 20), + onPressed: () => Get.offNamed('/dashboard'), ), - const SizedBox(height: 2), - GetBuilder( - builder: (projectController) { - final projectName = - projectController.selectedProject?.name ?? - 'Select Project'; - return MyText.bodySmall( - projectName, - fontWeight: 600, - maxLines: 1, - overflow: TextOverflow.ellipsis, - color: Colors.grey[700], - ); - }, + MySpacing.width(8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + MyText.titleLarge( + 'Employees', + fontWeight: 700, + color: Colors.black, + ), + MySpacing.height(2), + GetBuilder( + builder: (projectController) { + final projectName = + projectController.selectedProject?.name ?? + 'Select Project'; + return Row( + children: [ + const Icon(Icons.work_outline, + size: 14, color: Colors.grey), + MySpacing.width(4), + Expanded( + child: MyText.bodySmall( + projectName, + fontWeight: 600, + overflow: TextOverflow.ellipsis, + color: Colors.grey[700], + ), + ), + ], + ); + }, + ), + ], + ), ), ], ), diff --git a/lib/view/taskPlaning/daily_progress.dart b/lib/view/taskPlaning/daily_progress.dart index 8a7e8a4..21d704a 100644 --- a/lib/view/taskPlaning/daily_progress.dart +++ b/lib/view/taskPlaning/daily_progress.dart @@ -67,48 +67,58 @@ class _DailyProgressReportScreenState extends State Widget build(BuildContext context) { return Scaffold( appBar: PreferredSize( - preferredSize: const Size.fromHeight(80), + preferredSize: const Size.fromHeight(72), child: AppBar( backgroundColor: const Color(0xFFF5F5F5), elevation: 0.5, - foregroundColor: Colors.black, + automaticallyImplyLeading: false, titleSpacing: 0, - centerTitle: false, - leading: Padding( - padding: const EdgeInsets.only(top: 15.0), // Aligns with title - child: IconButton( - icon: const Icon(Icons.arrow_back_ios_new, - color: Colors.black, size: 20), - onPressed: () { - Get.offNamed('/dashboard'); - }, - ), - ), title: Padding( - padding: const EdgeInsets.only(top: 15.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, + padding: MySpacing.xy(16, 0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - MyText.titleLarge( - 'Daily Task Progress', - fontWeight: 700, - color: Colors.black, + IconButton( + icon: const Icon(Icons.arrow_back_ios_new, + color: Colors.black, size: 20), + onPressed: () => Get.offNamed('/dashboard'), ), - const SizedBox(height: 2), - GetBuilder( - builder: (projectController) { - final projectName = - projectController.selectedProject?.name ?? - 'Select Project'; - return MyText.bodySmall( - projectName, - fontWeight: 600, - maxLines: 1, - overflow: TextOverflow.ellipsis, - color: Colors.grey[700], - ); - }, + MySpacing.width(8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + MyText.titleLarge( + 'Daily Task Progress', + fontWeight: 700, + color: Colors.black, + ), + MySpacing.height(2), + GetBuilder( + builder: (projectController) { + final projectName = + projectController.selectedProject?.name ?? + 'Select Project'; + return Row( + children: [ + const Icon(Icons.work_outline, + size: 14, color: Colors.grey), + MySpacing.width(4), + Expanded( + child: MyText.bodySmall( + projectName, + fontWeight: 600, + overflow: TextOverflow.ellipsis, + color: Colors.grey[700], + ), + ), + ], + ); + }, + ), + ], + ), ), ], ), diff --git a/lib/view/taskPlaning/daily_task_planing.dart b/lib/view/taskPlaning/daily_task_planing.dart index 0e96a60..d734b14 100644 --- a/lib/view/taskPlaning/daily_task_planing.dart +++ b/lib/view/taskPlaning/daily_task_planing.dart @@ -53,48 +53,58 @@ class _DailyTaskPlaningScreenState extends State Widget build(BuildContext context) { return Scaffold( appBar: PreferredSize( - preferredSize: const Size.fromHeight(80), + preferredSize: const Size.fromHeight(72), child: AppBar( backgroundColor: const Color(0xFFF5F5F5), elevation: 0.5, - foregroundColor: Colors.black, + automaticallyImplyLeading: false, titleSpacing: 0, - centerTitle: false, - leading: Padding( - padding: const EdgeInsets.only(top: 15.0), // Aligns with title - child: IconButton( - icon: const Icon(Icons.arrow_back_ios_new, - color: Colors.black, size: 20), - onPressed: () { - Get.offNamed('/dashboard'); - }, - ), - ), title: Padding( - padding: const EdgeInsets.only(top: 15.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, + padding: MySpacing.xy(16, 0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - MyText.titleLarge( - 'Daily Task Planning', - fontWeight: 700, - color: Colors.black, + IconButton( + icon: const Icon(Icons.arrow_back_ios_new, + color: Colors.black, size: 20), + onPressed: () => Get.offNamed('/dashboard'), ), - const SizedBox(height: 2), - GetBuilder( - builder: (projectController) { - final projectName = - projectController.selectedProject?.name ?? - 'Select Project'; - return MyText.bodySmall( - projectName, - fontWeight: 600, - maxLines: 1, - overflow: TextOverflow.ellipsis, - color: Colors.grey[700], - ); - }, + MySpacing.width(8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + MyText.titleLarge( + 'Daily Task Planing', + fontWeight: 700, + color: Colors.black, + ), + MySpacing.height(2), + GetBuilder( + builder: (projectController) { + final projectName = + projectController.selectedProject?.name ?? + 'Select Project'; + return Row( + children: [ + const Icon(Icons.work_outline, + size: 14, color: Colors.grey), + MySpacing.width(4), + Expanded( + child: MyText.bodySmall( + projectName, + fontWeight: 600, + overflow: TextOverflow.ellipsis, + color: Colors.grey[700], + ), + ), + ], + ); + }, + ), + ], + ), ), ], ), diff --git a/pubspec.yaml b/pubspec.yaml index 5ed79e3..1849534 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -73,6 +73,7 @@ dependencies: jwt_decoder: ^2.0.1 font_awesome_flutter: ^10.8.0 flutter_html: ^3.0.0 + tab_indicator_styler: ^2.0.0 dev_dependencies: flutter_test: sdk: flutter