commit 6c69efd3eaff3b291a48f743a38d9d23da181eb0 Author: Vincze József Date: Thu May 16 16:03:54 2024 +0200 First Commit 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 0000000..925b1e3 Binary files /dev/null and b/assets/cloudlogo.png differ diff --git a/assets/nfc.png b/assets/nfc.png new file mode 100644 index 0000000..178003d Binary files /dev/null and b/assets/nfc.png differ diff --git a/assets/nfcdenied.png b/assets/nfcdenied.png new file mode 100644 index 0000000..28aff4b Binary files /dev/null and b/assets/nfcdenied.png differ 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); + }); +}