Compare commits
4 Commits
main
...
Vaibhav_Lo
Author | SHA1 | Date | |
---|---|---|---|
350fe3d9ce | |||
b9e2f72d11 | |||
d6043eb38c | |||
3d28cf9e09 |
1
.gitignore
vendored
@ -9,6 +9,7 @@
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
android/app/.cxx/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
|
@ -1,6 +1,6 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application
|
||||
android:label="maxdash"
|
||||
android:label="Marco"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
<!-- <item android:drawable="?android:colorBackground" /> -->
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.5 KiB |
BIN
assets/logo/logo.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 46 KiB |
28
lib/api/api_client.dart
Normal file
@ -0,0 +1,28 @@
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class ApiClient {
|
||||
static final ApiClient instance = ApiClient('http://10.0.2.2:5032/api');
|
||||
final String _baseUrl;
|
||||
|
||||
ApiClient(this._baseUrl);
|
||||
|
||||
Future<http.Response> get(String endpoint) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final token = prefs.getString('auth_token');
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('No token found. User might not be logged in.');
|
||||
}
|
||||
|
||||
final url = '$_baseUrl/$endpoint';
|
||||
print("Url received: $url");
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
return await http.get(Uri.parse(url), headers: headers);
|
||||
}
|
||||
}
|
||||
|
22
lib/api/api_service.dart
Normal file
@ -0,0 +1,22 @@
|
||||
import 'dart:convert';
|
||||
import 'package:maxdash/api/api_client.dart';
|
||||
|
||||
class ApiService {
|
||||
final ApiClient _apiClient = ApiClient.instance;
|
||||
|
||||
Future<dynamic> getProjectSummaries() async {
|
||||
try {
|
||||
final response = await _apiClient.get('project/list');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
// print('Response body: ${response.body}'); // Parsed JSON
|
||||
return jsonDecode(response.body);
|
||||
|
||||
} else {
|
||||
throw Exception('Failed to load project summaries: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Error in getProjectSummaries: $e');
|
||||
}
|
||||
}
|
||||
}
|
@ -1,54 +1,24 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:maxdash/controller/my_controller.dart';
|
||||
import 'package:maxdash/helpers/services/auth_service.dart';
|
||||
import 'package:maxdash/helpers/widgets/my_form_validator.dart';
|
||||
import 'package:maxdash/helpers/widgets/my_validators.dart';
|
||||
|
||||
class LoginController extends MyController {
|
||||
class LoginController extends GetxController {
|
||||
MyFormValidator basicValidator = MyFormValidator();
|
||||
|
||||
bool showPassword = false, isChecked = false;
|
||||
|
||||
final String _dummyEmail = "demo@example.com";
|
||||
final String _dummyPassword = "1234567";
|
||||
bool showPassword = false;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
basicValidator.addField('email', required: true, label: "Email", validators: [MyEmailValidator()], controller: TextEditingController(text: _dummyEmail));
|
||||
|
||||
basicValidator.addField('password',
|
||||
required: true, label: "Password", validators: [MyLengthValidator(min: 6, max: 10)], controller: TextEditingController(text: _dummyPassword));
|
||||
|
||||
basicValidator.addField('username', required: true, label: "Username", controller: TextEditingController());
|
||||
basicValidator.addField('password', required: true, label: "Password", controller: TextEditingController());
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
void onChangeCheckBox(bool? value) {
|
||||
isChecked = value ?? isChecked;
|
||||
update();
|
||||
}
|
||||
|
||||
void onChangeShowPassword() {
|
||||
showPassword = !showPassword;
|
||||
update();
|
||||
}
|
||||
|
||||
Future<void> onLogin() async {
|
||||
if (basicValidator.validateForm()) {
|
||||
update();
|
||||
var errors = await AuthService.loginUser(basicValidator.getData());
|
||||
if (errors != null) {
|
||||
basicValidator.addErrors(errors);
|
||||
basicValidator.validateForm();
|
||||
basicValidator.clearErrors();
|
||||
} else {
|
||||
String nextUrl = Uri.parse(ModalRoute.of(Get.context!)?.settings.name ?? "").queryParameters['next'] ?? "/home";
|
||||
Get.toNamed(nextUrl);
|
||||
}
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void goToForgotPassword() {
|
||||
Get.toNamed('/auth/forgot_password');
|
||||
}
|
||||
@ -56,4 +26,47 @@ class LoginController extends MyController {
|
||||
void gotoRegister() {
|
||||
Get.offAndToNamed('/auth/register_account');
|
||||
}
|
||||
Future<void> onLogin() async {
|
||||
if (basicValidator.validateForm()) {
|
||||
final data = basicValidator.getData();
|
||||
|
||||
// Log the data being sent
|
||||
print('Sending login request with data: $data');
|
||||
|
||||
// Show a loading dialog
|
||||
Get.dialog(
|
||||
Center(child: CircularProgressIndicator()),
|
||||
barrierDismissible: false,
|
||||
);
|
||||
|
||||
try {
|
||||
// Call the AuthService to perform the login
|
||||
final errors = await AuthService.loginUser(data);
|
||||
print('Login errors: $errors');
|
||||
|
||||
// Close the loading dialog
|
||||
Get.back();
|
||||
|
||||
if (errors != null) {
|
||||
// Display errors if login fails
|
||||
print('Login failed with errors: $errors');
|
||||
basicValidator.addErrors(errors);
|
||||
basicValidator.validateForm();
|
||||
basicValidator.clearErrors();
|
||||
} else {
|
||||
// Navigate to the home screen on successful login
|
||||
print('Login successful. Navigating to home...');
|
||||
Get.offAllNamed('/home');
|
||||
}
|
||||
} catch (e) {
|
||||
// Close the loading dialog in case of an exception
|
||||
Get.back();
|
||||
|
||||
// Show a generic error message
|
||||
print('Unexpected error: $e');
|
||||
Get.snackbar('Error', 'An unexpected error occurred. Please try again.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -2,12 +2,15 @@ import 'package:maxdash/controller/my_controller.dart';
|
||||
import 'package:maxdash/model/chart_model.dart';
|
||||
import 'package:maxdash/model/project_summary_model.dart';
|
||||
import 'package:maxdash/model/task_list_model.dart';
|
||||
import 'package:maxdash/model/ProjectSummaryApiModel.dart';
|
||||
import 'package:maxdash/api/api_service.dart';
|
||||
import 'package:syncfusion_flutter_charts/charts.dart';
|
||||
|
||||
class ProjectController extends MyController {
|
||||
TooltipBehavior? tooltipBehavior;
|
||||
List<TaskListModel> task = [];
|
||||
List<ProjectSummaryModel> projectSummary = [];
|
||||
List<ProjectSummaryModel> projectSummary = []; // Dummy data (local)
|
||||
List<ProjectSummaryApiModel> projectSummaryApiList = []; // API data
|
||||
List<ChartSampleData>? chartData;
|
||||
|
||||
@override
|
||||
@ -20,6 +23,11 @@ class ProjectController extends MyController {
|
||||
projectSummary = value.sublist(0, 5);
|
||||
update();
|
||||
});
|
||||
|
||||
// Fetch project summary from API
|
||||
fetchProjectSummariesFromApi();
|
||||
|
||||
// Dummy chart data
|
||||
chartData = <ChartSampleData>[
|
||||
ChartSampleData(x: 'Jan', y: 10, secondSeriesYValue: 8, thirdSeriesYValue: 12),
|
||||
ChartSampleData(x: 'Feb', y: 5, secondSeriesYValue: 6, thirdSeriesYValue: 7),
|
||||
@ -43,4 +51,19 @@ class ProjectController extends MyController {
|
||||
task.isSelectTask = !task.isSelectTask;
|
||||
update();
|
||||
}
|
||||
|
||||
Future<void> fetchProjectSummariesFromApi() async {
|
||||
try {
|
||||
final response = await ApiService().getProjectSummaries();
|
||||
|
||||
if (response != null && response is List) {
|
||||
projectSummaryApiList = response
|
||||
.map<ProjectSummaryApiModel>((json) => ProjectSummaryApiModel.fromJson(json))
|
||||
.toList();
|
||||
update();
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading API project summaries: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,46 @@
|
||||
import 'package:maxdash/helpers/services/storage/local_storage.dart';
|
||||
import 'package:maxdash/model/user.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class AuthService {
|
||||
static const String baseUrl = 'https://api.marcoaiot.com/api';
|
||||
|
||||
static bool isLoggedIn = false;
|
||||
|
||||
static User get dummyUser => User(-1, "demo@example.com", "Denish", "Navadiya");
|
||||
static Future<Map<String, String>?> loginUser(Map<String, dynamic> data) async {
|
||||
try {
|
||||
print('Sending login request with data: $data');
|
||||
|
||||
static Future<Map<String, String>?> loginUser(
|
||||
Map<String, dynamic> data) async {
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
if (data['email'] != dummyUser.email) {
|
||||
return {"email": "This email is not registered"};
|
||||
} else if (data['password'] != "1234567") {
|
||||
return {"password": "Password is incorrect"};
|
||||
// Make the API call
|
||||
final response = await http.post(
|
||||
Uri.parse('$baseUrl/Auth/login'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonEncode(data),
|
||||
);
|
||||
|
||||
print('Response received: ${response.body}');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final responseData = jsonDecode(response.body);
|
||||
|
||||
// Extract the token from the "data" field
|
||||
final token = responseData['data']?['token']; // Adjust based on your API response
|
||||
if (token != null) {
|
||||
// Save the token locally
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('auth_token', token);
|
||||
isLoggedIn = true;
|
||||
return null; // No errors
|
||||
} else {
|
||||
return {'error': 'Token not found in response'};
|
||||
}
|
||||
} else {
|
||||
final responseData = jsonDecode(response.body);
|
||||
return {'error': responseData['message'] ?? 'Invalid response from server'};
|
||||
}
|
||||
} catch (e) {
|
||||
print('Unexpected error: $e');
|
||||
return {'error': 'Unexpected error: $e'};
|
||||
}
|
||||
|
||||
isLoggedIn = true;
|
||||
await LocalStorage.setLoggedInUser(true);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -5,10 +5,10 @@ class Images {
|
||||
static List<String> dummy = List.generate(5, (index) => 'assets/dummy/dummy_${index + 1}.jpg');
|
||||
static List<String> product = List.generate(10, (index) => 'assets/dummy/ecommerce/product_${index + 1}.jpg');
|
||||
|
||||
static String logoLight = 'assets/logo/logo_light.png';
|
||||
static String logoLightSmall = 'assets/logo/logo_light_small.png';
|
||||
static String logoDark = 'assets/logo/logo_dark.png';
|
||||
static String logoDarkSmall = 'assets/logo/logo_dark_small.png';
|
||||
static String logoLight = 'assets/logo/logo.png';
|
||||
static String logoLightSmall = 'assets/logo/logo.png';
|
||||
static String logoDark = 'assets/logo/logo.png';
|
||||
static String logoDarkSmall = 'assets/logo/logo.png';
|
||||
static String authBackground = 'assets/auth_background.jpg';
|
||||
|
||||
static String randomImage(List<String> images) {
|
||||
|
31
lib/model/ProjectSummaryApiModel.dart
Normal file
@ -0,0 +1,31 @@
|
||||
class ProjectSummaryApiModel {
|
||||
final int id;
|
||||
final String name;
|
||||
final String projectAddress;
|
||||
final String contactPerson;
|
||||
final DateTime startDate;
|
||||
final DateTime endDate;
|
||||
final int projectStatusId;
|
||||
|
||||
ProjectSummaryApiModel({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.projectAddress,
|
||||
required this.contactPerson,
|
||||
required this.startDate,
|
||||
required this.endDate,
|
||||
required this.projectStatusId,
|
||||
});
|
||||
|
||||
factory ProjectSummaryApiModel.fromJson(Map<String, dynamic> json) {
|
||||
return ProjectSummaryApiModel(
|
||||
id: json['id'],
|
||||
name: json['name'],
|
||||
projectAddress: json['projectAddress'],
|
||||
contactPerson: json['contactPerson'],
|
||||
startDate: DateTime.parse(json['startDate']),
|
||||
endDate: DateTime.parse(json['endDate']),
|
||||
projectStatusId: json['projectStatusId'],
|
||||
);
|
||||
}
|
||||
}
|
@ -1,14 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_lucide/flutter_lucide.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:maxdash/controller/auth/login_controller.dart';
|
||||
import 'package:maxdash/helpers/theme/app_theme.dart';
|
||||
import 'package:maxdash/helpers/utils/mixins/ui_mixin.dart';
|
||||
import 'package:maxdash/helpers/widgets/my_button.dart';
|
||||
import 'package:maxdash/helpers/widgets/my_spacing.dart';
|
||||
import 'package:maxdash/helpers/widgets/my_text.dart';
|
||||
import 'package:maxdash/helpers/widgets/my_text_style.dart';
|
||||
import 'package:maxdash/view/layouts/auth_layout.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
const LoginScreen({super.key});
|
||||
@ -17,7 +9,7 @@ class LoginScreen extends StatefulWidget {
|
||||
State<LoginScreen> createState() => _LoginScreenState();
|
||||
}
|
||||
|
||||
class _LoginScreenState extends State<LoginScreen> with UIMixin{
|
||||
class _LoginScreenState extends State<LoginScreen> {
|
||||
late LoginController controller;
|
||||
|
||||
@override
|
||||
@ -28,115 +20,71 @@ class _LoginScreenState extends State<LoginScreen> with UIMixin{
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AuthLayout(
|
||||
child: GetBuilder(
|
||||
init: controller,
|
||||
tag: 'login_controller',
|
||||
builder: (controller) {
|
||||
return Form(
|
||||
return Scaffold(
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Form(
|
||||
key: controller.basicValidator.formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
MyText.titleLarge("Sign in with email", fontWeight: 600),
|
||||
MySpacing.height(12),
|
||||
MyText.bodyMedium("Make a new doc to bring your words, data and terms together. For free", fontWeight: 600, xMuted: true),
|
||||
MySpacing.height(12),
|
||||
TextFormField(
|
||||
validator: controller.basicValidator.getValidation('email'),
|
||||
controller: controller.basicValidator.getController('email'),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
style: MyTextStyle.labelMedium(),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Email Address",
|
||||
labelStyle: MyTextStyle.bodySmall(xMuted: true),
|
||||
border: OutlineInputBorder(borderSide: BorderSide.none),
|
||||
filled: true,
|
||||
fillColor: contentTheme.secondary.withAlpha(36),
|
||||
prefixIcon: const Icon(LucideIcons.mail, size: 16),
|
||||
contentPadding: MySpacing.all(14),
|
||||
isDense: true,
|
||||
isCollapsed: true,
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never),
|
||||
Text(
|
||||
'Welcome Back!',
|
||||
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
||||
),
|
||||
MySpacing.height(20),
|
||||
SizedBox(height: 10),
|
||||
Text(
|
||||
'Please login to your account.',
|
||||
style: TextStyle(fontSize: 16, color: Colors.grey),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
TextFormField(
|
||||
controller: controller.basicValidator.getController('username'),
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Username',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
TextFormField(
|
||||
validator: controller.basicValidator.getValidation('password'),
|
||||
controller: controller.basicValidator.getController('password'),
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
obscureText: !controller.showPassword,
|
||||
style: MyTextStyle.labelMedium(),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Password",
|
||||
labelStyle: MyTextStyle.bodySmall(xMuted: true),
|
||||
border: OutlineInputBorder(borderSide: BorderSide.none),
|
||||
filled: true,
|
||||
fillColor: contentTheme.secondary.withAlpha(36),
|
||||
prefixIcon: const Icon(LucideIcons.mail, size: 16),
|
||||
contentPadding: MySpacing.all(16),
|
||||
isCollapsed: true,
|
||||
isDense: true,
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
suffixIcon: InkWell(
|
||||
onTap: controller.onChangeShowPassword,
|
||||
child: Icon(controller.showPassword ? LucideIcons.eye : LucideIcons.eye_off, size: 16),
|
||||
)),
|
||||
labelText: 'Password',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
obscureText: true,
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
ElevatedButton(
|
||||
onPressed: controller.onLogin,
|
||||
child: Text('Login'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: Size(double.infinity, 50),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => controller.onChangeCheckBox(!controller.isChecked),
|
||||
child: Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
onChanged: controller.onChangeCheckBox,
|
||||
value: controller.isChecked,
|
||||
fillColor: WidgetStatePropertyAll(Colors.white),
|
||||
activeColor: theme.colorScheme.primary,
|
||||
checkColor: contentTheme.primary,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
visualDensity: getCompactDensity,
|
||||
),
|
||||
MySpacing.width(8),
|
||||
MyText.bodySmall("Remember Me"),
|
||||
],
|
||||
),
|
||||
),
|
||||
MyButton.text(
|
||||
onPressed: controller.goToForgotPassword,
|
||||
elevation: 0,
|
||||
padding: MySpacing.xy(8, 0),
|
||||
splashColor: contentTheme.secondary.withAlpha(36),
|
||||
child: MyText.bodySmall('Forgot password?', color: contentTheme.secondary),
|
||||
Text('Don’t have an account?'),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Get.toNamed('/auth/register_account'); // Navigate to the register screen
|
||||
},
|
||||
child: Text('Sign Up'),
|
||||
),
|
||||
],
|
||||
),
|
||||
MySpacing.height(28),
|
||||
Center(
|
||||
child: MyButton.rounded(
|
||||
onPressed: controller.onLogin,
|
||||
elevation: 0,
|
||||
padding: MySpacing.xy(20, 16),
|
||||
backgroundColor: contentTheme.primary,
|
||||
child: MyText.labelMedium('Login', color: contentTheme.onPrimary),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: MyButton.text(
|
||||
onPressed: controller.gotoRegister,
|
||||
elevation: 0,
|
||||
padding: MySpacing.x(16),
|
||||
splashColor: contentTheme.secondary.withValues(alpha:0.1),
|
||||
child: MyText.bodySmall('I haven\'t account'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Get.toNamed('/auth/forgot_password'); // Navigate to the forgot password screen
|
||||
},
|
||||
child: Text('Forgot Password?'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -370,66 +370,55 @@ class _ProjectScreenState extends State<ProjectScreen> with UIMixin {
|
||||
}
|
||||
|
||||
Widget projectSummary() {
|
||||
return MyCard.bordered(
|
||||
borderRadiusAll: 4,
|
||||
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: [
|
||||
shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom),
|
||||
paddingAll: 24,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
MyText.bodyMedium("Project Summary", fontWeight: 600),
|
||||
MySpacing.height(24),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: DataTable(
|
||||
sortAscending: true,
|
||||
columnSpacing: 84,
|
||||
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('Title', color: contentTheme.primary)),
|
||||
DataColumn(label: MyText.labelLarge('Assign to', color: contentTheme.primary)),
|
||||
DataColumn(label: MyText.labelLarge('Due Date', color: contentTheme.primary)),
|
||||
DataColumn(label: MyText.labelLarge('Priority', color: contentTheme.primary)),
|
||||
DataColumn(label: MyText.labelLarge('Status', color: contentTheme.primary)),
|
||||
DataColumn(label: MyText.labelLarge('Action', color: contentTheme.primary)),
|
||||
],
|
||||
rows: controller.projectSummary
|
||||
.mapIndexed((index, data) => DataRow(cells: [
|
||||
DataCell(MyText.bodyMedium("#${data.id}", fontWeight: 600)),
|
||||
DataCell(MyText.bodyMedium(data.title, fontWeight: 600)),
|
||||
DataCell(MyText.bodyMedium(data.assignTo, fontWeight: 600)),
|
||||
DataCell(MyText.bodyMedium(Utils.getDateStringFromDateTime(data.date), fontWeight: 600)),
|
||||
DataCell(MyText.bodyMedium(data.priority, fontWeight: 600)),
|
||||
DataCell(MyText.bodyMedium(data.status, fontWeight: 600)),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
MyContainer(
|
||||
onTap: () {},
|
||||
color: contentTheme.primary,
|
||||
paddingAll: 8,
|
||||
child: Icon(LucideIcons.download, size: 16, color: contentTheme.onPrimary),
|
||||
),
|
||||
MySpacing.width(12),
|
||||
MyContainer(
|
||||
onTap: () {},
|
||||
color: contentTheme.secondary,
|
||||
paddingAll: 8,
|
||||
child: Icon(LucideIcons.pencil, size: 16, color: contentTheme.onPrimary),
|
||||
),
|
||||
],
|
||||
))
|
||||
]))
|
||||
MySpacing.height(24),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: DataTable(
|
||||
sortAscending: true,
|
||||
columnSpacing: 84,
|
||||
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('Name', color: contentTheme.primary)),
|
||||
DataColumn(label: MyText.labelLarge('Address', color: contentTheme.primary)),
|
||||
DataColumn(label: MyText.labelLarge('Contact Person', color: contentTheme.primary)),
|
||||
DataColumn(label: MyText.labelLarge('Start Date', color: contentTheme.primary)),
|
||||
DataColumn(label: MyText.labelLarge('End Date', color: contentTheme.primary)),
|
||||
DataColumn(label: MyText.labelLarge('Status ID', color: contentTheme.primary)),
|
||||
],
|
||||
rows: controller.projectSummaryApiList
|
||||
.mapIndexed((index, data) => DataRow(cells: [
|
||||
DataCell(MyText.bodyMedium("#${index + 1}", fontWeight: 600)),
|
||||
DataCell(MyText.bodyMedium(data.name, fontWeight: 600)),
|
||||
DataCell(MyText.bodyMedium(data.projectAddress, fontWeight: 600)),
|
||||
DataCell(MyText.bodyMedium(data.contactPerson, fontWeight: 600)),
|
||||
DataCell(MyText.bodyMedium(Utils.getDateStringFromDateTime(data.startDate), fontWeight: 600)),
|
||||
DataCell(MyText.bodyMedium(Utils.getDateStringFromDateTime(data.endDate), fontWeight: 600)),
|
||||
DataCell(MyText.bodyMedium(data.projectStatusId.toString(), fontWeight: 600)),
|
||||
]))
|
||||
.toList()),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ packages:
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
collection:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
@ -366,7 +366,7 @@ packages:
|
||||
source: hosted
|
||||
version: "0.15.5"
|
||||
http:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
|
||||
|
@ -58,7 +58,8 @@ dependencies:
|
||||
appflowy_board: ^0.1.2
|
||||
syncfusion_flutter_calendar: ^28.2.6
|
||||
syncfusion_flutter_maps: ^28.1.33
|
||||
|
||||
http: ^1.2.2
|
||||
collection: ^1.18.0
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
BIN
web/favicon.png
Before Width: | Height: | Size: 917 B After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 2.5 KiB |
BIN
web/img/logo.png
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 2.5 KiB |
@ -29,7 +29,7 @@
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
|
||||
<title>maxdash</title>
|
||||
<title>Marco PMS</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
</head>
|
||||
<head>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "maxdash",
|
||||
"short_name": "maxdash",
|
||||
"name": "Marco PMS",
|
||||
"short_name": "Marco",
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"background_color": "#0175C2",
|
||||
|