updates finance screen
This commit is contained in:
parent
a2f5414240
commit
0b0c072473
@ -3,6 +3,7 @@ import 'package:flutter_lucide/flutter_lucide.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:marco/controller/project_controller.dart';
|
import 'package:marco/controller/project_controller.dart';
|
||||||
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
|
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
|
||||||
|
import 'package:marco/helpers/widgets/my_card.dart';
|
||||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||||
import 'package:marco/helpers/widgets/my_text.dart';
|
import 'package:marco/helpers/widgets/my_text.dart';
|
||||||
|
|
||||||
@ -106,364 +107,63 @@ class _FinanceScreenState extends State<FinanceScreen>
|
|||||||
opacity: _fadeAnimation,
|
opacity: _fadeAnimation,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Column(
|
child: _buildFinanceModulesCompact(),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
_buildWelcomeSection(),
|
|
||||||
MySpacing.height(24),
|
|
||||||
_buildFinanceModules(),
|
|
||||||
MySpacing.height(24),
|
|
||||||
_buildQuickStatsSection(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildWelcomeSection() {
|
// --- Finance Modules (Single Row, Equally Spaced) ---
|
||||||
final projectSelected = projectController.selectedProject != null;
|
Widget _buildFinanceModulesCompact() {
|
||||||
|
|
||||||
return Container(
|
|
||||||
width: double.infinity,
|
|
||||||
padding: const EdgeInsets.all(20),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: [
|
|
||||||
contentTheme.primary.withValues(alpha: 0.1),
|
|
||||||
contentTheme.info.withValues(alpha: 0.05),
|
|
||||||
],
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
border: Border.all(
|
|
||||||
color: contentTheme.primary.withValues(alpha: 0.2),
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(10),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: contentTheme.primary.withValues(alpha: 0.1),
|
|
||||||
blurRadius: 8,
|
|
||||||
offset: const Offset(0, 2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
LucideIcons.landmark,
|
|
||||||
color: contentTheme.primary,
|
|
||||||
size: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
MySpacing.width(12),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
MyText.titleMedium(
|
|
||||||
'Financial Management',
|
|
||||||
fontWeight: 700,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
MySpacing.height(2),
|
|
||||||
MyText.bodySmall(
|
|
||||||
projectSelected
|
|
||||||
? 'Manage your project finances'
|
|
||||||
: 'Select a project to get started',
|
|
||||||
color: Colors.grey[600],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (!projectSelected) ...[
|
|
||||||
MySpacing.height(12),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.orange.withValues(alpha: 0.1),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
border: Border.all(color: Colors.orange.withValues(alpha: 0.3)),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
LucideIcons.badge_alert,
|
|
||||||
size: 16,
|
|
||||||
color: Colors.orange[700],
|
|
||||||
),
|
|
||||||
MySpacing.width(8),
|
|
||||||
Expanded(
|
|
||||||
child: MyText.bodySmall(
|
|
||||||
'Please select a project to access finance modules',
|
|
||||||
color: Colors.orange[700],
|
|
||||||
fontWeight: 500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildFinanceModules() {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
MyText.titleMedium(
|
|
||||||
'Finance Modules',
|
|
||||||
fontWeight: 700,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
MySpacing.height(4),
|
|
||||||
MyText.bodySmall(
|
|
||||||
'Select a module to manage',
|
|
||||||
color: Colors.grey[600],
|
|
||||||
),
|
|
||||||
MySpacing.height(16),
|
|
||||||
_buildModuleGrid(),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildModuleGrid() {
|
|
||||||
final stats = [
|
final stats = [
|
||||||
_FinanceStatItem(
|
_FinanceStatItem(LucideIcons.badge_dollar_sign, "Expense",
|
||||||
LucideIcons.badge_dollar_sign,
|
contentTheme.info, "/dashboard/expense-main-page"),
|
||||||
"Expense",
|
_FinanceStatItem(LucideIcons.receipt_text, "Payment Request",
|
||||||
"Track and manage expenses",
|
contentTheme.primary, "/dashboard/payment-request"),
|
||||||
contentTheme.info,
|
_FinanceStatItem(LucideIcons.wallet, "Advance Payment",
|
||||||
"/dashboard/expense-main-page",
|
contentTheme.warning, "/dashboard/advance-payment"),
|
||||||
),
|
|
||||||
_FinanceStatItem(
|
|
||||||
LucideIcons.receipt_text,
|
|
||||||
"Payment Request",
|
|
||||||
"Submit payment requests",
|
|
||||||
contentTheme.primary,
|
|
||||||
"/dashboard/payment-request",
|
|
||||||
),
|
|
||||||
_FinanceStatItem(
|
|
||||||
LucideIcons.wallet,
|
|
||||||
"Advance Payment",
|
|
||||||
"Manage advance payments",
|
|
||||||
contentTheme.warning,
|
|
||||||
"/dashboard/advance-payment",
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
final projectSelected = projectController.selectedProject != null;
|
final projectSelected = projectController.selectedProject != null;
|
||||||
|
|
||||||
return GridView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: 2,
|
|
||||||
crossAxisSpacing: 12,
|
|
||||||
mainAxisSpacing: 12,
|
|
||||||
childAspectRatio: 1.1,
|
|
||||||
),
|
|
||||||
itemCount: stats.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return _buildModernFinanceCard(
|
|
||||||
stats[index],
|
|
||||||
projectSelected,
|
|
||||||
index,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildModernFinanceCard(
|
|
||||||
_FinanceStatItem statItem,
|
|
||||||
bool isProjectSelected,
|
|
||||||
int index,
|
|
||||||
) {
|
|
||||||
final bool isEnabled = isProjectSelected;
|
|
||||||
|
|
||||||
return TweenAnimationBuilder<double>(
|
|
||||||
duration: Duration(milliseconds: 400 + (index * 100)),
|
|
||||||
tween: Tween(begin: 0.0, end: 1.0),
|
|
||||||
curve: Curves.easeOutCubic,
|
|
||||||
builder: (context, value, child) {
|
|
||||||
return Transform.scale(
|
|
||||||
scale: value,
|
|
||||||
child: Opacity(
|
|
||||||
opacity: isEnabled ? 1.0 : 0.5,
|
|
||||||
child: Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () => _onCardTap(statItem, isEnabled),
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
border: Border.all(
|
|
||||||
color: isEnabled
|
|
||||||
? statItem.color.withValues(alpha: 0.2)
|
|
||||||
: Colors.grey.withValues(alpha: 0.2),
|
|
||||||
width: 1.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
// Content
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
child: Icon(
|
|
||||||
statItem.icon,
|
|
||||||
size: 28,
|
|
||||||
color: statItem.color,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
MySpacing.height(12),
|
|
||||||
MyText.titleSmall(
|
|
||||||
statItem.title,
|
|
||||||
fontWeight: 700,
|
|
||||||
color: Colors.black87,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
MySpacing.height(4),
|
|
||||||
if (isEnabled)
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
MyText.bodySmall(
|
|
||||||
'View Details',
|
|
||||||
color: statItem.color,
|
|
||||||
fontWeight: 600,
|
|
||||||
fontSize: 11,
|
|
||||||
),
|
|
||||||
MySpacing.width(4),
|
|
||||||
Icon(
|
|
||||||
LucideIcons.arrow_right,
|
|
||||||
size: 14,
|
|
||||||
color: statItem.color,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Lock icon for disabled state
|
|
||||||
if (!isEnabled)
|
|
||||||
Positioned(
|
|
||||||
top: 12,
|
|
||||||
right: 12,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(4),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.grey[200],
|
|
||||||
borderRadius: BorderRadius.circular(6),
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
LucideIcons.lock,
|
|
||||||
size: 14,
|
|
||||||
color: Colors.grey[600],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildQuickStatsSection() {
|
|
||||||
final projectSelected = projectController.selectedProject != null;
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
MyText.titleMedium(
|
|
||||||
'Quick Stats',
|
|
||||||
fontWeight: 700,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
MySpacing.height(4),
|
|
||||||
MyText.bodySmall(
|
|
||||||
'Overview of your finances',
|
|
||||||
color: Colors.grey[600],
|
|
||||||
),
|
|
||||||
MySpacing.height(16),
|
|
||||||
_buildStatsRow(projectSelected),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildStatsRow(bool projectSelected) {
|
|
||||||
final stats = [
|
|
||||||
_QuickStat(
|
|
||||||
icon: LucideIcons.trending_up,
|
|
||||||
label: 'Total Expenses',
|
|
||||||
value: projectSelected ? '₹0' : '--',
|
|
||||||
color: contentTheme.danger,
|
|
||||||
),
|
|
||||||
_QuickStat(
|
|
||||||
icon: LucideIcons.clock,
|
|
||||||
label: 'Pending',
|
|
||||||
value: projectSelected ? '0' : '--',
|
|
||||||
color: contentTheme.warning,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
children: stats
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
.map((stat) => Expanded(
|
children: stats.map((stat) {
|
||||||
|
return Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(right: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
child: _buildStatCard(stat, projectSelected),
|
child: _buildFinanceModuleCard(stat, projectSelected),
|
||||||
),
|
),
|
||||||
))
|
);
|
||||||
.toList(),
|
}).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildStatCard(_QuickStat stat, bool isEnabled) {
|
Widget _buildFinanceModuleCard(
|
||||||
return Container(
|
_FinanceStatItem stat, bool isProjectSelected) {
|
||||||
padding: const EdgeInsets.all(16),
|
final bool isEnabled = isProjectSelected;
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
return Opacity(
|
||||||
borderRadius: BorderRadius.circular(12),
|
opacity: isEnabled ? 1.0 : 0.4,
|
||||||
border: Border.all(
|
child: IgnorePointer(
|
||||||
color: stat.color.withValues(alpha: 0.2),
|
ignoring: !isEnabled,
|
||||||
width: 1,
|
child: InkWell(
|
||||||
),
|
onTap: () => _onCardTap(stat, isEnabled),
|
||||||
),
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
child: MyCard.bordered(
|
||||||
|
height: 80,
|
||||||
|
paddingAll: 5,
|
||||||
|
borderRadiusAll: 5,
|
||||||
|
border: Border.all(color: Colors.grey.withOpacity(0.15)),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(6),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: stat.color.withValues(alpha: 0.1),
|
color: stat.color.withOpacity(0.1),
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
stat.icon,
|
stat.icon,
|
||||||
@ -471,119 +171,48 @@ class _FinanceScreenState extends State<FinanceScreen>
|
|||||||
color: stat.color,
|
color: stat.color,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
MySpacing.height(12),
|
MySpacing.height(6),
|
||||||
MyText.bodySmall(
|
Flexible(
|
||||||
stat.label,
|
child: Text(
|
||||||
color: Colors.grey[600],
|
stat.title,
|
||||||
fontSize: 11,
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
softWrap: true,
|
||||||
),
|
),
|
||||||
MySpacing.height(4),
|
|
||||||
MyText.titleLarge(
|
|
||||||
stat.value,
|
|
||||||
fontWeight: 700,
|
|
||||||
color: isEnabled ? Colors.black87 : Colors.grey[400],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onCardTap(_FinanceStatItem statItem, bool isEnabled) {
|
void _onCardTap(_FinanceStatItem statItem, bool isEnabled) {
|
||||||
if (!isEnabled) {
|
if (!isEnabled) {
|
||||||
Get.dialog(
|
Get.defaultDialog(
|
||||||
Dialog(
|
title: "No Project Selected",
|
||||||
shape: RoundedRectangleBorder(
|
middleText: "Please select a project before accessing this section.",
|
||||||
borderRadius: BorderRadius.circular(16),
|
confirm: ElevatedButton(
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(24),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.orange.withValues(alpha: 0.1),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
LucideIcons.badge_alert,
|
|
||||||
color: Colors.orange[700],
|
|
||||||
size: 32,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
MySpacing.height(16),
|
|
||||||
MyText.titleMedium(
|
|
||||||
"No Project Selected",
|
|
||||||
fontWeight: 700,
|
|
||||||
color: Colors.black87,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
MySpacing.height(8),
|
|
||||||
MyText.bodyMedium(
|
|
||||||
"Please select a project before accessing this section.",
|
|
||||||
color: Colors.grey[600],
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
MySpacing.height(24),
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: () => Get.back(),
|
onPressed: () => Get.back(),
|
||||||
style: ElevatedButton.styleFrom(
|
child: const Text("OK"),
|
||||||
backgroundColor: contentTheme.primary,
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
elevation: 0,
|
|
||||||
),
|
|
||||||
child: MyText.bodyMedium(
|
|
||||||
"OK",
|
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: 600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return;
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
Get.toNamed(statItem.route);
|
Get.toNamed(statItem.route);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _FinanceStatItem {
|
class _FinanceStatItem {
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
final String title;
|
final String title;
|
||||||
final String subtitle;
|
|
||||||
final Color color;
|
final Color color;
|
||||||
final String route;
|
final String route;
|
||||||
|
|
||||||
_FinanceStatItem(
|
_FinanceStatItem(this.icon, this.title, this.color, this.route);
|
||||||
this.icon,
|
|
||||||
this.title,
|
|
||||||
this.subtitle,
|
|
||||||
this.color,
|
|
||||||
this.route,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _QuickStat {
|
|
||||||
final IconData icon;
|
|
||||||
final String label;
|
|
||||||
final String value;
|
|
||||||
final Color color;
|
|
||||||
|
|
||||||
_QuickStat({
|
|
||||||
required this.icon,
|
|
||||||
required this.label,
|
|
||||||
required this.value,
|
|
||||||
required this.color,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user