[
  {
    "path": ".gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# Environment Variable\n.env\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n**/doc/api/\n**/ios/Flutter/.last_build_id\n.dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n.packages\n.pub-cache/\n.pub/\n/build/\n\n# Web related\nlib/generated_plugin_registrant.dart\n\n# Symbolication related\napp.*.symbols\n\n# Obfuscation related\napp.*.map.json\n"
  },
  {
    "path": ".metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: 8874f21e79d7ec66d0457c7ab338348e31b17f1d\n  channel: stable\n\nproject_type: app\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n    <img src=\"https://i.ibb.co/jGgBj1n/icon.png\" height=\"200\"/>\n</p>\n\n<h1 align=\"center\">SiAP</h1>\n\nAplikasi Android untuk manajemen data presensi pegawai di lingkungan kantor pemerintahan Kecamatan Balaesang. Aplikasi ini dibangun dengan [Flutter](https://flutter.dev)\n\n[![SIAP](https://i.ibb.co/Xz9Dppd/thumb.png)](https://play.google.com/store/apps/details?id=com.banuacoders.siap)\n\n## About\n\nAplikasi ini dibangun untuk mengatasi permasalahan pencatatan absensi pegawai di lingkungan kantor pemerintahan Kecamatan Balaesang. Pencatatan kehadiran pegawai di kantor pemerintahan Kecamatan Balaesang selama ini masih dilakukan secara manual yaitu dengan memberi paraf pada absensi.\n\nPermasalahan timbul saat sebagian besar pegawai tidak jujur dalam mengisi absen tersebut, ada yang titip ke teman untuk diparaf namanya, ada yang langsung isi absen sampai beberapa hari ke depan, ada yang mengisi absen diluar waktunya, dsb. Dengan adanya sistem ini, diharapkan bisa membantu mengatasi permasalahan-permasalahan yang telah disebutkan.\n\n## License\n\n**SiAP** is open-sourced software licensed under the [GPL v2.0](https://www.gnu.org/licenses/gpl-2.0.html).\n\n## Konfigurasi\n\n*Clone* repository back-end aplikasinya [disini](https://github.com/ryanaidilp/sistem_absensi_pegawai). Buat file **.env** pada root folder aplikasi ini lalu tambahkan variabel berikut\n\n```dotenv\n    BASE_URL=BASE URL ONLINE\n    LOCAL_URL=LOCAL_URL #Jika kamu ingin menyambungkian ke server di localhost\n    ONE_SIGNAL_APP_ID=APP ID UNTUK ONE SIGNAL\n    ADMIN_PHONE_NUMBER=Nomor handphone admin server\n```\n\nIsikan variabel sesuai dengan konfigurasi anda.\n\nUntuk mendapatkan `ONE_SIGNAL_APP_ID`, buat akun di [One Signal](https://app.onesignal.com) lalu ikuti petunjuk cara untuk mendapatkan `APP_KEY` melalui dokumentasi resmi One Signal.\nPastikan `ONE_SIGNAL_APP_ID` pada aplikasi ini sama dengan yang digunakan di aplikasi [backend](https://github.com/ryanaidilp/sistem_absensi_pegawai)\n\n## Screenshoot\n\n![Splash Screen](https://i.ibb.co/p4n5K3D/splash-screen.gif)![Login Screen](https://i.ibb.co/5TmZYTT/login-screen.gif)![Home Screen](https://i.ibb.co/qyJB11s/home-screen.gif)![Presence Screen](https://i.ibb.co/SVGrL5r/presence-screen.gif)![Presence Process Screen](https://i.ibb.co/6H8YmB9/presence-process-screen.gif)![Employee List Screen](https://i.ibb.co/4dGZXR3/employee-list-screen.gif)![Statistics Screen](https://i.ibb.co/5n2gGzc/statistics-screen.gif)![Statistics Screen](https://i.ibb.co/RYtT7gH/statistics-screen-2.gif)![Absent Permissions Screen](https://i.ibb.co/XSFKdX6/absent-permission-screen.gif)![Outstations Screen](https://i.ibb.co/VqSvMQF/outstation-screen.gif)![Paid Leaves Screen](https://i.ibb.co/zV6m0C8/paid-leave-screen.gif)![Employee Presence Screen](https://i.ibb.co/KXMyMq6/employee-presence-list.gif)![Employee Presence List Screen](https://i.ibb.co/bN1cBnJ/employee-presence-screen-2.gif)![Notifications List & Create Notification Screen](https://i.ibb.co/NtH4pbT/notification-list-screen.gif)\n"
  },
  {
    "path": "analysis_options.yaml",
    "content": "include: package:lint/analysis_options.yaml"
  },
  {
    "path": "android/.gitignore",
    "content": "gradle-wrapper.jar\n/.gradle\n/captures/\n/gradlew\n/gradlew.bat\n/local.properties\nGeneratedPluginRegistrant.java\n\n# Remember to never publicly share your keystore.\n# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app\nkey.properties\n"
  },
  {
    "path": "android/app/build.gradle",
    "content": "def localProperties = new Properties()\ndef localPropertiesFile = rootProject.file('local.properties')\nif (localPropertiesFile.exists()) {\n    localPropertiesFile.withReader('UTF-8') { reader ->\n        localProperties.load(reader)\n    }\n}\n\ndef flutterRoot = localProperties.getProperty('flutter.sdk')\nif (flutterRoot == null) {\n    throw new GradleException(\"Flutter SDK not found. Define location with flutter.sdk in the local.properties file.\")\n}\n\ndef flutterVersionCode = localProperties.getProperty('flutter.versionCode')\nif (flutterVersionCode == null) {\n    flutterVersionCode = '1'\n}\n\ndef flutterVersionName = localProperties.getProperty('flutter.versionName')\nif (flutterVersionName == null) {\n    flutterVersionName = '1.0'\n}\n\napply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply from: \"$flutterRoot/packages/flutter_tools/gradle/flutter.gradle\"\napply plugin: 'com.onesignal.androidsdk.onesignal-gradle-plugin'\napply from: project(':flutter_config').projectDir.getPath() + \"/dotenv.gradle\"\n\ndef keystoreProperties = new Properties()\ndef keystorePropertiesFile = rootProject.file('key.properties')\nif(keystorePropertiesFile.exists()) {\n    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))\n}\n\nandroid {\n    compileSdkVersion 30\n\n    sourceSets {\n        main.java.srcDirs += 'src/main/kotlin'\n    }\n\n    lintOptions {\n        disable 'InvalidPackage'\n    }\n\n    defaultConfig {\n        applicationId \"com.banuacoders.siap\"\n        minSdkVersion 24\n        targetSdkVersion 30\n        versionCode 46\n        versionName '5.0.3'\n    }\n\n    signingConfigs {\n        release {\n            keyAlias keystoreProperties['keyAlias']\n            keyPassword keystoreProperties['keyPassword']\n            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null\n            storePassword keystoreProperties['storePassword']\n        }\n    }\n\n    buildTypes {\n        release {\n            signingConfig signingConfigs.release\n        }\n    }\n}\n\nflutter {\n    source '../..'\n}\n\ndependencies {\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n}\n"
  },
  {
    "path": "android/app/proguard-rules.pro",
    "content": "-keep class com.banuacoders.siap.BuildConfig{ *; }\n## Flutter wrapper\n-keep class io.flutter.app.** { *; }\n-keep class io.flutter.plugin.**  { *; }\n-keep class io.flutter.util.**  { *; }\n-keep class io.flutter.view.**  { *; }\n-keep class io.flutter.**  { *; }\n-keep class io.flutter.plugins.**  { *; }\n-dontwarn io.flutter.embedding.**\n\n## Gson rules\n# Gson uses generic type information stored in a class file when working with fields. Proguard\n# removes such information by default, so configure it to keep all of it.\n-keepattributes Signature\n\n# For using GSON @Expose annotation\n-keepattributes *Annotation*\n\n# Gson specific classes\n-dontwarn sun.misc.**\n#-keep class com.google.gson.stream.** { *; }\n\n# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,\n# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)\n-keep class * extends com.google.gson.TypeAdapter\n-keep class * implements com.google.gson.TypeAdapterFactory\n-keep class * implements com.google.gson.JsonSerializer\n-keep class * implements com.google.gson.JsonDeserializer\n\n# Prevent R8 from leaving Data object members always null\n-keepclassmembers,allowobfuscation class * {\n  @com.google.gson.annotations.SerializedName <fields>;\n}"
  },
  {
    "path": "android/app/src/debug/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.banuacoders.siap\">\n    <!-- Flutter needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "android/app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.banuacoders.siap\">\n    <!-- io.flutter.app.FlutterApplication is an android.app.Application that\n         calls FlutterMain.startInitialization(this); in its onCreate method.\n         In most cases you can leave this as-is, but you if you want to provide\n         additional functionality it is fine to subclass or reimplement\n         FlutterApplication and put your custom class here. -->\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" />\n    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n\n    <application\n        android:name=\"io.flutter.app.FlutterApplication\"\n        android:requestLegacyExternalStorage=\"true\"\n        android:label=\"SiAP Balaesang\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:icon=\"@mipmap/ic_launcher\">\n        <activity\n            android:name=\".MainActivity\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/LaunchTheme\"\n            android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode\"\n            android:hardwareAccelerated=\"true\"\n            android:showWhenLocked=\"true\"\n            android:turnScreenOn=\"true\"\n            android:windowSoftInputMode=\"adjustResize\">\n            <!-- Specifies an Android theme to apply to this Activity as soon as\n                 the Android process has started. This theme is visible to the user\n                 while the Flutter UI initializes. After that, this theme continues\n                 to determine the Window background behind the Flutter UI. -->\n            <meta-data\n              android:name=\"io.flutter.embedding.android.NormalTheme\"\n              android:resource=\"@style/NormalTheme\"\n              />\n            <!-- Displays an Android View that continues showing the launch screen\n                 Drawable until Flutter paints its first frame, then this splash\n                 screen fades out. A splash screen is useful to avoid any visual\n                 gap between the end of Android's launch screen and the painting of\n                 Flutter's first frame. -->\n            <meta-data\n              android:name=\"io.flutter.embedding.android.SplashScreenDrawable\"\n              android:resource=\"@drawable/launch_background\"\n              />\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n        <!-- Don't delete the meta-data below.\n             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->\n        <meta-data\n            android:name=\"flutterEmbedding\"\n            android:value=\"2\" />\n    </application>\n</manifest>\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/banuacoders/siap/MainActivity.kt",
    "content": "package com.banuacoders.siap\n\nimport io.flutter.embedding.android.FlutterActivity\n\nimport android.os.Build\nimport android.view.ViewTreeObserver\nimport android.view.WindowManager\nclass MainActivity: FlutterActivity()"
  },
  {
    "path": "android/app/src/main/res/drawable/gradient_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <gradient\n        android:startColor=\"@color/splash_color\"\n        android:angle=\"90\"\n        android:endColor=\"@color/end_color\"/>\n</shape>"
  },
  {
    "path": "android/app/src/main/res/drawable/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/gradient_background\" />\n\n    <!-- You can insert your own image assets here -->\n    <!-- <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@mipmap/launch_image\" />\n    </item> -->\n    <item>\n        <bitmap android:gravity=\"center\" android:src=\"@drawable/splash\" />\n    </item>\n\n</layer-list>\n"
  },
  {
    "path": "android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <background android:drawable=\"@drawable/ic_launcher_background\"/>\n  <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>\n"
  },
  {
    "path": "android/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"splash_color\">#0C2979</color>\n    <color name=\"end_color\">#71A7F8</color>\n</resources>"
  },
  {
    "path": "android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             Flutter draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n         \n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <item name=\"android:windowBackground\">@android:color/white</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/app/src/profile/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.banuacoders.siap\">\n    <!-- Flutter needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "android/build.gradle",
    "content": "buildscript {\n    ext.kotlin_version = '1.3.50'\n    repositories {\n        google()\n        jcenter()\n        maven { url 'https://plugins.gradle.org/m2/' }\n    }\n\n    dependencies {\n        classpath 'com.android.tools.build:gradle:3.5.0'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n        classpath 'gradle.plugin.com.onesignal:onesignal-gradle-plugin:[0.12.9, 0.99.99]'\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        jcenter()\n    }\n}\n\nrootProject.buildDir = '../build'\nsubprojects {\n    project.buildDir = \"${rootProject.buildDir}/${project.name}\"\n}\nsubprojects {\n    project.evaluationDependsOn(':app')\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "android/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Fri Jun 23 08:50:38 CEST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-5.6.2-all.zip\n"
  },
  {
    "path": "android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx1536M\nandroid.useAndroidX=true\nandroid.enableJetifier=true\nandroid.enableR8=true\n"
  },
  {
    "path": "android/settings.gradle",
    "content": "include ':app'\n\ndef localPropertiesFile = new File(rootProject.projectDir, \"local.properties\")\ndef properties = new Properties()\n\nassert localPropertiesFile.exists()\nlocalPropertiesFile.withReader(\"UTF-8\") { reader -> properties.load(reader) }\n\ndef flutterSdkPath = properties.getProperty(\"flutter.sdk\")\nassert flutterSdkPath != null, \"flutter.sdk not set in local.properties\"\napply from: \"$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle\"\n"
  },
  {
    "path": "android/settings_aar.gradle",
    "content": "include ':app'\n"
  },
  {
    "path": "ios/.gitignore",
    "content": "*.mode1v3\n*.mode2v3\n*.moved-aside\n*.pbxuser\n*.perspectivev3\n**/*sync/\n.sconsign.dblite\n.tags*\n**/.vagrant/\n**/DerivedData/\nIcon?\n**/Pods/\n**/.symlinks/\nprofile\nxcuserdata\n**/.generated/\nFlutter/App.framework\nFlutter/Flutter.framework\nFlutter/Flutter.podspec\nFlutter/Generated.xcconfig\nFlutter/app.flx\nFlutter/app.zip\nFlutter/flutter_assets/\nFlutter/flutter_export_environment.sh\nServiceDefinitions.json\nRunner/GeneratedPluginRegistrant.*\n\n# Exceptions to above rules.\n!default.mode1v3\n!default.mode2v3\n!default.pbxuser\n!default.perspectivev3\n"
  },
  {
    "path": "ios/Flutter/AppFrameworkInfo.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n  <key>CFBundleDevelopmentRegion</key>\n  <string>$(DEVELOPMENT_LANGUAGE)</string>\n  <key>CFBundleExecutable</key>\n  <string>App</string>\n  <key>CFBundleIdentifier</key>\n  <string>io.flutter.flutter.app</string>\n  <key>CFBundleInfoDictionaryVersion</key>\n  <string>6.0</string>\n  <key>CFBundleName</key>\n  <string>App</string>\n  <key>CFBundlePackageType</key>\n  <string>FMWK</string>\n  <key>CFBundleShortVersionString</key>\n  <string>1.0</string>\n  <key>CFBundleSignature</key>\n  <string>????</string>\n  <key>CFBundleVersion</key>\n  <string>1.0</string>\n  <key>MinimumOSVersion</key>\n  <string>9.0</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Flutter/Debug.xcconfig",
    "content": "#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "ios/Flutter/Release.xcconfig",
    "content": "#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "ios/Runner/AppDelegate.swift",
    "content": "import UIKit\nimport Flutter\n\n@UIApplicationMain\n@objc class AppDelegate: FlutterAppDelegate {\n  override func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -> Bool {\n    var flutter_native_splash = 1\n    UIApplication.shared.isStatusBarHidden = false\n\n    GeneratedPluginRegistrant.register(with: self)\n    return super.application(application, didFinishLaunchingWithOptions: launchOptions)\n  }\n}"
  },
  {
    "path": "ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"83.5x83.5\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-83.5x83.5@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"1024x1024\",\n      \"idiom\" : \"ios-marketing\",\n      \"filename\" : \"Icon-App-1024x1024@1x.png\",\n      \"scale\" : \"1x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md",
    "content": "# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images."
  },
  {
    "path": "ios/Runner/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"12121\" systemVersion=\"16G29\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"12089\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"Ydg-fD-yQy\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"xbc-2k-c8Z\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <imageView opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" image=\"LaunchImage\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"YRO-k0-Ey4\">\n                            </imageView>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"0.4196078431372549\" green=\"0.5568627450980392\" blue=\"0.9372549019607843\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerX\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerX\" id=\"1a2-6s-vTC\"/>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerY\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerY\" id=\"4X2-HB-R7a\"/>\n                        </constraints>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"LaunchImage\" width=\"512\" height=\"512\"/>\n    </resources>\n</document>"
  },
  {
    "path": "ios/Runner/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"10117\" systemVersion=\"15F34\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"10085\"/>\n    </dependencies>\n    <scenes>\n        <!--Flutter View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"FlutterViewController\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"y3c-jy-aDJ\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"wfy-db-euE\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"600\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"calibratedWhite\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "ios/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>spo_balaesang</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIViewControllerBasedStatusBarAppearance</key>\n\t<false/>\n\t<key>UIStatusBarHidden</key>\n\t<true/>\n\n</dict>\n</plist>"
  },
  {
    "path": "ios/Runner/Runner-Bridging-Header.h",
    "content": "#import \"GeneratedPluginRegistrant.h\"\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };\n\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };\n\t\t74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };\n\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };\n\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };\n\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t9705A1C41CF9048500538489 /* Embed Frameworks */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Embed Frameworks\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = \"<group>\"; };\n\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = \"<group>\"; };\n\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = \"<group>\"; };\n\t\t74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"Runner-Bridging-Header.h\"; sourceTree = \"<group>\"; };\n\t\t74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = \"<group>\"; };\n\t\t97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t97C146EB1CF9000F007C117D /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t9740EEB11CF90186004384FC /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */,\n\t\t\t);\n\t\t\tname = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146E51CF9000F007C117D = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9740EEB11CF90186004384FC /* Flutter */,\n\t\t\t\t97C146F01CF9000F007C117D /* Runner */,\n\t\t\t\t97C146EF1CF9000F007C117D /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146EF1CF9000F007C117D /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146EE1CF9000F007C117D /* Runner.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146F01CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146FA1CF9000F007C117D /* Main.storyboard */,\n\t\t\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */,\n\t\t\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,\n\t\t\t\t97C147021CF9000F007C117D /* Info.plist */,\n\t\t\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,\n\t\t\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,\n\t\t\t\t74858FAE1ED2DC5600515810 /* AppDelegate.swift */,\n\t\t\t\t74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t97C146ED1CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t9740EEB61CF901F6004384FC /* Run Script */,\n\t\t\t\t97C146EA1CF9000F007C117D /* Sources */,\n\t\t\t\t97C146EB1CF9000F007C117D /* Frameworks */,\n\t\t\t\t97C146EC1CF9000F007C117D /* Resources */,\n\t\t\t\t9705A1C41CF9048500538489 /* Embed Frameworks */,\n\t\t\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 97C146EE1CF9000F007C117D /* Runner.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t97C146E61CF9000F007C117D /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 1020;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t97C146ED1CF9000F007C117D = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 7.3.1;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 97C146E51CF9000F007C117D;\n\t\t\tproductRefGroup = 97C146EF1CF9000F007C117D /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t97C146ED1CF9000F007C117D /* Runner */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t97C146EC1CF9000F007C117D /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,\n\t\t\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,\n\t\t\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Thin Binary\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" embed_and_thin\";\n\t\t};\n\t\t9740EEB61CF901F6004384FC /* Run Script */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Run Script\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" build\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t97C146EA1CF9000F007C117D /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,\n\t\t\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t97C146FA1CF9000F007C117D /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146FB1CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C147001CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t249021D3217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 9.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t249021D4217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.banuacoders.siapesang;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t97C147031CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 9.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147041CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 9.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Owholemodule\";\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t97C147061CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.banuacoders.siapesang;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147071CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Flutter\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.banuacoders.siapesang;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147031CF9000F007C117D /* Debug */,\n\t\t\t\t97C147041CF9000F007C117D /* Release */,\n\t\t\t\t249021D3217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147061CF9000F007C117D /* Debug */,\n\t\t\t\t97C147071CF9000F007C117D /* Release */,\n\t\t\t\t249021D4217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 97C146E61CF9000F007C117D /* Project object */;\n}"
  },
  {
    "path": "ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1020\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n               BuildableName = \"Runner.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "ios/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "lib/main.dart",
    "content": "import 'dart:math';\nimport 'dart:typed_data';\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_config/flutter_config.dart';\nimport 'package:flutter_local_notifications/flutter_local_notifications.dart';\nimport 'package:get/get.dart';\nimport 'package:intl/date_symbol_data_local.dart';\nimport 'package:intl/intl.dart';\nimport 'package:onesignal_flutter/onesignal_flutter.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/network/api.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/splash_screen.dart';\nimport 'package:timezone/data/latest.dart' as tz;\nimport 'package:timezone/timezone.dart' as tz;\n\nimport 'network/api_service.dart';\n\nfinal FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =\n    FlutterLocalNotificationsPlugin();\n\nFuture<void> main() async {\n  await initializeDateFormatting();\n  WidgetsFlutterBinding.ensureInitialized();\n  await FlutterConfig.loadEnvVariables();\n  Intl.defaultLocale = 'id_ID';\n  OneSignal.shared.setLogLevel(OSLogLevel.verbose, OSLogLevel.none);\n  OneSignal.shared.setLocationShared(true);\n  OneSignal.shared.init(FlutterConfig.get(\"ONE_SIGNAL_APP_ID\").toString(),\n      iOSSettings: {\n        OSiOSSettings.autoPrompt: false,\n        OSiOSSettings.inAppLaunchUrl: false\n      });\n  OneSignal.shared\n      .setInFocusDisplayType(OSNotificationDisplayType.notification);\n\n  const initializedSettingsAndroid =\n      AndroidInitializationSettings('ic_stat_onesignal_default');\n  const initializationSettings =\n      InitializationSettings(android: initializedSettingsAndroid);\n  await flutterLocalNotificationsPlugin.initialize(initializationSettings);\n\n  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])\n      .then((_) {\n    runApp(MyApp());\n  });\n}\n\nclass MyApp extends StatefulWidget {\n  @override\n  _MyAppState createState() => _MyAppState();\n}\n\nclass _MyAppState extends State<MyApp> {\n  @override\n  Widget build(BuildContext context) {\n    return Provider(\n      create: (_) => DataRepository(apiService: ApiService(api: API())),\n      child: GetMaterialApp(\n          debugShowCheckedModeBanner: false,\n          title: 'SIAP Balaesang',\n          theme: ThemeData(\n              primarySwatch: Colors.blue,\n              backgroundColor: Colors.white,\n              scaffoldBackgroundColor: Colors.grey[100],\n              appBarTheme: const AppBarTheme(\n                brightness: Brightness.dark,\n              )),\n          home: SplashScreen()),\n    );\n  }\n}\n\nFuture<void> scheduleAlarm(\n    DateTime scheduledNotificationDateTime, String body) async {\n  final androidPlatformChannelSpecifics = AndroidNotificationDetails(\n      'alarm_id', 'alarm_id', 'Channel alarm',\n      icon: 'ic_stat_onesignal_default',\n      enableLights: true,\n      priority: Priority.high,\n      importance: Importance.max,\n      vibrationPattern: Int64List.fromList([0, 1000, 5000, 2000]));\n\n  tz.initializeTimeZones();\n  tz.setLocalLocation(tz.getLocation('Asia/Makassar'));\n\n  final scheduleTime = tz.TZDateTime.from(\n      scheduledNotificationDateTime, tz.getLocation('Asia/Makassar'));\n\n  final platformChannelSpecifics =\n      NotificationDetails(android: androidPlatformChannelSpecifics);\n\n  await flutterLocalNotificationsPlugin.zonedSchedule(\n    Random().nextInt(int.parse(pow(2, 31).toString())),\n    'Pengingat',\n    body,\n    scheduleTime,\n    platformChannelSpecifics,\n    uiLocalNotificationDateInterpretation:\n        UILocalNotificationDateInterpretation.absoluteTime,\n    androidAllowWhileIdle: true,\n  );\n}\n"
  },
  {
    "path": "lib/models/absent_permission.dart",
    "content": "import 'package:spo_balaesang/models/user.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\n\nclass AbsentPermission {\n  const AbsentPermission(\n      {this.id,\n      this.title,\n      this.dueDate,\n      this.startDate,\n      this.description,\n      this.photo,\n      this.approvalStatus,\n      this.isApproved,\n      this.user});\n\n  final int id;\n  final String title;\n  final String description;\n  final bool isApproved;\n  final String photo;\n  final String approvalStatus;\n  final DateTime dueDate;\n  final DateTime startDate;\n  final User user;\n\n  factory AbsentPermission.fromJson(Map<String, dynamic> json) {\n    return AbsentPermission(\n        id: json[absentPermissionIdField] as int,\n        title: json[absentPermissionTitleField] as String,\n        description: json[absentPermissionDescriptionField] as String,\n        isApproved: json[absentPermissionIsApprovedField] as bool,\n        photo: json[absentPermissionPhotoField] as String,\n        approvalStatus: json[approvalStatusField] as String,\n        dueDate: DateTime.parse(json[absentPermissionDueDateField].toString()),\n        startDate:\n            DateTime.parse(json[absentPermissionStartDateField].toString()),\n        user: json[absentPermissionUserField] != null\n            ? User.fromJson(\n                json[absentPermissionUserField] as Map<String, dynamic>)\n            : null);\n  }\n}\n"
  },
  {
    "path": "lib/models/employee.dart",
    "content": "import 'package:spo_balaesang/models/presence.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\n\nclass Employee {\n  const Employee(\n      {this.id,\n      this.nip,\n      this.name,\n      this.phone,\n      this.gender,\n      this.department,\n      this.status,\n      this.position,\n      this.presences,\n      this.rank,\n      this.group});\n\n  final int id;\n  final String nip;\n  final String name;\n  final String phone;\n  final String gender;\n  final String department;\n  final String status;\n  final String position;\n  final String rank;\n  final String group;\n  final List<Presence> presences;\n\n  factory Employee.fromJson(Map<String, dynamic> json) {\n    return Employee(\n        id: json[userIdField] as int,\n        nip: json[userNipField] as String,\n        name: json[userNameField] as String,\n        phone: json[userPhoneField] as String,\n        gender: json[userGenderField] as String,\n        department: json[userDepartmentField] as String,\n        rank: json[userRankField] as String,\n        group: json[userGroupField] as String,\n        status: json[userStatusField] as String,\n        position: json[userPositionField] as String,\n        presences: json[userPresencesField] != null\n            ? (json[userPresencesField] as List<dynamic>)\n                .map((json) => Presence.fromJson(json as Map<String, dynamic>))\n                .toList()\n            : null);\n  }\n\n  Map<String, dynamic> toJson() => {\n        userIdField: id,\n        userNameField: name,\n        userNipField: nip,\n        userPhoneField: phone,\n        userGenderField: gender,\n        userDepartmentField: department,\n        userRankField: rank,\n        userGroupField: group,\n        userStatusField: status,\n        userPositionField: position\n      };\n}\n"
  },
  {
    "path": "lib/models/holiday.dart",
    "content": "import 'package:spo_balaesang/utils/app_const.dart';\n\nclass Holiday {\n  const Holiday({this.date, this.name, this.description});\n\n  final DateTime date;\n  final String name;\n  final String description;\n\n  factory Holiday.fromJson(Map<String, dynamic> json) => Holiday(\n      date: json == null\n          ? DateTime.now()\n          : DateTime.parse(json[holidayDateField].toString()),\n      name: json[holidayNameField] as String,\n      description: json[holidayDescriptionField] as String);\n}\n"
  },
  {
    "path": "lib/models/location.dart",
    "content": "import 'package:spo_balaesang/utils/app_const.dart';\n\nclass Location {\n  const Location({this.latitude, this.longitude, this.address});\n\n  final double latitude;\n  final double longitude;\n  final String address;\n\n  factory Location.fromJson(Map<String, dynamic> json) {\n    return Location(\n        latitude: double.parse(json[locationLatitudeField].toString()),\n        longitude: double.parse(json[locationLongitudeField].toString()),\n        address: json[locationAddressField] as String);\n  }\n\n  Map<String, dynamic> toJson() => {\n        locationLatitudeField: latitude,\n        locationLongitudeField: longitude,\n        locationAddressField: address\n      };\n}\n"
  },
  {
    "path": "lib/models/notification.dart",
    "content": "import 'package:spo_balaesang/utils/app_const.dart';\n\nclass UserNotification {\n  const UserNotification(\n      {this.id,\n      this.notifiableId,\n      this.notifiableType,\n      this.data,\n      this.isRead});\n\n  final String id;\n  final int notifiableId;\n  final String notifiableType;\n  final Map<String, dynamic> data;\n  final bool isRead;\n\n  factory UserNotification.fromJson(Map<String, dynamic> json) {\n    return UserNotification(\n        id: json[notificationIdField] as String,\n        notifiableId: json[notificationNotifiableIdField] as int,\n        notifiableType: json[notificationNotifiableTypeField] as String,\n        data: json[jsonDataField] as Map<String, dynamic>,\n        isRead: json[notificationIsReadField] as bool);\n  }\n}\n"
  },
  {
    "path": "lib/models/outstation.dart",
    "content": "import 'package:spo_balaesang/models/user.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\n\nclass Outstation {\n  const Outstation(\n      {this.id,\n      this.title,\n      this.description,\n      this.isApproved,\n      this.photo,\n      this.approvalStatus,\n      this.dueDate,\n      this.startDate,\n      this.user});\n\n  final int id;\n  final String title;\n  final String description;\n  final bool isApproved;\n  final String photo;\n  final String approvalStatus;\n  final DateTime dueDate;\n  final DateTime startDate;\n  final User user;\n\n  factory Outstation.fromJson(Map<String, dynamic> json) {\n    return Outstation(\n        id: json[outstationIdField] as int,\n        title: json[outstationTitleField] as String,\n        description: json[outstationDescriptionField] as String,\n        isApproved: json[outstationIsApprovedField] as bool,\n        photo: json[outstationPhotoField] as String,\n        approvalStatus: json[approvalStatusField] as String,\n        dueDate: DateTime.parse(json[outstationDueDateField].toString()),\n        startDate: DateTime.parse(json[outstationStartDateField].toString()),\n        user: json[outstationUserField] != null\n            ? User.fromJson(json[outstationUserField] as Map<String, dynamic>)\n            : null);\n  }\n}\n"
  },
  {
    "path": "lib/models/paid_leave.dart",
    "content": "import 'package:spo_balaesang/models/user.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\n\nclass PaidLeave {\n  const PaidLeave(\n      {this.title,\n      this.id,\n      this.category,\n      this.photo,\n      this.approvalStatus,\n      this.description,\n      this.startDate,\n      this.dueDate,\n      this.isApproved,\n      this.user});\n\n  final int id;\n  final String title;\n  final String category;\n  final String description;\n  final bool isApproved;\n  final String approvalStatus;\n  final DateTime startDate;\n  final DateTime dueDate;\n  final String photo;\n  final User user;\n\n  factory PaidLeave.fromJson(Map<String, dynamic> json) => PaidLeave(\n      id: json[paidLeaveIdField] as int,\n      title: json[paidLeaveTitleField] as String,\n      category: json[paidLeaveCategoryField] as String,\n      description: json[paidLeaveDescriptionField] as String,\n      isApproved: json[paidLeaveIsApprovedField] as bool,\n      approvalStatus: json[approvalStatusField] as String,\n      startDate: DateTime.parse(json[paidLeaveStartDateField].toString()),\n      dueDate: DateTime.parse(json[paidLeaveDueDateField].toString()),\n      photo: json[paidLeavePhotoField] as String,\n      user: json[paidLeaveUserField] != null\n          ? User.fromJson(json[paidLeaveUserField] as Map<String, dynamic>)\n          : null);\n}\n"
  },
  {
    "path": "lib/models/presence.dart",
    "content": "import 'package:spo_balaesang/models/location.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\n\nclass Presence {\n  const Presence(\n      {this.id,\n      this.date,\n      this.codeType,\n      this.status,\n      this.attendTime,\n      this.location,\n      this.photo,\n      this.startTime,\n      this.endTime});\n\n  final int id;\n  final DateTime date;\n  final String codeType;\n  final String status;\n  final String attendTime;\n  final DateTime startTime;\n  final DateTime endTime;\n  final Location location;\n  final String photo;\n\n  factory Presence.fromJson(Map<String, dynamic> json) {\n    return Presence(\n      id: json[userIdField] as int,\n      date: DateTime.parse(json[presenceDateField].toString()),\n      codeType: json[presenceCodeTypeField] as String,\n      status: json[presenceStatusField] as String,\n      attendTime: json[presenceAttendTimeField] as String,\n      location: Location.fromJson(\n          json[presenceLocationField] as Map<String, dynamic>),\n      photo: json[presencePhotoField] as String,\n      startTime: DateTime.parse(json[presenceStartTimeField].toString()),\n      endTime: DateTime.parse(json[presenceEndTimeField].toString()),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/models/report/absent_report.dart",
    "content": "import 'package:spo_balaesang/models/holiday.dart';\nimport 'package:spo_balaesang/models/report/daily.dart';\nimport 'package:spo_balaesang/models/report/monthly.dart';\nimport 'package:spo_balaesang/models/report/yearly.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\n\nclass AbsentReport {\n  const AbsentReport(\n      {this.daily,\n      this.monthly,\n      this.yearly,\n      this.holidays,\n      this.totalWorkDay});\n\n  final List<Daily> daily;\n  final Monthly monthly;\n  final Yearly yearly;\n  final List<Holiday> holidays;\n  final int totalWorkDay;\n\n  factory AbsentReport.fromJson(Map<String, dynamic> json) => AbsentReport(\n      daily: (json[absentReportDailyField] as List<dynamic>)\n          .map((item) => Daily.fromJson(item as Map<String, dynamic>))\n          .toList(),\n      monthly: Monthly.fromJson(\n          json[absentReportMonthlyField] as Map<String, dynamic>),\n      yearly: Yearly.fromJson(\n          json[absentReportYearlyField] as Map<String, dynamic>),\n      holidays: (json[absentReportHolidaysField] as List<dynamic>)\n          .map((item) => Holiday.fromJson(item as Map<String, dynamic>))\n          .toList(),\n      totalWorkDay: json[reportTotalWorkDayField] as int);\n}\n"
  },
  {
    "path": "lib/models/report/daily.dart",
    "content": "import 'package:spo_balaesang/models/location.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\n\nclass Daily {\n  const Daily({this.date, this.attendancePercentage, this.attendances});\n\n  final DateTime date;\n  final double attendancePercentage;\n  final List<DailyData> attendances;\n\n  factory Daily.fromJson(Map<String, dynamic> json) {\n    final List<dynamic> _presences = json[dailyPresencesField] as List<dynamic>;\n\n    return Daily(\n      date: DateTime.parse(json[dailyDateField].toString()),\n      attendancePercentage:\n          double.parse(json[reportAttendancePercentageFieldField].toString()),\n      attendances: _presences\n          .map((json) => DailyData.fromJson(json as Map<String, dynamic>))\n          .toList(),\n    );\n  }\n}\n\nclass DailyData {\n  const DailyData(\n      {this.id,\n      this.date,\n      this.location,\n      this.attendType,\n      this.attendTime,\n      this.attendStatus,\n      this.startTime,\n      this.endTime,\n      this.address,\n      this.photo});\n\n  final int id;\n  final String date;\n  final Location location;\n  final String attendType;\n  final String attendTime;\n  final String attendStatus;\n  final DateTime startTime;\n  final DateTime endTime;\n  final String address;\n  final String photo;\n\n  factory DailyData.fromJson(Map<String, dynamic> json) => DailyData(\n      id: json[userIdField] as int,\n      date: json[presenceDateField].toString(),\n      location: Location.fromJson(\n          json[presenceLocationField] as Map<String, dynamic>),\n      attendTime: json[dailyDataAttendTimeField].toString(),\n      attendType: json[dailyDataAttendTypeField].toString(),\n      attendStatus: json[dailyDataAttendStatusField].toString(),\n      startTime: DateTime.parse(json[presenceStartTimeField].toString()),\n      endTime: DateTime.parse(json[presenceEndTimeField].toString()),\n      address: json[locationAddressField] as String,\n      photo: json[presencePhotoField] as String);\n\n  Map<String, dynamic> toMap() => <String, dynamic>{\n        userIdField: id,\n        dailyDataAttendTypeField: attendType,\n        dailyDataAttendTimeField: attendTime,\n        dailyDataAttendStatusField: attendStatus,\n        presenceStartTimeField: startTime.toString(),\n        locationAddressField: address,\n        presencePhotoField: photo\n      };\n\n  Map<String, dynamic> toPresenceJson() => <String, dynamic>{\n        userIdField: id,\n        presenceDateField: date,\n        presenceLocationField: location.toJson(),\n        presenceCodeTypeField: attendType,\n        presenceStatusField: attendStatus,\n        presenceAttendTimeField: attendTime,\n        presenceStartTimeField: startTime.toString(),\n        presenceEndTimeField: endTime.toString(),\n        presencePhotoField: photo\n      };\n}\n"
  },
  {
    "path": "lib/models/report/monthly.dart",
    "content": "import 'package:spo_balaesang/utils/app_const.dart';\n\nclass Monthly {\n  const Monthly(\n      {this.lateCount,\n      this.attendancePercentage,\n      this.leaveEarlyCount,\n      this.notMorningParadeCount,\n      this.earlyLunchBreakCount,\n      this.notComeAfterLunchBreakCount});\n\n  final double attendancePercentage;\n  final int lateCount;\n  final int leaveEarlyCount;\n  final int notMorningParadeCount;\n  final int earlyLunchBreakCount;\n  final int notComeAfterLunchBreakCount;\n\n  factory Monthly.fromJson(Map<String, dynamic> json) => Monthly(\n      attendancePercentage:\n          double.parse(json[reportAttendancePercentageFieldField].toString()),\n      lateCount: json[reportLateCountField] as int,\n      leaveEarlyCount: json[reportLeaveEarlyFieldCountField] as int,\n      notMorningParadeCount: json[reportNotMorningParadeCountField] as int,\n      earlyLunchBreakCount: json[reportEarlyLunchBreakCountField] as int,\n      notComeAfterLunchBreakCount:\n          json[reportNotComeAfterLunchBreakCountField] as int);\n}\n"
  },
  {
    "path": "lib/models/report/yearly.dart",
    "content": "import 'package:spo_balaesang/utils/app_const.dart';\n\nclass Yearly {\n  const Yearly({\n    this.attendancePercentage,\n    this.outstation,\n    this.absent,\n    this.lateCount,\n    this.absentPermission,\n    this.leaveEarlyCount,\n    this.earlyLunchBreakCount,\n    this.notComeAfterLunchBreakCount,\n    this.notMorningParadeCount,\n    this.annualLeave,\n    this.importantReasonLeave,\n    this.sickLeave,\n    this.maternityLeave,\n    this.outOfLiabilityLeave,\n  });\n\n  final double attendancePercentage;\n  final int lateCount;\n  final int leaveEarlyCount;\n  final int notMorningParadeCount;\n  final int earlyLunchBreakCount;\n  final int notComeAfterLunchBreakCount;\n  final Map<String, dynamic> absentPermission;\n  final Map<String, dynamic> outstation;\n  final Map<String, dynamic> absent;\n  final Map<String, dynamic> annualLeave;\n  final Map<String, dynamic> importantReasonLeave;\n  final Map<String, dynamic> sickLeave;\n  final Map<String, dynamic> maternityLeave;\n  final Map<String, dynamic> outOfLiabilityLeave;\n\n  factory Yearly.fromJson(Map<String, dynamic> json) {\n    return Yearly(\n        lateCount: json[reportLateCountField] as int,\n        attendancePercentage:\n            double.parse(json[reportAttendancePercentageFieldField].toString()),\n        absent: json[yearlyAbsentField] as Map<String, dynamic>,\n        absentPermission:\n            json[yearlyAbsentPermissionField] as Map<String, dynamic>,\n        outstation: json[yearlyOutstationField] as Map<String, dynamic>,\n        leaveEarlyCount: json[reportLeaveEarlyFieldCountField] as int,\n        notMorningParadeCount: json[reportNotMorningParadeCountField] as int,\n        earlyLunchBreakCount: json[reportEarlyLunchBreakCountField] as int,\n        notComeAfterLunchBreakCount:\n            json[reportNotComeAfterLunchBreakCountField] as int,\n        annualLeave: json[reportAnnualLeaveField] as Map<String, dynamic>,\n        importantReasonLeave:\n            json[reportImportantReasonLeaveField] as Map<String, dynamic>,\n        sickLeave: json[reportSickLeaveField] as Map<String, dynamic>,\n        maternityLeave: json[reportMaternityLeaveField] as Map<String, dynamic>,\n        outOfLiabilityLeave:\n            json[reportOutOfLiabilityLeaveField] as Map<String, dynamic>);\n  }\n}\n"
  },
  {
    "path": "lib/models/user.dart",
    "content": "import 'package:spo_balaesang/models/holiday.dart';\nimport 'package:spo_balaesang/models/presence.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\n\nclass User {\n  const User(\n      {this.id,\n      this.nip,\n      this.name,\n      this.phone,\n      this.gender,\n      this.department,\n      this.status,\n      this.position,\n      this.unreadNotification,\n      this.token,\n      this.nextPresence,\n      this.presences,\n      this.holiday,\n      this.isWeekend,\n      this.rank,\n      this.group});\n\n  final int id;\n  final String nip;\n  final String name;\n  final String phone;\n  final String gender;\n  final String department;\n  final String status;\n  final String position;\n  final int unreadNotification;\n  final String token;\n  final Presence nextPresence;\n  final List<Presence> presences;\n  final Holiday holiday;\n  final bool isWeekend;\n  final String rank;\n  final String group;\n\n  factory User.fromJson(Map<String, dynamic> json) {\n    return User(\n        id: json[userIdField] as int,\n        nip: json[userNipField] as String,\n        name: json[userNameField] as String,\n        phone: json[userPhoneField] as String,\n        gender: json[userGenderField] as String,\n        department: json[userDepartmentField] as String,\n        status: json[userStatusField] as String,\n        position: json[userPositionField] as String,\n        unreadNotification: json[userUnreadNotificationsCountField] as int,\n        holiday: json[userIsHolidayField] == null\n            ? null\n            : Holiday.fromJson(\n                json[userIsHolidayField] as Map<String, dynamic>),\n        isWeekend: json[userIsWeekendField] as bool,\n        token: json[userTokenField] as String,\n        nextPresence: json[userNextPresenceField] != null\n            ? Presence.fromJson(json[userNextPresenceField][jsonDataField]\n                as Map<String, dynamic>)\n            : null,\n        presences: ((json[userPresencesField] != null) &&\n                (json[userPresencesField] as List<dynamic>).isNotEmpty)\n            ? (json[userPresencesField] as List<dynamic>)\n                .map((json) => Presence.fromJson(json as Map<String, dynamic>))\n                .toList()\n            : [],\n        rank: json[userRankField] as String,\n        group: json[userGroupField] as String);\n  }\n}\n"
  },
  {
    "path": "lib/network/api.dart",
    "content": "import 'package:flutter_config/flutter_config.dart';\n\nenum Endpoint {\n  login,\n  logout,\n  changePass,\n  users,\n  presence,\n  my,\n  permission,\n  employeePermission,\n  approvePermission,\n  outstation,\n  employeeOutstation,\n  approveOutstation,\n  notifications,\n  readNotifications,\n  deleteNotifications,\n  sendNotifications,\n  statistics,\n  paidLeave,\n  employeePaidLeave,\n  approvePaidLeave,\n  cancelAttendance,\n  changePermissionPhoto,\n  changeOutstationPhoto,\n  changePaidLeavePhoto,\n}\n\nclass API {\n  final String host = FlutterConfig.get(\"BASE_URL\").toString();\n\n  String endpointUri(Endpoint endpoint) => '$host/api/${_paths[endpoint]}';\n\n  static final Map<Endpoint, String> _paths = {\n    Endpoint.login: 'login',\n    Endpoint.logout: 'logout',\n    Endpoint.changePass: 'change_password',\n    Endpoint.users: 'user',\n    Endpoint.presence: 'presence',\n    Endpoint.my: 'my',\n    Endpoint.permission: 'permission',\n    Endpoint.employeePermission: 'permission/all',\n    Endpoint.approvePermission: 'permission/approve',\n    Endpoint.outstation: 'outstation',\n    Endpoint.employeeOutstation: 'outstation/all',\n    Endpoint.approveOutstation: 'outstation/approve',\n    Endpoint.notifications: 'notifications',\n    Endpoint.readNotifications: 'notifications/read',\n    Endpoint.deleteNotifications: 'notifications/delete',\n    Endpoint.sendNotifications: 'notifications/send',\n    Endpoint.statistics: 'statistics',\n    Endpoint.paidLeave: 'paid-leave',\n    Endpoint.employeePaidLeave: 'paid-leave/all',\n    Endpoint.approvePaidLeave: 'paid-leave/approve',\n    Endpoint.cancelAttendance: 'presence/cancel',\n    Endpoint.changePermissionPhoto: 'permission/picture',\n    Endpoint.changeOutstationPhoto: 'outstation/picture',\n    Endpoint.changePaidLeavePhoto: 'paid-leave/picture',\n  };\n}\n"
  },
  {
    "path": "lib/network/api_service.dart",
    "content": "import 'dart:convert';\n\nimport 'package:flutter/cupertino.dart';\nimport 'package:http/http.dart' as http;\nimport 'package:shared_preferences/shared_preferences.dart';\nimport 'package:spo_balaesang/network/api.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\n\nclass ApiService {\n  ApiService({@required this.api});\n\n  final API api;\n  String token = '';\n\n  Future<void> _getToken() async {\n    final SharedPreferences localStorage =\n        await SharedPreferences.getInstance();\n\n    token = jsonDecode(localStorage.getString(prefsTokenKey)) as String;\n  }\n\n  Future<Map<String, dynamic>> getEndpointData(\n      {@required Endpoint endpoint, Map<String, String> query}) async {\n    final url = api.endpointUri(endpoint);\n    await _getToken();\n    Uri uri = Uri.parse(url);\n    if (query != null) {\n      uri = uri.replace(queryParameters: query);\n    }\n    final response = await http.get(uri, headers: _setHeaders());\n    if (response.statusCode == 200) {\n      final Map<String, dynamic> data =\n          json.decode(response.body) as Map<String, dynamic>;\n      if (data['success'] as bool) {\n        return data;\n      }\n    }\n    throw response;\n  }\n\n  Future<http.Response> postEndpointWithoutToken(\n      {@required Endpoint endpoint, Map<String, dynamic> data}) async {\n    final url = api.endpointUri(endpoint);\n    final Uri uri = Uri.parse(url);\n    final response = await http.post(uri, body: data);\n    return response;\n  }\n\n  Future<http.Response> postEndpointWithToken(\n      {@required Endpoint endpoint, Map<String, dynamic> data}) async {\n    await _getToken();\n    final url = api.endpointUri(endpoint);\n    final Uri uri = Uri.parse(url);\n    final response =\n        http.post(uri, body: jsonEncode(data), headers: _setHeaders());\n    return response;\n  }\n\n  Map<String, String> _setHeaders() => <String, String>{\n        'Content-type': 'application/json',\n        'Accept': 'application/json',\n        'Authorization': 'Bearer $token'\n      };\n}\n"
  },
  {
    "path": "lib/repositories/data_repository.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:flutter/cupertino.dart';\nimport 'package:http/http.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\nimport 'package:spo_balaesang/models/employee.dart';\nimport 'package:spo_balaesang/models/user.dart';\nimport 'package:spo_balaesang/network/api.dart';\nimport 'package:spo_balaesang/network/api_service.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\n\nclass DataRepository {\n  DataRepository({@required this.apiService});\n\n  final ApiService apiService;\n\n  Future<List<Employee>> getAllEmployee() async {\n    final SharedPreferences prefs = await SharedPreferences.getInstance();\n    List<Employee> employee;\n    try {\n      final Map<String, dynamic> _data =\n          await apiService.getEndpointData(endpoint: Endpoint.users);\n      final List<dynamic> _result = _data['data'] as List<dynamic>;\n      if (prefs.containsKey(prefsEmployeeKey)) {\n        prefs.remove(prefsEmployeeKey);\n        prefs.reload();\n      }\n      prefs.setString(prefsEmployeeKey, jsonEncode(_data['data']));\n      employee = _result\n          .map(\n              (dynamic json) => Employee.fromJson(json as Map<String, dynamic>))\n          .toList();\n    } on SocketException {\n      final Map<String, dynamic> data =\n          jsonDecode(prefs.getString(prefsEmployeeKey)) as Map<String, dynamic>;\n      final List<dynamic> _data = data['data'] as List<dynamic>;\n      employee = _data\n          .map(\n              (dynamic json) => Employee.fromJson(json as Map<String, dynamic>))\n          .toList();\n    } catch (e) {}\n    return employee;\n  }\n\n  Future<User> getMyData() async {\n    final SharedPreferences prefs = await SharedPreferences.getInstance();\n    User user;\n    try {\n      final Map<String, dynamic> _data =\n          await apiService.getEndpointData(endpoint: Endpoint.my);\n      prefs.remove(prefsUserKey);\n      prefs.reload();\n      prefs.setString(prefsUserKey, jsonEncode(_data['data']));\n      user = User.fromJson(_data['data'] as Map<String, dynamic>);\n    } on SocketException {\n      return null;\n    } catch (e) {}\n    return user;\n  }\n\n  Future<Map<String, dynamic>> logout() async {\n    Map<String, dynamic> response;\n    try {\n      final Map<String, dynamic> _data =\n          await apiService.getEndpointData(endpoint: Endpoint.logout);\n      response = _data;\n    } catch (e) {}\n    return response;\n  }\n\n  Future<Response> login(Map<String, dynamic> data) async {\n    Response response;\n    try {\n      response = await apiService.postEndpointWithoutToken(\n          endpoint: Endpoint.login, data: data);\n    } catch (e) {}\n    return response;\n  }\n\n  Future<Response> changePass(Map<String, dynamic> data) async {\n    Response response;\n    try {\n      response = await apiService.postEndpointWithToken(\n          endpoint: Endpoint.changePass, data: data);\n    } catch (e) {}\n    return response;\n  }\n\n  Future<Map<String, dynamic>> permission(Map<String, dynamic> data) async {\n    Map<String, dynamic> result;\n    try {\n      final response = await apiService.postEndpointWithToken(\n          endpoint: Endpoint.permission, data: data);\n      result = jsonDecode(response.body) as Map<String, dynamic>;\n    } catch (e) {}\n    return result;\n  }\n\n  Future<Map<String, dynamic>> getAllPermissions(DateTime date) async {\n    Map<String, dynamic> data;\n    try {\n      data = await apiService.getEndpointData(\n          endpoint: Endpoint.permission, query: {'date': date.toString()});\n    } catch (e) {}\n    return data;\n  }\n\n  Future<Map<String, dynamic>> getAllEmployeePermissions(DateTime date) async {\n    Map<String, dynamic> data;\n    try {\n      data = await apiService.getEndpointData(\n          endpoint: Endpoint.employeePermission,\n          query: {'date': date.toString()});\n    } catch (e) {}\n    return data;\n  }\n\n  Future<Response> approvePermission(Map<String, dynamic> data) async {\n    Response response;\n    try {\n      response = await apiService.postEndpointWithToken(\n          endpoint: Endpoint.approvePermission, data: data);\n    } catch (e) {}\n    return response;\n  }\n\n  Future<Map<String, dynamic>> changePermissionPhoto(\n      Map<String, dynamic> data) async {\n    Map<String, dynamic> result;\n    try {\n      final response = await apiService.postEndpointWithToken(\n          endpoint: Endpoint.changePermissionPhoto, data: data);\n      result = jsonDecode(response.body) as Map<String, dynamic>;\n    } catch (e) {}\n    return result;\n  }\n\n  Future<Map<String, dynamic>> outstation(Map<String, dynamic> data) async {\n    Map<String, dynamic> result;\n    try {\n      final response = await apiService.postEndpointWithToken(\n          endpoint: Endpoint.outstation, data: data);\n      result = jsonDecode(response.body) as Map<String, dynamic>;\n    } catch (e) {}\n    return result;\n  }\n\n  Future<Map<String, dynamic>> getAllOutstation(DateTime date) async {\n    Map<String, dynamic> data;\n    try {\n      data = await apiService.getEndpointData(\n          endpoint: Endpoint.outstation, query: {'date': date.toString()});\n    } catch (e) {}\n    return data;\n  }\n\n  Future<Map<String, dynamic>> getAllEmployeeOutstation(DateTime date) async {\n    Map<String, dynamic> data;\n    try {\n      data = await apiService.getEndpointData(\n          endpoint: Endpoint.employeeOutstation,\n          query: {'date': date.toString()});\n    } catch (e) {}\n    return data;\n  }\n\n  Future<Response> approveOutstation(Map<String, dynamic> data) async {\n    Response response;\n    try {\n      response = await apiService.postEndpointWithToken(\n          endpoint: Endpoint.approveOutstation, data: data);\n    } catch (e) {}\n    return response;\n  }\n\n  Future<Map<String, dynamic>> changeOutstationPhoto(\n      Map<String, dynamic> data) async {\n    Map<String, dynamic> result;\n    try {\n      final response = await apiService.postEndpointWithToken(\n          endpoint: Endpoint.changeOutstationPhoto, data: data);\n      result = jsonDecode(response.body) as Map<String, dynamic>;\n    } catch (e) {}\n    return result;\n  }\n\n  Future<Map<String, dynamic>> getAllNotifications() async {\n    Map<String, dynamic> data;\n    try {\n      data = await apiService.getEndpointData(endpoint: Endpoint.notifications);\n    } catch (e) {}\n    return data;\n  }\n\n  Future<Map<String, dynamic>> getStatistics(DateTime date,\n      [int userId]) async {\n    Map<String, dynamic> data;\n\n    final Map<String, String> queries = {\n      'year': date.year.toString(),\n      'month': date.month.toString(),\n    };\n\n    if (userId != null) {\n      queries['user_id'] = userId.toString();\n    }\n\n    try {\n      data = await apiService.getEndpointData(\n          endpoint: Endpoint.statistics, query: queries);\n    } catch (e) {}\n    return data;\n  }\n\n  Future<Response> readNotification(Map<String, dynamic> data) async {\n    Response response;\n    try {\n      response = await apiService.postEndpointWithToken(\n          endpoint: Endpoint.notifications, data: data);\n    } catch (e) {}\n    return response;\n  }\n\n  Future<Map<String, dynamic>> readAllNotifications() async {\n    Map<String, dynamic> data;\n    try {\n      data = await apiService.getEndpointData(\n          endpoint: Endpoint.readNotifications);\n    } catch (e) {}\n    return data;\n  }\n\n  Future<Map<String, dynamic>> deleteAllNotifications() async {\n    Map<String, dynamic> data;\n    try {\n      data = await apiService.getEndpointData(\n          endpoint: Endpoint.deleteNotifications);\n    } catch (e) {}\n    return data;\n  }\n\n  Future<Map<String, dynamic>> sendNotification(\n      Map<String, dynamic> data) async {\n    Map<String, dynamic> result;\n    try {\n      final response = await apiService.postEndpointWithToken(\n          endpoint: Endpoint.sendNotifications, data: data);\n      result = jsonDecode(response.body) as Map<String, dynamic>;\n    } catch (e) {}\n    return result;\n  }\n\n  Future<Response> presence(Map<String, dynamic> data) async {\n    Response response;\n    try {\n      response = await apiService.postEndpointWithToken(\n          endpoint: Endpoint.presence, data: data);\n    } catch (e) {}\n    return response;\n  }\n\n  Future<Map<String, dynamic>> getAllPaidLeave(DateTime date) async {\n    Map<String, dynamic> data;\n    try {\n      data = await apiService.getEndpointData(\n          endpoint: Endpoint.paidLeave, query: {'date': date.toString()});\n    } catch (e) {}\n    return data;\n  }\n\n  Future<Map<String, dynamic>> getAllEmployeePaidLeave(DateTime date) async {\n    Map<String, dynamic> data;\n    try {\n      data = await apiService.getEndpointData(\n          endpoint: Endpoint.employeePaidLeave,\n          query: {'date': date.toString()});\n    } catch (e) {}\n    return data;\n  }\n\n  Future<Map<String, dynamic>> changePaidLeavePhoto(\n      Map<String, dynamic> data) async {\n    Map<String, dynamic> result;\n    try {\n      final response = await apiService.postEndpointWithToken(\n          endpoint: Endpoint.changePaidLeavePhoto, data: data);\n      result = jsonDecode(response.body) as Map<String, dynamic>;\n    } catch (e) {}\n    return result;\n  }\n\n  Future<Response> approvePaidLeave(Map<String, dynamic> data) async {\n    Response response;\n    try {\n      response = await apiService.postEndpointWithToken(\n          endpoint: Endpoint.approvePaidLeave, data: data);\n    } catch (e) {}\n    return response;\n  }\n\n  Future<Map<String, dynamic>> paidLeave(Map<String, dynamic> data) async {\n    Map<String, dynamic> result;\n    try {\n      final response = await apiService.postEndpointWithToken(\n          endpoint: Endpoint.paidLeave, data: data);\n      result = jsonDecode(response.body) as Map<String, dynamic>;\n    } catch (e) {}\n    return result;\n  }\n\n  Future<Response> cancelAttendance(Map<String, dynamic> data) async {\n    Response response;\n    try {\n      response = await apiService.postEndpointWithToken(\n          endpoint: Endpoint.cancelAttendance, data: data);\n    } catch (e) {}\n    return response;\n  }\n}\n"
  },
  {
    "path": "lib/screen/application_screen.dart",
    "content": "import 'dart:convert';\n\nimport 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'package:onesignal_flutter/onesignal_flutter.dart';\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\nimport 'package:spo_balaesang/main.dart';\nimport 'package:spo_balaesang/models/presence.dart';\nimport 'package:spo_balaesang/models/user.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/change_pass_screen.dart';\nimport 'package:spo_balaesang/screen/employee_attendance_screen.dart';\nimport 'package:spo_balaesang/screen/employee_outstation.dart';\nimport 'package:spo_balaesang/screen/employee_paid_leave_screen.dart';\nimport 'package:spo_balaesang/screen/employee_permission.dart';\nimport 'package:spo_balaesang/screen/forgot_pass_screen.dart';\nimport 'package:spo_balaesang/screen/login_screen.dart';\nimport 'package:spo_balaesang/screen/outstation_list_screen.dart';\nimport 'package:spo_balaesang/screen/paid_leave_list_screen.dart';\nimport 'package:spo_balaesang/screen/permission_list_screen.dart';\nimport 'package:spo_balaesang/screen/regulation_screen.dart';\nimport 'package:spo_balaesang/screen/report_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\n\nclass ApplicationScreen extends StatefulWidget {\n  @override\n  _ApplicationScreenState createState() => _ApplicationScreenState();\n}\n\nclass _ApplicationScreenState extends State<ApplicationScreen> {\n  User user;\n  bool _isAlarmActive = false;\n  List<Presence> _presences;\n\n  Future<void> loadData() async {\n    final sp = await SharedPreferences.getInstance();\n    final _data = sp.get(prefsUserKey);\n    bool _alarm = false;\n\n    if (sp.containsKey(prefsAlarmKey)) {\n      _alarm = sp.get(prefsAlarmKey) as bool;\n    } else {\n      sp.setBool(prefsAlarmKey, _alarm);\n    }\n\n    final Map<String, dynamic> _json =\n        jsonDecode(_data.toString()) as Map<String, dynamic>;\n\n    if (_alarm) {\n      await flutterLocalNotificationsPlugin.cancelAll();\n    }\n\n    setState(() {\n      user = User.fromJson(_json);\n      _presences = user.presences;\n      _isAlarmActive = _alarm;\n      if (_isAlarmActive) {\n        _presences.forEach(_setAlarm);\n      }\n    });\n  }\n\n  Future<void> logout() async {\n    try {\n      Get.defaultDialog(\n          title: 'Keluar',\n          content: Padding(\n            padding: const EdgeInsets.all(8.0),\n            child: Column(\n              children: const <Widget>[\n                Icon(\n                  Icons.dangerous,\n                  color: Colors.red,\n                  size: 72,\n                ),\n                sizedBoxH10,\n                Text('Apakah anda yakin ingin keluar dari aplikasi?'),\n              ],\n            ),\n          ),\n          actions: <Widget>[\n            TextButton(\n                onPressed: () async {\n                  Get.back();\n                  final ProgressDialog pd =\n                      ProgressDialog(context, isDismissible: false);\n                  pd.show();\n                  final dataRepo =\n                      Provider.of<DataRepository>(context, listen: false);\n                  final Map<String, dynamic> _response =\n                      await dataRepo.logout();\n                  if (_response['success'] as bool) {\n                    final SharedPreferences prefs =\n                        await SharedPreferences.getInstance();\n                    prefs.remove(prefsTokenKey);\n                    prefs.remove(prefsUserKey);\n                    prefs.remove(prefsAlarmKey);\n                    pd.hide();\n                    OneSignal.shared.removeExternalUserId();\n                    Get.off(() => LoginScreen());\n                  }\n                },\n                child: const Text('Ya',\n                    style: TextStyle(\n                      color: Colors.blueAccent,\n                    ))),\n            TextButton(\n                onPressed: () {\n                  Get.back();\n                },\n                child: const Text('Tidak',\n                    style: TextStyle(\n                      color: Colors.blueAccent,\n                    ))),\n          ]);\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n    }\n  }\n\n  Future<void> _handleSelected(bool value) async {\n    final SharedPreferences prefs = await SharedPreferences.getInstance();\n    prefs.reload();\n    prefs.setBool(prefsAlarmKey, value);\n    setState(() {\n      _isAlarmActive = value;\n    });\n    if (value) {\n      _presences.forEach(_setAlarm);\n      showAlertDialog('success', 'Sukses', 'Berhasil mengaktifkan alarm!',\n          dismissible: true);\n    } else {\n      await flutterLocalNotificationsPlugin.cancelAll();\n      showAlertDialog('success', 'Sukses', 'Berhasil menonaktifkan alarm!',\n          dismissible: true);\n    }\n  }\n\n  void _setAlarm(Presence presence) {\n    const dur10Min = Duration(minutes: 10);\n    const dur1Day = Duration(days: 1);\n    if (presence.startTime.isAfter(DateTime.now())) {\n      scheduleAlarm(presence.startTime.subtract(dur10Min),\n          '${presence.codeType} akan dimulai dalam 10 menit!');\n    } else {\n      if (presence.startTime.weekday < DateTime.friday) {\n        scheduleAlarm(presence.startTime.add(dur1Day),\n            '${presence.codeType} akan dimulai dalam 10 menit!');\n      }\n    }\n\n    if (presence.endTime.isAfter(DateTime.now())) {\n      scheduleAlarm(presence.endTime.subtract(dur10Min),\n          '${presence.codeType} akan selesai dalam 10 menit!');\n    } else {\n      if (presence.endTime.weekday < DateTime.friday) {\n        scheduleAlarm(presence.endTime.add(dur1Day),\n            '${presence.codeType} akan selesai dalam 10 menit!');\n      }\n    }\n  }\n\n  @override\n  void setState(void Function() fn) {\n    if (mounted) {\n      super.setState(fn);\n    }\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    loadData();\n  }\n\n  Widget _buildStakeholderMenu() {\n    if (user?.position != 'Camat') {\n      return sizedBox;\n    }\n\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: <Widget>[\n        const Text(\n          'Atasan',\n          style: TextStyle(\n              fontWeight: FontWeight.w600,\n              fontSize: 18.0,\n              color: Colors.blueAccent),\n        ),\n        dividerT1,\n        sizedBoxH10,\n        Card(\n          elevation: 2.0,\n          child: InkWell(\n            onTap: () {\n              Get.to(() => EmployeeAttendanceScreen());\n            },\n            child: const ListTile(\n              dense: false,\n              leading: Icon(\n                Icons.playlist_add_check_rounded,\n                color: Colors.green,\n                size: 32.0,\n              ),\n              title: Text(\n                'Presensi Pegawai',\n                style: TextStyle(fontWeight: FontWeight.bold),\n              ),\n              subtitle: Text(\n                'Lihat & konfirmasi kehadiran pegawai',\n                style: TextStyle(color: Colors.black87),\n              ),\n            ),\n          ),\n        ),\n        sizedBoxH10,\n        Card(\n          elevation: 2.0,\n          child: InkWell(\n            onTap: () {\n              Get.to(() => EmployeePermissionScreen());\n            },\n            child: const ListTile(\n              leading: Icon(\n                Icons.playlist_add_check_rounded,\n                color: Colors.green,\n                size: 32.0,\n              ),\n              title: Text(\n                'Persetujuan Izin',\n                style: TextStyle(fontWeight: FontWeight.bold),\n              ),\n              subtitle: Text(\n                'Setujui Izin yang diajukan',\n                style: TextStyle(color: Colors.black87),\n              ),\n            ),\n          ),\n        ),\n        sizedBoxH10,\n        Card(\n          elevation: 2.0,\n          child: InkWell(\n            onTap: () {\n              Get.to(() => EmployeeOutstationScreen());\n            },\n            child: const ListTile(\n              leading: Icon(\n                Icons.playlist_add_check_rounded,\n                color: Colors.green,\n                size: 32.0,\n              ),\n              title: Text(\n                'Persetujuan Dinas Luar',\n                style: TextStyle(fontWeight: FontWeight.bold),\n              ),\n              subtitle: Text(\n                'Setujui Dinas Luar yang diajukan',\n                style: TextStyle(color: Colors.black87),\n              ),\n            ),\n          ),\n        ),\n        sizedBoxH10,\n        Card(\n          elevation: 2.0,\n          child: InkWell(\n            onTap: () {\n              Get.to(() => EmployeePaidLeaveScreen());\n            },\n            child: const ListTile(\n              leading: Icon(\n                Icons.playlist_add_check_rounded,\n                color: Colors.green,\n                size: 32.0,\n              ),\n              title: Text(\n                'Persetujuan Cuti',\n                style: TextStyle(fontWeight: FontWeight.bold),\n              ),\n              subtitle: Text(\n                'Setujui Cuti yang diajukan',\n                style: TextStyle(color: Colors.black87),\n              ),\n            ),\n          ),\n        ),\n        sizedBoxH30,\n      ],\n    );\n  }\n\n  Widget _buildCutiSection() {\n    if (user?.status == 'Honorer') {\n      return sizedBox;\n    }\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: <Widget>[\n        sizedBoxH10,\n        Card(\n          elevation: 2.0,\n          child: InkWell(\n            onTap: () {\n              Get.to(() => PaidLeaveListScreen());\n            },\n            child: const ListTile(\n              leading: Icon(\n                Icons.card_giftcard_rounded,\n                color: Colors.red,\n                size: 32.0,\n              ),\n              title: Text(\n                'Cuti',\n                style: TextStyle(fontWeight: FontWeight.bold),\n              ),\n              subtitle: Text(\n                'Pengajuan dan riwayat Cuti',\n                style: TextStyle(color: Colors.black87),\n              ),\n            ),\n          ),\n        ),\n      ],\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      backgroundColor: Colors.grey[200],\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        leading: Image.asset('assets/logo/logo.png'),\n        leadingWidth: Get.width * 0.25,\n        title: const Text('Aplikasi'),\n      ),\n      body: SingleChildScrollView(\n        child: Container(\n          margin: const EdgeInsets.symmetric(horizontal: 8.0),\n          padding: const EdgeInsets.symmetric(vertical: 8.0),\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              const Text(\n                'Pengaturan & Personalisasi',\n                style: TextStyle(\n                    fontWeight: FontWeight.w600,\n                    fontSize: 18.0,\n                    color: Colors.blueAccent),\n              ),\n              dividerT1,\n              sizedBoxH10,\n              Card(\n                elevation: 2.0,\n                child: SwitchListTile(\n                  onChanged: _handleSelected,\n                  activeColor: Colors.blueAccent,\n                  value: _isAlarmActive,\n                  secondary: const Icon(\n                    Icons.alarm,\n                    color: Colors.indigo,\n                    size: 32.0,\n                  ),\n                  title: const Text(\n                    'Aktifkan Alarm Absensi',\n                    style: TextStyle(fontWeight: FontWeight.bold),\n                  ),\n                  subtitle: const Text(\n                    'Pengingat waktu absen',\n                    style: TextStyle(color: Colors.black87),\n                  ),\n                ),\n              ),\n              sizedBoxH30,\n              const Text(\n                'Presensi',\n                style: TextStyle(\n                    fontWeight: FontWeight.w600,\n                    fontSize: 18.0,\n                    color: Colors.blueAccent),\n              ),\n              dividerT1,\n              sizedBoxH10,\n              Card(\n                elevation: 2.0,\n                child: InkWell(\n                  onTap: () {\n                    Get.to(() => ReportScreen(user: user));\n                  },\n                  child: const ListTile(\n                    leading: Icon(\n                      Icons.bar_chart_rounded,\n                      color: Colors.deepOrangeAccent,\n                      size: 32.0,\n                    ),\n                    title: Text(\n                      'Statistik',\n                      style: TextStyle(fontWeight: FontWeight.bold),\n                    ),\n                    subtitle: Text(\n                      'Lihat statistik presensi anda',\n                      style: TextStyle(color: Colors.black87),\n                    ),\n                  ),\n                ),\n              ),\n              sizedBoxH10,\n              Card(\n                elevation: 2.0,\n                child: InkWell(\n                  onTap: () {\n                    Get.to(() => PermissionListScreen());\n                  },\n                  child: const ListTile(\n                    leading: Icon(\n                      Icons.calendar_today_rounded,\n                      color: Colors.purple,\n                      size: 32.0,\n                    ),\n                    title: Text(\n                      'Izin',\n                      style: TextStyle(fontWeight: FontWeight.bold),\n                    ),\n                    subtitle: Text(\n                      'Pengajuan dan riwayat Izin',\n                      style: TextStyle(color: Colors.black87),\n                    ),\n                  ),\n                ),\n              ),\n              sizedBoxH10,\n              Card(\n                elevation: 2.0,\n                child: InkWell(\n                  onTap: () {\n                    Get.to(() => OutstationListScreen());\n                  },\n                  child: ListTile(\n                    leading: Icon(\n                      Icons.card_travel_rounded,\n                      color: Colors.yellow[800],\n                      size: 32.0,\n                    ),\n                    title: const Text(\n                      'Dinas Luar',\n                      style: TextStyle(fontWeight: FontWeight.bold),\n                    ),\n                    subtitle: const Text(\n                      'Pengajuan dan riwayat Dinas Luar',\n                      style: TextStyle(color: Colors.black87),\n                    ),\n                  ),\n                ),\n              ),\n              _buildCutiSection(),\n              sizedBoxH30,\n              _buildStakeholderMenu(),\n              const Text(\n                'Bantuan',\n                style: TextStyle(\n                    fontWeight: FontWeight.w600,\n                    fontSize: 18.0,\n                    color: Colors.blueAccent),\n              ),\n              dividerT1,\n              Card(\n                elevation: 2.0,\n                child: InkWell(\n                  onTap: () {\n                    Get.to(() => ForgotPassScreen());\n                  },\n                  child: const ListTile(\n                    leading: Icon(\n                      Icons.warning,\n                      color: Colors.pink,\n                      size: 32.0,\n                    ),\n                    title: Text(\n                      'Lapor',\n                      style: TextStyle(fontWeight: FontWeight.bold),\n                    ),\n                    subtitle: Text(\n                      'Lapor kendala & pelanggaran',\n                      style: TextStyle(color: Colors.black87),\n                    ),\n                  ),\n                ),\n              ),\n              sizedBoxH10,\n              Card(\n                elevation: 2.0,\n                child: InkWell(\n                  onTap: () {\n                    Get.to(() => RegulationScreen());\n                  },\n                  child: ListTile(\n                    leading: Icon(\n                      Icons.room_preferences,\n                      color: Colors.lime[800],\n                      size: 32.0,\n                    ),\n                    title: const Text(\n                      'Rujukan',\n                      style: TextStyle(fontWeight: FontWeight.bold),\n                    ),\n                    subtitle: const Text(\n                      'Daftar aturan yang menjadi rujukan SiAP Balaesang',\n                      style: TextStyle(color: Colors.black87),\n                    ),\n                  ),\n                ),\n              ),\n              sizedBoxH30,\n              const Text(\n                'Akun',\n                style: TextStyle(\n                    fontWeight: FontWeight.w600,\n                    fontSize: 18.0,\n                    color: Colors.blueAccent),\n              ),\n              dividerT1,\n              Card(\n                elevation: 2.0,\n                child: InkWell(\n                  onTap: () {\n                    Get.to(() => ChangePasswordScreen());\n                  },\n                  child: const ListTile(\n                    leading: Icon(\n                      Icons.lock_outline,\n                      color: Colors.blueAccent,\n                      size: 32.0,\n                    ),\n                    title: Text(\n                      'Password',\n                      style: TextStyle(fontWeight: FontWeight.bold),\n                    ),\n                    subtitle: Text(\n                      'Ubah password akun',\n                      style: TextStyle(color: Colors.black87),\n                    ),\n                  ),\n                ),\n              ),\n              sizedBoxH10,\n              Card(\n                elevation: 2.0,\n                child: InkWell(\n                  onTap: () {\n                    logout();\n                  },\n                  child: Padding(\n                    padding: const EdgeInsets.symmetric(vertical: 8.0),\n                    child: ListTile(\n                      leading: Icon(\n                        Icons.exit_to_app_rounded,\n                        color: Colors.red[800],\n                        size: 32.0,\n                      ),\n                      title: const Text(\n                        'Keluar Dari Aplikasi',\n                        style: TextStyle(fontWeight: FontWeight.bold),\n                      ),\n                      subtitle: const Text(\n                        'Keluar dari aplikasi dan menghapus sesi saat ini. Gunakan ini jika aplikasi terasa berat atau anda ingin mengganti akun.',\n                        style: TextStyle(color: Colors.black87),\n                      ),\n                    ),\n                  ),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/bottom_nav_screen.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:spo_balaesang/screen/application_screen.dart';\nimport 'package:spo_balaesang/screen/home_screen.dart';\n\nclass BottomNavScreen extends StatefulWidget {\n  @override\n  _BottomNavScreenState createState() => _BottomNavScreenState();\n}\n\nclass _BottomNavScreenState extends State<BottomNavScreen> {\n  final List<Widget> _screens = [HomeScreen(), ApplicationScreen()];\n  int _currentIndex = 0;\n\n  final Map<String, dynamic> _bottomNavItems = {\n    'Beranda': Icons.home_filled,\n    'Aplikasi': Icons.apps_rounded,\n  };\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n        body: IndexedStack(\n          index: _currentIndex,\n          children: _screens,\n        ),\n        bottomNavigationBar: BottomNavigationBar(\n          currentIndex: _currentIndex,\n          onTap: (index) => setState(() => _currentIndex = index),\n          type: BottomNavigationBarType.fixed,\n          backgroundColor: Colors.white,\n          showUnselectedLabels: true,\n          selectedItemColor: Colors.blueAccent,\n          selectedLabelStyle: const TextStyle(\n              color: Colors.blueAccent, fontWeight: FontWeight.bold),\n          unselectedLabelStyle: const TextStyle(\n            color: Colors.grey,\n            fontWeight: FontWeight.normal,\n          ),\n          unselectedItemColor: Colors.grey,\n          items: _bottomNavItems\n              .map((key, value) => MapEntry(\n                    key,\n                    BottomNavigationBarItem(\n                      label: key,\n                      icon: Icon(value as IconData),\n                    ),\n                  ))\n              .values\n              .toList(),\n        ));\n  }\n}\n"
  },
  {
    "path": "lib/screen/change_absent_permission_photo_screen.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:get/get.dart';\nimport 'package:image_picker/image_picker.dart';\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/models/absent_permission.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/bottom_nav_screen.dart';\nimport 'package:spo_balaesang/screen/image_detail_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/file_util.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/employee_proposal_info_widget.dart';\nimport 'package:spo_balaesang/widgets/image_placeholder_widget.dart';\n\nclass ChangePermissionPhotoScreen extends StatefulWidget {\n  const ChangePermissionPhotoScreen({this.permission});\n\n  final AbsentPermission permission;\n\n  @override\n  _ChangePermissionPhotoScreenState createState() =>\n      _ChangePermissionPhotoScreenState();\n}\n\nclass _ChangePermissionPhotoScreenState\n    extends State<ChangePermissionPhotoScreen> {\n  String _base64Image;\n  String _fileName;\n  File _tmpFile;\n  AbsentPermission _permission;\n\n  Future<void> _openCamera() async {\n    final picture = await ImagePicker().getImage(source: ImageSource.camera);\n    final file = await compressAndGetFile(File(picture.path),\n        '/storage/emulated/0/Android/data/com.banuacoders.siap/files/Pictures/images.jpg');\n    setState(() {\n      _tmpFile = file;\n      _base64Image = base64Encode(_tmpFile.readAsBytesSync());\n      _fileName = _tmpFile.path.split('/').last;\n    });\n    await file.delete(recursive: true);\n  }\n\n  Future<void> _uploadData(AbsentPermission permission) async {\n    final ProgressDialog pd = ProgressDialog(context, isDismissible: false);\n    try {\n      pd.show();\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final Map<String, dynamic> data = {\n        'photo': _base64Image,\n        'file_name': _fileName,\n        'permission_id': permission.id\n      };\n      final Map<String, dynamic> _res =\n          await dataRepo.changePermissionPhoto(data);\n      if (_res['success'] as bool) {\n        pd.hide();\n        showAlertDialog('success', \"Sukses\", _res['message'].toString(),\n            dismissible: false);\n        Timer(\n            const Duration(seconds: 5), () => Get.off(() => BottomNavScreen()));\n      } else {\n        if (pd.isShowing()) pd.hide();\n        showErrorDialog(_res);\n      }\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n      pd.hide();\n    }\n  }\n\n  Widget _showImage() {\n    if (_base64Image == null) {\n      return InkWell(\n        onTap: () {\n          Get.to(() => ImageDetailScreen(\n                imageUrl: _permission.photo,\n                tag: _permission.id.toString(),\n              ));\n        },\n        child: Hero(\n          tag: _permission.id.toString(),\n          child: ClipRRect(\n            borderRadius: BorderRadius.circular(10.0),\n            child: CachedNetworkImage(\n              placeholder: (_, __) => const ImagePlaceholderWidget(\n                label: 'Memuat Foto',\n                child: SpinKitFadingCircle(\n                  size: 25.0,\n                  color: Colors.blueAccent,\n                ),\n              ),\n              imageUrl: _permission.photo,\n              fit: BoxFit.cover,\n              errorWidget: (_, __, ___) => const ImagePlaceholderWidget(\n                label: 'Gagal memuat foto!',\n                child: Icon(\n                  Icons.image_not_supported_rounded,\n                  color: Colors.grey,\n                ),\n              ),\n              width: Get.width,\n              height: 250.0,\n            ),\n          ),\n        ),\n      );\n    }\n\n    final Uint8List bytes = base64Decode(_base64Image);\n    return InkWell(\n      onTap: () {\n        Get.to(() => ImageDetailScreen(\n              bytes: bytes,\n              tag: 'image',\n            ));\n      },\n      child: Hero(\n        tag: 'image',\n        child: ClipRRect(\n          borderRadius: BorderRadius.circular(10.0),\n          child: Image.memory(\n            bytes,\n            width: Get.width,\n            height: 250,\n            fit: BoxFit.cover,\n          ),\n        ),\n      ),\n    );\n  }\n\n  @override\n  void initState() {\n    _permission = widget.permission;\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      backgroundColor: Colors.white,\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Perbarui Lampiran'),\n      ),\n      body: SingleChildScrollView(\n        child: Padding(\n          padding: const EdgeInsets.all(8.0),\n          child: Column(\n            mainAxisAlignment: MainAxisAlignment.spaceAround,\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              const Text(\n                  'Pastikan gembar yang akan dikirim adalah salah satu dari memo absen, SPPD, atau surat keterangan dokter. ' +\n                      'Pastikan juga surat sudah ditandatangani oleh pihak yang berwenang disertai dengan cap resmi.'),\n              sizedBoxH20,\n              EmployeeProposalInfoWidget(\n                title: _permission.title,\n                startDate: _permission.startDate,\n                dueDate: _permission.dueDate,\n                label: 'Izin',\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Foto Surat Izin'),\n                  SizedBox(width: 5.0),\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Lampirkan foto surat izin atau surat lainnya seperti surat keterangan dokter, dsb.',\n                style: TextStyle(color: Colors.grey),\n              ),\n              const Text(\n                '*tekan untuk memperbesar',\n                style: TextStyle(\n                    fontSize: 12.0,\n                    color: Colors.black87,\n                    fontStyle: FontStyle.italic),\n              ),\n              sizedBoxH20,\n              _showImage(),\n              sizedBoxH20,\n              SizedBox(\n                width: Get.width,\n                height: 40.0,\n                child: ElevatedButton(\n                  style: ElevatedButton.styleFrom(\n                    shape: RoundedRectangleBorder(\n                        borderRadius: BorderRadius.circular(6)),\n                    primary: Colors.blueAccent,\n                    onPrimary: Colors.white,\n                  ),\n                  onPressed: _openCamera,\n                  child: const Text('Ubah Foto'),\n                ),\n              ),\n              sizedBoxH20,\n              SizedBox(\n                width: Get.width,\n                height: 40,\n                child: ElevatedButton(\n                  style: ElevatedButton.styleFrom(\n                    shape: RoundedRectangleBorder(\n                        borderRadius: BorderRadius.circular(6)),\n                    primary: Colors.green,\n                    onPrimary: Colors.white,\n                  ),\n                  onPressed: () {\n                    _uploadData(_permission);\n                  },\n                  child: const Text('Kirim'),\n                ),\n              ),\n              sizedBoxH20,\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/change_outstation_photo_screen.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:get/get.dart';\nimport 'package:image_picker/image_picker.dart';\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/models/outstation.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/bottom_nav_screen.dart';\nimport 'package:spo_balaesang/screen/image_detail_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/file_util.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/employee_proposal_info_widget.dart';\nimport 'package:spo_balaesang/widgets/image_placeholder_widget.dart';\n\nclass ChangeOutstationPhotoScreen extends StatefulWidget {\n  const ChangeOutstationPhotoScreen({this.outstation});\n\n  final Outstation outstation;\n\n  @override\n  _ChangeOutstationPhotoScreenState createState() =>\n      _ChangeOutstationPhotoScreenState();\n}\n\nclass _ChangeOutstationPhotoScreenState\n    extends State<ChangeOutstationPhotoScreen> {\n  String _base64Image;\n  String _fileName;\n  File _tmpFile;\n  Outstation _outstation;\n\n  Future<void> _openCamera() async {\n    final picture = await ImagePicker().getImage(source: ImageSource.camera);\n    final file = await compressAndGetFile(File(picture.path),\n        '/storage/emulated/0/Android/data/com.banuacoders.siap/files/Pictures/images.jpg');\n    setState(() {\n      _tmpFile = file;\n      _base64Image = base64Encode(_tmpFile.readAsBytesSync());\n      _fileName = _tmpFile.path.split('/').last;\n    });\n    await file.delete(recursive: true);\n  }\n\n  Future<void> _uploadData(Outstation outstation) async {\n    final ProgressDialog pd = ProgressDialog(context, isDismissible: false);\n    try {\n      pd.show();\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final Map<String, dynamic> data = {\n        'photo': _base64Image,\n        'file_name': _fileName,\n        'outstation_id': outstation.id\n      };\n      final Map<String, dynamic> _res =\n          await dataRepo.changeOutstationPhoto(data);\n      if (_res['success'] as bool) {\n        pd.hide();\n        showAlertDialog('success', \"Sukses\", _res['message'].toString(),\n            dismissible: false);\n        Timer(\n            const Duration(seconds: 5), () => Get.off(() => BottomNavScreen()));\n      } else {\n        if (pd.isShowing()) pd.hide();\n        showErrorDialog(_res);\n      }\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n      pd.hide();\n    }\n  }\n\n  Widget _showImage() {\n    if (_base64Image == null) {\n      return InkWell(\n        onTap: () {\n          Get.to(() => ImageDetailScreen(\n                imageUrl: _outstation.photo,\n                tag: _outstation.id.toString(),\n              ));\n        },\n        child: Hero(\n          tag: _outstation.id.toString(),\n          child: ClipRRect(\n            borderRadius: BorderRadius.circular(10.0),\n            child: CachedNetworkImage(\n              placeholder: (_, __) => const ImagePlaceholderWidget(\n                label: 'Memuat Foto',\n                child: SpinKitFadingCircle(\n                  size: 25.0,\n                  color: Colors.blueAccent,\n                ),\n              ),\n              imageUrl: _outstation.photo,\n              fit: BoxFit.cover,\n              errorWidget: (_, __, ___) => const ImagePlaceholderWidget(\n                label: 'Gagal memuat foto!',\n                child: Icon(\n                  Icons.image_not_supported_rounded,\n                  color: Colors.grey,\n                ),\n              ),\n              width: Get.width,\n              height: 250.0,\n            ),\n          ),\n        ),\n      );\n    }\n\n    final Uint8List bytes = base64Decode(_base64Image);\n    return InkWell(\n      onTap: () {\n        Get.to(() => ImageDetailScreen(\n              bytes: bytes,\n              tag: 'image',\n            ));\n      },\n      child: Hero(\n        tag: 'image',\n        child: ClipRRect(\n          borderRadius: BorderRadius.circular(10.0),\n          child: Image.memory(\n            bytes,\n            width: Get.width,\n            height: 250,\n            fit: BoxFit.cover,\n          ),\n        ),\n      ),\n    );\n  }\n\n  @override\n  void initState() {\n    _outstation = widget.outstation;\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      backgroundColor: Colors.white,\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Perbarui Lampiran'),\n      ),\n      body: SingleChildScrollView(\n        child: Padding(\n          padding: const EdgeInsets.all(8.0),\n          child: Column(\n            mainAxisAlignment: MainAxisAlignment.spaceAround,\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              const Text(\n                  'Pastikan gembar yang akan dikirim adalah salah satu dari memo absen, SPPD, atau surat keterangan dokter. ' +\n                      'Pastikan juga surat sudah ditandatangani oleh pihak yang berwenang disertai dengan cap resmi.'),\n              sizedBoxH20,\n              EmployeeProposalInfoWidget(\n                title: _outstation.title,\n                startDate: _outstation.startDate,\n                dueDate: _outstation.dueDate,\n                label: 'Dinas Luar',\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Foto Surat Tugas'),\n                  SizedBox(width: 5.0),\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Lampirkan foto SPPD atau surat lainnya seperti memo absen, dsb.',\n                style: TextStyle(color: Colors.grey),\n              ),\n              const Text(\n                '*tekan untuk memperbesar',\n                style: TextStyle(\n                    fontSize: 12.0,\n                    color: Colors.black87,\n                    fontStyle: FontStyle.italic),\n              ),\n              sizedBoxH20,\n              _showImage(),\n              sizedBoxH20,\n              SizedBox(\n                width: Get.width,\n                height: 40.0,\n                child: ElevatedButton(\n                  style: ElevatedButton.styleFrom(\n                    shape: RoundedRectangleBorder(\n                        borderRadius: BorderRadius.circular(6)),\n                    primary: Colors.blueAccent,\n                    onPrimary: Colors.white,\n                  ),\n                  onPressed: _openCamera,\n                  child: const Text('Ubah Foto'),\n                ),\n              ),\n              sizedBoxH20,\n              SizedBox(\n                width: Get.width,\n                height: 40.0,\n                child: ElevatedButton(\n                  style: ElevatedButton.styleFrom(\n                    shape: RoundedRectangleBorder(\n                        borderRadius: BorderRadius.circular(6)),\n                    primary: Colors.green,\n                    onPrimary: Colors.white,\n                  ),\n                  onPressed: () {\n                    _uploadData(_outstation);\n                  },\n                  child: const Text('Kirim'),\n                ),\n              ),\n              sizedBoxH20,\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/change_paid_leave_photo_screen.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:get/get.dart';\nimport 'package:image_picker/image_picker.dart';\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/models/paid_leave.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/bottom_nav_screen.dart';\nimport 'package:spo_balaesang/screen/image_detail_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/file_util.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/employee_proposal_info_widget.dart';\nimport 'package:spo_balaesang/widgets/image_placeholder_widget.dart';\n\nclass ChangePaidLeavePhotoScreen extends StatefulWidget {\n  const ChangePaidLeavePhotoScreen({this.paidLeave});\n\n  final PaidLeave paidLeave;\n\n  @override\n  _ChangePaidLeavePhotoScreenState createState() =>\n      _ChangePaidLeavePhotoScreenState();\n}\n\nclass _ChangePaidLeavePhotoScreenState\n    extends State<ChangePaidLeavePhotoScreen> {\n  String _base64Image;\n  String _fileName;\n  File _tmpFile;\n  PaidLeave _paidLeave;\n\n  Future<void> _openCamera() async {\n    final picture = await ImagePicker().getImage(source: ImageSource.camera);\n    final file = await compressAndGetFile(File(picture.path),\n        '/storage/emulated/0/Android/data/com.banuacoders.siap/files/Pictures/images.jpg');\n    setState(() {\n      _tmpFile = file;\n      _base64Image = base64Encode(_tmpFile.readAsBytesSync());\n      _fileName = _tmpFile.path.split('/').last;\n    });\n    await file.delete(recursive: true);\n  }\n\n  Future<void> _uploadData(PaidLeave paidLeave) async {\n    final ProgressDialog pd = ProgressDialog(context, isDismissible: false);\n    try {\n      pd.show();\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final Map<String, dynamic> data = {\n        'photo': _base64Image,\n        'file_name': _fileName,\n        'paid_leave_id': paidLeave.id\n      };\n      final Map<String, dynamic> _res =\n          await dataRepo.changePaidLeavePhoto(data);\n      if (_res['success'] as bool) {\n        pd.hide();\n        showAlertDialog('success', \"Sukses\", _res['message'].toString(),\n            dismissible: false);\n        Timer(\n            const Duration(seconds: 5), () => Get.off(() => BottomNavScreen()));\n      } else {\n        if (pd.isShowing()) pd.hide();\n        showErrorDialog(_res);\n      }\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n      pd.hide();\n    }\n  }\n\n  Widget _showImage() {\n    if (_base64Image == null) {\n      return InkWell(\n        onTap: () {\n          Get.to(() => ImageDetailScreen(\n                imageUrl: _paidLeave.photo,\n                tag: _paidLeave.id.toString(),\n              ));\n        },\n        child: Hero(\n          tag: _paidLeave.id.toString(),\n          child: ClipRRect(\n            borderRadius: BorderRadius.circular(10.0),\n            child: CachedNetworkImage(\n              placeholder: (_, __) => const ImagePlaceholderWidget(\n                label: 'Memuat Foto',\n                child: SpinKitFadingCircle(\n                  size: 25.0,\n                  color: Colors.blueAccent,\n                ),\n              ),\n              imageUrl: _paidLeave.photo,\n              fit: BoxFit.cover,\n              errorWidget: (_, __, ___) => const ImagePlaceholderWidget(\n                label: 'Gagal memuat foto!',\n                child: Icon(\n                  Icons.image_not_supported_rounded,\n                  color: Colors.grey,\n                ),\n              ),\n              width: Get.width,\n              height: 250.0,\n            ),\n          ),\n        ),\n      );\n    }\n\n    final Uint8List bytes = base64Decode(_base64Image);\n    return InkWell(\n      onTap: () {\n        Get.to(() => ImageDetailScreen(\n              bytes: bytes,\n              tag: 'image',\n            ));\n      },\n      child: Hero(\n        tag: 'image',\n        child: ClipRRect(\n          borderRadius: BorderRadius.circular(10.0),\n          child: Image.memory(\n            bytes,\n            width: Get.width,\n            height: 250,\n            fit: BoxFit.cover,\n          ),\n        ),\n      ),\n    );\n  }\n\n  @override\n  void initState() {\n    _paidLeave = widget.paidLeave;\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      backgroundColor: Colors.white,\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Perbarui Lampiran'),\n      ),\n      body: SingleChildScrollView(\n        child: Padding(\n          padding: const EdgeInsets.all(8.0),\n          child: Column(\n            mainAxisAlignment: MainAxisAlignment.spaceAround,\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              const Text(\n                  'Pastikan gembar yang akan dikirim adalah surat pengajuan cuti atau surat keterangan dokter ' +\n                      'yang sudah ditandatangani oleh pihak berwenang disertai dengan cap resmi.'),\n              sizedBoxH20,\n              EmployeeProposalInfoWidget(\n                title: _paidLeave.title,\n                startDate: _paidLeave.startDate,\n                dueDate: _paidLeave.dueDate,\n                label: _paidLeave.category,\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Foto Surat Pengajuan'),\n                  SizedBox(width: 5.0),\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Lampirkan foto surat pengajuan atau surat lainnya seperti surat keterangan dokter, dsb.',\n                style: TextStyle(color: Colors.grey),\n              ),\n              const Text(\n                '*tekan untuk memperbesar',\n                style: TextStyle(\n                    fontSize: 12.0,\n                    color: Colors.black87,\n                    fontStyle: FontStyle.italic),\n              ),\n              sizedBoxH20,\n              _showImage(),\n              sizedBoxH20,\n              SizedBox(\n                width: Get.width,\n                height: 40.0,\n                child: ElevatedButton(\n                  style: ElevatedButton.styleFrom(\n                    shape: RoundedRectangleBorder(\n                        borderRadius: BorderRadius.circular(6)),\n                    primary: Colors.blueAccent,\n                    onPrimary: Colors.white,\n                  ),\n                  onPressed: _openCamera,\n                  child: const Text('Ubah Foto'),\n                ),\n              ),\n              sizedBoxH20,\n              SizedBox(\n                width: Get.width,\n                height: 40.0,\n                child: ElevatedButton(\n                  style: ElevatedButton.styleFrom(\n                    shape: RoundedRectangleBorder(\n                        borderRadius: BorderRadius.circular(6)),\n                    primary: Colors.green,\n                    onPrimary: Colors.white,\n                  ),\n                  onPressed: () {\n                    _uploadData(_paidLeave);\n                  },\n                  child: const Text('Kirim'),\n                ),\n              ),\n              sizedBoxH20,\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/change_pass_screen.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\n\nimport 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'package:http/http.dart' as http;\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/bottom_nav_screen.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\n\nclass ChangePasswordScreen extends StatefulWidget {\n  @override\n  _ChangePasswordScreenState createState() => _ChangePasswordScreenState();\n}\n\nclass _ChangePasswordScreenState extends State<ChangePasswordScreen> {\n  final TextEditingController _oldPassCtrl = TextEditingController();\n  final TextEditingController _newPassCtrl = TextEditingController();\n  final TextEditingController _newPass2Ctrl = TextEditingController();\n\n  final GlobalKey<FormState> _formKey = GlobalKey();\n\n  bool isLoading = false;\n  bool isOldPassVisible = false;\n  bool isNewPassVisible = false;\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      resizeToAvoidBottomInset: false,\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Ubah Password'),\n      ),\n      body: Padding(\n        padding: const EdgeInsets.all(8.0),\n        child: Stack(\n          children: <Widget>[\n            SizedBox(\n              height: Get.height,\n              child: Form(\n                  key: _formKey,\n                  child: ListView(\n                    children: <Widget>[\n                      Column(\n                        crossAxisAlignment: CrossAxisAlignment.start,\n                        children: <Widget>[\n                          const Text('Password Lama',\n                              style: TextStyle(fontSize: 12.0)),\n                          TextFormField(\n                            controller: _oldPassCtrl,\n                            validator: (String value) {\n                              return value.isEmpty\n                                  ? 'Password lama tidak boleh kosong!'\n                                  : null;\n                            },\n                            obscureText: !isNewPassVisible,\n                            decoration: InputDecoration(\n                                suffixIcon: IconButton(\n                                    icon: Icon(isNewPassVisible\n                                        ? Icons.visibility_off\n                                        : Icons.visibility),\n                                    onPressed: () {\n                                      setState(() {\n                                        isNewPassVisible = !isNewPassVisible;\n                                      });\n                                    }),\n                                hintText: 'Password Lama'),\n                          ),\n                        ],\n                      ),\n                      const SizedBox(height: 20.0),\n                      Column(\n                        crossAxisAlignment: CrossAxisAlignment.start,\n                        children: <Widget>[\n                          const Text('Password Baru',\n                              style: TextStyle(fontSize: 12.0)),\n                          TextFormField(\n                            controller: _newPassCtrl,\n                            validator: (String value) {\n                              if (value.isEmpty) {\n                                return 'Password baru tidak boleh kosong!';\n                              }\n                              if (value == _oldPassCtrl.text) {\n                                return 'Password baru tidak boleh sama dengan Password Lama!';\n                              }\n                              return null;\n                            },\n                            obscureText: !isOldPassVisible,\n                            decoration: InputDecoration(\n                                suffixIcon: IconButton(\n                                    icon: Icon(isOldPassVisible\n                                        ? Icons.visibility_off\n                                        : Icons.visibility),\n                                    onPressed: () {\n                                      setState(() {\n                                        isOldPassVisible = !isOldPassVisible;\n                                      });\n                                    }),\n                                hintText: 'Password Baru'),\n                          ),\n                        ],\n                      ),\n                      const SizedBox(height: 20.0),\n                      Column(\n                        crossAxisAlignment: CrossAxisAlignment.start,\n                        children: <Widget>[\n                          const Text('Ulangi Password Baru',\n                              style: TextStyle(fontSize: 12.0)),\n                          TextFormField(\n                            controller: _newPass2Ctrl,\n                            validator: (String value) {\n                              if (value.isEmpty) {\n                                return 'Konfirmasi Password baru tidak boleh kosong!';\n                              }\n                              if (value != _newPassCtrl.text) {\n                                return 'Kolom ini harus sama dengan password baru!';\n                              }\n                              return null;\n                            },\n                            obscureText: true,\n                            decoration: const InputDecoration(\n                                hintText: 'Konfirmasi Password Baru'),\n                          ),\n                        ],\n                      )\n                    ],\n                  )),\n            ),\n            Positioned(\n              left: 0,\n              right: 0,\n              bottom: MediaQuery.of(context).viewInsets.bottom,\n              child: ElevatedButton(\n                style: ElevatedButton.styleFrom(\n                  onPrimary: Colors.white,\n                  shape: RoundedRectangleBorder(\n                      borderRadius: BorderRadius.circular(10.0)),\n                  primary: Colors.blueAccent,\n                ),\n                onPressed: () async {\n                  final ProgressDialog pd =\n                      ProgressDialog(context, isDismissible: false);\n                  pd.show();\n                  try {\n                    final dataRepo =\n                        Provider.of<DataRepository>(context, listen: false);\n                    final Map<String, dynamic> data = {\n                      'old_pass': _oldPassCtrl.value.text,\n                      'new_pass': _newPassCtrl.value.text,\n                      'new_pass_conf': _newPass2Ctrl.value.text\n                    };\n                    final http.Response response =\n                        await dataRepo.changePass(data);\n                    final Map<String, dynamic> _res =\n                        jsonDecode(response.body) as Map<String, dynamic>;\n                    if (response.statusCode == 200) {\n                      pd.hide();\n                      showAlertDialog(\n                          'success', \"Sukses\", _res['message'].toString(),\n                          dismissible: false);\n                      Timer(const Duration(seconds: 1),\n                          () => Get.off(() => BottomNavScreen()));\n                    } else {\n                      pd.hide();\n                      showErrorDialog(_res);\n                    }\n                  } catch (e) {\n                    pd.hide();\n                    showErrorDialog({\n                      'message': 'Kesalahan',\n                      'errors': {\n                        'exception': ['Terjadi kesalahan!']\n                      }\n                    });\n                  }\n                },\n                child: isLoading\n                    ? const SizedBox(\n                        height: 30.0,\n                        width: 30.0,\n                        child: CircularProgressIndicator(\n                          valueColor:\n                              AlwaysStoppedAnimation<Color>(Colors.white),\n                        ),\n                      )\n                    : const Text('SIMPAN'),\n              ),\n            )\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/create_notification_screen.dart",
    "content": "import 'dart:async';\n\nimport 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/bottom_nav_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\n\nclass CreateNotificationScreen extends StatefulWidget {\n  @override\n  _CreateNotificationScreenState createState() =>\n      _CreateNotificationScreenState();\n}\n\nclass _CreateNotificationScreenState extends State<CreateNotificationScreen> {\n  final TextEditingController _titleController = TextEditingController();\n  final TextEditingController _descriptionController = TextEditingController();\n\n  Future<void> _sendNotification() async {\n    final ProgressDialog pd = ProgressDialog(context, isDismissible: false);\n    try {\n      pd.show();\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final Map<String, dynamic> data = {\n        'title': _titleController.value.text,\n        'content': _descriptionController.value.text,\n      };\n      final Map<String, dynamic> _res = await dataRepo.sendNotification(data);\n      if (_res['success'] as bool) {\n        pd.hide();\n        showAlertDialog('success', \"Sukses\", _res['message'].toString(),\n            dismissible: false);\n        Timer(\n            const Duration(seconds: 5), () => Get.off(() => BottomNavScreen()));\n      } else {\n        if (pd.isShowing()) pd.hide();\n        showErrorDialog(_res);\n      }\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n      pd.hide();\n    }\n  }\n\n  @override\n  void dispose() {\n    _titleController.dispose();\n    _descriptionController.dispose();\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Buat Pemberitahuan'),\n      ),\n      body: SingleChildScrollView(\n        child: Padding(\n          padding: const EdgeInsets.all(8.0),\n          child: Column(\n            mainAxisAlignment: MainAxisAlignment.spaceAround,\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              Row(\n                children: const <Widget>[\n                  Text('Judul Pemberitahuan'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              TextFormField(\n                keyboardType: TextInputType.text,\n                controller: _titleController,\n                decoration: const InputDecoration(\n                    alignLabelWithHint: true,\n                    hintText: 'Judul',\n                    focusedBorder: UnderlineInputBorder(\n                        borderSide: BorderSide(color: Colors.blueAccent)),\n                    labelStyle: TextStyle(color: Colors.grey)),\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Isi Pemberitahuan'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              TextFormField(\n                keyboardType: TextInputType.multiline,\n                minLines: 1,\n                maxLines: 5,\n                controller: _descriptionController,\n                decoration: const InputDecoration(\n                    alignLabelWithHint: true,\n                    hintText: 'Deskripsi',\n                    focusedBorder: UnderlineInputBorder(\n                        borderSide: BorderSide(color: Colors.blueAccent)),\n                    labelStyle: TextStyle(color: Colors.grey)),\n              ),\n              sizedBoxH20,\n              SizedBox(\n                width: Get.width,\n                height: 40.0,\n                child: ElevatedButton(\n                  style: ElevatedButton.styleFrom(\n                    shape: RoundedRectangleBorder(\n                        borderRadius: BorderRadius.circular(6)),\n                    primary: Colors.blueAccent,\n                    onPrimary: Colors.white,\n                  ),\n                  onPressed: _sendNotification,\n                  child: const Text('Kirim'),\n                ),\n              ),\n              sizedBoxH20,\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/create_outstation_screen.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'package:image_picker/image_picker.dart';\nimport 'package:intl/intl.dart';\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/bottom_nav_screen.dart';\nimport 'package:spo_balaesang/screen/image_detail_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/file_util.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/image_placeholder_widget.dart';\nimport 'package:table_calendar/table_calendar.dart';\n\nclass CreateOutstationScreen extends StatefulWidget {\n  @override\n  _CreateOutstationScreenState createState() => _CreateOutstationScreenState();\n}\n\nclass _CreateOutstationScreenState extends State<CreateOutstationScreen> {\n  String _base64Image;\n  String _fileName;\n  File _tmpFile;\n  DateTime _dueDate = DateTime.now();\n  DateTime _startDate = DateTime.now();\n  bool _isDateChange = false;\n\n  final TextEditingController _titleController = TextEditingController();\n  final TextEditingController _descriptionController = TextEditingController();\n\n  Future<void> _openCamera() async {\n    final picture = await ImagePicker().getImage(source: ImageSource.camera);\n    final file = await compressAndGetFile(File(picture.path),\n        '/storage/emulated/0/Android/data/com.banuacoders.siap/files/Pictures/images.jpg');\n    setState(() {\n      _tmpFile = file;\n      _base64Image = base64Encode(_tmpFile.readAsBytesSync());\n      _fileName = _tmpFile.path.split('/').last;\n    });\n    await file.delete(recursive: true);\n  }\n\n  Future<void> _uploadData() async {\n    if (!_isDateChange) {\n      showAlertDialog('failed', 'Pelanggaran', 'Pilih tanggal terlebih dahulu!',\n          dismissible: true);\n    } else {\n      final ProgressDialog pd = ProgressDialog(context, isDismissible: false);\n      try {\n        pd.show();\n        final dataRepo = Provider.of<DataRepository>(context, listen: false);\n        final Map<String, dynamic> data = {\n          'title': _titleController.value.text,\n          'description': _descriptionController.value.text,\n          'photo': _base64Image,\n          'due_date': _dueDate.toIso8601String(),\n          'start_date': _startDate.toIso8601String(),\n          'file_name': _fileName\n        };\n        final Map<String, dynamic> _res = await dataRepo.outstation(data);\n        if (_res['success'] as bool) {\n          pd.hide();\n          showAlertDialog('success', \"Sukses\", _res['message'].toString(),\n              dismissible: false);\n          Timer(const Duration(seconds: 5),\n              () => Get.off(() => BottomNavScreen()));\n        } else {\n          if (pd.isShowing()) pd.hide();\n          showErrorDialog(_res);\n        }\n      } catch (e) {\n        showErrorDialog({\n          'message': 'Kesalahan',\n          'errors': {\n            'exception': ['Terjadi kesalahan!']\n          }\n        });\n        pd.hide();\n      }\n    }\n  }\n\n  Widget _showImage() {\n    if (_base64Image == null) {\n      return const ImagePlaceholderWidget(\n        label: 'Ambil Foto',\n        child: Icon(\n          Icons.camera_alt_rounded,\n          color: Colors.grey,\n        ),\n      );\n    }\n\n    final Uint8List bytes = base64Decode(_base64Image);\n    return InkWell(\n      onTap: () {\n        Get.to(() => ImageDetailScreen(\n              bytes: bytes,\n              tag: 'image',\n            ));\n      },\n      child: Hero(\n        tag: 'image',\n        child: ClipRRect(\n          borderRadius: BorderRadius.circular(10.0),\n          child: Image.memory(\n            bytes,\n            width: Get.width,\n            height: 250,\n            fit: BoxFit.cover,\n          ),\n        ),\n      ),\n    );\n  }\n\n  Future<void> _selectDueDate() async {\n    Get.defaultDialog(\n        title: 'Pilih Tanggal Selesai',\n        content: SizedBox(\n          width: Get.width * 0.9,\n          height: Get.height * 0.4,\n          child: TableCalendar(\n            availableCalendarFormats: const <CalendarFormat, String>{\n              CalendarFormat.month: '1 bulan',\n            },\n            calendarStyle: const CalendarStyle(\n              weekendTextStyle: TextStyle(color: Colors.red),\n            ),\n            calendarBuilders: const CalendarBuilders(\n              dowBuilder: dowBuilder,\n            ),\n            calendarFormat: CalendarFormat.month,\n            availableGestures: AvailableGestures.horizontalSwipe,\n            shouldFillViewport: true,\n            headerStyle: const HeaderStyle(titleCentered: true),\n            startingDayOfWeek: StartingDayOfWeek.monday,\n            firstDay: DateTime.now().subtract(const Duration(days: 7)),\n            focusedDay: _dueDate,\n            selectedDayPredicate: (day) {\n              return isSameDay(_dueDate, day);\n            },\n            lastDay: DateTime(DateTime.now().year + 5),\n            locale: 'in_ID',\n            onDaySelected: (day, focusedDay) {\n              Get.back();\n              setState(() {\n                if (!_isDateChange) {\n                  _isDateChange = true;\n                }\n                _dueDate = day;\n                if (_dueDate.isBefore(_startDate)) {\n                  _startDate = day;\n                }\n              });\n            },\n          ),\n        ));\n  }\n\n  Future<void> _selectStartDate() async {\n    Get.defaultDialog(\n        title: 'Pilih Tanggal Mulai',\n        content: SizedBox(\n          width: Get.width * 0.9,\n          height: Get.height * 0.4,\n          child: TableCalendar(\n            availableCalendarFormats: const <CalendarFormat, String>{\n              CalendarFormat.month: '1 bulan',\n            },\n            calendarStyle: const CalendarStyle(\n              weekendTextStyle: TextStyle(color: Colors.red),\n            ),\n            calendarBuilders: const CalendarBuilders(\n              dowBuilder: dowBuilder,\n            ),\n            shouldFillViewport: true,\n            calendarFormat: CalendarFormat.month,\n            availableGestures: AvailableGestures.horizontalSwipe,\n            headerStyle: const HeaderStyle(titleCentered: true),\n            startingDayOfWeek: StartingDayOfWeek.monday,\n            firstDay: DateTime.now().subtract(const Duration(days: 7)),\n            focusedDay: _startDate,\n            lastDay: DateTime(DateTime.now().year + 5),\n            locale: 'in_ID',\n            selectedDayPredicate: (day) {\n              return isSameDay(_startDate, day);\n            },\n            onDaySelected: (day, focusedDay) {\n              Get.back();\n              setState(() {\n                if (!_isDateChange) {\n                  _isDateChange = true;\n                }\n                _startDate = day;\n                if (_startDate.isAfter(_dueDate)) {\n                  _dueDate = day;\n                }\n              });\n            },\n          ),\n        ));\n  }\n\n  @override\n  void dispose() {\n    _titleController.dispose();\n    _descriptionController.dispose();\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      backgroundColor: Colors.white,\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Ajukan Dinas Luar'),\n      ),\n      body: SingleChildScrollView(\n        child: Padding(\n          padding: const EdgeInsets.all(8.0),\n          child: Column(\n            mainAxisAlignment: MainAxisAlignment.spaceAround,\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              const Text(\n                  'Pastikan data yang dikirim sudah benar. Anda tidak dapat mengubah dokumen setelah dikirim. Jika terjadi kesalahan, hubungi administrator sistem.'),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Judul Surat'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              TextFormField(\n                keyboardType: TextInputType.text,\n                controller: _titleController,\n                decoration: const InputDecoration(\n                    alignLabelWithHint: true,\n                    hintText: 'Judul',\n                    focusedBorder: UnderlineInputBorder(\n                        borderSide: BorderSide(color: Colors.blueAccent)),\n                    labelStyle: TextStyle(color: Colors.grey)),\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Deskripsi'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Jelaskan secara singkat tentang Dinas Luar yang diajukan!',\n                style: TextStyle(color: Colors.grey),\n              ),\n              TextFormField(\n                keyboardType: TextInputType.multiline,\n                minLines: 1,\n                maxLines: 5,\n                controller: _descriptionController,\n                decoration: const InputDecoration(\n                    alignLabelWithHint: true,\n                    hintText: 'Deskripsi',\n                    focusedBorder: UnderlineInputBorder(\n                        borderSide: BorderSide(color: Colors.blueAccent)),\n                    labelStyle: TextStyle(color: Colors.grey)),\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Tanggal Mulai'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Pilih kapan tugas Dinas Luar mulai!',\n                style: TextStyle(color: Colors.grey),\n              ),\n              Row(\n                children: <Widget>[\n                  Expanded(\n                    child: Text(\n                      DateFormat('EEEE, d MMMM y').format(_startDate),\n                      style: const TextStyle(fontWeight: FontWeight.w600),\n                    ),\n                  ),\n                  IconButton(\n                      icon: const Icon(\n                        Icons.calendar_today_rounded,\n                        color: Colors.blueAccent,\n                      ),\n                      onPressed: () {\n                        _selectStartDate();\n                      })\n                ],\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Tanggal Selesai'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Pilih sampai kapan Dinas Luar yang diajukan berlaku!',\n                style: TextStyle(color: Colors.grey),\n              ),\n              Row(\n                children: <Widget>[\n                  Expanded(\n                    child: Text(\n                      DateFormat('EEEE, d MMMM y').format(_dueDate),\n                      style: const TextStyle(fontWeight: FontWeight.w600),\n                    ),\n                  ),\n                  IconButton(\n                      icon: const Icon(\n                        Icons.calendar_today_rounded,\n                        color: Colors.blueAccent,\n                      ),\n                      onPressed: () {\n                        _selectDueDate();\n                      })\n                ],\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Foto Surat Tugas'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Lampirkan foto surat tugas atau Surat Perintah Perjalanan Dinas (SPPD).',\n                style: TextStyle(color: Colors.grey),\n              ),\n              const Text(\n                '*tekan untuk memperbesar',\n                style: TextStyle(\n                    fontSize: 12.0,\n                    color: Colors.black87,\n                    fontStyle: FontStyle.italic),\n              ),\n              sizedBoxH20,\n              _showImage(),\n              sizedBoxH20,\n              SizedBox(\n                width: Get.width,\n                height: 40.0,\n                child: ElevatedButton(\n                  style: ElevatedButton.styleFrom(\n                    shape: RoundedRectangleBorder(\n                        borderRadius: BorderRadius.circular(6)),\n                    primary: Colors.blueAccent,\n                    onPrimary: Colors.white,\n                  ),\n                  onPressed: _openCamera,\n                  child:\n                      Text(_base64Image == null ? 'Ambil Foto' : 'Ubah Foto'),\n                ),\n              ),\n              sizedBoxH20,\n              SizedBox(\n                width: Get.width,\n                height: 40.0,\n                child: ElevatedButton(\n                  style: ElevatedButton.styleFrom(\n                    shape: RoundedRectangleBorder(\n                        borderRadius: BorderRadius.circular(6)),\n                    primary: Colors.green,\n                    onPrimary: Colors.white,\n                  ),\n                  onPressed: _uploadData,\n                  child: const Text('Kirim'),\n                ),\n              ),\n              sizedBoxH20,\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/create_paid_leave_screen.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'package:image_picker/image_picker.dart';\nimport 'package:intl/intl.dart';\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/bottom_nav_screen.dart';\nimport 'package:spo_balaesang/screen/image_detail_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/file_util.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/image_placeholder_widget.dart';\nimport 'package:table_calendar/table_calendar.dart';\n\nclass CreatePaidLeaveScreen extends StatefulWidget {\n  @override\n  _CreatePaidLeaveScreenState createState() => _CreatePaidLeaveScreenState();\n}\n\nclass _CreatePaidLeaveScreenState extends State<CreatePaidLeaveScreen> {\n  String _base64Image;\n  String _fileName;\n  File _tmpFile;\n  DateTime _startDate = DateTime.now();\n  DateTime _dueDate = DateTime.now();\n  final Map<String, dynamic> _categories = paidLeaveCategories;\n  int _category = 1;\n  bool _isDateChange = false;\n\n  final TextEditingController _titleController = TextEditingController();\n  final TextEditingController _descriptionController = TextEditingController();\n\n  Future<void> _openCamera() async {\n    final picture = await ImagePicker().getImage(source: ImageSource.camera);\n    final file = await compressAndGetFile(File(picture.path),\n        '/storage/emulated/0/Android/data/com.banuacoders.siap/files/Pictures/images.jpg');\n    setState(() {\n      _tmpFile = file;\n      _base64Image = base64Encode(_tmpFile.readAsBytesSync());\n      _fileName = _tmpFile.path.split('/').last;\n    });\n    await file.delete(recursive: true);\n  }\n\n  Widget _showImage() {\n    if (_base64Image == null) {\n      return const ImagePlaceholderWidget(\n        label: 'Ambil Foto',\n        child: Icon(\n          Icons.camera_alt_rounded,\n          color: Colors.grey,\n        ),\n      );\n    }\n\n    final Uint8List bytes = base64Decode(_base64Image);\n    return InkWell(\n      onTap: () {\n        Get.to(() => ImageDetailScreen(\n              bytes: bytes,\n              tag: 'image',\n            ));\n      },\n      child: Hero(\n        tag: 'image',\n        child: ClipRRect(\n          borderRadius: BorderRadius.circular(10.0),\n          child: Image.memory(\n            bytes,\n            width: Get.width,\n            height: 250,\n            fit: BoxFit.cover,\n          ),\n        ),\n      ),\n    );\n  }\n\n  Future<void> _uploadData() async {\n    if (!_isDateChange) {\n      showAlertDialog('failed', 'Pelanggaran', 'Pilih tanggal terlebih dahulu!',\n          dismissible: true);\n    } else {\n      final ProgressDialog pd = ProgressDialog(context, isDismissible: false);\n      try {\n        pd.show();\n        final dataRepo = Provider.of<DataRepository>(context, listen: false);\n        final Map<String, dynamic> data = {\n          'category': _category,\n          'title': _titleController.value.text,\n          'description': _descriptionController.value.text,\n          'photo': _base64Image,\n          'due_date': _dueDate.toIso8601String(),\n          'start_date': _startDate.toIso8601String(),\n          'file_name': _fileName\n        };\n        final Map<String, dynamic> _res = await dataRepo.paidLeave(data);\n        if (_res['success'] as bool) {\n          pd.hide();\n          showAlertDialog('success', \"Sukses\", _res['message'].toString(),\n              dismissible: false);\n          Timer(const Duration(seconds: 5),\n              () => Get.off(() => BottomNavScreen()));\n        } else {\n          if (pd.isShowing()) pd.hide();\n          showErrorDialog(_res);\n        }\n      } catch (e) {\n        showErrorDialog({\n          'message': 'Kesalahan',\n          'errors': {\n            'exception': ['Terjadi kesalahan!']\n          }\n        });\n        pd.hide();\n      }\n    }\n  }\n\n  void _selectDueDate() {\n    Get.defaultDialog(\n        title: 'Pilih Tanggal Selesai',\n        content: SizedBox(\n          width: Get.width * 0.9,\n          height: Get.height * 0.4,\n          child: TableCalendar(\n            availableCalendarFormats: const <CalendarFormat, String>{\n              CalendarFormat.month: '1 bulan',\n            },\n            calendarStyle: const CalendarStyle(\n              weekendTextStyle: TextStyle(color: Colors.red),\n            ),\n            calendarBuilders: const CalendarBuilders(\n              dowBuilder: dowBuilder,\n            ),\n            shouldFillViewport: true,\n            calendarFormat: CalendarFormat.month,\n            availableGestures: AvailableGestures.horizontalSwipe,\n            headerStyle: const HeaderStyle(titleCentered: true),\n            startingDayOfWeek: StartingDayOfWeek.monday,\n            firstDay: DateTime.now().subtract(const Duration(days: 7)),\n            focusedDay: _dueDate,\n            lastDay: DateTime.now().add(const Duration(days: 180)),\n            locale: 'in_ID',\n            selectedDayPredicate: (day) {\n              return isSameDay(_dueDate, day);\n            },\n            onDaySelected: (day, focusedDay) {\n              Get.back();\n              setState(() {\n                if (!_isDateChange) {\n                  _isDateChange = true;\n                }\n                _dueDate = day;\n                if (_dueDate.isBefore(_startDate)) {\n                  _startDate = day;\n                }\n              });\n            },\n          ),\n        ));\n  }\n\n  void _selectStartDate() {\n    Get.defaultDialog(\n        title: 'Pilih Tanggal Mulai',\n        content: SizedBox(\n          width: Get.width * 0.9,\n          height: Get.height * 0.4,\n          child: TableCalendar(\n            availableCalendarFormats: const <CalendarFormat, String>{\n              CalendarFormat.month: '1 bulan',\n            },\n            calendarStyle: const CalendarStyle(\n              weekendTextStyle: TextStyle(color: Colors.red),\n            ),\n            calendarBuilders: const CalendarBuilders(\n              dowBuilder: dowBuilder,\n            ),\n            shouldFillViewport: true,\n            calendarFormat: CalendarFormat.month,\n            availableGestures: AvailableGestures.horizontalSwipe,\n            headerStyle: const HeaderStyle(titleCentered: true),\n            startingDayOfWeek: StartingDayOfWeek.monday,\n            firstDay: DateTime.now().subtract(const Duration(days: 7)),\n            focusedDay: _startDate,\n            lastDay: DateTime.now().add(const Duration(days: 180)),\n            locale: 'in_ID',\n            selectedDayPredicate: (day) {\n              return isSameDay(_startDate, day);\n            },\n            onDaySelected: (day, focusedDay) {\n              Get.back();\n              setState(() {\n                if (!_isDateChange) {\n                  _isDateChange = true;\n                }\n                _startDate = day;\n                if (_startDate.isAfter(_dueDate)) {\n                  _dueDate = day;\n                }\n              });\n            },\n          ),\n        ));\n  }\n\n  @override\n  void dispose() {\n    _titleController.dispose();\n    _descriptionController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      backgroundColor: Colors.white,\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Ajukan Cuti'),\n      ),\n      body: SingleChildScrollView(\n        child: Padding(\n          padding: const EdgeInsets.all(8),\n          child: Column(\n            mainAxisAlignment: MainAxisAlignment.spaceAround,\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              const Text(\n                  'Pastikan data yang dikirim sudah benar. Anda tidak dapat mengubah dokumen setelah dikirim. Jika terjadi kesalahan, hubungi administrator sistem.'),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Subjek Cuti'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Ringkasan dari kegiatan cuti anda.',\n                style: TextStyle(color: Colors.grey),\n              ),\n              TextFormField(\n                keyboardType: TextInputType.text,\n                controller: _titleController,\n                decoration: const InputDecoration(\n                    alignLabelWithHint: true,\n                    hintText: 'Judul/Subjek',\n                    focusedBorder: UnderlineInputBorder(\n                        borderSide: BorderSide(color: Colors.blueAccent)),\n                    labelStyle: TextStyle(color: Colors.grey)),\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Jenis Cuti'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              DropdownButton(\n                  isExpanded: true,\n                  elevation: 10,\n                  value: _category,\n                  items: _categories.entries\n                      .map((option) => DropdownMenuItem<dynamic>(\n                            value: option.value,\n                            child: Text(option.key),\n                          ))\n                      .toList(),\n                  onChanged: (value) {\n                    setState(() {\n                      _category = int.parse(value.toString());\n                    });\n                  }),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Deskripsi'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Jelaskan secara singkat tentang cuti yang diajukan!',\n                style: TextStyle(color: Colors.grey),\n              ),\n              TextFormField(\n                keyboardType: TextInputType.multiline,\n                minLines: 1,\n                maxLines: 5,\n                controller: _descriptionController,\n                decoration: const InputDecoration(\n                    alignLabelWithHint: true,\n                    hintText: 'Deskripsi',\n                    focusedBorder: UnderlineInputBorder(\n                        borderSide: BorderSide(color: Colors.blueAccent)),\n                    labelStyle: TextStyle(color: Colors.grey)),\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Tanggal Mulai'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Pilih kapan anda mulai cuti!',\n                style: TextStyle(color: Colors.grey),\n              ),\n              Row(\n                children: <Widget>[\n                  Expanded(\n                    child: Text(\n                      DateFormat('EEEE, d MMMM y').format(_startDate),\n                      style: const TextStyle(\n                        fontWeight: FontWeight.w600,\n                      ),\n                    ),\n                  ),\n                  IconButton(\n                      icon: const Icon(\n                        Icons.calendar_today_rounded,\n                        color: Colors.blueAccent,\n                      ),\n                      onPressed: _selectStartDate)\n                ],\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Tanggal Selesai'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Pilih sampai kapan anda cuti!',\n                style: TextStyle(color: Colors.grey),\n              ),\n              Row(\n                children: <Widget>[\n                  Expanded(\n                    child: Text(\n                      DateFormat('EEEE, d MMMM y').format(_dueDate),\n                      style: const TextStyle(fontWeight: FontWeight.w600),\n                    ),\n                  ),\n                  IconButton(\n                      icon: const Icon(\n                        Icons.calendar_today_rounded,\n                        color: Colors.blueAccent,\n                      ),\n                      onPressed: _selectDueDate)\n                ],\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Foto Surat Pengajuan'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Lampirkan foto surat pengajuan cuti seperti surat keterangan sakit dsb.',\n                style: TextStyle(color: Colors.grey),\n              ),\n              const Text(\n                '*tekan untuk memperbesar',\n                style: TextStyle(\n                    fontSize: 12.0,\n                    color: Colors.black87,\n                    fontStyle: FontStyle.italic),\n              ),\n              sizedBoxH20,\n              _showImage(),\n              sizedBoxH20,\n              SizedBox(\n                width: Get.width,\n                height: 40.0,\n                child: ElevatedButton(\n                  style: ElevatedButton.styleFrom(\n                    shape: RoundedRectangleBorder(\n                        borderRadius: BorderRadius.circular(6)),\n                    primary: Colors.blueAccent,\n                    onPrimary: Colors.white,\n                  ),\n                  onPressed: _openCamera,\n                  child:\n                      Text(_base64Image == null ? 'Ambil Foto' : 'Ubah Foto'),\n                ),\n              ),\n              sizedBoxH20,\n              SizedBox(\n                width: Get.width,\n                height: 40.0,\n                child: ElevatedButton(\n                  style: ElevatedButton.styleFrom(\n                    shape: RoundedRectangleBorder(\n                        borderRadius: BorderRadius.circular(6)),\n                    primary: Colors.green,\n                    onPrimary: Colors.white,\n                  ),\n                  onPressed: _uploadData,\n                  child: const Text('Kirim'),\n                ),\n              ),\n              sizedBoxH20,\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/create_permission_screen.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'package:image_picker/image_picker.dart';\nimport 'package:intl/intl.dart';\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/bottom_nav_screen.dart';\nimport 'package:spo_balaesang/screen/image_detail_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/file_util.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/image_placeholder_widget.dart';\nimport 'package:table_calendar/table_calendar.dart';\n\nclass CreatePermissionScreen extends StatefulWidget {\n  @override\n  _CreatePermissionScreenState createState() => _CreatePermissionScreenState();\n}\n\nclass _CreatePermissionScreenState extends State<CreatePermissionScreen> {\n  String _base64Image;\n  String _fileName;\n  File _tmpFile;\n  DateTime _dueDate = DateTime.now();\n  DateTime _startDate = DateTime.now();\n  bool _isDateChange = false;\n\n  final TextEditingController _titleController = TextEditingController();\n  final TextEditingController _descriptionController = TextEditingController();\n\n  Future<void> _openCamera() async {\n    final picture = await ImagePicker().getImage(source: ImageSource.camera);\n    final file = await compressAndGetFile(File(picture.path),\n        '/storage/emulated/0/Android/data/com.banuacoders.siap/files/Pictures/images.jpg');\n    setState(() {\n      _tmpFile = file;\n      _base64Image = base64Encode(_tmpFile.readAsBytesSync());\n      _fileName = _tmpFile.path.split('/').last;\n    });\n    await file.delete(recursive: true);\n  }\n\n  Future<void> _uploadData() async {\n    if (!_isDateChange) {\n      showAlertDialog('failed', 'Pelanggaran', 'Pilih tanggal terlebih dahulu!',\n          dismissible: true);\n    } else {\n      final ProgressDialog pd = ProgressDialog(context, isDismissible: false);\n      try {\n        pd.show();\n        final dataRepo = Provider.of<DataRepository>(context, listen: false);\n        final Map<String, dynamic> data = {\n          'title': _titleController.value.text,\n          'description': _descriptionController.value.text,\n          'photo': _base64Image,\n          'due_date': _dueDate.toIso8601String(),\n          'start_date': _startDate.toIso8601String(),\n          'file_name': _fileName\n        };\n        final Map<String, dynamic> _res = await dataRepo.permission(data);\n        if (_res['success'] as bool) {\n          pd.hide();\n          showAlertDialog('success', \"Sukses\", _res['message'].toString(),\n              dismissible: false);\n          Timer(const Duration(seconds: 5),\n              () => Get.off(() => BottomNavScreen()));\n        } else {\n          if (pd.isShowing()) pd.hide();\n          showErrorDialog(_res);\n        }\n      } catch (e) {\n        showErrorDialog({\n          'message': 'Kesalahan',\n          'errors': {\n            'exception': ['Terjadi kesalahan!']\n          }\n        });\n        pd.hide();\n      }\n    }\n  }\n\n  Widget _showImage() {\n    if (_base64Image == null) {\n      return const ImagePlaceholderWidget(\n        label: 'Ambil Foto',\n        child: Icon(\n          Icons.camera_alt_rounded,\n          color: Colors.grey,\n        ),\n      );\n    }\n\n    final Uint8List bytes = base64Decode(_base64Image);\n    return InkWell(\n      onTap: () {\n        Get.to(() => ImageDetailScreen(\n              bytes: bytes,\n              tag: 'image',\n            ));\n      },\n      child: Hero(\n        tag: 'image',\n        child: ClipRRect(\n          borderRadius: BorderRadius.circular(10.0),\n          child: Image.memory(\n            bytes,\n            width: Get.width,\n            height: 250,\n            fit: BoxFit.cover,\n          ),\n        ),\n      ),\n    );\n  }\n\n  Future<void> _selectDueDate() async {\n    Get.defaultDialog(\n        title: 'Pilih Tanggal Selesai',\n        content: SizedBox(\n          width: Get.width * 0.9,\n          height: Get.height * 0.4,\n          child: TableCalendar(\n            availableCalendarFormats: const <CalendarFormat, String>{\n              CalendarFormat.month: '1 bulan',\n            },\n            calendarStyle: const CalendarStyle(\n              weekendTextStyle: TextStyle(color: Colors.red),\n            ),\n            calendarBuilders: const CalendarBuilders(\n              dowBuilder: dowBuilder,\n            ),\n            shouldFillViewport: true,\n            calendarFormat: CalendarFormat.month,\n            availableGestures: AvailableGestures.horizontalSwipe,\n            headerStyle: const HeaderStyle(titleCentered: true),\n            startingDayOfWeek: StartingDayOfWeek.monday,\n            firstDay: DateTime.now().subtract(const Duration(days: 7)),\n            focusedDay: _dueDate,\n            lastDay: DateTime.now().add(const Duration(days: 180)),\n            locale: 'in_ID',\n            selectedDayPredicate: (day) {\n              return isSameDay(_dueDate, day);\n            },\n            onDaySelected: (day, focusedDay) {\n              Get.back();\n              setState(() {\n                if (!_isDateChange) {\n                  _isDateChange = true;\n                }\n                _dueDate = day;\n                if (_dueDate.isBefore(_startDate)) {\n                  _startDate = day;\n                }\n              });\n            },\n          ),\n        ));\n  }\n\n  Future<void> _selectStartDate() async {\n    Get.defaultDialog(\n        title: 'Pilih Tanggal Mulai',\n        content: SizedBox(\n          height: Get.height * 0.4,\n          width: Get.width * 0.9,\n          child: TableCalendar(\n            availableCalendarFormats: const <CalendarFormat, String>{\n              CalendarFormat.month: '1 bulan',\n            },\n            calendarStyle: const CalendarStyle(\n              weekendTextStyle: TextStyle(color: Colors.red),\n            ),\n            calendarBuilders: const CalendarBuilders(\n              dowBuilder: dowBuilder,\n            ),\n            shouldFillViewport: true,\n            calendarFormat: CalendarFormat.month,\n            availableGestures: AvailableGestures.horizontalSwipe,\n            headerStyle: const HeaderStyle(titleCentered: true),\n            startingDayOfWeek: StartingDayOfWeek.monday,\n            firstDay: DateTime.now().subtract(const Duration(days: 7)),\n            focusedDay: _startDate,\n            lastDay: DateTime.now().add(const Duration(days: 180)),\n            locale: 'in_ID',\n            selectedDayPredicate: (day) {\n              return isSameDay(_startDate, day);\n            },\n            onDaySelected: (day, focusedDay) {\n              Get.back();\n              setState(() {\n                if (!_isDateChange) {\n                  _isDateChange = true;\n                }\n                _startDate = day;\n                if (_startDate.isAfter(_dueDate)) {\n                  _dueDate = day;\n                }\n              });\n            },\n          ),\n        ));\n  }\n\n  @override\n  void dispose() {\n    _titleController.dispose();\n    _descriptionController.dispose();\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      backgroundColor: Colors.white,\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Buat Izin'),\n      ),\n      body: SingleChildScrollView(\n        child: Padding(\n          padding: const EdgeInsets.all(8.0),\n          child: Column(\n            mainAxisAlignment: MainAxisAlignment.spaceAround,\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              const Text(\n                  'Pastikan data yang dikirim sudah benar. Anda tidak dapat mengubah dokumen setelah dikirim. Jika terjadi kesalahan, hubungi administrator sistem.'),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Judul Surat'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Mis: Izin Sakit, dsb.',\n                style: TextStyle(color: Colors.grey),\n              ),\n              TextFormField(\n                keyboardType: TextInputType.text,\n                controller: _titleController,\n                decoration: const InputDecoration(\n                    alignLabelWithHint: true,\n                    hintText: 'Judul',\n                    focusedBorder: UnderlineInputBorder(\n                        borderSide: BorderSide(color: Colors.blueAccent)),\n                    labelStyle: TextStyle(color: Colors.grey)),\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Deskripsi'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Jelaskan secara singkat tentang izin yang diajukan!',\n                style: TextStyle(color: Colors.grey),\n              ),\n              TextFormField(\n                keyboardType: TextInputType.multiline,\n                minLines: 1,\n                maxLines: 5,\n                controller: _descriptionController,\n                decoration: const InputDecoration(\n                    alignLabelWithHint: true,\n                    hintText: 'Deskripsi',\n                    focusedBorder: UnderlineInputBorder(\n                        borderSide: BorderSide(color: Colors.blueAccent)),\n                    labelStyle: TextStyle(color: Colors.grey)),\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Tanggal Izin'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Pilih kapan izin mulai berlaku!',\n                style: TextStyle(color: Colors.grey),\n              ),\n              Row(\n                children: <Widget>[\n                  Expanded(\n                    child: Text(\n                      DateFormat('EEEE, d MMMM y').format(_startDate),\n                      style: const TextStyle(fontWeight: FontWeight.w600),\n                    ),\n                  ),\n                  IconButton(\n                      icon: const Icon(\n                        Icons.calendar_today_rounded,\n                        color: Colors.blueAccent,\n                      ),\n                      onPressed: () {\n                        _selectStartDate();\n                      })\n                ],\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Tanggal Kadaluarsa'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Pilih sampai kapan izin yang diajukan berlaku!',\n                style: TextStyle(color: Colors.grey),\n              ),\n              Row(\n                children: <Widget>[\n                  Expanded(\n                    child: Text(\n                      DateFormat('EEEE, d MMMM y').format(_dueDate),\n                      style: const TextStyle(fontWeight: FontWeight.w600),\n                    ),\n                  ),\n                  IconButton(\n                      icon: const Icon(\n                        Icons.calendar_today_rounded,\n                        color: Colors.blueAccent,\n                      ),\n                      onPressed: () {\n                        _selectDueDate();\n                      })\n                ],\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Foto Surat Izin'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Lampirkan foto surat izin atau surat lainnya seperti surat keterangan sakit dsb.',\n                style: TextStyle(color: Colors.grey),\n              ),\n              const Text(\n                '*tekan untuk memperbesar',\n                style: TextStyle(\n                    fontSize: 12.0,\n                    color: Colors.black87,\n                    fontStyle: FontStyle.italic),\n              ),\n              sizedBoxH20,\n              _showImage(),\n              sizedBoxH20,\n              SizedBox(\n                width: Get.width,\n                height: 40.0,\n                child: ElevatedButton(\n                  style: ElevatedButton.styleFrom(\n                    shape: RoundedRectangleBorder(\n                        borderRadius: BorderRadius.circular(6)),\n                    primary: Colors.blueAccent,\n                    onPrimary: Colors.white,\n                  ),\n                  onPressed: _openCamera,\n                  child:\n                      Text(_base64Image == null ? 'Ambil Foto' : 'Ubah Foto'),\n                ),\n              ),\n              sizedBoxH20,\n              SizedBox(\n                width: Get.width,\n                height: 40.0,\n                child: ElevatedButton(\n                  style: ElevatedButton.styleFrom(\n                    shape: RoundedRectangleBorder(\n                        borderRadius: BorderRadius.circular(6)),\n                    primary: Colors.green,\n                    onPrimary: Colors.white,\n                  ),\n                  onPressed: _uploadData,\n                  child: const Text('Kirim'),\n                ),\n              ),\n              sizedBoxH20,\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/employee_attendance_screen.dart",
    "content": "import 'package:flare_flutter/flare_actor.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:get/get.dart';\nimport 'package:provider/provider.dart';\nimport 'package:search_page/search_page.dart';\nimport 'package:spo_balaesang/models/employee.dart';\nimport 'package:spo_balaesang/models/user.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/report_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\n\nclass EmployeeAttendanceScreen extends StatefulWidget {\n  @override\n  _EmployeeAttendanceScreenState createState() =>\n      _EmployeeAttendanceScreenState();\n}\n\nclass _EmployeeAttendanceScreenState extends State<EmployeeAttendanceScreen> {\n  List<Employee> _employees;\n  bool _isLoading = false;\n\n  @override\n  void setState(void Function() fn) {\n    if (mounted) super.setState(fn);\n  }\n\n  void _setLoading(bool isLoading) {\n    setState(() {\n      _isLoading = isLoading;\n    });\n  }\n\n  Future<void> loadData() async {\n    try {\n      _setLoading(true);\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final List<Employee> _jsonEmployees = await dataRepo.getAllEmployee();\n      setState(() {\n        _employees = _jsonEmployees;\n      });\n    } catch (e) {} finally {\n      _setLoading(false);\n    }\n  }\n\n  @override\n  void initState() {\n    loadData();\n    super.initState();\n  }\n\n  Widget _buildPnsSection(Employee employee) {\n    if (employee.status != 'PNS') {\n      return sizedBox;\n    }\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: <Widget>[\n        sizedBoxH4,\n        Row(\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          children: <Widget>[\n            Text(\n              'Golongan',\n              style: TextStyle(color: Colors.grey[600]),\n            ),\n            Text(employee.group ?? '')\n          ],\n        ),\n        sizedBoxH4,\n        Row(\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          children: <Widget>[\n            Text(\n              'NIP',\n              style: TextStyle(color: Colors.grey[600]),\n            ),\n            Text(employee.nip ?? '')\n          ],\n        ),\n      ],\n    );\n  }\n\n  Widget _buildEmployeeCard(Employee employee) {\n    return Container(\n      width: Get.width,\n      margin: const EdgeInsets.only(bottom: 8.0),\n      child: Card(\n        elevation: 2.0,\n        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),\n        child: Padding(\n          padding: const EdgeInsets.all(8),\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              Text(\n                employee.name ?? '',\n                style: const TextStyle(\n                  fontWeight: FontWeight.w600,\n                ),\n              ),\n              dividerT1,\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  Text(\n                    'Jabatan',\n                    style: TextStyle(color: Colors.grey[600]),\n                  ),\n                  Text(employee.position ?? '')\n                ],\n              ),\n              sizedBoxH4,\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  Text(\n                    'Bagian ',\n                    style: TextStyle(color: Colors.grey[600]),\n                  ),\n                  Text(employee.department ?? '')\n                ],\n              ),\n              sizedBoxH4,\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  Text(\n                    'Status',\n                    style: TextStyle(color: Colors.grey[600]),\n                  ),\n                  Text(employee.status ?? '')\n                ],\n              ),\n              _buildPnsSection(employee),\n              sizedBoxH4,\n              dividerT1,\n              Center(\n                child: SizedBox(\n                  width: Get.width * 0.9,\n                  child: ElevatedButton(\n                    style: ElevatedButton.styleFrom(\n                      shape: RoundedRectangleBorder(\n                          borderRadius: BorderRadius.circular(6)),\n                      onPrimary: Colors.white,\n                      primary: Colors.blueAccent,\n                    ),\n                    onPressed: () {\n                      final User user = User.fromJson(employee.toJson());\n                      Get.to(() => ReportScreen(user: user, isApproval: true));\n                    },\n                    child: const Text('Lihat data kehadiran'),\n                  ),\n                ),\n              )\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget _buildEmptyWidget() {\n    return Center(\n      child: Column(\n        mainAxisAlignment: MainAxisAlignment.center,\n        children: <Widget>[\n          SizedBox(\n            width: Get.width * 0.5,\n            height: Get.height * 0.3,\n            child: const FlareActor(\n              'assets/flare/not_found.flr',\n              animation: 'empty',\n            ),\n          ),\n          const Text('Data tidak ditemukan :(')\n        ],\n      ),\n    );\n  }\n\n  Widget _buildContent() {\n    if (_isLoading) {\n      return const SizedBox(\n        child: Center(\n            child: SpinKitCircle(\n          size: 45,\n          color: Colors.blueAccent,\n        )),\n      );\n    }\n\n    if (_employees == null || _employees.isEmpty) {\n      return SizedBox(\n        child: Center(\n          child: Column(\n              mainAxisAlignment: MainAxisAlignment.center,\n              children: <Widget>[\n                SizedBox(\n                  width: Get.width * 0.5,\n                  height: Get.height * 0.3,\n                  child: const FlareActor(\n                    'assets/flare/failure.flr',\n                    animation: 'failure',\n                  ),\n                ),\n                const Text('Gagal memuat data pegawai!'),\n                sizedBoxH20,\n                ElevatedButton(\n                  style: ElevatedButton.styleFrom(\n                      shape: RoundedRectangleBorder(\n                          borderRadius: BorderRadius.circular(6)),\n                      primary: Colors.blueAccent,\n                      onPrimary: Colors.white),\n                  onPressed: loadData,\n                  child: const Text('Coba Lagi'),\n                )\n              ]),\n        ),\n      );\n    }\n\n    return ListView.builder(\n      itemBuilder: (context, index) => _buildEmployeeCard(_employees[index]),\n      itemCount: _employees?.length ?? 0,\n    );\n  }\n\n  Future<Employee> _onSearchButtonPressed() async {\n    if (_employees == null || _employees.isEmpty) {\n      return null;\n    }\n\n    return showSearch(\n        context: context,\n        delegate: SearchPage(\n            searchLabel: 'Cari Pegawai',\n            barTheme: ThemeData(\n              appBarTheme: const AppBarTheme(\n                brightness: Brightness.dark,\n                color: Colors.blueAccent,\n              ),\n            ),\n            showItemsOnEmpty: true,\n            failure: _buildEmptyWidget(),\n            builder: (Employee employee) => Padding(\n                  padding: const EdgeInsets.symmetric(horizontal: 8.0),\n                  child: _buildEmployeeCard(employee),\n                ),\n            filter: (Employee employee) => [\n                  employee.name,\n                  employee.status,\n                  employee.nip,\n                  employee.department\n                ],\n            items: _employees));\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Presensi Pegawai'),\n        actions: <Widget>[\n          IconButton(\n              onPressed: _onSearchButtonPressed,\n              icon: const Icon(Icons.search_rounded))\n        ],\n      ),\n      body: Padding(\n        padding: const EdgeInsets.only(bottom: 8, left: 8, right: 8),\n        child: _buildContent(),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/employee_list_screen.dart",
    "content": "import 'package:flare_flutter/flare_actor.dart';\nimport 'package:flutter/material.dart';\nimport 'package:font_awesome_flutter/font_awesome_flutter.dart';\nimport 'package:get/get.dart';\nimport 'package:intl/intl.dart';\nimport 'package:search_page/search_page.dart';\nimport 'package:spo_balaesang/models/employee.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/extensions.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:timeline_tile/timeline_tile.dart';\nimport 'package:url_launcher/url_launcher.dart';\n\nclass EmployeeListScreen extends StatefulWidget {\n  const EmployeeListScreen({this.employees});\n\n  final List<Employee> employees;\n\n  @override\n  State<StatefulWidget> createState() => _EmployeeListScreenState();\n}\n\nclass _EmployeeListScreenState extends State<EmployeeListScreen> {\n  List<Employee> employees;\n  List<bool> _isExpanded = <bool>[]; // ignore: prefer_final_fields\n\n  @override\n  void initState() {\n    super.initState();\n    employees = widget.employees;\n    _isExpanded\n        .addAll(List.generate(employees.length, (index) => false).toList());\n  }\n\n  Widget _buildPnsInfoSection(Employee employee) {\n    if (employee.status == 'Honorer') {\n      return sizedBox;\n    }\n\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: <Widget>[\n        Row(\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          children: <Widget>[\n            Text(\n              \"Golongan\",\n              style: labelTextStyle,\n            ),\n            Text(employee.group ?? '')\n          ],\n        ),\n        sizedBoxH2,\n        Row(\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          children: <Widget>[\n            Text(\n              \"Pangkat\",\n              style: labelTextStyle,\n            ),\n            Text(employee.rank ?? '')\n          ],\n        ),\n        sizedBoxH2,\n        Row(\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          children: <Widget>[\n            Text(\n              \"NIP\",\n              style: labelTextStyle,\n            ),\n            Text(employee.nip ?? '')\n          ],\n        ),\n        sizedBoxH2,\n      ],\n    );\n  }\n\n  Widget _buildPresenceSection(int index) {\n    if (employees[index].presences.isEmpty) {\n      return sizedBox;\n    }\n\n    return Column(\n      children: <Widget>[\n        dividerT1,\n        ExpansionPanelList(\n          animationDuration: const Duration(milliseconds: 500),\n          elevation: 0,\n          expandedHeaderPadding: const EdgeInsets.all(1.0),\n          expansionCallback: (i, isOpen) =>\n              setState(() => {_isExpanded[index] = !isOpen}),\n          children: <ExpansionPanel>[\n            ExpansionPanel(\n                canTapOnHeader: true,\n                isExpanded: _isExpanded[index],\n                headerBuilder: (_, isOpen) {\n                  return Row(\n                    children: <Widget>[\n                      sizedBoxW12,\n                      const Text(\n                        'Kehadiran : ',\n                        style: TextStyle(color: Colors.blueGrey),\n                      ),\n                      Text(\n                        DateFormat(\"EEEE, d MMMM y\").format(DateTime.now()),\n                        style: const TextStyle(fontSize: 12.0),\n                      )\n                    ],\n                  );\n                },\n                body: Column(\n                  children: employees[index].presences.map((presence) {\n                    final int _index =\n                        employees[index].presences.indexOf(presence);\n\n                    String status = presence.status;\n\n                    if (presence.status == 'Terlambat') {\n                      final duration = calculateLateTime(\n                          presence.startTime, presence.attendTime);\n                      status = '${presence.status} $duration';\n                    }\n\n                    final isFirst = _index == 0;\n\n                    final isLast = _index == 3;\n\n                    final isOnGoing = presence.startTime.isOnGoing();\n\n                    final statusColor = isOnGoing\n                        ? Colors.grey\n                        : checkStatusColor(presence.status);\n\n                    final afterLineColor =\n                        isOnGoing ? Colors.grey[300] : statusColor;\n\n                    var beforeLineColor = Colors.grey[300];\n\n                    if (!isFirst) {\n                      final _presence = employees[index].presences[_index - 1];\n                      beforeLineColor = _presence.startTime.isFinished()\n                          ? checkStatusColor(_presence.status)\n                          : Colors.grey[300];\n                    }\n\n                    final indicatorIcon = _checkIconData(presence.status);\n\n                    return TimelineTile(\n                      isFirst: isFirst,\n                      isLast: isLast,\n                      startChild: Center(\n                        child: Text(\n                          presence.attendTime.isEmpty\n                              ? \"--:--:--\"\n                              : presence.attendTime,\n                          style: TextStyle(\n                              color: isOnGoing ? Colors.grey : Colors.black),\n                        ),\n                      ),\n                      alignment: TimelineAlign.manual,\n                      lineXY: 0.2,\n                      endChild: ListTile(\n                        dense: true,\n                        title: Text(presence.codeType,\n                            style: TextStyle(\n                                fontWeight: FontWeight.w500,\n                                color: isOnGoing ? Colors.grey : Colors.black)),\n                        subtitle:\n                            Text(status, style: TextStyle(color: statusColor)),\n                        trailing: Text(\n                            formatPercentage(\n                                checkPresencePercentage(presence.status)),\n                            style: TextStyle(color: statusColor)),\n                      ),\n                      indicatorStyle: IndicatorStyle(\n                        color: afterLineColor,\n                        width: isOnGoing ? 20 : 23,\n                        iconStyle: IconStyle(\n                          color: Colors.white,\n                          iconData: indicatorIcon,\n                          fontSize: 16,\n                        ),\n                      ),\n                      afterLineStyle: LineStyle(color: afterLineColor),\n                      beforeLineStyle: LineStyle(color: beforeLineColor),\n                    );\n                  }).toList(),\n                ))\n          ],\n        ),\n      ],\n    );\n  }\n\n  IconData _checkIconData(String status) {\n    switch (status) {\n      case 'Terlambat':\n        return Icons.thumb_down;\n      case 'Tidak Hadir':\n        return Icons.close_rounded;\n      case 'Tepat Waktu':\n        return Icons.check_rounded;\n      default:\n        return Icons.calendar_today_rounded;\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Pegawai'),\n        actions: [\n          IconButton(\n              onPressed: () => showSearch(\n                  context: context,\n                  delegate: SearchPage(\n                    searchLabel: 'Cari Pegawai',\n                    searchStyle: const TextStyle(color: Colors.white),\n                    barTheme: ThemeData(\n                      appBarTheme: const AppBarTheme(\n                        color: Colors.blueAccent,\n                        brightness: Brightness.dark,\n                      ),\n                    ),\n                    showItemsOnEmpty: true,\n                    failure: Center(\n                      child: Column(\n                        mainAxisAlignment: MainAxisAlignment.center,\n                        children: <Widget>[\n                          SizedBox(\n                            width: Get.width * 0.5,\n                            height: Get.height * 0.3,\n                            child: const FlareActor(\n                              'assets/flare/not_found.flr',\n                              animation: 'empty',\n                            ),\n                          ),\n                          const Text('Data tidak ditemukan :(')\n                        ],\n                      ),\n                    ),\n                    builder: (Employee employee) => Padding(\n                      padding: const EdgeInsets.symmetric(horizontal: 8.0),\n                      child: _buildEmployeeCardSection(employee),\n                    ),\n                    filter: (Employee employee) => [\n                      employee.name,\n                      employee.status,\n                      employee.department,\n                      employee.nip,\n                    ],\n                    items: employees,\n                  )),\n              icon: const Icon(Icons.search_rounded))\n        ],\n      ),\n      body: Padding(\n        padding: const EdgeInsets.only(bottom: 8, left: 8, right: 8),\n        child: ListView.builder(\n          itemBuilder: (_, index) =>\n              _buildEmployeeCardSection(employees[index]),\n          itemCount: employees.length,\n        ),\n      ),\n    );\n  }\n\n  Widget _buildEmployeeCardSection(Employee employee) {\n    final int index = employees.indexOf(employee);\n    return Container(\n      margin: const EdgeInsets.only(bottom: 16),\n      child: Card(\n        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),\n        elevation: 3.0,\n        child: Padding(\n          padding: const EdgeInsets.all(8),\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              Text(\n                employee.name,\n                style: const TextStyle(fontWeight: FontWeight.w600),\n              ),\n              dividerT1,\n              Column(\n                crossAxisAlignment: CrossAxisAlignment.start,\n                children: <Widget>[\n                  Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: <Widget>[\n                      Text(\n                        \"Jabatan\",\n                        style: labelTextStyle,\n                      ),\n                      Text(employee.position)\n                    ],\n                  ),\n                  sizedBoxH2,\n                  Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: <Widget>[\n                      Text(\n                        \"Bagian\",\n                        style: labelTextStyle,\n                      ),\n                      Text(employee.department)\n                    ],\n                  ),\n                  sizedBoxH2,\n                  Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: <Widget>[\n                      Text(\n                        \"Status\",\n                        style: labelTextStyle,\n                      ),\n                      Text(employee.status)\n                    ],\n                  ),\n                  sizedBoxH2,\n                  _buildPnsInfoSection(employee)\n                ],\n              ),\n              dividerT1,\n              Center(\n                child: Wrap(\n                  spacing: 8.0,\n                  children: <Widget>[\n                    IconButton(\n                      onPressed: () {\n                        launch('tel:${trimPhoneNumber(employee.phone)}');\n                      },\n                      color: Colors.blueAccent,\n                      icon: const Icon(Icons.phone),\n                      tooltip: 'Hubungi via Telpon',\n                    ),\n                    IconButton(\n                      onPressed: () async {\n                        final whatsappUrl =\n                            \"whatsapp://send?phone=${trimPhoneNumber(employee.phone)}\";\n                        await canLaunch(whatsappUrl)\n                            ? launch(whatsappUrl)\n                            : Get.defaultDialog(\n                                title: 'Gagal',\n                                content:\n                                    const Text('WhatsApp tidak ditemukan!'),\n                              );\n                      },\n                      color: Colors.green[600],\n                      icon: const FaIcon(FontAwesomeIcons.whatsapp),\n                      tooltip: 'Hubungi via WA',\n                    ),\n                    IconButton(\n                      onPressed: () async {\n                        final smsUrl =\n                            \"smsto:${trimPhoneNumber(employee.phone)}\";\n                        await canLaunch(smsUrl)\n                            ? launch(smsUrl)\n                            : Get.defaultDialog(\n                                title: 'Gagal',\n                                content:\n                                    const Text('Aplikasi SMS tidak ditemukan!'),\n                              );\n                      },\n                      color: Colors.red[800],\n                      icon: const FaIcon(FontAwesomeIcons.mailBulk),\n                      tooltip: 'Hubungi via SMS',\n                    ),\n                  ],\n                ),\n              ),\n              _buildPresenceSection(index)\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/employee_outstation.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\n\nimport 'package:flare_flutter/flare_actor.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:get/get.dart';\nimport 'package:http/http.dart' as http;\nimport 'package:intl/intl.dart';\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/models/outstation.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/employee_proposal_widget.dart';\nimport 'package:table_calendar/table_calendar.dart';\n\nclass EmployeeOutstationScreen extends StatefulWidget {\n  @override\n  _EmployeeOutstationScreenState createState() =>\n      _EmployeeOutstationScreenState();\n}\n\nclass _EmployeeOutstationScreenState extends State<EmployeeOutstationScreen> {\n  List<Outstation> _outstations = <Outstation>[];\n  bool _isLoading = false;\n  final TextEditingController _reasonController = TextEditingController();\n  final TextEditingController _nameController = TextEditingController();\n  List<Outstation> _filteredOutstation = <Outstation>[];\n  Set<String> choices = {'Semua', 'Disetujui', 'Belum Disetujui'};\n  String _selectedChoice = 'Semua';\n  DateTime _selectedDate = DateTime.now();\n\n  @override\n  void setState(void Function() fn) {\n    if (mounted) {\n      super.setState(fn);\n    }\n  }\n\n  Future<void> _fetchOutstationData() async {\n    try {\n      setState(() {\n        _isLoading = true;\n      });\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final Map<String, dynamic> _result =\n          await dataRepo.getAllEmployeeOutstation(_selectedDate);\n      final List<dynamic> outstations = _result['data'] as List<dynamic>;\n\n      final _data = outstations\n          .map((json) => Outstation.fromJson(json as Map<String, dynamic>))\n          .toList();\n      setState(() {\n        _outstations = _data;\n        _filteredOutstation = _outstations;\n      });\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n    } finally {\n      setState(() {\n        _isLoading = false;\n      });\n    }\n  }\n\n  void _rejectOutstation(Outstation outstation) {\n    Get.defaultDialog(\n        title: 'Alasan Pembatalan!',\n        content: Flexible(\n          child: Container(\n            padding: const EdgeInsets.all(8),\n            width: Get.width * 0.9,\n            child: TextFormField(\n              controller: _reasonController,\n              decoration: const InputDecoration(\n                  labelText: 'Alasan',\n                  focusColor: Colors.blueAccent,\n                  focusedBorder: UnderlineInputBorder(\n                      borderSide: BorderSide(color: Colors.blueAccent))),\n            ),\n          ),\n        ),\n        confirm: ElevatedButton(\n          style: ElevatedButton.styleFrom(\n            shape:\n                RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),\n            primary: Colors.blueAccent,\n            onPrimary: Colors.white,\n          ),\n          onPressed: () {\n            Get.back();\n            _sendData(outstation, false);\n          },\n          child: const Text('OK'),\n        ));\n  }\n\n  // ignore: always_declare_return_types\n  _approveOutstation(Outstation outstation) {\n    _sendData(outstation, true);\n  }\n\n  SizedBox _cancelButton(String label, Outstation outstation) {\n    return SizedBox(\n      width: Get.width,\n      child: ElevatedButton(\n        style: ElevatedButton.styleFrom(\n          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),\n          primary: Colors.red,\n          onPrimary: Colors.white,\n        ),\n        onPressed: () {\n          _rejectOutstation(outstation);\n        },\n        child: Text(label),\n      ),\n    );\n  }\n\n  SizedBox _approveButton(Outstation outstation) {\n    return SizedBox(\n      width: Get.width,\n      child: ElevatedButton(\n        style: ElevatedButton.styleFrom(\n          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),\n          primary: Colors.blueAccent,\n          onPrimary: Colors.white,\n        ),\n        onPressed: () {\n          _approveOutstation(outstation);\n        },\n        child: const Text('Setujui'),\n      ),\n    );\n  }\n\n  Widget _buildButtonSection(Outstation outstation) {\n    switch (outstation.approvalStatus) {\n      case 'Menunggu Persetujuan':\n        return Column(\n          children: <Widget>[\n            _approveButton(outstation),\n            _cancelButton('Tolak', outstation)\n          ],\n        );\n      case 'Disetujui':\n        return _cancelButton('Batal Setujui', outstation);\n      case 'Ditolak':\n        return _approveButton(outstation);\n      default:\n        return sizedBox;\n    }\n  }\n\n  Future<void> _sendData(Outstation outstation, bool isApproved) async {\n    final ProgressDialog pd = ProgressDialog(context, isDismissible: false);\n    pd.show();\n    try {\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final Map<String, dynamic> data = {\n        'user_id': outstation.user.id,\n        'is_approved': isApproved,\n        'outstation_id': outstation.id,\n        'reason': _reasonController.value.text\n      };\n      final http.Response response = await dataRepo.approveOutstation(data);\n      final Map<String, dynamic> _res =\n          jsonDecode(response.body) as Map<String, dynamic>;\n      if (response.statusCode == 200) {\n        pd.hide();\n        showAlertDialog(\"success\", \"Sukses\", _res['message'].toString(),\n            dismissible: false);\n        _fetchOutstationData();\n      } else {\n        if (pd.isShowing()) pd.hide();\n        showErrorDialog(_res);\n      }\n    } catch (e) {\n      pd.hide();\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n    }\n  }\n\n  @override\n  void dispose() {\n    _reasonController.dispose();\n    _nameController.dispose();\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    _fetchOutstationData();\n  }\n\n  Widget _buildBody() {\n    if (_isLoading) {\n      return SizedBox(\n        height: Get.height * 0.8,\n        child: const Center(\n            child: SpinKitFadingFour(\n          size: 45,\n          color: Colors.blueAccent,\n        )),\n      );\n    }\n    if (_filteredOutstation.isEmpty) {\n      return SizedBox(\n        height: Get.height * 0.6,\n        child: Center(\n          child: Column(\n              mainAxisAlignment: MainAxisAlignment.center,\n              children: <Widget>[\n                SizedBox(\n                  width: Get.width * 0.5,\n                  height: Get.height * 0.3,\n                  child: const FlareActor(\n                    'assets/flare/not_found.flr',\n                    animation: 'empty',\n                  ),\n                ),\n                const Text('Belum ada Dinas Luar yang diajukan!')\n              ]),\n        ),\n      );\n    }\n    return Column(\n      children: _filteredOutstation.map((outstation) {\n        final DateTime dueDate = outstation.dueDate;\n        final DateTime startDate = outstation.startDate;\n        return EmployeeProposalWidget(\n          isApprovalCard: true,\n          employeeName: outstation.user.name,\n          button: _buildButtonSection(outstation),\n          photo: outstation.photo,\n          heroTag: outstation.id.toString(),\n          isApproved: outstation.isApproved,\n          approvalStatus: outstation.approvalStatus,\n          startDate: startDate,\n          dueDate: dueDate,\n          description: outstation.description,\n          title: outstation.title,\n        );\n      }).toList(),\n    );\n  }\n\n  List<Outstation> _setFilter(String value) {\n    if (value == 'Disetujui') {\n      return _outstations\n          .where((element) => element.isApproved == true)\n          .toList();\n    }\n\n    if (value == 'Belum Disetujui') {\n      return _outstations\n          .where((element) => element.isApproved == false)\n          .toList();\n    }\n\n    return _outstations;\n  }\n\n  void _selectDate() {\n    Get.defaultDialog(\n        title: 'Pilih Tanggal',\n        content: Flexible(\n          child: SizedBox(\n            height: Get.height * 0.4,\n            width: Get.width * 0.9,\n            child: TableCalendar(\n              availableCalendarFormats: const <CalendarFormat, String>{\n                CalendarFormat.month: '1 bulan',\n              },\n              calendarStyle: const CalendarStyle(\n                weekendTextStyle: TextStyle(color: Colors.red),\n              ),\n              calendarBuilders: const CalendarBuilders(\n                dowBuilder: dowBuilder,\n              ),\n              calendarFormat: CalendarFormat.month,\n              availableGestures: AvailableGestures.horizontalSwipe,\n              shouldFillViewport: true,\n              headerStyle: const HeaderStyle(titleCentered: true),\n              startingDayOfWeek: StartingDayOfWeek.monday,\n              firstDay: DateTime(2021),\n              focusedDay: _selectedDate,\n              selectedDayPredicate: (day) {\n                return isSameDay(_selectedDate, day);\n              },\n              lastDay: DateTime(DateTime.now().year + 5),\n              locale: 'in_ID',\n              onDaySelected: (day, focusedDay) {\n                Get.back();\n                setState(() {\n                  _selectedDate = day;\n                  _fetchOutstationData();\n                });\n              },\n            ),\n          ),\n        ));\n  }\n\n  void _searchByName(String value) {\n    setState(() {\n      if (value.isNotEmpty) {\n        if (_filteredOutstation.isNotEmpty) {\n          _filteredOutstation = _filteredOutstation\n              .where((element) =>\n                  element.user.name.toLowerCase().contains(value.toLowerCase()))\n              .toList();\n        }\n      } else {\n        _filteredOutstation = _setFilter(_selectedChoice);\n      }\n    });\n  }\n\n  Widget _buildLabelSection() {\n    if (_selectedChoice == 'Tanggal') {\n      return Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: <Widget>[\n          Text('Hasil      : ${_filteredOutstation.length} dinas luar'),\n          Text(\n              'Tanggal : ${DateFormat.yMMMMEEEEd('id_ID').format(_selectedDate)}')\n        ],\n      );\n    }\n    return Text('Hasil : ${_filteredOutstation.length} dinas luar');\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Daftar Dinas Luar Pegawai'),\n      ),\n      body: Container(\n        padding: const EdgeInsets.all(8),\n        child: SingleChildScrollView(\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              TextFormField(\n                controller: _nameController,\n                onChanged: _searchByName,\n                decoration: const InputDecoration(\n                    prefixIcon: Icon(Icons.search),\n                    labelText: 'Cari dengan nama pegawai'),\n              ),\n              sizedBoxH10,\n              Row(\n                children: <Widget>[\n                  const Expanded(\n                    child: Text(\n                      'Filter : ',\n                      style: TextStyle(\n                          fontWeight: FontWeight.w600, fontSize: 16.0),\n                    ),\n                  ),\n                  Expanded(\n                    child: Container(\n                      padding: const EdgeInsets.symmetric(horizontal: 4),\n                      decoration: BoxDecoration(\n                          borderRadius: BorderRadius.circular(10.0),\n                          border: Border.all(color: Colors.grey[600])),\n                      child: DropdownButtonHideUnderline(\n                        child: DropdownButton(\n                            isExpanded: true,\n                            value: _selectedChoice,\n                            items: choices\n                                .map(\n                                  (choice) => DropdownMenuItem(\n                                    value: choice,\n                                    child: Text(\n                                      choice,\n                                    ),\n                                  ),\n                                )\n                                .toList(),\n                            onChanged: (value) {\n                              setState(() {\n                                _selectedChoice = value.toString();\n                                _filteredOutstation =\n                                    _setFilter(value.toString());\n                                if (_nameController.value.text.isNotEmpty) {\n                                  _searchByName(_nameController.value.text);\n                                }\n                              });\n                            }),\n                      ),\n                    ),\n                  )\n                ],\n              ),\n              const Text(\n                'Pilih Tahun & Bulan : ',\n              ),\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  Text(\n                    DateFormat.yMMMMEEEEd('id_ID').format(_selectedDate),\n                    style: const TextStyle(\n                      fontSize: 18.0,\n                      fontWeight: FontWeight.w600,\n                    ),\n                  ),\n                  IconButton(\n                      icon: Icon(\n                        Icons.calendar_today_rounded,\n                        color: Colors.blueAccent[400],\n                      ),\n                      onPressed: () {\n                        _selectDate();\n                      })\n                ],\n              ),\n              dividerT1,\n              sizedBoxH4,\n              _buildLabelSection(),\n              sizedBoxH8,\n              _buildBody(),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/employee_paid_leave_screen.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\n\nimport 'package:flare_flutter/flare_actor.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:get/get.dart';\nimport 'package:http/http.dart' as http;\nimport 'package:intl/intl.dart';\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/models/paid_leave.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/employee_proposal_widget.dart';\nimport 'package:table_calendar/table_calendar.dart';\n\nclass EmployeePaidLeaveScreen extends StatefulWidget {\n  @override\n  _EmployeePaidLeaveScreenState createState() =>\n      _EmployeePaidLeaveScreenState();\n}\n\nclass _EmployeePaidLeaveScreenState extends State<EmployeePaidLeaveScreen> {\n  List<PaidLeave> _paidLeaves = <PaidLeave>[];\n  bool _isLoading = false;\n  final TextEditingController _reasonController = TextEditingController();\n  final TextEditingController _nameController = TextEditingController();\n  List<PaidLeave> _filteredPaidLeave = <PaidLeave>[];\n  Set<String> choices = {'Semua', 'Disetujui', 'Belum Disetujui'};\n  String _selectedChoice = 'Semua';\n  DateTime _selectedDate = DateTime.now();\n\n  Future<void> _fetchPaidLeaveData() async {\n    try {\n      setState(() {\n        _isLoading = true;\n      });\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final Map<String, dynamic> _result =\n          await dataRepo.getAllEmployeePaidLeave(_selectedDate);\n      final List<dynamic> paidLeaves = _result['data'] as List<dynamic>;\n\n      final List<PaidLeave> _data = paidLeaves\n          .map((json) => PaidLeave.fromJson(json as Map<String, dynamic>))\n          .toList();\n      setState(() {\n        _paidLeaves = _data;\n        _filteredPaidLeave = _data;\n      });\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n    } finally {\n      setState(() {\n        _isLoading = false;\n      });\n    }\n  }\n\n  void _approvePaidLeave(PaidLeave paidLeave) {\n    _sendData(paidLeave, true);\n  }\n\n  Future<void> _sendData(PaidLeave paidLeave, bool isApproved) async {\n    final ProgressDialog pd = ProgressDialog(context, isDismissible: false);\n    pd.show();\n    try {\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final Map<String, dynamic> data = {\n        'user_id': paidLeave.user.id,\n        'is_approved': isApproved,\n        'paid_leave_id': paidLeave.id,\n        'reason': _reasonController.value.text\n      };\n      final http.Response response = await dataRepo.approvePaidLeave(data);\n      final Map<String, dynamic> _res =\n          jsonDecode(response.body) as Map<String, dynamic>;\n      if (response.statusCode == 200) {\n        pd.hide();\n        showAlertDialog(\"success\", \"Sukses\", _res['message'].toString(),\n            dismissible: true);\n        _fetchPaidLeaveData();\n      } else {\n        if (pd.isShowing()) pd.hide();\n        showErrorDialog(_res);\n      }\n    } catch (e) {\n      pd.hide();\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n    }\n  }\n\n  @override\n  void dispose() {\n    _reasonController.dispose();\n    _nameController.dispose();\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    _fetchPaidLeaveData();\n    super.initState();\n  }\n\n  @override\n  void setState(void Function() fn) {\n    if (mounted) {\n      super.setState(fn);\n    }\n  }\n\n  Widget _buildBody() {\n    if (_isLoading) {\n      return SizedBox(\n        height: Get.height * 0.8,\n        child: const Center(\n            child: SpinKitFadingGrid(\n          size: 45,\n          color: Colors.blueAccent,\n        )),\n      );\n    }\n\n    if (_paidLeaves.isEmpty) {\n      return SizedBox(\n        height: Get.height * 0.6,\n        child: Center(\n          child: Column(\n              mainAxisAlignment: MainAxisAlignment.center,\n              children: <Widget>[\n                SizedBox(\n                  width: Get.width * 0.5,\n                  height: Get.height * 0.3,\n                  child: const FlareActor(\n                    'assets/flare/not_found.flr',\n                    animation: 'empty',\n                  ),\n                ),\n                const Text('Belum ada Cuti yang diajukan!')\n              ]),\n        ),\n      );\n    }\n    return Column(\n      children: _filteredPaidLeave\n          .map((paidLeave) => _buildPaidLeaveItem(paidLeave))\n          .toList(),\n    );\n  }\n\n  void _rejectPaidLeave(PaidLeave paidLeave) {\n    Get.defaultDialog(\n        title: 'Alasan Pembatalan!',\n        content: Flexible(\n          child: Container(\n            padding: const EdgeInsets.all(8),\n            width: Get.width * 0.9,\n            child: TextFormField(\n              controller: _reasonController,\n              decoration: const InputDecoration(\n                  labelText: 'Alasan',\n                  focusColor: Colors.blueAccent,\n                  focusedBorder: UnderlineInputBorder(\n                      borderSide: BorderSide(color: Colors.blueAccent))),\n            ),\n          ),\n        ),\n        confirm: ElevatedButton(\n          style: ElevatedButton.styleFrom(\n            shape:\n                RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),\n            primary: Colors.blueAccent,\n            onPrimary: Colors.white,\n          ),\n          onPressed: () {\n            Get.back();\n            _sendData(paidLeave, false);\n          },\n          child: const Text('OK'),\n        ));\n  }\n\n  SizedBox _cancelButton(String label, PaidLeave paidLeave) {\n    return SizedBox(\n      width: Get.width,\n      child: ElevatedButton(\n        style: ElevatedButton.styleFrom(\n          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),\n          primary: Colors.red,\n          onPrimary: Colors.white,\n        ),\n        onPressed: () {\n          _rejectPaidLeave(paidLeave);\n        },\n        child: Text(label),\n      ),\n    );\n  }\n\n  SizedBox _approveButton(PaidLeave paidLeave) {\n    return SizedBox(\n      width: Get.width,\n      child: ElevatedButton(\n        style: ElevatedButton.styleFrom(\n          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),\n          primary: Colors.blueAccent,\n          onPrimary: Colors.white,\n        ),\n        onPressed: () {\n          _approvePaidLeave(paidLeave);\n        },\n        child: const Text('Setujui'),\n      ),\n    );\n  }\n\n  Widget _buildButtonSection(PaidLeave paidLeave) {\n    switch (paidLeave.approvalStatus) {\n      case 'Menunggu Persetujuan':\n        return Column(\n          children: <Widget>[\n            _approveButton(paidLeave),\n            _cancelButton('Tolak', paidLeave)\n          ],\n        );\n      case 'Disetujui':\n        return _cancelButton('Batal Setujui', paidLeave);\n      case 'Ditolak':\n        return _approveButton(paidLeave);\n      default:\n        return sizedBox;\n    }\n  }\n\n  Widget _buildPaidLeaveItem(PaidLeave paidLeave) {\n    final startDate = paidLeave.startDate;\n    final dueDate = paidLeave.dueDate;\n    return EmployeeProposalWidget(\n      isApprovalCard: true,\n      isPaidLeave: true,\n      description: paidLeave.description,\n      dueDate: dueDate,\n      category: paidLeave.category,\n      startDate: startDate,\n      heroTag: paidLeave.id.toString(),\n      photo: paidLeave.photo,\n      approvalStatus: paidLeave.approvalStatus,\n      employeeName: paidLeave.user.name,\n      button: _buildButtonSection(paidLeave),\n      isApproved: paidLeave.isApproved,\n      title: paidLeave.title,\n    );\n  }\n\n  List<PaidLeave> _setFilter(String value) {\n    if (value == 'Disetujui') {\n      return _paidLeaves\n          .where((element) => element.isApproved == true)\n          .toList();\n    }\n\n    if (value == 'Belum Disetujui') {\n      return _paidLeaves\n          .where((element) => element.isApproved == false)\n          .toList();\n    }\n\n    return _paidLeaves;\n  }\n\n  void _selectDate() {\n    Get.defaultDialog(\n        title: 'Pilih Tanggal',\n        content: Flexible(\n          child: SizedBox(\n            height: Get.height * 0.4,\n            width: Get.width * 0.9,\n            child: TableCalendar(\n              availableCalendarFormats: const <CalendarFormat, String>{\n                CalendarFormat.month: '1 bulan',\n              },\n              calendarStyle: const CalendarStyle(\n                weekendTextStyle: TextStyle(color: Colors.red),\n              ),\n              calendarBuilders: const CalendarBuilders(\n                dowBuilder: dowBuilder,\n              ),\n              calendarFormat: CalendarFormat.month,\n              availableGestures: AvailableGestures.horizontalSwipe,\n              shouldFillViewport: true,\n              headerStyle: const HeaderStyle(titleCentered: true),\n              startingDayOfWeek: StartingDayOfWeek.monday,\n              firstDay: DateTime(2021),\n              focusedDay: _selectedDate,\n              selectedDayPredicate: (day) {\n                return isSameDay(_selectedDate, day);\n              },\n              lastDay: DateTime(DateTime.now().year + 5),\n              locale: 'in_ID',\n              onDaySelected: (day, focusedDay) {\n                Get.back();\n                setState(() {\n                  _selectedDate = day;\n                  _fetchPaidLeaveData();\n                });\n              },\n            ),\n          ),\n        ));\n  }\n\n  void _searchByName(String value) {\n    setState(() {\n      if (value.isNotEmpty) {\n        if (_filteredPaidLeave.isNotEmpty) {\n          _filteredPaidLeave = _filteredPaidLeave\n              .where((element) =>\n                  element.user.name.toLowerCase().contains(value.toLowerCase()))\n              .toList();\n        }\n      } else {\n        _filteredPaidLeave = _setFilter(_selectedChoice);\n      }\n    });\n  }\n\n  Widget _buildLabelSection() {\n    if (_selectedChoice == 'Tanggal') {\n      return Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: <Widget>[\n          Text('Hasil      : ${_filteredPaidLeave.length} cuti'),\n          Text(\n              'Tanggal : ${DateFormat.yMMMMEEEEd('id_ID').format(_selectedDate)}')\n        ],\n      );\n    }\n    return Text('Hasil : ${_filteredPaidLeave.length} cuti');\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Daftar Cuti Pegawai'),\n      ),\n      body: Container(\n        padding: const EdgeInsets.all(8),\n        child: SingleChildScrollView(\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              TextFormField(\n                controller: _nameController,\n                decoration: const InputDecoration(\n                  prefixIcon: Icon(Icons.search),\n                  labelText: 'Cari dengan nama pegawai',\n                ),\n                onChanged: _searchByName,\n              ),\n              sizedBoxH10,\n              Row(\n                children: <Widget>[\n                  const Expanded(\n                    child: Text(\n                      'Filter : ',\n                      style: TextStyle(\n                          fontWeight: FontWeight.w600, fontSize: 16.0),\n                    ),\n                  ),\n                  Expanded(\n                    child: Container(\n                      padding: const EdgeInsets.symmetric(horizontal: 4),\n                      decoration: BoxDecoration(\n                          borderRadius: BorderRadius.circular(10.0),\n                          border: Border.all(color: Colors.grey[600])),\n                      child: DropdownButtonHideUnderline(\n                        child: DropdownButton(\n                            isExpanded: true,\n                            value: _selectedChoice,\n                            items: choices\n                                .map(\n                                  (choice) => DropdownMenuItem(\n                                    value: choice,\n                                    child: Text(\n                                      choice,\n                                    ),\n                                  ),\n                                )\n                                .toList(),\n                            onChanged: (value) {\n                              setState(() {\n                                _selectedChoice = value.toString();\n                                _filteredPaidLeave =\n                                    _setFilter(value.toString());\n                                if (_nameController.value.text.isNotEmpty) {\n                                  _searchByName(_nameController.value.text);\n                                }\n                              });\n                            }),\n                      ),\n                    ),\n                  )\n                ],\n              ),\n              const Text(\n                'Pilih Tahun & Bulan : ',\n              ),\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  Text(\n                    DateFormat.yMMMMEEEEd('id_ID').format(_selectedDate),\n                    style: const TextStyle(\n                      fontSize: 18.0,\n                      fontWeight: FontWeight.w600,\n                    ),\n                  ),\n                  IconButton(\n                      icon: Icon(\n                        Icons.calendar_today_rounded,\n                        color: Colors.blueAccent[400],\n                      ),\n                      onPressed: () {\n                        _selectDate();\n                      })\n                ],\n              ),\n              dividerT1,\n              sizedBoxH4,\n              _buildLabelSection(),\n              sizedBoxH8,\n              _buildBody(),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/employee_permission.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\n\nimport 'package:flare_flutter/flare_actor.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:get/get.dart';\nimport 'package:http/http.dart' as http;\nimport 'package:intl/intl.dart';\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/models/absent_permission.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/employee_proposal_widget.dart';\nimport 'package:table_calendar/table_calendar.dart';\n\nclass EmployeePermissionScreen extends StatefulWidget {\n  @override\n  _EmployeePermissionScreenState createState() =>\n      _EmployeePermissionScreenState();\n}\n\nclass _EmployeePermissionScreenState extends State<EmployeePermissionScreen> {\n  List<AbsentPermission> _permissions = <AbsentPermission>[];\n  bool _isLoading = false;\n  final TextEditingController _reasonController = TextEditingController();\n  final TextEditingController _nameController = TextEditingController();\n  List<AbsentPermission> _filteredPermission = <AbsentPermission>[];\n  Set<String> choices = {'Semua', 'Disetujui', 'Belum Disetujui'};\n  String _selectedChoice = 'Semua';\n  DateTime _selectedDate = DateTime.now();\n\n  @override\n  void setState(void Function() fn) {\n    if (mounted) {\n      super.setState(fn);\n    }\n  }\n\n  Future<void> _fetchPermissionData() async {\n    try {\n      setState(() {\n        _isLoading = true;\n      });\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final Map<String, dynamic> _result =\n          await dataRepo.getAllEmployeePermissions(_selectedDate);\n      final List<dynamic> permissions = _result['data'] as List<dynamic>;\n\n      final List<AbsentPermission> _data = permissions\n          .map(\n              (json) => AbsentPermission.fromJson(json as Map<String, dynamic>))\n          .toList();\n      setState(() {\n        _permissions = _data;\n        _filteredPermission = _data;\n      });\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n    } finally {\n      _isLoading = false;\n    }\n  }\n\n  void _rejectPermission(AbsentPermission permission) {\n    Get.defaultDialog(\n        title: 'Alasan Pembatalan!',\n        content: Flexible(\n          child: Container(\n            padding: const EdgeInsets.all(8),\n            width: Get.width * 0.9,\n            child: TextFormField(\n              decoration: const InputDecoration(\n                  labelText: 'Alasan',\n                  focusColor: Colors.blueAccent,\n                  focusedBorder: UnderlineInputBorder(\n                      borderSide: BorderSide(color: Colors.blueAccent))),\n              controller: _reasonController,\n            ),\n          ),\n        ),\n        confirm: ElevatedButton(\n          style: ElevatedButton.styleFrom(\n            shape:\n                RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),\n            primary: Colors.blueAccent,\n            onPrimary: Colors.white,\n          ),\n          onPressed: () {\n            Get.back();\n            _sendData(permission, false);\n          },\n          child: const Text('OK'),\n        ));\n  }\n\n  void _approvePermission(AbsentPermission permission) {\n    _sendData(permission, true);\n  }\n\n  SizedBox _cancelButton(String label, AbsentPermission permission) {\n    return SizedBox(\n      width: Get.width,\n      child: ElevatedButton(\n        style: ElevatedButton.styleFrom(\n          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),\n          primary: Colors.red,\n          onPrimary: Colors.white,\n        ),\n        onPressed: () {\n          _rejectPermission(permission);\n        },\n        child: Text(label),\n      ),\n    );\n  }\n\n  SizedBox _approveButton(AbsentPermission permission) {\n    return SizedBox(\n      width: Get.width,\n      child: ElevatedButton(\n        style: ElevatedButton.styleFrom(\n          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),\n          primary: Colors.blueAccent,\n          onPrimary: Colors.white,\n        ),\n        onPressed: () {\n          _approvePermission(permission);\n        },\n        child: const Text('Setujui'),\n      ),\n    );\n  }\n\n  Widget _buildButtonSection(AbsentPermission permission) {\n    switch (permission.approvalStatus) {\n      case 'Menunggu Persetujuan':\n        return Column(\n          children: <Widget>[\n            _approveButton(permission),\n            _cancelButton('Tolak', permission)\n          ],\n        );\n      case 'Disetujui':\n        return _cancelButton('Batal Setujui', permission);\n      case 'Ditolak':\n        return _approveButton(permission);\n      default:\n        return sizedBox;\n    }\n  }\n\n  Future<void> _sendData(AbsentPermission permission, bool isApproved) async {\n    final ProgressDialog pd = ProgressDialog(context, isDismissible: false);\n    pd.show();\n    try {\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final Map<String, dynamic> data = {\n        'user_id': permission.user.id,\n        'is_approved': isApproved,\n        'permission_id': permission.id,\n        'reason': _reasonController.value.text\n      };\n      final http.Response response = await dataRepo.approvePermission(data);\n      final Map<String, dynamic> _res =\n          jsonDecode(response.body) as Map<String, dynamic>;\n      if (response.statusCode == 200) {\n        pd.hide();\n        showAlertDialog(\"success\", \"Sukses\", _res['message'].toString(),\n            dismissible: false);\n        _fetchPermissionData();\n      } else {\n        if (pd.isShowing()) pd.hide();\n        showErrorDialog(_res);\n      }\n    } catch (e) {\n      pd.hide();\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n    }\n  }\n\n  @override\n  void dispose() {\n    _reasonController.dispose();\n    _nameController.dispose();\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    _fetchPermissionData();\n    super.initState();\n  }\n\n  Widget _buildBody() {\n    if (_isLoading) {\n      return SizedBox(\n        height: Get.height * 0.8,\n        child: const Center(\n            child: SpinKitFadingCircle(\n          size: 45,\n          color: Colors.blueAccent,\n        )),\n      );\n    }\n    if (_filteredPermission.isEmpty) {\n      return SizedBox(\n        height: Get.height * 0.6,\n        child: Center(\n          child: Column(\n              mainAxisAlignment: MainAxisAlignment.center,\n              children: <Widget>[\n                SizedBox(\n                  width: Get.width * 0.5,\n                  height: Get.height * 0.3,\n                  child: const FlareActor(\n                    'assets/flare/not_found.flr',\n                    animation: 'empty',\n                  ),\n                ),\n                const Text('Belum ada izin yang diajukan!')\n              ]),\n        ),\n      );\n    }\n    return Column(\n      children: _filteredPermission.map((permission) {\n        final DateTime dueDate = permission.dueDate;\n        final DateTime startDate = permission.startDate;\n        return EmployeeProposalWidget(\n          title: permission.title,\n          description: permission.description,\n          startDate: startDate,\n          dueDate: dueDate,\n          employeeName: permission.user.name,\n          isApproved: permission.isApproved,\n          approvalStatus: permission.approvalStatus,\n          heroTag: permission.id.toString(),\n          photo: permission.photo,\n          isApprovalCard: true,\n          button: _buildButtonSection(permission),\n        );\n      }).toList(),\n    );\n  }\n\n  List<AbsentPermission> _setFilter(String value) {\n    if (value == 'Disetujui') {\n      return _permissions\n          .where((element) => element.isApproved == true)\n          .toList();\n    }\n\n    if (value == 'Belum Disetujui') {\n      return _permissions\n          .where((element) => element.isApproved == false)\n          .toList();\n    }\n\n    return _permissions;\n  }\n\n  void _selectDate() {\n    Get.defaultDialog(\n        title: 'Pilih Tanggal',\n        content: Flexible(\n          child: SizedBox(\n            height: Get.height * 0.4,\n            width: Get.width * 0.9,\n            child: TableCalendar(\n              availableCalendarFormats: const <CalendarFormat, String>{\n                CalendarFormat.month: '1 bulan',\n              },\n              calendarStyle: const CalendarStyle(\n                weekendTextStyle: TextStyle(color: Colors.red),\n              ),\n              calendarBuilders: const CalendarBuilders(\n                dowBuilder: dowBuilder,\n              ),\n              calendarFormat: CalendarFormat.month,\n              availableGestures: AvailableGestures.horizontalSwipe,\n              shouldFillViewport: true,\n              headerStyle: const HeaderStyle(titleCentered: true),\n              startingDayOfWeek: StartingDayOfWeek.monday,\n              firstDay: DateTime(2021),\n              focusedDay: _selectedDate,\n              selectedDayPredicate: (day) {\n                return isSameDay(_selectedDate, day);\n              },\n              lastDay: DateTime(DateTime.now().year + 5),\n              locale: 'in_ID',\n              onDaySelected: (day, focusedDay) {\n                Get.back();\n                setState(() {\n                  _selectedDate = day;\n                  _fetchPermissionData();\n                });\n              },\n            ),\n          ),\n        ));\n  }\n\n  void _searchByName(String value) {\n    setState(() {\n      if (value.isNotEmpty) {\n        if (_filteredPermission.isNotEmpty) {\n          _filteredPermission = _filteredPermission\n              .where((element) =>\n                  element.user.name.toLowerCase().contains(value.toLowerCase()))\n              .toList();\n        }\n      } else {\n        _filteredPermission = _setFilter(_selectedChoice);\n      }\n    });\n  }\n\n  Widget _buildLabelSection() {\n    if (_selectedChoice == 'Tanggal') {\n      return Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: <Widget>[\n          Text('Hasil      : ${_filteredPermission.length} izin'),\n          Text(\n              'Tanggal : ${DateFormat.yMMMMEEEEd('id_ID').format(_selectedDate)}')\n        ],\n      );\n    }\n    return Text('Hasil : ${_filteredPermission.length} izin');\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Daftar Izin Pegawai'),\n      ),\n      body: Container(\n        padding: const EdgeInsets.all(8),\n        child: SingleChildScrollView(\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              TextFormField(\n                controller: _nameController,\n                decoration: const InputDecoration(\n                  prefixIcon: Icon(Icons.search),\n                  labelText: 'Cari dengan nama pegawai',\n                ),\n                onChanged: _searchByName,\n              ),\n              sizedBoxH10,\n              Row(\n                children: <Widget>[\n                  const Expanded(\n                    child: Text(\n                      'Filter : ',\n                      style: TextStyle(\n                          fontWeight: FontWeight.w600, fontSize: 16.0),\n                    ),\n                  ),\n                  Expanded(\n                    child: Container(\n                      padding: const EdgeInsets.symmetric(horizontal: 4),\n                      decoration: BoxDecoration(\n                          borderRadius: BorderRadius.circular(10.0),\n                          border: Border.all(color: Colors.grey[600])),\n                      child: DropdownButtonHideUnderline(\n                        child: DropdownButton(\n                            isExpanded: true,\n                            value: _selectedChoice,\n                            items: choices\n                                .map(\n                                  (choice) => DropdownMenuItem(\n                                    value: choice,\n                                    child: Text(\n                                      choice,\n                                    ),\n                                  ),\n                                )\n                                .toList(),\n                            onChanged: (value) {\n                              setState(() {\n                                _selectedChoice = value.toString();\n                                _filteredPermission =\n                                    _setFilter(value.toString());\n                                if (_nameController.value.text.isNotEmpty) {\n                                  _searchByName(_nameController.value.text);\n                                }\n                              });\n                            }),\n                      ),\n                    ),\n                  )\n                ],\n              ),\n              sizedBoxH10,\n              const Text(\n                'Pilih Tahun & Bulan : ',\n              ),\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  Text(\n                    DateFormat.yMMMMEEEEd('id_ID').format(_selectedDate),\n                    style: const TextStyle(\n                      fontSize: 18.0,\n                      fontWeight: FontWeight.w600,\n                    ),\n                  ),\n                  IconButton(\n                      icon: Icon(\n                        Icons.calendar_today_rounded,\n                        color: Colors.blueAccent[400],\n                      ),\n                      onPressed: () {\n                        _selectDate();\n                      })\n                ],\n              ),\n              dividerT1,\n              sizedBoxH4,\n              _buildLabelSection(),\n              sizedBoxH8,\n              _buildBody(),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/forgot_pass_screen.dart",
    "content": "import 'dart:ui';\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter_config/flutter_config.dart';\nimport 'package:font_awesome_flutter/font_awesome_flutter.dart';\nimport 'package:get/get.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:url_launcher/url_launcher.dart';\n\nclass ForgotPassScreen extends StatelessWidget {\n  final double getSmallDiameter = Get.width * 2 / 3;\n\n  final double getBigDiameter = Get.width * 7 / 8;\n\n  final String adminPhoneNumber =\n      FlutterConfig.get(\"ADMIN_PHONE_NUMBER\").toString();\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      backgroundColor: Colors.grey[200],\n      body: Stack(\n        alignment: Alignment.center,\n        children: <Widget>[\n          Positioned(\n            right: -getSmallDiameter / 3,\n            top: -getSmallDiameter / 3,\n            child: Container(\n              width: getSmallDiameter,\n              height: getSmallDiameter,\n              decoration: BoxDecoration(\n                shape: BoxShape.circle,\n                gradient: LinearGradient(\n                    colors: [Colors.lightBlue[200], Colors.blueAccent],\n                    begin: Alignment.topCenter,\n                    end: Alignment.bottomCenter),\n              ),\n            ),\n          ),\n          Positioned(\n            left: -getBigDiameter / 4,\n            top: -getBigDiameter / 4,\n            child: Container(\n              width: getBigDiameter,\n              height: getBigDiameter,\n              decoration: BoxDecoration(\n                shape: BoxShape.circle,\n                gradient: LinearGradient(\n                    colors: [Colors.blueAccent[700], Colors.blueAccent],\n                    begin: Alignment.topCenter,\n                    end: Alignment.bottomCenter),\n              ),\n              child: Column(\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: <Widget>[\n                  Image.asset(\n                    'assets/logo/logo.png',\n                    width: 200,\n                  ),\n                  const Text(\n                    'Sistem Absensi Pegawai',\n                    style: TextStyle(fontSize: 12.0, color: Colors.white),\n                  ),\n                ],\n              ),\n            ),\n          ),\n          Align(\n            alignment: Alignment.bottomCenter,\n            child: ListView(\n              children: <Widget>[\n                ClipRRect(\n                  borderRadius: BorderRadius.circular(6),\n                  child: Container(\n                    margin:\n                        EdgeInsets.fromLTRB(5.0, Get.height * 0.35, 5.0, 10),\n                    padding: const EdgeInsets.fromLTRB(10, 0, 10, 25),\n                    child: Card(\n                      shape: RoundedRectangleBorder(\n                          borderRadius: BorderRadius.circular(10)),\n                      elevation: 6.0,\n                      child: Padding(\n                        padding: const EdgeInsets.all(8.0),\n                        child: Column(\n                          children: <Widget>[\n                            sizedBoxH16,\n                            const Text(\n                              'Fajrian Aidil Pratama',\n                              style: TextStyle(\n                                fontSize: 18.0,\n                                fontWeight: FontWeight.w600,\n                              ),\n                            ),\n                            const Text(\n                              'Administrator\\nFounder of @BanuaCoders',\n                              style: TextStyle(color: Colors.grey),\n                              textAlign: TextAlign.center,\n                            ),\n                            sizedBoxH2,\n                            const Divider(\n                              color: Colors.black26,\n                              thickness: 1,\n                            ),\n                            sizedBoxH2,\n                            Text(\n                              'Tekan tombol dibawah untuk menghubungi administrator sistem dan melaporkan masalah anda',\n                              textAlign: TextAlign.center,\n                              style: TextStyle(\n                                color: Colors.grey[700],\n                              ),\n                            ),\n                            Wrap(\n                              spacing: 8.0,\n                              children: <Widget>[\n                                IconButton(\n                                  onPressed: () {\n                                    launch('tel:$adminPhoneNumber');\n                                  },\n                                  color: Colors.blueAccent,\n                                  icon: const Icon(Icons.phone),\n                                  tooltip: 'Hubungi via Telpon',\n                                ),\n                                IconButton(\n                                  onPressed: () async {\n                                    final String whatsappUrl =\n                                        'whatsapp://send?phone=$adminPhoneNumber';\n                                    await canLaunch(whatsappUrl)\n                                        ? launch(whatsappUrl)\n                                        : Get.defaultDialog(\n                                            title: 'Gagal',\n                                            content: const Text(\n                                                'WhatsApp tidak ditemukan!'));\n                                  },\n                                  color: Colors.green[600],\n                                  icon: const FaIcon(FontAwesomeIcons.whatsapp),\n                                  tooltip: 'Hubungi via WA',\n                                ),\n                              ],\n                            )\n                          ],\n                        ),\n                      ),\n                    ),\n                  ),\n                ),\n              ],\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/home_screen.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\n\nimport 'package:auto_size_text/auto_size_text.dart';\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flare_flutter/flare_actor.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/rendering.dart';\nimport 'package:flutter_countdown_timer/countdown_timer_controller.dart';\nimport 'package:flutter_countdown_timer/flutter_countdown_timer.dart';\nimport 'package:get/get.dart';\nimport 'package:intl/intl.dart';\nimport 'package:onesignal_flutter/onesignal_flutter.dart';\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:pull_to_refresh/pull_to_refresh.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\nimport 'package:shimmer/shimmer.dart';\nimport 'package:spo_balaesang/models/employee.dart';\nimport 'package:spo_balaesang/models/user.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/employee_list_screen.dart';\nimport 'package:spo_balaesang/screen/notification_list_screen.dart';\nimport 'package:spo_balaesang/screen/presence_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/employee_presence_card_widget.dart';\nimport 'package:spo_balaesang/widgets/next_presence_empty_card_widget.dart';\n\nclass HomeScreen extends StatefulWidget {\n  @override\n  _HomeScreenState createState() => _HomeScreenState();\n}\n\nclass _HomeScreenState extends State<HomeScreen> {\n  List<String> _images = [];\n  CountdownTimerController _countdownController;\n  User user;\n  List<Employee> _users;\n  bool isLoading = false;\n  double _percentage = 0;\n  final RefreshController _refreshController = RefreshController();\n\n  @override\n  void setState(void Function() fn) {\n    if (mounted) {\n      super.setState(fn);\n    }\n  }\n\n  Widget _buildImageStack() {\n    if (_images.isNotEmpty) {\n      final widgets = _images\n          .sublist(0, 5)\n          .map(\n            (e) => Padding(\n              padding: const EdgeInsets.only(right: 4.0, top: 4.0),\n              child: ClipRRect(\n                borderRadius: BorderRadius.circular(100),\n                child: CachedNetworkImage(\n                  imageUrl: e,\n                  placeholder: (_, __) => Shimmer.fromColors(\n                    baseColor: Colors.grey[400],\n                    highlightColor: Colors.white,\n                    child: Padding(\n                      padding: const EdgeInsets.only(right: 4.0, top: 4.0),\n                      child: ClipRRect(\n                        borderRadius: BorderRadius.circular(100),\n                        child: Material(\n                          color: Colors.grey[300],\n                          child: InkWell(\n                            onTap: () {},\n                            splashColor: Colors.white,\n                            borderRadius: BorderRadius.circular(100),\n                            child: Padding(\n                              padding: const EdgeInsets.all(16.0),\n                              child: Icon(\n                                Icons.add,\n                                color: Colors.grey[300],\n                              ),\n                            ),\n                          ),\n                        ),\n                      ),\n                    ),\n                  ),\n                  height: 55.0,\n                ),\n              ),\n            ),\n          )\n          .toList();\n      widgets.add(Padding(\n        padding: const EdgeInsets.only(right: 4.0, top: 4.0),\n        child: ClipRRect(\n          borderRadius: BorderRadius.circular(100),\n          child: Material(\n            color: Colors.grey[300],\n            child: InkWell(\n              onTap: () {\n                Get.to(() => EmployeeListScreen(employees: _users));\n              },\n              splashColor: Colors.white,\n              borderRadius: BorderRadius.circular(100),\n              child: const Padding(\n                padding: EdgeInsets.all(16.0),\n                child: Icon(\n                  Icons.add,\n                  color: Colors.grey,\n                ),\n              ),\n            ),\n          ),\n        ),\n      ));\n      return SingleChildScrollView(\n        scrollDirection: Axis.horizontal,\n        child: Row(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: widgets,\n        ),\n      );\n    }\n    return SingleChildScrollView(\n      scrollDirection: Axis.horizontal,\n      child: Row(\n        children: List<Widget>.generate(\n            7,\n            (index) => Shimmer.fromColors(\n                  baseColor: Colors.grey[400],\n                  highlightColor: Colors.white,\n                  child: Padding(\n                    padding: const EdgeInsets.only(right: 4.0, top: 4.0),\n                    child: ClipRRect(\n                      borderRadius: BorderRadius.circular(100),\n                      child: Material(\n                        color: Colors.grey[300],\n                        child: InkWell(\n                          onTap: () {},\n                          splashColor: Colors.white,\n                          borderRadius: BorderRadius.circular(100),\n                          child: Padding(\n                            padding: const EdgeInsets.all(16.0),\n                            child: Icon(\n                              Icons.add,\n                              color: Colors.grey[300],\n                            ),\n                          ),\n                        ),\n                      ),\n                    ),\n                  ),\n                )),\n      ),\n    );\n  }\n\n  Future<void> _getAllEmployee({ProgressDialog pd}) async {\n    try {\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final List<Employee> users = await dataRepo.getAllEmployee();\n      setState(() {\n        _users = users;\n        _images = users\n            .map((e) =>\n                \"https://ui-avatars.com/api/?name=${e.name.replaceAll(' ', '+')}&size=248\"\n                    .toString())\n            .toList();\n        if (pd != null && pd.isShowing()) pd?.hide();\n      });\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n    }\n  }\n\n  Future<void> _getUser() async {\n    try {\n      setState(() {\n        isLoading = true;\n      });\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      User _user = await dataRepo.getMyData();\n      if (_user == null) {\n        showAlertDialog(\n          'failed',\n          'Kesalahan',\n          'Pastikan anda terhubung ke internet lalu tekan tombol refresh.',\n          dismissible: true,\n        );\n        final SharedPreferences prefs = await SharedPreferences.getInstance();\n        final data = jsonDecode(prefs.getString(prefsUserKey));\n        _user = User.fromJson(data as Map<String, dynamic>);\n      }\n      OneSignal.shared.setExternalUserId(_user.id.toString());\n      setState(() {\n        user = _user;\n        _countAttendancePercentage();\n      });\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n    } finally {\n      if (mounted) {\n        setState(() {\n          isLoading = false;\n          _countdownController = CountdownTimerController(\n              onEnd: () {\n                _getUser();\n              },\n              endTime: checkTime());\n        });\n      }\n    }\n  }\n\n  void _countAttendancePercentage() {\n    double sum = 0;\n    if (user == null) {\n      return;\n    }\n\n    if (user.presences == null) {\n      return;\n    }\n\n    if (user.presences.isEmpty) {\n      _percentage = 0;\n    }\n\n    // ignore: avoid_function_literals_in_foreach_calls\n    user.presences.forEach((presence) {\n      switch (presence.status) {\n        case 'Tepat Waktu':\n        case 'Dinas Luar':\n        case 'Cuti Tahunan':\n          sum += 25;\n          break;\n        case 'Cuti Bersalin':\n        case 'Cuti Sakit':\n        case 'Cuti Alasan Penting':\n          sum += 24.375;\n          break;\n        case 'Terlambat':\n          sum += 6.25;\n          break;\n        case 'Izin':\n          sum += 12.5;\n          break;\n        default:\n          sum += 0;\n          break;\n      }\n    });\n\n    _percentage = sum;\n  }\n\n  Widget _buildShimmerSection(double width, double height) {\n    return Shimmer.fromColors(\n      baseColor: Colors.grey[300],\n      highlightColor: Colors.white,\n      child: Container(\n        decoration: BoxDecoration(\n            borderRadius: BorderRadius.circular(6.0), color: Colors.blueAccent),\n        width: width,\n        height: height,\n      ),\n    );\n  }\n\n  Widget _buildUserNameSection() {\n    if (isLoading) {\n      return _buildShimmerSection(200, 20);\n    }\n\n    if (user == null) {\n      const Text(\n        'Gagal memuat data',\n        style: TextStyle(\n            color: Colors.white, fontSize: 20.0, fontWeight: FontWeight.w600),\n      );\n    }\n\n    final nipSection = user.status == 'PNS'\n        ? Text(\n            'NIP : ${user.nip}',\n            style: const TextStyle(\n              color: Colors.white,\n              fontSize: 12.0,\n            ),\n          )\n        : sizedBox;\n\n    return Column(\n      children: <Widget>[\n        Text(\n          user.name ?? '',\n          style: const TextStyle(\n              color: Colors.white, fontSize: 20.0, fontWeight: FontWeight.w600),\n        ),\n        sizedBoxH5,\n        nipSection\n      ],\n    );\n  }\n\n  Widget _buildPositionSection() {\n    if (isLoading) {\n      return Column(\n        children: <Widget>[sizedBoxH10, _buildShimmerSection(60, 15)],\n      );\n    }\n    if (user != null) {\n      final text = user.position == 'Camat' || user.position == 'Sekcam'\n          ? user.position\n          : \"${user.position} - ${user.department}\";\n      return AutoSizeText(\n        '($text)',\n        style: const TextStyle(color: Colors.white),\n        maxFontSize: 14.0,\n      );\n    }\n    return const Text(\n      'Coba untuk memuat kembali data!',\n      style: TextStyle(\n          color: Colors.white, fontSize: 20.0, fontWeight: FontWeight.w600),\n    );\n  }\n\n  List<Widget> _buildPresenceSection() {\n    if (isLoading) {\n      return [\n        _buildShimmerSection(Get.width, 250),\n        sizedBoxH10,\n        _buildShimmerSection(Get.width, 250),\n        sizedBoxH10,\n        _buildShimmerSection(Get.width, 250),\n        sizedBoxH10,\n        _buildShimmerSection(Get.width, 250),\n        sizedBoxH10,\n      ];\n    }\n    if (user != null && user.presences.isNotEmpty) {\n      return user.presences.map((presence) {\n        final Color color = checkStatusColor(presence.status);\n        String status = presence.status ?? '';\n        if (presence.status == 'Terlambat') {\n          final String duration =\n              calculateLateTime(presence.startTime, presence.attendTime);\n          status = '${presence.status} $duration';\n        }\n        return EmployeePresenceCardWidget(\n          photo: presence.photo,\n          heroTag: presence.id.toString(),\n          status: status,\n          color: color,\n          address: presence.location.address,\n          attendTime: presence.attendTime,\n          point: formatPercentage(checkPresencePercentage(presence.status)),\n          presenceType: presence.codeType,\n        );\n      }).toList();\n    }\n    return [\n      Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: const <Widget>[\n            SizedBox(\n              width: 150,\n              height: 150,\n              child: FlareActor(\n                'assets/flare/empty.flr',\n                animation: 'empty',\n              ),\n            ),\n            Text('Tidak ada presensi hari ini!')\n          ])\n    ];\n  }\n\n  int checkTime() {\n    if (user != null &&\n        user.nextPresence != null &&\n        user.nextPresence.startTime.isAfter(DateTime.now())) {\n      return user?.nextPresence?.startTime?.millisecondsSinceEpoch;\n    }\n    if (user != null &&\n        user.nextPresence != null &&\n        user.nextPresence.endTime.isAfter(DateTime.now())) {\n      return user?.nextPresence?.endTime?.millisecondsSinceEpoch;\n    }\n    return 0;\n  }\n\n  String _checkTimeLabel() {\n    if (user != null &&\n        user.nextPresence != null &&\n        user.nextPresence.startTime.isAfter(DateTime.now())) {\n      return \"MULAI DALAM :\";\n    }\n    return \"SELESAI DALAM :\";\n  }\n\n  Widget _checkStatusIcon(String status) {\n    switch (status) {\n      case 'Tepat Waktu':\n        return Column(\n          children: <Widget>[\n            Icon(Icons.check, color: checkStatusColor(status), size: 54),\n            const Text(\n              'Hadir',\n              style: TextStyle(color: Colors.blueGrey),\n            )\n          ],\n        );\n      case 'Tidak Hadir':\n      case 'Terlambat':\n        return Column(\n          children: <Widget>[\n            Icon(Icons.warning, color: checkStatusColor(status), size: 54),\n            Text(\n              status,\n              style: const TextStyle(color: Colors.blueGrey),\n            )\n          ],\n        );\n      case 'Dinas Luar':\n      case 'Cuti Tahunan':\n      case 'Cuti Bersalin':\n      case 'Cuti Alasan Penting':\n      case 'Cuti Sakit':\n      case 'Izin':\n        return Column(\n          children: <Widget>[\n            Icon(Icons.calendar_today,\n                size: 54, color: checkStatusColor(status)),\n            Text(\n              status,\n              style: const TextStyle(color: Colors.blueGrey),\n            )\n          ],\n        );\n      default:\n        return Column(\n          children: <Widget>[\n            Icon(Icons.warning, color: checkStatusColor(status), size: 54),\n            Text(\n              status,\n              style: const TextStyle(color: Colors.blueGrey),\n            )\n          ],\n        );\n    }\n  }\n\n  Widget _buildStatusSection() {\n    if (user.nextPresence.attendTime.isNotEmpty ||\n        user.nextPresence.startTime.isAfter(DateTime.now())) {\n      return _checkStatusIcon(user.nextPresence.status);\n    }\n\n    return Center(\n      child: ClipRRect(\n        borderRadius: BorderRadius.circular(20.0),\n        child: Card(\n          color: Colors.green[300],\n          child: InkWell(\n            onTap: () {\n              Get.to(() => PresenceScreen(user: user))\n                  .then((value) => _getUser());\n            },\n            child: Padding(\n              padding: const EdgeInsets.all(8.0),\n              child: Column(\n                children: const <Widget>[\n                  Icon(\n                    Icons.qr_code_rounded,\n                    size: 84,\n                    color: Colors.white,\n                  ),\n                  Text(\n                    'Mulai Absen',\n                    style: TextStyle(\n                      color: Colors.white,\n                    ),\n                  ),\n                ],\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n\n  String _checkPresenceStatus(double percentage) {\n    if (percentage >= 25 && percentage < 50) {\n      return 'Buruk';\n    } else if (percentage >= 50 && percentage < 75) {\n      return 'Cukup Baik';\n    } else if (percentage >= 75 && percentage < 85) {\n      return 'Baik';\n    } else if (percentage >= 85 && percentage <= 100) {\n      return 'Sangat Baik';\n    }\n\n    return 'Sangat Buruk';\n  }\n\n  Color _checkPresenceStatusColor(double percentage) {\n    if (percentage >= 25 && percentage < 50) {\n      return Colors.red;\n    } else if (percentage >= 50 && percentage < 75) {\n      return Colors.orange;\n    } else if (percentage >= 75 && percentage < 85) {\n      return Colors.blueAccent;\n    } else if (percentage >= 85 && percentage <= 100) {\n      return Colors.green;\n    }\n\n    return Colors.red[800];\n  }\n\n  Widget _buildCountdownSection() {\n    if (_countdownController == null) {\n      return const Text('Memuat Timer...');\n    }\n\n    return CountdownTimer(\n      controller: _countdownController,\n      endWidget: const AutoSizeText(\n        'Semua absen hari ini telah selesai',\n        maxFontSize: 12.0,\n        minFontSize: 10.0,\n      ),\n    );\n  }\n\n  Widget _buildTimerSection() {\n    if (isLoading) {\n      return _buildShimmerSection(Get.width, 180);\n    }\n\n    if (user?.nextPresence != null) {\n      String status = user.nextPresence.status;\n      double fontSize = 14;\n      if (status == 'Terlambat') {\n        final duration = calculateLateTime(\n            user.nextPresence.startTime, user.nextPresence.attendTime);\n        status += ' $duration';\n        fontSize = 12;\n      }\n      return Card(\n        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),\n        elevation: 4.0,\n        child: Padding(\n          padding: const EdgeInsets.all(8.0),\n          child: Column(\n            children: <Widget>[\n              Row(\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: <Widget>[\n                  Text(\n                    DateFormat.EEEE().format(user.nextPresence.date),\n                    style: const TextStyle(fontWeight: FontWeight.w600),\n                  ),\n                  const SizedBox(width: 5.0),\n                  const Text('|'),\n                  const SizedBox(width: 5.0),\n                  Text(\n                    DateFormat.yMMMd().format(user.nextPresence.date),\n                  ),\n                  const SizedBox(width: 5.0),\n                  const Text('|'),\n                  const SizedBox(width: 5.0),\n                  Text(\n                    user.nextPresence.attendTime.isEmpty\n                        ? '-'\n                        : '${user.nextPresence.attendTime} WITA',\n                  ),\n                ],\n              ),\n              const Divider(\n                thickness: 1.0,\n                color: Colors.black26,\n              ),\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  Column(\n                    crossAxisAlignment: CrossAxisAlignment.start,\n                    children: <Widget>[\n                      const Text(\n                        'SKEMA ABSENSI :',\n                        style: TextStyle(fontWeight: FontWeight.bold),\n                      ),\n                      sizedBoxH2,\n                      Text(user.nextPresence.codeType),\n                      sizedBoxH10,\n                      const Text(\n                        'JADWAL ABSENSI :',\n                        style: TextStyle(fontWeight: FontWeight.bold),\n                      ),\n                      sizedBoxH2,\n                      Row(\n                        children: <Widget>[\n                          Text(\n                            DateFormat('HH:mm')\n                                .format(user.nextPresence.startTime),\n                          ),\n                          const Text(' - '),\n                          Text(\n                            DateFormat('HH:mm')\n                                .format(user.nextPresence.endTime),\n                          )\n                        ],\n                      ),\n                      sizedBoxH10,\n                      const Text(\n                        'STATUS KEHADIRAN :',\n                        style: TextStyle(fontWeight: FontWeight.bold),\n                      ),\n                      sizedBoxH2,\n                      Text(\n                        status,\n                        style: TextStyle(\n                            fontSize: fontSize,\n                            color: checkStatusColor(user.nextPresence.status)),\n                      ),\n                      sizedBoxH10,\n                      Text(\n                        _checkTimeLabel(),\n                        style: const TextStyle(fontWeight: FontWeight.bold),\n                      ),\n                      sizedBoxH2,\n                      _buildCountdownSection(),\n                    ],\n                  ),\n                  Expanded(child: _buildStatusSection()),\n                ],\n              ),\n            ],\n          ),\n        ),\n      );\n    }\n\n    if (user?.holiday != null) {\n      return NextPresenceEmptyCardWidget(\n        color: checkStatusColor('Izin'),\n        topLabel: 'Libur',\n        firstLabel: 'JENIS LIBUR',\n        firstContent: 'Libur Nasional',\n        secondLabel: 'NAMA LIBUR',\n        secondContent: user.holiday.name,\n        thirdLabel: 'STATUS KEHADIRAN',\n        thirdContent: 'Libur',\n        fourthLabel: 'CATATAN',\n        fourthContent: '-',\n        trailingLabel: 'Libur Nasional',\n        trailingTop: Icon(Icons.calendar_today_rounded,\n            color: checkStatusColor('Izin'), size: 72),\n      );\n    }\n\n    if (user.isWeekend) {\n      return NextPresenceEmptyCardWidget(\n        color: checkStatusColor('Izin'),\n        topLabel: 'Akhir Pekan',\n        firstLabel: 'JENIS LIBUR',\n        firstContent: 'Akhir Pekan',\n        secondLabel: 'NAMA LIBUR',\n        secondContent: 'Akhir Pekan',\n        thirdLabel: 'STATUS KEHADIRAN',\n        thirdContent: 'Libur',\n        fourthLabel: 'CATATAN',\n        fourthContent: 'Tidak Ada Presensi Hari Ini',\n        trailingLabel: 'AKHIR PEKAN',\n        trailingTop: Icon(Icons.calendar_today_rounded,\n            color: checkStatusColor('Izin'), size: 72),\n      );\n    }\n\n    return NextPresenceEmptyCardWidget(\n      color: _checkPresenceStatusColor(_percentage),\n      topLabel: 'Selesai',\n      firstLabel: 'SKEMA ABSENSI',\n      firstContent: '-',\n      secondLabel: 'JADWAL ABSENSI',\n      secondContent: '-',\n      thirdLabel: 'STATUS KEHADIRAN',\n      thirdContent: _checkPresenceStatus(_percentage),\n      fourthLabel: 'CATATAN',\n      fourthContent: 'Semua Presensi Sudah Selesai',\n      trailingLabel: 'KEHADIRAN',\n      trailingTop: Text(\n        formatPercentage(_percentage),\n        style: TextStyle(\n          fontSize: 32,\n          color: _checkPresenceStatusColor(_percentage),\n        ),\n      ),\n    );\n  }\n\n  Widget _buildPNSHonorerSection() {\n    final pns = _countUserByStatus('PNS');\n    final honorer = _countUserByStatus('Honorer');\n    return Text(\n      '$pns PNS/$honorer Honorer',\n      style: const TextStyle(\n          fontWeight: FontWeight.w600, fontSize: 12.0, color: Colors.grey),\n    );\n  }\n\n  int _countUserByStatus(String status) {\n    if (_users == null) {\n      return 0;\n    }\n    return _users.where((element) => element.status == status).length +\n        (user?.status == status ? 1 : 0);\n  }\n\n  @override\n  void dispose() {\n    _countdownController.dispose();\n    super.dispose();\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    Future.wait([_getUser(), _getAllEmployee()]);\n  }\n\n  Widget _buildUnreadNotificationCount() {\n    if (user != null && user.unreadNotification > 0) {\n      return Positioned(\n        right: 8,\n        top: 6,\n        child: Container(\n          padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 2.0),\n          decoration: BoxDecoration(\n              color: Colors.red, borderRadius: BorderRadius.circular(50)),\n          child: Text(\n            user?.unreadNotification.toString(),\n            style: const TextStyle(fontSize: 12.0, fontWeight: FontWeight.bold),\n          ),\n        ),\n      );\n    }\n\n    return sizedBox;\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      backgroundColor: Colors.grey[100],\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        elevation: 0.0,\n        actions: <Widget>[\n          Stack(\n            children: <Widget>[\n              IconButton(\n                icon: Icon(\n                  (user != null && user.unreadNotification > 0)\n                      ? Icons.notifications_active_rounded\n                      : Icons.notifications_none_rounded,\n                  color: Colors.white,\n                ),\n                onPressed: () {\n                  Get.to(() => NotificationListScreen())\n                      .then((value) => _getUser());\n                },\n              ),\n              _buildUnreadNotificationCount(),\n            ],\n          ),\n        ],\n        leadingWidth: Get.width * 0.25,\n        leading: Image.asset(\n          'assets/logo/logo.png',\n        ),\n      ),\n      body: SmartRefresher(\n        header: const MaterialClassicHeader(),\n        controller: _refreshController,\n        onRefresh: () async {\n          final ProgressDialog pd = ProgressDialog(context);\n          final showing = await pd.show();\n          try {\n            await Future.wait([_getUser(), _getAllEmployee(pd: pd)]);\n          } catch (e) {\n            _refreshController.refreshFailed();\n            showErrorDialog({\n              'message': 'Kesalahan',\n              'errors': {\n                'exception': ['Terjadi kesalahan!']\n              }\n            });\n          } finally {\n            _refreshController.refreshCompleted();\n            if (showing) {\n              pd.hide();\n            }\n          }\n        },\n        child: CustomScrollView(\n          physics: const ClampingScrollPhysics(),\n          slivers: <Widget>[_buildHeader(), _buildNextPresence()],\n        ),\n      ),\n    );\n  }\n\n  SliverToBoxAdapter _buildHeader() {\n    return SliverToBoxAdapter(\n      child: Stack(\n        children: <Widget>[\n          Container(\n            padding: EdgeInsets.only(\n                left: 20.0, right: 20.0, bottom: Get.height * 0.13, top: 5.0),\n            decoration: const BoxDecoration(\n                boxShadow: [\n                  BoxShadow(\n                    color: Colors.black26,\n                    offset: Offset(0, 5),\n                    blurRadius: 10.0,\n                  )\n                ],\n                color: Colors.blueAccent,\n                borderRadius: BorderRadius.only(\n                    bottomLeft: Radius.circular(40.0),\n                    bottomRight: Radius.circular(40.0))),\n            child: Center(\n              child: Column(\n                crossAxisAlignment: CrossAxisAlignment.start,\n                children: <Widget>[\n                  Column(\n                    children: <Widget>[\n                      const Text(\n                        'Selamat Datang',\n                        style: TextStyle(fontSize: 12.0, color: Colors.white),\n                      ),\n                      sizedBoxH10,\n                      _buildUserNameSection(),\n                      _buildPositionSection(),\n                    ],\n                  )\n                ],\n              ),\n            ),\n          ),\n          Align(\n            alignment: Alignment.bottomCenter,\n            child: Container(\n              decoration: const BoxDecoration(\n                boxShadow: [\n                  BoxShadow(\n                    color: Colors.black26,\n                    offset: Offset(0, 3),\n                    blurRadius: 15.0,\n                  )\n                ],\n              ),\n              margin: EdgeInsets.only(\n                top: Get.height * 0.15,\n                left: 20.0,\n                right: 20.0,\n              ),\n              child: ClipRRect(\n                borderRadius: BorderRadius.circular(10),\n                child: SizedBox(\n                  width: Get.width,\n                  child: Card(\n                    shape: RoundedRectangleBorder(\n                        borderRadius: BorderRadius.circular(8)),\n                    child: Padding(\n                      padding: const EdgeInsets.all(8.0),\n                      child: Column(\n                        mainAxisSize: MainAxisSize.min,\n                        children: <Widget>[\n                          Row(\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: <Widget>[\n                              Text(\n                                '${_users == null ? 0 : (_users.length + 1)} Pegawai',\n                                style: const TextStyle(\n                                    fontWeight: FontWeight.w600),\n                              ),\n                              sizedBoxH10,\n                              _buildPNSHonorerSection()\n                            ],\n                          ),\n                          sizedBoxH10,\n                          Padding(\n                            padding: const EdgeInsets.all(8.0),\n                            child: _buildImageStack(),\n                          )\n                        ],\n                      ),\n                    ),\n                  ),\n                ),\n              ),\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n\n  SliverToBoxAdapter _buildNextPresence() {\n    return SliverToBoxAdapter(\n      child: Container(\n        margin: const EdgeInsets.only(top: 12.0),\n        padding: const EdgeInsets.all(8),\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: <Widget>[\n            const Text(\n              'Absen Selanjutnya',\n              style: TextStyle(\n                fontWeight: FontWeight.w600,\n                fontSize: 16.0,\n              ),\n            ),\n            Center(\n              child: _buildTimerSection(),\n            ),\n            sizedBoxH30,\n            const Text(\n              'Absen Hari Ini',\n              style: TextStyle(\n                fontWeight: FontWeight.w600,\n                fontSize: 16.0,\n              ),\n            ),\n            Center(\n              child: Column(\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: _buildPresenceSection(),\n              ),\n            )\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/image_detail_screen.dart",
    "content": "import 'dart:typed_data';\n\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:photo_view/photo_view.dart';\n\nclass ImageDetailScreen extends StatefulWidget {\n  const ImageDetailScreen({this.imageUrl, this.bytes, @required this.tag});\n\n  final String imageUrl;\n  final Uint8List bytes;\n  final String tag;\n\n  @override\n  _ImageDetailScreenState createState() => _ImageDetailScreenState();\n}\n\nclass _ImageDetailScreenState extends State<ImageDetailScreen> {\n  @override\n  void initState() {\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final provider = widget.imageUrl == null\n        ? MemoryImage(widget.bytes)\n        : CachedNetworkImageProvider(widget.imageUrl);\n    return Scaffold(\n      body: Center(\n          child: PhotoView(\n              heroAttributes: PhotoViewHeroAttributes(\n                  tag: widget.tag, transitionOnUserGestures: true),\n              maxScale: 5.0,\n              imageProvider: provider as ImageProvider)),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/login_screen.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:device_info/device_info.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'package:http/http.dart' as http;\nimport 'package:onesignal_flutter/onesignal_flutter.dart';\nimport 'package:provider/provider.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/bottom_nav_screen.dart';\nimport 'package:spo_balaesang/screen/forgot_pass_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\n\nclass LoginScreen extends StatefulWidget {\n  @override\n  _LoginScreenState createState() => _LoginScreenState();\n}\n\nclass _LoginScreenState extends State<LoginScreen> {\n  double getSmallDiameter = Get.width * 2 / 3;\n\n  double getBigDiameter = Get.size.width * 7 / 8;\n\n  bool _isLoading = false;\n\n  final TextEditingController _phoneController = TextEditingController();\n  final TextEditingController _passwordController = TextEditingController();\n  bool isPasswordVisible = false;\n\n  Widget _buildPhoneForm() {\n    return TextFormField(\n      validator: (String value) {\n        if (value.isEmpty) {\n          return 'Nomor telpon tidak boleh kosong!';\n        }\n        return null;\n      },\n      controller: _phoneController,\n      keyboardType: TextInputType.phone,\n      decoration: InputDecoration(\n          focusedBorder: const UnderlineInputBorder(\n              borderSide: BorderSide(color: Colors.blueAccent)),\n          prefixIcon: Icon(\n            Icons.phone_android,\n            color: Colors.blueAccent[700],\n          ),\n          labelText: 'Nomor Telpon',\n          labelStyle: TextStyle(color: Colors.blueAccent[200])),\n      style: TextStyle(color: Colors.blueAccent[700]),\n    );\n  }\n\n  Widget _buildPasswordForm() {\n    return TextFormField(\n      validator: (String value) {\n        if (value.isEmpty) {\n          return 'Kata sandi tidak boleh kosong!';\n        }\n        return null;\n      },\n      obscureText: !isPasswordVisible,\n      controller: _passwordController,\n      keyboardType: TextInputType.text,\n      decoration: InputDecoration(\n          focusedBorder: const UnderlineInputBorder(\n              borderSide: BorderSide(color: Colors.blueAccent)),\n          suffixIcon: GestureDetector(\n            onTap: () {\n              setState(() {\n                isPasswordVisible = !isPasswordVisible;\n              });\n            },\n            child: Icon(\n              isPasswordVisible ? Icons.visibility_off : Icons.visibility,\n              color: Colors.blueAccent[700],\n            ),\n          ),\n          prefixIcon: Icon(\n            Icons.lock_outline,\n            color: Colors.blueAccent[700],\n          ),\n          labelText: 'Password',\n          labelStyle: TextStyle(color: Colors.blueAccent[200])),\n      style: TextStyle(color: Colors.blueAccent[700]),\n    );\n  }\n\n  Future<String> getDeviceInfo() async {\n    final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();\n    String model;\n    if (Platform.isAndroid) {\n      final AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;\n      model = androidInfo.model;\n    }\n    return model;\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      backgroundColor: Colors.grey[200],\n      body: Stack(\n        children: <Widget>[\n          Positioned(\n            right: -getSmallDiameter / 3,\n            top: -getSmallDiameter / 3,\n            child: Container(\n              width: getSmallDiameter,\n              height: getSmallDiameter,\n              decoration: BoxDecoration(\n                shape: BoxShape.circle,\n                gradient: LinearGradient(\n                    colors: [Colors.lightBlue[200], Colors.blueAccent],\n                    begin: Alignment.topCenter,\n                    end: Alignment.bottomCenter),\n              ),\n            ),\n          ),\n          Positioned(\n            left: -getBigDiameter / 4,\n            top: -getBigDiameter / 4,\n            child: Container(\n              width: getBigDiameter,\n              height: getBigDiameter,\n              decoration: BoxDecoration(\n                shape: BoxShape.circle,\n                gradient: LinearGradient(\n                    colors: [Colors.blueAccent[700], Colors.blueAccent],\n                    begin: Alignment.topCenter,\n                    end: Alignment.bottomCenter),\n              ),\n              child: Column(\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: <Widget>[\n                  Image.asset(\n                    'assets/logo/logo.png',\n                    width: 200,\n                  ),\n                  const Text(\n                    'Sistem Absensi Pegawai',\n                    style: TextStyle(fontSize: 12.0, color: Colors.white),\n                  ),\n                ],\n              ),\n            ),\n          ),\n          Align(\n            alignment: Alignment.bottomCenter,\n            child: ListView(\n              children: <Widget>[\n                ClipRRect(\n                  borderRadius: BorderRadius.circular(6),\n                  child: Container(\n                    margin: const EdgeInsets.fromLTRB(5.0, 350, 5.0, 10),\n                    padding: const EdgeInsets.fromLTRB(10, 0, 10, 25),\n                    child: Card(\n                      elevation: 6.0,\n                      child: Column(\n                        children: <Widget>[\n                          _buildPhoneForm(),\n                          _buildPasswordForm()\n                        ],\n                      ),\n                    ),\n                  ),\n                ),\n                Align(\n                  alignment: Alignment.centerRight,\n                  child: Container(\n                    margin: const EdgeInsets.only(right: 20.0, bottom: 20.0),\n                    child: InkWell(\n                      onTap: () {\n                        Get.to(() => ForgotPassScreen(),\n                            fullscreenDialog: true);\n                      },\n                      child: Text(\n                        'Lupa Password?',\n                        style: TextStyle(color: Colors.blue[800]),\n                      ),\n                    ),\n                  ),\n                ),\n                Container(\n                  margin: const EdgeInsets.fromLTRB(20, 0, 20, 30),\n                  child: Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: <Widget>[\n                      SizedBox(\n                        width: MediaQuery.of(context).size.width * 0.5,\n                        height: 40.0,\n                        child: Container(\n                          decoration: BoxDecoration(\n                            borderRadius: BorderRadius.circular(10.0),\n                            gradient: LinearGradient(\n                                colors: [\n                                  Colors.lightBlue[700],\n                                  Colors.lightBlue[900]\n                                ],\n                                begin: Alignment.topLeft,\n                                end: Alignment.bottomRight),\n                          ),\n                          child: Material(\n                            color: Colors.transparent,\n                            borderRadius: BorderRadius.circular(10.0),\n                            child: InkWell(\n                              borderRadius: BorderRadius.circular(10.0),\n                              onTap: _isLoading\n                                  ? null\n                                  : () async {\n                                      final SharedPreferences prefs =\n                                          await SharedPreferences.getInstance();\n                                      setState(() {\n                                        _isLoading = true;\n                                      });\n                                      final String _deviceName =\n                                          await getDeviceInfo();\n                                      final Map<String, dynamic> data =\n                                          <String, dynamic>{\n                                        'phone': _phoneController.value.text,\n                                        'password':\n                                            _passwordController.value.text,\n                                        'device_name': _deviceName\n                                      };\n                                      try {\n                                        final dataRepository =\n                                            Provider.of<DataRepository>(context,\n                                                listen: false);\n                                        final http.Response response =\n                                            await dataRepository.login(data);\n                                        final Map<String, dynamic> result =\n                                            jsonDecode(response.body)\n                                                as Map<String, dynamic>;\n                                        if (response.statusCode == 200) {\n                                          prefs.setString(\n                                              prefsTokenKey,\n                                              jsonEncode(result[jsonDataField]\n                                                  [prefsTokenKey]));\n                                          prefs.setString(\n                                              prefsUserKey,\n                                              jsonEncode(\n                                                  result[jsonDataField]));\n                                          OneSignal.shared.setExternalUserId(\n                                              result[jsonDataField][userIdField]\n                                                  .toString());\n                                          Get.off(() => BottomNavScreen());\n                                        } else {\n                                          showErrorDialog(result);\n                                        }\n                                      } on SocketException catch (e) {\n                                        showErrorDialog({\n                                          'message': 'Kesalahan',\n                                          'errors': {\n                                            'exception': [e.message]\n                                          }\n                                        });\n                                      } catch (e) {\n                                        showErrorDialog({\n                                          'message': 'Kesalahan',\n                                          'errors': {\n                                            'exception': ['Terjadi kesalahan!']\n                                          }\n                                        });\n                                      } finally {\n                                        setState(() {\n                                          _isLoading = false;\n                                        });\n                                      }\n                                    },\n                              child: Center(\n                                child: _isLoading\n                                    ? const SizedBox(\n                                        height: 30.0,\n                                        width: 30.0,\n                                        child: CircularProgressIndicator(\n                                          valueColor: AlwaysStoppedAnimation(\n                                              Colors.white),\n                                        ),\n                                      )\n                                    : const Text(\n                                        'MASUK',\n                                        style: TextStyle(\n                                            color: Colors.white,\n                                            fontWeight: FontWeight.w600),\n                                      ),\n                              ),\n                            ),\n                          ),\n                        ),\n                      )\n                    ],\n                  ),\n                ),\n                Column(\n                  mainAxisAlignment: MainAxisAlignment.center,\n                  children: const <Widget>[\n                    Text(\n                      'v5.0.3',\n                      style: TextStyle(\n                          color: Colors.grey,\n                          fontSize: 12.0,\n                          fontWeight: FontWeight.w500),\n                    ),\n                    Text(\n                      'Sistem Absensi Pegawai Online by Banua Coders ',\n                      style: TextStyle(\n                          color: Colors.grey,\n                          fontSize: 12.0,\n                          fontWeight: FontWeight.w500),\n                    )\n                  ],\n                ),\n              ],\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/notification_list_screen.dart",
    "content": "import 'dart:convert';\n\nimport 'package:flare_flutter/flare_actor.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:get/get.dart';\nimport 'package:http/http.dart' as http;\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\nimport 'package:spo_balaesang/models/notification.dart';\nimport 'package:spo_balaesang/models/user.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/create_notification_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\n\nclass NotificationListScreen extends StatefulWidget {\n  @override\n  _NotificationListScreenState createState() => _NotificationListScreenState();\n}\n\nclass _NotificationListScreenState extends State<NotificationListScreen> {\n  List<UserNotification> notifications = [];\n  bool _isLoading = false;\n  DataRepository dataRepo;\n  Set<String> choices = {'Tandai Semua Dibaca', 'Hapus Semua'};\n  User _user;\n\n  Future<void> getUser() async {\n    final sp = await SharedPreferences.getInstance();\n    final _data = sp.get(prefsUserKey);\n    final Map<String, dynamic> _json =\n        jsonDecode(_data.toString()) as Map<String, dynamic>;\n\n    setState(() {\n      _user = User.fromJson(_json);\n    });\n  }\n\n  @override\n  void setState(void Function() fn) {\n    if (mounted) {\n      super.setState(fn);\n    }\n  }\n\n  Future<void> _fetchNotificationsData() async {\n    try {\n      setState(() {\n        _isLoading = true;\n      });\n      final Map<String, dynamic> _result = await dataRepo.getAllNotifications();\n      final List<dynamic> _notifications = _result['data'] as List<dynamic>;\n      final List<UserNotification> _data = _notifications\n          .map(\n              (json) => UserNotification.fromJson(json as Map<String, dynamic>))\n          .toList();\n      setState(() {\n        notifications = _data;\n      });\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n    } finally {\n      setState(() {\n        _isLoading = false;\n      });\n    }\n  }\n\n  Future<void> _readNotification(String id) async {\n    final ProgressDialog pd = ProgressDialog(context, isDismissible: false);\n    try {\n      pd.show();\n      final Map<String, dynamic> data = {'notification_id': id};\n      final http.Response response = await dataRepo.readNotification(data);\n      final Map<String, dynamic> _res =\n          jsonDecode(response.body) as Map<String, dynamic>;\n      if (response.statusCode == 200) {\n        pd.hide();\n        showAlertDialog(\"success\", \"Sukses\", _res['message'].toString(),\n            dismissible: true);\n        _fetchNotificationsData();\n      } else {\n        if (pd.isShowing()) pd.hide();\n        showErrorDialog(_res);\n      }\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n      pd.hide();\n    }\n  }\n\n  Future<void> _readAllNotifications() async {\n    final ProgressDialog pd = ProgressDialog(context, isDismissible: false);\n    try {\n      pd.show();\n      final Map<String, dynamic> _res = await dataRepo.readAllNotifications();\n      if (_res['success'] as bool) {\n        pd.hide();\n        showAlertDialog(\"success\", \"Sukses\", _res['message'].toString(),\n            dismissible: true);\n        _fetchNotificationsData();\n      } else {\n        if (pd.isShowing()) pd.hide();\n        showErrorDialog(_res);\n      }\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n      pd.hide();\n    }\n  }\n\n  Future<void> _deleteAllNotifications() async {\n    final ProgressDialog pd = ProgressDialog(context, isDismissible: false);\n    try {\n      pd.show();\n      final Map<String, dynamic> _res = await dataRepo.deleteAllNotifications();\n      if (_res['success'] as bool) {\n        pd.hide();\n        showAlertDialog(\"success\", \"Sukses\", _res['message'].toString(),\n            dismissible: true);\n        _fetchNotificationsData();\n      } else {\n        if (pd.isShowing()) pd.hide();\n        showErrorDialog(_res);\n      }\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n      pd.hide();\n    }\n  }\n\n  Widget _buildMarker(bool isRead) {\n    return isRead\n        ? const SizedBox()\n        : Container(\n            decoration: BoxDecoration(\n              borderRadius: BorderRadius.circular(50.0),\n              color: Colors.red,\n            ),\n            padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 1.0),\n            child: const Text(\n              '',\n              style: TextStyle(fontSize: 10.0),\n            ),\n          );\n  }\n\n  Widget _buildBody() {\n    if (_isLoading) {\n      return const Center(\n          child: SpinKitChasingDots(\n        size: 45,\n        color: Colors.blueAccent,\n      ));\n    }\n    if (notifications.isEmpty) {\n      return Center(\n        child: Column(\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: <Widget>[\n              SizedBox(\n                width: Get.width * 0.5,\n                height: Get.height * 0.3,\n                child: const FlareActor(\n                  'assets/flare/not_found.flr',\n                  animation: 'empty',\n                ),\n              ),\n              const Text('Belum ada pemberitahuan!')\n            ]),\n      );\n    }\n    return Padding(\n      padding: const EdgeInsets.all(8.0),\n      child: ListView.builder(\n        itemBuilder: (_, index) {\n          final UserNotification notification = notifications[index];\n          return Container(\n            margin: const EdgeInsets.only(bottom: 8.0),\n            child: Card(\n              shape: RoundedRectangleBorder(\n                  borderRadius: BorderRadius.circular(6)),\n              elevation: 2.0,\n              child: InkWell(\n                onTap: () {\n                  _readNotification(notification.id);\n                },\n                child: Padding(\n                    padding: const EdgeInsets.all(8.0),\n                    child: Column(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: <Widget>[\n                        Row(\n                          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                          children: <Widget>[\n                            Expanded(\n                              child: Text(\n                                '${notification.data['heading']}',\n                                style: const TextStyle(\n                                  fontWeight: FontWeight.w600,\n                                ),\n                              ),\n                            ),\n                            _buildMarker(notification.isRead)\n                          ],\n                        ),\n                        dividerT1,\n                        Text(notification.data['body'].toString())\n                      ],\n                    )),\n              ),\n            ),\n          );\n        },\n        itemCount: notifications.length,\n      ),\n    );\n  }\n\n  List<Widget> _buildActionMenu() {\n    return notifications.isNotEmpty\n        ? <Widget>[\n            PopupMenuButton<String>(\n              itemBuilder: (BuildContext context) {\n                return choices\n                    .map((String choice) => PopupMenuItem<String>(\n                          value: choice,\n                          child: Text(choice),\n                        ))\n                    .toList();\n              },\n              onSelected: (value) {\n                if (value == choices.first) {\n                  _readAllNotifications();\n                }\n                if (value == choices.last) {\n                  _deleteAllNotifications();\n                }\n              },\n              offset: const Offset(0, 100),\n            )\n          ]\n        : [];\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    dataRepo = Provider.of<DataRepository>(context, listen: false);\n    getUser();\n    _fetchNotificationsData();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        actions: _buildActionMenu(),\n        title: const Text(\n          'Pemberitahuan',\n          style: TextStyle(color: Colors.white),\n        ),\n        backgroundColor: Colors.blueAccent,\n        iconTheme: const IconThemeData(color: Colors.white),\n      ),\n      body: _buildBody(),\n      floatingActionButton: _user?.position == 'Camat'\n          ? FloatingActionButton(\n              onPressed: () {\n                Get.to(() => CreateNotificationScreen());\n              },\n              backgroundColor: Colors.blueAccent,\n              child: const Icon(Icons.add),\n            )\n          : null,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/on_boarding_screen.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'package:introduction_screen/introduction_screen.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\n\nclass OnBoardingScreen extends StatelessWidget {\n  const OnBoardingScreen({this.page});\n\n  final Widget page;\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      body: IntroductionScreen(\n        pages: onBoardingScreens,\n        showSkipButton: true,\n        skip: const Text(\"Skip\"),\n        next: const Icon(\n          Icons.navigate_next,\n          color: Colors.blueAccent,\n          size: 32,\n        ),\n        onSkip: () {\n          Get.off(() => page);\n        },\n        done: const Text(\"Selesai\",\n            style: TextStyle(fontWeight: FontWeight.w600)),\n        onDone: () {\n          Get.off(() => page);\n        },\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/outstation_list_screen.dart",
    "content": "import 'package:flare_flutter/flare_actor.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_cupertino_datetime_picker/flutter_cupertino_datetime_picker.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:get/get.dart';\nimport 'package:intl/intl.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/models/outstation.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/change_outstation_photo_screen.dart';\nimport 'package:spo_balaesang/screen/create_outstation_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/employee_proposal_widget.dart';\n\nclass OutstationListScreen extends StatefulWidget {\n  @override\n  _OutstationListScreenState createState() => _OutstationListScreenState();\n}\n\nclass _OutstationListScreenState extends State<OutstationListScreen> {\n  List<Outstation> _outstations = <Outstation>[];\n  bool _isLoading = false;\n  DateTime _date;\n\n  @override\n  void setState(void Function() fn) {\n    if (mounted) {\n      super.setState(fn);\n    }\n  }\n\n  Future<void> _fetchOutstationData() async {\n    try {\n      setState(() {\n        _isLoading = true;\n      });\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final Map<String, dynamic> _result =\n          await dataRepo.getAllOutstation(_date);\n      final List<dynamic> outstations = _result['data'] as List<dynamic>;\n      final List<Outstation> _data = outstations\n          .map((json) => Outstation.fromJson(json as Map<String, dynamic>))\n          .toList();\n      setState(() {\n        _outstations = _data;\n      });\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n    } finally {\n      setState(() {\n        _isLoading = false;\n      });\n    }\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    _date = DateTime.now();\n    _fetchOutstationData();\n  }\n\n  Future<void> _selectDate(BuildContext context) async {\n    DatePicker.showDatePicker(context,\n        initialDateTime: _date,\n        minDateTime: DateTime(2021),\n        maxDateTime: DateTime(DateTime.now().year + 5), onConfirm: (picked, _) {\n      if (picked != null) {\n        setState(() {\n          _date = picked;\n        });\n        _fetchOutstationData();\n      }\n    }, locale: DateTimePickerLocale.id, dateFormat: 'MMMM-y');\n  }\n\n  Widget _buildBody() {\n    if (_isLoading) {\n      return SizedBox(\n        height: Get.height * 0.7,\n        child: const Center(\n            child: SpinKitFadingFour(\n          size: 45,\n          color: Colors.blueAccent,\n        )),\n      );\n    }\n    if (_outstations.isEmpty) {\n      return SizedBox(\n        height: Get.height * 0.7,\n        child: Center(\n          child: Column(\n              mainAxisAlignment: MainAxisAlignment.center,\n              children: <Widget>[\n                SizedBox(\n                  width: Get.width * 0.5,\n                  height: Get.height * 0.3,\n                  child: const FlareActor(\n                    'assets/flare/not_found.flr',\n                    animation: 'empty',\n                  ),\n                ),\n                const Text('Belum ada Dinas Luar yang diajukan!')\n              ]),\n        ),\n      );\n    }\n    return Column(\n      children: _outstations.map((Outstation outstation) {\n        final DateTime dueDate = outstation.dueDate;\n        final DateTime startDate = outstation.startDate;\n        return EmployeeProposalWidget(\n          title: outstation.title,\n          description: outstation.description,\n          dueDate: dueDate,\n          startDate: startDate,\n          approvalStatus: outstation.approvalStatus,\n          isApproved: outstation.isApproved,\n          heroTag: outstation.id.toString(),\n          photo: outstation.photo,\n          updateWidget: ChangeOutstationPhotoScreen(outstation: outstation),\n        );\n      }).toList(),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n        appBar: AppBar(\n          backgroundColor: Colors.blueAccent,\n          title: const Text('Daftar Dinas Luar'),\n        ),\n        floatingActionButton: FloatingActionButton(\n          backgroundColor: Colors.blueAccent,\n          onPressed: () {\n            Get.to(() => CreateOutstationScreen());\n          },\n          child: const Icon(Icons.add),\n        ),\n        body: Container(\n          height: Get.height,\n          padding: const EdgeInsets.all(8.0),\n          child: SingleChildScrollView(\n            child: Column(\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: <Widget>[\n                const Text(\n                  'Pilih Tahun & Bulan : ',\n                ),\n                Row(\n                  mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                  children: <Widget>[\n                    Text(\n                      DateFormat.yMMMM('id_ID').format(_date),\n                      style: const TextStyle(\n                        fontSize: 22.0,\n                        fontWeight: FontWeight.w600,\n                      ),\n                    ),\n                    IconButton(\n                        icon: Icon(\n                          Icons.calendar_today_rounded,\n                          color: Colors.blueAccent[400],\n                        ),\n                        onPressed: () {\n                          _selectDate(context);\n                        })\n                  ],\n                ),\n                sizedBoxH4,\n                dividerT1,\n                sizedBoxH4,\n                _buildBody()\n              ],\n            ),\n          ),\n        ));\n  }\n}\n"
  },
  {
    "path": "lib/screen/paid_leave_list_screen.dart",
    "content": "import 'package:flare_flutter/flare_actor.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_cupertino_datetime_picker/flutter_cupertino_datetime_picker.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:get/get.dart';\nimport 'package:intl/intl.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/models/paid_leave.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/change_paid_leave_photo_screen.dart';\nimport 'package:spo_balaesang/screen/create_paid_leave_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/employee_proposal_widget.dart';\n\nclass PaidLeaveListScreen extends StatefulWidget {\n  @override\n  _PaidLeaveListScreenState createState() => _PaidLeaveListScreenState();\n}\n\nclass _PaidLeaveListScreenState extends State<PaidLeaveListScreen> {\n  List<PaidLeave> _paidLeaves;\n  bool _isLoading = false;\n  DateTime _date;\n\n  @override\n  void setState(void Function() fn) {\n    if (mounted) {\n      super.setState(fn);\n    }\n  }\n\n  Future<void> _fetchPaidLeaveData() async {\n    try {\n      setState(() {\n        _isLoading = true;\n      });\n      final DataRepository dataRepo =\n          Provider.of<DataRepository>(context, listen: false);\n      final Map<String, dynamic> _result =\n          await dataRepo.getAllPaidLeave(_date);\n      final List<dynamic> paidLeaves = _result['data'] as List<dynamic>;\n      final List<PaidLeave> _data = paidLeaves\n          .map((json) => PaidLeave.fromJson(json as Map<String, dynamic>))\n          .toList();\n      setState(() {\n        _paidLeaves = _data;\n      });\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n    } finally {\n      _isLoading = false;\n    }\n  }\n\n  @override\n  void initState() {\n    _date = DateTime.now();\n    _fetchPaidLeaveData();\n    super.initState();\n  }\n\n  Future<void> _selectDate(BuildContext context) async {\n    DatePicker.showDatePicker(context,\n        initialDateTime: _date,\n        minDateTime: DateTime(2021),\n        maxDateTime: DateTime(DateTime.now().year + 5), onConfirm: (picked, _) {\n      if (picked != null) {\n        setState(() {\n          _date = picked;\n        });\n        _fetchPaidLeaveData();\n      }\n    }, locale: DateTimePickerLocale.id, dateFormat: 'MMMM-y');\n  }\n\n  Widget _buildBody() {\n    if (_isLoading) {\n      return SizedBox(\n        height: Get.height * 0.7,\n        child: const Center(\n            child: SpinKitFadingGrid(\n          size: 45,\n          color: Colors.blueAccent,\n        )),\n      );\n    }\n\n    if (_paidLeaves.isEmpty) {\n      return SizedBox(\n        height: Get.height * 0.7,\n        child: Center(\n          child: Column(\n              mainAxisAlignment: MainAxisAlignment.center,\n              children: <Widget>[\n                SizedBox(\n                  width: Get.width * 0.5,\n                  height: Get.height * 0.3,\n                  child: const FlareActor(\n                    'assets/flare/not_found.flr',\n                    animation: 'empty',\n                  ),\n                ),\n                const Text('Belum ada Cuti yang diajukan!')\n              ]),\n        ),\n      );\n    }\n    return Column(\n      children: _paidLeaves\n          .map((PaidLeave paidLeave) => _buildPaidLeaveItem(paidLeave))\n          .toList(),\n    );\n  }\n\n  Widget _buildPaidLeaveItem(PaidLeave paidLeave) {\n    final startDate = paidLeave.startDate;\n    final dueDate = paidLeave.dueDate;\n    return EmployeeProposalWidget(\n      title: paidLeave.title,\n      description: paidLeave.description,\n      startDate: startDate,\n      dueDate: dueDate,\n      approvalStatus: paidLeave.approvalStatus,\n      photo: paidLeave.photo,\n      isApproved: paidLeave.isApproved,\n      isPaidLeave: true,\n      updateWidget: ChangePaidLeavePhotoScreen(paidLeave: paidLeave),\n      heroTag: paidLeave.id.toString(),\n      category: paidLeave.category,\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Daftar Cuti'),\n      ),\n      floatingActionButton: FloatingActionButton(\n        backgroundColor: Colors.blueAccent,\n        onPressed: () {\n          Get.to(() => CreatePaidLeaveScreen());\n        },\n        child: const Icon(Icons.add),\n      ),\n      body: Container(\n        height: Get.height,\n        padding: const EdgeInsets.all(8.0),\n        child: SingleChildScrollView(\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              const Text(\n                'Pilih Tahun & Bulan : ',\n              ),\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  Text(\n                    DateFormat.yMMMM('id_ID').format(_date),\n                    style: const TextStyle(\n                      fontSize: 22.0,\n                      fontWeight: FontWeight.w600,\n                    ),\n                  ),\n                  IconButton(\n                      icon: Icon(\n                        Icons.calendar_today_rounded,\n                        color: Colors.blueAccent[400],\n                      ),\n                      onPressed: () {\n                        _selectDate(context);\n                      })\n                ],\n              ),\n              sizedBoxH4,\n              dividerT1,\n              sizedBoxH4,\n              _buildBody()\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/permission_list_screen.dart",
    "content": "import 'package:flare_flutter/flare_actor.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_cupertino_datetime_picker/flutter_cupertino_datetime_picker.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:get/get.dart';\nimport 'package:intl/intl.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/models/absent_permission.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/change_absent_permission_photo_screen.dart';\nimport 'package:spo_balaesang/screen/create_permission_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/employee_proposal_widget.dart';\n\nclass PermissionListScreen extends StatefulWidget {\n  @override\n  _PermissionListScreenState createState() => _PermissionListScreenState();\n}\n\nclass _PermissionListScreenState extends State<PermissionListScreen> {\n  List<AbsentPermission> _permissions = <AbsentPermission>[];\n  bool _isLoading = false;\n  DateTime _date;\n\n  @override\n  void setState(void Function() fn) {\n    if (mounted) {\n      super.setState(fn);\n    }\n  }\n\n  Future<void> _fetchPermissionData() async {\n    try {\n      setState(() {\n        _isLoading = true;\n      });\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final Map<String, dynamic> _result =\n          await dataRepo.getAllPermissions(_date);\n      final List<dynamic> permissions = _result['data'] as List<dynamic>;\n      final List<AbsentPermission> _data = permissions\n          .map(\n              (json) => AbsentPermission.fromJson(json as Map<String, dynamic>))\n          .toList();\n      setState(() {\n        _permissions = _data;\n        _isLoading = false;\n      });\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n    }\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    _date = DateTime.now();\n    _fetchPermissionData();\n  }\n\n  Future<void> _selectDate(BuildContext context) async {\n    DatePicker.showDatePicker(context,\n        initialDateTime: _date,\n        minDateTime: DateTime(2021),\n        maxDateTime: DateTime(DateTime.now().year + 5), onConfirm: (picked, _) {\n      if (picked != null) {\n        setState(() {\n          _date = picked;\n        });\n        _fetchPermissionData();\n      }\n    }, locale: DateTimePickerLocale.id, dateFormat: 'MMMM-y');\n  }\n\n  Widget _buildBody() {\n    if (_isLoading) {\n      return SizedBox(\n        height: Get.height * 0.7,\n        child: const Center(\n            child: SpinKitFadingFour(\n          size: 45,\n          color: Colors.blueAccent,\n        )),\n      );\n    }\n\n    if (_permissions.isEmpty) {\n      return SizedBox(\n        height: Get.height * 0.7,\n        child: Center(\n          child: Column(\n              mainAxisAlignment: MainAxisAlignment.center,\n              children: <Widget>[\n                SizedBox(\n                  width: Get.width * 0.5,\n                  height: Get.height * 0.3,\n                  child: const FlareActor(\n                    'assets/flare/not_found.flr',\n                    animation: 'empty',\n                  ),\n                ),\n                const Text('Belum ada izin yang diajukan!')\n              ]),\n        ),\n      );\n    }\n\n    return Column(\n      children: _permissions.map((AbsentPermission permission) {\n        final DateTime dueDate = permission.dueDate;\n        final DateTime startDate = permission.startDate;\n        return EmployeeProposalWidget(\n          photo: permission.photo,\n          heroTag: permission.id.toString(),\n          isApproved: permission.isApproved,\n          startDate: startDate,\n          dueDate: dueDate,\n          approvalStatus: permission.approvalStatus,\n          description: permission.description,\n          title: permission.title,\n          updateWidget: ChangePermissionPhotoScreen(permission: permission),\n        );\n      }).toList(),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n        appBar: AppBar(\n          backgroundColor: Colors.blueAccent,\n          title: const Text('Daftar Izin'),\n        ),\n        floatingActionButton: FloatingActionButton(\n          backgroundColor: Colors.blueAccent,\n          onPressed: () {\n            Get.to(() => CreatePermissionScreen());\n          },\n          child: const Icon(Icons.add),\n        ),\n        body: Container(\n          height: Get.height,\n          padding: const EdgeInsets.all(8.0),\n          child: SingleChildScrollView(\n            child: Column(\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: <Widget>[\n                const Text(\n                  'Pilih Tahun & Bulan : ',\n                ),\n                Row(\n                  mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                  children: <Widget>[\n                    Text(\n                      DateFormat.yMMMM('id_ID').format(_date),\n                      style: const TextStyle(\n                        fontSize: 22.0,\n                        fontWeight: FontWeight.w600,\n                      ),\n                    ),\n                    IconButton(\n                        icon: Icon(\n                          Icons.calendar_today_rounded,\n                          color: Colors.blueAccent[400],\n                        ),\n                        onPressed: () {\n                          _selectDate(context);\n                        })\n                  ],\n                ),\n                sizedBoxH4,\n                dividerT1,\n                sizedBoxH4,\n                _buildBody()\n              ],\n            ),\n          ),\n        ));\n  }\n}\n"
  },
  {
    "path": "lib/screen/presence_screen.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:android_intent/android_intent.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:geocoding/geocoding.dart';\nimport 'package:geolocator/geolocator.dart';\nimport 'package:get/get.dart';\nimport 'package:http/http.dart' as http;\nimport 'package:image_picker/image_picker.dart';\nimport 'package:location/location.dart' as location;\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:qr_code_scanner/qr_code_scanner.dart';\nimport 'package:spo_balaesang/models/user.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/screen/bottom_nav_screen.dart';\nimport 'package:spo_balaesang/screen/image_detail_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/file_util.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/image_placeholder_widget.dart';\nimport 'package:spo_balaesang/widgets/user_info_card_widget.dart';\n\nclass PresenceScreen extends StatefulWidget {\n  const PresenceScreen({this.user});\n\n  final User user;\n\n  @override\n  _PresenceScreenState createState() => _PresenceScreenState();\n}\n\nclass _PresenceScreenState extends State<PresenceScreen> {\n  String _base64Image;\n  String _fileName;\n  File _tmpFile;\n  String _address = \"\";\n  double _latitude = 0;\n  double _longitude = 0;\n  String _code;\n  User _user;\n\n  final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');\n  QRViewController controller;\n\n  Future<void> _openCamera() async {\n    if (_address == null || _address.isEmpty) {\n      showAlertDialog('failure', 'Lokasi tidak ditemukan.',\n          'Pastikan anda sudah menyalakan akses lokasi dan mengizinkan aplikasi untuk mengakses lokasi anda',\n          dismissible: false);\n    } else {\n      final picture = await ImagePicker().getImage(source: ImageSource.camera);\n      final file = await compressAndGetFile(File(picture.path),\n          '/storage/emulated/0/Android/data/com.banuacoders.siap/files/Pictures/images.jpg');\n      setState(() {\n        _tmpFile = file;\n        _base64Image = base64Encode(_tmpFile.readAsBytesSync());\n        _fileName = _tmpFile.path.split('/').last;\n      });\n      await file.delete(recursive: true);\n    }\n  }\n\n  void _onQRViewCreated(QRViewController controller) {\n    this.controller = controller;\n\n    controller.scannedDataStream.listen((scanData) {\n      setState(() {\n        _code = scanData.code;\n      });\n      if (_address != null || _address.isNotEmpty) {\n        this.controller?.pauseCamera();\n        _uploadData();\n      } else {\n        showAlertDialog('failure', 'Gagal',\n            'Lokasi tidak ditemukan.\\nPastikan lokasi sudah diaktifkan!',\n            dismissible: false);\n      }\n    });\n  }\n\n  Future<void> _uploadData() async {\n    final ProgressDialog pd = ProgressDialog(context, isDismissible: false);\n    try {\n      pd.show();\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final Map<String, dynamic> data = {\n        'code': _code,\n        'latitude': _latitude,\n        'longitude': _longitude,\n        'address': _address,\n        'photo': _base64Image,\n        'file_name': _fileName\n      };\n      final http.Response response = await dataRepo.presence(data);\n      final Map<String, dynamic> _res =\n          jsonDecode(response.body) as Map<String, dynamic>;\n      if (response.statusCode == 200) {\n        pd.hide();\n        showAlertDialog(\"success\", \"Sukses\", _res['message'].toString(),\n            dismissible: false);\n        Timer(\n            const Duration(seconds: 5), () => Get.off(() => BottomNavScreen()));\n      } else {\n        controller?.resumeCamera();\n        if (pd.isShowing()) pd.hide();\n        showErrorDialog(_res);\n      }\n    } catch (e) {\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n      pd.hide();\n    }\n  }\n\n  Future<void> _getUserLocation() async {\n    if (_address.isNotEmpty) {\n      setState(() {\n        _address = null;\n      });\n    }\n\n    if (!(await Geolocator.isLocationServiceEnabled())) {\n      final bool isLocationServiceEnable =\n          await location.Location().requestService();\n      if (!isLocationServiceEnable) {\n        const AndroidIntent intent =\n            AndroidIntent(action: 'android.settings.LOCATION_SOURCE_SETTINGS');\n        intent.launch();\n      }\n    }\n\n    final Position position = await Geolocator.getCurrentPosition(\n        desiredAccuracy: LocationAccuracy.bestForNavigation);\n    final address = await placemarkFromCoordinates(\n        position.latitude, position.longitude,\n        localeIdentifier: 'id');\n\n    setState(() {\n      _latitude = position.latitude;\n      _longitude = position.longitude;\n      _address =\n          \"${address.first.name}, ${address.first.street},  ${address.first.locality}, ${address.first.subAdministrativeArea}, ${address.first.administrativeArea} ${address.first.postalCode}\";\n    });\n  }\n\n  Widget _showImage() {\n    if (_base64Image == null) {\n      return const ImagePlaceholderWidget(\n        label: 'Ambil Foto',\n        child: Icon(\n          Icons.camera_alt_rounded,\n          color: Colors.grey,\n        ),\n      );\n    }\n\n    final Uint8List bytes = base64Decode(_base64Image);\n    return InkWell(\n      onTap: () {\n        Get.to(() => ImageDetailScreen(\n              bytes: bytes,\n              tag: 'image',\n            ));\n      },\n      child: Hero(\n        tag: 'image',\n        child: ClipRRect(\n          borderRadius: BorderRadius.circular(10.0),\n          child: Image.memory(\n            bytes,\n            width: Get.width,\n            height: 250,\n            fit: BoxFit.cover,\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget _buildQrScanner() {\n    if (_address == null || _base64Image == null) {\n      return const ImagePlaceholderWidget(\n        label: 'Scan Kode Absen',\n        child: Icon(\n          Icons.qr_code_rounded,\n          color: Colors.grey,\n        ),\n      );\n    }\n    return SizedBox(\n        height: 300.0,\n        child: ClipRRect(\n          borderRadius: BorderRadius.circular(20.0),\n          child: QRView(\n            key: qrKey,\n            onQRViewCreated: _onQRViewCreated,\n            overlay: QrScannerOverlayShape(\n              borderRadius: 10,\n              borderLength: 30,\n              borderWidth: 10,\n              cutOutSize: 300,\n            ),\n          ),\n        ));\n  }\n\n  Widget _buildPlaceholderQR() {\n    if (_address == null || _base64Image == null) {\n      return const Text(\n        'Scanner akan aktif setelah lokasi berhasil dideteksi dan anda telah mengambil foto',\n        style: TextStyle(color: Colors.grey),\n      );\n    }\n    return const Text(\n      'Arahkan kamera ke layar komputer',\n      style: TextStyle(color: Colors.grey),\n    );\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    _user = widget.user;\n    _getUserLocation();\n  }\n\n  @override\n  void dispose() {\n    controller?.dispose();\n    super.dispose();\n  }\n\n  Widget _buildLocationSection() {\n    List<Widget> _children;\n    if (_address == null || _address.isEmpty) {\n      _children = <Widget>[\n        sizedBoxH6,\n        Row(\n          children: const <Widget>[\n            Text(\n              'Memuat lokasi..',\n              style: TextStyle(color: Colors.grey),\n            ),\n            sizedBoxW6,\n            SpinKitCircle(\n              color: Colors.blueAccent,\n              size: 18.0,\n            )\n          ],\n        )\n      ];\n    } else {\n      _children = <Widget>[\n        sizedBoxH6,\n        Text(\n          _address,\n          style: const TextStyle(color: Colors.grey),\n        ),\n        sizedBoxH10,\n        SizedBox(\n          width: Get.width,\n          height: 40.0,\n          child: ElevatedButton(\n            onPressed: _getUserLocation,\n            style: ElevatedButton.styleFrom(\n              shape: RoundedRectangleBorder(\n                  borderRadius: BorderRadius.circular(6)),\n              primary: Colors.blueAccent[200],\n              onPrimary: Colors.white,\n            ),\n            child: const Text('Muat Ulang Lokasi'),\n          ),\n        )\n      ];\n    }\n\n    return Column(\n      children: _children,\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      backgroundColor: Colors.white,\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Presensi'),\n      ),\n      body: SingleChildScrollView(\n        child: Padding(\n          padding: const EdgeInsets.all(8.0),\n          child: Column(\n            mainAxisAlignment: MainAxisAlignment.spaceAround,\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              UserInfoCardWidget(\n                department: _user.department,\n                name: _user.name,\n                position: _user.position,\n                status: _user.status,\n                rank: _user?.rank,\n                group: _user?.group,\n                nip: _user?.nip,\n              ),\n              sizedBoxH6,\n              const Text(\n                '*) : Pastikan data pegawai sesuai sebelum melakukan presensi.',\n                style: TextStyle(color: Colors.grey),\n              ),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Scanner'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              _buildPlaceholderQR(),\n              sizedBoxH10,\n              _buildQrScanner(),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Lokasi'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              _buildLocationSection(),\n              sizedBoxH20,\n              Row(\n                children: const <Widget>[\n                  Text('Foto Diri'),\n                  sizedBoxW5,\n                  Text(\n                    '*',\n                    style: TextStyle(color: Colors.red),\n                  ),\n                ],\n              ),\n              const Text(\n                'Ambil foto selfie anda sebagai bukti bahwa anda melakukan presensi di kantor tanpa diwakili orang lain. Pastikan wajah terlihat jelas.',\n                style: TextStyle(color: Colors.grey),\n              ),\n              sizedBoxH10,\n              const Text(\n                '*): Absen akan dibatalkan jika foto tidak sesuai dengan ketentuan. Tekan untuk memperbesar.',\n                style: TextStyle(\n                    fontSize: 10.0,\n                    color: Colors.red,\n                    fontStyle: FontStyle.italic),\n              ),\n              sizedBoxH20,\n              _showImage(),\n              sizedBoxH10,\n              Align(\n                alignment: Alignment.bottomRight,\n                child: SizedBox(\n                  width: Get.width,\n                  height: 40,\n                  child: ElevatedButton(\n                    style: ElevatedButton.styleFrom(\n                      shape: RoundedRectangleBorder(\n                          borderRadius: BorderRadius.circular(6)),\n                      primary: Colors.blueAccent,\n                      onPrimary: Colors.white,\n                    ),\n                    onPressed: _openCamera,\n                    child:\n                        Text(_base64Image == null ? 'Ambil Foto' : 'Ubah Foto'),\n                  ),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/regulation_screen.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:url_launcher/url_launcher.dart';\n\nclass RegulationScreen extends StatelessWidget {\n  final List<Map<String, dynamic>> _regulations = [\n    {\n      'title': 'PP Nomor 11 Tahun 2017',\n      'description': 'Tentang Manajemen PNS',\n      'link':\n          'https://peraturan.bpk.go.id/Home/Download/40728/PP%2011%20TAHUN%202017.pdf'\n    },\n    {\n      'title': 'Permenpan No. 6/2018',\n      'description': 'Tentang Hari Kerja Dan Jam Kerja PNS',\n      'link':\n          'https://peraturan.bpk.go.id/Home/Download/123285/Permenpan%20no%206%20Tahun%202018.pdf',\n    },\n    {\n      'title': 'PP Nomor 53 Tahun 2010',\n      'description': 'Tentang Disiplin PNS',\n      'link':\n          'https://peraturan.bpk.go.id/Home/Download/36030/PP%2053%20Tahun%202010.pdf'\n    },\n    {\n      'title': 'Contoh Surat Izin',\n      'description': 'Beberapa contoh surat izin resmi sebagai referensi',\n      'link': 'https://www.suratresmi.id/contoh-surat-izin-kerja/'\n    },\n    {\n      'title': 'Contoh Surat Izin Sakit',\n      'description': 'Referensi saat membuat surat izin sakit',\n      'link': 'https://www.ashadin.com/2019/05/surat-izin-sakit-formal-pns.html'\n    }\n  ];\n\n  Widget _buildBody() {\n    return SingleChildScrollView(\n      child: Padding(\n        padding: const EdgeInsets.all(8.0),\n        child: Column(\n          children: _regulations\n              .map((regulation) => Container(\n                    margin: const EdgeInsets.only(bottom: 4.0),\n                    child: Card(\n                      elevation: 4.0,\n                      child: ListTile(\n                        onTap: () {\n                          launch(regulation['link'].toString());\n                        },\n                        title: Text(\n                          regulation['title'].toString(),\n                          style: const TextStyle(fontWeight: FontWeight.w600),\n                        ),\n                        subtitle: Text(regulation['description'].toString()),\n                        trailing: IconButton(\n                          icon: const Icon(Icons.chevron_right),\n                          onPressed: () {\n                            launch(regulation['link'].toString());\n                          },\n                        ),\n                        dense: true,\n                      ),\n                    ),\n                  ))\n              .toList(),\n        ),\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: const Text('Daftar Rujukan'),\n        backgroundColor: Colors.blueAccent,\n      ),\n      body: _buildBody(),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/report_screen.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\n\nimport 'package:flare_flutter/flare_actor.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_cupertino_datetime_picker/flutter_cupertino_datetime_picker.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:get/get.dart';\nimport 'package:http/http.dart' as http;\nimport 'package:intl/intl.dart';\nimport 'package:progress_dialog/progress_dialog.dart';\nimport 'package:provider/provider.dart';\nimport 'package:spo_balaesang/models/holiday.dart';\nimport 'package:spo_balaesang/models/presence.dart';\nimport 'package:spo_balaesang/models/report/absent_report.dart';\nimport 'package:spo_balaesang/models/report/daily.dart';\nimport 'package:spo_balaesang/models/user.dart';\nimport 'package:spo_balaesang/repositories/data_repository.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/extensions.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/employee_presence_card_widget.dart';\nimport 'package:spo_balaesang/widgets/statistics_card_widget.dart';\nimport 'package:spo_balaesang/widgets/user_info_card_widget.dart';\nimport 'package:table_calendar/table_calendar.dart';\n\nimport 'bottom_nav_screen.dart';\n\nclass ReportScreen extends StatefulWidget {\n  const ReportScreen({this.user, this.isApproval = false});\n\n  final User user;\n  final bool isApproval;\n\n  @override\n  _ReportScreenState createState() => _ReportScreenState();\n}\n\nclass _ReportScreenState extends State<ReportScreen> {\n  AbsentReport _absentReport;\n  bool _isLoading = false;\n  bool _isApproval;\n  DataRepository _dataRepo;\n  DateTime _year;\n  DateTime _selectedDate = DateTime.now();\n  final Map<DateTime, List<dynamic>> _events = {};\n  List<DailyData> _selectedEvents;\n  List<Holiday> _selectedHolidays;\n  final Map<DateTime, List<dynamic>> _holidays = {};\n  User _user;\n  final TextEditingController _salaryController = TextEditingController();\n  double _salary = 0;\n  final TextEditingController _reasonController = TextEditingController();\n\n  Future<void> _fetchReportData() async {\n    try {\n      setState(() {\n        _isLoading = true;\n      });\n      final Map<String, dynamic> _result =\n          await _dataRepo.getStatistics(_year, _user.id);\n      final AbsentReport absentReport =\n          AbsentReport.fromJson(_result['data'] as Map<String, dynamic>);\n      setState(() {\n        _absentReport = absentReport;\n        _events.addEntries(absentReport.daily\n            .map((daily) => MapEntry(daily.date, daily.attendances)));\n        _holidays.addEntries(absentReport.holidays\n            .map((holiday) => MapEntry(holiday.date, <Holiday>[holiday])));\n        if (_events.entries.last.key.isSameDate(DateTime.now())) {\n          _selectedEvents = _events.entries.last.value as List<DailyData>;\n        } else {\n          _selectedEvents = [];\n        }\n        // _selectedHolidays = _getHolidayForDay(DateTime.now()) as List<Holiday>;\n      });\n    } catch (e) {\n      //\n    } finally {\n      setState(() {\n        _isLoading = false;\n      });\n    }\n  }\n\n  void _onDaySelected(DateTime day, DateTime focusedDay) {\n    final List<dynamic> _eventsData = _getEventsForDay(day);\n    final List<dynamic> _holidaysData = _getHolidayForDay(day);\n    setState(() {\n      if (_eventsData is List<DailyData>) {\n        _selectedEvents = _eventsData;\n      } else {\n        _selectedEvents = [];\n      }\n      if (_holidaysData is List<Holiday>) {\n        _selectedHolidays = _holidaysData;\n      } else {\n        _selectedHolidays = [];\n      }\n      _selectedDate = day;\n    });\n  }\n\n  Color _checkAttendancePercentageColor(double percentage) {\n    if (percentage >= 25 && percentage < 50) {\n      return Colors.yellow[800];\n    }\n\n    if (percentage >= 50 && percentage < 70) {\n      return Colors.indigo;\n    }\n\n    if (percentage >= 70 && percentage <= 80) {\n      return Colors.blueAccent;\n    }\n    if (percentage >= 80 && percentage <= 100) {\n      return Colors.green[600];\n    }\n    return Colors.red[800];\n  }\n\n  Widget _buildUserInfoSection() {\n    return UserInfoCardWidget(\n      name: _user?.name,\n      status: _user?.status,\n      group: _user?.group,\n      rank: _user?.rank,\n      department: _user?.department,\n      nip: _user?.nip,\n      position: _user?.position,\n    );\n  }\n\n  Widget _buildStatisticSection(AbsentReport report, DateTime year) {\n    return StatisticCard(\n      report: report,\n      year: year,\n      status: _user?.status,\n    );\n  }\n\n  Widget _buildSalaryCalculator() {\n    final String date = DateFormat.yMMMM('id_ID').format(_year);\n    String label = 'Kalkulator Gaji : $date';\n    String _prefix = 'Gaji';\n    if (_user?.status == 'PNS') {\n      label = 'Kalkulator Tunjangan : $date';\n      _prefix = 'Tunjangan';\n    }\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: <Widget>[\n        Text(\n          label,\n          style: const TextStyle(\n            fontSize: 16.0,\n            fontWeight: FontWeight.w600,\n          ),\n        ),\n        const Divider(),\n        Card(\n          child: Padding(\n            padding: const EdgeInsets.all(8.0),\n            child: Column(\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: <Widget>[\n                TextFormField(\n                  inputFormatters: [FilteringTextInputFormatter.digitsOnly],\n                  controller: _salaryController,\n                  decoration: InputDecoration(\n                    labelStyle: const TextStyle(color: Colors.blueAccent),\n                    border: const UnderlineInputBorder(\n                        borderSide: BorderSide(color: Colors.blueAccent)),\n                    prefixIcon: const Icon(\n                      Icons.money_rounded,\n                      color: Colors.blueAccent,\n                    ),\n                    labelText: 'Jumlah $_prefix Bulanan',\n                  ),\n                  keyboardType: TextInputType.number,\n                  onChanged: (value) {\n                    if (value.isNotEmpty) {\n                      setState(() {\n                        _salary = double.parse(value);\n                      });\n                    }\n                  },\n                ),\n                sizedBoxH2,\n                const Divider(color: Colors.black26),\n                Row(\n                  mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                  children: <Widget>[\n                    const Text('Kehadiran      : '),\n                    Text(\n                      formatPercentage(\n                          _absentReport.monthly.attendancePercentage),\n                      style: const TextStyle(fontWeight: FontWeight.w600),\n                    )\n                  ],\n                ),\n                sizedBoxH6,\n                Row(\n                  mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                  children: <Widget>[\n                    const Text('Gaji Bulanan  : '),\n                    Text(\n                      formatCurrency(_salary),\n                      style: const TextStyle(fontWeight: FontWeight.w600),\n                    )\n                  ],\n                ),\n                sizedBoxH6,\n                Row(\n                  mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                  children: <Widget>[\n                    const Text('Potongan       : '),\n                    Text(\n                      formatCurrency(_countSalaryCuts()),\n                      style: const TextStyle(fontWeight: FontWeight.w600),\n                    )\n                  ],\n                ),\n                sizedBoxH6,\n                const Divider(color: Colors.black26),\n                sizedBoxH6,\n                Row(\n                  mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                  children: <Widget>[\n                    const Text('Total               : '),\n                    Text(\n                      formatCurrency(_countTotalSalary()),\n                      style: const TextStyle(fontWeight: FontWeight.w600),\n                    )\n                  ],\n                ),\n              ],\n            ),\n          ),\n        ),\n        const Divider(),\n        sizedBoxH10,\n      ],\n    );\n  }\n\n  Widget _buildBody() {\n    if (_isLoading) {\n      return SizedBox(\n        height: Get.height * 0.7,\n        child: const Center(\n          child: SpinKitFadingCircle(\n            size: 45,\n            color: Colors.blueAccent,\n          ),\n        ),\n      );\n    }\n    if (_absentReport == null) {\n      return Center(\n        child: Column(\n          children: <Widget>[\n            SizedBox(\n              width: Get.width * 0.6,\n              height: 200,\n              child: const FlareActor(\n                'assets/flare/failure.flr',\n                animation: 'failure',\n                fit: BoxFit.cover,\n              ),\n            ),\n            const Text('Gagal memuat data'),\n            const SizedBox(height: 20.0),\n            ElevatedButton(\n              style: ElevatedButton.styleFrom(\n                  shape: RoundedRectangleBorder(\n                    borderRadius: BorderRadius.circular(6.0),\n                  ),\n                  primary: Colors.blueAccent,\n                  onPrimary: Colors.white),\n              onPressed: _fetchReportData,\n              child: const Text('Coba Lagi'),\n            )\n          ],\n        ),\n      );\n    }\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: <Widget>[\n        _buildStatisticSection(_absentReport, _year),\n        const Divider(),\n        sizedBoxH10,\n        _buildSalaryCalculator(),\n        const Text(\n          'Kalender Presensi : ',\n          style: TextStyle(\n            fontSize: 16.0,\n            fontWeight: FontWeight.w600,\n          ),\n        ),\n        sizedBoxH10,\n        const Divider(),\n        _buildTableCalendar(),\n        const Divider(),\n        Text(\n          'Presensi ${DateFormat.yMMMMEEEEd('id_ID').format(_selectedDate)} : ',\n          style: const TextStyle(\n            fontSize: 14.0,\n            fontWeight: FontWeight.w600,\n          ),\n        ),\n        sizedBoxH10,\n        AnimatedSwitcher(\n            duration: const Duration(milliseconds: 300),\n            child: _buildEventList())\n      ],\n    );\n  }\n\n  double _countTotalSalary() {\n    return _salary * _absentReport.monthly.attendancePercentage / 100;\n  }\n\n  double _countSalaryCuts() {\n    return _salary - _countTotalSalary();\n  }\n\n  double _countAttendancePercentage(List<DailyData> presences) {\n    double sum = 0;\n    if (presences == null) {\n      return 0;\n    }\n\n    if (presences.isEmpty) {\n      return 0;\n    }\n\n    // ignore: avoid_function_literals_in_foreach_calls\n    presences.forEach((presence) {\n      switch (presence.attendStatus) {\n        case 'Tepat Waktu':\n        case 'Dinas Luar':\n        case 'Cuti Tahunan':\n          sum += 25;\n          break;\n        case 'Cuti Alasan Penting':\n        case 'Cuti Sakit':\n        case 'Cuti Bersalin':\n          sum += 24.375;\n          break;\n        case 'Terlambat':\n          sum += 6.25;\n          break;\n        case 'Izin':\n          sum += 12.5;\n          break;\n        default:\n          sum += 0;\n          break;\n      }\n    });\n\n    return sum;\n  }\n\n  Widget _buildTableCalendar() {\n    return Center(\n      child: Card(\n        elevation: 2.0,\n        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),\n        child: SizedBox(\n          child: TableCalendar(\n            startingDayOfWeek: StartingDayOfWeek.monday,\n            availableCalendarFormats: const <CalendarFormat, String>{\n              CalendarFormat.month: '1 bulan',\n            },\n            calendarFormat: CalendarFormat.month,\n            firstDay: DateTime(2021),\n            lastDay: DateTime(DateTime.now().year + 5),\n            focusedDay: _selectedDate,\n            selectedDayPredicate: (day) {\n              return isSameDay(_selectedDate, day);\n            },\n            headerStyle: const HeaderStyle(titleCentered: true),\n            onDaySelected: _onDaySelected,\n            availableGestures: AvailableGestures.horizontalSwipe,\n            calendarBuilders: CalendarBuilders(\n              defaultBuilder: (_, date, focusedDay) {\n                return holidayBuilder(date,\n                    isNotEmpty: _getHolidayForDay(date).isNotEmpty);\n              },\n              dowBuilder: dowBuilder,\n              markerBuilder: (context, date, events) {\n                if (events.isNotEmpty) {\n                  return Positioned(\n                    bottom: 1,\n                    child: _buildEventsMarker(date, events),\n                  );\n                }\n\n                return const SizedBox();\n              },\n            ),\n            eventLoader: _getEventsForDay,\n          ),\n        ),\n      ),\n    );\n  }\n\n  List<dynamic> _getEventsForDay(DateTime day) {\n    List<dynamic> _event;\n    try {\n      _event = _events.entries\n          .singleWhere((element) => isSameDay(day, element.key))\n          .value;\n    } catch (e) {\n      printError(info: e.toString());\n    }\n    return _event ?? [];\n  }\n\n  List<dynamic> _getHolidayForDay(DateTime day) {\n    List<dynamic> _holiday;\n    try {\n      _holiday = _holidays.entries\n          .firstWhere((element) => isSameDay(day, element.key))\n          .value;\n    } catch (e) {\n      printError(info: e.toString());\n    }\n    return _holiday ?? [];\n  }\n\n  Widget _buildEventsMarker(DateTime date, List events) {\n    return AnimatedContainer(\n      duration: const Duration(milliseconds: 300),\n      width: 32.0,\n      height: 16.0,\n      decoration: BoxDecoration(\n        borderRadius: BorderRadius.circular(6),\n        color: date.isSameDate(_selectedDate)\n            ? _checkAttendancePercentageColor(\n                _countAttendancePercentage(events as List<DailyData>))\n            : date.isToday()\n                ? date.isSameDate(_selectedDate)\n                    ? _checkAttendancePercentageColor(\n                        _countAttendancePercentage(events as List<DailyData>))\n                    : Colors.white\n                : Colors.white,\n      ),\n      child: Center(\n        child: Text(\n          formatPercentage(_countAttendancePercentage(events as List<DailyData>)\n              .toPrecision(0)),\n          style: TextStyle(\n            color: isSameDay(_selectedDate, date)\n                ? Colors.white\n                : _checkAttendancePercentageColor(\n                    _countAttendancePercentage(events as List<DailyData>)),\n            fontSize: 10.0,\n            fontWeight: FontWeight.bold,\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget _buildEventList() {\n    if (_selectedEvents == null) {\n      return Center(\n        child: Column(\n          children: <Widget>[\n            SizedBox(\n              width: Get.width * 0.6,\n              height: 300,\n              child: const FlareActor(\n                'assets/flare/not_found.flr',\n                animation: 'empty',\n              ),\n            ),\n            const Text('Gagal memuat data'),\n            const SizedBox(height: 20.0),\n            ElevatedButton(\n              style: ElevatedButton.styleFrom(\n                  shape: RoundedRectangleBorder(\n                      borderRadius: BorderRadius.circular(6)),\n                  primary: Colors.blueAccent,\n                  onPrimary: Colors.white),\n              onPressed: _fetchReportData,\n              child: const Text('Coba Lagi'),\n            )\n          ],\n        ),\n      );\n    }\n\n    if (_selectedEvents.isEmpty) {\n      if (_selectedHolidays != null && _selectedHolidays.isNotEmpty) {\n        return Column(\n          children: _selectedHolidays\n              .map((Holiday holiday) => Card(\n                    margin: const EdgeInsets.symmetric(vertical: 6.0),\n                    child: ListTile(\n                      title: Text(\n                        'Libur Nasional : ${holiday.name}',\n                        style: const TextStyle(fontWeight: FontWeight.w600),\n                      ),\n                      subtitle: Text(\n                        holiday.description,\n                        maxLines: 3,\n                        overflow: TextOverflow.ellipsis,\n                      ),\n                    ),\n                  ))\n              .toList(),\n        );\n      }\n\n      return Center(\n        child: Column(\n          children: <Widget>[\n            SizedBox(\n              width: Get.width * 0.6,\n              height: 300,\n              child: const FlareActor(\n                'assets/flare/not_found.flr',\n                animation: 'empty',\n              ),\n            ),\n            const Text('Tidak ada presensi'),\n            const SizedBox(height: 20.0),\n          ],\n        ),\n      );\n    }\n\n    return Column(\n      children: _selectedEvents.map((event) {\n        final Color color = checkStatusColor(event.attendStatus);\n        String status = event.attendStatus ?? '';\n        if (event.attendStatus == 'Terlambat') {\n          final duration = calculateLateTime(event.startTime, event.attendTime);\n          status = '${event.attendStatus} $duration';\n        }\n        return EmployeePresenceCardWidget(\n          isApprovalCard: _isApproval,\n          photo: event.photo,\n          heroTag: event.photo,\n          status: status,\n          color: color,\n          address: event.address,\n          attendTime: event.attendTime,\n          point: formatPercentage(checkPresencePercentage(event.attendStatus)),\n          presenceType: event.attendType,\n          buttonWidget: SizedBox(\n            width: Get.width * 0.9,\n            child: ElevatedButton(\n              style: ElevatedButton.styleFrom(\n                shape: RoundedRectangleBorder(\n                    borderRadius: BorderRadius.circular(6)),\n                primary: Colors.red[600],\n                onPrimary: Colors.white,\n              ),\n              onPressed: () {\n                _cancelAttendance(Presence.fromJson(event.toPresenceJson()));\n              },\n              child: const Text('Batalkan'),\n            ),\n          ),\n        );\n      }).toList(),\n    );\n  }\n\n  void _cancelAttendance(Presence presence) {\n    Get.defaultDialog(\n        title: 'Alasan Pembatalan!',\n        content: Flexible(\n          child: Container(\n            padding: const EdgeInsets.all(8),\n            width: Get.width * 0.9,\n            child: TextFormField(\n              controller: _reasonController,\n              decoration: const InputDecoration(\n                  labelText: 'Alasan',\n                  focusColor: Colors.blueAccent,\n                  focusedBorder: UnderlineInputBorder(\n                      borderSide: BorderSide(color: Colors.blueAccent))),\n            ),\n          ),\n        ),\n        confirm: ElevatedButton(\n          style: ElevatedButton.styleFrom(\n            shape:\n                RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),\n            primary: Colors.blueAccent,\n            onPrimary: Colors.white,\n          ),\n          onPressed: () {\n            Get.back();\n            _sendData(presence);\n          },\n          child: const Text('OK'),\n        ));\n  }\n\n  @override\n  void initState() {\n    _year = DateTime.now().year == 2021 ? DateTime.now() : DateTime.utc(2021);\n    _dataRepo = Provider.of<DataRepository>(context, listen: false);\n    super.initState();\n    _user = widget.user;\n    _isApproval = widget.isApproval;\n    _fetchReportData();\n  }\n\n  @override\n  void dispose() {\n    _reasonController.dispose();\n    super.dispose();\n  }\n\n  Future<void> _selectYear(BuildContext context) async {\n    DatePicker.showDatePicker(context,\n        initialDateTime: _year,\n        minDateTime: DateTime(2021),\n        maxDateTime: DateTime(DateTime.now().year + 5), onConfirm: (picked, _) {\n      if (picked != null) {\n        setState(() {\n          _year = picked;\n        });\n        _fetchReportData();\n      }\n    }, locale: DateTimePickerLocale.id, dateFormat: 'MMMM-y');\n  }\n\n  Future<void> _sendData(Presence presence) async {\n    final ProgressDialog pd = ProgressDialog(context, isDismissible: false);\n    pd.show();\n    try {\n      final dataRepo = Provider.of<DataRepository>(context, listen: false);\n      final Map<String, dynamic> data = {\n        'presence_id': presence.id,\n        'reason': _reasonController.value.text\n      };\n      final http.Response response = await dataRepo.cancelAttendance(data);\n      final Map<String, dynamic> _res =\n          jsonDecode(response.body) as Map<String, dynamic>;\n      if (response.statusCode == 200) {\n        pd.hide();\n        showAlertDialog(\"success\", \"Sukses\", _res['message'].toString(),\n            dismissible: false);\n        Timer(\n            const Duration(seconds: 5), () => Get.off(() => BottomNavScreen()));\n      } else {\n        if (pd.isShowing()) pd.hide();\n        showErrorDialog(_res);\n      }\n    } catch (e) {\n      pd.hide();\n      showErrorDialog({\n        'message': 'Kesalahan',\n        'errors': {\n          'exception': ['Terjadi kesalahan!']\n        }\n      });\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        backgroundColor: Colors.blueAccent,\n        title: const Text('Statistik'),\n      ),\n      body: SingleChildScrollView(\n        child: Container(\n          width: Get.width,\n          padding: const EdgeInsets.all(8),\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              _buildUserInfoSection(),\n              dividerT1,\n              const Text(\n                'Pilih Tahun & Bulan : ',\n              ),\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  Text(\n                    DateFormat.yMMMM('id_ID').format(_year),\n                    style: const TextStyle(\n                      fontSize: 22.0,\n                      fontWeight: FontWeight.w600,\n                    ),\n                  ),\n                  IconButton(\n                      icon: Icon(\n                        Icons.calendar_today_rounded,\n                        color: Colors.blueAccent[400],\n                      ),\n                      onPressed: () {\n                        _selectYear(context);\n                      })\n                ],\n              ),\n              dividerT1,\n              _buildBody(),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/screen/splash_screen.dart",
    "content": "import 'dart:async';\n\nimport 'package:android_intent/android_intent.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:geolocator/geolocator.dart';\nimport 'package:get/get.dart';\nimport 'package:location/location.dart' as location;\nimport 'package:permission_handler/permission_handler.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\nimport 'package:spo_balaesang/screen/bottom_nav_screen.dart';\nimport 'package:spo_balaesang/screen/login_screen.dart';\nimport 'package:spo_balaesang/screen/on_boarding_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\n\nclass SplashScreen extends StatefulWidget {\n  @override\n  _SplashScreenState createState() => _SplashScreenState();\n}\n\nclass _SplashScreenState extends State<SplashScreen> {\n  bool _isLoggedIn = false;\n  bool _isFirstSeen;\n\n  Future<void> _askPermission() async {\n    try {\n      final PermissionStatus locationPerms =\n          await Permission.locationWhenInUse.status;\n      if (locationPerms != PermissionStatus.granted) {\n        await Permission.locationWhenInUse.request();\n      }\n\n      final PermissionStatus cameraPerms = await Permission.camera.status;\n      if (cameraPerms != PermissionStatus.granted) {\n        await Permission.camera.request();\n      }\n\n      final PermissionStatus storagePerms = await Permission.storage.status;\n      if (storagePerms != PermissionStatus.granted) {\n        await Permission.storage.request();\n      }\n\n      final PermissionStatus phonePerms = await Permission.phone.status;\n      if (phonePerms != PermissionStatus.granted) {\n        await Permission.phone.request();\n      }\n\n      final PermissionStatus notificationPerms =\n          await Permission.notification.status;\n      if (notificationPerms != PermissionStatus.granted) {\n        await Permission.notification.request();\n      }\n    } catch (e) {\n      debugPrint(e.toString());\n    }\n  }\n\n  Future<void> checkIsFirstSeen() async {\n    final SharedPreferences prefs = await SharedPreferences.getInstance();\n    if (prefs.containsKey(prefsSeenKey)) {\n      setState(() {\n        _isFirstSeen = true;\n      });\n    } else {\n      prefs.setBool(prefsSeenKey, true);\n      setState(() {\n        _isFirstSeen = false;\n      });\n    }\n  }\n\n  Future<void> _checkIfLoggedIn() async {\n    final SharedPreferences prefs = await SharedPreferences.getInstance();\n    final String token = prefs.getString(prefsTokenKey);\n    if (token != null) {\n      setState(() {\n        _isLoggedIn = true;\n      });\n    }\n  }\n\n  void navigationPage() {\n    final Widget _page = _isLoggedIn ? BottomNavScreen() : LoginScreen();\n    if (_isFirstSeen) {\n      Get.off<Widget>(() => _page,\n          transition: Transition.rightToLeftWithFade,\n          curve: Curves.easeIn,\n          duration: const Duration(milliseconds: 500));\n    } else {\n      Get.off<Widget>(() => OnBoardingScreen(page: _page));\n    }\n  }\n\n  Future<Timer> _loadWidget() async {\n    const Duration _duration = Duration(seconds: 5);\n    return Timer(_duration, navigationPage);\n  }\n\n  Future<void> _checkGps() async {\n    if (!(await Geolocator.isLocationServiceEnabled())) {\n      Get.defaultDialog(\n        title: 'Perhatian',\n        content: const Center(\n          child: Text(\n            'Tidak dapat mendeteksi lokasi saat ini! Pastikan GPS sudah aktif dan coba lagi!',\n            textAlign: TextAlign.center,\n          ),\n        ),\n        actions: <Widget>[\n          TextButton(\n            onPressed: () async {\n              final _serviceEnabled =\n                  await location.Location().requestService();\n              if (!_serviceEnabled) {\n                const AndroidIntent intent = AndroidIntent(\n                    action: 'android.settings.LOCATION_SOURCE_SETTINGS');\n                intent.launch();\n              }\n              Get.back();\n            },\n            child: const Text('OK',\n                style: TextStyle(\n                  color: Colors.blueAccent,\n                )),\n          ),\n          TextButton(\n            onPressed: () {\n              Get.back();\n            },\n            child: const Text('TIDAK',\n                style: TextStyle(\n                  color: Colors.blueAccent,\n                )),\n          ),\n        ],\n      );\n    }\n  }\n\n  @override\n  void initState() {\n    _checkIfLoggedIn();\n    checkIsFirstSeen();\n    super.initState();\n    _askPermission();\n    _checkGps();\n    _loadWidget();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      height: Get.height,\n      width: Get.width,\n      decoration: const BoxDecoration(\n          gradient: LinearGradient(colors: <Color>[\n        Color(0xFF6B8EEF),\n        Color(0xFF0C2979),\n      ], begin: Alignment.topCenter, end: Alignment.bottomCenter)),\n      child: Scaffold(\n        backgroundColor: Colors.transparent,\n        body: Stack(\n          fit: StackFit.expand,\n          children: <Widget>[\n            Align(\n              child: Column(\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: <Widget>[\n                  Image.asset(\n                    'assets/launcher/icon.png',\n                    width: Get.width * 0.3,\n                  ),\n                  sizedBoxH4,\n                  const Text(\n                    'SiAP Balaesang',\n                    style: TextStyle(\n                        color: Colors.white, fontWeight: FontWeight.w700),\n                  )\n                ],\n              ),\n            ),\n            Align(\n              alignment: Alignment.bottomCenter,\n              child: Column(\n                mainAxisAlignment: MainAxisAlignment.end,\n                children: <Widget>[\n                  const SpinKitFadingCircle(\n                    color: Colors.white,\n                    size: 35.0,\n                  ),\n                  const SizedBox(height: 10.0),\n                  Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceAround,\n                    children: const <Widget>[\n                      Spacer(),\n                      Text('SiAP Balaesang',\n                          style: TextStyle(\n                              color: Colors.white,\n                              fontWeight: FontWeight.w700)),\n                      SizedBox(width: 2.0),\n                      Text('v5.0.3', style: TextStyle(color: Colors.white)),\n                      Spacer()\n                    ],\n                  ),\n                  const SizedBox(height: 10.0),\n                  Container(\n                    width: Get.width * 0.6,\n                    decoration: BoxDecoration(\n                      color: Colors.white,\n                      borderRadius: BorderRadius.circular(6),\n                    ),\n                    child: IntrinsicHeight(\n                      child: Row(\n                        mainAxisAlignment: MainAxisAlignment.center,\n                        children: <Widget>[\n                          const Spacer(),\n                          Image.asset(\n                            'assets/logo/banuacoders.png',\n                            width: Get.width * 0.25,\n                            fit: BoxFit.fitHeight,\n                          ),\n                          const Spacer(),\n                          verticalDiv,\n                          const Spacer(),\n                          Image.asset(\n                            'assets/logo/balaesang.png',\n                            width: Get.width * 0.25,\n                            fit: BoxFit.scaleDown,\n                          ),\n                          const Spacer(),\n                        ],\n                      ),\n                    ),\n                  ),\n                  const SizedBox(height: 30.0),\n                ],\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/utils/app_const.dart",
    "content": "// Shared Preferences Keys\n\nimport 'package:flutter/material.dart';\n\nconst String prefsTokenKey = 'token';\nconst String prefsUserKey = 'user';\nconst String prefsEmployeeKey = 'employee';\nconst String prefsAlarmKey = 'alarm';\nconst String prefsSeenKey = 'seen';\n\n// General\nconst String jsonDataField = 'data';\nconst String approvalStatusField = 'approval_status';\n\n// User/Employee\nconst String userIdField = 'id';\nconst String userNipField = 'nip';\nconst String userNameField = 'name';\nconst String userPhoneField = 'phone';\nconst String userGenderField = 'gender';\nconst String userDepartmentField = 'department';\nconst String userStatusField = 'status';\nconst String userPositionField = 'position';\nconst String userUnreadNotificationsCountField = 'unread_notifications';\nconst String userIsHolidayField = 'holiday';\nconst String userIsWeekendField = 'is_weekend';\nconst String userTokenField = 'token';\nconst String userNextPresenceField = 'next_presence';\nconst String userPresencesField = 'presence';\nconst String userRankField = 'rank';\nconst String userGroupField = 'group';\n\n// Presence\nconst String presenceDateField = 'date';\nconst String presenceCodeTypeField = 'code_type';\nconst String presenceStatusField = 'status';\nconst String presenceAttendTimeField = 'attend_time';\nconst String presenceLocationField = 'location';\nconst String presencePhotoField = 'photo';\nconst String presenceStartTimeField = 'start_time';\nconst String presenceEndTimeField = 'end_time';\n\n// Outstation/Permission\nconst String outstationIdField = 'id';\nconst String outstationTitleField = 'title';\nconst String outstationDescriptionField = 'description';\nconst String outstationIsApprovedField = 'is_approved';\nconst String outstationPhotoField = 'photo';\nconst String outstationDueDateField = 'due_date';\nconst String outstationStartDateField = 'start_date';\nconst String outstationUserField = 'user';\n\n// Paid Leave\nconst String paidLeaveIdField = 'id';\nconst String paidLeaveTitleField = 'title';\nconst String paidLeaveCategoryField = 'category';\nconst String paidLeaveDescriptionField = 'description';\nconst String paidLeaveIsApprovedField = 'is_approved';\nconst String paidLeavePhotoField = 'photo';\nconst String paidLeaveDueDateField = 'due_date';\nconst String paidLeaveStartDateField = 'start_date';\nconst String paidLeaveUserField = 'user';\n\n// Absent Permission\nconst String absentPermissionIdField = 'id';\nconst String absentPermissionTitleField = 'title';\nconst String absentPermissionDescriptionField = 'description';\nconst String absentPermissionIsApprovedField = 'is_approved';\nconst String absentPermissionPhotoField = 'photo';\nconst String absentPermissionDueDateField = 'due_date';\nconst String absentPermissionStartDateField = 'start_date';\nconst String absentPermissionUserField = 'user';\n\n// Notification\nconst String notificationIdField = 'id';\nconst String notificationNotifiableIdField = 'notifiable_id';\nconst String notificationNotifiableTypeField = 'notifiable_type';\nconst String notificationIsReadField = 'is_read';\n\n// Location\nconst String locationLatitudeField = 'latitude';\nconst String locationLongitudeField = 'longitude';\nconst String locationAddressField = 'address';\n\n// Absent Report\nconst String reportAttendancePercentageFieldField = 'attendance_percentage';\nconst String reportDayField = 'day';\nconst String reportLimitField = 'limit';\nconst String reportLateCountField = 'late_count';\nconst String reportLeaveEarlyFieldCountField = 'leave_early_count';\nconst String reportNotMorningParadeCountField = 'not_morning_parade_count';\nconst String reportEarlyLunchBreakCountField = 'early_lunch_break_count';\nconst String reportNotComeAfterLunchBreakCountField =\n    'not_come_after_lunch_break_count';\nconst String reportTotalWorkDayField = 'total_work_day';\nconst String reportAnnualLeaveField = 'annual_leave';\nconst String reportImportantReasonLeaveField = 'important_reason_leave';\nconst String reportSickLeaveField = 'sick_leave';\nconst String reportMaternityLeaveField = 'maternity_leave';\nconst String reportOutOfLiabilityLeaveField = 'out_of_liability_leave';\nconst String absentReportPercentageField = 'percentage';\nconst String absentReportYearlyField = 'yearly';\nconst String absentReportMonthlyField = 'monthly';\nconst String absentReportDailyField = 'daily';\nconst String absentReportHolidaysField = 'holidays';\n\n// DailyData (Report)\nconst String dailyDateField = 'date';\nconst String dailyPresencesField = 'attendances';\nconst String dailyDataAttendTimeField = 'attend_time';\nconst String dailyDataAttendTypeField = 'absent_type';\nconst String dailyDataAttendStatusField = 'attend_status';\n\n// Yearly\nconst String yearlyAbsentField = 'absent';\nconst String yearlyAbsentPermissionField = 'absent_permission';\nconst String yearlyOutstationField = 'outstation';\n\n// Holiday\nconst String holidayDateField = 'date';\nconst String holidayNameField = 'name';\nconst String holidayDescriptionField = 'description';\n\nconst Map<String, dynamic> paidLeaveCategories = {\n  'Cuti Tahunan (97.5%)': 1,\n  'Cuti Alasan Penting (97.5%)': 2,\n  'Cuti Bersalin (97.5%)': 3,\n  'Cuti Sakit (97.5%)': 4,\n  'Cuti Diluar Tanggungan (0%)': 5\n};\n\nconst sizedBox = SizedBox();\nconst sizedBoxW2 = SizedBox(width: 2);\nconst sizedBoxW4 = SizedBox(width: 4);\nconst sizedBoxW5 = SizedBox(width: 5);\nconst sizedBoxW6 = SizedBox(width: 6);\nconst sizedBoxW8 = SizedBox(width: 8);\nconst sizedBoxW10 = SizedBox(width: 10);\nconst sizedBoxW12 = SizedBox(width: 12);\nconst sizedBoxW16 = SizedBox(width: 16);\nconst sizedBoxW20 = SizedBox(width: 20);\nconst sizedBoxW30 = SizedBox(width: 30);\nconst sizedBoxH2 = SizedBox(height: 2);\nconst sizedBoxH4 = SizedBox(height: 4);\nconst sizedBoxH5 = SizedBox(height: 5);\nconst sizedBoxH6 = SizedBox(height: 6);\nconst sizedBoxH8 = SizedBox(height: 8);\nconst sizedBoxH10 = SizedBox(height: 10);\nconst sizedBoxH12 = SizedBox(height: 12);\nconst sizedBoxH16 = SizedBox(height: 16);\nconst sizedBoxH20 = SizedBox(height: 20);\nconst sizedBoxH30 = SizedBox(height: 30);\n\nconst dividerT1 = Divider(thickness: 1);\nconst dividerT2 = Divider(thickness: 2);\nconst verticalDiv = VerticalDivider(\n  thickness: 1,\n  color: Colors.black,\n);\n"
  },
  {
    "path": "lib/utils/extensions.dart",
    "content": "extension DateOnlyCompare on DateTime {\n  bool isSameDate(DateTime other) {\n    return year == other.year && month == other.month && day == other.day;\n  }\n\n  bool isToday() {\n    final now = DateTime.now();\n    final diff = now.difference(this).inDays;\n    return diff == 0 && now.day == day;\n  }\n\n  bool isWeekend() {\n    return weekday == 6 || weekday == 7;\n  }\n\n  bool isFinished() {\n    return isBefore(DateTime.now());\n  }\n\n  bool isOnGoing() {\n    return isAfter(DateTime.now());\n  }\n}\n"
  },
  {
    "path": "lib/utils/file_util.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_image_compress/flutter_image_compress.dart';\n\nFuture<File> compressAndGetFile(File file, String targetPath) async {\n  final result = await FlutterImageCompress.compressAndGetFile(\n    file.absolute.path,\n    targetPath,\n    quality: 60,\n  );\n  return result;\n}\n"
  },
  {
    "path": "lib/utils/view_util.dart",
    "content": "import 'package:flare_flutter/flare_actor.dart';\nimport 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'package:intl/intl.dart';\nimport 'package:introduction_screen/introduction_screen.dart';\n\nimport 'extensions.dart';\n\nFuture showAlertDialog(String type, String title, String content,\n    {bool dismissible}) async {\n  final List<Widget> actions = type == 'success'\n      ? []\n      : [\n          TextButton(\n            onPressed: () {\n              Get.back();\n            },\n            child: const Text(\n              'OK',\n              style: TextStyle(\n                color: Colors.blueAccent,\n              ),\n            ),\n          )\n        ];\n  final color = type == 'success' ? Colors.green : Colors.red;\n  final icon = type == 'success' ? Icons.check_circle : Icons.dangerous;\n  return Get.defaultDialog(\n      title: title,\n      content: Column(\n        children: <Widget>[\n          Icon(\n            icon,\n            color: color,\n            size: 72,\n          ),\n          const SizedBox(height: 10.0),\n          Center(\n              child: Text(\n            content,\n            textAlign: TextAlign.center,\n          )),\n        ],\n      ),\n      actions: actions,\n      barrierDismissible: dismissible);\n}\n\nFuture showErrorDialog(Map<String, dynamic> json) {\n  return Get.defaultDialog(\n    title: 'Gagal',\n    content: Column(\n      children: <Widget>[\n        const Icon(\n          Icons.dangerous,\n          color: Colors.red,\n          size: 72.0,\n        ),\n        const SizedBox(height: 10.0),\n        Column(\n          mainAxisSize: MainAxisSize.min,\n          children: <Widget>[\n            Text(\n              json['message'].toString(),\n              textAlign: TextAlign.center,\n            ),\n            const SizedBox(height: 10.0),\n            Column(\n                children: (json['errors'] as Map<String, dynamic>)\n                    .entries\n                    .map((e) => Text(e.value[0].toString()))\n                    .toList()),\n          ],\n        ),\n      ],\n    ),\n    cancel: TextButton(\n      onPressed: () {\n        Get.back();\n      },\n      child: const Text(\n        'OK',\n        style: TextStyle(\n          color: Colors.blueAccent,\n        ),\n      ),\n    ),\n  );\n}\n\nColor percentageLabelColor(double percentage) {\n  if (percentage < 50) {\n    return Colors.black87;\n  }\n  return Colors.white;\n}\n\nColor checkStatusColor(String status) {\n  switch (status) {\n    case 'Tidak Hadir':\n      return Colors.red[800];\n    case 'Tepat Waktu':\n      return Colors.green;\n    case 'Terlambat':\n      return Colors.orange;\n    case 'Dinas Luar':\n    case 'Izin':\n      return Colors.blueAccent;\n    case 'Cuti Tahunan':\n    case 'Cuti Bersalin':\n    case 'Cuti Sakit':\n    case 'Cuti Alasan Penting':\n      return Colors.pink;\n    default:\n      return Colors.red[800];\n  }\n}\n\ndouble checkPresencePercentage(String status) {\n  switch (status) {\n    case 'Tepat Waktu':\n    case 'Dinas Luar':\n    case 'Cuti Tahunan':\n      return 100;\n    case 'Cuti Bersalin':\n    case 'Cuti Sakit':\n    case 'Cuti Alasan Penting':\n      return 97.5;\n    case 'Terlambat':\n      return 25;\n      break;\n    case 'Izin':\n      return 50;\n    default:\n      return 0;\n  }\n}\n\nList<PageViewModel> onBoardingScreens = [\n  PageViewModel(\n    title: \"QR Code\",\n    body: \"Lakukan presensi cukup dengan scan QR Code\",\n    image: const Center(\n        child: FlareActor(\n      'assets/flare/qrcode.flr',\n      animation: 'scan',\n    )),\n    decoration: const PageDecoration(\n        titleTextStyle: TextStyle(\n            fontSize: 20.0,\n            fontWeight: FontWeight.w700,\n            color: Colors.blueAccent),\n        bodyTextStyle:\n            TextStyle(fontSize: 12.0, fontWeight: FontWeight.normal)),\n  ),\n  PageViewModel(\n    title: \"Izin, Cuti, & Dinas Luar\",\n    body: \"Ajukan Izin, Cuti, dan Dinas Luar melalui aplikasi.\",\n    image: const Center(\n        child: FlareActor(\n      'assets/flare/documents.flr',\n      animation: 'document',\n    )),\n    decoration: const PageDecoration(\n        titleTextStyle: TextStyle(\n            fontSize: 20.0,\n            fontWeight: FontWeight.w700,\n            color: Colors.blueAccent),\n        bodyTextStyle:\n            TextStyle(fontSize: 12.0, fontWeight: FontWeight.normal)),\n  )\n];\n\nString calculateLateTime(DateTime startTime, String attendTime) {\n  const dur = Duration(minutes: 30);\n  final attendDate =\n      '${startTime.year.toString()}-${startTime.month.toString().padLeft(2, '0')}-${startTime.day.toString().padLeft(2, '0')}';\n  final diff =\n      DateTime.parse('$attendDate $attendTime').difference(startTime.add(dur));\n  var duration = diff.inMinutes;\n\n  if (duration == 0) {\n    duration = diff.inSeconds;\n    return '$duration detik';\n  }\n\n  if (duration > 59) {\n    duration = diff.inHours;\n    return '$duration jam';\n  }\n\n  return '$duration menit';\n}\n\nfinal TextStyle labelTextStyle = TextStyle(color: Colors.grey[600]);\n\nString formatPercentage(double percentage) {\n  return '${NumberFormat.decimalPattern('id_ID').format(percentage)}%';\n}\n\nString formatCurrency(double salary) {\n  return NumberFormat.currency(locale: 'id_ID', symbol: 'Rp. ').format(salary);\n}\n\nString trimPhoneNumber(String phoneNumber) {\n  final phone = phoneNumber.replaceAll(' ', '');\n  return '62${phone.substring(1, phone.length)}';\n}\n\nWidget dowBuilder(BuildContext context, DateTime date) {\n  TextStyle _style = const TextStyle(color: Colors.black);\n  if (date.isWeekend()) {\n    _style = const TextStyle(color: Colors.red);\n  }\n  final text = DateFormat.E().format(date);\n  return Center(\n    child: Text(\n      text,\n      style: _style,\n    ),\n  );\n}\n\nWidget holidayBuilder(DateTime date, {bool isNotEmpty}) {\n  TextStyle _style = const TextStyle(color: Colors.black);\n  if (isNotEmpty || date.isWeekend()) {\n    _style = const TextStyle(color: Colors.red);\n  }\n  final text = DateFormat.d().format(date);\n  return Center(\n    child: Text(\n      text,\n      style: _style,\n    ),\n  );\n}\n"
  },
  {
    "path": "lib/widgets/employee_presence_card_widget.dart",
    "content": "import 'package:auto_size_text/auto_size_text.dart';\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:get/get.dart';\nimport 'package:spo_balaesang/screen/image_detail_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/image_placeholder_widget.dart';\n\nclass EmployeePresenceCardWidget extends StatelessWidget {\n  const EmployeePresenceCardWidget(\n      {this.presenceType,\n      this.attendTime,\n      this.status,\n      this.address,\n      this.photo,\n      this.heroTag,\n      this.isApprovalCard = false,\n      this.buttonWidget,\n      this.point,\n      this.color});\n\n  final String presenceType;\n  final String point;\n  final String attendTime;\n  final String status;\n  final String address;\n  final String photo;\n  final String heroTag;\n  final bool isApprovalCard;\n  final Widget buttonWidget;\n  final Color color;\n\n  Widget _buildCancelButton(String status) {\n    if ((status != 'Tepat Waktu' && !status.contains('Terlambat')) ||\n        !isApprovalCard) {\n      return const SizedBox();\n    }\n    return Column(\n      children: <Widget>[const Divider(), buttonWidget],\n    );\n  }\n\n  Widget _showImage(String photo) {\n    if (photo.isEmpty) {\n      return const ImagePlaceholderWidget(\n        label: 'Tidak ada foto!',\n        child: Icon(\n          Icons.image_not_supported_rounded,\n          color: Colors.grey,\n        ),\n      );\n    }\n\n    return InkWell(\n      onTap: () {\n        Get.to(() => ImageDetailScreen(\n              tag: heroTag,\n              imageUrl: photo,\n            ));\n      },\n      child: Hero(\n        tag: heroTag,\n        child: ClipRRect(\n          borderRadius: BorderRadius.circular(10.0),\n          child: CachedNetworkImage(\n            placeholder: (_, __) => const ImagePlaceholderWidget(\n              label: 'Memuat Foto',\n              child: SpinKitFadingCircle(\n                size: 25.0,\n                color: Colors.blueAccent,\n              ),\n            ),\n            imageUrl: photo,\n            fit: BoxFit.cover,\n            errorWidget: (_, __, ___) => const ImagePlaceholderWidget(\n              label: 'Gagal memuat foto',\n              child: Icon(\n                Icons.image_not_supported_rounded,\n                color: Colors.grey,\n              ),\n            ),\n            width: Get.width,\n            height: 250.0,\n          ),\n        ),\n      ),\n    );\n  }\n\n  String checkAddressLabel() {\n    if (int.parse(point.substring(0, point.length - 1)) == 0 &&\n        address.isNotEmpty) {\n      return 'Alasan Pembatalan';\n    }\n\n    return 'Lokasi';\n  }\n\n  IconData checkAddressIcon() {\n    if (int.parse(point.substring(0, point.length - 1)) == 0 &&\n        address.isNotEmpty) {\n      return Icons.notes_rounded;\n    }\n\n    return Icons.location_on_rounded;\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      margin: const EdgeInsets.only(top: 16.0),\n      width: Get.width,\n      child: Card(\n        elevation: 2.0,\n        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),\n        child: Padding(\n          padding: const EdgeInsets.all(8.0),\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              Text(\n                presenceType,\n                style: const TextStyle(fontWeight: FontWeight.w600),\n              ),\n              const Divider(thickness: 1.0),\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  Text(\n                    'Jam Absen',\n                    style: labelTextStyle,\n                  ),\n                  Text(\n                    attendTime.isEmpty ? '-' : attendTime,\n                  )\n                ],\n              ),\n              sizedBoxH4,\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  Text(\n                    'Poin Kehadiran',\n                    style: labelTextStyle,\n                  ),\n                  Text(\n                    point,\n                    style: TextStyle(\n                      color: color,\n                    ),\n                  )\n                ],\n              ),\n              sizedBoxH4,\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  Text(\n                    'Status Kehadiran',\n                    style: labelTextStyle,\n                  ),\n                  Text(\n                    status,\n                    style: TextStyle(\n                      color: color,\n                    ),\n                  )\n                ],\n              ),\n              sizedBoxH4,\n              dividerT1,\n              Row(\n                children: <Widget>[\n                  Icon(\n                    checkAddressIcon(),\n                    color: Colors.grey[600],\n                    size: 20.0,\n                  ),\n                  sizedBoxW4,\n                  Text(checkAddressLabel(), style: labelTextStyle)\n                ],\n              ),\n              sizedBoxH4,\n              AutoSizeText(\n                address.isEmpty ? '-' : address,\n                maxLines: 3,\n                minFontSize: 10.0,\n                maxFontSize: 12.0,\n                textAlign: TextAlign.justify,\n                overflow: TextOverflow.ellipsis,\n              ),\n              dividerT1,\n              Row(\n                children: <Widget>[\n                  Icon(\n                    Icons.photo,\n                    color: Colors.grey[600],\n                    size: 20.0,\n                  ),\n                  sizedBoxW4,\n                  Text('Foto Wajah', style: labelTextStyle)\n                ],\n              ),\n              sizedBoxH4,\n              _showImage(photo),\n              const SizedBox(height: 8.0),\n              _buildCancelButton(status)\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/employee_proposal_info_widget.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:intl/intl.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\n\nclass EmployeeProposalInfoWidget extends StatelessWidget {\n  const EmployeeProposalInfoWidget(\n      {this.title, this.label, this.startDate, this.dueDate});\n\n  final String label;\n  final DateTime startDate;\n  final DateTime dueDate;\n  final String title;\n\n  @override\n  Widget build(BuildContext context) {\n    return Card(\n      elevation: 4.0,\n      child: Padding(\n        padding: const EdgeInsets.all(8.0),\n        child: Column(\n          children: <Widget>[\n            Text(\n              'Informasi $label',\n              style: labelTextStyle.copyWith(\n                fontWeight: FontWeight.w600,\n              ),\n            ),\n            dividerT1,\n            Row(\n              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n              children: <Widget>[\n                Text('Judul ', style: labelTextStyle),\n                sizedBoxW5,\n                Text(title),\n              ],\n            ),\n            sizedBoxH5,\n            Row(\n              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n              children: <Widget>[\n                Text('Tanggal Mulai ', style: labelTextStyle),\n                sizedBoxW5,\n                Text(DateFormat.yMMMMEEEEd('id_ID').format(startDate)),\n              ],\n            ),\n            sizedBoxH5,\n            Row(\n              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n              children: <Widget>[\n                Text('Tanggal Selesai ', style: labelTextStyle),\n                sizedBoxW5,\n                Text(DateFormat.yMMMMEEEEd('id_ID').format(dueDate)),\n              ],\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/employee_proposal_widget.dart",
    "content": "import 'package:auto_size_text/auto_size_text.dart';\nimport 'package:cached_network_image/cached_network_image.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\nimport 'package:get/get.dart';\nimport 'package:spo_balaesang/screen/image_detail_screen.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\nimport 'package:spo_balaesang/widgets/image_placeholder_widget.dart';\n\nclass EmployeeProposalWidget extends StatelessWidget {\n  const EmployeeProposalWidget(\n      {this.title,\n      this.isApproved,\n      this.employeeName,\n      this.approvalStatus,\n      this.isApprovalCard = false,\n      this.isPaidLeave = false,\n      this.startDate,\n      this.dueDate,\n      this.description,\n      this.category,\n      this.photo,\n      this.updateWidget,\n      this.heroTag,\n      this.button});\n\n  final String title;\n  final bool isApproved;\n  final String employeeName;\n  final String approvalStatus;\n  final bool isApprovalCard;\n  final bool isPaidLeave;\n  final DateTime startDate;\n  final DateTime dueDate;\n  final String description;\n  final String category;\n  final String photo;\n  final String heroTag;\n  final Widget button;\n  final Widget updateWidget;\n\n  Color _checkStatusColor(String status) {\n    switch (status) {\n      case 'Disetujui':\n        return Colors.green;\n      case 'Menunggu Persetujuan':\n        return Colors.deepOrange;\n      default:\n        return Colors.red[800];\n    }\n  }\n\n  Widget _buildEmployeeNameSection() {\n    if (isApprovalCard) {\n      return Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: <Widget>[\n          sizedBoxH4,\n          Row(\n            children: <Widget>[\n              Text(\n                'Diajukan oleh : ',\n                style: labelTextStyle.copyWith(fontSize: 12.0),\n              ),\n              Text(\n                employeeName,\n                style: const TextStyle(\n                  fontSize: 12.0,\n                  fontWeight: FontWeight.w600,\n                ),\n              ),\n            ],\n          ),\n          sizedBoxH5,\n        ],\n      );\n    }\n    return sizedBoxH5;\n  }\n\n  Widget _buildCategorySection() {\n    if (isPaidLeave) {\n      return Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: <Widget>[\n          sizedBoxH4,\n          Row(\n            children: <Widget>[\n              Text(\n                'Kategori          : ',\n                style: labelTextStyle.copyWith(fontSize: 12.0),\n              ),\n              Text(\n                category,\n                style: const TextStyle(fontSize: 12.0),\n              ),\n            ],\n          ),\n        ],\n      );\n    }\n    return sizedBox;\n  }\n\n  Widget _buildButtonSection() {\n    if (isApprovalCard) {\n      return Column(\n        children: [\n          dividerT1,\n          button,\n        ],\n      );\n    }\n    return sizedBox;\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      margin: const EdgeInsets.only(bottom: 16.0),\n      child: Card(\n        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),\n        elevation: 4.0,\n        child: Padding(\n            padding: const EdgeInsets.all(8.0),\n            child: Column(\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: <Widget>[\n                Text(\n                  title,\n                  style: const TextStyle(\n                      fontWeight: FontWeight.w600, fontSize: 16.0),\n                ),\n                sizedBoxH5,\n                dividerT1,\n                sizedBoxH5,\n                Row(\n                  children: <Widget>[\n                    Text(\n                      'Status              : ',\n                      style: labelTextStyle.copyWith(fontSize: 12.0),\n                    ),\n                    Text(\n                      approvalStatus,\n                      style: TextStyle(\n                          fontSize: 12.0,\n                          color: _checkStatusColor(approvalStatus)),\n                    ),\n                  ],\n                ),\n                _buildCategorySection(),\n                _buildEmployeeNameSection(),\n                dividerT1,\n                sizedBoxH5,\n                const Text(\n                  'Masa Berlaku : ',\n                  style: TextStyle(fontSize: 12.0, color: Colors.grey),\n                ),\n                sizedBoxH5,\n                Row(\n                  children: <Widget>[\n                    const Icon(\n                      Icons.calendar_today_rounded,\n                      size: 16.0,\n                    ),\n                    sizedBoxW5,\n                    Text(\n                      '${startDate.day}/${startDate.month}/${startDate.year} - ${dueDate.day}/${dueDate.month}/${dueDate.year}',\n                      style: const TextStyle(fontSize: 12.0),\n                    ),\n                  ],\n                ),\n                sizedBoxH10,\n                const Text(\n                  'Deskripsi : ',\n                  style: TextStyle(fontSize: 12.0, color: Colors.grey),\n                ),\n                AutoSizeText(\n                  description,\n                  maxFontSize: 12.0,\n                  minFontSize: 10.0,\n                  maxLines: 4,\n                  overflow: TextOverflow.ellipsis,\n                ),\n                sizedBoxH10,\n                const Text(\n                  'Lampiran : ',\n                  style: TextStyle(fontSize: 12.0, color: Colors.grey),\n                ),\n                const Text(\n                  '*tekan untuk memperbesar',\n                  style: TextStyle(\n                      fontSize: 12.0,\n                      color: Colors.black87,\n                      fontStyle: FontStyle.italic),\n                ),\n                sizedBoxH5,\n                InkWell(\n                  onLongPress: () {\n                    if (updateWidget != null) {\n                      Get.to(() => updateWidget);\n                    }\n                  },\n                  onTap: () {\n                    Get.to(() => ImageDetailScreen(\n                          imageUrl: photo,\n                          tag: heroTag,\n                        ));\n                  },\n                  child: Hero(\n                    tag: heroTag,\n                    child: ClipRRect(\n                      borderRadius: BorderRadius.circular(10.0),\n                      child: CachedNetworkImage(\n                        placeholder: (_, __) => const ImagePlaceholderWidget(\n                          label: 'Memuat Foto',\n                          child: SpinKitFadingCircle(\n                            size: 25.0,\n                            color: Colors.blueAccent,\n                          ),\n                        ),\n                        imageUrl: photo,\n                        fit: BoxFit.cover,\n                        errorWidget: (_, __, ___) =>\n                            const ImagePlaceholderWidget(\n                          label: 'Gagal memuat foto!',\n                          child: Icon(\n                            Icons.image_not_supported_rounded,\n                            color: Colors.grey,\n                          ),\n                        ),\n                        width: Get.width,\n                        height: 250.0,\n                      ),\n                    ),\n                  ),\n                ),\n                _buildButtonSection()\n              ],\n            )),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/image_placeholder_widget.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\n\nclass ImagePlaceholderWidget extends StatelessWidget {\n  const ImagePlaceholderWidget({this.label, this.child});\n\n  final String label;\n  final Widget child;\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n        width: Get.width,\n        height: 250,\n        decoration: BoxDecoration(\n          color: Colors.grey[100],\n          borderRadius: BorderRadius.circular(20.0),\n          border: Border.all(color: Colors.grey[400]),\n        ),\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            child,\n            sizedBoxH5,\n            Text(\n              label,\n              style: const TextStyle(color: Colors.grey),\n            )\n          ],\n        ));\n  }\n}\n"
  },
  {
    "path": "lib/widgets/next_presence_empty_card_widget.dart",
    "content": "import 'package:auto_size_text/auto_size_text.dart';\nimport 'package:flutter/material.dart';\nimport 'package:intl/intl.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\n\nclass NextPresenceEmptyCardWidget extends StatelessWidget {\n  const NextPresenceEmptyCardWidget(\n      {this.topLabel,\n      this.trailingLabel,\n      this.firstLabel,\n      this.firstContent,\n      this.secondLabel,\n      this.secondContent,\n      this.thirdLabel,\n      this.thirdContent,\n      this.fourthLabel,\n      this.fourthContent,\n      this.color,\n      this.trailingTop});\n\n  final String topLabel;\n  final String trailingLabel;\n  final String firstLabel;\n  final String firstContent;\n  final String secondLabel;\n  final String secondContent;\n  final String thirdLabel;\n  final String thirdContent;\n  final String fourthLabel;\n  final String fourthContent;\n  final Color color;\n  final Widget trailingTop;\n\n  @override\n  Widget build(BuildContext context) {\n    return Card(\n      elevation: 4.0,\n      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),\n      child: Padding(\n        padding: const EdgeInsets.all(8.0),\n        child: Center(\n          child: Column(\n            children: <Widget>[\n              Row(\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: <Widget>[\n                  Text(\n                    DateFormat.EEEE().format(DateTime.now()),\n                    style: const TextStyle(fontWeight: FontWeight.w600),\n                  ),\n                  sizedBoxW5,\n                  const Text('|'),\n                  sizedBoxW5,\n                  Text(\n                    DateFormat.yMMMd().format(DateTime.now()),\n                  ),\n                  sizedBoxW5,\n                  const Text('|'),\n                  sizedBoxW5,\n                  Text(topLabel),\n                ],\n              ),\n              const Divider(\n                thickness: 1.0,\n                color: Colors.black26,\n              ),\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  Column(\n                    crossAxisAlignment: CrossAxisAlignment.start,\n                    children: <Widget>[\n                      Text(\n                        '$firstLabel :',\n                        style: const TextStyle(fontWeight: FontWeight.bold),\n                      ),\n                      Text(firstContent),\n                      sizedBoxH10,\n                      Text(\n                        '$secondLabel :',\n                        style: const TextStyle(fontWeight: FontWeight.bold),\n                      ),\n                      sizedBoxH2,\n                      Text(secondContent),\n                      sizedBoxH10,\n                      Text(\n                        '$thirdLabel :',\n                        style: const TextStyle(fontWeight: FontWeight.bold),\n                      ),\n                      sizedBoxH2,\n                      Text(\n                        thirdContent,\n                        style: TextStyle(color: color),\n                      ),\n                      sizedBoxH10,\n                      Text(\n                        '$fourthLabel :',\n                        style: const TextStyle(fontWeight: FontWeight.bold),\n                      ),\n                      sizedBoxH2,\n                      AutoSizeText(\n                        fourthContent,\n                        minFontSize: 10.0,\n                        maxFontSize: 12.0,\n                      )\n                    ],\n                  ),\n                  Expanded(\n                    child: Column(children: <Widget>[\n                      trailingTop,\n                      sizedBoxH2,\n                      Text(\n                        trailingLabel,\n                        style: const TextStyle(\n                          fontWeight: FontWeight.bold,\n                          color: Colors.blueGrey,\n                        ),\n                      )\n                    ]),\n                  )\n                ],\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/statistics_card_widget.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'package:intl/intl.dart';\nimport 'package:percent_indicator/circular_percent_indicator.dart';\nimport 'package:percent_indicator/linear_percent_indicator.dart';\nimport 'package:spo_balaesang/models/report/absent_report.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\nimport 'package:spo_balaesang/utils/view_util.dart';\n\nclass StatisticCard extends StatelessWidget {\n  const StatisticCard({this.report, this.year, this.status});\n\n  final AbsentReport report;\n  final DateTime year;\n  final String status;\n\n  Color _checkAttendancePercentageColor(double percentage) {\n    if (percentage >= 25 && percentage < 50) {\n      return Colors.yellow[800];\n    }\n\n    if (percentage >= 50 && percentage < 70) {\n      return Colors.indigo;\n    }\n\n    if (percentage >= 70 && percentage <= 80) {\n      return Colors.blueAccent;\n    }\n    if (percentage >= 80 && percentage <= 100) {\n      return Colors.green[600];\n    }\n    return Colors.red[800];\n  }\n\n  Widget _buildCircularPercentage(\n      double percentage, String header, String suffix, String type) {\n    return CircularPercentIndicator(\n      radius: Get.width * 0.3,\n      linearGradient: LinearGradient(colors: <Color>[\n        _checkAttendancePercentageColor(percentage).withOpacity(1),\n        _checkAttendancePercentageColor(percentage).withOpacity(0.5)\n      ]),\n      animation: true,\n      header: Text('${suffix[0].toUpperCase()}${suffix.substring(1)}'),\n      animationDuration: 1000,\n      percent: percentage / 100,\n      lineWidth: 10.0,\n      circularStrokeCap: CircularStrokeCap.round,\n      backgroundColor:\n          _checkAttendancePercentageColor(percentage).withOpacity(0.2),\n      center: Column(\n        mainAxisAlignment: MainAxisAlignment.center,\n        children: <Widget>[\n          Text(\n            type,\n            style: const TextStyle(\n              fontSize: 10.0,\n              color: Colors.black87,\n            ),\n          ),\n          Row(\n            mainAxisAlignment: MainAxisAlignment.center,\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              Text(\n                '$percentage',\n                style: const TextStyle(\n                  fontWeight: FontWeight.w600,\n                  fontSize: 24.0,\n                ),\n              ),\n              const Text(\n                '%',\n                style: TextStyle(\n                  fontWeight: FontWeight.w600,\n                  fontSize: 14.0,\n                ),\n              )\n            ],\n          ),\n          Text(\n            '/$suffix',\n            style: const TextStyle(color: Colors.grey, fontSize: 12.0),\n          )\n        ],\n      ),\n      footer: Text(\n        header.toUpperCase(),\n        style: const TextStyle(\n          fontWeight: FontWeight.w600,\n        ),\n      ),\n    );\n  }\n\n  Widget _buildLinearPercentage(double percentage, String label, Widget footer,\n      Color color, double width) {\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: <Widget>[\n        Row(\n          children: <Widget>[\n            sizedBoxW2,\n            Text(\n              label,\n              style: const TextStyle(\n                fontWeight: FontWeight.w600,\n                fontSize: 16.0,\n              ),\n            ),\n          ],\n        ),\n        sizedBoxH6,\n        LinearPercentIndicator(\n          progressColor: color,\n          animationDuration: 1000,\n          percent: percentage / 100,\n          width: width,\n          animation: true,\n          lineHeight: 15.0,\n          backgroundColor: color.withOpacity(0.15),\n          center: Text(\n            '$percentage%',\n            style: TextStyle(\n                fontSize: 12.0, color: percentageLabelColor(percentage)),\n          ),\n        ),\n        sizedBoxH6,\n        Row(\n          children: <Widget>[sizedBoxW2, footer],\n        )\n      ],\n    );\n  }\n\n  Widget _buildPaidLeaveSection() {\n    if (status == 'Honorer') {\n      return sizedBox;\n    }\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: <Widget>[\n        const Divider(color: Colors.black26),\n        Text(\n          'Cuti',\n          style: TextStyle(\n            fontWeight: FontWeight.w600,\n            fontSize: 16.0,\n            color: Colors.grey[600],\n          ),\n        ),\n        sizedBoxH4,\n        _buildLinearPercentage(\n          double.parse(report\n              .yearly.outOfLiabilityLeave[absentReportPercentageField]\n              .toString()),\n          'Diluar Tanggungan',\n          Row(\n            crossAxisAlignment: CrossAxisAlignment.baseline,\n            textBaseline: TextBaseline.alphabetic,\n            children: <Widget>[\n              sizedBoxW6,\n              Text(\n                report.yearly.outOfLiabilityLeave[reportDayField].toString(),\n                style: TextStyle(\n                  color: Colors.grey[800],\n                ),\n              ),\n              Text(\n                '/${report.yearly.outOfLiabilityLeave[reportLimitField]} hari',\n                style: const TextStyle(color: Colors.grey, fontSize: 12.0),\n              )\n            ],\n          ),\n          Colors.red[800],\n          Get.width * 0.85,\n        ),\n        sizedBoxH20,\n        Row(\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          children: <Widget>[\n            _buildLinearPercentage(\n                double.parse(report\n                    .yearly.annualLeave[absentReportPercentageField]\n                    .toString()),\n                'Tahunan',\n                Row(\n                  crossAxisAlignment: CrossAxisAlignment.baseline,\n                  textBaseline: TextBaseline.alphabetic,\n                  children: <Widget>[\n                    Text(\n                      report.yearly.annualLeave[reportDayField].toString(),\n                      style: TextStyle(\n                        color: Colors.grey[800],\n                      ),\n                    ),\n                    Text(\n                      '/${report.yearly.annualLeave[reportLimitField]} hari',\n                      style:\n                          const TextStyle(color: Colors.grey, fontSize: 12.0),\n                    )\n                  ],\n                ),\n                Colors.indigo[300],\n                Get.width * 0.4),\n            _buildLinearPercentage(\n                double.parse(report\n                    .yearly.importantReasonLeave[absentReportPercentageField]\n                    .toString()),\n                'Alasan Penting',\n                Row(\n                  crossAxisAlignment: CrossAxisAlignment.baseline,\n                  textBaseline: TextBaseline.alphabetic,\n                  children: <Widget>[\n                    Text(\n                      report.yearly.importantReasonLeave[reportDayField]\n                          .toString(),\n                      style: TextStyle(\n                        color: Colors.grey[800],\n                      ),\n                    ),\n                    Text(\n                      '/${report.yearly.importantReasonLeave[reportLimitField]} hari',\n                      style:\n                          const TextStyle(color: Colors.grey, fontSize: 12.0),\n                    )\n                  ],\n                ),\n                Colors.pink[400],\n                Get.width * 0.4),\n          ],\n        ),\n        sizedBoxH20,\n        Row(\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          children: <Widget>[\n            _buildLinearPercentage(\n                double.parse(report\n                    .yearly.sickLeave[absentReportPercentageField]\n                    .toString()),\n                'Sakit',\n                Row(\n                  crossAxisAlignment: CrossAxisAlignment.baseline,\n                  textBaseline: TextBaseline.alphabetic,\n                  children: <Widget>[\n                    Text(\n                      report.yearly.sickLeave[reportDayField].toString(),\n                      style: TextStyle(\n                        color: Colors.grey[800],\n                      ),\n                    ),\n                    Text(\n                      '/${report.yearly.sickLeave[reportLimitField]} hari',\n                      style:\n                          const TextStyle(color: Colors.grey, fontSize: 12.0),\n                    )\n                  ],\n                ),\n                Colors.green[300],\n                Get.width * 0.4),\n            _buildLinearPercentage(\n                double.parse(report\n                    .yearly.maternityLeave[absentReportPercentageField]\n                    .toString()),\n                'Bersalin',\n                Row(\n                  crossAxisAlignment: CrossAxisAlignment.baseline,\n                  textBaseline: TextBaseline.alphabetic,\n                  children: <Widget>[\n                    Text(\n                      report.yearly.maternityLeave[reportDayField].toString(),\n                      style: TextStyle(\n                        color: Colors.grey[800],\n                      ),\n                    ),\n                    Text(\n                      '/${report.yearly.maternityLeave[reportLimitField]} hari',\n                      style:\n                          const TextStyle(color: Colors.grey, fontSize: 12.0),\n                    )\n                  ],\n                ),\n                Colors.orange[300],\n                Get.width * 0.4),\n          ],\n        ),\n      ],\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    // ignore: sized_box_for_whitespace\n    return Container(\n      width: Get.width,\n      child: Card(\n        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),\n        elevation: 2.0,\n        child: Padding(\n          padding: const EdgeInsets.all(8.0),\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: <Widget>[\n              const Text(\n                'Statistik',\n                style: TextStyle(\n                  fontSize: 16.0,\n                  fontWeight: FontWeight.w600,\n                ),\n              ),\n              const Divider(color: Colors.black38),\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceAround,\n                children: <Widget>[\n                  _buildCircularPercentage(\n                    report.monthly.attendancePercentage,\n                    DateFormat.MMMM('id_ID').format(year),\n                    'bulan',\n                    'Kehadiran',\n                  ),\n                  _buildCircularPercentage(\n                    report.yearly.attendancePercentage,\n                    '${year.year}',\n                    'tahun',\n                    'Kehadiran',\n                  ),\n                ],\n              ),\n              const Divider(color: Colors.black26),\n              _buildLinearPercentage(\n                double.parse(report.yearly.absent[absentReportPercentageField]\n                    .toString()),\n                'Alpa',\n                Row(\n                  crossAxisAlignment: CrossAxisAlignment.baseline,\n                  textBaseline: TextBaseline.alphabetic,\n                  children: <Widget>[\n                    sizedBoxW6,\n                    Text(\n                      report.yearly.absent[reportDayField].toString(),\n                      style: TextStyle(\n                        color: Colors.grey[800],\n                      ),\n                    ),\n                    Text(\n                      '/${report.yearly.absent[reportLimitField]} hari',\n                      style:\n                          const TextStyle(color: Colors.grey, fontSize: 12.0),\n                    )\n                  ],\n                ),\n                Colors.red[800],\n                Get.width * 0.85,\n              ),\n              sizedBoxH20,\n              Row(\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  _buildLinearPercentage(\n                      double.parse(report\n                          .yearly.absentPermission[absentReportPercentageField]\n                          .toString()),\n                      'Izin',\n                      Row(\n                        crossAxisAlignment: CrossAxisAlignment.baseline,\n                        textBaseline: TextBaseline.alphabetic,\n                        children: <Widget>[\n                          Text(\n                            report.yearly.absentPermission[reportDayField]\n                                .toString(),\n                            style: TextStyle(\n                              color: Colors.grey[800],\n                            ),\n                          ),\n                          Text(\n                            '/${report.totalWorkDay} hari kerja',\n                            style: const TextStyle(\n                                color: Colors.grey, fontSize: 12.0),\n                          )\n                        ],\n                      ),\n                      Colors.blueAccent[400],\n                      Get.width * 0.4),\n                  _buildLinearPercentage(\n                      double.parse(report\n                          .yearly.outstation[absentReportPercentageField]\n                          .toString()),\n                      'Dinas Luar',\n                      Row(\n                        crossAxisAlignment: CrossAxisAlignment.baseline,\n                        textBaseline: TextBaseline.alphabetic,\n                        children: <Widget>[\n                          Text(\n                            report.yearly.outstation[reportDayField].toString(),\n                            style: TextStyle(\n                              color: Colors.grey[800],\n                            ),\n                          ),\n                          Text(\n                            '/${report.totalWorkDay} hari kerja',\n                            style: const TextStyle(\n                                color: Colors.grey, fontSize: 12.0),\n                          )\n                        ],\n                      ),\n                      Colors.deepOrange[600],\n                      Get.width * 0.4),\n                ],\n              ),\n              _buildPaidLeaveSection(),\n              sizedBoxH6,\n              const Divider(color: Colors.black26),\n              Column(\n                crossAxisAlignment: CrossAxisAlignment.start,\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  const Text(\n                    'Tidak Apel Pagi',\n                    style: TextStyle(\n                      fontWeight: FontWeight.w600,\n                      fontSize: 14.0,\n                    ),\n                  ),\n                  sizedBoxH8,\n                  Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceAround,\n                    children: <Widget>[\n                      Row(\n                        crossAxisAlignment: CrossAxisAlignment.baseline,\n                        textBaseline: TextBaseline.alphabetic,\n                        children: <Widget>[\n                          Text(\n                            report.monthly.notMorningParadeCount.toString(),\n                            style: const TextStyle(\n                              fontWeight: FontWeight.w600,\n                              fontSize: 32.0,\n                            ),\n                          ),\n                          const Text(\n                            ' kali/bulan',\n                            style:\n                                TextStyle(color: Colors.grey, fontSize: 12.0),\n                          ),\n                        ],\n                      ),\n                      const SizedBox(height: 40.0, child: verticalDiv),\n                      Row(\n                        crossAxisAlignment: CrossAxisAlignment.baseline,\n                        textBaseline: TextBaseline.alphabetic,\n                        children: <Widget>[\n                          Text(\n                            report.yearly.notMorningParadeCount.toString(),\n                            style: const TextStyle(\n                              fontWeight: FontWeight.w600,\n                              fontSize: 32.0,\n                            ),\n                          ),\n                          const Text(\n                            ' kali/tahun',\n                            style:\n                                TextStyle(color: Colors.grey, fontSize: 12.0),\n                          ),\n                        ],\n                      ),\n                    ],\n                  )\n                ],\n              ),\n              sizedBoxH6,\n              const Divider(color: Colors.black26),\n              Column(\n                crossAxisAlignment: CrossAxisAlignment.start,\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  const Text(\n                    'Terlambat',\n                    style: TextStyle(\n                      fontWeight: FontWeight.w600,\n                      fontSize: 14.0,\n                    ),\n                  ),\n                  sizedBoxH8,\n                  Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceAround,\n                    children: <Widget>[\n                      Row(\n                        crossAxisAlignment: CrossAxisAlignment.baseline,\n                        textBaseline: TextBaseline.alphabetic,\n                        children: <Widget>[\n                          Text(\n                            report.monthly.lateCount.toString(),\n                            style: const TextStyle(\n                              fontWeight: FontWeight.w600,\n                              fontSize: 32.0,\n                            ),\n                          ),\n                          const Text(\n                            ' kali/bulan',\n                            style:\n                                TextStyle(color: Colors.grey, fontSize: 12.0),\n                          ),\n                        ],\n                      ),\n                      const SizedBox(height: 40.0, child: verticalDiv),\n                      Row(\n                        crossAxisAlignment: CrossAxisAlignment.baseline,\n                        textBaseline: TextBaseline.alphabetic,\n                        children: <Widget>[\n                          Text(\n                            report.yearly.lateCount.toString(),\n                            style: const TextStyle(\n                              fontWeight: FontWeight.w600,\n                              fontSize: 32.0,\n                            ),\n                          ),\n                          const Text(\n                            ' kali/tahun',\n                            style:\n                                TextStyle(color: Colors.grey, fontSize: 12.0),\n                          ),\n                        ],\n                      ),\n                    ],\n                  )\n                ],\n              ),\n              sizedBoxH6,\n              const Divider(color: Colors.black26),\n              Column(\n                crossAxisAlignment: CrossAxisAlignment.start,\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  const Text(\n                    'Istrahat Sebelum Waktunya',\n                    style: TextStyle(\n                      fontWeight: FontWeight.w600,\n                      fontSize: 14.0,\n                    ),\n                  ),\n                  sizedBoxH8,\n                  Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceAround,\n                    children: <Widget>[\n                      Row(\n                        crossAxisAlignment: CrossAxisAlignment.baseline,\n                        textBaseline: TextBaseline.alphabetic,\n                        children: <Widget>[\n                          Text(\n                            report.monthly.earlyLunchBreakCount.toString(),\n                            style: const TextStyle(\n                              fontWeight: FontWeight.w600,\n                              fontSize: 32.0,\n                            ),\n                          ),\n                          const Text(\n                            ' kali/bulan',\n                            style:\n                                TextStyle(color: Colors.grey, fontSize: 12.0),\n                          ),\n                        ],\n                      ),\n                      const SizedBox(height: 40.0, child: verticalDiv),\n                      Row(\n                        crossAxisAlignment: CrossAxisAlignment.baseline,\n                        textBaseline: TextBaseline.alphabetic,\n                        children: <Widget>[\n                          Text(\n                            report.yearly.earlyLunchBreakCount.toString(),\n                            style: const TextStyle(\n                              fontWeight: FontWeight.w600,\n                              fontSize: 32.0,\n                            ),\n                          ),\n                          const Text(\n                            ' kali/tahun',\n                            style:\n                                TextStyle(color: Colors.grey, fontSize: 12.0),\n                          ),\n                        ],\n                      ),\n                    ],\n                  )\n                ],\n              ),\n              sizedBoxH6,\n              const Divider(color: Colors.black26),\n              Column(\n                crossAxisAlignment: CrossAxisAlignment.start,\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  const Text(\n                    'Tidak Masuk Siang',\n                    style: TextStyle(\n                      fontWeight: FontWeight.w600,\n                      fontSize: 14.0,\n                    ),\n                  ),\n                  sizedBoxH8,\n                  Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceAround,\n                    children: <Widget>[\n                      Row(\n                        crossAxisAlignment: CrossAxisAlignment.baseline,\n                        textBaseline: TextBaseline.alphabetic,\n                        children: <Widget>[\n                          Text(\n                            report.monthly.notComeAfterLunchBreakCount\n                                .toString(),\n                            style: const TextStyle(\n                              fontWeight: FontWeight.w600,\n                              fontSize: 32.0,\n                            ),\n                          ),\n                          const Text(\n                            ' kali/bulan',\n                            style:\n                                TextStyle(color: Colors.grey, fontSize: 12.0),\n                          ),\n                        ],\n                      ),\n                      const SizedBox(height: 40.0, child: verticalDiv),\n                      Row(\n                        crossAxisAlignment: CrossAxisAlignment.baseline,\n                        textBaseline: TextBaseline.alphabetic,\n                        children: <Widget>[\n                          Text(\n                            report.yearly.notComeAfterLunchBreakCount\n                                .toString(),\n                            style: const TextStyle(\n                              fontWeight: FontWeight.w600,\n                              fontSize: 32.0,\n                            ),\n                          ),\n                          const Text(\n                            ' kali/tahun',\n                            style:\n                                TextStyle(color: Colors.grey, fontSize: 12.0),\n                          ),\n                        ],\n                      ),\n                    ],\n                  )\n                ],\n              ),\n              sizedBoxH6,\n              const Divider(color: Colors.black26),\n              Column(\n                crossAxisAlignment: CrossAxisAlignment.start,\n                mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                children: <Widget>[\n                  const Text(\n                    'Pulang Cepat',\n                    style: TextStyle(\n                      fontWeight: FontWeight.w600,\n                      fontSize: 14.0,\n                    ),\n                  ),\n                  sizedBoxH8,\n                  Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceAround,\n                    children: <Widget>[\n                      Row(\n                        crossAxisAlignment: CrossAxisAlignment.baseline,\n                        textBaseline: TextBaseline.alphabetic,\n                        children: <Widget>[\n                          Text(\n                            report.monthly.leaveEarlyCount.toString(),\n                            style: const TextStyle(\n                              fontWeight: FontWeight.w600,\n                              fontSize: 32.0,\n                            ),\n                          ),\n                          const Text(\n                            ' kali/bulan',\n                            style:\n                                TextStyle(color: Colors.grey, fontSize: 12.0),\n                          ),\n                        ],\n                      ),\n                      const SizedBox(height: 40.0, child: verticalDiv),\n                      Row(\n                        crossAxisAlignment: CrossAxisAlignment.baseline,\n                        textBaseline: TextBaseline.alphabetic,\n                        children: <Widget>[\n                          Text(\n                            report.yearly.leaveEarlyCount.toString(),\n                            style: const TextStyle(\n                              fontWeight: FontWeight.w600,\n                              fontSize: 32.0,\n                            ),\n                          ),\n                          const Text(\n                            ' kali/tahun',\n                            style:\n                                TextStyle(color: Colors.grey, fontSize: 12.0),\n                          ),\n                        ],\n                      ),\n                    ],\n                  )\n                ],\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/user_info_card_widget.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:spo_balaesang/utils/app_const.dart';\n\nclass UserInfoCardWidget extends StatelessWidget {\n  const UserInfoCardWidget(\n      {this.status,\n      this.name,\n      this.position,\n      this.rank,\n      this.group,\n      this.nip,\n      this.department});\n\n  final String status;\n  final String name;\n  final String position;\n  final String rank;\n  final String group;\n  final String nip;\n  final String department;\n\n  Widget _buildPnsInfoSection() {\n    if (status == 'Honorer') {\n      return sizedBox;\n    }\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: <Widget>[\n        Row(\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          children: <Widget>[\n            Text(\n              'Golongan',\n              style: TextStyle(color: Colors.grey[700]),\n            ),\n            Text(\n              group ?? '',\n              style: const TextStyle(\n                fontWeight: FontWeight.w600,\n              ),\n            ),\n          ],\n        ),\n        sizedBoxH4,\n        Row(\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          children: <Widget>[\n            Text(\n              'Pangkat',\n              style: TextStyle(color: Colors.grey[700]),\n            ),\n            Text(\n              rank?.toUpperCase() ?? '',\n              style: const TextStyle(\n                fontWeight: FontWeight.w600,\n              ),\n            ),\n          ],\n        ),\n        sizedBoxH4,\n        Row(\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          children: <Widget>[\n            Text(\n              'NIP',\n              style: TextStyle(color: Colors.grey[700]),\n            ),\n            Text(\n              nip ?? '',\n              style: const TextStyle(\n                fontWeight: FontWeight.w600,\n              ),\n            ),\n          ],\n        ),\n        sizedBoxH4,\n      ],\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Card(\n      elevation: 4.0,\n      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),\n      child: Padding(\n        padding: const EdgeInsets.all(8.0),\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: <Widget>[\n            Text(\n              'Informasi Pegawai : ',\n              style: TextStyle(\n                  fontSize: 16.0,\n                  fontWeight: FontWeight.w600,\n                  color: Colors.grey[700]),\n            ),\n            const Divider(color: Colors.black38),\n            Center(\n              child: Column(\n                children: <Widget>[\n                  Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: <Widget>[\n                      Text(\n                        'Nama',\n                        style: TextStyle(color: Colors.grey[700]),\n                      ),\n                      Text(\n                        name ?? '',\n                        style: const TextStyle(\n                          fontWeight: FontWeight.w600,\n                        ),\n                      ),\n                    ],\n                  ),\n                  sizedBoxH4,\n                  Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: <Widget>[\n                      Text(\n                        'Jabatan',\n                        style: TextStyle(color: Colors.grey[700]),\n                      ),\n                      Text(\n                        position ?? '',\n                        style: const TextStyle(\n                          fontWeight: FontWeight.w600,\n                        ),\n                      ),\n                    ],\n                  ),\n                  sizedBoxH4,\n                  Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: <Widget>[\n                      Text(\n                        'Bagian',\n                        style: TextStyle(color: Colors.grey[700]),\n                      ),\n                      Text(\n                        department ?? '',\n                        style: const TextStyle(\n                          fontWeight: FontWeight.w600,\n                        ),\n                      ),\n                    ],\n                  ),\n                  sizedBoxH4,\n                  Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: <Widget>[\n                      Text(\n                        'Status',\n                        style: TextStyle(color: Colors.grey[700]),\n                      ),\n                      Text(\n                        status ?? '',\n                        style: const TextStyle(\n                          fontWeight: FontWeight.w600,\n                        ),\n                      ),\n                    ],\n                  ),\n                  sizedBoxH4,\n                  _buildPnsInfoSection(),\n                ],\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "privacy-policy.md",
    "content": "﻿PRIVACY NOTICE\n\n\nLast updated November 12, 2020\n\n\n\n\n\n\nThank you for choosing to be part of our community at Banua Coders (\"Company\", \"we\", \"us\", \"our\"). We are committed to protecting your personal information and your right to privacy. If you have any questions or concerns about this privacy notice, or our practices with regards to your personal information, please contact us at admin@banuacoders.com.\n\n\nWhen you use our mobile application, as the case may be (the \"App\") and more generally, use any of our services (the \"Services\", which include the App), we appreciate that you are trusting us with your personal information. We take your privacy very seriously. In this privacy notice, we seek to explain to you in the clearest way possible what information we collect, how we use it and what rights you have in relation to it. We hope you take some time to read through it carefully, as it is important. If there are any terms in this privacy notice that you do not agree with, please discontinue use of our Services immediately.\n\n\nThis privacy notice applies to all information collected through our Services (which, as described above, includes our App), as well as, any related services, sales, marketing or events.\n\n\nPlease read this privacy notice carefully as it will help you understand what we do with the information that we collect.\n\n\nTABLE OF CONTENTS\n\n\n1. WHAT INFORMATION DO WE COLLECT?\n2. HOW DO WE USE YOUR INFORMATION?\n3. WILL YOUR INFORMATION BE SHARED WITH ANYONE?\n4. HOW LONG DO WE KEEP YOUR INFORMATION?\n5. HOW DO WE KEEP YOUR INFORMATION SAFE?\n6. DO WE COLLECT INFORMATION FROM MINORS?\n7. WHAT ARE YOUR PRIVACY RIGHTS?\n8. CONTROLS FOR DO-NOT-TRACK FEATURES\n9. DO CALIFORNIA RESIDENTS HAVE SPECIFIC PRIVACY RIGHTS?\n10. DO WE MAKE UPDATES TO THIS NOTICE?\n11. HOW CAN YOU CONTACT US ABOUT THIS NOTICE?\n12. HOW CAN YOU REVIEW, UPDATE OR DELETE THE DATA WE COLLECT FROM YOU?\n\n\n1. WHAT INFORMATION DO WE COLLECT?\n\nPersonal information you disclose to us\n\n\nIn Short:  We collect personal information that you provide to us.\n\n\nWe collect personal information that you voluntarily provide to us when you express an interest in obtaining information about us or our products and Services, when you participate in activities on the App (such as by posting messages in our online forums or entering competitions, contests or giveaways) or otherwise when you contact us.\n\n\nThe personal information that we collect depends on the context of your interactions with us and the App, the choices you make and the products and features you use. The personal information we collect may include the following:\n\n\nPersonal Information Provided by You. We collect names; phone numbers; email addresses; job titles; passwords; and other similar information.\n\n\nAll personal information that you provide to us must be true, complete and accurate, and you must notify us of any changes to such personal information.\n\nInformation collected through our App\n\n\nIn Short:  We collect information regarding your geo-location, mobile device, when you use our App.\n\n\nIf you use our App, we also collect the following information:\nGeo-Location Information. We may request access or permission to and track location-based information from your mobile device, either continuously or while you are using our App, to provide certain location-based services. If you wish to change our access or permissions, you may do so in your device's settings.\nMobile Device Access. We may request access or permission to certain features from your mobile device, including your mobile device's camera, storage, and other features. If you wish to change our access or permissions, you may do so in your device's settings.\nMobile Device Data. We automatically collect device information (such as your mobile device ID, model and manufacturer), operating system, version information and system configuration information, device and application identification numbers, browser type and version, hardware model Internet service provider and/or mobile carrier, and Internet Protocol (IP) address (or proxy server). If you are using our App, we may also collect information about the phone network associated with your mobile device, your mobile device’s operating system or platform, the type of mobile device you use, your mobile device’s unique device ID and information about the features of our App you accessed.\nThis information is primarily needed to maintain the security and operation of our App, for troubleshooting and for our internal analytics and reporting purposes.\n\nInformation collected from other sources\n\n\nIn Short:  We may collect limited data from public databases, marketing partners, and other outside sources.\n\n\nIn order to enhance our ability to provide relevant marketing, offers and services to you and update our records, we may obtain information about you from other sources, such as public databases, joint marketing partners, affiliate programs, data providers, as well as from other third parties. This information includes mailing addresses, job titles, email addresses, phone numbers, intent data (or user behavior data), Internet Protocol (IP) addresses, social media profiles, social media URLs and custom profiles, for purposes of targeted advertising and event promotion.\n\n\n2. HOW DO WE USE YOUR INFORMATION?\n\n\nIn Short:  We process your information for purposes based on legitimate business interests, the fulfillment of our contract with you, compliance with our legal obligations, and/or your consent.\n\n\nWe use personal information collected via our App for a variety of business purposes described below. We process your personal information for these purposes in reliance on our legitimate business interests, in order to enter into or perform a contract with you, with your consent, and/or for compliance with our legal obligations. We indicate the specific processing grounds we rely on next to each purpose listed below.\n\n\nWe use the information we collect or receive:\nTo facilitate account creation and logon process. If you choose to link your account with us to a third-party account (such as your Google or Facebook account), we use the information you allowed us to collect from those third parties to facilitate account creation and logon process for the performance of the contract.\nTo post testimonials. We post testimonials on our App that may contain personal information. Prior to posting a testimonial, we will obtain your consent to use your name and the content of the testimonial. If you wish to update, or delete your testimonial, please contact us at admin@banuacoders.com and be sure to include your name, testimonial location, and contact information.\nRequest feedback. We may use your information to request feedback and to contact you about your use of our App.\nTo enable user-to-user communications. We may use your information in order to enable user-to-user communications with each user's consent.\nTo manage user accounts. We may use your information for the purposes of managing our account and keeping it in working order.\nTo send administrative information to you. We may use your personal information to send you product, service and new feature information and/or information about changes to our terms, conditions, and policies.\nTo protect our Services. We may use your information as part of our efforts to keep our App safe and secure (for example, for fraud monitoring and prevention).\nTo enforce our terms, conditions and policies for business purposes, to comply with legal and regulatory requirements or in connection with our contract.\nTo respond to legal requests and prevent harm. If we receive a subpoena or other legal request, we may need to inspect the data we hold to determine how to respond.\n\n\n3. WILL YOUR INFORMATION BE SHARED WITH ANYONE?\n\n\nIn Short:  We only share information with your consent, to comply with laws, to provide you with services, to protect your rights, or to fulfill business obligations.\n\n\nWe may process or share your data that we hold based on the following legal basis:\nConsent: We may process your data if you have given us specific consent to use your personal information for a specific purpose.\nLegitimate Interests: We may process your data when it is reasonably necessary to achieve our legitimate business interests.\nPerformance of a Contract: Where we have entered into a contract with you, we may process your personal information to fulfill the terms of our contract.\nLegal Obligations: We may disclose your information where we are legally required to do so in order to comply with applicable law, governmental requests, a judicial proceeding, court order, or legal process, such as in response to a court order or a subpoena (including in response to public authorities to meet national security or law enforcement requirements).\nVital Interests: We may disclose your information where we believe it is necessary to investigate, prevent, or take action regarding potential violations of our policies, suspected fraud, situations involving potential threats to the safety of any person and illegal activities, or as evidence in litigation in which we are involved.\nMore specifically, we may need to process your data or share your personal information in the following situations:\nBusiness Transfers. We may share or transfer your information in connection with, or during negotiations of, any merger, sale of company assets, financing, or acquisition of all or a portion of our business to another company.\nOther Users. When you share personal information or otherwise interact with public areas of the App, such personal information may be viewed by all users and may be publicly made available outside the App in perpetuity. Similarly, other users will be able to view descriptions of your activity, communicate with you within our App, and view your profile.\n\n\n4. HOW LONG DO WE KEEP YOUR INFORMATION?\n\n\nIn Short:  We keep your information for as long as necessary to fulfill the purposes outlined in this privacy notice unless otherwise required by law.\n\n\nWe will only keep your personal information for as long as it is necessary for the purposes set out in this privacy notice, unless a longer retention period is required or permitted by law (such as tax, accounting or other legal requirements). No purpose in this notice will require us keeping your personal information for longer than 1 year.\n\n\nWhen we have no ongoing legitimate business need to process your personal information, we will either delete or anonymize such information, or, if this is not possible (for example, because your personal information has been stored in backup archives), then we will securely store your personal information and isolate it from any further processing until deletion is possible.\n\n\n5. HOW DO WE KEEP YOUR INFORMATION SAFE?\n\n\nIn Short:  We aim to protect your personal information through a system of organizational and technical security measures.\n\n\nWe have implemented appropriate technical and organizational security measures designed to protect the security of any personal information we process. However, despite our safeguards and efforts to secure your information, no electronic transmission over the Internet or information storage technology can be guaranteed to be 100% secure, so we cannot promise or guarantee that hackers, cybercriminals, or other unauthorized third parties will not be able to defeat our security, and improperly collect, access, steal, or modify your information. Although we will do our best to protect your personal information, transmission of personal information to and from our App is at your own risk. You should only access the App within a secure environment.\n\n\n6. DO WE COLLECT INFORMATION FROM MINORS?\n\n\nIn Short:  We do not knowingly collect data from or market to children under 18 years of age.\n\n\nWe do not knowingly solicit data from or market to children under 18 years of age. By using the App, you represent that you are at least 18 or that you are the parent or guardian of such a minor and consent to such minor dependent’s use of the App. If we learn that personal information from users less than 18 years of age has been collected, we will deactivate the account and take reasonable measures to promptly delete such data from our records. If you become aware of any data we may have collected from children under age 18, please contact us at admin@banuacoders.com.\n\n\n7. WHAT ARE YOUR PRIVACY RIGHTS?\n\n\nIn Short:  You may review, change, or terminate your account at any time.\n \nIf you are a resident in the European Economic Area and you believe we are unlawfully processing your personal information, you also have the right to complain to your local data protection supervisory authority. You can find their contact details here: http://ec.europa.eu/justice/data-protection/bodies/authorities/index_en.htm.\n\n\nIf you are a resident in Switzerland, the contact details for the data protection authorities are available here: https://www.edoeb.admin.ch/edoeb/en/home.html.\n\n\n8. CONTROLS FOR DO-NOT-TRACK FEATURES\n\n\nMost web browsers and some mobile operating systems and mobile applications include a Do-Not-Track (\"DNT\") feature or setting you can activate to signal your privacy preference not to have data about your online browsing activities monitored and collected. At this stage no uniform technology standard for recognizing and implementing DNT signals has been finalized. As such, we do not currently respond to DNT browser signals or any other mechanism that automatically communicates your choice not to be tracked online. If a standard for online tracking is adopted that we must follow in the future, we will inform you about that practice in a revised version of this privacy notice. \n\n\n9. DO CALIFORNIA RESIDENTS HAVE SPECIFIC PRIVACY RIGHTS?\n\n\nIn Short:  Yes, if you are a resident of California, you are granted specific rights regarding access to your personal information.\n\n\nCalifornia Civil Code Section 1798.83, also known as the \"Shine The Light\" law, permits our users who are California residents to request and obtain from us, once a year and free of charge, information about categories of personal information (if any) we disclosed to third parties for direct marketing purposes and the names and addresses of all third parties with which we shared personal information in the immediately preceding calendar year. If you are a California resident and would like to make such a request, please submit your request in writing to us using the contact information provided below.\n\n\nIf you are under 18 years of age, reside in California, and have a registered account with the App, you have the right to request removal of unwanted data that you publicly post on the App. To request removal of such data, please contact us using the contact information provided below, and include the email address associated with your account and a statement that you reside in California. We will make sure the data is not publicly displayed on the App, but please be aware that the data may not be completely or comprehensively removed from all our systems (e.g. backups, etc.).\n\n\n10. DO WE MAKE UPDATES TO THIS NOTICE?     \n\n\nIn Short:  Yes, we will update this notice as necessary to stay compliant with relevant laws.\n\n\nWe may update this privacy notice from time to time. The updated version will be indicated by an updated \"Revised\" date and the updated version will be effective as soon as it is accessible. If we make material changes to this privacy notice, we may notify you either by prominently posting a notice of such changes or by directly sending you a notification. We encourage you to review this privacy notice frequently to be informed of how we are protecting your information.\n\n\n11. HOW CAN YOU CONTACT US ABOUT THIS NOTICE?     \n\n\nIf you have questions or comments about this notice, you may email us at admin@banuacoders.com or by post to:\n\n\nBanua Coders\nJl. Poros Palu - Toli-Toli, Dusun V Moluid, Desa Tambu, Kec. Balaesang, Kab. Donggala, Sulawesi Tengah\nDonggala, Sulawesi Tengah 94355\nIndonesia\n\n\n12. HOW CAN YOU REVIEW, UPDATE, OR DELETE THE DATA WE COLLECT FROM YOU?     \n\n\nBased on the applicable laws of your country, you may have the right to request access to the personal information we collect from you, change that information, or delete it in some circumstances. To request to review, update, or delete your personal information, please submit a request form by clicking here. We will respond to your request within 30 days. \nThis privacy policy was created using Termly’s Privacy Policy Generator."
  },
  {
    "path": "pubspec.yaml",
    "content": "name: spo_balaesang\nversion: 2.0.0+1\npublish_to: none\ndescription: Sistem presensi online kantor pemerintahan kecamatan balaesang.\nenvironment: \n  sdk: '>=2.7.0 <3.0.0'\n\ndependencies: \n  font_awesome_flutter: ^9.0.0\n  flutter_cupertino_datetime_picker: ^2.0.1\n  percent_indicator: ^3.0.1\n  table_calendar: ^3.0.0\n  cupertino_icons: ^1.0.3\n  http: ^0.13.1\n  provider: ^5.0.0\n  intl: ^0.17.0\n  url_launcher: ^6.0.4\n  shared_preferences: ^2.0.5\n  permission_handler: ^7.1.0\n  slugify: ^2.0.0\n  device_info: ^2.0.1\n  auto_size_text: ^3.0.0-nullsafety.0\n  geolocator: ^7.0.3\n  android_intent: ^2.0.0\n  app_settings: ^4.1.0\n  flutter_config: any\n  camera: ^0.8.1\n  path_provider: ^2.0.1\n  path: ^1.8.0\n  flutter_image_compress: ^1.0.0\n  qr_code_scanner: ^0.4.0\n  flutter_countdown_timer: ^4.0.2\n  image_picker: ^0.7.4\n  geocoding: ^2.0.0\n  progress_dialog: ^1.2.4\n  shimmer: ^2.0.0-nullsafety.0\n  get: ^4.1.1\n  flare_flutter: ^3.0.0\n  introduction_screen: ^2.1.0\n  flutter_launcher_icons: ^0.9.0\n  onesignal_flutter: ^2.6.2\n  flutter_local_notifications: ^5.0.0\n  cached_network_image: ^3.0.0\n  photo_view: ^0.11.1\n  flutter_native_splash: ^1.1.5+1\n  flutter_spinkit: ^5.0.0\n  lint: ^1.5.3\n  pull_to_refresh: 2.0.0\n  timeline_tile: 2.0.0\n  search_page: 2.0.0+1\n  location: 4.1.1\n  flutter: \n    sdk: flutter\n\ndev_dependencies: \n  flutter_test: \n    sdk: flutter\n\nflutter_icons: \n  image_path_ios: assets/launcher/icon.png\n  image_path_android: assets/launcher/icon.png\n  adaptive_icon_background: assets/launcher/launcher_bg.png\n  adaptive_icon_foreground: assets/launcher/launcher_fg.png\n  ios: true\n  android: true\n\nflutter_native_splash: \n  image: assets/launcher/icon.png\n  color: 6b8eef\n  android_disable_fullscreen: true\n\nflutter: \n  assets: \n    - assets/images/\n    - assets/logo/\n    - assets/flare/\n    - assets/launcher/\n  uses-material-design: true\n\n"
  },
  {
    "path": "test/widget_test.dart",
    "content": "// This is a basic Flutter widget test.\n//\n// To perform an interaction with a widget in your test, use the WidgetTester\n// utility that Flutter provides. For example, you can send tap and scroll\n// gestures. You can also use WidgetTester to find child widgets in the widget\n// tree, read text, and verify that the values of widget properties are correct.\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nimport 'package:spo_balaesang/main.dart';\n\nvoid main() {\n  testWidgets('Counter increments smoke test', (WidgetTester tester) async {\n    // Build our app and trigger a frame.\n    await tester.pumpWidget(MyApp());\n\n    // Verify that our counter starts at 0.\n    expect(find.text('0'), findsOneWidget);\n    expect(find.text('1'), findsNothing);\n\n    // Tap the '+' icon and trigger a frame.\n    await tester.tap(find.byIcon(Icons.add));\n    await tester.pump();\n\n    // Verify that our counter has incremented.\n    expect(find.text('0'), findsNothing);\n    expect(find.text('1'), findsOneWidget);\n  });\n}\n"
  }
]