Vaibhav Surve 99902e743c Flutter application
- Created generated_plugin_registrant.cc and generated_plugin_registrant.h to manage plugin registration.
- Added generated_plugins.cmake for plugin configuration in CMake.
- Implemented CMakeLists.txt for the Windows runner, defining build settings and dependencies.
- Created Runner.rc for application resources including versioning and icons.
- Developed flutter_window.cpp and flutter_window.h to manage the Flutter window lifecycle.
- Implemented main.cpp as the entry point for the Windows application.
- Added resource.h for resource definitions.
- Included app icon in resources.
- Created runner.exe.manifest for application settings.
- Developed utils.cpp and utils.h for console management and command line argument handling.
- Implemented win32_window.cpp and win32_window.h for high DPI-aware window management.
2025-04-17 12:30:38 +05:30

524 lines
22 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:get/get.dart';
import 'package:marco/controller/dashboard/crm_controller.dart';
import 'package:marco/helpers/theme/app_theme.dart';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
import 'package:marco/helpers/utils/my_shadow.dart';
import 'package:marco/helpers/utils/utils.dart';
import 'package:marco/helpers/widgets/my_breadcrumb.dart';
import 'package:marco/helpers/widgets/my_breadcrumb_item.dart';
import 'package:marco/helpers/widgets/my_card.dart';
import 'package:marco/helpers/widgets/my_container.dart';
import 'package:marco/helpers/widgets/my_flex.dart';
import 'package:marco/helpers/widgets/my_flex_item.dart';
import 'package:marco/helpers/widgets/my_list_extension.dart';
import 'package:marco/helpers/widgets/my_progress_bar.dart';
import 'package:marco/helpers/widgets/my_spacing.dart';
import 'package:marco/helpers/widgets/my_text.dart';
import 'package:marco/images.dart';
import 'package:marco/model/chart_model.dart';
import 'package:marco/view/layouts/layout.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
class CrmScreen extends StatefulWidget {
const CrmScreen({super.key});
@override
State<CrmScreen> createState() => _CrmScreenState();
}
class _CrmScreenState extends State<CrmScreen> with UIMixin {
CrmController controller = Get.put(CrmController());
@override
Widget build(BuildContext context) {
return GetBuilder(
init: controller,
tag: 'crm_dashboard_controller',
builder: (controller) {
return Layout(
child: Column(
children: [
Padding(
padding: MySpacing.x(flexSpacing),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MyText.titleMedium("Crm", fontSize: 18, fontWeight: 600),
MyBreadcrumb(
children: [
MyBreadcrumbItem(name: 'Dashboard'),
MyBreadcrumbItem(name: 'Crm', active: true),
],
),
],
),
),
MySpacing.height(flexSpacing),
Padding(
padding: MySpacing.x(flexSpacing / 2),
child: MyFlex(
children: [
MyFlexItem(
sizes: "lg-3 md-6 sm-6",
child:
stats(LucideIcons.circle_dollar_sign, "Nominal Balance", "\$5,780", "Nominal Balance last month", "\$6,290", contentTheme.primary)),
MyFlexItem(
sizes: "lg-3 md-6 sm-6",
child: stats(LucideIcons.package, "Total Stock Product", "5.264", "Total Stock product last month", "2.546", contentTheme.secondary)),
MyFlexItem(
sizes: "lg-3 md-6 sm-6",
child: stats(LucideIcons.file_down, "Nominal Revenue", "5.264", "Total revenue last month", "2.546", contentTheme.success)),
MyFlexItem(
sizes: "lg-3 md-6 sm-6",
child: stats(LucideIcons.file_up, "Nominal Expenses", "\$19,644", "Total expenses last month", "\$18,946", contentTheme.warning)),
MyFlexItem(sizes: 'lg-9', child: revenueForecast()),
MyFlexItem(sizes: 'lg-3', child: topDeals()),
MyFlexItem(sizes: 'lg-3 md-6 sm-6', child: dealSource()),
MyFlexItem(sizes: 'lg-3 md-6 sm-6', child: leadResponse()),
MyFlexItem(sizes: 'lg-3 md-6 sm-6', child: openDeals()),
MyFlexItem(sizes: 'lg-3 md-6 sm-6', child: leadSource()),
MyFlexItem(child: leadReport()),
],
),
),
],
),
);
},
);
}
Widget stats(IconData icon, String title, String subTitle, String statsMonthName, String statsMonthRevenue, Color color) {
return MyCard.bordered(
borderRadiusAll: 4,
border: Border.all(color: Colors.grey.withValues(alpha:.2)),
shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom),
paddingAll: 24,
child: SizedBox(
height: 100,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
MyContainer.bordered(paddingAll: 8, borderColor: color, child: Icon(icon, size: 16, color: color)),
MySpacing.width(12),
MyText.bodyMedium(title, fontWeight: 600),
],
),
MyText.titleMedium(subTitle, fontWeight: 600),
Row(
children: [
MyText.labelMedium(statsMonthName, fontWeight: 600, xMuted: true),
MySpacing.width(8),
Expanded(child: MyText.labelMedium(statsMonthRevenue, fontWeight: 600, maxLines: 1)),
],
),
],
),
),
);
}
Widget revenueForecast() {
return MyCard.bordered(
borderRadiusAll: 4,
border: Border.all(color: Colors.grey.withValues(alpha:.2)),
shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom),
paddingAll: 24,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MyText.bodyMedium("Revenue Forecast", fontWeight: 600),
PopupMenuButton(
offset: Offset(0, 20),
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide.none),
itemBuilder: (BuildContext context) => [
PopupMenuItem(padding: MySpacing.xy(16, 8), height: 10, child: MyText.bodySmall("Download", fontWeight: 600)),
PopupMenuItem(padding: MySpacing.xy(16, 8), height: 10, child: MyText.bodySmall("Import", fontWeight: 600)),
PopupMenuItem(padding: MySpacing.xy(16, 8), height: 10, child: MyText.bodySmall("Export", fontWeight: 600)),
],
child: Icon(LucideIcons.ellipsis_vertical, size: 20),
)
],
),
MySpacing.height(24),
SfCartesianChart(
plotAreaBorderWidth: 0,
primaryXAxis: const CategoryAxis(
majorGridLines: MajorGridLines(width: 0),
),
margin: MySpacing.zero,
primaryYAxis: const NumericAxis(maximum: 20, minimum: 0, interval: 4, axisLine: AxisLine(width: 0), majorTickLines: MajorTickLines(size: 0)),
series: [
ColumnSeries<ChartSampleData, String>(
width: 0.8,
spacing: 0.2,
dataSource: controller.chartData,
color: contentTheme.primary,
xValueMapper: (ChartSampleData sales, _) => sales.x as String,
yValueMapper: (ChartSampleData sales, _) => sales.y,
name: 'Sales Revenue'),
ColumnSeries<ChartSampleData, String>(
dataSource: controller.chartData,
width: 0.8,
spacing: 0.2,
color: contentTheme.secondary,
xValueMapper: (ChartSampleData sales, _) => sales.x as String,
yValueMapper: (ChartSampleData sales, _) => sales.secondSeriesYValue,
name: 'Product Cost'),
],
legend: Legend(isVisible: true, position: LegendPosition.bottom),
tooltipBehavior: controller.tooltipBehavior,
)
],
),
);
}
Widget topDeals() {
Widget topDealsWidget(String image, String name, String email, String price) {
return Row(
children: [
MyContainer.rounded(
paddingAll: 0,
height: 40,
width: 40,
child: Image.asset(image, fit: BoxFit.cover),
),
MySpacing.width(12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.bodyMedium(name, fontWeight: 600),
MyText.labelMedium(email, fontWeight: 600, muted: true, maxLines: 1),
],
),
),
MyText.labelMedium(price, fontWeight: 600),
],
);
}
return MyCard.bordered(
borderRadiusAll: 4,
border: Border.all(color: Colors.grey.withValues(alpha:.2)),
shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom),
paddingAll: 24,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MyText.bodyMedium("Top Deals", fontWeight: 600),
PopupMenuButton(
offset: Offset(0, 20),
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide.none),
itemBuilder: (BuildContext context) => [
PopupMenuItem(padding: MySpacing.xy(16, 8), height: 10, child: MyText.bodySmall("Week", fontWeight: 600)),
PopupMenuItem(padding: MySpacing.xy(16, 8), height: 10, child: MyText.bodySmall("Month", fontWeight: 600)),
PopupMenuItem(padding: MySpacing.xy(16, 8), height: 10, child: MyText.bodySmall("Year", fontWeight: 600)),
],
child: Icon(LucideIcons.ellipsis_vertical, size: 20),
)
],
),
MySpacing.height(24),
topDealsWidget(Images.avatars[0], "Christopher", "christopher123@gmail.com", "\$14,541"),
MySpacing.height(24),
topDealsWidget(Images.avatars[1], "Edward", "edward15@gmail.com", "\$21,548"),
MySpacing.height(24),
topDealsWidget(Images.avatars[2], "Michael", "michael@gmail.com", "\$13,645"),
MySpacing.height(24),
topDealsWidget(Images.avatars[3], "Sebastian", "sebastian@gmail.com", "\$51,254"),
MySpacing.height(24),
topDealsWidget(Images.avatars[4], "Nicholas", "nicholas@gmail.com", "\$15,487"),
],
),
);
}
Widget dealSource() {
Widget dealSourceWidget(String image, String title, String subtitle, String totalLeads) {
return Row(
children: [
MyContainer.rounded(
height: 32,
width: 32,
paddingAll: 0,
child: Image.asset(image, fit: BoxFit.cover),
),
MySpacing.width(12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.bodyMedium(title, fontWeight: 600),
MyText.bodySmall(subtitle),
],
),
),
MyText.labelSmall('$totalLeads Leads', fontWeight: 600),
],
);
}
return MyCard.bordered(
borderRadiusAll: 4,
border: Border.all(color: Colors.grey.withValues(alpha:.2)),
shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom),
paddingAll: 24,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.bodyMedium("Deal Source", fontWeight: 600),
MySpacing.height(24),
dealSourceWidget("assets/social/uxerflow_logo.png", "Website", "userflow.com", "50"),
MySpacing.height(24),
dealSourceWidget("assets/social/dribbble-logo.png", "Dribbble", "dribbble.com", "50"),
MySpacing.height(24),
dealSourceWidget("assets/social/facebook-logo.png", "Facebook", "facebook.com", "50"),
MySpacing.height(24),
dealSourceWidget("assets/social/instagram-logo.png", "Instagram", "instagram.com", "50"),
MySpacing.height(24),
dealSourceWidget("assets/social/LinkedIn-logo.png", "Linkedin", "linkedin.com", "50"),
],
),
);
}
Widget leadResponse() {
Widget leadData(String image, name, processRate, double progress) {
return Row(
children: [
MyContainer(
paddingAll: 0,
height: 32,
width: 32,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Image.asset(image, fit: BoxFit.cover),
),
MySpacing.width(24),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: MyText.labelLarge(name, fontWeight: 600, overflow: TextOverflow.ellipsis),
),
MyText.labelSmall(processRate, fontWeight: 600, overflow: TextOverflow.ellipsis),
],
),
MySpacing.height(8),
MyProgressBar(
width: 300,
height: 7,
progress: progress,
radius: 8,
activeColor: contentTheme.primary,
inactiveColor: contentTheme.primary.withAlpha(32),
)
],
),
)
],
);
}
return MyCard.bordered(
borderRadiusAll: 4,
border: Border.all(color: Colors.grey.withValues(alpha:.2)),
shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom),
paddingAll: 24,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.bodyMedium("Lead Response", fontWeight: 600),
MySpacing.height(24),
leadData(Images.avatars[0], "Amelia Thomson", "1.3", .3),
MySpacing.height(24),
leadData(Images.avatars[1], "Ian Ferguson", "1.4", .4),
MySpacing.height(24),
leadData(Images.avatars[2], "Simon Ross", "2", .8),
MySpacing.height(24),
leadData(Images.avatars[3], "Heather", "1.5", .5),
MySpacing.height(24),
leadData(Images.avatars[4], "Madeleine Simpson", "1.9", .7),
],
),
);
}
Widget openDeals() {
Widget dealsData(String image, dealsType, dealsDate, price) {
return Row(
children: [
MyContainer(
paddingAll: 0,
height: 46,
width: 46,
borderRadiusAll: 8,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Image.asset(image, fit: BoxFit.cover),
),
MySpacing.width(24),
Expanded(
child: SizedBox(
height: 32,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MyText.bodyMedium(dealsType, fontWeight: 600, overflow: TextOverflow.ellipsis),
MyText.labelSmall("Closing deal date ${dealsDate}", fontWeight: 600, overflow: TextOverflow.ellipsis)
],
),
),
),
MyText.labelMedium("\$${numberFormatter(price)}", fontWeight: 600, color: contentTheme.primary),
],
);
}
return MyCard.bordered(
borderRadiusAll: 4,
border: Border.all(color: Colors.grey.withValues(alpha:.2)),
shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom),
paddingAll: 24,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.bodyMedium("Open Deals", fontWeight: 600),
MySpacing.height(24),
dealsData(Images.avatars[1], "SASS app workflow", "26 Jan", "15478"),
MySpacing.height(24),
dealsData(Images.avatars[0], "Create new component", "8 Fab", "54791"),
MySpacing.height(24),
dealsData(Images.avatars[3], "New Email Design Template", "16 March", "54876"),
MySpacing.height(24),
dealsData(Images.avatars[4], "React Developer", "12 Fab", "1564"),
],
),
);
}
Widget leadSource() {
return MyCard.bordered(
borderRadiusAll: 4,
border: Border.all(color: Colors.grey.withValues(alpha:.2)),
shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom),
paddingAll: 24,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.bodyMedium("Lead Source", fontWeight: 600),
SizedBox(
height: 280,
child: SfCircularChart(
series: [
PieSeries<ChartSampleData, String>(
explode: true,
explodeIndex: 0,
dataSource: <ChartSampleData>[
ChartSampleData(x: 'Prospecting', y: 13, text: 'Prospecting \n 13%'),
ChartSampleData(x: 'Negotiation', y: 24, text: 'Negotiation \n 24%'),
ChartSampleData(x: 'Proposal', y: 25, text: 'Proposal \n 25%'),
ChartSampleData(x: 'Qualification', y: 38, text: 'Qualification \n 38%'),
],
xValueMapper: (ChartSampleData data, _) => data.x as String,
yValueMapper: (ChartSampleData data, _) => data.y,
dataLabelMapper: (ChartSampleData data, _) => data.text,
dataLabelSettings: DataLabelSettings(isVisible: true)),
],
),
)
],
),
);
}
Widget leadReport() {
return MyCard.bordered(
borderRadiusAll: 4,
border: Border.all(color: Colors.grey.withValues(alpha:.2)),
shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom),
paddingAll: 24,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.bodyMedium("Lead Report", fontWeight: 600),
MySpacing.height(24),
if (controller.leadReport.isNotEmpty)
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
sortAscending: true,
columnSpacing: 80,
onSelectAll: (_) => {},
headingRowColor: WidgetStatePropertyAll(contentTheme.primary.withAlpha(40)),
dataRowMaxHeight: 60,
showBottomBorder: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
border: TableBorder.all(borderRadius: BorderRadius.circular(4), style: BorderStyle.solid, width: .4, color: Colors.grey),
columns: [
DataColumn(label: MyText.labelLarge('S.No', color: contentTheme.primary)),
DataColumn(label: MyText.labelLarge('Lead', color: contentTheme.primary)),
DataColumn(label: MyText.labelLarge('Company Name', color: contentTheme.primary)),
DataColumn(label: MyText.labelLarge('Phone Number', color: contentTheme.primary)),
DataColumn(label: MyText.labelLarge('Status', color: contentTheme.primary)),
DataColumn(label: MyText.labelLarge('Location', color: contentTheme.primary)),
DataColumn(label: MyText.labelLarge('Date', color: contentTheme.primary)),
DataColumn(label: MyText.labelLarge('Amount', color: contentTheme.primary)),
],
rows: controller.leadReport
.mapIndexed((index, data) => DataRow(cells: [
DataCell(MyText.bodyMedium("#${data.id}", fontWeight: 600)),
DataCell(Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
MyContainer(
height: 44,
width: 44,
paddingAll: 0,
child: Image.asset(Images.avatars[index % Images.avatars.length], fit: BoxFit.cover),
),
MySpacing.width(24),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText.bodyMedium(data.firstName, fontWeight: 600),
MyText.labelMedium(data.email),
],
)
],
)),
DataCell(MyText.bodyMedium(data.companyName, fontWeight: 600)),
DataCell(MyText.bodyMedium(data.phoneNumber, fontWeight: 600)),
DataCell(MyText.bodyMedium(data.status, fontWeight: 600)),
DataCell(MyText.bodyMedium(data.location, fontWeight: 600)),
DataCell(MyText.bodyMedium("${Utils.getDateStringFromDateTime(data.date)}", fontWeight: 600)),
DataCell(MyText.bodyMedium("\$${data.amount}", fontWeight: 600)),
]))
.toList()),
),
],
));
}
}