From 6c69efd3eaff3b291a48f743a38d9d23da181eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincze=20J=C3=B3zsef?= Date: Thu, 16 May 2024 16:03:54 +0200 Subject: [PATCH] First Commit --- analysis_options.yaml | 28 + android/app/build.gradle | 81 ++ android/app/src/debug/AndroidManifest.xml | 8 + android/app/src/main/AndroidManifest.xml | 37 + android/app/src/profile/AndroidManifest.xml | 7 + android/build.gradle | 30 + android/gradle.properties | 3 + android/settings.gradle | 29 + assets/cloudlogo.png | Bin 0 -> 4550 bytes assets/nfc.png | Bin 0 -> 51304 bytes assets/nfcdenied.png | Bin 0 -> 73204 bytes gradle.properties | 14 + lib/app_worktime/dailyReport.dart | 468 +++++++++ lib/app_worktime/reportNavPage.dart | 207 ++++ lib/app_worktime/worktime_details.dart | 765 +++++++++++++++ lib/classes/common_classes.dart | 105 ++ lib/classes/named_route_args.dart | 7 + lib/main.dart | 154 +++ lib/main_page.dart | 999 ++++++++++++++++++++ lib/models/employee_model.dart | 68 ++ lib/models/reportListModel.dart | 148 +++ lib/models/workTimeDetailsModel.dart | 275 ++++++ lib/register_page.dart | 179 ++++ lib/report_selectWork.dart | 226 +++++ lib/services/device_info.dart | 183 ++++ lib/services/storage.dart | 32 + lib/widgets/reportCardWidget.dart | 193 ++++ lib/widgets/reportCreate.dart | 341 +++++++ lib/widgets/reportDetails.dart | 279 ++++++ lib/widgets/reportList.dart | 376 ++++++++ lib/widgets/reportNew.dart | 489 ++++++++++ pubspec.yaml | 119 +++ test/widget_test.dart | 30 + 33 files changed, 5880 insertions(+) create mode 100644 analysis_options.yaml create mode 100644 android/app/build.gradle create mode 100644 android/app/src/debug/AndroidManifest.xml create mode 100644 android/app/src/main/AndroidManifest.xml create mode 100644 android/app/src/profile/AndroidManifest.xml create mode 100644 android/build.gradle create mode 100644 android/gradle.properties create mode 100644 android/settings.gradle create mode 100644 assets/cloudlogo.png create mode 100644 assets/nfc.png create mode 100644 assets/nfcdenied.png create mode 100644 gradle.properties create mode 100644 lib/app_worktime/dailyReport.dart create mode 100644 lib/app_worktime/reportNavPage.dart create mode 100644 lib/app_worktime/worktime_details.dart create mode 100644 lib/classes/common_classes.dart create mode 100644 lib/classes/named_route_args.dart create mode 100644 lib/main.dart create mode 100644 lib/main_page.dart create mode 100644 lib/models/employee_model.dart create mode 100644 lib/models/reportListModel.dart create mode 100644 lib/models/workTimeDetailsModel.dart create mode 100644 lib/register_page.dart create mode 100644 lib/report_selectWork.dart create mode 100644 lib/services/device_info.dart create mode 100644 lib/services/storage.dart create mode 100644 lib/widgets/reportCardWidget.dart create mode 100644 lib/widgets/reportCreate.dart create mode 100644 lib/widgets/reportDetails.dart create mode 100644 lib/widgets/reportList.dart create mode 100644 lib/widgets/reportNew.dart create mode 100644 pubspec.yaml create mode 100644 test/widget_test.dart diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..29f2435 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,81 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} +def keystoreProperties = new Properties() +def keystorePropertiesFile = rootProject.file('key.properties') +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) +} +android { + namespace "com.example.mobile_portal_23" + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.mobile_portal_23" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdkVersion flutter.minSdkVersion + multiDexEnabled = true + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + signingConfigs { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } + } + buildTypes { + release { + signingConfig signingConfigs.release + } + } + +} + +flutter { + source '../..' +} + +dependencies { + +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..9a995dd --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..cbc079a --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..b478d78 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,30 @@ +buildscript { + ext.kotlin_version = '1.9.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..598d13f --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..7cd7128 --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,29 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + } + settings.ext.flutterSdkPath = flutterSdkPath() + + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } + + plugins { + id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false +} + +include ":app" diff --git a/assets/cloudlogo.png b/assets/cloudlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..925b1e366eeb9c39290c8716086316fda71a93ba GIT binary patch literal 4550 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 literal 0 HcmV?d00001 diff --git a/assets/nfc.png b/assets/nfc.png new file mode 100644 index 0000000000000000000000000000000000000000..178003dd4cc012ab781f5c20c3aad98855734868 GIT binary patch literal 51304 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%0DHuGObe_D_9AIdm*ee)ffmHRD0_Bt#&uOfgze0g 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 literal 0 HcmV?d00001 diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..24ff78f --- /dev/null +++ b/gradle.properties @@ -0,0 +1,14 @@ +## For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx1024m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +# +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +#Thu Nov 23 15:42:22 CET 2023 +org.gradle.jvmargs=-Xmx1536M -Dkotlin.daemon.jvm.options\="-Xmx1536M" diff --git a/lib/app_worktime/dailyReport.dart b/lib/app_worktime/dailyReport.dart new file mode 100644 index 0000000..c273d0d --- /dev/null +++ b/lib/app_worktime/dailyReport.dart @@ -0,0 +1,468 @@ +import 'package:flutter/material.dart'; +import 'package:dropdown_search/dropdown_search.dart'; +//import 'package:flutter/rendering.dart'; +import 'package:intl/intl.dart'; +import '../models/employee_model.dart'; +//import '../http_service.dart'; +//import '../post_model.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:expandable/expandable.dart'; +//import 'package:shared_preferences/shared_preferences.dart'; +import 'package:mobile_portal_23/classes/common_classes.dart'; + +late EmployeePageArguments employeePageArguments; +String appDomain = "iotechnic.eu"; +//String appDomain = "192.168.0.144"; +GlobalKey _key = GlobalKey(); +List appbarAct = [ + const IconButton( + icon: Icon(Icons.save), + tooltip: 'Open shopping cart', + onPressed: null, + ) +]; + +class DailyReportApp extends StatelessWidget { + final State? homeBodyState = _key.currentState; + + DailyReportApp({super.key}); + + @override + Widget build(BuildContext context) { + return DailyReport(key: key); + /*Scaffold( + appBar: AppBar( + title: Text('Napi Jelentés'), + elevation: 10.0, + leading: Icon(Icons.arrow_back_ios_new), + actions: appbarAct), + body: DailyReport(key: _key), + backgroundColor: Colors.grey[300], + );*/ + } +} + +class DailyReport extends StatefulWidget { + const DailyReport({Key? key}) : super(key: key); + + @override + _DailyReportState createState() => _DailyReportState(); +} + +class EmpList { + String? id; + String? name; + bool? value; +} + +class _DailyReportState extends State { + //final HttpService httpService = HttpService(); + String dropdownValue = 'One'; + DateTime selectedDate = DateTime.now(); + bool datePickerEnabled = false; + bool textMessageEnabled = false; + bool isSwitched = false; + TextEditingController dateinput = TextEditingController(); + List switches = []; + List emplist = []; + //Future> futureEmployee; + List employeeList = []; + List workList = []; + bool _isData = false; + String textMessage = ""; + // String selectedWork = ""; + bool saveEnabled = false; + bool _textMessageOK = false; + WorkLs? selectedWork; // "XST8X8F-6Q9M1FG-PE9948X-SFPHVX9" + String? userApiKey; // = "XST8X8F-6Q9M1FG-PE9948X-SPFHVX9"; + final bool _expanded = false; + final bool _appBarSaveEnabled = false; + var currentFocus; + /*Future getSharedPrefs() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + userApiKey = prefs.getString("apiKey"); + }*/ + + unfocus() { + currentFocus = FocusScope.of(context); + + if (!currentFocus.hasPrimaryFocus) { + currentFocus.unfocus(); + } + } + + /*_loadApiKey() async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + return prefs.getString('apiKey'); + }*/ + + Future fetchdata() async { + print("http://$appDomain/apiGetDailyReport/${employeePageArguments.apiKey!}"); + var res = await http.get( + + // "http://iotechnic.eu/apiemployeelist/X8B0PQS-2KYMCV3-G5WD74N-8G0CRAH"); + + Uri.parse("http://$appDomain/apiGetDailyReport/${employeePageArguments.apiKey!}")); //userApiKey!)); + + //"http://192.168.0.144/apiGetDailyReport/X8B0PQS-2KYMCV3-G5WD74N-8G0CRAH"); + if (res.statusCode == 200) { + var obj = json.decode(res.body); + return obj; + } + } + + _print(String p) { + print(p); + } + + _enableSave() { + setState(() { + appbarAct = [ + IconButton( + icon: const Icon(Icons.save), + tooltip: 'Open shopping cart', + onPressed: saveEnabled ? _print("Save Click") : null), + ]; + }); + } + + _saveForm() { + EasyLoading.show(status: 'Mentés...'); + // _onLoading2(); + debugPrint(DateFormat('yyyy-MM-dd').format(selectedDate)); + debugPrint("Title: ${selectedWork!.title}"); + debugPrint("WorkId: ${selectedWork!.id}"); + debugPrint("Message: $textMessage"); + List emp = []; + //print("Jelenlévők: "); + for (var hobby in employeeList) { + if (hobby.value == true) { + //print(hobby.name); + emp.add(hobby.id); + } + } + var report = { + 'userApiKey': userApiKey, + 'workId': selectedWork?.id, + 'date': DateFormat('yyyy.MM.dd').format(selectedDate), + 'msg': textMessage, + 'employeeList': emp + }; + //print(json.encode(report)); + postDailyReport(report).then((value) { + if (value.contains("OK")) { + EasyLoading.showSuccess('Sikeres Mentés'); + Future.delayed(const Duration(seconds: 3), () { + // Navigator.pop(context); //pop dialog + EasyLoading.dismiss(); + + Navigator.of(context).pop(); + //_login(); + }); + } + }); + } + + Future postDailyReport(var json) async { + final response = await http.post( + Uri.parse("http://$appDomain/apiPostDailyReport/${employeePageArguments.apiKey!}"), + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + }, + body: jsonEncode(json), + ); + + if (response.statusCode == 200) { + // If the server did return a 201 CREATED response, + // then parse the JSON. + return response.body; + } else { + // If the server did not return a 201 CREATED response, + // then throw an exception. + throw Exception('Failed to create album.'); + } + } + + @override + initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + final args = ModalRoute.of(context)!.settings.arguments as Map; + + employeePageArguments = args['userArgs']; + // empArgs = (ModalRoute.of(context)?.settings.arguments ?? + // {}) as EmployeeArguments; + // getSharedPrefs().then((value) { + }); + print("KEY: ${employeePageArguments.apiKey}"); + fetchdata().then((data) { + for (var item in data["employeeList"]) { + //debugPrint(item["_id"]); + employeeList.add(EmployeeLs( + id: item["_id"], name: item["name"], value: false)); + } + for (var item in data["workList"]) { + debugPrint(item["_id"]); + workList.add(WorkLs( + id: item["_id"], + title: item["title"], + body: item["body"], + state: item["state"] ??"", + //value: false + )); + } + _isData = true; + setState(() { + debugPrint('Fetch ok.'); + }); + //employeeList = data; + }); + + //userApiKey = _loadApiKey(); + _enableSave(); + //_isData = false; + dateinput.text = ""; //set the initial value of text field + } + + @override + void dispose() { + // Clean up the controller when the widget is removed from the + // widget tree. + + super.dispose(); + } + + Column myUi() { + saveEnabled = false; + _enableSave(); + //debugPrint("UI Update." + _isData.toString()); + return Column( + children: employeeList.map((hobby) { + if (hobby.value == true) { + saveEnabled = true; + _enableSave(); + // debugPrint(hobby.name); + } + + return SwitchListTile( + //controlAffinity: ListTileControlAffinity.trailing, + activeColor: Colors.green, + value: hobby.value, + title: Text(hobby.name), + onChanged: _textMessageOK == false + ? null + : (newValue) { + setState(() { + hobby.value = newValue; + }); + }); + }).toList()); + } + + DropdownSearch workUi() { + return DropdownSearch( + + //mode: Mode.MENU, + //showSelectedItem: true, + items: workList, + /*.map((e) { + return DropdownMenuItem(child: Text(e.title), value: e); + }).toList(),*/ + //showSearchBox: true, + //;["Brazil", "Italia (Disabled)", "Tunisia", 'Canada'], + //label: "Munka kiválasztása", + // hint: "country in menu mode", + //popupItemDisabled: (String s) => s.startsWith('I'), + onChanged: (data) { + //_dateSelected(); + selectedWork = data; + + //_selectDate(context); + setState(() { + datePickerEnabled = true; + }); + }); + } + + @override + Widget build(BuildContext context) => GestureDetector( + onTap: () { + //_enableSave(); + FocusScope.of(context).unfocus(); + }, + child: Scaffold( + appBar: AppBar( + title: const Text('Napi Jelentés'), + elevation: 10.0, + leading: const Icon(Icons.arrow_back_ios_new), + actions: [ + IconButton( + icon: const Icon(Icons.save), + tooltip: 'Open shopping cart', + onPressed: saveEnabled ? _print("Save Click") : null), + ]), + //body: DailyReport(key: _key), + backgroundColor: Colors.grey[300], + body: SingleChildScrollView( + keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag, + //itemExtent: 100, + // shrinkWrap: true, + child: + //saveBegin == true + // ? _onLoading() + //: _isData == false + + _isData == false + ? SizedBox( + height: MediaQuery.of(context).size.height / 1.3, + width: MediaQuery.of(context).size.width, + child: const Center( + child: CircularProgressIndicator(), + ), + ) + : Column( + //diameterRatio: 10, + children: [ + Container( + padding: const EdgeInsets.all(15), + height: 100, + child: workUi(), + ), + Container( + padding: const EdgeInsets.only(left: 10, right: 10), + child: Center( + child: TextField( + enabled: datePickerEnabled, + controller: + dateinput, //editing controller of this TextField + decoration: const InputDecoration( + icon: Icon(Icons + .calendar_today), //icon of text field + labelText: + "Jelentés Dátuma" //label text of field + ), + readOnly: + true, //set it true, so that user will not able to edit text + onTap: () async { + DateTime? pickedDate = await showDatePicker( + context: context, + initialDate: DateTime.now(), + firstDate: DateTime( + 2000), //DateTime.now() - not to allow to choose before today. + lastDate: DateTime.now()); + + if (pickedDate != null) { + print( + pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000 + String formattedDate = + DateFormat('yyyy.MM.dd') + .format(pickedDate); + print( + formattedDate); //formatted date output using intl package => 2021-03-16 + //you can implement different kind of Date Format here according to your requirement + + setState(() { + textMessageEnabled = true; + selectedDate = pickedDate; + + dateinput.text = + formattedDate; //set output date to TextField value. + }); + } else { + print("Nem választott dátumot!"); + } + }, + )), + ), + Container( + padding: const EdgeInsets.only( + top: 20, left: 15, right: 15), + //margin: const EdgeInsets.only(left: 5, right: 5), + height: 100, + //child: Padding( + //padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16), + + child: TextField( + enabled: textMessageEnabled, + //keyboardType: TextInputType.multiline, + maxLines: null, + onSubmitted: (value) => { + textMessage = value, + (value.length > 3) == true + ? _textMessageOK = true + : _textMessageOK = false, + }, + + //expands: true, + textInputAction: TextInputAction.done, + decoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'Munkafolyamat leírása', + ), + /* onChanged: (value) => { + textMessage = value, + (value.length > 3) == true + ? _textMessageOK = true + : _textMessageOK = false, + //myUi.call() + }*/ + )), + //), + // The checkboxes will be here + Container( + padding: const EdgeInsets.only(left: 10, right: 10), + child: Card( + //elevation: 2, + child: ExpandablePanel( + header: Container( + height: 35, + padding: const EdgeInsets.all(5), + color: Colors.indigo, + child: const Text( + "Jelenlévők kiválasztása", + style: TextStyle( + fontSize: 18, + color: Colors.white, + fontWeight: FontWeight.bold), + ), + ), + collapsed: + const Text("Érintse a lenyitáshoz."), + expanded: AbsorbPointer( + absorbing: !_textMessageOK, + child: myUi(), + ) + // tapHeaderToExpand: true, + //hasIcon: true, + )) + /* Column(children: [ + ListTile( + title: const Text( + "Jelenlévők", + style: TextStyle(fontWeight: FontWeight.bold), + ), + leading: Icon(Icons.arrow_drop_down_circle), + //onTap: , + ), + _isData == false + ? Center(child: new CircularProgressIndicator()) + : myUi(), + ]),*/ + ), + + Container( + padding: const EdgeInsets.all(20), + child: Center( + child: ElevatedButton.icon( + onPressed: saveEnabled ? _saveForm : null, + icon: const Icon(Icons.save), + label: const Text('Save')), + // something like 2013-04-20 + //Text(DateFormat('yyyy.MM.dd').format(selectedDate)) + ), + ), + ], + )))); +} diff --git a/lib/app_worktime/reportNavPage.dart b/lib/app_worktime/reportNavPage.dart new file mode 100644 index 0000000..1872af8 --- /dev/null +++ b/lib/app_worktime/reportNavPage.dart @@ -0,0 +1,207 @@ +import 'package:flutter/material.dart'; +import 'package:mobile_portal_23/classes/common_classes.dart'; +//import 'package:shared_preferences/shared_preferences.dart'; + +//late EmployeeArguments empArgs; +String? userApiKey; // = "XST8X8F-6Q9M1FG-PE9948X-SPFHVX9"; +bool _error = false; +late EmployeePageArguments employeePageArguments; + +class reportNav extends StatefulWidget { + const reportNav({Key? key}) : super(key: key); + + @override + State createState() => _reportNavState(); +} + +/* +Future fetchData(String userApiKey) async { + var url = "http://" + appDomain + "/apiemployee/" + userApiKey; + http.Response response = await http.get(Uri.parse(url)); + return response; + + //return resp.map((m) => EmployeeLs.fromJson(m)).toList(); +} +*/ +Future _showMyDialog(BuildContext cont) async { + return showDialog( + context: cont, + barrierDismissible: false, // user must tap button! + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Mobile Portal'), + content: const Text("Nem található érvényes api Key."), + actions: [ + TextButton( + child: const Text('Bezárás'), + onPressed: () { + Navigator.pop(cont); + }, + ), + ], + ); + }, + ); +} + +class _reportNavState extends State { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + final args = ModalRoute.of(context)!.settings.arguments as Map; + + employeePageArguments = args['userArgs']; + // empArgs = (ModalRoute.of(context)?.settings.arguments ?? + // {}) as EmployeeArguments; + // getSharedPrefs().then((value) { + }); + //print("KEY: ${employeePageArguments.apiKey}"); + } + + @override + void dispose() { + // TODO: implement dispose + super.dispose(); + } + + /*Future getSharedPrefs() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + userApiKey = prefs.getString("apiKey"); + + if (userApiKey == null) { + _error = true; + WidgetsBinding.instance! + .addPostFrameCallback((timeStamp) => _showMyDialog(context)); + } + if (userApiKey!.length != 31) { + _error = true; + WidgetsBinding.instance! + .addPostFrameCallback((timeStamp) => _showMyDialog(context)); + } + }*/ + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Jelentések"), + /* actions: [ + IconButton( + icon: Icon(Icons.search), + onPressed: () {}, + ), + IconButton( + icon: Icon( + Icons.more_vert, + ), + onPressed: () {}, + ) + ],*/ + actionsIconTheme: const IconThemeData( + size: 32, + ), + ), + //drawer: Drawer(), + body: _error ? Container() : page(context)); + } +} + +Container page(BuildContext context) { + final args = ModalRoute.of(context)!.settings.arguments as Map; + + EmployeePageArguments employeePageArguments = args['userArgs']; + if (employeePageArguments.apiKey == null) { + _showMyDialog(context); + } + + return Container( + child: GridView.count( + crossAxisSpacing: 10, + mainAxisSpacing: 10, + crossAxisCount: 2, + shrinkWrap: true, + padding: const EdgeInsets.only(top: 24, left: 24, right: 24), + children: [ + /* MaterialButton( + onPressed: () { + Navigator.pushNamed(context, '/newDailyReport'); + }, + color: Colors.blue, + textColor: Colors.white, + child: Column( + children: [ + Icon( + Icons.add, + size: 48, + ), + Text( + "Új jelentés", + style: TextStyle( + fontSize: 18, + color: Colors.white, + fontWeight: FontWeight.bold), + ) + ], + ), + padding: EdgeInsets.only(top: 32.0), + //shape: RoundedRectangleBorder(side:,borderRadius: Radius())), // CircleBorder(), + ),*/ + MaterialButton( + onPressed: () { + Navigator.pushNamed(context, '/reportList', arguments: { + 'userArgs': employeePageArguments, + 'dataArgs': null + }); + }, + color: Colors.blue, + textColor: Colors.white, + padding: const EdgeInsets.only(top: 32.0), + child: const Column( + children: [ + Icon( + Icons.list, + size: 48, + ), + Text( + "Jelentések", + style: TextStyle( + fontSize: 18, + color: Colors.white, + fontWeight: FontWeight.bold), + ) + ], + ), + //shape: RoundedRectangleBorder(side:,borderRadius: Radius())), // CircleBorder(), + ), + MaterialButton( + onPressed: () { + Navigator.pushNamed(context, '/reportCreate', arguments: { + 'userArgs': employeePageArguments, + 'dataArgs': null + }); + }, + color: Colors.blue, + textColor: Colors.white, + padding: const EdgeInsets.only(top: 32.0), + child: const Column( + children: [ + Icon( + Icons.add, + size: 48, + ), + Text( + "Új jelentés", + style: TextStyle( + fontSize: 18, + color: Colors.white, + fontWeight: FontWeight.bold), + ) + ], + ), + //shape: RoundedRectangleBorder(side:,borderRadius: Radius())), // CircleBorder(), + ), + ], + ), + ); +} diff --git a/lib/app_worktime/worktime_details.dart b/lib/app_worktime/worktime_details.dart new file mode 100644 index 0000000..764b5ab --- /dev/null +++ b/lib/app_worktime/worktime_details.dart @@ -0,0 +1,765 @@ +import 'package:expandable/expandable.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:intl/date_symbol_data_local.dart'; +import 'dart:convert'; +import 'package:mobile_portal_23/models/workTimeDetailsModel.dart'; +import 'package:mobile_portal_23/classes/common_classes.dart'; +import 'package:http/http.dart' as http; +import 'package:jiffy/jiffy.dart'; + +extension on Duration { + String format(String s) => '$this'.split('.')[0].padLeft(8, '0'); +} + +class workTimeDetails extends StatefulWidget { + const workTimeDetails({Key? key}) : super(key: key); + static const routeName = '/wtdetails'; + @override + State createState() => _workTimeDetailsState(); +} + +late EmployeePageArguments employeePageArguments; +late WorkTimeDetailsModel workTimeList; +bool _isError = false; +bool _isData = false; + +final DateFormat nowformat = DateFormat('yyyy.MM'); + +class _workTimeDetailsState extends State { + final DateFormat formatter = DateFormat('yyyy MMMM'); + DateTime selectedDate = DateTime.now(); + + String appDomain = "iotechnic.eu"; + @override + void initState() { + super.initState(); + initializeDateFormatting('hu'); + + WidgetsBinding.instance.addPostFrameCallback((_) { + final args = ModalRoute.of(context)!.settings.arguments as Map; + + employeePageArguments = args['userArgs']; + // empArgs = (ModalRoute.of(context)?.settings.arguments ?? + // {}) as EmployeeArguments; + // getSharedPrefs().then((value) { + reloadData(nowformat.format(selectedDate)); + }); + // }); + } + + reloadData(String date) { + fetchdata(date).then((data) { + if (data != null) { + // for (var item in data) { + workTimeList = + WorkTimeDetailsModel.fromJson(data); //{'wd': data['wd']})); + // } + + setState(() { + _isData = true; + debugPrint('Fetch ok.'); + }); + } else { + _isError = true; + } + //employeeList = data; + }); + } + + Future fetchdata(String date) async { + var res = await http.get( + + // "http://iotechnic.eu/apiGetAllReport/X8B0PQS-2KYMCV3-G5WD74N-8G0CRAH"); + Uri.parse("http://$appDomain/employee/androidAccessList/${employeePageArguments.apiKey!}/$date")); + //"http://192.168.0.144/apiGetDailyReport/X8B0PQS-2KYMCV3-G5WD74N-8G0CRAH"); + if (res.statusCode == 200) { + var obj = json.decode(res.body); + return obj; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: _isData ? Text(formatter.format(selectedDate)) : const Text("ss"), + elevation: 10.0, + actions: [ + IconButton( + icon: const Icon(Icons.navigate_before), + tooltip: 'Show Snackbar', + onPressed: () { + selectedDate = Jiffy.parse(selectedDate.toString()).subtract(months: 1).dateTime; + reloadData(nowformat.format(selectedDate)); + }), + IconButton( + icon: const Icon(Icons.navigate_next_rounded), + color: Colors.white, + tooltip: 'Go to the next page', + onPressed: selectedDate.difference(DateTime.now()).inDays < 0 + ? () { + _isData = false; + _isError = false; + selectedDate = Jiffy.parse(selectedDate.toString()).add(months: 1).dateTime; + // String jiffy=selectedDate. + reloadData(nowformat.format(selectedDate)); + } + : null, + ), + ], + // leading: Icon(Icons.arrow_back_ios_new), + ), + body: _isData + ? detailsPage( + context, workTimeList) //reportCard(context, workTimeList) + : const Center(child: CircularProgressIndicator()), + backgroundColor: Colors.grey[300], + ); + } + + String minToTime(var seconds) { + int minutes = (seconds / 60).truncate(); + int sec = (seconds % 60); + String minutesStr = "${minutes.toString().padLeft(2, "0")}.${sec.toString().padLeft(2, "0")}"; + return minutesStr; + } + + /* Widget _buildContent(BuildContext context) { + return Container( + color: Color(0xffECF0F1), + child: ListView.builder( + itemCount: workTimeList + .length, //workTimeList.wd?.length, // The length Of the array + + padding: EdgeInsets.all(5), + shrinkWrap: true, + + itemBuilder: (context, index) => Container( + child: //if (_isData) + reportCard(context, workTimeList, index)), + )); + }*/ + Widget Arrive(e, Wd wtd) { + var dateformat = DateFormat.E('hu'); + final DateFormat formatter = DateFormat('HH:mm'); + return //Row(crossAxisAlignment: CrossAxisAlignment.center, children: [ + + /* Expanded( + // flex: 1, + child: Text( + wtd.date!.substring(8, 11).toString(), + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87), + )), + Expanded( + //flex: 1, + child: Text( + //"$item.month.fullWorkHours", + wtd.isHoliday == true + ? dateformat + .format(DateFormat("yyyy.MM.DD").parse(wtd.date.toString())) + : dateformat + .format(DateFormat("yyyy.MM.DD.").parse(wtd.date.toString())), + //item.month?.fullWorkHours.toString(), + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.bold, color: Colors.blue), + )),*/ + Expanded( + flex: 8, + child: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text( + 'É: ${formatter.format(DateFormat("H:mm") + .parse(e.start.substring(12, e.start.length)))} ' + + e.arriveLoc, + overflow: TextOverflow.ellipsis), + if (e.stop == '-') + const Text('--:--') + else + Text( + 'T: ${formatter.format(DateFormat("H:mm") + .parse(e.stop.substring(12, e.stop.length)))} ' + + e.getawayLoc, + overflow: TextOverflow.ellipsis), + ])); + } + + Container startStopWidget(BuildContext context, Wd wtd) { + var dateformat = DateFormat.E('hu'); + + final DateFormat formatter = DateFormat('HH:mm'); + List v = []; + v.add(Expanded( + // flex: 1, + child: Text( + wtd.date!.substring(8, 10).toString(), + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87), + ))); + v.add( + Expanded( + //flex: 1, + child: Text( + //"$item.month.fullWorkHours", + wtd.isHoliday == true + ? dateformat + .format(DateFormat("yyyy.MM.DD").parse(wtd.date.toString())) + : dateformat + .format(DateFormat("yyyy.MM.DD.").parse(wtd.date.toString())), + //item.month?.fullWorkHours.toString(), + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.bold, color: Colors.blue), + )), + ); + if (wtd.isHoliday == false) { + for (var e in wtd.startStop!) { + v.add(Expanded( + flex: 8, + child: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text( + 'É: ${formatter.format(DateFormat("yyyy.MM.dd. H:mm") + .parse(e.start.toString()))} ${e.arriveLoc}', + overflow: TextOverflow.ellipsis), + if (e.stop == '-') + const Text('--:--') + else + Text( + 'T: ${formatter.format(DateFormat("yyyy.MM.dd. H:mm") + .parse(e.stop.toString()))} ${e.getawayLoc}', + overflow: TextOverflow.ellipsis), + ]))); + debugPrint(wtd.toString()); + } + } + return Container( + color: const Color.fromARGB(255, 206, 206, 206), + margin: const EdgeInsets.all(10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: wtd.isHoliday == true + ? [ + Text(wtd.date.toString()), + const Text("Szabadság"), + const Divider( + thickness: 1.0, + ), + ] + : //wtd.startStop!.map((e) => Arrive(e, wtd)).toList()), + [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: + v /*[ + Expanded( + // flex: 1, + child: Text( + wtd.date!.substring(8, 11).toString(), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.black87), + ) + ), + Expanded( + //flex: 1, + child: Text( + //"$item.month.fullWorkHours", + wtd.isHoliday == true + ? dateformat.format(DateFormat("yyyy.MM.DD") + .parse(wtd.date.toString())) + : dateformat.format(DateFormat("yyyy.MM.DD.") + .parse(wtd.date.toString())), + //item.month?.fullWorkHours.toString(), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.blue), + ) + ), + + wtd.startStop!.map((e) => Arrive(e, wtd)).toList()), + ]*/ + ) + ])); + } + + Container detailsPage(BuildContext context, WorkTimeDetailsModel item) { + return Container( + child: ListView.builder( + itemCount: item.wd!.length, + itemBuilder: (BuildContext context, int index) { + _isData = false; + return //startStopWidget(context, item.wd![index]); + //reportCard2(context, item.wd![index]); + buildCard(context, item.wd![index]); + }, + )); + } + + Widget buildCard(BuildContext context, Wd itemx) { + var dayformat = DateFormat.d('hu'); + var dateformat = DateFormat.E('hu'); + final DateFormat formatter = DateFormat('HH:mm'); + return Padding( + padding: const EdgeInsets.all(10), + child: Card( + child: Padding( + padding: const EdgeInsets.all(10), + child: ExpandablePanel( + header: Column(children: [ + Row( + children: [ + Text( + itemx.isHoliday == true + ? '${dayformat.format(DateFormat("yyyy.MM.dd") + .parse(itemx.date.toString()))}.' + : '${dayformat.format(DateFormat("yyyy.MM.dd.") + .parse(itemx.date.toString()))}.', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.black), + ), + const SizedBox( + width: 10, + ), + Text( + //"$item.month.fullWorkHours", + itemx.isHoliday == true + ? dateformat.format(DateFormat("yyyy.MM.dd") + .parse(itemx.date.toString())) + : dateformat.format(DateFormat("yyyy.MM.dd.") + .parse(itemx.date.toString())), + //item.month?.fullWorkHours.toString(), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.blue), + ), + ], + ), + const Divider( + color: Colors.blue, + thickness: 1, + ) + ]), + //Text("Header"), + + collapsed: //Text('collapsed'), + itemx.isHoliday == true + ? const Text("Szabadság") + : Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + //Text(itemx.fullHours.toString()), + + Text('Normál óra'), + Text('Túlóra óra'), + Text('Összes óra'), + ]), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + children: [ + Text( + formatter.format(DateFormat("mm") + .parse(itemx.hours.toString())), + textAlign: TextAlign.center), + Text( + formatter.format(DateFormat("mm") + .parse(itemx.overTime.toString())), + textAlign: TextAlign.center), + Text( + formatter.format(DateFormat("mm") + .parse(itemx.fullHours.toString())), + textAlign: TextAlign.center), + ]), + ]), + expanded: itemx.isHoliday == true + ? const Text("Szabadság") + : Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + //Text(itemx.fullHours.toString()), + + Text('Érkezés'), + Text('Távozás'), + Text('Óra'), + ]), + Column( + children: itemx.startStop! + .map((e) => _getrow(e)) + .toList()) + ]), + )), + ), + ); + } + + Widget _getrow(StartStop e) { + final DateFormat formatter = DateFormat('HH:mm'); + var start = DateFormat("yyyy.MM.dd. HH:mm").parse(e.start.toString()); + DateTime stop; + late Duration diff; + if (e.stop != '-') { + stop = DateFormat("yyyy.MM.dd. HH:mm").parse(e.stop.toString()); + diff = stop.difference(start); + } + return Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ + Text(formatter.format(start), textAlign: TextAlign.center), + if (e.stop == '-') ...[ + const Text('--:--'), + const Text('--:--') + ] else ...[ + Text(formatter.format(stop), textAlign: TextAlign.center), + Text( + formatter.format(DateFormat("mm").parse(diff.inMinutes.toString())), + textAlign: TextAlign.center), + ] + ]); + } + + Card reportCard2(BuildContext context, Wd itemx) { + var dateformat = DateFormat.E('hu'); + final DateFormat formatter = DateFormat('HH:mm'); + return Card( + child: Padding( + padding: const EdgeInsets.all(15.0), + child: + Column(mainAxisAlignment: MainAxisAlignment.center, children: [ + Expanded( + flex: 1, + child: Text( + //"$item.month.fullWorkHours", + itemx.isHoliday == true + ? dateformat.format(DateFormat("yyyy.MM.dd") + .parse(itemx.date.toString())) + : dateformat.format(DateFormat("yyyy.MM.dd.") + .parse(itemx.date.toString())), + //item.month?.fullWorkHours.toString(), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.blue), + )), + const Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + //Text(itemx.fullHours.toString()), + + Text('Normál óra'), + Text('Túlóra óra'), + Text('Összes óra'), + ]), + Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ + Text( + formatter + .format(DateFormat("mm").parse(itemx.hours.toString())), + textAlign: TextAlign.center), + Text( + formatter.format( + DateFormat("mm").parse(itemx.overTime.toString())), + textAlign: TextAlign.center), + Text( + formatter.format( + DateFormat("mm").parse(itemx.fullHours.toString())), + textAlign: TextAlign.center), + ]), + ]))); + } + + Card reportCard(BuildContext context, WorkTimeDetailsModel itemx) { + int workHour = 0; + bool workOpened = false; + //bool? isHoliday = item.wd![index].isHoliday; + var dateformat = DateFormat.E('hu'); + return Card( + clipBehavior: Clip.antiAlias, + child: ListView.builder( + itemCount: itemx.wd?.length, + itemBuilder: (BuildContext context, int index) { + List start = itemx.wd![index].isHoliday != true + ? itemx.wd![index].startStop! + .map( + (e) => Text( + e.start.toString(), + //item.dailyReport[0].workTitle!, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ) + //.cast() + .toList() + : [const Text("Szabi")]; + bool? isHoliday = itemx.wd![index].isHoliday; + return ListTile( + leading: CircleAvatar( + backgroundColor: Colors.blue, + child: Text( + //"$item.month.fullWorkHours", + isHoliday == true + ? dateformat.format(DateFormat("yyyy.MM.DD") + .parse(itemx.wd![index].date.toString())) + : dateformat.format(DateFormat("yyyy.MM.DD.") + .parse(itemx.wd![index].date.toString())), + //item.month?.fullWorkHours.toString(), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white), + ), + ), + title: isHoliday == false + ? Column(children: [ + Row( + children: + start /*itemx.wd![index].startStop! + .map( + (e) => Text( + e.start.toString(), + //item.dailyReport[0].workTitle!, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ) + .toList(),*/ + ), + const Divider( + thickness: 1.0, + ), + ]) + : const Text("Szabi")); + //Text(itemx.wd![index].date.toString()); + }, + ), + /*Column( + children: [ + ListTile( + tileColor: Colors.blue, + onTap: () {}, + leading: CircleAvatar( + backgroundColor: Colors.white, + child: Text( + //"$item.month.fullWorkHours", + isHoliday == true + ? dateformat.format(DateFormat("yyyy.MM.DD") + .parse(item.wd![index].date.toString())) + : dateformat.format(DateFormat("yyyy.MM.DD.") + .parse(item.wd![index].date.toString())), + //item.month?.fullWorkHours.toString(), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.blue), + )), + title: Text( + item.wd?[index].date as String, + style: TextStyle(color: Colors.white), + ), + subtitle: isHoliday == true + ? Text( + "Szabadság", + style: TextStyle( + color: Colors.yellow, + fontWeight: FontWeight.bold, + fontSize: 18), + ) + : item.wd![index].notes != null + ? Text( + item.wd![index].notes as String, + style: + TextStyle(color: Colors.black.withOpacity(0.6)), + ) + : Text("Nincs")), + Padding( + padding: const EdgeInsets.all(10.0), + child: IntrinsicHeight( + child: Column(children: [ + Row(children: [ + Text( + 'KL', //+ item.workNumber!, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + VerticalDivider(color: Colors.grey, thickness: 1, width: 30), + Text( + 'Órák: ' + + Duration(minutes: item.month!.fullWorkHours!.toInt()) + .format(), + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + VerticalDivider(color: Colors.grey, thickness: 1, width: 30), + Text( + 'PO:', // + item.poNumber!, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ]), + Divider( + thickness: 1.0, + ), + Row(children: [ + Text( + item.wd![index].date.toString(), + //item.dailyReport[0].workTitle!, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ] + /* .map( + (e) => Text( + e.date.toString(), + //item.dailyReport[0].workTitle!, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ) + .toList(),*/ + ), + Divider( + thickness: 1.0, + ), + ]))), + ], + ),*/ + ); + } + +/* + Widget _buildContent(BuildContext context) { + return Container( + color: Color(0xffECF0F1), + child: ListView.builder( + itemCount: workTimeList.wd?.length, // The length Of the array + + padding: EdgeInsets.all(5), + shrinkWrap: true, + + itemBuilder: (context, index) => Container( + child: //if (_isData) + reportCard(context, workTimeList, index)), + )); + } +*/ + +/* Card reportCard(BuildContext context, WorkTimeDetailsModel item, int index) { + int workHour = 0; + bool workOpened = false; + bool? isHoliday = item.wd![index].isHoliday; + var dateformat = new DateFormat.E('hu'); + return Card( + clipBehavior: Clip.antiAlias, + child: Column( + children: [ + ListTile( + tileColor: Colors.blue, + onTap: () {}, + leading: CircleAvatar( + backgroundColor: Colors.white, + child: Text( + //"$item.month.fullWorkHours", + isHoliday == true + ? dateformat.format(DateFormat("yyyy.MM.DD") + .parse(item.wd![index].date.toString())) + : dateformat.format(DateFormat("yyyy.MM.DD.") + .parse(item.wd![index].date.toString())), + //item.month?.fullWorkHours.toString(), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.blue), + )), + title: Text( + item.wd?[index].date as String, + style: TextStyle(color: Colors.white), + ), + subtitle: isHoliday == true + ? Text( + "Szabadság", + style: TextStyle( + color: Colors.yellow, + fontWeight: FontWeight.bold, + fontSize: 18), + ) + : item.wd![index].notes != null + ? Text( + item.wd![index].notes as String, + style: + TextStyle(color: Colors.black.withOpacity(0.6)), + ) + : Text("Nincs")), + Padding( + padding: const EdgeInsets.all(10.0), + child: IntrinsicHeight( + child: Column(children: [ + Row(children: [ + Text( + 'KL', //+ item.workNumber!, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + VerticalDivider(color: Colors.grey, thickness: 1, width: 30), + Text( + 'Órák: ' + + Duration(minutes: item.month!.fullWorkHours!.toInt()) + .format(), + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + VerticalDivider(color: Colors.grey, thickness: 1, width: 30), + Text( + 'PO:', // + item.poNumber!, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ]), + Divider( + thickness: 1.0, + ), + Row(children: [ + Text( + item.wd![index].date.toString(), + //item.dailyReport[0].workTitle!, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ] + /* .map( + (e) => Text( + e.date.toString(), + //item.dailyReport[0].workTitle!, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ) + .toList(),*/ + ), + Divider( + thickness: 1.0, + ), + ]))), + ], + ), + ); + }*/ +} diff --git a/lib/classes/common_classes.dart b/lib/classes/common_classes.dart new file mode 100644 index 0000000..8804794 --- /dev/null +++ b/lib/classes/common_classes.dart @@ -0,0 +1,105 @@ +import 'package:mobile_portal_23/models/reportListModel.dart'; +class EmployeePageArguments { + final String? name; + final String? id; + final String? apiKey; + final bool? isForeman; + final bool? mobilePortalAccessEnabled; + EmployeePageArguments( + {this.name, + this.id, + this.apiKey, + this.isForeman, + this.mobilePortalAccessEnabled}); + +/*EmployeePageArguments.fromJson(Map json) { + name = json['name']; + id = json['id']; + apiKey = json['apiKey']; + isForeman = json['isForeman']; + mobilePortalAccessEnabled = json["mobilePortalAccessEnabled"]; + }*/ +} + +class QRCodeModel { + late final String? name; + late final String? id; + late final String? qrCode; + + QRCodeModel( + {this.name, + this.id, + this.qrCode + }); + + QRCodeModel.fromJson(Map json) { + name = json['name']; + id = json['id']; + qrCode = json['qrCode']; + + } + Map toJson() { + final Map data = {}; + data['name'] = name; + data['id'] = id; + data['qrCode'] = qrCode; + return data; + } +} + +class WorkStateModel { + late final String? workState; + + WorkStateModel( + {this.workState, + + }); + + WorkStateModel.fromJson(Map json) { + workState = json['workState']; + + + } + +} + +class ReportArguments { + final List report; + //final String userApiKey; + final String workName; + final String workId; + final bool state; + //final bool isForeman; + ReportArguments( + this.report, //this.userApiKey, + this.workName, + this.workId, + this.state, + //this.isForeman + ); + +} +class EmployeeArguments { + String? name; + String? id; + bool? isForeman; + bool? mobilePortalAccessEnabled; + EmployeeArguments( + {this.name, this.id, this.isForeman, this.mobilePortalAccessEnabled}); + + EmployeeArguments.fromJson(Map json) { + name = json['name']; + id = json['id']; + isForeman = json['isForeman']; + mobilePortalAccessEnabled = json["mobilePortalAccessEnabled"]; + } + + Map toJson() { + final Map data = {}; + data['name'] = name; + data['id'] = id; + data['isForeman'] = isForeman; + data['mobilePortalAccessEnabled'] = mobilePortalAccessEnabled; + return data; + } +} \ No newline at end of file diff --git a/lib/classes/named_route_args.dart b/lib/classes/named_route_args.dart new file mode 100644 index 0000000..2805ee2 --- /dev/null +++ b/lib/classes/named_route_args.dart @@ -0,0 +1,7 @@ +class DeviceArguments { + final String Id; + final String Name; + final String Brand; + + DeviceArguments(this.Id, this.Name, this.Brand); +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..30b0b95 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,154 @@ +import 'package:flutter/material.dart'; +import 'main_page.dart' as main_page; +import 'register_page.dart' as register_page; +import 'app_worktime/worktime_details.dart' as worktime_page; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'app_worktime/reportNavPage.dart' as dailyReportNav_page; +import 'package:mobile_portal_23/widgets/reportList.dart' as reportList_page; +import 'package:mobile_portal_23/widgets/reportNew.dart' as reportNew_page; +import 'package:mobile_portal_23/widgets/reportDetails.dart' as reportDetails_page; +import 'package:mobile_portal_23/report_selectWork.dart' as reportSelectWork_page; +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Mobile Portal', + theme: ThemeData( + // This is the theme of your application. + // + // TRY THIS: Try running your application with "flutter run". You'll see + // the application has a purple toolbar. Then, without quitting the app, + // try changing the seedColor in the colorScheme below to Colors.green + // and then invoke "hot reload" (save your changes or press the "hot + // reload" button in a Flutter-supported IDE, or press "r" if you used + // the command line to start the app). + // + // Notice that the counter didn't reset back to zero; the application + // state is not lost during the reload. To reset the state, use hot + // restart instead. + // + // This works for code too, not just values: Most code changes can be + // tested with just a hot reload. + colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), + useMaterial3: true, + ), + //home: const MyHomePage(title: 'Munkaidő rögzítő'), + routes: { + //'/':(context)=>const MyHomePage(title: 'Munkaidő rögzítő'), + //'/register':(context)=>const register_page.RegisterPage(), + register_page.RegisterPage.routeName:(context)=>const register_page.RegisterPage(), + '/':(context)=>const main_page.MainPage(), + '/wtdetails': (context) => const worktime_page.workTimeDetails(), + '/reportNav': (context)=> const dailyReportNav_page.reportNav(), + //'/reportCreate': (context)=> testRepCreate_page.ReportCreate(),//reportSelectWork_page.reportCreate(), + '/reportCreate': (context)=> const reportSelectWork_page.reportCreate(), + '/reportList':(context)=> const reportList_page.dailyReportList(), + '/reportNew': (context)=>const reportNew_page.reportNew(), + '/reportDetails': (context)=>const reportDetails_page.reportDetails(), + }, + builder: EasyLoading.init(), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + int _counter = 0; + + void _incrementCounter() { + setState(() { + // This call to setState tells the Flutter framework that something has + // changed in this State, which causes it to rerun the build method below + // so that the display can reflect the updated values. If we changed + // _counter without calling setState(), then the build method would not be + // called again, and so nothing would appear to happen. + _counter++; + }); + } + + @override + Widget build(BuildContext context) { + // This method is rerun every time setState is called, for instance as done + // by the _incrementCounter method above. + // + // The Flutter framework has been optimized to make rerunning build methods + // fast, so that you can just rebuild anything that needs updating rather + // than having to individually change instances of widgets. + return Scaffold( + appBar: AppBar( + // TRY THIS: Try changing the color here to a specific color (to + // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar + // change color while the other colors stay the same. + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text(widget.title), + ), + body: Center( + // Center is a layout widget. It takes a single child and positions it + // in the middle of the parent. + child: Column( + // Column is also a layout widget. It takes a list of children and + // arranges them vertically. By default, it sizes itself to fit its + // children horizontally, and tries to be as tall as its parent. + // + // Column has various properties to control how it sizes itself and + // how it positions its children. Here we use mainAxisAlignment to + // center the children vertically; the main axis here is the vertical + // axis because Columns are vertical (the cross axis would be + // horizontal). + // + // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" + // action in the IDE, or press "p" in the console), to see the + // wireframe for each widget. + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + style: ButtonStyle( + foregroundColor: MaterialStateProperty.all(Colors.blue), + ), + onPressed: () { Navigator.pushNamed(context, '/main');}, + child: const Text('TextButton') + ), + const Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.headlineMedium, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: const Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} diff --git a/lib/main_page.dart b/lib/main_page.dart new file mode 100644 index 0000000..3631a1a --- /dev/null +++ b/lib/main_page.dart @@ -0,0 +1,999 @@ +//import 'dart:ffi'; + +import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; +import 'dart:async'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'package:flutter/services.dart'; +import 'package:nfc_manager/platform_tags.dart'; +import 'services/storage.dart' as storage; +import 'services/device_info.dart' as device_info; +import 'classes/named_route_args.dart'; +import 'package:location/location.dart'; +import 'common_widgets/custom_icon_button.dart'; +import 'classes/common_classes.dart'; +import 'package:nfc_manager/nfc_manager.dart'; +import "package:hex/hex.dart"; +import 'package:geolocator/geolocator.dart' as geolocator; +import 'package:geocoding/geocoding.dart' as geocoding; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:simple_barcode_scanner/simple_barcode_scanner.dart'; +import 'dart:io'; +import 'package:qr_flutter/qr_flutter.dart'; +class MainPage extends StatefulWidget { + const MainPage({Key? key}) : super(key: key); + + @override + State createState() => _MainPageState(); +} + +class _MainPageState extends State with TickerProviderStateMixin{ + String appDomain = "iotechnic.eu"; + + bool _showNFClogo = false; + bool _reportButtonDisabled = true; + final bool _nfcOk = false; + final bool _deviceRegistered = false; + bool _initOK = false; + bool _isApiKeyValid = false; + bool _isScanVisible = false; + bool _isLocationMocked=false; + bool _isLocationValid=false; + geolocator.Position? _currentPosition; + late LocationData _currentLocation; + String _currentAddress = ""; + String result = ""; + String accessName = ""; + String accessMsg = ""; + String accessWorktime = ""; + String accessDateTime = ""; + String accessLocation = ""; + String appVersion = ""; + String appCode = ""; + String qrdata=""; + String currentWorkState=""; + QRCodeModel currentQRcode=QRCodeModel(); + WorkStateModel workState=WorkStateModel(); + late EmployeeArguments ea; + EmployeePageArguments employeePageArgs = EmployeePageArguments(); + device_info.DeviceInfo di=device_info.DeviceInfo(); + var deviceData = {}; + + Future ?apiKey ; + bool apiKeyValid=false; + Map _deviceData = {}; +// Location + final Location location = Location(); + bool _loading = false; + LocationData? _location; + String? _error; + + late final AnimationController _controller = AnimationController( + duration: const Duration(seconds: 1), + vsync: this, + lowerBound: 0.5, + upperBound: 1.0, + value: 1, + )..repeat(reverse: true); + late final Animation _animation = CurvedAnimation( + parent: _controller, + curve: Curves.easeInOutCirc, + ); + +// Aktuális QR kód lekérése szervertől + Future getQRCode(String userApiKey) async { + if (userApiKey.isEmpty) return false; + var url = "http://$appDomain/apiGetQRCode/$userApiKey"; + http.Response response = await http.get(Uri.parse(url)); + if (response.statusCode == 200) { + var resp = jsonDecode(response.body); + currentQRcode = QRCodeModel.fromJson(resp); + setState(() { + qrdata=currentQRcode.qrCode!; + }); + return true; + } + + return false; + //return resp.map((m) => EmployeeLs.fromJson(m)).toList(); + } + +// Aktuális Munkaidő állapot lekérése szervertől + Future getWorkState(String userApiKey) async { + if (userApiKey.isEmpty) return false; + var url = "http://$appDomain/apiGetWorkState/$userApiKey"; + http.Response response = await http.get(Uri.parse(url)); + if (response.statusCode == 200) { + var resp = jsonDecode(response.body); + workState = WorkStateModel.fromJson(resp); + setState(() { + currentWorkState=workState.workState!; + }); + return true; + } + + return false; + //return resp.map((m) => EmployeeLs.fromJson(m)).toList(); + } + + Future checkApiKeyValidity(String userApiKey) async { + if (userApiKey.isEmpty) return false; + var url = "http://$appDomain/apiemployee/$userApiKey"; + http.Response response = await http.get(Uri.parse(url)); + if (response.statusCode == 200) { + var resp = jsonDecode(response.body); + ea = EmployeeArguments.fromJson(resp); + setState(() { + _reportButtonDisabled = false; + }); + if (ea.mobilePortalAccessEnabled == false) { + _showAccessDeniedDialog(); + } + employeePageArgs = EmployeePageArguments( + name: ea.name, + id: ea.id, + apiKey: userApiKey, + isForeman: ea.isForeman, + mobilePortalAccessEnabled: ea.mobilePortalAccessEnabled); + setState(() { + _reportButtonDisabled = !ea.isForeman!; + }); + if (ea.id!.isNotEmpty) { + return true; + } else { + return false; + } + } + + return false; + //return resp.map((m) => EmployeeLs.fromJson(m)).toList(); + } + @override + void dispose(){ + NfcManager.instance; + _controller.dispose(); + NfcManager.instance; + SystemChrome.setPreferredOrientations([ + DeviceOrientation.landscapeRight, + DeviceOrientation.landscapeLeft, + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + ]); + super.dispose(); + } + @override + void initState() { + // TODO: implement initStateA + super.initState(); + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + ]); + + di.initPlatformState().then((value) + { + deviceData = value; + + //checkApiKey(); + //saveApiKey(); + storage.SecureStorage sc= storage.SecureStorage(); + //sc.deleteSecureData('API'); + Future res= storage.SecureStorage().checkContainsKey('API');// sc.checkContainsKey('API'); + res.then((value) { + //_isApiKeyValid=value; + //apiKeyValid=value; + if (value==false) + { + Navigator.pushNamed(context, '/register', arguments: DeviceArguments( + deviceData['id'], deviceData['device'], deviceData['brand'])); + } + else + { + + Future userApiKey=sc.readSecureData("API"); + userApiKey.then((apiKey) => checkApiKeyValidity(apiKey).then((apiKeyState) { + // print("API key State: $apiKeyState"); + + setState(() { + _isApiKeyValid = apiKeyState; + if (ea.isForeman==true) + { + _reportButtonDisabled=false; + } + if (ea.mobilePortalAccessEnabled==true) + { + NfcManager.instance.isAvailable().then((nfcState){ + if (_isApiKeyValid && !_isLocationMocked) + { + _showNFClogo=true; + _tagRead(); + } + else + { + _showNFClogo=false; + } + } ); + getWorkState(apiKey); + getQRCode(apiKey); + /* NfcManager.instance.isAvailable().then((nfcvalue) { + if (nfcvalue) { + _nfcOk = true; + _showNFClogo = true; + if (_location?.isMock == false) { + _tagRead(); + } + } + });*/ + } + _getLocation(); + // _getCurrentLocation(); + _isApiKeyValid = apiKeyState; + }); + } + ) + ); + + } + }); + + //sc.writeSecureData("API", deviceData['id']); + //sc.checkContainsKey('API'); + //sc.readSecureData("API"); + }); + + _getLocation(); + PackageInfo.fromPlatform().then((value) => { + appCode=value.buildNumber, + appVersion=value.version + });//getAppVersion() as PackageInfo; + + + + if (!mounted) return; + setState(() { + _initOK=true; + _deviceData = deviceData; + + }); + //getWorkState(apiKey as String); + + //apiKey=//_getApiKey(); +// _storage.write(key: 'API', value: _deviceData['id']); + } + Future _getLocation() async { + setState(() { + _error = null; + _loading = true; + _isLocationValid=false; + _isScanVisible=false; + }); + try { + final locationResult = await location.getLocation(); + setState(() { + _location = locationResult; + _loading = false; + _isLocationValid = true; + _isLocationMocked=locationResult.isMock!; + _isScanVisible = true; + _currentLocation=locationResult; + _getAddressFromLatLng(locationResult.latitude,locationResult.longitude); + + }); + } on PlatformException catch (err) { + setState(() { + _error = err.code; + _loading = false; + _isLocationValid=false; + _isScanVisible=false; + }); + } + } + + + /* String _getAppBarTitle() => kIsWeb + ? 'Web Browser info' + : switch (defaultTargetPlatform) { + TargetPlatform.android => 'Android DI $apiKeyValid ${_deviceData['id']}', + TargetPlatform.iOS => 'iOS Device Info', + TargetPlatform.linux => 'Linux Device Info', + TargetPlatform.windows => 'Windows Device Info', + TargetPlatform.macOS => 'MacOS Device Info', + TargetPlatform.fuchsia => 'Fuchsia Device Info', + };*/ + Widget nfcLogo() { + if (_location?.isMock==true) { + return const Padding( + padding: EdgeInsets.all(8.0), + child: Image( + color: Colors.white, + image: AssetImage('graphics/nfcdenied.png'), + width: 128, + ), + ); + } else { + return ScaleTransition( + scale: _animation, + child: const Padding( + padding: EdgeInsets.all(8.0), + child: Image( + color: Colors.white, + image: AssetImage('assets/nfc.png'), + width: 128, + ), + ), + ); + } + } + Future _scanQR() async { + /*try { + String qrResult = await BarcodeScanner.scan(); + setState(() { + _login(qrResult); + /* result = qrResult; + TcpClient a = new TcpClient(socket, 'iotechnic.eu', 8080); + + _getId(); + a.sendAccess( + deviceId, + result, + _currentAddress, + _currentPosition.latitude.toString(), + _currentPosition.longitude.toString());*/ + }); + } on PlatformException catch (ex) { + if (ex.code == BarcodeScanner.CameraAccessDenied) { + setState(() { + result = "Camera permission was denied"; + }); + } else { + setState(() { + result = "Unknown Error $ex"; + }); + } + } on FormatException { + setState(() { + result = "You pressed the back button before scanning anything"; + }); + } catch (ex) { + setState(() { + result = "Unknown Error $ex"; + }); + }*/ + } + _loginQR(qrCode) { + setState(() { + _showNFClogo = false; + }); + //DeviceInfo devInfo = _getDeviceInfo() as DeviceInfo; + + String indexRequest = '{' + '"cmd":"ACCESSNOGEOQR",' + '"CID1":"${deviceData['id']}",' + '"CID2":"${deviceData['device']} ${deviceData['brand']}",' + '"address":"$_currentAddress",' + '"qrCode":"$qrCode",' + '"LAT":"${_currentLocation.latitude}",' + '"LON":"${_currentLocation.longitude}",' + '"mode":"GETAWAY"}'; + + debugPrint('Connecting...'); + //AESEncryption enc = new AESEncryption(); + //enc.encryptMsg(indexRequest); + //connect to google port 80 + Socket.connect("iotechnic.eu", 8080).then((socket) { + if (kDebugMode) { + print('Connected to: ' + '${socket.remoteAddress.address}:${socket.remotePort}'); + } + + //Establish the onData, and onDone callbacks + socket.listen((data) { + //print(new String.fromCharCodes(data).trim()); + var decoded = utf8.decode(data); + //var jsonString = json.encode(new String.fromCharCodes(data).trim()); + Map resp = jsonDecode(decoded); + debugPrint(decoded); + debugPrint(resp['data']); + getWorkState(employeePageArgs.apiKey as String); + if (resp['data'] == 'VALID CODE') { + setState(() { + result = ""; + accessName = resp['employeeName']; + accessMsg = resp['message']; + accessLocation = resp['city']; + accessWorktime = resp['worktime']; + accessDateTime = resp['datetime']; + }); + } else { + setState(() { + result = resp['data']; + accessName = ""; + accessMsg = ""; + accessLocation = ""; + accessWorktime = ""; + accessDateTime = ""; + }); + } + Timer.periodic(const Duration(seconds: 5), (timer) { + //print(DateTime.now()); + timer.cancel(); + result = "Üzemkész"; + accessName = ""; + accessMsg = ""; + accessLocation = ""; + accessWorktime = ""; + accessDateTime = ""; + setState(() { + _showNFClogo = true; + }); + }); + }, onDone: () { + if (kDebugMode) { + print("Done"); + } + socket.destroy(); + }); + + //Send the request + debugPrint("Request->$indexRequest"); + socket.write(indexRequest); + //socket.write(enc); + socket.flush(); + }); + } + _login(rfid) { + setState(() { + _showNFClogo = false; + }); + //DeviceInfo devInfo = _getDeviceInfo() as DeviceInfo; + + String indexRequest = '{' + '"cmd":"ACCESSNOGEO",' + '"CID1":"${deviceData['id']}",' + '"CID2":"${deviceData['device']} ${deviceData['brand']}",' + '"address":"$_currentAddress",' + '"rfid":"$rfid",' + '"LAT":"${_currentLocation.latitude}",' + '"LON":"${_currentLocation.longitude}",' + '"mode":"GETAWAY"}'; + + debugPrint('Connecting...'); + //AESEncryption enc = new AESEncryption(); + //enc.encryptMsg(indexRequest); + //connect to google port 80 + Socket.connect("iotechnic.eu", 8080).then((socket) { + if (kDebugMode) { + print('Connected to: ' + '${socket.remoteAddress.address}:${socket.remotePort}'); + } + + //Establish the onData, and onDone callbacks + socket.listen((data) { + //print(new String.fromCharCodes(data).trim()); + var decoded = utf8.decode(data); + //var jsonString = json.encode(new String.fromCharCodes(data).trim()); + Map resp = jsonDecode(decoded); + debugPrint(decoded); + debugPrint(resp['data']); + getWorkState(employeePageArgs.apiKey as String); + if (resp['data'] == 'VALID CODE') { + setState(() { + result = ""; + accessName = resp['employeeName']; + accessMsg = resp['message']; + accessLocation = resp['city']; + accessWorktime = resp['worktime']; + accessDateTime = resp['datetime']; + }); + } else { + setState(() { + result = resp['data']; + accessName = ""; + accessMsg = ""; + accessLocation = ""; + accessWorktime = ""; + accessDateTime = ""; + }); + } + Timer.periodic(const Duration(seconds: 5), (timer) { + //print(DateTime.now()); + timer.cancel(); + result = "Üzemkész"; + accessName = ""; + accessMsg = ""; + accessLocation = ""; + accessWorktime = ""; + accessDateTime = ""; + setState(() { + _showNFClogo = true; + }); + }); + }, onDone: () { + if (kDebugMode) { + print("Done"); + } + socket.destroy(); + }); + + //Send the request + debugPrint("Request->$indexRequest"); + socket.write(indexRequest); + //socket.write(enc); + socket.flush(); + }); + } + + _getAddressFromLatLng(lat,lon) async { + try { + List placemarks = await geocoding.placemarkFromCoordinates( + //_currentPosition!.latitude, _currentPosition!.longitude); + lat,lon); + geocoding.Placemark place = placemarks[0]; + + setState(() { + _currentAddress = + "${place.locality}, ${place.thoroughfare}, ${place.subThoroughfare}"; + result = "Üzemkész"; + }); + } catch (e) { + if (kDebugMode) { + print(e); + } + } + } + + Future _showAccessDeniedDialog() { + return showDialog( + barrierDismissible: false, + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Hozzáférés elutasítva'), + content: const Text('Az applikációs hozzáférése nincs engedélyezve!'), + actions: [ + TextButton( + onPressed: () { + SystemNavigator.pop(); + }, + child: const Text('Kilépés')), + ], + ); + }); + } + void _tagRead() { + const HexEncoder(upperCase: true); + + //print(HEX.encode(const [1, 2, 3, 12])); + NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async { + MifareClassic? nfca = MifareClassic.from(tag); + String nfcResult = HEX.encode(nfca!.identifier).toUpperCase(); + if (kDebugMode) { + print("TAG: $nfcResult"); + } + _getLocation(); + + _login(nfcResult); + //ag.data; + /* setState(() { + tagOk = true; + });*/ + /* if (tagOk = true) { + Future.delayed(Duration(milliseconds: 3000), () { + setState(() { + tagOk = false; + }); + + NfcManager.instance.stopSession(); + }); + }*/ + }); + } + showQRCode() { + + showDialog( + context: context, + builder: (context) { + return AlertDialog( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular( + 20.0, + ), + ), + ), + contentPadding: const EdgeInsets.only( + top: 10.0, + ), + title: Text( + "Státusz: $currentWorkState", + style: const TextStyle(fontSize: 24.0), + textAlign: TextAlign.center, + ), + content: Container( + //height: 400, + child: SingleChildScrollView( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + + Container( + padding: const EdgeInsets.all(8.0), + + child: qrdata.isNotEmpty ? QrImageView( + data: qrdata, + version: QrVersions.auto, + size: 200.0, + embeddedImageEmitsError: true, + ) + : const Text('HIBA!'), + ), + Container( + padding: const EdgeInsets.all(8.0), + child: ElevatedButton( + child: const Text('Bezár'), + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ], + ), + ), + ), + ); + }); + } + @override + Widget build(BuildContext context) { + //var qrResult; + /*return MaterialApp( + theme: ThemeData( + useMaterial3: true, + colorSchemeSeed: const Color(0x9f4376f8), + ),*/ + //return + // bool _positionIsMocked = _location?.isMock ?? false;//_currentPosition?.isMocked ?? false; + return + Scaffold( + appBar: AppBar( + title: Text("Mobile Portal V$appCode$appVersion"),//_getAppBarTitle()), + actions: [ + IconButton( + icon: const Icon(Icons.qr_code), + tooltip: 'Show Snackbar', + onPressed: () { + getWorkState(employeePageArgs.apiKey as String); + getQRCode(employeePageArgs.apiKey as String); + showQRCode(); + }, + ), + ], + + elevation: 4, + ), + /* floatingActionButton: new Visibility( + visible: _isScanVisible && _showNFClogo && _isApiKeyValid , + child: new FloatingActionButton.extended( + icon: Icon(Icons.qr_code), + label: Text("Beolvasás"), + //onPressed: _scanQR, + onPressed: () async { + MobileScanner( + // fit: BoxFit.contain, + controller: MobileScannerController( + detectionSpeed: DetectionSpeed.normal, + facing: CameraFacing.front, + torchEnabled: true, + ), + onDetect: (capture) { + final List barcodes = capture.barcodes; + final Uint8List? image = capture.image; + + for (final barcode in barcodes) { + qrResult=barcode; + debugPrint('Barcode found! ${barcode.rawValue}'); + } + setState(() { + if (qrResult is String) { + //result = res; + _login(qrResult); + } + } + + ); + }, + ); + }),//floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, + ),*/ + floatingActionButton: Visibility( + visible: _isScanVisible && _showNFClogo && _isApiKeyValid , + child: FloatingActionButton.extended( + icon: const Icon(Icons.qr_code), + label: const Text("Beolvasás"), + //onPressed: _scanQR, + onPressed: () async { + var qrResult = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SimpleBarcodeScannerPage(), + )); + setState(() { + if (qrResult is String) { + //result = res; + _loginQR(qrResult); + } + }); + }, + ), + //floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, + ), + body: + + Padding( + padding: const EdgeInsets.all(14.0), + child: _initOK + ? Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Flexible( + flex: 1, + //fit: FlexFit.tight, + child: + SizedBox( + //width: 300, + height: 50, + //color: Colors.amber, + child: + Column( + children: [ + Text(currentWorkState), + Text(currentQRcode.name as String, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold + ), + ), + ], + ), + ), + ), + Flexible( + flex: 2, + fit: FlexFit.tight, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + flex: 1, + fit: FlexFit.tight, + child: Container( + width: 200, + height: 200, + decoration: BoxDecoration( + //borderRadius: BorderRadius.circular(10), + color: Colors.indigo[600], + ), //BoxDecoration + child: Transform.scale( + scale: 2, + child: CustomIconButton( + icon: const Icon(Icons.pending_actions), + isDisabled: _isApiKeyValid == false, + color: _isScanVisible || + !_isLocationMocked || + _isApiKeyValid == true + ? Colors.indigo[600]! + : Colors.grey.shade50, + onPressed: () { + if (_isApiKeyValid == true) { + Navigator.pushNamed(context, '/wtdetails', + arguments: { + 'userArgs': employeePageArgs, + 'dataArgs': null + }); + } + }), + ), + ), //Container + ), //Flexible + const SizedBox( + width: 10, + ), //SizedBox + Flexible( + flex: 1, + fit: FlexFit.tight, + child: Container( + width: 200, + height: 200, + decoration: BoxDecoration( + //borderRadius: BorderRadius.circular(10), + color: Colors.indigo[600], + ), + child: Transform.scale( + scale: 2, + child: CustomIconButton( + icon: const Icon(Icons.list), + isDisabled: _reportButtonDisabled || + _isApiKeyValid == false, + color: Colors.indigo[600]!, + onPressed: () { + Navigator.pushNamed(context, '/reportNav', + arguments: { + 'userArgs': employeePageArgs, + 'dataArgs': null + }); + //WorkTimeDetails())); + }, + ), //BoxDecoration + ), //Container + ), + ) //Flexible + ], + ), //Row + ), + + /*const SizedBox( + height: 5, + ),*/ //SixedBox + Flexible( + flex: 4, + fit: FlexFit.tight, + child: Container( + width: 380, + height: 200, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.blue), //BoxDecoration + child: _isApiKeyValid == true + ? Column( + children: [ + const SizedBox( + height: 10, + ), + if (!_isLocationValid) ...[ + //Text("Várakozás heladatokra..."), + const CircularProgressIndicator( + color: Colors.white, + ), + ], + if (!_isScanVisible) ...[ + TextButton( + child: const Text( + "Várakozás heladatokra...", + style: TextStyle( + fontSize: 24.0, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + onPressed: () { + _getLocation(); + }, + ), + const SizedBox( + height: 30, + ), + const Icon( + Icons.location_on_outlined, + size: 64, + color: Colors.white, + ), + ], + if (_isScanVisible && !_showNFClogo) + Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Text( + accessName, + style: const TextStyle( + fontSize: 30.0, + fontWeight: FontWeight.normal), + ), + Text( + accessMsg, + style: const TextStyle( + fontSize: 30.0, + fontWeight: FontWeight.bold), + ), + Text( + accessDateTime, + style: const TextStyle( + fontSize: 30.0, + fontWeight: FontWeight.bold), + ), + Text( + accessWorktime, + style: const TextStyle( + fontSize: 30.0, + fontWeight: FontWeight.bold), + ), + Text( + accessLocation, + style: const TextStyle( + fontSize: 16.0, + fontWeight: FontWeight.bold), + ), + ]), + if (_isScanVisible) ...[ + Text( + result, + style: const TextStyle( + color: Colors.white, + fontSize: 30.0, + fontWeight: FontWeight.bold), + ), + const SizedBox( + height: 20, + ), + if (_showNFClogo) + nfcLogo() + ], + ], + ) + : const Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Hozzáférés nem engedélyezett!', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 30.0, + fontWeight: FontWeight.bold), + + ), + ], + )), //Container + ), //Flexible + + ], + ) + : Container( + alignment: Alignment.topCenter, + margin: const EdgeInsets.only(top: 20), + child: const CircularProgressIndicator( + value: 0.8, + )) //Column + ) + + ); //Padding + + } + + + /*Scaffold( + appBar: AppBar( + title: Text(_getAppBarTitle()), + elevation: 4, + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Location: ${_error ?? '${_location ?? "unknown"}'}', + style: Theme.of(context).textTheme.bodyLarge, + ), + Row( + children: [ + ElevatedButton( + onPressed: _getLocation, + child: _loading + ? const CircularProgressIndicator( + color: Colors.white, + ) + : const Text('Get'), + ) + ], + ), + ], + ) + );*/ + // ); + } + + diff --git a/lib/models/employee_model.dart b/lib/models/employee_model.dart new file mode 100644 index 0000000..a8c70a7 --- /dev/null +++ b/lib/models/employee_model.dart @@ -0,0 +1,68 @@ + +/* +class Employee { + final String id; + final String name; + //final bool value; + Employee({ + @required this.id, + @required this.name, + //@required this.value, + }); + + factory Employee.fromJson(Map json) { + return Employee( + id: json['_id'] as String, + name: json['name'] as String, + //value: json['value'] as bool, + ); + } +} +*/ +class EmployeeLs { + String id; + String name; + bool value; + EmployeeLs({ + required this.id, + required this.name, + required this.value, + }); + + factory EmployeeLs.fromJson(Map json) { + return EmployeeLs( + id: json['_id'] as String, + name: json['name'] as String, + value: json['value'] ?? false //as bool, + ); + } +} + +class WorkLs { + String id; + String title; + String body; + String state; + //bool value; + WorkLs({ + required this.id, + required this.title, + required this.body, + required this.state, + // @required this.value, + }); + @override + String toString() { + return '$title '; + } + + factory WorkLs.fromJson(Map json) { + return WorkLs( + id: json['_id'] as String, + title: json['title'] as String, + body: json['body'] as String, + state: json['state'] as String, + // value: json['value'] as bool, + ); + } +} diff --git a/lib/models/reportListModel.dart b/lib/models/reportListModel.dart new file mode 100644 index 0000000..f662bd2 --- /dev/null +++ b/lib/models/reportListModel.dart @@ -0,0 +1,148 @@ + +class ModelReportList { + String? sId; + String? title; + String? body; + String? poNumber; + String? workNumber; + String? state; + late List dailyReport; + + ModelReportList( + {required this.sId, + required this.title, + required this.body, + required this.poNumber, + required this.workNumber, + required this.state, + required this.dailyReport}); + + ModelReportList.fromJson(Map json) { + sId = json['_id']; + title = json['title']; + body = json['body']; + poNumber = json['poNumber']; + workNumber = json['workNumber']; + state = json['state']; + if (json['dailyReport'] != null) { + dailyReport = []; + json['dailyReport'].forEach((v) { + dailyReport.add(DailyReport.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = {}; + data['_id'] = sId; + data['title'] = title; + data['body'] = body; + data['poNumber'] = poNumber; + data['workNumber'] = workNumber; + data['state'] = state; + data['dailyReport'] = dailyReport.map((v) => v.toJson()).toList(); + return data; + } +} + +class DailyReport { + late List employeeList; + String? sId; + String? date; + String? foremanId; + String? workTitle; + + DailyReport( + {required this.employeeList, + required this.sId, + required this.date, + required this.foremanId, + required this.workTitle}); + + DailyReport.fromJson(Map json) { + employeeList = json['employeeList'].cast(); + sId = json['_id']; + date = json['date']; + foremanId = json['foremanId']; + workTitle = json['workTitle']; + } + + Map toJson() { + final Map data = {}; + data['employeeList'] = employeeList; + data['_id'] = sId; + data['date'] = date; + data['foremanId'] = foremanId; + data['workTitle'] = workTitle; + return data; + } +} + +class WorkListModel { + String? _sId; + String? _title; + String? _body; + String? _poNumber; + String? _workNumber; + String? _state; + + WorkListModel( + {String? sId, + String? title, + String? body, + String? poNumber, + String? workNumber, + String? state}) { + if (sId != null) { + _sId = sId; + } + if (title != null) { + _title = title; + } + if (body != null) { + _body = body; + } + if (poNumber != null) { + _poNumber = poNumber; + } + if (workNumber != null) { + _workNumber = workNumber; + } + if (state != null) { + _state = state; + } + } + + String? get sId => _sId; + set sId(String? sId) => _sId = sId; + String? get title => _title; + set title(String? title) => _title = title; + String? get body => _body; + set body(String? body) => _body = body; + String? get poNumber => _poNumber; + set poNumber(String? poNumber) => _poNumber = poNumber; + String? get workNumber => _workNumber; + set workNumber(String? workNumber) => _workNumber = workNumber; + String? get state => _state; + set state(String? state) => _state = state; + + WorkListModel.fromJson(Map json) { + _sId = json['_id']; + _title = json['title']; + _body = json['body']; + _poNumber = json['poNumber']; + _workNumber = json['workNumber']; + _state = json['state']; + } + + Map toJson() { + final Map data = {}; + data['_id'] = _sId; + data['title'] = _title; + data['body'] = _body; + data['poNumber'] = _poNumber; + data['workNumber'] = _workNumber; + data['state'] = _state; + return data; + } +} diff --git a/lib/models/workTimeDetailsModel.dart b/lib/models/workTimeDetailsModel.dart new file mode 100644 index 0000000..41269db --- /dev/null +++ b/lib/models/workTimeDetailsModel.dart @@ -0,0 +1,275 @@ + +class WorkTimeDetailsModel { + final List? wd; + final List? munkaszunet; + final Month? month; + final Weekend? weekend; + + WorkTimeDetailsModel({ + this.wd, + this.munkaszunet, + this.month, + this.weekend, + }); + + WorkTimeDetailsModel.fromJson(Map json) + : wd = (json['wd'] as List?) + ?.map((dynamic e) => Wd.fromJson(e as Map)) + .toList(), + munkaszunet = json['munkaszunet'] as List?, + month = (json['month'] as Map?) != null + ? Month.fromJson(json['month'] as Map) + : null, + weekend = (json['weekend'] as Map?) != null + ? Weekend.fromJson(json['weekend'] as Map) + : null; + + Map toJson() => { + 'wd': wd?.map((e) => e.toJson()).toList(), + 'munkaszunet': munkaszunet, + 'month': month?.toJson(), + 'weekend': weekend?.toJson() + }; +} + +class Wd { + final String? date; + final bool? isMunkaszunet; + final bool? isHoliday; + final bool? isWeekend; + final String? location; + final String? notes; + final List? startStop; + final int? fullHours; + final int? hours; + final int? overTime; + + Wd({ + this.date, + this.isMunkaszunet, + this.isHoliday, + this.isWeekend, + this.location, + this.notes, + this.startStop, + this.fullHours, + this.hours, + this.overTime, + }); + + Wd.fromJson(Map json) + : date = json['date'] as String?, + isMunkaszunet = json['isMunkaszunet'] as bool?, + isHoliday = json['isHoliday'] as bool?, + isWeekend = json['isWeekend'] as bool?, + location = json['location'] as String?, + notes = json['notes'] as String?, + startStop = (json['startStop'] as List?) + ?.map((dynamic e) => StartStop.fromJson(e as Map)) + .toList(), + fullHours = json['fullHours'] as int?, + hours = json['hours'] as int?, + overTime = json['overTime'] as int?; + + Map toJson() => { + 'date': date, + 'isMunkaszunet': isMunkaszunet, + 'isHoliday': isHoliday, + 'isWeekend': isWeekend, + 'location': location, + 'notes': notes, + 'startStop': startStop?.map((e) => e.toJson()).toList(), + 'fullHours': fullHours, + 'hours': hours, + 'overTime': overTime + }; +} + +class StartStop { + final String? start; + final String? stop; + final String? arriveLoc; + final String? getawayLoc; + final ArriveCoords? arriveCoords; + final GetavayCoords? getavayCoords; + + StartStop({ + this.start, + this.stop, + this.arriveLoc, + this.getawayLoc, + this.arriveCoords, + this.getavayCoords, + }); + + StartStop.fromJson(Map json) + : start = json['start'] as String?, + stop = json['stop'] as String?, + arriveLoc = json['arriveLoc'] as String?, + getawayLoc = json['getawayLoc'] as String?, + arriveCoords = (json['arriveCoords'] as Map?) != null + ? ArriveCoords.fromJson( + json['arriveCoords'] as Map) + : null, + getavayCoords = (json['getavayCoords'] as Map?) != null + ? GetavayCoords.fromJson( + json['getavayCoords'] as Map) + : null; + + Map toJson() => { + 'start': start, + 'stop': stop, + 'arriveLoc': arriveLoc, + 'getawayLoc': getawayLoc, + 'arriveCoords': arriveCoords?.toJson(), + 'getavayCoords': getavayCoords?.toJson() + }; +} + +class ArriveCoords { + late final double? lat; + late final double? lon; + + ArriveCoords({ + this.lat, + this.lon, + }); + + ArriveCoords.fromJson(Map json) { + if (json.isNotEmpty && + json['lat'] != null && + json['lon'] != null && + json['lat'] != 0 && + json['lon'] != 0) { + lat = (json['lat'] as double).toDouble(); + lon = (json['lon'] as double).toDouble(); + } else { + lat = 0.0; + lon = 0.0; + } + } + Map toJson() => {'lat': lat, 'lon': lon}; +} + +class GetavayCoords { + late final double? lat; + late final double? lon; + + GetavayCoords({ + this.lat, + this.lon, + }); + + GetavayCoords.fromJson(Map json) { + if (json.isNotEmpty && + json['lat'] != null && + json['lon'] != null && + json['lat'] != 0 && + json['lon'] != 0) { + lat = (json['lat'] as double).toDouble(); + lon = (json['lon'] as double).toDouble(); + } else { + lat = 0.0; + lon = 0.0; + } + } + Map toJson() => {'lat': lat, 'lon': lon}; +} + +class Month { + final int? overTime; + final int? workHours; + final int? fullWorkHours; + + Month({ + this.overTime, + this.workHours, + this.fullWorkHours, + }); + + Month.fromJson(Map json) + : overTime = json['overTime'] as int?, + workHours = json['workHours'] as int?, + fullWorkHours = json['fullWorkHours'] as int?; + + Map toJson() => { + 'overTime': overTime, + 'workHours': workHours, + 'fullWorkHours': fullWorkHours + }; +} + +class Weekend { + final Saturday? saturday; + final Sunday? sunday; + + Weekend({ + this.saturday, + this.sunday, + }); + + Weekend.fromJson(Map json) + : saturday = (json['Saturday'] as Map?) != null + ? Saturday.fromJson(json['Saturday'] as Map) + : null, + sunday = (json['Sunday'] as Map?) != null + ? Sunday.fromJson(json['Sunday'] as Map) + : null; + + Map toJson() => + {'Saturday': saturday?.toJson(), 'Sunday': sunday?.toJson()}; +} + +class Saturday { + final int? overTime; + final int? workHours; + final int? fullWorkHours; + final int? days; + + Saturday({ + this.overTime, + this.workHours, + this.fullWorkHours, + this.days, + }); + + Saturday.fromJson(Map json) + : overTime = json['overTime'] as int?, + workHours = json['workHours'] as int?, + fullWorkHours = json['fullWorkHours'] as int?, + days = json['days'] as int?; + + Map toJson() => { + 'overTime': overTime, + 'workHours': workHours, + 'fullWorkHours': fullWorkHours, + 'days': days + }; +} + +class Sunday { + final int? overTime; + final int? workHours; + final int? fullWorkHours; + final int? days; + + Sunday({ + this.overTime, + this.workHours, + this.fullWorkHours, + this.days, + }); + + Sunday.fromJson(Map json) + : overTime = json['overTime'] as int?, + workHours = json['workHours'] as int?, + fullWorkHours = json['fullWorkHours'] as int?, + days = json['days'] as int?; + + Map toJson() => { + 'overTime': overTime, + 'workHours': workHours, + 'fullWorkHours': fullWorkHours, + 'days': days + }; +} diff --git a/lib/register_page.dart b/lib/register_page.dart new file mode 100644 index 0000000..cf406c8 --- /dev/null +++ b/lib/register_page.dart @@ -0,0 +1,179 @@ +//import 'dart:js_interop'; + +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +//import 'package:mobile_portal_23/services/device_info.dart'; +//import 'services/storage.dart' as storage; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +//import 'package:device_info_plus/device_info_plus.dart'; +import 'services/storage.dart' as storage; +import 'dart:convert'; +import 'classes/named_route_args.dart'; +//import 'dart:developer'; + +class RegisterPage extends StatefulWidget { + const RegisterPage({Key? key}) : super(key: key); + static const routeName = '/register'; + @override + State createState() => _RegisterPageState(); +} +String appDomain = "iotechnic.eu"; +class _RegisterPageState extends State { + final usernameTxtController = TextEditingController(); + final passwordTxtController = TextEditingController(); + //Map _deviceData = {}; + // Eszköz regisztráció + Future deviceRegistration(String userName, String password) async { + storage.SecureStorage sc= storage.SecureStorage(); + //final DeviceInfo deviceInfo = + /* _deviceData= + ModalRoute.of(context)!.settings.arguments as Map;*/ + final args=ModalRoute.of(context)!.settings.arguments as DeviceArguments; + + var url = "http://$appDomain/apiRegisterDevice"; + var report = { + 'deviceId': args.Id, + 'username': userName, + 'password': password, + 'deviceType': '${args.Name} ${args.Brand}' + }; + //log('data: $report'); + final response = await http.post( + Uri.parse(url), + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + }, + body: jsonEncode(report), + ); + + + if (response.statusCode == 404) { + var resp = jsonDecode(response.body); + //log('resp:'+ resp['message']); + EasyLoading.showError(resp['message']); + Future.delayed(const Duration(seconds: 3), () { + // Navigator.pop(context); //pop dialog + EasyLoading.dismiss(); + + // Navigator.of(context).pop(); + //_login(); + }); + } + if (response.statusCode == 200) { + var resp = jsonDecode(response.body); + if (resp['apiKey']==null) + { + EasyLoading.showError(resp['message']); + Future.delayed(const Duration(seconds: 3), () { + // Navigator.pop(context); //pop dialog + EasyLoading.dismiss(); + }); + + } + else { + EasyLoading.showSuccess(resp['message']); + sc.writeSecureData("API", resp['apiKey']); + Future.delayed(const Duration(seconds: 3), () { + // Navigator.pop(context); //pop dialog + EasyLoading.dismiss(); + + Navigator.of(context).pop(); + }); + } + // _saveApiKey(resp['apiKey']); + + //_login(); + + } + + return false; + //return resp.map((m) => EmployeeLs.fromJson(m)).toList(); + } + @override + void dispose() { + // TODO: implement dispose + usernameTxtController.dispose(); + passwordTxtController.dispose(); + super.dispose(); + } + @override + Widget build(BuildContext context) { + + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + title: const Text("Eszköz regisztráció"), + ), + body: SingleChildScrollView( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 60.0), + child: Center( + child: SizedBox( + width: 200, + height: 150, + /*decoration: BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.circular(50.0)),*/ + child: Image.asset('assets/cloudlogo.png')), + ), + ), + Padding( + //padding: const EdgeInsets.only(left:15.0,right: 15.0,top:0,bottom: 0), + padding: const EdgeInsets.symmetric(horizontal: 15), + child: TextField( + controller: usernameTxtController, + decoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'Teljes név', + hintText: 'Teljes név pl.: Gipsz Jakab'), + ), + ), + Padding( + padding: const EdgeInsets.only( + left: 15.0, right: 15.0, top: 15, bottom: 0), + //padding: EdgeInsets.symmetric(horizontal: 15), + child: TextField( + controller: passwordTxtController, + obscureText: true, + decoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'Jelszó', + hintText: 'Adja meg KL portal jelszavát!'), + ), + ), + TextButton( + onPressed: (){ + //TODO FORGOT PASSWORD SCREEN GOES HERE + }, + child: const Text( + 'Forgot Password', + style: TextStyle(color: Colors.blue, fontSize: 15), + ), + ), + Container( + height: 50, + width: 250, + decoration: BoxDecoration( + color: Colors.orange, borderRadius: BorderRadius.circular(20)), + child: TextButton( + onPressed: () { + if(usernameTxtController.text.isNotEmpty && passwordTxtController.text.isNotEmpty) + { deviceRegistration(usernameTxtController.text, passwordTxtController.text);} + /*Navigator.pop( + context);*/ + }, + child: const Text( + 'Belépés', + style: TextStyle(color: Colors.white, fontSize: 25), + ), + ), + ), + + ], + ), + ), + ); + } +} diff --git a/lib/report_selectWork.dart b/lib/report_selectWork.dart new file mode 100644 index 0000000..2b0192d --- /dev/null +++ b/lib/report_selectWork.dart @@ -0,0 +1,226 @@ +import 'package:flutter/material.dart'; +import 'package:mobile_portal_23/classes/common_classes.dart'; +import 'package:mobile_portal_23/models/reportListModel.dart'; +import 'package:http/http.dart' as http; +import 'package:mobile_portal_23/widgets/reportNew.dart'; +import 'dart:convert'; +import 'package:shared_preferences/shared_preferences.dart'; + +class reportCreate extends StatefulWidget { + static const routeName = '/reportCreate'; + const reportCreate({Key? key}) : super(key: key); + + @override + State createState() => _reportCreateState(); +} + +late EmployeePageArguments employeePageArguments; + +class _reportCreateState extends State { + String appDomain = "iotechnic.eu"; +// String? userApiKey; // = "XST8X8F-6Q9M1FG-PE9948X-SPFHVX9"; + //late List reportList = []; + + late List workList = []; + Future _saveFavoriteWorks(List favoriteWorks) async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.setStringList('favoriteWorks', favoriteWorks); + } + + Future getSharedPrefs() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + // userApiKey = prefs.getString("apiKey"); + favoriteWorks = prefs.getStringList("favoriteWorks") ?? []; + } + + Future fetchdata() async { + var res = await http.get( + + // "http://iotechnic.eu/apiGetAllReport/X8B0PQS-2KYMCV3-G5WD74N-8G0CRAH"); + Uri.parse("http://$appDomain/apiGetAllWork/${employeePageArguments.apiKey!}")); + //"http://192.168.0.144/apiGetDailyReport/X8B0PQS-2KYMCV3-G5WD74N-8G0CRAH"); + if (res.statusCode == 200) { + var obj = json.decode(res.body); + return obj; + } + } + + late List favoriteWorks = []; + final bool _showFaworites = false; + // late EmployeeArguments empArgs; + bool _isError = false; + bool _isData = false; + @override + void initState() { + // TODO: implement initState + WidgetsBinding.instance.addPostFrameCallback((_) { + final args = ModalRoute.of(context)!.settings.arguments as Map; + + employeePageArguments = args['userArgs']; + }); + getSharedPrefs().then((value) { + fetchdata().then((data) { + if (data != null) { + for (var item in data) { + // reportList.add(ModelReportList.fromJson(item)); + workList.add(WorkListModel.fromJson(item)); + } + _isData = true; + setState(() { + debugPrint('Fetch ok.'); + }); + } else { + _isError = true; + } + //employeeList = data; + /* empArgs = (ModalRoute.of(context)?.settings.arguments ?? + {}) as EmployeeArguments;*/ + }); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Új jelentés')), body: page(context)); + } + + Container page(BuildContext context) { + return _isData == false + ? _isError + ? Container( + child: const AlertDialog( + title: Text('Hálózati Hiba!'), + content: Icon(Icons.error_outline), + elevation: 24, + backgroundColor: Colors.red), + ) + : Container( + child: SizedBox( + height: MediaQuery.of(context).size.height / 1.3, + width: MediaQuery.of(context).size.width, + child: const Center( + child: CircularProgressIndicator(), + ), + )) + : gridList(context); + } + + Container gridList(BuildContext context) { + //List _reportList = []; + List workList = []; + if (_showFaworites) { + workList = workList + .where((element) => favoriteWorks.contains(element.sId)) + .toList(); + } else { + workList = workList; + } + return Container( + color: const Color(0xffECF0F1), + child: ListView.builder( + itemCount: workList.length, // The length Of the array + + padding: const EdgeInsets.all(5), + shrinkWrap: true, + + itemBuilder: (context, index) => Container( + child: reportCard(context, workList[index]), + ), + ), + ); + } + + Card reportCard(BuildContext context, WorkListModel item) { + int workHour = 0; + bool workOpened = false; + bool isFaworite = favoriteWorks.contains(item.sId); + if (item.state!.contains("Opened")) workOpened = true; + /* item.dailyReport.forEach((element) { + workHour += element.employeeList.length; + });*/ + return Card( + clipBehavior: Clip.antiAlias, + child: Column( + children: [ + ListTile( + tileColor: workOpened ? Colors.indigo : Colors.grey, + trailing: IconButton( + onPressed: () { + if (isFaworite) { + favoriteWorks.remove(item.sId); + } else { + String? id = item.sId; + favoriteWorks.add(id!); + } + _saveFavoriteWorks(favoriteWorks); + }, + icon: isFaworite + ? Icon( + Icons.star, + color: favoriteWorks.contains(item.sId) + ? Colors.yellow + : null, + ) + : const Icon(Icons.star_border)), + onTap: workOpened + ? () { + ReportArguments reportArgs = ReportArguments( + List.empty(), + //userApiKey!, + item.title!, + item.sId!, + item.state == "Opened" ? true : false, + //empArgs.isForeman! + ); + + Navigator.pushNamed(context, reportNew.routeName, + arguments: { + 'userArgs': employeePageArguments, + 'dataArgs': reportArgs + }); + } + : null, + title: Text( + item.title!, + style: const TextStyle(color: Colors.white), + ), + /*subtitle: Text( + item.body!, + style: TextStyle(color: Colors.yellow.withOpacity(0.6)), + ),*/ + ), + Padding( + padding: const EdgeInsets.all(10.0), + child: IntrinsicHeight( + child: Column(children: [ + Text( + item.body!.replaceAll("\n", " "), + style: TextStyle(color: Colors.green.withOpacity(0.6)), + ), + const Divider( + thickness: 1.0, + ), + Row(children: [ + + Text( + 'KL${item.workNumber!}', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + const VerticalDivider(color: Colors.grey, thickness: 1, width: 30), + Text( + 'PO: ${item.poNumber!}', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ]), + ]))), + ], + ), + ); + } +} diff --git a/lib/services/device_info.dart b/lib/services/device_info.dart new file mode 100644 index 0000000..914e101 --- /dev/null +++ b/lib/services/device_info.dart @@ -0,0 +1,183 @@ +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + + +class DeviceInfo{ + static final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); + + + Future> initPlatformState() async { + var deviceData = {}; + + try { + if (kIsWeb) { + deviceData = _readWebBrowserInfo(await deviceInfoPlugin.webBrowserInfo); + } else { + deviceData = switch (defaultTargetPlatform) { + TargetPlatform.android => + _readAndroidBuildData(await deviceInfoPlugin.androidInfo), + TargetPlatform.iOS => + _readIosDeviceInfo(await deviceInfoPlugin.iosInfo), + TargetPlatform.linux => + _readLinuxDeviceInfo(await deviceInfoPlugin.linuxInfo), + TargetPlatform.windows => + _readWindowsDeviceInfo(await deviceInfoPlugin.windowsInfo), + TargetPlatform.macOS => + _readMacOsDeviceInfo(await deviceInfoPlugin.macOsInfo), + TargetPlatform.fuchsia => { + 'Error:': 'Fuchsia platform isn\'t supported' + }, + }; + } + return deviceData; + } on PlatformException { + deviceData = { + 'Error:': 'Failed to get platform version.' + }; + return deviceData; + } + + + } + Map _readAndroidBuildData(AndroidDeviceInfo build) { + return { + 'version.securityPatch': build.version.securityPatch, + 'version.sdkInt': build.version.sdkInt, + 'version.release': build.version.release, + 'version.previewSdkInt': build.version.previewSdkInt, + 'version.incremental': build.version.incremental, + 'version.codename': build.version.codename, + 'version.baseOS': build.version.baseOS, + 'board': build.board, + 'bootloader': build.bootloader, + 'brand': build.brand, + 'device': build.device, + 'display': build.display, + 'fingerprint': build.fingerprint, + 'hardware': build.hardware, + 'host': build.host, + 'id': build.id, + 'manufacturer': build.manufacturer, + 'model': build.model, + 'product': build.product, + 'supported32BitAbis': build.supported32BitAbis, + 'supported64BitAbis': build.supported64BitAbis, + 'supportedAbis': build.supportedAbis, + 'tags': build.tags, + 'type': build.type, + 'isPhysicalDevice': build.isPhysicalDevice, + 'systemFeatures': build.systemFeatures, + 'displaySizeInches': + ((build.displayMetrics.sizeInches * 10).roundToDouble() / 10), + 'displayWidthPixels': build.displayMetrics.widthPx, + 'displayWidthInches': build.displayMetrics.widthInches, + 'displayHeightPixels': build.displayMetrics.heightPx, + 'displayHeightInches': build.displayMetrics.heightInches, + 'displayXDpi': build.displayMetrics.xDpi, + 'displayYDpi': build.displayMetrics.yDpi, + 'serialNumber': build.serialNumber, + }; + } + + Map _readIosDeviceInfo(IosDeviceInfo data) { + return { + 'name': data.name, + 'systemName': data.systemName, + 'systemVersion': data.systemVersion, + 'model': data.model, + 'localizedModel': data.localizedModel, + 'identifierForVendor': data.identifierForVendor, + 'isPhysicalDevice': data.isPhysicalDevice, + 'utsname.sysname:': data.utsname.sysname, + 'utsname.nodename:': data.utsname.nodename, + 'utsname.release:': data.utsname.release, + 'utsname.version:': data.utsname.version, + 'utsname.machine:': data.utsname.machine, + }; + } + + Map _readLinuxDeviceInfo(LinuxDeviceInfo data) { + return { + 'name': data.name, + 'version': data.version, + 'id': data.id, + 'idLike': data.idLike, + 'versionCodename': data.versionCodename, + 'versionId': data.versionId, + 'prettyName': data.prettyName, + 'buildId': data.buildId, + 'variant': data.variant, + 'variantId': data.variantId, + 'machineId': data.machineId, + }; + } + + Map _readWebBrowserInfo(WebBrowserInfo data) { + return { + 'browserName': data.browserName, + 'appCodeName': data.appCodeName, + 'appName': data.appName, + 'appVersion': data.appVersion, + 'deviceMemory': data.deviceMemory, + 'language': data.language, + 'languages': data.languages, + 'platform': data.platform, + 'product': data.product, + 'productSub': data.productSub, + 'userAgent': data.userAgent, + 'vendor': data.vendor, + 'vendorSub': data.vendorSub, + 'hardwareConcurrency': data.hardwareConcurrency, + 'maxTouchPoints': data.maxTouchPoints, + }; + } + + Map _readMacOsDeviceInfo(MacOsDeviceInfo data) { + return { + 'computerName': data.computerName, + 'hostName': data.hostName, + 'arch': data.arch, + 'model': data.model, + 'kernelVersion': data.kernelVersion, + 'majorVersion': data.majorVersion, + 'minorVersion': data.minorVersion, + 'patchVersion': data.patchVersion, + 'osRelease': data.osRelease, + 'activeCPUs': data.activeCPUs, + 'memorySize': data.memorySize, + 'cpuFrequency': data.cpuFrequency, + 'systemGUID': data.systemGUID, + }; + } + + Map _readWindowsDeviceInfo(WindowsDeviceInfo data) { + return { + 'numberOfCores': data.numberOfCores, + 'computerName': data.computerName, + 'systemMemoryInMegabytes': data.systemMemoryInMegabytes, + 'userName': data.userName, + 'majorVersion': data.majorVersion, + 'minorVersion': data.minorVersion, + 'buildNumber': data.buildNumber, + 'platformId': data.platformId, + 'csdVersion': data.csdVersion, + 'servicePackMajor': data.servicePackMajor, + 'servicePackMinor': data.servicePackMinor, + 'suitMask': data.suitMask, + 'productType': data.productType, + 'reserved': data.reserved, + 'buildLab': data.buildLab, + 'buildLabEx': data.buildLabEx, + 'digitalProductId': data.digitalProductId, + 'displayVersion': data.displayVersion, + 'editionId': data.editionId, + 'installDate': data.installDate, + 'productId': data.productId, + 'productName': data.productName, + 'registeredOwner': data.registeredOwner, + 'releaseId': data.releaseId, + 'deviceId': data.deviceId, + }; + } +} diff --git a/lib/services/storage.dart b/lib/services/storage.dart new file mode 100644 index 0000000..074db62 --- /dev/null +++ b/lib/services/storage.dart @@ -0,0 +1,32 @@ +import 'dart:developer'; + +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + +class SecureStorage { + final FlutterSecureStorage storage = const FlutterSecureStorage(); + + writeSecureData(String key, String value) async { + await storage.write(key: key, value: value); + log('Data writed to secure storage: $key $value'); + //print('Data writed to secure storage: $key $value'); + } + + Future readSecureData(String key) async { + log('Data start read from secure storage: $key'); + String value = await storage.read(key: key) ?? 'No data found!'; + log('Data read from secure storage: $value'); + //print('Data read from secure storage: $value'); + return value; + } + Future checkContainsKey(String key) async { + bool value = await storage.containsKey(key: key); + log('Key $key checked: $value'); + //print('Key $key checked: $value'); + return value; + } + deleteSecureData(String key) async { + await storage.delete(key: key); + log('Data deleted from secure storage: $key'); + //print('Data deleted from secure storage: $key'); + } +} \ No newline at end of file diff --git a/lib/widgets/reportCardWidget.dart b/lib/widgets/reportCardWidget.dart new file mode 100644 index 0000000..19699ba --- /dev/null +++ b/lib/widgets/reportCardWidget.dart @@ -0,0 +1,193 @@ +import 'package:flutter/material.dart'; +import 'package:mobile_portal_23/models/reportListModel.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:badges/badges.dart' as badges; +import 'package:mobile_portal_23/classes/common_classes.dart'; + +import 'package:mobile_portal_23/widgets/reportDetails.dart'; + +class reportCardWidget extends StatefulWidget { + // reportCardWidget({Key? key}) : super(key: key); + + final ModelReportList reportListItem; + const reportCardWidget(this.reportListItem, {super.key}); + @override + State createState() => _reportCardWidgetState(); +} + +List favoriteWorks = []; +//late List reportList = []; +bool _showFaworites = false; +late EmployeePageArguments employeePageArguments; +Future _saveFavoriteWorks(List favoriteWorks) async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.setStringList('favoriteWorks', favoriteWorks); +} + +Future getSharedPrefs() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + // userApiKey = prefs.getString("apiKey"); + favoriteWorks = prefs.getStringList("favoriteWorks") ?? []; +} + +class _reportCardWidgetState extends State { + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) { + final args = ModalRoute.of(context)!.settings.arguments as Map; + + employeePageArguments = args['userArgs']; + }); + getSharedPrefs(); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return reportCard(context, widget.reportListItem); + } + + Card reportCard(BuildContext context, ModelReportList item) { + int workHour = 0; + bool workOpened = false; + bool isFaworite = favoriteWorks.contains(item.sId); + if (item.state!.contains("Opened")) workOpened = true; + for (var element in item.dailyReport) { + workHour += element.employeeList.length; + } + return Card( + clipBehavior: Clip.antiAlias, + child: Column( + children: [ + badges.Badge( + showBadge: !workOpened, + position: badges.BadgePosition.topEnd(top: 3, end: 3), + badgeContent: const Icon(Icons.lock_outlined), + child: ListTile( + tileColor: workOpened ? Colors.blue : Colors.grey, + trailing: IconButton( + onPressed: () { + if (isFaworite) { + favoriteWorks.remove(item.sId); + } else { + String? id = item.sId; + favoriteWorks.add(id!); + } + _saveFavoriteWorks(favoriteWorks); + setState(() {}); + }, + icon: isFaworite + ? Icon( + Icons.star, + color: favoriteWorks.contains(item.sId) + ? Colors.yellow + : null, + ) + : const Icon(Icons.star_border)), + /* Icon( + Icons.keyboard_arrow_right, + // size: 48, + color: Colors.white, + + ),*/ + onTap: () { + ReportArguments reportArgs = ReportArguments( + item.dailyReport, + //userApiKey!, + item.title!, + item.sId!, + item.state == "Opened" ? true : false, + //employeePageArguments.isForeman! + ); + Navigator.pushNamed(context, reportDetails.routeName, + arguments: { + 'userArgs': employeePageArguments, + 'dataArgs': reportArgs + }); + }, + leading: CircleAvatar( + backgroundColor: Colors.white, + child: Text( + item.dailyReport.length.toString(), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.blue), + )), + title: Text( + item.title!, + style: const TextStyle(color: Colors.white), + ), + subtitle: Text( + item.body!, + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(10.0), + child: IntrinsicHeight( + child: Column(children: [ + Row(children: [ + Text( + 'KL${item.workNumber!}', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + const VerticalDivider(color: Colors.grey, thickness: 1, width: 30), + Text( + 'Órák: ${workHour * 8} h', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + const VerticalDivider(color: Colors.grey, thickness: 1, width: 30), + Text( + 'PO:${item.poNumber!}', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ]), + const Divider( + thickness: 1.0, + ), + Row( + children: [ + Flexible( + child: Text( + item.dailyReport[0].workTitle!, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ) + ], + ), + const Divider( + thickness: 1.0, + ), + ]))), + /* ButtonBar( + alignment: MainAxisAlignment.start, + children: [ + ElevatedButton.icon( + onPressed: () { + // Perform some action + ReportArguments arg = new ReportArguments( + reportList[index].dailyReport, userApiKey!); + Navigator.pushNamed(context, reportDetails.routeName, + arguments: arg); + }, + icon: const Icon(Icons.mode_edit), + label: const Text('Megnyitás')), + ], + ),*/ + //Image.asset('assets/card-sample-image-2.jpg'), + ], + ), + ); + } +} diff --git a/lib/widgets/reportCreate.dart b/lib/widgets/reportCreate.dart new file mode 100644 index 0000000..cb1db7b --- /dev/null +++ b/lib/widgets/reportCreate.dart @@ -0,0 +1,341 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:mobile_portal_23/classes/common_classes.dart'; +import 'package:mobile_portal_23/models/employee_model.dart'; +import 'package:mobile_portal_23/models/reportListModel.dart'; +import 'dart:convert'; +import 'package:http/http.dart' as http; +import 'package:shared_preferences/shared_preferences.dart'; + +String appDomain = "iotechnic.eu"; + +class ReportCreate extends StatefulWidget { + const ReportCreate({Key? key}) : super(key: key); + static const routeName = '/reportNew'; + @override + State createState() => _ReportCreateState(); +} +late EmployeePageArguments employeePageArguments; +late ReportArguments reportArguments; + +Future> fetchData(String userApiKey) async { + var url = "http://$appDomain/apiemployeelist/$userApiKey"; + http.Response response = await http.get(Uri.parse(url)); + var resp = jsonDecode(response.body); + if (kDebugMode) { + print(resp.toString()); + } + + return resp.map((m) => EmployeeLs.fromJson(m)).toList(); +} + + +class _ReportCreateState extends State { + final _formKey = GlobalKey(); + late List favoriteWorks = []; + bool _isError = false; + bool _isData = false; + final bool _showFaworites = false; + late List workList = []; + // late EmployeeArguments empArgs; + + Future getSharedPrefs() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + // userApiKey = prefs.getString("apiKey"); + favoriteWorks = prefs.getStringList("favoriteWorks") ?? []; + } + + Future fetchdata() async { + var res = await http.get( + + // "http://iotechnic.eu/apiGetAllReport/X8B0PQS-2KYMCV3-G5WD74N-8G0CRAH"); + Uri.parse("http://$appDomain/apiGetAllWork/${employeePageArguments.apiKey!}")); + //"http://192.168.0.144/apiGetDailyReport/X8B0PQS-2KYMCV3-G5WD74N-8G0CRAH"); + if (res.statusCode == 200) { + var obj = json.decode(res.body); + return obj; + } + } + @override + void initState() { + // TODO: implement initState + WidgetsBinding.instance.addPostFrameCallback((_) { + final args = ModalRoute.of(context)!.settings.arguments as Map; + + employeePageArguments = args['userArgs']; + }); + getSharedPrefs().then((value) { + fetchdata().then((data) { + if (data != null) { + for (var item in data) { + // reportList.add(ModelReportList.fromJson(item)); + workList.add(WorkListModel.fromJson(item)); + } + _isData = true; + setState(() { + debugPrint('Fetch ok.'); + }); + } else { + _isError = true; + } + //employeeList = data; + /* empArgs = (ModalRoute.of(context)?.settings.arguments ?? + {}) as EmployeeArguments;*/ + }); + }); + super.initState(); + } + int currentStep = 0; + @override + Widget build(BuildContext context) { + List emp = []; + + /* final arguments = + ModalRoute.of(context)!.settings.arguments as ReportArguments;*/ + final args = ModalRoute + .of(context)! + .settings + .arguments as Map; + + // employeePageArguments = args['userArgs']; + //reportArguments = args['dataArgs']; + return Scaffold( + //resizeToAvoidBottomInset: false, + appBar: AppBar( + title: const Text("Új jelentés"), + actions: [ + IconButton( + icon: const Icon(Icons.search), + onPressed: () {}, + ), + + ], + actionsIconTheme: const IconThemeData( + size: 32, + ), + ), + //drawer: Drawer(), + + body: page(context), + + bottomNavigationBar: BottomAppBar( + color: Colors.cyan, + child: Container( + height: 50.0, + ), + + ) + + ); + //visible: _enableSave, + + + } + + Widget page(BuildContext context) { + + return Container( + padding: const EdgeInsets.all(20), + child: Stepper( + type: StepperType.horizontal, + currentStep: currentStep, + onStepCancel: () => currentStep == 0 + ? null + : setState(() { + currentStep -= 1; + }), + onStepContinue: () { + bool isLastStep = (currentStep == getSteps().length - 1); + if (isLastStep) { + //Do something with this information + } else { + setState(() { + currentStep += 1; + }); + } + }, + onStepTapped: (step) => setState(() { + currentStep = step; + }), + steps: getSteps(), + ), + ); + } + + List getSteps() { + return [ + Step( + state: currentStep > 0 ? StepState.complete : StepState.indexed, + isActive: currentStep >= 0, + title: const Text("Ibrányi"), + content: selectWork(context) + ), + Step( + state: currentStep > 1 ? StepState.complete : StepState.indexed, + isActive: currentStep >= 1, + title: const Text("Address"), + content: const Column( + children: [ + Text("Ibrányi"), + ], + ), + ), + Step( + state: currentStep > 2 ? StepState.complete : StepState.indexed, + isActive: currentStep >= 2, + title: const Text("Misc"), + content: const Column( + children: [ + Text("Ibrányi"), + ], + ), + ), + ]; + } + + + Container selectWork(BuildContext context) { + return _isData == false + ? _isError + ? Container( + child: const AlertDialog( + title: Text('Hálózati Hiba!'), + content: Icon(Icons.error_outline), + elevation: 24, + backgroundColor: Colors.red), + ) + : Container( + child: SizedBox( + height: MediaQuery + .of(context) + .size + .height / 1.3, + width: MediaQuery + .of(context) + .size + .width, + child: const Center( + child: CircularProgressIndicator(), + ), + )) + : gridList(context); + } + + Container gridList(BuildContext context) { + //List _reportList = []; + List workList = []; + if (_showFaworites) { + workList = workList + .where((element) => favoriteWorks.contains(element.sId)) + .toList(); + } else { + workList = workList; + } + return Container( + color: const Color(0xffECF0F1), + child: ListView.builder( + itemCount: workList.length, // The length Of the array + + padding: const EdgeInsets.all(5), + shrinkWrap: true, + + itemBuilder: (context, index) => Container( + child: reportCard(context, workList[index]), + ), + ), + ); + } + Card reportCard(BuildContext context, WorkListModel item) { + int workHour = 0; + bool workOpened = false; + bool isFaworite = favoriteWorks.contains(item.sId); + if (item.state!.contains("Opened")) workOpened = true; + /* item.dailyReport.forEach((element) { + workHour += element.employeeList.length; + });*/ + return Card( + clipBehavior: Clip.antiAlias, + child: Column( + children: [ + ListTile( + tileColor: workOpened ? Colors.indigo : Colors.grey, + trailing: IconButton( + onPressed: () { + if (isFaworite) { + favoriteWorks.remove(item.sId); + } else { + String? id = item.sId; + favoriteWorks.add(id!); + } + // _saveFavoriteWorks(favoriteWorks); + }, + icon: isFaworite + ? Icon( + Icons.star, + color: favoriteWorks.contains(item.sId) + ? Colors.yellow + : null, + ) + : const Icon(Icons.star_border)), + onTap: workOpened + ? () { + ReportArguments reportArgs = ReportArguments( + List.empty(), + //userApiKey!, + item.title!, + item.sId!, + item.state == "Opened" ? true : false, + //empArgs.isForeman! + ); + + /** Navigator.pushNamed(context, reportNew.routeName, + arguments: { + 'userArgs': employeePageArguments, + 'dataArgs': reportArgs + });*/ + } + : null, + title: Text( + item.title!, + style: const TextStyle(color: Colors.white), + ), + /*subtitle: Text( + item.body!, + style: TextStyle(color: Colors.yellow.withOpacity(0.6)), + ),*/ + ), + Padding( + padding: const EdgeInsets.all(10.0), + child: IntrinsicHeight( + child: Column(children: [ + Text( + item.body!.replaceAll("\n", " "), + style: TextStyle(color: Colors.green.withOpacity(0.6)), + ), + const Divider( + thickness: 1.0, + ), + Row(children: [ + + Text( + 'KL${item.workNumber!}', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + const VerticalDivider(color: Colors.grey, thickness: 1, width: 30), + Text( + 'PO: ${item.poNumber!}', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ]), + ]))), + ], + ), + ); + } +} + + diff --git a/lib/widgets/reportDetails.dart b/lib/widgets/reportDetails.dart new file mode 100644 index 0000000..90cd59f --- /dev/null +++ b/lib/widgets/reportDetails.dart @@ -0,0 +1,279 @@ +import 'package:flutter/material.dart'; +import 'package:mobile_portal_23/models/reportListModel.dart'; +import 'package:mobile_portal_23/classes/common_classes.dart'; +import 'package:intl/intl.dart'; +import 'package:intl/date_symbol_data_local.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'package:mobile_portal_23/models/modelEmployee.dart'; +import 'package:mobile_portal_23/widgets/reportNew.dart'; + +class reportDetails extends StatefulWidget { + const reportDetails({Key? key}) : super(key: key); + static const routeName = '/reportDetails'; + @override + State createState() => _reportDetailsState(); +} + +late EmployeePageArguments employeePageArguments; +late ReportArguments reportArguments; +//late String userApiKey = ""; //"X8B0PQS-2KYMCV3-G5WD74N-8G0CRAH"; +bool _isData = false; +List employeeList = []; + +class _reportDetailsState extends State { + String appDomain = "iotechnic.eu"; + + Future fetchEmployeeList() async { + var res = await http.get(Uri.parse("http://$appDomain/apiemployeelist/${employeePageArguments.apiKey!}")); + + if (res.statusCode == 200) { + var obj = json.decode(res.body); + return obj; + } + } + + @override + void initState() { + // TODO: implement initState + super.initState(); + initializeDateFormatting(); + Intl.defaultLocale = "HU"; //sets global, + + WidgetsBinding.instance.addPostFrameCallback((_) { + /*final arguments = (ModalRoute.of(context)?.settings.arguments ?? + {}) as ReportArguments;*/ + //var args = ModalRoute.of(context)!.settings.arguments; + //userApiKey = arguments.userApiKey; // as List; + final args = ModalRoute.of(context)!.settings.arguments as Map; + + employeePageArguments = args['userArgs']; + reportArguments = args['dataArgs']; + + fetchEmployeeList().then((data) { + if (data != null) { + for (var item in data) { + employeeList.add(ModelEmployee.fromJson(item)); + } + _isData = true; + setState(() { + debugPrint('Fetch ok.'); + }); + } + //employeeList = data; + }); + }); + } + + @override + void dispose() { + // TODO: implement dispose + super.dispose(); + } + + @override + Widget build(BuildContext context) { + /* final arguments = (ModalRoute.of(context)?.settings.arguments ?? + {}) as ReportArguments;*/ + final args = ModalRoute.of(context)!.settings.arguments as Map; + + employeePageArguments = args['userArgs']; + reportArguments = args['dataArgs']; + return Scaffold( + appBar: AppBar( + title: const Text("Minden jelentés"), + /* actions: [ + IconButton( + icon: Icon(Icons.search), + onPressed: () {}, + ), + IconButton( + icon: Icon( + Icons.more_vert, + ), + onPressed: () {}, + ) + ],*/ + actionsIconTheme: const IconThemeData( + size: 32, + ), + ), + bottomNavigationBar: reportArguments.state //arguments.state + ? null + : const BottomAppBar( + child: SizedBox( + height: 30, + child: Icon( + Icons.lock, + color: Colors.red, + ))), + //drawer: Drawer(), + body: _isData + ? gridList(context) + : Container( + child: SizedBox( + height: MediaQuery.of(context).size.height / 1.3, + width: MediaQuery.of(context).size.width, + child: const Center( + child: CircularProgressIndicator(), + ), + )), + floatingActionButton: Visibility( + visible: reportArguments.state && employeePageArguments.isForeman!, + child: FloatingActionButton( + onPressed: () => { + Navigator.pushNamed(context, reportNew.routeName, arguments: { + 'userArgs': employeePageArguments, + 'dataArgs': reportArguments + }).then((value) { + setState(() {}); + }), + }, + child: const Icon(Icons.add), + ), + )); + // ); + } +} + +Container gridList(BuildContext context) { + /*final arguments = (ModalRoute.of(context)?.settings.arguments ?? + {}) as ReportArguments;*/ + //var args = ModalRoute.of(context)!.settings.arguments; + List reportDetailsList = + reportArguments.report; //arguments.report; // as List; + return Container( + color: const Color(0xffECF0F1), + child: ListView.builder( + itemCount: reportDetailsList.length, // The length Of the array + + padding: const EdgeInsets.all(5), + shrinkWrap: true, + + /* gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisSpacing: 1, + mainAxisSpacing: 1, + crossAxisCount: 1, + childAspectRatio: (4 / 3), + ), + */ + itemBuilder: (context, index) => Container( + child: reportDetailsCard(context, index), + ), + ), + ); +} + +Card reportDetailsCard(BuildContext context, int index) { + bool workOpened = false; + ModelEmployee findEmployee(String id) => + employeeList.firstWhere((emlement2) => emlement2.sId == id); + //String languageCode = Localizations.localeOf(context).languageCode; + /* final arguments = (ModalRoute.of(context)?.settings.arguments ?? + {}) as ReportArguments;*/ + //var args = ModalRoute.of(context)!.settings.arguments; + List reportDetailsList = + reportArguments.report; //arguments.report; + + String? foreManName = findEmployee(reportDetailsList[index].foremanId!) + .name; //employeeList.where((element) => element.sId == reportDetailsList[index].foremanId!); + + return Card( + clipBehavior: Clip.antiAlias, + child: Column( + children: [ + ListTile( + tileColor: reportArguments.state ? Colors.cyan : Colors.grey, + trailing: employeePageArguments.isForeman! + ? Icon( + Icons.edit, + + // size: 48, + color: reportArguments.state ? Colors.white : Colors.grey, + ) + : null, + onTap: reportArguments.state && employeePageArguments.isForeman! + ? () { + /* ReportArguments arg = new ReportArguments( + reportList[index].dailyReport, userApiKey!); + Navigator.pushNamed(context, reportDetails.routeName, + arguments: arg);*/ + } + : null, + leading: CircleAvatar( + backgroundColor: Colors.white, + child: Text( + //reportDetailsList[index].employeeList.length.toString(), + DateFormat('EE').format(DateFormat('yyyy.MM.dd') + .parse(reportDetailsList[index].date!)), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.blue), + )), + title: Text( + foreManName!, + // reportDetailsList[index].foremanId!, + + style: const TextStyle(color: Colors.white, fontSize: 18), + ), + subtitle: Text( + reportDetailsList[index].date!, + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + ), + + Padding( + padding: const EdgeInsets.all(10.0), + child: IntrinsicHeight( + child: Column(children: [ + Row(children: [ + Flexible( + child: Text( + reportDetailsList[index].workTitle!, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ), + ]), + const Divider( + thickness: 1.0, + ), + Column( + children: reportDetailsList[index] + .employeeList + .map( + (e) => Text( + '• ${findEmployee(e).name!}', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ) + .toList(), + ), + const Divider( + thickness: 1.0, + ), + ]))), + /* ButtonBar( + alignment: MainAxisAlignment.start, + children: [ + ElevatedButton.icon( + onPressed: () { + // Perform some action + + /* Navigator.pushNamed(context, reportDetails.routeName, + arguments: reportList[index]);*/ + }, + icon: const Icon(Icons.mode_edit), + label: const Text('Szerkeszt')), + ], + ),*/ + //Image.asset('assets/card-sample-image-2.jpg'), + ], + ), + ); +} diff --git a/lib/widgets/reportList.dart b/lib/widgets/reportList.dart new file mode 100644 index 0000000..68b0ea0 --- /dev/null +++ b/lib/widgets/reportList.dart @@ -0,0 +1,376 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:http/http.dart' as http; +import 'package:mobile_portal_23/models/reportListModel.dart'; +import 'package:mobile_portal_23/classes/common_classes.dart'; +import 'dart:convert'; +import 'package:mobile_portal_23/widgets/reportCardWidget.dart'; + +class dailyReportList extends StatefulWidget { + const dailyReportList({Key? key}) : super(key: key); + + @override + State createState() => _dailyReportListState(); +} + +late EmployeePageArguments employeePageArguments; +//late EmployeeArguments empArgs; +bool _isError = false; +bool _isData = false; +//String? userApiKey; // = "XST8X8F-6Q9M1FG-PE9948X-SPFHVX9"; +List favoriteWorks = []; +List reportList = []; +bool _showFaworites = false; +Future _saveFavoriteWorks(List favoriteWorks) async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.setStringList('favoriteWorks', favoriteWorks); +} + +Future getSharedPrefs() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + // userApiKey = prefs.getString("apiKey"); + favoriteWorks = prefs.getStringList("favoriteWorks") ?? []; +} + +class _dailyReportListState extends State { + String appDomain = "iotechnic.eu"; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + final args = ModalRoute.of(context)!.settings.arguments as Map; + + employeePageArguments = args['userArgs']; + // empArgs = (ModalRoute.of(context)?.settings.arguments ?? + // {}) as EmployeeArguments; + // getSharedPrefs().then((value) { + fetchdata().then((data) { + if (data != null) { + for (var item in data) { + reportList.add(ModelReportList.fromJson(item)); + } + _isData = true; + setState(() { + debugPrint('Fetch ok.'); + }); + } else { + _isError = true; + } + //employeeList = data; + }); + }); + // }); + } + + @override + void dispose() { + // TODO: implement dispose + super.dispose(); + } + + Future fetchdata() async { + var res = await http.get( + + // "http://iotechnic.eu/apiGetAllReport/X8B0PQS-2KYMCV3-G5WD74N-8G0CRAH"); + Uri.parse("http://$appDomain/apiGetAllReport/${employeePageArguments.apiKey!}")); + //"http://192.168.0.144/apiGetDailyReport/X8B0PQS-2KYMCV3-G5WD74N-8G0CRAH"); + if (res.statusCode == 200) { + var obj = json.decode(res.body); + return obj; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Minden jelentés"), + actions: [ + IconButton( + icon: const Icon(Icons.search), + onPressed: !_showFaworites + ? () { + showSearch( + context: context, + delegate: ReportSearchDelegate(), + ); + setState(() {}); + } + : null, + ), + IconButton( + onPressed: () { + getSharedPrefs(); + _showFaworites = !_showFaworites; + setState(() {}); + }, + icon: _showFaworites + ? const Icon(Icons.star, color: Colors.yellow) + : const Icon(Icons.star_border)), + /* IconButton( + icon: Icon( + Icons.more_vert, + ), + onPressed: () {}, + )*/ + ], + actionsIconTheme: const IconThemeData( + size: 32, + ), + ), + //drawer: Drawer(), + body: page(context)); + } +} + +class ReportSearchDelegate extends SearchDelegate { + @override + Widget? buildLeading(BuildContext context) => IconButton( + onPressed: () => close(context, null), + icon: const Icon(Icons.arrow_back)); + + @override + List? buildActions(BuildContext context) { + // TODO: implement buildActions + IconButton( + onPressed: () { + if (query.isEmpty) { + close(context, null); + } else { + query = ""; + } + }, + icon: const Icon(Icons.clear)); + return null; + } + + @override + Widget buildResults(BuildContext context) { + return Container( + color: const Color(0xffECF0F1), + child: ListView.builder( + itemCount: reportList.length, // The length Of the array + + padding: const EdgeInsets.all(5), + shrinkWrap: true, + + itemBuilder: (context, index) => + Container(child: reportCardWidget(reportList[index]) + //reportCard(context, reportList[index]), + ), + ), + ); + } + + @override + Widget buildSuggestions(BuildContext context) { + // TODO: implement buildSuggestions + List suggestions = reportList.where((element) { + final result = element.title!.toLowerCase(); + final input = query.toLowerCase(); + return result.contains(input); + }).toList(); + return Container( + color: const Color(0xffECF0F1), + child: ListView.builder( + itemCount: suggestions.length, // The length Of the array + + padding: const EdgeInsets.all(5), + shrinkWrap: true, + + itemBuilder: (context, index) => + Container(child: reportCardWidget(suggestions[index]) + //reportCard(context, suggestions[index]), + ), + ), + ); + } +} + +Container page(BuildContext context) { + return _isData == false + ? _isError + ? Container( + child: const AlertDialog( + title: Text('Hálózati Hiba!'), + content: Icon(Icons.error_outline), + elevation: 24, + backgroundColor: Colors.red), + ) + : Container( + child: SizedBox( + height: MediaQuery.of(context).size.height / 1.3, + width: MediaQuery.of(context).size.width, + child: const Center( + child: CircularProgressIndicator(), + ), + )) + : gridList(context); +} + +Container gridList(BuildContext context) { + List reportList = []; + if (_showFaworites) { + reportList = reportList + .where((element) => favoriteWorks.contains(element.sId)) + .toList(); + } else { + reportList = reportList; + } + return Container( + color: const Color(0xffECF0F1), + child: ListView.builder( + itemCount: reportList.length, // The length Of the array + + padding: const EdgeInsets.all(5), + shrinkWrap: true, + + itemBuilder: (context, index) => Container( + child: reportCardWidget( + reportList[index]) //reportCard(context, _reportList[index]), + ), + ), + ); +} +/* +Card reportCard(BuildContext context, ModelReportList item) { + int workHour = 0; + bool workOpened = false; + bool isFaworite = favoriteWorks.contains(item.sId); + if (item.state!.contains("Opened")) workOpened = true; + item.dailyReport.forEach((element) { + workHour += element.employeeList.length; + }); + return Card( + clipBehavior: Clip.antiAlias, + child: Column( + children: [ + Badge( + showBadge: !workOpened, + position: BadgePosition.topEnd(top: 3, end: 3), + badgeContent: Icon(Icons.lock_outlined), + child: ListTile( + tileColor: workOpened ? Colors.blue : Colors.grey, + trailing: IconButton( + onPressed: () { + if (isFaworite) { + favoriteWorks.remove(item.sId); + } else { + String? id = item.sId; + favoriteWorks.add(id!); + } + _saveFavoriteWorks(favoriteWorks); + }, + icon: isFaworite + ? Icon( + Icons.star, + color: favoriteWorks.contains(item.sId) + ? Colors.yellow + : null, + ) + : Icon(Icons.star_border)), + /* Icon( + Icons.keyboard_arrow_right, + // size: 48, + color: Colors.white, + + ),*/ + onTap: () { + ReportArguments reportArgs = new ReportArguments( + item.dailyReport, + //userApiKey!, + item.title!, + item.sId!, + item.state == "Opened" ? true : false, + //employeePageArguments.isForeman! + ); + Navigator.pushNamed(context, reportDetails.routeName, arguments: { + 'userArgs': employeePageArguments, + 'dataArgs': reportArgs + }); + }, + leading: CircleAvatar( + backgroundColor: Colors.white, + child: Text( + item.dailyReport.length.toString(), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.blue), + )), + title: Text( + item.title!, + style: TextStyle(color: Colors.white), + ), + subtitle: Text( + item.body!, + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(10.0), + child: IntrinsicHeight( + child: Column(children: [ + Row(children: [ + Text( + 'KL' + item.workNumber!, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + VerticalDivider(color: Colors.grey, thickness: 1, width: 30), + Text( + 'Órák: ' + (workHour * 8).toString() + ' h', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + VerticalDivider(color: Colors.grey, thickness: 1, width: 30), + Text( + 'PO:' + item.poNumber!, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ]), + Divider( + thickness: 1.0, + ), + Row( + children: [ + Flexible( + child: Text( + item.dailyReport[0].workTitle!, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ) + ], + ), + Divider( + thickness: 1.0, + ), + ]))), + /* ButtonBar( + alignment: MainAxisAlignment.start, + children: [ + ElevatedButton.icon( + onPressed: () { + // Perform some action + ReportArguments arg = new ReportArguments( + reportList[index].dailyReport, userApiKey!); + Navigator.pushNamed(context, reportDetails.routeName, + arguments: arg); + }, + icon: const Icon(Icons.mode_edit), + label: const Text('Megnyitás')), + ], + ),*/ + //Image.asset('assets/card-sample-image-2.jpg'), + ], + ), + ); +}*/ diff --git a/lib/widgets/reportNew.dart b/lib/widgets/reportNew.dart new file mode 100644 index 0000000..7d5d302 --- /dev/null +++ b/lib/widgets/reportNew.dart @@ -0,0 +1,489 @@ +import 'package:flutter/material.dart'; +import 'package:mobile_portal_23/classes/common_classes.dart'; +import 'package:mobile_portal_23/models/employee_model.dart'; +import 'package:mobile_portal_23/models/reportListModel.dart'; +import 'package:intl/intl.dart'; +import 'dart:convert'; +import 'package:http/http.dart' as http; +//import 'package:shared_preferences/shared_preferences.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; + +//String? userApiKey; +TextEditingController dateinput = TextEditingController(); +String appDomain = "iotechnic.eu"; +bool _enableSave = false; +bool _textMessageOK = false; +String textMessage = ""; +late EmployeePageArguments employeePageArguments; +late ReportArguments reportArguments; +/*Future getSharedPrefs() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + userApiKey = prefs.getString("apiKey"); +}*/ + +class reportNew extends StatefulWidget { + const reportNew({Key? key}) : super(key: key); + static const routeName = '/reportNew'; + @override + State createState() => _reportNewState(); +} + +Future fetchReport(BuildContext context) async { + var res = await http.get(Uri.parse("http://$appDomain/apiGetAllReport/${employeePageArguments.apiKey!}")); + if (res.statusCode == 200) { + var data = json.decode(res.body); + //return obj; + if (data != null) { + for (var item in data) { + ModelReportList mr = ModelReportList.fromJson(item); + + if (mr.sId == reportArguments.workId) { + reportArguments.report.clear(); + reportArguments.report.addAll(mr.dailyReport); + } + } + + //employeeList = data; + } + } +} + +Future fetchEmployeeData(String userApiKey) async { + var res = await http.get( + + // "http://iotechnic.eu/apiemployeelist/X8B0PQS-2KYMCV3-G5WD74N-8G0CRAH"); + Uri.parse("http://$appDomain/apiGetDailyReport/$userApiKey")); + //"http://192.168.0.144/apiGetDailyReport/X8B0PQS-2KYMCV3-G5WD74N-8G0CRAH"); + if (res.statusCode == 200) { + var obj = json.decode(res.body); + return obj; + } else { + throw ("ERROR"); + } +} + +Future> fetchData(String userApiKey) async { + var url = "http://$appDomain/apiemployeelist/$userApiKey"; + http.Response response = await http.get(Uri.parse(url)); + var resp = jsonDecode(response.body); + print(resp.toString()); + + return resp.map((m) => EmployeeLs.fromJson(m)).toList(); +} + +Future postDailyReport(var json) async { + final response = await http.post( + Uri.parse("http://$appDomain/apiPostDailyReport/${employeePageArguments.apiKey!}"), + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + }, + body: jsonEncode(json), + ); + + if (response.statusCode == 200) { + // If the server did return a 201 CREATED response, + // then parse the JSON. + return response.body; + } else { + // If the server did not return a 201 CREATED response, + // then throw an exception. + throw Exception('Failed to create album.'); + } +} + +_saveForm(BuildContext context, ReportArguments arguments, + List employeeList) { + EasyLoading.show(status: 'Mentés...'); + // _onLoading2(); + print("SUBMITTED!"); + print(dateinput.text); + print("Title: ${arguments.workName}"); + print("WorkId: ${arguments.workId}"); + print("Message: $textMessage"); + List emp = []; + //print("Jelenlévők: "); + for (var hobby in employeeList) { + if (hobby.value == true) { + //print(hobby.name); + emp.add(hobby.id); + } + } + var report = { + 'userApiKey': employeePageArguments.apiKey, + 'workId': arguments.workId, + 'date': dateinput.text, + 'msg': textMessage, + 'employeeList': emp + }; + //print(json.encode(report)); + postDailyReport(report).then((value) { + if (value.contains("OK")) { + EasyLoading.showSuccess('Sikeres Mentés'); + fetchReport(context).then((value) { + Future.delayed(const Duration(seconds: 3), () { + // Navigator.pop(context); //pop dialog + EasyLoading.dismiss(); + + Navigator.of(context).pop(); + //_login(); + }); + }); + } + }); +} + +class _reportNewState extends State { + final _formKey = GlobalKey(); + List employeeList = []; + bool _isData = false; + @override + void initState() { + // TODO: implement initState + // getSharedPrefs().then((args) { + WidgetsBinding.instance.addPostFrameCallback((_) { + /*final arguments = (ModalRoute.of(context)?.settings.arguments ?? + {}) as ReportArguments;*/ + //var args = ModalRoute.of(context)!.settings.arguments; + //userApiKey = arguments.userApiKey; // as List; + final args = ModalRoute.of(context)!.settings.arguments as Map; + + employeePageArguments = args['userArgs']; + reportArguments = args['dataArgs']; + fetchData(employeePageArguments.apiKey!).then((value) { + setState(() { + employeeList.addAll(value); + _isData = true; + }); + }); + }); + // }); + dateinput.text = DateFormat('yyyy.MM.dd').format(DateTime.now()); + //set output date to TextField value + super.initState(); + } + + checkize() { + _enableSave = false; + for (var item in employeeList) { + if (item.value == true) { + setState(() { + if (_textMessageOK) _enableSave = true; + }); + + print("Save Enabled"); + } + } + } + + Widget _buildLastName() { + return Form( + key: _formKey, + //margin: const EdgeInsets.only(left: 40), + child: TextFormField( + validator: (value) { + if (value == null || value.isEmpty) { + textMessage = ""; + _textMessageOK = false; + checkize(); + return 'A mező nem lehet üres!'; + } + if (value.length < 3) { + textMessage = ""; + _textMessageOK = false; + checkize(); + return "Legalább 3 karakter!"; + } else { + textMessage = value; + _textMessageOK = true; + checkize(); + return null; + } + }, + onChanged: (_) { + _formKey.currentState!.validate(); + }, + onFieldSubmitted: (_) { + _formKey.currentState!.validate(); + }, + style: const TextStyle( + color: Color.fromARGB(255, 3, 163, 29), + fontFamily: 'RadikalLight'), + decoration: //_buildInputDecoration("Last name", ''), + const InputDecoration(labelText: 'Munka leírása'))); + } + + Container gridList(BuildContext context) { + //_enableSave = false; + + return Container( + color: Colors.white, + child: ListView.builder( + itemCount: employeeList.length, // The length Of the array + + padding: const EdgeInsets.all(5), + shrinkWrap: true, + + itemBuilder: (context, index) => Container( + child: employeeWidget(context, index), + ), + ), + ); + } + + SwitchListTile employeeWidget(BuildContext context, index) { + return SwitchListTile( + //controlAffinity: ListTileControlAffinity.trailing, + activeColor: Colors.green, + value: employeeList[index].value, + title: Text(employeeList[index].name), + onChanged: (newValue) { + setState(() { + employeeList[index].value = newValue; + checkize(); + }); + }); + } + + @override + void dispose() { + // TODO: implement dispose + super.dispose(); + } + + @override + Widget build(BuildContext context) { + List emp = []; + /* final arguments = + ModalRoute.of(context)!.settings.arguments as ReportArguments;*/ + final args = ModalRoute.of(context)!.settings.arguments as Map; + + employeePageArguments = args['userArgs']; + reportArguments = args['dataArgs']; + //{}) as ReportArguments; + return Scaffold( + //resizeToAvoidBottomInset: false, + appBar: AppBar( + title: const Text("Új jelentés"), + actions: [ + IconButton( + icon: const Icon(Icons.search), + onPressed: () {}, + ), + /* IconButton( + icon: Icon( + Icons.save, + //color: Colors.white, + ), + onPressed: _enableSave + ? () { + print("Save"); + } + : null)*/ + ], + actionsIconTheme: const IconThemeData( + size: 32, + ), + ), + //drawer: Drawer(), + + body: SingleChildScrollView( + child: page(context), + ), + bottomNavigationBar: BottomAppBar( + color: Colors.cyan, + child: Container( + height: 50.0, + ), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, + floatingActionButton: Visibility( + visible: _enableSave, + child: FloatingActionButton( + onPressed: _enableSave + ? () => { + if (_formKey.currentState!.validate()) + { + /* print("SUBMITTED!"), + print(dateinput.text), + print("Title: " + arguments.workName), + print("WorkId: " + arguments.workId), + print("Message: " + textMessage), + + //print("Jelenlévők: "); + for (var hobby in employeeList) + { + if (hobby.value == true) + { + //print(hobby.name); + emp.add(hobby.id), + } + }, + print(emp),*/ + // TODO submit + + _saveForm(context, reportArguments, employeeList) + } + // Navigator.pushNamed(context, reportNew.routeName, + // arguments: arguments), + } + : null, + child: const Icon(Icons.save), + ), + )); + } + + Widget page(BuildContext context) { + /* final arguments = (ModalRoute.of(context)?.settings.arguments ?? + {}) as ReportArguments;*/ + final args = ModalRoute.of(context)!.settings.arguments as Map; + + employeePageArguments = args['userArgs']; + reportArguments = args['dataArgs']; + List reportDetailsList = + reportArguments.report; // as List; + String textMessage = ""; + + DateTime selectedDate = DateTime.now(); + return Container( + child: Column(children: [ + Card( + clipBehavior: Clip.antiAlias, + child: Column( + children: [ + ListTile( + tileColor: Colors.cyan, + /* trailing: IconButton( + color: Colors.green, + icon: Icon( + Icons.save, + //color: Colors.white, + ), + onPressed: _enableSave + ? () { + print("Save"); + } + : null),*/ + /* ReportArguments arg = new ReportArguments( + reportList[index].dailyReport, userApiKey!); + Navigator.pushNamed(context, reportDetails.routeName, + arguments: arg);*/ + + title: Text( + reportArguments.workName, + // reportDetailsList[index].foremanId!, + + style: const TextStyle(color: Colors.white, fontSize: 18), + ), + /*subtitle: Text( + "Date", + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ),*/ + ), + Padding( + padding: const EdgeInsets.all(10.0), + child: IntrinsicHeight( + child: Column(children: [ + /* Row(children: [ + Flexible( + child: Text( + arguments.workName, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ), + ]), + Divider( + thickness: 1.0, + ),*/ + Container( + padding: const EdgeInsets.only(left: 10, right: 10), + child: Center( + child: TextField( + //enabled: datePickerEnabled, + controller: + dateinput, //editing controller of this TextField + decoration: const InputDecoration( + icon: + Icon(Icons.calendar_today), //icon of text field + labelText: "Jelentés Dátuma" //label text of field + ), + readOnly: + true, //set it true, so that user will not able to edit text + onTap: () async { + DateTime? pickedDate = await showDatePicker( + context: context, + initialDate: DateTime.now(), + firstDate: DateTime( + 2020), //DateTime.now() - not to allow to choose before today. + lastDate: DateTime.now()); + + if (pickedDate != null) { + //print(pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000 + String formattedDate = + DateFormat('yyyy.MM.dd').format(pickedDate); + //print(formattedDate); //formatted date output using intl package => 2021-03-16 + //you can implement different kind of Date Format here according to your requirement + + // setState(() { + //textMessageEnabled = true; + selectedDate = pickedDate; + + dateinput.text = + formattedDate; //set output date to TextField value. + // }); + } else { + print("Nem választott dátumot!"); + } + }, + )), + ), + Container( + padding: const EdgeInsets.only(top: 20, left: 15, right: 15), + //margin: const EdgeInsets.only(left: 5, right: 5), + height: 100, + //child: Padding( + //padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16), + + child: _buildLastName(), + ), + const Divider( + thickness: 1.0, + ), + SizedBox( + height: 300, + child: _isData + ? gridList(context) + : SizedBox( + height: + MediaQuery.of(context).size.height / 1.3, + width: MediaQuery.of(context).size.width, + child: const Center( + child: CircularProgressIndicator(), + ), + )) + //ListView(children: UsersUi(arguments.userApiKey)), + + /*new Column( + children: reportDetailsList[index] + .employeeList + .map( + (e) => new Text( + '• ' + findEmployee(e).name!, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.black.withOpacity(0.6)), + ), + ) + .toList(), + ), + */ + ]))), + ], + ), + ), + ]), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..5c8e095 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,119 @@ +name: mobile_portal_23 +description: "Mobile Portal 2023" +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: '>=3.2.0 <4.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + device_info_plus: ^9.1.1 + flutter_secure_storage: ^9.0.0 + http: ^0.13.0 + flutter_easyloading: ^3.0.3 + location: ^5.0.3 + nfc_manager: ^3.3.0 + hex: ^0.2.0 + geolocator: ^10.1.0 + geocoding: ^2.1.1 + expandable: ^5.0.1 + intl: ^0.18.1 + jiffy: ^6.2.1 + dropdown_search: ^5.0.6 + badges: ^3.1.2 + shared_preferences: ^2.2.2 + simple_barcode_scanner: ^0.1.0 + #mobile_scanner: ^3.5.7 + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + flutter_native_splash: ^2.3.9 + package_info_plus: ^5.0.1 + qr_flutter: ^4.1.0 + +dev_dependencies: + flutter_launcher_icons: ^0.13.1 + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +flutter_icons: + android: true + ios: true + image_path: "assets/cloudlogo.png" + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec +flutter_native_splash: + color: "#9ae79a" + image: assets/cloudlogo.png +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + assets: + - assets/cloudlogo.png + - assets/nfc.png + - assets/nfcdenied.png + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..6e6640b --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:mobile_portal_23/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +}